Search code examples
pythonpyqt

copy pyqt table selection, including column and row headers


I'd like to copy the selected part of a qt table, including the headers. The default qt table copy only copies one cell, whereas I need the entire selection, which can be multiple columns and multiple rows. I've spliced some code together from various places (mostly here), and it seems to work well except that it only copies the cells (data), not the headers. I need some help getting the headers for the selection as well. Here's my slimmed down sample:

from PyQt4 import QtGui, QtCore
import sys

class MainWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)

        self.table = QtGui.QTableWidget(parent=self)
        self.table.setColumnCount(2)
        self.table.setRowCount(2)
        self.table.setHorizontalHeaderLabels(['col1','col2'])
        self.table.setVerticalHeaderLabels(['row1','row2'])
        self.table.setItem(0,0,QtGui.QTableWidgetItem('foo'))
        self.table.setItem(0,1,QtGui.QTableWidgetItem('bar'))
        self.table.setItem(1,0,QtGui.QTableWidgetItem('baz'))
        self.table.setItem(1,1,QtGui.QTableWidgetItem('qux'))

        layout = QtGui.QGridLayout()
        layout.addWidget(self.table, 1, 0)
        self.setLayout(layout)

        self.clip = QtGui.QApplication.clipboard()


    def keyPressEvent(self, e):
        if (e.modifiers() & QtCore.Qt.ControlModifier):
            selected = self.table.selectedRanges()

            if e.key() == QtCore.Qt.Key_C: #copy
                s = ""

                for r in xrange(selected[0].topRow(), selected[0].bottomRow()+1):
                    for c in xrange(selected[0].leftColumn(), selected[0].rightColumn()+1):
                        try:
                            s += str(self.table.item(r,c).text()) + "\t"
                        except AttributeError:
                            s += "\t"
                    s = s[:-1] + "\n" #eliminate last '\t'
                self.clip.setText(s)


if __name__ == '__main__':

    # Initialize the application
    app = QtGui.QApplication(sys.argv)
    mw = MainWidget()
    mw.show()
    app.exec_()

The sample table looks like this:

     col1 col2
row1 foo  bar
row2 baz  qux

For example if I selected bar and bux, I'd like the copy to be:

\t col2\n
row1 \t bar\n
row2 \t qux\n

My actual case has a lot of columns and rows. My difficulty is in figuring out the headers for the selected cells; I can deal with the formatting. Any help or suggestions much appreciated. Thanks in advance!


Solution

  • The clue is in the header-items:

    class MainWidget(QtGui.QWidget):
        def __init__(self, parent=None):
            super(MainWidget, self).__init__(parent)
    
            self.table = QtGui.QTableWidget(parent=self)
            self.table.setColumnCount(2)
            self.table.setRowCount(2)
            self.table.setHorizontalHeaderLabels(['col1','col2'])
            self.table.setVerticalHeaderLabels(['row1','row2'])
            self.table.setItem(0,0,QtGui.QTableWidgetItem('foo'))
            self.table.setItem(0,1,QtGui.QTableWidgetItem('bar'))
            self.table.setItem(1,0,QtGui.QTableWidgetItem('baz'))
            self.table.setItem(1,1,QtGui.QTableWidgetItem('qux'))
    
            layout = QtGui.QGridLayout()
            layout.addWidget(self.table, 1, 0)
            self.setLayout(layout)
    
            self.clip = QtGui.QApplication.clipboard()
    
    
        def keyPressEvent(self, e):
            if (e.modifiers() & QtCore.Qt.ControlModifier):
                selected = self.table.selectedRanges()
    
                if e.key() == QtCore.Qt.Key_C: #copy
                    s = '\t'+"\t".join([str(self.table.horizontalHeaderItem(i).text()) for i in xrange(selected[0].leftColumn(), selected[0].rightColumn()+1)])
                    s = s + '\n'
    
                    for r in xrange(selected[0].topRow(), selected[0].bottomRow()+1):
                        s += self.table.verticalHeaderItem(r).text() + '\t'
                        for c in xrange(selected[0].leftColumn(), selected[0].rightColumn()+1):
                            try:
                                s += str(self.table.item(r,c).text()) + "\t"
                            except AttributeError:
                                s += "\t"
                        s = s[:-1] + "\n" #eliminate last '\t'
                    self.clip.setText(s)