How To use PyQt and TAU
A collection of recipes to make first steps easier ...
How to create a TAU and PyQt GUI or Widgets
Resources
[Controls website, gui framworks] [Qt4 Documentation] [Qt4 Classes reference]
Create a GUI
Copied from http://www.cells.es/Members/srubio/howto/pyqt
- open qt designer
- create your widget
- save the ui_XXX.ui file
- > pyuic4 ui_XXX.ui > ui_XXX.py
- edit your XXX.py file
- Create a new class inheriting from the object that you want to create (QMainWindow or QDialog)
- in the __init__ method create an ui_XXX object and call to setupUi and retranslateUi methods
- This self.ui object will contain all the widgets and other objects that you added on QtDesigner.
- Add to your class the methods (Slots) that you want to execute for each Widget event (Signal)
- Connect Signals and Slots
- Browse to http://www.riverbankcomputing.com/static/Docs/PyQt4/html/classes.html to see what each PyQt class offers to you.
# This is the contents of the MyGui.py file
# It assumes that you have created ui_MyGui.ui with QtDesigner
# You have to execute >/pyuic4 ui_MyGui.ui > ui_MyGui.py to generate the file to import
from PyQt4 import QtGui,QtCore
from ui_MyGui import ui_MyGui
class MyGUI(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = ui_MyGui()
self.ui.setupUi(self)
self.connect(self.ui.push_button,QtCore.SIGNAL('clicked(bool)'),self.DoAction)
def DoAction(self,arg = None):
print 'What a %s action.' % ('wonderful' if arg else 'sad')
app = QtGui.QApplication(sys.argv[0:1])
gui = MyGui()
gui.show()
sys.exit(app.exec_())
Using TAU Models
Using a Database Model
This explanation is referred to this DbWidget Template.
Methods that has been modified from the raw template are:
- init: to initialize the model by default (it should be done using Qt Properties!) added: self.setModel('controls01:10000')
- getModelClass : to return the right Model class added: return tau.core.TauDatabase?
- updateStyle : to set the values to be displayed added:
obj = self.getModelObj(); if obj: self.setText(obj.get_info()) # or any other PyTango?.Database method call
NOTE: It has been done here because Database object does not have events to subscribe;
for event-driven objects other methods must be modified.
To create a DeviceTable? widget with a TauDatabase? model:
- Inherit from QTableWidget
* A method refresh(self) must be added to be able to update the widget using an external button
- Missing QtProperties? to add:
- ClassName? (Class of devices to be shown, it could be a list of comma-separated names)
- TangoHost? (Database Identifier)
Some Recipes
Standard Dialogs and Widgets
QMessageBox(parent,Title,Question,FlagsForButtons?)
v = QtGui.QMessageBox.warning(None,'SplitterTester', \
'You must introduce or select an IP from IPAddress Combo Box',QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel);
if v == QtGui.QMessageBox.Cancel:
return
QInputDialog(parent,title,question,QLineEdit,defaultText)
returns (text,OkPressed?)
In [29]:QtGui.QInputDialog.getText(None,'hola','dame argo',QtGui.QLineEdit.Normal,'una moneiya')
Out[29]:(<PyQt4.QtCore.QString object at 0x83ad2ac>, True)
QFont: Setting a monospaced font in a QTextEdit panel
font = QtGui.QFont()
font.setStyleHint(QtGui.QFont.TypeWriter)
font.setFamily("unexistentfont")
self.ui.TextPanel.setFont(font)
Focus: .setFocus(), hasFocus() available on most of widgets
QDialog vs QMainWindow (KeyEvents)
- QDialog automatically connects Return and Escape keys to Ok and Cancel buttons.
- In QMainWindow it must be hacked using keyPressEvent methods
Managing Key Events
def keyPressEvent(self,event):
if event.key() == QtCore.Qt.Key_Escape:
self.reject()
elif event.key() in [QtCore.Qt.Key_Return,QtCore.Qt.Key_Enter]:
buttons = [b for b in \
[self.ui.ScanButton,self.ui.buttonBox.buttons()[1],self.ui.AddressEdit,self.ui.RangeEdit] \
if b.hasFocus()]
if buttons:
for b in buttons:
if hasattr(b,'click'): b.click()
else:
self.accept()
return
Signals and Slots
Connecting signals and slots
Connections between signals and slots (and other signals) are made using the QtCore?.QObject.connect() method. For example:
QtCore.QObject.connect(a, QtCore.SIGNAL("QtSig()"), b, QtCore.SLOT("QtSlot()"))
QtCore.QObject.connect(a, QtCore.SIGNAL("PySig()"), b, QtCore.SLOT("QtSlot()"))
QtCore.QObject.connect(a, QtCore.SIGNAL("QtSig()"), pyFunction) #A python function with NO arguments
QtCore.QObject.connect(a, QtCore.SIGNAL("QtSig()"), pyClass.pyMethod) #A python function with NO arguments
QtCore.QObject.connect(a, QtCore.SIGNAL("PySig"), pyFunction) #A python signal WITH arguments
Emitting signals
Any instance of a class that is derived from the QtCore?.QObject class can emit a signal using its emit() method. This takes a minimum of one argument which is the signal. Any other arguments are passed to the connected slots as the signal arguments. For example:
a.emit(QtCore.SIGNAL("clicked()")) #A python function with NO arguments
a.emit(QtCore.SIGNAL("pySig"), "Hello", "World") #A python function WITH arguments
QThreads
QThreads object cannot access directly to GUI objects ... All access must be done using Signals and Slots; so any change to GUI (including displaying messages) must be done through emit and declared functions with arguments.
class QPinger(QtCore.QThread):
def __init__(self,parent,ipstart,iprange):
QtCore.QThread.__init__(self)
...
if hasattr(parent,'scan_finished'):
QtCore.QObject.connect(self, QtCore.SIGNAL("scan_finished"), parent.scan_finished)
def run(self):
...
self.emit(QtCore.SIGNAL("scan_finished"), founds)
Random remarks
Note: it works with milliseconds instead of seconds'':
- QThread.start() to launch QThread.run() method
- QThread.wait() equals to threading.Thread.join()
- QThread.wait(millis): equals to threading.Event.wait(seconds)
- QMutex.lock() ; QMutex.unlock ... typical mutex
- QWaitCondition: Like threading.Event but a mutex is unlocked during wait and locked again when the wait finishes.
- QWaitCondition.wait(QMutex): Forces the Thread to wait until QWaitCondition.wakeOne() or .wakeAll() is called in another thread.
- QWaitCondition.wait(QMutex, time=int(millis)): This works like threading.Event.wait(seconds).
- QWaitCondition.wait(QMutex): Forces the Thread to wait until QWaitCondition.wakeOne() or .wakeAll() is called in another thread.
- Signals: QThread.started,.finished,.terminated (launched by terminate(); it is thread unsafe!)
How to create a TAU Widget
from Thiago's internal presentation http://www.cells.es/CELLS/Intranet/Divisions/Computing/Controls/Applications/tau/Tau_Internal_Presentation
Writing your own widgets
- goto widget/utils
- type:
python widgetgen.py <classname> <superclass> <outputfile> [<qtfile>]
But it fails a lot!!!
- Things that needed modification (bugzilla):
- Import TauBaseWidget? and tau.widget.taubase instead of TauWidget?
- Inherit from TauBaseWidget? instead of TauWidget?, update all references
- It must inherit SuperClass? first, and then TauBaseWidget?
- Change all tabs to spaces (now are mixed)
- there's an indentation error at the end of event_received
- Delete all QtProperties? and QtHandlers? part and paste the ones from tau/widget/scalarlabel.py
Things to do to setup the module (DBaseWidget example)
- init: to initialize the model by default (it should be done using Qt Properties!)
self.setModel('controls01:10000')
- getModelClass : to return the right Model class
return tau.core.TauDatabase
- updateStyle : to set the values to be displayed
obj = self.getModelObj();
if obj: self.setText(obj.get_info()) # or any other PyTango.Database method call
Notes:
- widget module is for generic tango widgets only
- there will be modules for gl, qwt, etc
- if you implement a widget that has reference to any system or interprets tango data in a very particular way it is not a widget. Just part of your application
More things …
from unfinished Thiago's tutorial
- Must inherit from TauBaseWidget?
- Must inherit from QWidget or any of its subclasses
- Must provide the following constructor:
def __init__(self, parent = None, designMode = False)
- must call the TauBaseWidgetConstructor?:
self.call__init__(TauBaseWidget, name, designMode=designMode)
- Must define two properties:
- model
- parentModel
- Should implement getModelClass
- Should implement updateStyle

