PySide - Source functions defined outside of a class
-
First, I am very new to pySide. Been using about a week.
I am using designer-qt4 in Mint Linux to create the GUI and pyside-uic utility to generate the python code. For the most part it works very well. One thing I have not figured out is how to properly call functions not defined within the class structure that pyside-uic creates.
Example the following works when added to the class definition.
@def get_quota(self):
process = subprocess.Popen(['df', '-kh'], stdout=subprocess.PIPE)
quotaoutput = process.communicate()[0]
self.txtQuota.clear()
self.txtQuota.append(quotaoutput)@The function is called with a pushbutton, 'self.btnQuota.clicked.connect(self.get_quota)'.
The push button and most of the GUI elements are created in a def statement called 'setupUi' defined within the class object. The class is called Ui_StatCheck(object).
If this code is in a seperate file called defs.py and use 'from defs import *' I get errors.
First, the GUI wouldn't start and I got this error returned;
bq. Traceback (most recent call last):
File "./top.py", line 13, in <module>
statchk = ControlStatCheck()
File "./top.py", line 9, in init
self.ui.setupUi(self)
File "/home/howell/scripts/pySide/statchk_scratch/statchk.py", line 44, in setupUi
self.btnQuota.clicked.connect(self.get_quota)
AttributeError: 'Ui_StatCheck' object has no attribute 'get_quota'Changed the command to 'self.btnQuota.clicked.connect(get_quota)' and the GUI would start but clicking the button caused the following error.
bq. TypeError: get_quota() takes exactly 1 argument (0 given)
I can keep the function definitions within the class and keep working but I'd prefer to minimize the editing everytime I change the GUI in designer.
I am open to suggestions. Thanks in advance.
-
Your issue is simply that if 'get_quota' is defined as a function rather than a method then it cannot expect (and will not receive) the 'self' parameter. The following example works with PyQt4 (and should with PySide):
@
from PyQt4.QtCore import *
from PyQt4.QtGui import *def mySlot(): print "SLOT"
class MyGui(QWidget):
def init(self, parent=None, **kwargs):
QWidget.init(self, parent, **kwargs)l=QVBoxLayout(self) p=QPushButton("Test", self) p.clicked.connect(mySlot) l.addWidget(p)
if name=="main":
from sys import argv, exita=QApplication(argv) m=MyGui() m.show() m.raise_() exit(a.exec_())
@
Hope this helps ;o)
-
Also, the following example shows keeping the subprocess functionality generic by creating it's own class which is simply instantiated by the GUI and using signals and slots to communicate to remove requirement for your static function:
@
from PyQt4.QtCore import *
from PyQt4.QtGui import *class QuotaProcess(QProcess):
quota=pyqtSignal(str)
def getQuota(self):
self.start('df',['-kh'])if self.waitForReadyRead(): self.quota.emit(str(self.readAllStandardOutput())) self.waitForFinished()
class MyGui(QWidget):
def init(self, parent=None, **kwargs):
QWidget.init(self, parent, **kwargs)qp=QuotaProcess(self) l=QVBoxLayout(self) p=QPushButton("Test", self) p.clicked.connect(qp.getQuota) l.addWidget(p) q=QLabel(self) qp.quota.connect(q.setText) l.addWidget(q)
if name=="main":
from sys import argv, exita=QApplication(argv) m=MyGui() m.show() m.raise_() exit(a.exec_())
@
Hope this helps too ;o)
-
Can you actually run my examples simply changing the imports from PyQt4 to PySide (they both work fine for me)? Try that before trying to integrate your code with mine. Also, posting your full code would help us help you ;o)
Your name error for QProcess probably means you haven't imported it:
@
...
from PySide.QtCore import QProcess
...
@Hope this helps ;o)
-
I created a simple demo to illustrate what I am trying to do.
@#!/usr/bin/python
import subprocess
from PySide import QtCore, QtGuiclass Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(526, 305)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pshLs = QtGui.QPushButton(self.centralwidget)
self.pshLs.setGeometry(QtCore.QRect(20, 20, 51, 27))
self.pshLs.setObjectName("pshLs")
self.textEdit = QtGui.QTextEdit(self.centralwidget)
self.textEdit.setGeometry(QtCore.QRect(90, 20, 411, 261))
self.textEdit.setObjectName("textEdit")
MainWindow.setCentralWidget(self.centralwidget)
self.pshLs.clicked.connect(self.list_dir)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) self.pshLs.setText(QtGui.QApplication.translate("MainWindow", "ls", None, QtGui.QApplication.UnicodeUTF8)) def list_dir(self): process = subprocess.Popen(['ls'], stdout=subprocess.PIPE) quotaoutput = process.communicate()[0] self.textEdit.clear() self.textEdit.append(quotaoutput)
if name == "main":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
@The preview appears to have broken the indentation but the above works. What does not work is when I place the 'list_dir' function in another file called 'list_dir.py and use 'import list_dir' in the above.
-
BTW: this is the error I get;
@Traceback (most recent call last):
File "./ls.py", line 39, in <module>
ui.setupUi(MainWindow)
File "./ls.py", line 25, in setupUi
self.pshLs.clicked.connect(list_dir(self))
RuntimeError: Failed to connect signal clicked().
@ -
jazzycamel, in your second example -is there a pyside version of pyqtSignal-?
update: nevermind - I found it.
Noticed that your method works with QWidget but not with QMainWindow.
Tried this and the window comes up but the test button does not do anything. No errors are returned.
@#!/usr/bin/python
from PySide.QtCore import *
from PySide.QtGui import *class QuotaProcess(QProcess):
quota=Signal(str)
def getQuota(self):
self.start('df',['-kh'])if self.waitForReadyRead(): self.quota.emit(str(self.readAllStandardOutput())) self.waitForFinished()
class MyGui(QMainWindow):
def init(self, parent=None, **kwargs):
QMainWindow.init(self, parent, **kwargs)qp=QuotaProcess(self) #l=QVBoxLayout(self) p=QPushButton("Test", self) p.clicked.connect(qp.getQuota) #self.addWidget(p) q=QLabel(self) qp.quota.connect(q.setText) #self.addWidget(q)
if name=="main":
from sys import argv, exita=QApplication(argv) m=MyGui() m.show() m.raise_() exit(a.exec_())
@
-
When using QMainWindow you need to place your widgets on a single central widget, read the "Qt Main Window Framework":http://qt-project.org/doc/qt-4.8/qmainwindow.html#details section of the docs for more information. I've modified your code to work as follows:
@
#!/usr/bin/python
from PySide.QtCore import *
from PySide.QtGui import *class QuotaProcess(QProcess):
quota=Signal(str)def getQuota(self): self.start('df',['-kh']) if self.waitForReadyRead(): self.quota.emit(str(self.readAllStandardOutput())) self.waitForFinished()
class MyGui(QMainWindow):
def init(self, parent=None, **kwargs):
QMainWindow.init(self, parent, **kwargs)qp=QuotaProcess(self) w=QWidget(self) l=QVBoxLayout(w) p=QPushButton("Test", self) p.clicked.connect(qp.getQuota) l.addWidget(p) q=QLabel(self) qp.quota.connect(q.setText) l.addWidget(q) self.setCentralWidget(w)
if name=="main":
from sys import argv, exita=QApplication(argv) m=MyGui() m.show() m.raise_() exit(a.exec_())
@
Hope this helps ;o)
-
Still new to PySide but learning. My motivation for this was to isolate the GUI/interface coding from the functional coding.
I found a way to reference .ui xml files created in qt designer directly. Now I can easily make changes to the ui without having to touch the python code at all.