3

I have a Qtablewidget shown below. I want to copy multiple cells from the table and paste in other row in the same table.

So far, I can do the same on single cell, but is there a way i can do multiple cells at the same time??

Also, if possible, to copy multiple rows and paste it in same table below?

I tried looking at some answers in SO but quite didn't get it to implement in PyQt5.

Thanks in advance.

Snippet for Single Row Copy/Paste

Snippet for multiple Row Copy/Paste

Sample Code (By QtDesigner):

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1048, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(40, 40, 861, 511))
        self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.DoubleClicked|QtWidgets.QAbstractItemView.EditKeyPressed)
        self.tableWidget.setRowCount(5)
        self.tableWidget.setColumnCount(5)
        self.tableWidget.setObjectName("tableWidget")
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(4, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(0, 0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(0, 1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(0, 2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(0, 3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(0, 4, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(1, 0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(1, 1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(1, 2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(1, 3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(1, 4, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(2, 0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(2, 1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(2, 2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(2, 3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setItem(2, 4, item)
        self.tableWidget.horizontalHeader().setVisible(True)
        self.tableWidget.verticalHeader().setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Name"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "ID"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "City"))
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText(_translate("MainWindow", "Number"))
        item = self.tableWidget.horizontalHeaderItem(4)
        item.setText(_translate("MainWindow", "Occupation"))
        __sortingEnabled = self.tableWidget.isSortingEnabled()
        self.tableWidget.setSortingEnabled(False)
        item = self.tableWidget.item(0, 0)
        item.setText(_translate("MainWindow", "Mark"))
        item = self.tableWidget.item(0, 1)
        item.setText(_translate("MainWindow", "1"))
        item = self.tableWidget.item(0, 2)
        item.setText(_translate("MainWindow", "Newyork"))
        item = self.tableWidget.item(0, 3)
        item.setText(_translate("MainWindow", "4796423643344"))
        item = self.tableWidget.item(0, 4)
        item.setText(_translate("MainWindow", "Teacher"))
        item = self.tableWidget.item(1, 0)
        item.setText(_translate("MainWindow", "Taylor"))
        item = self.tableWidget.item(1, 1)
        item.setText(_translate("MainWindow", "2"))
        item = self.tableWidget.item(1, 2)
        item.setText(_translate("MainWindow", "Chicago"))
        item = self.tableWidget.item(1, 3)
        item.setText(_translate("MainWindow", "43683284"))
        item = self.tableWidget.item(1, 4)
        item.setText(_translate("MainWindow", "Nurse"))
        self.tableWidget.setSortingEnabled(__sortingEnabled)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Derived Class:

from PyQt5 import QtWidgets

from demo import Ui_MainWindow

class demo_code(QtWidgets.QMainWindow, Ui_MainWindow):                    
    def __init__(self):
        super(demo_code, self).__init__()
        self.setupUi(self) 



if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = demo_code()    
    window.show()
    sys.exit(app.exec_())
Vi_py123
  • 73
  • 1
  • 9
  • 1) You say *I can do the same on single cell* and it would be good if you show it, 2) Will the rows that will be added always be the last rows? – eyllanesc Mar 17 '20 at 02:47
  • I think Single Cell Copy and Paste is by default. I will have a empty rows in table. So basically not inserting any rows, Just need to paste copied data in empty cells. – Vi_py123 Mar 17 '20 at 02:53
  • You say: *I think Single Cell Copy and Paste is by default*, have you checked that it works? You should be more precise and instead of pointing: *I can do the same on single cell* if you haven't already. – eyllanesc Mar 17 '20 at 02:57
  • Yes, I just checked Copying single cell and paste it in other cell, It works! – Vi_py123 Mar 17 '20 at 03:01
  • mmm, how strange, this functionality does not exist by default, do you select the row, press Ctrl + C, and then Ctrl + V to paste it? I have done that and it does not work. – eyllanesc Mar 17 '20 at 03:04
  • i meant only one cell not whole row or multiple cells. this can be done while entering values in table. – Vi_py123 Mar 17 '20 at 03:08
  • @Vi_py123 that's not copying the cell, it's copying the text while the cell is being edited. – musicamante Mar 17 '20 at 03:43

3 Answers3

5

Here is a solution subclassing QTableWidget. You have to reimplement the keyPressEvent, to catch the copy and paste key sequences. On copy, save the current selected items with QTableWidget.selectedIndexes(). On paste, set a new QTableWidgetItem for each cell in the list, translating the indexes to the new highlighted index. It will paste all the cells that were highlighted, they don't need to be in the same row. It assumes the cell with the smallest index will be placed in the highlighted cell on paste.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class Table(QTableWidget):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setRowCount(10)
        self.setColumnCount(10)
        # etc.

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if event.key() == Qt.Key_C and (event.modifiers() & Qt.ControlModifier):
            self.copied_cells = sorted(self.selectedIndexes())
        elif event.key() == Qt.Key_V and (event.modifiers() & Qt.ControlModifier):
            r = self.currentRow() - self.copied_cells[0].row()
            c = self.currentColumn() - self.copied_cells[0].column()
            for cell in self.copied_cells:
                self.setItem(cell.row() + r, cell.column() + c, QTableWidgetItem(cell.data()))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = Table()
    gui.show()
    sys.exit(app.exec_())
alec
  • 5,799
  • 1
  • 7
  • 20
  • The copy function also works when sub-classing QTableView, didn't check the paste – SiP May 23 '22 at 06:48
3

Here is a solution written for PyQt6 and supporting copying onto the clipboard, so that you can paste multiple-cell selections from a QTableWidget into a popular spreadsheet program like Google Sheets, Apple Numbers, or Microsoft excel. These programs expect tab ('\t') delimiting for a new column and newline ('\n') delimiting for a new row

from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *

import sys

class TableWithCopy(QTableWidget):
    """
    this class extends QTableWidget
    * supports copying multiple cell's text onto the clipboard
    * formatted specifically to work with multiple-cell paste into programs
      like google sheets, excel, or numbers
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if event.key() == Qt.Key.Key_C and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
            copied_cells = sorted(self.selectedIndexes())

            copy_text = ''
            max_column = copied_cells[-1].column()
            for c in copied_cells:
                copy_text += self.item(c.row(), c.column()).text()
                if c.column() == max_column:
                    copy_text += '\n'
                else:
                    copy_text += '\t'
                    
            QApplication.clipboard().setText(copy_text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = TableWithCopy()
    gui.setColumnCount(10)
    gui.setRowCount(10)
    gui.show()
    sys.exit(app.exec())
boochbrain
  • 69
  • 5
0

To extend boochbrain answere slightly with the paste function (in my use-case I needed the data to cover all rows and columns, to paste it):

from PyQt6.QtCore import *
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *

import sys

class TableWithCopy(QTableWidget):
    """
    this class extends QTableWidget
    * supports copying multiple cell's text onto the clipboard
    * formatted specifically to work with multiple-cell paste into programs
      like google sheets, excel, or numbers
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if event.key() == Qt.Key.Key_C and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
            copied_cells = sorted(self.selectedIndexes())

            copy_text = ''
            max_column = copied_cells[-1].column()
            for c in copied_cells:
                copy_text += self.item(c.row(), c.column()).text()
                if c.column() == max_column:
                    copy_text += '\n'
                else:
                    copy_text += '\t'
                    
            QApplication.clipboard().setText(copy_text)

        if event.key() == Qt.Key_V and (event.modifiers() & Qt.KeyboardModifier.ControlModifier):
            rows = QApplication.clipboard().text().split('\n')[:-1]
            if len(rows) == 0:
                return
            if len(rows) != self.rowCount() or len(rows[0].split('\t')) != self.columnCount():
                QMessageBox.information(None, 'Error', 'The pasted data does not contain the correct data')
                return
            self.clear()
            self.setRowCount(len(rows))
            self.setColumnCount(len(rows[0].split('\t')))
            for i, row in enumerate(rows):
                row = row.split('\t')
                item1 = QTableWidgetItem(row[0])
                item2 = QTableWidgetItem(row[1])
                self.setItem(i, 0, item1)
                self.setItem(i, 1, item2)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = TableWithCopy()
    gui.setColumnCount(10)
    gui.setRowCount(10)
    gui.show()
    sys.exit(app.exec())
axel_ande
  • 359
  • 1
  • 4
  • 20