Search code examples
python-3.xprintingpyqtqtablewidget

Printing the Table from QTableWidget


How can i print a QTableWidget from my destopapplication and have the cells adjusted to the length of the text ? The Table is edible by the user and contains text, words, pictures and a dropdownmenu ?

I tried to do it with the code below, but it exits with a 'Process finished with exit code -1073740791 (0xC0000409)'

I am using python 3.6 and pyqt5.

from PyQt5 import QtWidgets, QtCore, QtPrintSupport, QtGui

from PyQt5.QtWidgets import *

list_a =['word_a 1', 'word_a 2']
list_b =['word_b 1 ', 'word_b 2']
list_c =['word_c 1', 'word_c 2']
combo_box_options = ["Option 1","Option 2","Option 3"]
list_d = ['good','bad']
data = {'Wort A':list_a, 'Wort B':list_b, 'Wort C': list_c, 'Dropdown': [], 'Word D': list_d}


class Window(QTabWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        super().__init__()
        self.setWindowTitle(self.tr('Document Printer'))
        self.table = QtWidgets.QTableWidget()
        self.table.setRowCount(5)
        self.table.setColumnCount(5)

        horHeaders = []
        for col, key in enumerate(sorted(data.keys())):
            horHeaders.append(key)
            for row, item in enumerate(data[key]):
                newitem = QTableWidgetItem(item)
                newitem.setTextAlignment(QtCore.Qt.AlignCenter)
                self.table.setItem(row, col, newitem)


        combo_attr = ['bad word', 'good word', 'very nice word', 'delet']
        i = 0
        for j in horHeaders:
            comboBox = QtWidgets.QComboBox()
            for t in combo_attr:
                comboBox.addItem(t)
            self.table.setCellWidget(i,3,comboBox)
            i += 1


        self.table.setHorizontalHeaderLabels(
            'Word A|Word B|Word C|Dropdown|Word D'.split('|'))
        self.buttonPrint = QtWidgets.QPushButton('Print', self)
        self.buttonPrint.clicked.connect(self.handlePrint)
        self.buttonPreview = QtWidgets.QPushButton('Preview', self)
        self.buttonPreview.clicked.connect(self.handlePreview)
        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(self.table, 0, 0, 1, 2)
        layout.addWidget(self.buttonPrint, 1, 0)
        layout.addWidget(self.buttonPreview, 1, 1)

    def handlePrint(self):
        dialog = QtPrintSupport.QPrintDialog()
        if dialog.exec_() == QtWidgets.QDialog.Accepted:
            self.handlePaintRequest(dialog.printer())

    def handlePreview(self):
        dialog = QtPrintSupport.QPrintPreviewDialog()
        dialog.paintRequested.connect(self.handlePaintRequest)
        dialog.exec_()

    def handlePaintRequest(self, printer):
        document = QtGui.QTextDocument()
        cursor = QtGui.QTextCursor(document)
        table = cursor.insertTable(
            self.table.rowCount(), self.table.columnCount())

        for row in range(table.rows()):
            print(row)
            for col in range(table.columns()):
                print(col)
                cursor.insertText(self.table.newitem(row, col).text())
                cursor.movePosition(QtGui.QTextCursor.NextCell)
        document.print_(printer)




if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.resize(640, 480)
    window.show()
    sys.exit(app.exec_())

Solution

  • First of all I recommend that if the program crashes without giving you much information then you must run it in the terminal or cmd. If you do this you will get the following message:

    AttributeError: 'QTableWidget' object has no attribute 'newitem' 
    

    And the problem is obvious: QTableWidget does not have this method, a partial solution is to change newitem by item but that would also bring problems since some items can not exist so you must first verify:

    def handlePaintRequest(self, printer):
        document = QtGui.QTextDocument()
        cursor = QtGui.QTextCursor(document)
        table = cursor.insertTable(self.table.rowCount(), self.table.columnCount())
    
        for row in range(table.rows()):
            for col in range(table.columns()):
                it = self.table.item(row, col)
                if it is not None:
                    cursor.insertText(it.text())
                cursor.movePosition(QtGui.QTextCursor.NextCell)
        document.print_(printer)
    

    There is no general response to print a QTableWidget because as you point you have a widget, so you must modify the code according to the widgets you use, for example in this case I will create a function that returns a text according to the embedded widget:

        def handlePaintRequest(self, printer):
            document = QtGui.QTextDocument()
            cursor = QtGui.QTextCursor(document)
            table = cursor.insertTable(self.table.rowCount(), self.table.columnCount())
    
            for row in range(table.rows()):
                for col in range(table.columns()):
                    w = self.table.cellWidget(row, col)
                    it = self.table.item(row, col)
                    if w is not None:
                        cursor.insertText(get_text_from_widget(w))
                    elif it is not None:
                        cursor.insertText(it.text())
                    cursor.movePosition(QtGui.QTextCursor.NextCell)
            document.print_(printer)
    
    
    def get_text_from_widget(w):
        t = ""
        if isinstance(w, QtWidgets.QComboBox):
            t = w.currentText()
    
        # if isinstance(w, another_widget):
        # t = w.some_method()
    
        return t