Macro SpreadsheetTools

From FreeCAD Documentation
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Other languages:

Macro SpreadsheetTools

Description
This macro helps managing cells inside FreeCAD's Spreadsheet-workbench. It helps managing cells inside FreeCAD's Spreadsheet-workbench. With its help one is able to:Cut/delete data and/or alias information, Copy data and/or an alias information, Paste data and/or alias information

Macro version: 0.2.08
Last modified: 2017-11-03
FreeCAD version: All
Download: ToolBar Icon
Author: Wilfried Hortschitz
Author
Wilfried Hortschitz
Download
ToolBar Icon
Links
Macro Version
0.2.08
Date last modified
2017-11-03
FreeCAD Version(s)
All
Default shortcut
None
See also
None

Description

This macro helps managing cells inside FreeCAD's Spreadsheet-workbench. It helps managing cells inside FreeCAD's Spreadsheet-workbench. With its help one is able to:

  • Cut/delete data and/or alias information in the selected area of cells.
  • Copy data and/or an alias information in the selected area of cells to clipboard.
  • Paste data and/or alias information in the selected area of cells from clipboard.

More information might be found on FreeCAD forums: http://forum.freecadweb.org/ and particular on https://forum.freecadweb.org/viewtopic.php?f=22&t=20508&hilit=spreadsheet#p158443 and on Github on https://github.com/HoWilgh/FCSpreadsheetTools/blob/master/README.md .

Script

ToolBar Icon

Macro SpreadsheetTools.FCMacro

# ============================================================================================================
# ============================================================================================================
# ==                                                                                                        ==
# ==                                       Spreadsheet tools                                                ==
# ==                                                                                                        ==
# ============================================================================================================
# ============================================================================================================
# ABOUT
# ============================================================================================================
# version v0.2.08 !!! This is beta version code so backup your data first!!! The author assumes no liability for data loss.
#
# Macro developed for FreeCAD (http://www.freecadweb.org/).
# This macro helps managing cells inside FreeCAD Spreadsheet workbench. It is able to:
#        - Cut/delete data and/or alias fields in a selected area of cells
#        - Copy data and/or alias fields in a selected area of cells to clipboard
#        - Paste data and/or alias fields in a selected area of cells from cliboard
# More information might be found on FreeCAD forums: http://forum.freecadweb.org/
#
#
# LICENSE
# ============================================================================================================
#
# This work is licensed under GNU Lesser General Public License (LGPL).
# To view a copy of this license, visit https://www.gnu.org/licenses/lgpl-3.0.html.
# 
# ============================================================================================================
__title__   = "Spreadsheet_tools"
__author__  = "HoWil"
__version__ = "0.2.08"
__date__    = "2017-09-02"

__Comment__ = "This macro helps managing cells inside a single spreadsheet of FreeCAD Spreadsheet workbench. It is able to cut/delete, copy and paste an area of cells including format, alias and units. Merged cells are not supported."

__Status__ = "stable"
__Requires__ = "FreeCAD 0.17"

from PySide import QtGui, QtCore

import FreeCADGui
#import FreeCAD
import FreeCAD as App

import string


'''
*Working* Cut/delete the selected area
*Working* Copy to clipboard
*Working* Paste from clipboard
*Working* Copy and paste alias

TODO:
* Check if something is overwritten and show a warning.
* Use shortcuts like Ctrl-c.
* Test if pandas is installed and offer import-export if available.

* Right click menu??
* Copy and paste formating???

Known limitations:
* Does work only with one opened FC-Spreadsheet.
* A cell has to be selected before selecting one of the options of Spreadsheet_tools.
* Does not work on merged cells.
'''


try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)


# ===== Global variables ==============================================
alphabet_list = list(string.ascii_uppercase)

column_list = [] # is filled with A, B, C,.... AA, AB, AC,...
for i in range(0,26):
    column_list.append(alphabet_list[i])

for i in range(0,26):
    for j in range(0,26):
        column_list.append(alphabet_list[i] + alphabet_list[j])


