PySide Signals in separate file - python

I am writing a GUI application using Qt and PySide and I usually connect my signals in the init function of my class. How can I put all my signals in a separate file and then call the function from the file in my main file? Something like this.
import Signals
class Program(QtGui.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(Program, self).__init__(parent)
self.setupUi(self)
Signals.ConnectSignals()
Signals.py
class ConnectSignals(QtGui.QMainWindow, VUI.Ui_MainWindow):
def __init__(self, parent=None):
super(ConnectSignals, self).__init__(parent)
self.setupUi(self)
self.actionClose.triggered(self.close)
But when I do that, I get this message
self.actionClose.triggered(self.close)
TypeError: native Qt signal is not callable

I am not sure if the rest of this should/will work, but the error is because youare missing .connect I think it should be
self.actionClose.triggered.connect(self.close)
self.actionClose.triggered is a signal (as I assume actionClose is an action) and the error is telling you that signals do not have __call__ defined.

Related

AttributeError: "Dialog" object has no attribute 'setupUi'

I made two forms on pyqt5 - qt-designer. One is the Main Form and the second is a Dialog which I will use for user input. I converted both to py code.
First of all I should inform you that I do not modify the converted Ui.py files. I prefer to have an extra "Main" file where I set each modification. I do this so that I won't have to make the extra changes each time I modify with the Ui files.
So, I added a second class on my Main file and tried to call it from my MainForm Class through a menu item. Here is some example code:
class MainForm(QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.ui = Ui_MainForm()
self.ui.setupUi(self)
self.ui.actionMenu1.triggered.connect(self.open_my_dialog)
def open_my_dialog(self):
my_dialog = QDialog()
my_dialog.ui = MyDialog()
my_dialog.ui.setupUi(my_dialog)
# MainForm.hide(self)
my_dialog.exec_()
my_dialog.show()
class MyDialog(QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.ui = Ui_MyDialog()
self.ui.setupUi(self)
self.ui.pushButton_cancel.clicked.connect(self.cancel_dialog)
def cancel_dialog(self):
print("Closing Dialog Window...")
sys.exit()
When I run this and click the respective menu button I get the following error:
AttributeError: 'MyDialog' object has no attribute 'setupUi'
the Error is at this line:
self.ui.setupUi(self) # this is under MyDialog Class
I can get the code working if I reference it to the external (Ui_MyDialog) file directly without using the second class here. But as I stated at the beginning I want to control this from within this file, make the modifications directly here so that I won't have the keep track of the modifications to the Ui files in the future.
Given that the error occurs at this line
self.ui.setupUi(self)
and you assigned an Ui_MyDialog instance to self.ui, before
self.ui = Ui_MyDialog()
the error message should be mentioning class Ui_MyDialog and not MyDialog.
So, either you misreported the error message or the error does not occur 'under MyDialog Class'.
I also would point out: QWidget and derived classes have no setupUi method, themselves. Such method belongs to Ui_* classes, generated by uic, and is typically called from the constructor of a widget that inherits from a Ui_* class.
So, if you want to call setupUi on MyDialog instances, MyDialog must inherit from Ui_MyDialog, in the first place.
And you do call it in open_my_dialog:
my_dialog.ui = MyDialog()
my_dialog.ui.setupUi(my_dialog)
Finally I got it to work. But before I provide the answer, I would like to thank
#p-a-o-l-o and #LoïcG.. the latter helping me out all the way. Thanks!
The code worked when I changed it to the following:
class MainForm(QMainWindow):
def __init__(self, parent=None):
super(MainForm, self).__init__(parent)
self.ui = Ui_MainForm()
self.ui.setupUi(self)
self.ui.actionMenu1.triggered.connect(self.open_my_dialog)
def open_my_dialog(self):
my_dialog = MyDialog()
# my_dialog.show() <-- seems this line is not necessary either
my_dialog.exec_() # this alone was enough for the code to work
class MyDialog(QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.ui = Ui_MyDialog()
self.ui.setupUi(self)
self.ui.pushButton_cancel.clicked.connect(self.cancel_dialog)
def cancel_dialog(self):
print("Closing Dialog Window...")
self.close() # not important but corrected this also.
I hope this helps!
Edit: I corrected some lines and made the answer more simple thanks to
#LoïcG. !

PySide: Emiting signal from QThread in new syntax

I'm trying (and researching) with little success to emit a signal from a working Qthread to the main window. I don't seem to understand how I should go about this in the new syntax.
Here's a simple example.
from PySide.QtCore import *
from PySide.QtGui import *
import sys
import time
class Dialog(QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
button = QPushButton("Test me!")
layout = QVBoxLayout()
layout.addWidget(button)
self.setLayout(layout)
#self.button.clicked.connect(self.test) ----> 'Dialog' object has no attribute 'button'
self.connect(button, SIGNAL('clicked()'), self.test)
self.workerThread = WorkerThread()
def test(self):
self.workerThread.start()
QMessageBox.information(self, 'Done!', 'Done.')
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
time.sleep(5)
print "Thread done!"
app = QApplication(sys.argv)
dialog = Dialog()
dialog.show()
app.exec_()
I understand that if I didn't have another thread I'd create the signal inside the Dialog class and connect it in the __init__ but how can I create a custom signal that can be emitted from WorkerThread and be used test()?
As a side question. You can see it commented out of the code that the new syntax for connecting the signal errors out. Is it something in my configurations?
I'm on OsX El Capitan, Python 2.7
Any help is highly appreciated! Thanks a lot
TL:DR: I'd like to emmit a signal from the WorkerThread after 5 seconds so that the test function displays the QMessageBox only after WorkingThread is done using the new syntax.
Ok, it's been a long day trying to figure this out. My main resource was this: http://www.matteomattei.com/pyside-signals-and-slots-with-qthread-example/
In the new syntax, in order to handle signals from different threads, you have to create a class for your signal like so:
class WorkerThreadSignal(QObject):
workerThreadDone = Signal()
This is how the WorkerThread end up looking like:
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self.workerThreadSignal = WorkerThreadSignal()
def run(self):
time.sleep(3)
self.workerThreadSignal.workerThreadDone.emit()
And for the connections on the Dialog class:
self.workerThread = WorkerThread()
self.buttonn.clicked.connect(self.test)
and:
self.workerThreadSignal = WorkerThreadSignal()
self.workerThread.workerThreadSignal.workerThreadDone.connect(self.success)
def success(self):
QMessageBox.warning(self, 'Warning!', 'Thread executed to completion!')
So the success method is called once the signal is emitted.
What took me the longest to figure out was this last line of code. I originally thought I could connect directly to the WorkerThreadSignal class but, at least in this case, it only worked once I backtracked it's location. From the Dialog init to WorkerThread init back to the WorkerThreadSignal. I took this hint from the website mentioned above.
I find strange that I have to create the same local variables on both classes, maybe there's a way to create one global variable I can refer to instead all the current solution but it works for now.
I hope this helps someone also stuck in this process!
PS: The syntax problem for the connection was also solved. So everything is written with the new syntax, which is great.

How to catch a Signal from QMainWindow class by another thread on PySide?

I have a MainWindow class which have a Gui application running on it and i want that every time i click on a button from my application a signal is emitted and caught by another thread. There is my example code (sorry for not posting my real code but it is real big now):
from PySide.QtGui import *
from PySide.QtCore import *
import sys
import mainGui #Gui file
class MainWindow(QMainWindow, mainGui.Ui_MainWindow):
mySignal = Signal()
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.newThread = workThread()
self.newThread.start()
#myButton is part of Gui application
self.myButton.clicked.connect(self.myfunction)
def myfunction(self):
self.mySignal.emit()
(...) #Other functions and methods
class workThread(QThread):
def __init__(self, parent=None):
super(workThread, self).__init__(parent)
#The problem:
MainWindow.mySignal.connect(self.printMessage)
def run(self):
(...)
def printMessage(self):
print("Signal Recived")
(...)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__=="__main__":
main()
... and i get the following error:
MainWindow.mySignal.connect(self.printMessage)
AttributeError: 'PySide.QtCore.Signal' object has no attribute 'connect'
There is any ideia how can i solve this?
Thanks in advance!
Signals are just like methods - they must be bound to instances. They won't work correctly if you try to access them directly via the class.
One way to fix the example is to pass the instance of MainWindow in as the parent of the thread, like so:
self.newThread = workThread(self)
...
parent.mySignal.connect(self.printMessage)

How to accept close event of MainWindow when loading it with QUiLoader()?

How to receive close event in following code?
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.view = QUiLoader().load("sample.ui", self)
self.view.show()
def closeEvent(self, e):
print "close event recieved"
def main():
app = QApplication(sys.argv)
a=Main()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
If I convert sample.ui to sample.py using pyside-uic and importing this into main.py then I was able to receive close event.
from sample import Ui_MainWindow
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
def closeEvent(self, e):
print "close event recieved"
app = QApplication(sys.argv)
a=Main()
a.show()
sys.exit(app.exec_())
The second example works because it effectively becomes a subclass of the top-level class from Qt Designer. By contrast, the first example uses composition rather than subclassing, which puts all the gui elements inside an internal namespace. The Main class is just a container that acts as the parent of the view widget, and is never actually shown (which in turn means it doesn't receive any close events).
In PyQt, the uic module has several funtions which allow you to work around these issues, but there is currently nothing like that in PySide. Instead, you have to roll your own function. See this answer for an explanation of how to do that.
Alternatively, you could change the top-level class in Qt Designer to a QWidget, and then make view the central widget of your Main class. This is a lot less flexible than the above method, though.

Defining a wx.Panel destructor in wxpython

How do you define a destructor for a wx.Panel in wxpython?
META:
After inheriting a code base which uses wxpython and PyPubSub I've discovered a huge number of pubsub subscriptions in the __init__ functions of wx.Panel's that are never unsubscribed and cause errors later on in the program.
You should be able to bind to EVT_WINDOW_DESTROY and do the unsub in the handler.
For example:
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.NewId())
pub.subscribe(self.__handler, 'event')
def __destroy(_):
pub.unsubscribe(self.__handler, 'event')
self.Bind(wx.EVT_WINDOW_DESTROY, __destroy)
If above is not working you can protect against the PyDeadObjectError exception, by adding the following in the code where you try to access ExtendedWxPanel:
if instanceOfExctendedWxPanel:
then access it or methods of it.
I had the same issue, I solved it by doing the following:
My code (which gave the error) was:
import wx
from pubsub import pub
class My_panel(wx.panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.box = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.box)
#
pub.subscribe(self.do_something, 'Msg')
def do_something(self):
self.box.Layout()
The above panel was in a wx.Notebook page. In my app the user has the possibility to add or delete page from this notebook.
When the pub.sendMessage('Msg') code line was run after that the user deleted the notebook page containing this panel, I had the following error:
RuntimeError: wrapped C/C++ object of type BoxSizer has been deleted
which seems to be ~~ the new error type of 'wx.PyDeadObjectError exception' according to wxPython: https://wxpython.org/Phoenix/docs/html/MigrationGuide.html
What is explained in such documentation from wxPython is to use the nonzero() method which tests if the C++ object has been deleted.
Hence my working code is:
import wx
from pubsub import pub
class My_panel(wx.panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.box = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.box)
#
pub.subscribe(self.do_something, 'Msg')
def do_something(self):
if self.__nonzero__():
self.box.Layout()

Categories

Resources