Pyqt5 QTableWidget selectionChanged missing required argument 'slot' (pos 1) - pyqt5

I tried to connect to a method of QTableWidget object with QtCore.pyqtSignal() object, but this method expects me to return a value. Frankly, I couldn't find where I went wrong.
Table class
class TABLE(QtWidgets.QTableWidget):
current = QtCore.pyqtSignal()
def __init__(self, data, parent=None):
super(TABLE, self).__init__(parent)
self.show()
def selectionChanged(self, selected, deselected):
self.current.emit()
App class
from x import TABLE
class WpApp(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(WpApp, self).__init__(parent)
self.setupUi()
self.show()
def setupUi(self):
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
data = [["1","Hello, How are you","12-09-2022"]]
self.task = TABLE(data, self)
self.pushButton_3.clicked.connect(self.tablo)
QtCore.QMetaObject.connectSlotsByName(self)
def tablo(self):
self.task.current.connect()
I got this error when run it: TypeError: connect() missing required argument 'slot' (pos 1)
Why does the selectionChanged method expect me to return a value?

Related

QProgressBar stuck when QRunnable runs a loop function passed in

I have a calculating function that need to be sent to Qrunnable Worker as a parameter. Then, the calculating function runs with a 0-100 loop calculating in Worker's run function. The Problem is: the function is a separate loop function, I want to update a QProgressBar in the window. But, in the Worker, a progress signal can't send back to update progress bar value before the whole calculating function completed. This is really weird to me. I tried following code by updating the QProgressBar in the loop of the calculating function. But calculating is OK, the QProgressBar stuck....Why? How to handle this kind of problem?
import sys
import time
import traceback
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QMainWindow,QProgressBar,QPushButton, QVBoxLayout, QWidget
class WorkerSignals(QObject):
result_signal = pyqtSignal(str)
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super().__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
#pyqtSlot()
def run(self):
result = self.fn()
self.signals.result_signal.emit(result)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.btn = QPushButton('Start Test')
self.btn.clicked.connect(self.control_progressBar)
self.progressBar = QProgressBar()
self.progressBar.setValue(0)
self.vlayout = QVBoxLayout()
self.vlayout.addWidget(self.btn)
self.vlayout.addWidget((self.progressBar))
self.widget = QWidget()
self.widget.setLayout(self.vlayout)
self.setCentralWidget(self.widget)
self.thread = QThreadPool()
self.result = 0
self.show()
def control_progressBar(self):
worker = Worker(self.calculate)
worker.signals.result_signal.connect(self.output)
self.thread.start(worker)
def calculate(self):
for i in range(100):
self.result += i*i*i
self.progressBar.setValue(i)
return str(self.result)
def output(self, result):
print(result)
app = QApplication(sys.argv)
window = MainWindow()
app.exec_()

cannot be associated with QComboBox

button of class Main don't connect with class Qcombobox of Signals
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import *
import sys
from PyQt5 import QtGui
class Signals(QWidget):
asignal = pyqtSignal(str)
def __init__(self):
super(Signals, self).__init__()
self.setGeometry(300, 250, 400, 300)
self.ii()
self.show()
def ii(self):
vbox = QVBoxLayout()
self.combo = QComboBox()
self.combo.addItem("Python")
self.combo.addItem("Java")
self.combo.addItem("C++")
self.combo.addItem("C#")
self.combo.addItem("Ruby")
self.buttom = QPushButton("Click")
self.buttom.clicked.connect(self.windown2)
vbox.addWidget(self.combo)
vbox.addWidget(self.buttom)
self.setLayout(vbox)
def do_something(self):
self.asignal.emit(self.combo.currentText())
def windown2(self):
self.ggpp = Main()
self.ggpp.show()
class Main(QWidget):
def __init__(self):
super(Main, self).__init__()
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.coso1)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self):
s = Signals()
s.asignal.connect(lambda sig: print("self.combo.currentText()>>>>>" + sig))
s.do_something()
if __name__ == '__main__':
app = QApplication(sys.argv)
nals = Signals()
nals.show()
sys.exit(app.exec())
What you see happens because you're not using the existing instance of Signals, but you're creating a new one each time the button is clicked.
In your case, you could add a reference to the instance as an argument when you create the new window, so that you can correctly connect to its signal.
class Signals(QWidget):
# ...
def windown2(self):
self.ggpp = Main(self)
self.ggpp.show()
class Main(QWidget):
def __init__(self, signals):
super(Main, self).__init__()
self.signals = signals
self.signals.asignal.connect(self.coso1)
self.setGeometry(500,150, 600, 300)
vbox1 = QVBoxLayout()
self.buttom1 = QPushButton("Click")
self.buttom1.clicked.connect(self.signals.do_something)
vbox1.addWidget(self.buttom1)
self.setLayout(vbox1)
def coso1(self, sig):
print("self.combo.currentText()>>>>>" + sig)

