PDA

View Full Version : Import uic or use pyuic4?



ChrisOfBristol
25th March 2015, 18:40
I am a newbie to PyQt, but have created and published a simple app. I would like to make a few improvements to it.

I have been wondering what the pros and cons are for choosing between importing uic to load the GUI.ui or using pyuic4 to convert the GUI.ui into a .py file.
The first method is simpler for testing, but the second creates a .pyc file after the first run, so I imagine that it loads the GUi and app quicker. It's difficult to do a comparison as other factors are involved. Speeding up the loading would be helpful. have tried to assess the advantages of method myself but have no experience to base this on. A web search hasn't helped as it doesn't seem to be discussed.

What are the advantages and disadvantages to each method - I'd welcome opinions as well as facts.

anda_skoa
26th March 2015, 07:11
What do you mean with "importing uic"?
How do you load the UI in this case?
QUiLoader?

Cheers,
_

ChrisOfBristol
26th March 2015, 20:25
anda_skoa: I don't know the correct terminology, but I create a GUI called test.ui with Qt Designer which has a form called Ui_Form, then I could either:

1) Load the .ui file directly into the Python program which has:

from PyQt4 import QtCore, QtGui, uic
form_class = uic.loadUiType(test.ui)[0]

OR

2) Convert the test.ui into a Python script with:

pyuic4 test.ui -o test_ui.py

Load test_ui.py into the Python program which has:

from PyQt4 import QtCore, QtGui
from test_ui import Ui_Form

anda_skoa
27th March 2015, 07:29
The second case is the one usually used in C++.

I was trying to understand if the first case resembles usage of QUiLoader or if it was something PyQt offered as an additional thing.
Seems to be the latter.

