Как добавить кнопки в ячейку таблицы QTableWidget

UPD: Ответ найден

Использую PyQt6 и следующий код:

pushButton = QtWidgets.QPushButton('Кнопка')
self.tableMain.setCellWidget(0, 8, pushButton)

Ошибок никаких не вылетает, но при выполнении просто зависает окно и кнопка (точнее просто белый прямоугольник улетает вверх монитора) и больше ничего не работает.

Кусочек скриншота прикрепляю.

Обведен прямоугольник, который улетает вверх

Дополнительно, есть ли в PyQt6 возможность использовать несколько кнопок в одной ячейке QTableWidget?


Минимальный код следующий:

main.py

import sys
import time
from PyQt6 import QtWidgets
from table import Ui_MainWindow
from threading import Thread


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)
        th = Thread(target=self.insertInTable, args=())
        th.start()

    def insertInTable(self):
        for i in range(0, 3):
            rowPosition = self.tableWidget.rowCount()
            self.tableWidget.insertRow(rowPosition)
            cell1 = QtWidgets.QTableWidgetItem('1 ячейка')
            cell2 = QtWidgets.QTableWidgetItem('2 ячейка')
            self.tableWidget.setItem(rowPosition, 0, cell1)
            self.tableWidget.setItem(rowPosition, 1, cell2)
            pushButton = QtWidgets.QPushButton('Кнопка')
            self.tableWidget.setCellWidget(rowPosition, 2, pushButton)
            time.sleep(2)

app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

table.py

from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(526, 255)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(8, 8, 509, 187))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.tableWidget.horizontalHeader().setVisible(True)
        self.tableWidget.horizontalHeader().setDefaultSectionSize(140)
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        self.tableWidget.verticalHeader().setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 526, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "Новый столбец"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "1 столбец"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "3 столбец"))

Во время создания минимального кода - найдена проблема. Если использовать Thread, то результат в режиме Debag такой же, как и у меня в основном коде.

Переделал с использованием QThread и получил следующий код:

main.py

import sys
import time
from PyQt6 import QtWidgets, QtCore
from table import Ui_MainWindow
from PyQt6.QtCore import QThread

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)
        self.th = insertInTable()
        self.th.mysignal.connect(self.insertCell)
        self.th.start()

    def insertCell(self):
        rowPosition = window.tableWidget.rowCount()
        window.tableWidget.insertRow(rowPosition)
        cell1 = QtWidgets.QTableWidgetItem('1 ячейка')
        cell2 = QtWidgets.QTableWidgetItem('2 ячейка')
        window.tableWidget.setItem(rowPosition, 0, cell1)
        window.tableWidget.setItem(rowPosition, 1, cell2)
        pushButton = QtWidgets.QPushButton('Кнопка')
        window.tableWidget.setCellWidget(rowPosition, 2, pushButton)

class insertInTable(QThread):
    mysignal = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        for i in range(0, 3):
            self.mysignal.emit()
            time.sleep(2)

app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

С Python работаю всего дней 10 (как хобби), поэтому если есть явные ошибки в коде, можете смело на них указывать. Всем спасибо!


Ответы (1 шт):

Автор решения: S. Nick

Вам не нужен дополнительный поток, т.к. нельзя использовать виджеты в дополнительном потоке. Не используйте time.sleep(2) - это только замораживает интерфейс и ничего более.

import sys
#import time
'''
from PyQt6 import QtCore, QtGui, QtWidgets
'''
from PyQt5 import QtCore, QtGui, QtWidgets
#from threading import Thread

#from table import Ui_MainWindow
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(526, 255)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(8, 8, 509, 187))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.tableWidget.horizontalHeader().setVisible(True)
        self.tableWidget.horizontalHeader().setDefaultSectionSize(140)
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        self.tableWidget.verticalHeader().setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 526, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "Новый столбец"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "1 столбец"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "3 столбец"))
        

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)
        
        '''
        th = Thread(target=self.insertInTable, args=())
        th.start()
        '''
        self.insertInTable()

    def insertInTable(self):                                    # +++
        for i in range(0, 3):
            rowPosition = self.tableWidget.rowCount()
            self.tableWidget.insertRow(rowPosition)
            cell1 = QtWidgets.QTableWidgetItem(f'ячейка {i}:1 ячейка')
            cell2 = QtWidgets.QTableWidgetItem(f'ячейка {i}:2 ячейка')
            self.tableWidget.setItem(rowPosition, 0, cell1)
            self.tableWidget.setItem(rowPosition, 1, cell2)
            pushButton = QtWidgets.QPushButton(f'Кнопка {i+1}')
            pushButton.clicked.connect(
                lambda ch, btn=pushButton: print(btn.text()))
            self.tableWidget.setCellWidget(rowPosition, 2, pushButton)
#            time.sleep(2)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

введите сюда описание изображения


Update

В случае кода, который я предоставил sleep использовал лишь для того, чтобы эмитировать задержку на запросы и ожидание ответа, которые делает главная программа.
Из-за запросов и использую дополнительный поток, т.к. без потока долго ждать запуска программы, если например в таблице будет 30 строк и по каждой будет сделан запрос.

Не используйте time.sleep(2) - это только замораживает интерфейс и ничего более.

Класс QTimer предоставляет повторяющиеся и однократные таймеры.
Больше https://doc.qt.io/qt-5/qtimer.html

Если вам надо рабатать с потоками, вам надо лучше рассказать, что вы делаете в потоках и и какую информацию из дополнительного потока вы собираетесь передавать в основной поток.
Больше https://doc.qt.io/qt-5/signalsandslots.html

import sys
#import time
'''
from PyQt6 import QtCore, QtGui, QtWidgets
'''
from PyQt5 import QtCore, QtGui, QtWidgets
#from threading import Thread

#from table import Ui_MainWindow
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(526, 255)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(8, 8, 509, 187))
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.tableWidget.horizontalHeader().setVisible(True)
        self.tableWidget.horizontalHeader().setDefaultSectionSize(140)
        self.tableWidget.horizontalHeader().setStretchLastSection(True)
        self.tableWidget.verticalHeader().setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 526, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "Новый столбец"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "1 столбец"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "3 столбец"))
        

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setupUi(self)
        
        '''
        th = Thread(target=self.insertInTable, args=())
        th.start()
        '''
#        self.insertInTable()
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.insertInTable)
        self.timer.start(100)         # установите интервал какой вам нравится
        self.i = 0
    
    def insertInTable(self):    
#        for i in range(0, 3):
        rowPosition = self.tableWidget.rowCount()
        self.tableWidget.insertRow(rowPosition)
        cell1 = QtWidgets.QTableWidgetItem(f'ячейка {self.i}:1 ячейка')
        cell2 = QtWidgets.QTableWidgetItem(f'ячейка {self.i}:2 ячейка')
        self.tableWidget.setItem(rowPosition, 0, cell1)
        self.tableWidget.setItem(rowPosition, 1, cell2)
        pushButton = QtWidgets.QPushButton(f'Кнопка {self.i+1}')
        pushButton.clicked.connect(
            lambda ch, btn=pushButton: print(btn.text()))
        self.tableWidget.setCellWidget(rowPosition, 2, pushButton)
        
        self.i += 1
        if self.i == 30:
            self.timer.stop()
        
#            time.sleep(2)


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()

введите сюда описание изображения

→ Ссылка