介绍
最近用写Musorg程序的机会学了一下PyQT4编程,感觉很不错,不过也遇到了不少的问题。在这里为以后做一些记录吧。
QT4 Designer
QT4 Designer为界面的设计提供了非常大的帮助,并且在设计的同时,他能够生成相应的C++代码。不过,我编程一般都会用Python,所以希望能够直接调用设计好的界面。在PyGTK中,我们有pygtk.glade这个模块,能够动态加载GTK设计的XML文件。幸运的是,QT4也提供了动态加载的模块,但是更进一步的是,他提供了一个将设计文件转成Python代码的工具:pyuic4。
该工具,pyuic4的基本是用方法就是:pyuic4 -o <python_file> <qt_ui_file>。选项-o就是
告诉pyuic4应该将生成的Python代码输出到那个文件中。这样一来,我们就可以直接在主程序
中调用相应的UI模块了。至于命名,pyuic4会根据QT4 Designer中最上层窗口的对象名,在其
之前加上UI。
调用生成的代码
如果要调用生成的代码的话,可以用如下的方法:
from ui_api import UI_MusorgMainWindow
class MusorgUI(QtGui.MainWindow, UI_MusorgMainWindow)
def __init__(self):
QtGui.MainWindow.__init__(self)
self.setupUi(self)
...
QT4 Layout
在用Designer设计的时候,Layout一直困惑了我一阵。我以为,像GTK一样,QT4直接将Layouts里
边任意一个布局拖进控件里就行。结果发现每次拖进去,布局都没有自动展开,而只是被置于拖进
时的位置,而且大小是要手动调整,这显然不对。折腾了一阵后发现,原来是直接在对象查看器里
找到相应的容器控件,右击,然后选择布局便可。
QT4的信号发送与处理
QT4的信号发送与处理比较显而易见,所以没有遇到太大的问题。和GTK的信号发送与处理类似,也
是通过emit和connect(虽然函数名不太一样)。PyQT4中信号发送如下:
...
self.emit(SIGNAL("dataChanged(int, double)"), int_number, float_number)
...
假设之前这个信号是有一个叫做acontrol的QT4控件发送出来的,那么我们可以在另一个控件中定义
接受和处理该信号的函数:
def on_data_changed(self, int_param, float_param):
# process the data
pass
有了该函数之后,我们将信号与函数之间建立链接:
...
self.connect(acontrol, SIGNAL("dataChanged(int, double)"), self.on_data_changed)
...
这样就建立了信号的发送与接收的代码。每当程序执行到emit语句时,就会去查找相应的处理函数。
TreeView笔记
TreeView、ListView、TableView也许是任何一个UI库里边都比较复杂的控件。随着MVC模式的推广,
大部分UI库都在原有的Item-based控件之外又提供了Model-based控件。QT4里边的TreeView、ListView
和TableView就是Model-based,程序里我用的是TreeView。
QT4做的好的一点是的确将M,V,C分开了。M一般是由继承于QAbstractItemModel的类来控制,该类
需要实现以下函数:
- data(index, role): 返回对应于index的数据,需要注意的是,该函数在默认情况下应该只有在 role为Qt.DisplayRole的情况下才返回数据,其他时候应该返回空数据(QVariant())。
- rowCount(parent): 返回以parent为父节点的节点行数。
- index(row, column, parent): 返回以parent为父节点,row行column列的节点的索引。
- columnCount(parent): 返回以parent为父节点的节点列数。
- parent(index): 返回index索引所对应的父节点,如果没有父节点,返回空索引(QModelIndex())。
V一般是由继承于QAbstractItemDelegate的类来实现的,一般会重载paint()函数来绘画字符串以外
的内容,例如进度条、选项框、按钮等等。
C就是QTreeView本身。当用户选中某一个节点时,TreeView会发射信号,这就是控制器的一种表现。
QThread多线程笔记
刚开始用QThread时,不太习惯,而且犯了一个非常大的错误。该错误在今后的编程中也一定要注意。
其实是因为不知道要这么做,就是每一个QThread就是一个QObject,和其他QObject一样,应该有一个
parent,如果没有,当线程结束时(run函数返回时),它就会抛出异常:“Thread deleted while
it's running”。
Semaphore, Mutex, Wait Condition
这三个其实就是我们在进程调用和通信中常用的,没有太大区别。当有共享资源需要控制时,就会需要
用到这些。但是这些还可以用来控制开始/暂停,呵呵~~
用这些来实现开始/暂停的原理其实比较简单:
- 让暂停信号(pause_semaphore)拥有一个资源。
- 当运行时,程序检测暂停信号资源的个数,如果大于零,那么程序会正常运行
- 暂停时,我们获取一个暂停信号资源,这时程序发现资源数不够,就会停止。
- 当我们再次开始时,我们释放一个信号资源,这样程序就会继续运行了。
还要注意的是,QThread不能直接控制主程序中的数据(其实也不推荐这么做),所以可以通过发送
信号给主程序,让接受到信号的主程序来做相应的处理。