I tried following the solution laid out in this question, but nothing seems to happen once the signal is supposed to be emitted.
This isn't all of the code, but it should be enough to demonstrate what I'm trying to do:
class StartQT5(QMainWindow):
def __init__(self):
super().__init__()
[...]
self.getFileProperties(path, batch)
def getFileProperties(self, path, batch):
self.thread = QThread()
self.w = probeThread(self)
self.w.moveToThread(self.thread)
self.w.finished[dict].connect(self.setProbeOutput)
self.thread.started.connect(self.w.run)
self.thread.start()
#pyqtSlot(dict)
def setProbeOutput(self, data):
print("gotOutput")
self.probeOutput = data
print(data)
class probeThread(QObject):
finished = pyqtSignal(dict)
def __init__(self, parent):
super().__init__()
self.parent = parent
self.output = {}
def run(self):
self.output = json.loads(subprocess.check_output('%s -v 0 -print_format json -show_format -show_streams -count_frames "%s"' % (self.parent.ffprobePath, self.parent.file)).decode("utf-8"))
print("finished")
self.finished.emit(self.output)
The thread runs fine, but setProbeOutput never gets called. Sorry if this is something trivial, but I just can't seem to get it to work, thanks! :)
What about simply doing:
self.w.finished.connect(self.setProbeOutput)
You are defining a new signal and attaching it to a slot, you already declare the parameters types both in the signal and the slot declaration.
Related
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, title=title, size=(620, 665))
splitter = wx.SplitterWindow(self, wx.ID_ANY)
self.MainPanel = NewPanel(splitter)
self.MyNotebook = Nbook(splitter)
splitter.SplitHorizontally(self.MainPanel, self.MyNotebook, sashPosition=210)
self.sb = self.CreateStatusBar(3)
self.MainPanel.Show()
self.Centre()
def setNotebookDisabled(self):
self.MyNotebook.Enabled = False
def PrintSomething(self):
print("Something")
class NewPanel(wx.Panel):
def __init__(self, parent):
super(NewPanel, self).__init__(parent=parent)
def Disable_Controls(self):
self.GrandParent.MyNotebook.Enabled = False
class Nbook(wx.Notebook):
def __init__(self, parent):
super(Nbook, self).__init__(parent)
# self.MyNotebook = wx.Notebook(splitter,wx.ID_ANY)
page_one = NbPanel1(self)
page_two = NbPanel2(self)
page_three = NbPanel3(self)
self.AddPage(page_one, "Cable")
self.AddPage(page_two, "Busduct")
self.AddPage(page_three, "Transformer")
I can call PrintSomething() with success however...
The error:
File "c:/Users/Mark/Documents/FaultWireCalc/FCCalc.py", line 331, in Disable_Controls
self.GrandParent.MyNotebook.Enabled = False
AttributeError: 'MyFrame' object has no attribute 'MyNotebook'
This is not so much an answer as a work in progress, given that a comment simply wouldn't hack it.
Given that you haven't stated when or how the error occurs and it's occurring somewhere in 1000 lines of code, which we can't see, right about now might be a good time for
a minimal, complete and verifiable example (mcve)
https://stackoverflow.com/help/minimal-reproducible-example
As you seem reluctant to provide one, let's try this:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super(MyFrame, self).__init__(parent, title=title, size=(620, 665))
splitter = wx.SplitterWindow(self, wx.ID_ANY)
self.MainPanel = NewPanel(splitter)
self.MyNotebook = Nbook(splitter)
splitter.SplitHorizontally(self.MainPanel, self.MyNotebook, sashPosition=210)
self.sb = self.CreateStatusBar(3)
self.MainPanel.Show()
self.Centre()
self.Show()
def setNotebookDisabled(self):
self.MyNotebook.Enabled = False
def PrintSomething(self):
print("Something")
class NewPanel(wx.Panel):
def __init__(self, parent):
super(NewPanel, self).__init__(parent=parent)
testbut = wx.Button(self, -1, "Panel Hit Me")
self.Bind(wx.EVT_BUTTON, self.Disable_Controls)
def Disable_Controls(self, event):
self.GrandParent.MyNotebook.Enabled = not self.GrandParent.MyNotebook.IsEnabled()
class Nbook(wx.Notebook):
def __init__(self, parent):
super(Nbook, self).__init__(parent)
# self.MyNotebook = wx.Notebook(splitter,wx.ID_ANY)
page_one = wx.Panel(self)
page_two = wx.Panel(self)
page_three = wx.Panel(self)
self.AddPage(page_one, "Cable")
self.AddPage(page_two, "Busduct")
self.AddPage(page_three, "Transformer")
if __name__ == "__main__":
app = wx.App()
frame = MyFrame(None, "Test")
app.MainLoop()
As you can see, it does function (below) the notebook can be enabled/disabled.
So, can you make this produce the error or deduce from it, how your code differs in the way or when the code is called, that produces the error?
You may find that GrandParent is not what you think it is! (I suspect splitter is involved)
You might try:
print("I am",self)
print("My parent is",self.Parent)
print("Granddad is",self.GrandParent)
print("His parent is",self.GrandParent.GetParent())
The other thing you might try when calling the routine, is to call it directly:
MyFrame.setNotebookDisabled(parent.GetParent())
or a variation to get the right parent, depending on where you are calling it from.
Over to you.
I have an app that starts the GUI and performs "the heavy part" of the code in another thread using QThread. In this thread I emit a SIGNAL that is connected to the GUI Class and that performs addItem on QListWidget.
There are a massive "signaling" from this thread to the GUI and it "freeze".
Is there a way to avoid this? Have I to use another mini GUI in different thread only for QListWidget?
Thanks
EDIT:
This is the thread that execute the heavy logic
class YourThreadName(QThread):
def __init__(self, some variables):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# Here there is a for cycle that emits a SIGNAL
for ... :
...
self.emit(SIGNAL("needed_variable"), needed_variable)
...
In the GUI Class there are some methods, particularly:
class GUI(QtGui.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(GUI, self).__init__(parent)
self.setupUi(self)
def ... (self):
...
def start_main_code(self):
self.new_thread = YourThreadName(some variables)
self.connect(self.new_thread, SIGNAL("finished()"), self.done)
self.connect(self.new_thread, SIGNAL("needed_variable"), self.show_variable)
self.new_thread.start()
def show_variable(self, data):
self.QListWidget_object.addItem(data)
def ... (self):
...
The script below is a Minimal, Complete, and Verifiable Example based on the information currently given in your question and comments. It emits data from a worker thread every 10ms and updates a list-widget in the GUI. On my Linux system (using Python-3.6.3, Qt-4.8.7 and PyQt-4.12.1) it does not block or freeze the GUI. There is obviously some flickering whilst the list-widget is being updated, but I am able to select items, scroll up and down, click the button, etc. And if I increase the sleep to 25ms, I don't even get any flickering.
UPDATE:
The performance can be improved by using setUniformItemSizes and sending the messages in batches. On my system, after a slight initial delay, the list populates with fifty thousand items almost instantly.
import sys
from PyQt4 import QtCore, QtGui
class Worker(QtCore.QThread):
message = QtCore.pyqtSignal(object)
def run(self):
batch = []
for index in range(50000):
if len(batch) < 200:
batch.append(index)
continue
self.message.emit(batch)
batch = []
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.listWidget = QtGui.QListWidget()
self.listWidget.setUniformItemSizes(True)
self.button = QtGui.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.listWidget)
layout.addWidget(self.button)
self.worker = Worker()
self.worker.message.connect(self.handleMessages)
def handleMessages(self, batch):
for message in batch:
self.listWidget.addItem('Item (%s)' % message)
def handleButton(self):
if not self.worker.isRunning():
self.worker.start()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 50, 200, 400)
window.show()
sys.exit(app.exec_())
(Non-English native)
This is tricky to explain. I have 2 windows, each one with their own class created by PyQt5, on 2 different .py files. I want to open the 2nd window from a button inside the first one, and then I want that 2nd window to destroy itself when closed. However, in order to do this, I believe I have to set a specific variable in the 1st window to None, but since that is instanced I can't find out how:
First window in myfile.py
class MainForm(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.window_nuevocliente = None
self.actionNuevo_Cliente.triggered.connect(self.open_secondwindow)
def open_secondwindow(self):
if self.window_nuevocliente is None:
self.window_nuevocliente = addnewclient_logic.AddNewClientForm(self)
self.window_nuevocliente.setAttribute(Qt.WA_DeleteOnClose, True)
self.window_nuevocliente.show()
myapp = MainForm()
Second window in addnewclient_logic.py
import myfile
class AddNewClientForm(QtWidgets.QMainWindow, Ui_AddNewClient):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
def closeEvent(self, event):
# Do closing stuff
event.accept()
# Set the first class var window_nuevocliente back to None
myfile.myapp.window_nuevocliente = None
And here is where I'm stuck:
That last line doesn't work. When I close the window, the DeleteOnClose will totally destroy the window, but the first window will still have it assigned on the window_nuevocliente var and so it fails to re-create it from scratch. If I instead omit the check if it's None, the window can be opened multiple times at the same time (and I don't want that).
Managed to fix it by adding the destroyed signal.
def open_secondwindow(self):
if self.window_nuevocliente is None:
self.window_nuevocliente = addnewclient_logic.AddNewClientForm(self)
self.window_nuevocliente.setAttribute(Qt.WA_DeleteOnClose, True)
self.window_nuevocliente.destroyed.connect(self.reset_nuevocliente)
self.window_nuevocliente.show()
def reset_nuevocliente(self):
self.window_nuevocliente = None
I will accept a better solution, though :P
You can eliminate the window_nuevocliente attribute like this:
addnewclient_logic.py:
class AddNewClientForm(QtWidgets.QMainWindow, Ui_AddNewClient):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
self.setupUi(self)
myfile.py:
from addnewclient_logic import AddNewClientForm
class MainForm(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.actionNuevo_Cliente.triggered.connect(self.open_secondwindow)
def open_secondwindow(self):
window_nuevocliente = self.findChild(AddNewClientForm)
if window_nuevocliente is None:
window_nuevocliente = AddNewClientForm(self)
window_nuevocliente.show()
The parent window will hold a reference to the child window until it deletes itself on close - at which point, it will also be removed from the parent's list of children.
I'm trying to learn the basics of threading with PySide, and so put together the below code. What I'm trying to do is launch a thread that will update a QPlainTextEdit widget using a list of string, with a delay between each string. Instead what I'm getting is a crash to desktop, and I can't understand why:
import sys
import time
from PySide import QtCore, QtGui
class Worker(QtCore.QThread):
to_log = QtCore.Signal(str)
def __init__(self, txt, parent=None):
super(Worker, self).__init__(parent)
self.txt = txt
def run(self):
for i in self.txt:
self.to_log.emit(i)
time.sleep(1)
class TestThreadsApp(QtGui.QWidget):
def __init__(self):
super(TestThreadsApp, self).__init__()
self.initUI()
def initUI(self):
self.log = QtGui.QPlainTextEdit()
self.pb = QtGui.QPushButton('Go')
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.log)
hbox.addWidget(self.pb)
self.setLayout(hbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Test')
self.show()
self.pb.clicked.connect(self.get_worker)
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
def to_log(self, txt):
self.log.appendPlainText(txt)
def main():
app = QtGui.QApplication(sys.argv)
ex = TestThreadsApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If I update the get_worker() method to the below, it will run, but the QPlainTextEdit widget is updated with all the strings simultaneously, where as the behavior I'm wanting is for the widget to be updated by the threaded processes as each string is emitted - not altogether after both have been emitted:
def get_worker(self):
self.proceed = False
worker = Worker(['This is a test', 'to understand threading'])
worker.to_log.connect(self.to_log)
worker.start()
while not worker.isFinished():
pass
You need to keep a reference to the thread, otherwise it will be garbage-collected as soon as get_worker returns.
So do something like this, instead:
def get_worker(self):
self.worker = Worker(['This is a test', 'to understand threading'])
self.worker.to_log.connect(self.to_log)
self.worker.start()
I have the following piece of example code of my problem. Running this, I would expect that (if you type something in the lineedit) the A.updateValue slot would be called twice and thus show 'a.updatevalue called' and 'a2.updatevalue called'
However, it is only called once, namely for the self.a2 object and not for the self.a object, the latter which is sent from a worker thread to the GUI thread. How can I fix this so that this piece of code also triggers the slot for the self.a object?
Thank you,
David
import os, sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class A(QObject):
def __init__(self, name):
QObject.__init__(self)
self.name = name
def updateValue(self, value):
print(self.name + ".updatevalue called")
class workerthread(QThread):
def __init__(self, parent=None):
QThread.__init__(self, parent)
def run(self):
a = A('a')
QObject.emit(self, SIGNAL("mySignal"), a)
class Main(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.centralwidget = QWidget(self)
self.hbox = QHBoxLayout()
self.centralwidget.setLayout(self.hbox)
def update(self, a):
self.a = a
edit = QLineEdit("", self)
self.hbox.addWidget(edit)
edit.textChanged.connect(self.a.updateValue)
self.a2 = A('a2')
edit.textChanged.connect(self.a2.updateValue)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = Main()
worker = workerthread()
worker.connect(worker, SIGNAL('mySignal'), gui.update)
worker.start()
gui.show()
sys.exit(app.exec_())
Define a when you initialize workerThread
class workerthread(QThread):
def __init__(self, parent=None):
QThread.__init__(self, parent)
self.a = A('a')
def run(self):
QObject.emit(self, SIGNAL("mySignal"), self.a)