class Spreadsheet_Tools(QtGui.QDialog):
    """"""
    def __init__(self, MainWindow):
        super(Spreadsheet_Tools, self).__init__()
        self.window = MainWindow

        #MainWindow.setObjectName(_fromUtf8("MainWindow"))
        #MainWindow.resize(400, 450)
        #MainWindow.setMinimumSize(QtCore.QSize(400, 450))
        #MainWindow.setMaximumSize(QtCore.QSize(400, 450))
        #self.widget = QtGui.QWidget(MainWindow)
        #self.widget.setObjectName(_fromUtf8("widget"))


        self.init_UI()


    def init_UI(self):
        FreeCAD.Console.PrintMessage("init_UI")

        option3Button = QtGui.QPushButton("Copy to clipboard")
        option3Button.clicked.connect(self.copy_to_clipboard)
        option1Button = QtGui.QPushButton("Cut to clipboard/Delete selection")
        option1Button.clicked.connect(self.cut_delete_selection)
        option2Button = QtGui.QPushButton("Paste from clipboard")
        option2Button.clicked.connect(self.paste_from_clipboard)
        option4Button = QtGui.QPushButton("Close this dialog")
        option4Button.clicked.connect(self.close_dialog)

        buttonBox = QtGui.QDialogButtonBox()
        buttonBox = QtGui.QDialogButtonBox(QtCore.Qt.Vertical)
        buttonBox.setGeometry(QtCore.QRect(00, 100, 210, 200))
        buttonBox.addButton(option3Button, QtGui.QDialogButtonBox.ActionRole)
        buttonBox.addButton(option1Button, QtGui.QDialogButtonBox.ActionRole)
        buttonBox.addButton(option2Button, QtGui.QDialogButtonBox.ActionRole)
        buttonBox.addButton(option4Button, QtGui.QDialogButtonBox.ActionRole)



        self.groupBox = QtGui.QGroupBox()                                        # this is the group for associate
        self.groupBox.setGeometry(QtCore.QRect(00, 220, 125, 200))                          # coordinates position
        self.groupBox.setObjectName(_fromUtf8("groupBox"))                                  # name of window groupBox

#        section checkBox 1
        self.checkBox_1 = QtGui.QCheckBox(self.groupBox)                                    # create object QRadioButton in groupBox
        self.checkBox_1.setGeometry(QtCore.QRect(0, 0, 150, 20))                         # coordinates position
        self.checkBox_1.setObjectName(_fromUtf8("Copy/paste/cut visible cell-content"))                              # name of object
        self.checkBox_1.setChecked(True)                                                    # Check by default True or False

        self.checkBox_1.setToolTip(_translate("MainWindow", "Copy/paste/cut visible cell-content", None))
        self.checkBox_1.setText(_translate("MainWindow", "Use visible content", None))
        #self.checkBox_1.clicked.connect(self.on_checkBox_1_clicked)