The two C++ options differ a lot. Not only is the generated code obviously faster (no XML parsing at runtime), it is also safer to program (you get compiler errors when trying to access something that doesn't exist or is of the wrong type).

The lack of a build stage with Python obviously doesn't make the second thing a factor.

Cheers,
_

ChrisOfBristol
27th March 2015, 08:31
andy_skoa: Thanks, that makes sense to me. If there is a speed advantage it might be worth me using it. I must admit that I was hoping you would say there was no real advantage, because despite a lot of research and experiment, I have been struggling to work out how to change my original program to make it work. I cribbed the basics from this excellent tutorial. (https://blog.safaribooksonline.com/2014/01/22/create-basic-gui-using-pyqt/) to get:

# Temperature-conversion program using PyQt
import sys
from PyQt4 import QtCore, QtGui, uic

form_class = uic.loadUiType("tempconv.ui")[0] # Load the UI

class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
# Bind the event handlers to the buttons
blah-blah
# button event handlers
blah-blah

app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
but I don't understand object oriented programming etc well enough to know what to change apart from needing something like:

from PyQt4 import QtCore, QtGui
from test_ui import Ui_Form

This documentation (http://pyqt.sourceforge.net/Docs/PyQt4/designer.html) looks helpful, but once it starts talking about inheritance and classes I've had it!

anda_skoa
27th March 2015, 08:52
I don't know enought Python to be sure but it looks like your class is currently inheriting from QMainWindow and form_class.
In which case you should be able simply replace form_class in line 7 with Ui_Form

Cheers,
_

ChrisOfBristol
27th March 2015, 22:44
Unfortunately it doesn't quite work, there is just a cross on the screen a bit like this ╬. If I've got time over the weekend I'll edit out all the irrelevant stuff from the Python script created from the gui and the main Python code, and post it here, then perhaps a Python expert can point out what I have got wrong.

ChrisOfBristol
28th March 2015, 11:57
This works:

#! /usr/bin/python

import sys
from PyQt4 import QtCore, QtGui

from pyuic_ui import Ui_MainWindow #The name of my top level object is MainWindow

class MyForm(QtGui.QMainWindow): #The name of my top level object is MainWindow
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow() #The name of my top level object is MainWindow
self.ui.setupUi(self)

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = MyForm()
myapp.show()
sys.exit(app.exec_())

anda_skoa
28th March 2015, 12:40
That is also how one would do in in C++, i.e. have the generated UI class as a member of the widget class.

Cheers,
_

ChrisOfBristol
29th March 2015, 22:03
I spoke too soon! I have added a button to the script, but it causes an error even though the names are correct, as you can see from the script below and the attached files.

#! /usr/bin/python

import sys
from PyQt4 import QtCore, QtGui
from pyuic_ui import Ui_MainWindow

class MyWindowClass(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)

self.buttonProcess.triggered.connect(self.buttonPr ocessTriggered)

def buttonProcessTriggered(self):
print 'OK'

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mywindow = MyWindowClass()
mywindow.show()
sys.exit(app.exec_())


chris@Asus:~/Documents/Linux/Debreate/PyQt$ ./pyuic.py
Traceback (most recent call last):
File "./pyuic.py", line 23, in <module>
mywindow = MyWindowClass()
File "./pyuic.py", line 13, in __init__
buttonProcess.triggered.connect(self.buttonProcess Triggered)
NameError: global name 'buttonProcess' is not defined

There is also a strange anomaly with Qt, in that stuff I have deleted from the GUI with Qt Creator doesn't always get deleted from the .ui file. You can see from the attached files that there is only a button on the gui, but unwanted lines remain in the .ui file.

anda_skoa
30th March 2015, 07:14
The widgets created by Ui_MainWindow are members of its instance, i.e. members of the object you have as "ui"


self.ui.buttonProcess.triggered.connect(self.butto nProcessTriggered)


Cheers,
_

ChrisOfBristol
30th March 2015, 13:17
It doesn't work, so I tried the alternatives between the hashes, none of those work.


#! /usr/bin/python

import sys
from PyQt4 import QtCore, QtGui
from pyuic_ui import Ui_MainWindow

class MyWindowClass(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
#
buttonProcess.triggered.connect(self.buttonProcess Triggered)
ui.buttonProcess.triggered.connect(self.buttonProc essTriggered)
self.buttonProcess.triggered.connect(self.buttonPr ocessTriggered)
self.ui.buttonProcess.triggered.connect(self.butto nProcessTriggered)
#
def buttonProcessTriggered(self):
print 'OK'

if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mywindow = MyWindowClass()
mywindow.show()
sys.exit(app.exec_())

anda_skoa
30th March 2015, 13:52
I assume you are using the correct name for the button?
I.e. in your .ui it is called pushButton not buttonProcess, so either the code snippet does not match your .ui file or you have a wrong name in the connect

Cheers,
_

ChrisOfBristol
30th March 2015, 14:06
I checked all that and somehow missed it! Having corrected it to this

self.ui.pushButton.triggered.connect(self.pushButt onTriggered)
gives this

chris@Asus:~/Documents/Linux/Debreate/PyQt$ ./pyuic.py
Traceback (most recent call last):
File "./pyuic.py", line 23, in <module>
mywindow = MyWindowClass()
File "./pyuic.py", line 16, in __init__
self.ui.pushButton.triggered.connect(self.pushButt onTriggered)
AttributeError: 'QPushButton' object has no attribute 'triggered'
though.

anda_skoa
30th March 2015, 18:29
Right, the signal is called clicked().

QAction's have a triggered() signal.

Cheers,
_

ChrisOfBristol
30th March 2015, 20:46
It seems odd to me that the ui file and the ui.py file both have 'triggered', but in the Python file it has to be 'clicked'.

It's working now, and I have changed this in the app, and on a couple of other lines and the app works too! A quick couple of tries suggests that it loads much more quickly, which was what I wanted to achieve.

No doubt I would have been able to tackle this better if I was prepared to learn about object orientation/inheritance/classes, but I only started this app by accident really (to get something done), i had to learn Python and Qt from scratch and didn't want to have to learn anything I could possibly manage without.

Thanks for your extremely patient answers to my newbie questions.

ChrisOfBristol
4th April 2015, 11:36
I've now switched to Python3 and PyQt5, QtWidgets are separate in PyQt5 so a few minor changes are needed to make this script work:

#! /usr/bin/python3

import sys
from PyQt5 import QtCore, QtGui, QtWidgets #
from image3d_ui import Ui_MainWindow

class MyWindowClass(QtWidgets.QMainWindow): #
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent) #
self.ui = Ui_MainWindow()
self.ui.setupUi(self)

self.ui.pushButton.clicked.connect(self.pushButton Clicked)

def pushButtonClicked(self):
print ('OK')

if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv) #
mywindow = MyWindowClass()
mywindow.show()
sys.exit(app.exec_())