How to execute code after threads qthreadpool?

I have a GUI that implements 3 buttons
One of the buttons has the "start" function tied, which contains 2 processes, in fact, these are two cycles that should work multithreaded, but I need to add more code to the "start" function that, for example, changes some widgets.
The problem is that if I add some code after self.threadpool.start(thread2) self.threadpool.start(thread3), then it is not executed upon completion of these threads, but before that, or not at all.
I wrote a toy example that fully describes my requirements from the program
import sys
import time
from PySide2.QtCore import QThreadPool, QRunnable, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.threadpool = QThreadPool()
self.btn.clicked.connect(lambda: self.start())
def start(self):
t1 = Worker(th1)
t2 = Worker(th2)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
self.btn.setEnabled(True)
def initUI(self):
self.btn = QPushButton('Start', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('HELP')
self.show()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.fn = fn
#Slot() # QtCore.Slot
def run(self):
self.fn()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I need the button to turn off before the streams start and turn on only after their completion.
The solution should be fair for any code I want to use after streams.
First, using the Slot decorator in the methods of a QRunnable is useless since it is not a QObject.
On the other hand, the idea of implementing a multithreading logic is that the main thread does not block, so you should not expect that after invoking start() it will be executed after it finishes executing the threads.
The solution is to create a QObject that emits a signal before and after the execution of the thread, and with that logic implement the GUI update:
import sys
import time
from PySide2.QtCore import QObject, QThreadPool, QRunnable, Signal, Slot
from PySide2.QtWidgets import QApplication, QWidget, QPushButton
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.active_workers = 0
self.threadpool = QThreadPool()
self.btn.clicked.connect(self.start)
#Slot()
def start(self):
t1 = Worker(th1)
t1.signaller.started.connect(self.on_started)
t1.signaller.finished.connect(self.on_finished)
t2 = Worker(th2)
t2.signaller.started.connect(self.on_started)
t2.signaller.finished.connect(self.on_finished)
self.btn.setEnabled(False)
self.threadpool.start(t1)
self.threadpool.start(t2)
def initUI(self):
self.btn = QPushButton("Start", self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle("HELP")
self.show()
#Slot()
def on_started(self):
self.active_workers += 1
#Slot()
def on_finished(self):
self.active_workers -= 1
self.btn.setEnabled(self.active_workers == 0)
class Signaller(QObject):
started = Signal()
finished = Signal()
class Worker(QRunnable):
def __init__(self, fn):
super(Worker, self).__init__()
self.signaller = Signaller()
self.fn = fn
def run(self):
self.signaller.started.emit()
self.fn()
self.signaller.finished.emit()
def th1():
time.sleep(5)
print("th1")
def th2():
time.sleep(10)
print("th2")
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Calling method from other class

I'm very new to PyQt5, and I'm trying to make an interactive gui for plotting of data. However, this problem may well be completely unrelated to PyQt5 and more a problem with my understanding of object oriented programming in general.
I have a MainWindow class, a SupportClass1 and a SupportClass2. When I make an instance of SupportClass1, I want to call the method DoSomething in the MainWindow class by referring to the object window, but I get the error message NameError: name 'window' is not defined.
I have no problems creating a method in the SupportClass2 and calling that from the MainWindow class so I get the impression that I have not instantiated the MainWindow class correctly which I don't understand as I thought I had defined window as an instace of the MainWindow class.
Can anyone help me understand what is wrong in my logic and how to solve this problem?
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys
import os
from random import randint
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.graphWidget = pg.PlotWidget()
self.x = list(range(100))
self.y = [randint(0,100) for _ in range(100)]
self.graphWidget.setBackground('w')
pen = pg.mkPen(color=(255, 0, 0))
self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)
self.button = QPushButton('Test')
self.button.clicked.connect(self.InstantiateSupportClasses)
self.gui_box = QVBoxLayout()
self.gui_box.addWidget(self.graphWidget)
self.gui_box.addWidget(self.button)
self.setLayout(self.gui_box)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test application')
self.show()
def InstantiateSupportClasses(self):
supp_class2 = SupportClass2()
print(supp_class2.GetVariable())
supp_class1 = SupportClass1()
def DoSomething(self):
print('I did something!')
class SupportClass1():
def __init__(self):
window.DoSomething
class SupportClass2():
def __init__(self):
self.some_variable = 5
def GetVariable(self):
return self.some_variable
def main():
app = QApplication(sys.argv)
app.setStyle('Fusion')
window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()```
I see that you are using the "window" object from the "SupportClass1" class.
but that class does not recognize this object one solution is to insert that object to the "SupportClass1()"
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys
import os
from random import randint
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.graphWidget = pg.PlotWidget()
self.x = list(range(100))
self.y = [randint(0,100) for _ in range(100)]
self.graphWidget.setBackground('w')
pen = pg.mkPen(color=(255, 0, 0))
self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)
self.button = QPushButton('Test')
self.button.clicked.connect(self.InstantiateSupportClasses)
self.gui_box = QVBoxLayout()
self.gui_box.addWidget(self.graphWidget)
self.gui_box.addWidget(self.button)
self.setLayout(self.gui_box)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test application')
self.show()
def InstantiateSupportClasses(self):
supp_class2 = SupportClass2()
print(supp_class2.GetVariable())
supp_class1 = SupportClass1(self)
def DoSomething(self):
print('I did something!')
class SupportClass1():
def __init__(self, window):
window.DoSomething()
class SupportClass2():
def __init__(self):
self.some_variable = 5
def GetVariable(self):
return self.some_variable
def main():
app = QApplication(sys.argv)
app.setStyle('Fusion')
window = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQt5: drawing multiple rectangles using mouseEvents by implementing QGraphicsScene, QGraphicsView and QGraphicsItem [duplicate]

I have a scene like this
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
def mousePressEvent(self, event):
print('scene pressed')
self.wid = MyRect(event.pos(), event.pos())
self.addItem(self.wid)
self.wid.show()
I would like class MyRect(QtWidgets.QGraphicsRectItem) with painter, mouse event and so on to be a draggable rectangle.
all stuff in MyRect
So then I could have many Rectangle to the scene and even after draw line between them and so on (kind of diagram app), but keeping objects related editable options in MyRect, MyLine , ....
I thought :
class MyRect(QtWidgets.QGraphicsRectItem):
def __init__(self, begin, end, parent=None):
super().__init__(parent)
self.begin = begin
self.end = end
def paintEvent(self, event):
print('painting')
qp = QtGui.QPainter(self)
qp.drawRect(QtCore.QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
But I does not work (paint event not initiated whereas mousepressed event in scene is intiated)
I did not find what I wanted through the web so started totry do it by myself. I'm pretty sure it is a must known starting point but I cannot find it
First of all a QGraphicsItem is not a QWidget, so it has those events and does not handle them directly, that's what QGraphicsView and QGraphicsScene do. For example you say that you want to have a moveable rectangle because that task is simple is QGraphicsView, it is not necessary to overwrite:
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
If you want to change the way you paint the rectangle you must overwrite the paint() method as shown below:
from PyQt5 import QtCore, QtGui, QtWidgets
class RectItem(QtWidgets.QGraphicsRectItem):
def paint(self, painter, option, widget=None):
super(RectItem, self).paint(painter, option, widget)
painter.save()
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(QtCore.Qt.red)
painter.drawEllipse(option.rect)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
rect_item = RectItem(QtCore.QRectF(0, 0, 100, 100))
rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
scene.addItem(rect_item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Update:
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(GraphicsScene, self).__init__(QtCore.QRectF(-500, -500, 1000, 1000), parent)
self._start = QtCore.QPointF()
self._current_rect_item = None
def mousePressEvent(self, event):
if self.itemAt(event.scenePos(), QtGui.QTransform()) is None:
self._current_rect_item = QtWidgets.QGraphicsRectItem()
self._current_rect_item.setBrush(QtCore.Qt.red)
self._current_rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.addItem(self._current_rect_item)
self._start = event.scenePos()
r = QtCore.QRectF(self._start, self._start)
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._current_rect_item is not None:
r = QtCore.QRectF(self._start, event.scenePos()).normalized()
self._current_rect_item.setRect(r)
super(GraphicsScene, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self._current_rect_item = None
super(GraphicsScene, self).mouseReleaseEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene =GraphicsScene(self)
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())