#        section checkBox 2
        self.checkBox_2 = QtGui.QCheckBox(self.groupBox)                                    # create object QRadioButton in groupBox
        self.checkBox_2.setGeometry(QtCore.QRect(160, 0, 150, 20))                         # coordinates position
        self.checkBox_2.setObjectName(_fromUtf8("Copy/paste/cut cell-alias"))                              # name of object
        self.checkBox_2.setChecked(True)                                                    # Check by default True or False

        self.checkBox_2.setToolTip(_translate("MainWindow", "Use alias", None))
        self.checkBox_2.setText(_translate("MainWindow", "Use alias", None))
        #self.checkBox_2.clicked.connect(self.on_checkBox_2_clicked)

        self.checkBox_3 = QtGui.QCheckBox(self.groupBox)                                    # create object QRadioButton in groupBox
        self.checkBox_3.setGeometry(QtCore.QRect(280, 0, 150, 20))                         # coordinates position
        self.checkBox_3.setObjectName(_fromUtf8("Copy/paste/cut cell-alias"))                              # name of object
        self.checkBox_3.setChecked(True)                                                    # Check by default True or False

        self.checkBox_3.setToolTip(_translate("MainWindow", "Use formatting", None))
        self.checkBox_3.setText(_translate("MainWindow", "Use formatting", None))
        #self.checkBox_3.clicked.connect(self.on_checkBox_3_clicked)

        mainLayout = QtGui.QVBoxLayout()

        mainLayout.addWidget(self.groupBox)
        mainLayout.addWidget(buttonBox)

        self.setLayout(mainLayout)
        # define window     xLoc,yLoc,xDim,yDim
        self.setGeometry(   500, 500, 450, 220)
        self.setWindowTitle("Pick a Button")


    def get_selection(self):

        mw = FreeCADGui.getMainWindow()
        mdiarea = mw.findChild(QtGui.QMdiArea)

        subw = mdiarea.subWindowList()

        for i in subw:
            if i.widget().metaObject().className() == "SpreadsheetGui::SheetView":
                sheet = i.widget()

                table = sheet.findChild(QtGui.QTableView)
        ind = table.selectedIndexes()

        self.mw = mw    # mainwindow
        self.sheet = sheet  # spreadsheet
        self.table = table  # table
        self.ind = ind

        l_elements = len(ind)

        first_element = ind.__getitem__(0)
        fe_col = first_element.column()
        fe_row = first_element.row()
        fe_alphanum = column_list[fe_col] + str(fe_row+1)

        last_element = ind.__getitem__(l_elements-1)
        le_col = last_element.column()
        le_row = last_element.row()
        le_alphanum = column_list[le_col] + str(le_row+1)

        self.fe_alphanum = fe_alphanum
        self.fe_col = fe_col # numeric representation of column, D=>3
        self.fe_row = fe_row # numeric representation of row

        self.le_alphanum = le_alphanum
        self.le_col = le_col
        self.le_row = le_row


    def delete_selection(self):
        # Delete selection
        self.get_selection()

        ind = self.ind

        rows = range(self.fe_row, self.le_row+1)
        columns = range(self.fe_col, self.le_col+1)


        for row in rows:
            for column in columns:
                cell = column_list[column] + str(row+1)
                if self.checkBox_1.isChecked():
                    App.ActiveDocument.Spreadsheet.set(cell, str(' ') )
                if self.checkBox_2.isChecked():
                    try:
                        App.ActiveDocument.Spreadsheet.setAlias(cell, None)
                    except:
                        FreeCAD.Console.PrintMessage("\nCould not delete alias.")
                if self.checkBox_3.isChecked():
                    try:
                        App.ActiveDocument.Spreadsheet.set(cell, '')
                    except:
                        FreeCAD.Console.PrintMessage("\nCould not delete formatting.")

        App.activeDocument().recompute()


    def cut_delete_selection(self):
        # Cut selection
        self.copy_to_clipboard()
        self.delete_selection()


    def paste_from_clipboard(self):
        # Paste from clipboard

        self.get_selection()

        clipboard = QtGui.QApplication.clipboard()

        if clipboard.mimeData().hasText():

            cbtext = clipboard.mimeData().text()
            cbtext_split = [s.split('\t') for s in cbtext.splitlines()]

            if self.checkBox_1.isChecked() and self.checkBox_2.isChecked() and self.checkBox_3.isChecked():

                n_cells = len(cbtext_split)/3

                index_col = self.fe_col
                for n_line, line in zip(range(n_cells), cbtext_split[0:n_cells]):
                    for n_word, col in zip(range(len(line)), line):
                        cell = column_list[index_col+n_word] + str(self.fe_row+n_line+1)
                        App.ActiveDocument.Spreadsheet.set(cell, str(col) )

                for n_line, line in zip(range(n_cells), cbtext_split[n_cells:n_cells*2]):
                    for n_word, col in zip(range(len(line)), line):
                        cell = column_list[index_col+n_word] + str(self.fe_row+n_line+1)
                        try:
                            App.ActiveDocument.Spreadsheet.setAlias(cell, str(col))
                        except:
                            FreeCAD.Console.PrintMessage("\nCould not set/paste alias.")

                for n_line, line in zip(range(n_cells), cbtext_split[n_cells*2:]):
                    for n_word, col in zip(range(len(line)), line):
                        cell = column_list[index_col+n_word] + str(self.fe_row+n_line+1)
                        import ast
                        col = ast.literal_eval(col)

                        try:
                            if not(col['style'] == 'None'):
                                eval("App.ActiveDocument.Spreadsheet.setStyle('"+cell+"', "+col['style']+")")
                            if not(col['alignment'] == 'None'):
                                eval("App.ActiveDocument.Spreadsheet.setAlignment('"+cell+"', "+col['alignment']+")")
                        except:
                            FreeCAD.Console.PrintMessage("\nCould not set formatting (style, alignment).")

                        try:
                            if not(col['foreground'] == 'None'):
                                App.ActiveDocument.Spreadsheet.setForeground(cell, ast.literal_eval(col['foreground']))
                            if not(col['background'] == 'None'):
                                eval("App.ActiveDocument.Spreadsheet.setBackground('"+cell+"', "+col['background']+")")
                        except:
                            FreeCAD.Console.PrintMessage("\nCould not set formatting ( background, foreground).")

                        try:
                            if not(col['contents'] == ''):
                                App.ActiveDocument.Spreadsheet.set(cell, col['contents'])
                            if not(col['displayunit'] == 'None'):
                                App.ActiveDocument.Spreadsheet.setDisplayUnit(cell, col['displayunit'])
                        except:
                            FreeCAD.Console.PrintMessage("\nCould not set formatting (displayunit and contents).")


            if self.checkBox_1.isChecked() and (not self.checkBox_2.isChecked()):
                n_cells = len(cbtext_split)

                index_col = self.fe_col
                for n_line, line in zip(range(n_cells), cbtext_split[0:n_cells]):
                    for n_word, col in zip(range(len(line)), line):
                        cell = column_list[index_col+n_word] + str(self.fe_row+n_line+1)
                        App.ActiveDocument.Spreadsheet.set(cell, str(col) )

            if (not self.checkBox_1.isChecked()) and self.checkBox_2.isChecked():
                n_cells = len(cbtext_split)

                index_col = self.fe_col
                for n_line, line in zip(range(n_cells), cbtext_split[0:n_cells]):
                    for n_word, col in zip(range(len(line)), line):
                        cell = column_list[index_col+n_word] + str(self.fe_row+n_line+1)
                        try:
                            App.ActiveDocument.Spreadsheet.setAlias(cell, str(col))
                        except:
                            FreeCAD.Console.PrintMessage("\nCould not set/paste alias.")

            App.activeDocument().recompute()

        else:
            clipboard.setText(tr("Cannot display data! No proper information stored in clipboard."))


    def copy_to_clipboard(self):
        # Copy to clipboard

        self.get_selection()

        if len(self.ind) > 0:
            # sort select indexes into rows and columns
            previous = self.ind[0]
            columns = []
            rows = []
            clipboard = ""

            if self.checkBox_1.isChecked():

                for index in self.ind:
                    if previous.column() != index.column():
                        columns.append(rows)
                        rows = []
                    rows.append(index.data())

                    idx_col = index.column()
                    idx_row = index.row()
                    idx_alphanum = column_list[idx_col] + str(idx_row+1)

                    previous = index

                columns.append(rows)
                cell_content = columns
                self.cell_content = cell_content

                # add rows and columns to clipboard
                nrows = len(cell_content[0])
                ncols = len(cell_content)
                for r in xrange(nrows):
                    for c in xrange(ncols):
                        if cell_content[c][r] is not None:
                            clipboard += cell_content[c][r]
                        else:
                            clipboard += ''
                        if c != (ncols-1):
                            clipboard += '\t'
                    clipboard += '\n'

            previous_alias = self.ind[0]
            columns_alias = []
            rows_alias = []
            clipboard_alias = ""

            if self.checkBox_2.isChecked():

                for index in self.ind:
                    if previous_alias.column() != index.column():
                        columns_alias.append(rows_alias)
                        rows_alias = []

                    idx_col = index.column()
                    idx_row = index.row()
                    idx_alphanum = column_list[idx_col] + str(idx_row+1)
                    rows_alias.append(App.ActiveDocument.Spreadsheet.getAlias(idx_alphanum))

                    previous_alias = index

                columns_alias.append(rows_alias)
                cell_content_alias = columns_alias
                self.cell_content_alias = cell_content_alias

                # add rows and columns to clipboard
                nrows = len(cell_content_alias[0])
                ncols = len(cell_content_alias)
                for r in xrange(nrows):
                    for c in xrange(ncols):
                        if cell_content_alias[c][r] is not None:
                            clipboard_alias += cell_content_alias[c][r]
                        else:
                            clipboard_alias += ''
                        if c != (ncols-1):
                            clipboard_alias += '\t'
                    clipboard_alias += '\n'

            previous_formatting = self.ind[0]
            columns_formatting = []
            rows_formatting = []
            clipboard_formatting = ""

            if self.checkBox_3.isChecked():

                for index in self.ind:
                    if previous_formatting.column() != index.column():
                        columns_formatting.append(rows_formatting)
                        rows_formatting = []

                    idx_col = index.column()
                    idx_row = index.row()
                    idx_alphanum = column_list[idx_col] + str(idx_row+1)
                    formatting_dict = {'style':str(App.ActiveDocument.Spreadsheet.getStyle(idx_alphanum)),
                                        'foreground':str(App.ActiveDocument.Spreadsheet.getForeground(idx_alphanum)),
                                        'background':str(App.ActiveDocument.Spreadsheet.getBackground(idx_alphanum)),
                                        'contents':str(App.ActiveDocument.Spreadsheet.getContents(idx_alphanum)),
                                        'displayunit':str(App.ActiveDocument.Spreadsheet.getDisplayUnit(idx_alphanum)),
                                        'alignment':str(App.ActiveDocument.Spreadsheet.getAlignment(idx_alphanum))}
                    rows_formatting.append(formatting_dict)

                    previous_formatting = index

                columns_formatting.append(rows_formatting)
                cell_content_formatting = columns_formatting
                self.cell_content_formatting = cell_content_formatting
                FreeCAD.Console.PrintMessage(cell_content_formatting)
                # add rows and columns to clipboard
                nrows = len(cell_content_formatting[0])
                ncols = len(cell_content_formatting)
                for r in xrange(nrows):
                    for c in xrange(ncols):

                        if cell_content_formatting[c][r] is not None:
                            clipboard_formatting += str( cell_content_formatting[c][r] )
                        else:
                            clipboard_formatting += ''
                        if c != (ncols-1):
                            clipboard_formatting += '\t'

                    clipboard_formatting += '\n'

            # copy to the system clipboard
            sys_clip = QtGui.QApplication.clipboard()
            sys_clip.setText(clipboard + clipboard_alias + clipboard_formatting)

    def close_dialog(self):
        self.close()

MainWindow = QtGui.QMainWindow()
form = Spreadsheet_Tools(MainWindow)
form.show()