1.每个应用必须创建一个 QtGui.QApplication(sys.argv), 此时 QtGui.qApp 为此应用的实例
app = QtGui.QApplication(sys.argv) sys.exit(app.exec_()); #因为exec是关键字, 所以 Qt 用的 exec_, app.exec_() 表示进入循环
2.所有界面图形类都继承自 QtGui.QWidget, 比如 QtGui.QPushButton 等都可以使用 setGeometry() 等方法, QWidget 如果没有指定父, 那么它就是顶级窗口
必须设置可以显示的父类, 或者调用自身的 show()/exec_() 方法才可以显示
3.QtGui.QPushButton('Quit', widget), 创建一个 QPushButton 实例, 放在 widget 上
4.PyQt4 事件处理系统建立在 信号-槽 的基础上
槽详解可以参考以下两篇文章, PyQt4信号和槽用法总结, 信号和槽详解
事件(Events) 是 GUI 程序中很重要的一部分, 它由用户或系统产生, 当我们调用程序的 exec_() 方法时, 程序就会进入主循环中, 主循环捕获事件并将它们发送给相应的对象进行处理, 当用户单击一个按钮, 拖动一个滑块或进行其它动作时, 相应的信号就会被发射, 除此之外, 信号还可以因为环境的变化而被发射, 比如一个运行的时钟将会发射时间信号等, 而所谓的槽则是一个方法, 该方法将会响应它所连接的信号
信号与槽机制作为 Qt 最重要的特性, 提供了任意两个 Qt 对象之间的通信机制, 信号会在某个特定情况或动作下被触发, 槽是用于接收并处理信号的函数
每个Qt对象都包含预定的信号和槽, 当一某一特定事件发生时, 一个信号被发射, 与信号相关联的槽则会响应信号完成相应的处理
信号与槽机制常用的连接方式为: connect(Object1,SIGNAL(signal),Object2,SLOT(slot)), 其中槽函数可以是 PyQt 自带的的槽函数, 也可以是自定义的槽函数(@QtCore.pyqtSlot()), 甚至是任何 Python 可以调用的函数
w.connect(btn, QtCore.SIGNAL("clicked()"), QtGui.qAPP, QtCore.SLOT("quit()"))
或
#coding:utf-8 from PyQt4 import QtGui, QtCore app = QtGui.QApplication([]) w = QtGui.QWidget() def showMsg(): QtGui.QMessageBox.information(w, u"信息", u"ok") btn = QtGui.QPushButton(u"点我", w) w.connect(btn, QtCore.SIGNAL("clicked()"), showMsg) #或者 btn.clicked.connect(showMsg) w.show() app.exec_()
button 对象的 clicked() 信号连接到 showMsg 函数, 也就是说 ShowMsg 响应了一个按钮的点击事件
5.建立带菜单栏、工具栏、状态栏的窗口, 可使用或者继承 QMainWindow 类
statusBar(), menuBar(), addToolBar() 返回对应的类实例
fileMenu = xxx.menuBar().addMenu("&File")
fileMenu.addAction(aaa)
同理 toolBar
6.布局方式:
绝对定位: 使用 move(), setGeometry()
盒子定位: QHBoxLayout(水平定位), QVBoxLayout(垂直定位)
网格定位: QGridLayout(addWidget 的时候可以指定行或列的跨度)
addStretch() 增加一个可伸缩的占位符, 也相当于一个元素, 其中在添加 Widget 的时候也可以指定 stretch, 表示伸缩比例
addSpacing() 增加一个固定宽高的空白符
在使用布局时, 可以不指定控件的父窗口, 当父窗口 addLayout() 或者 setLayout() 时, 布局内的所有控件都会重定义其父窗口到父窗口
布局对象.setSizeConstraint(val) 用来设置窗体约束尺寸, 比如 val 可以为 QLayout.setFixedSize
6.常见对话框 QColorDialog、QErrorMessage、QFileDialog、QFontDialog、QInputDialog、QMessageBox、QProgressDialog、QTabDialog、QWizard
7.常用控件类
空白占位符: QSpacerItem
图标: QIcon
气泡: QToolTip
按钮: QPushButton
按钮组: QButtonGroup
会话框按钮组: QDialogButtonBox
文本: QLabel
文本浏览器: QTextBrowser 可以认为是不可编辑的QTextEdit
时间编辑器: QDateTimeEdit
单文本编辑器: QLineEdit
多文本编辑器: QTextEdit
加减数字: QSpinBox
数字LCD显示器: QLCDNumber(槽 display(int))
滑动条: QSlider(信号 valueChanged(int) 等)
颜色类: QColor
窗口或控件的调色板: QPalette, 每个窗口或空间都会有, 此类有两个基本概念: ColorGroup(包括焦点/失去/不可用) 和 ColorRole(指窗体的哪部分)
图片类: QPixMap(主要用来显示图片), QImage(主要用来修改图片), label->setPixMap(QtGui.QPixMap("icon/logo.png"))
进度条: QProgressBar
计时器: QBasicTimer, QTimer, 配合 "timeout()" 信号, 或者重定义 timerEvent 事件
时间: QTime
单选框: QRadioButton
多选框: QCheckbox
下拉列表: QStringList
下拉列表: QComboBox
图片按钮: QToolButton
工具箱列表: QToolBox
表格: QTableWidget
标签栏: QTabWidget
多窗口: QWorkspace, 使用时可以用 QMainWindow 实例的 setCentralWidget(QWorkspace())
分割窗口: QSplitter
可挪动窗口: QDockWidget
菜单: QMenu
菜单栏/工具栏按钮: QAction, "triggered()"
堆栈窗口实现: QListWidget, QStackedWidget 分别配合自身的 currentRowChanged(int) 和 setCurrentIndex(int) 实现, 另需配合 QSplitter
启动画面: QSplashScreen
打印机: QPrinter, QPrintDialog
图形: QPainerPath 它是由一些图形如曲线、矩形、椭圆组成的对象
8.信号与槽
所有继承自 QObject 的类都可以发射信号, emit 方法用来发射信号
自定义信号
class Emit(QtGui.QWidget): def __init__(self, parent = None): QtGui.QWidget.__ini__(self, parent) self.setWindowTitle("Emit") self.resize(250, 150) self.connect(self, QtCore.SIGNAL("closeEmitApp()"), self, QtCore.SLOT("close()")) def mousePressEvent(self, event): self.emit(QtCore.SIGNAL("closeEmitApp()"))
clicked(), 被点击时
valueChanged(int), 数值被该表时
triggered() 菜单栏/工具栏被触发时
9.槽
quit()
10.位置坐标等函数
self.x()
self.y()
self.frameGeometry().x(), self.frameGeometry().y(), self.frameGeometry().width(), self.frameGeometry().height()
self.pos().x(), self.pos().y()
self.geometry().x(), self.geometry().y(), self.geometry().width(), self.geometry().height()
self.width()
self.height()
self.rect().x(), self.rect().y(), self.rect().width(), self.rect().height()
self.size().width(), self.size().height()
rect()和 size(),调用它们获得的结果也都是对于窗体的中央区域而言的, size() 获得的是窗体中央区域的长,宽值, rect()与 geometry()一样返回一个 QRect 对象。其中, 两个函数获得的长,宽值是一样的,都是窗体中央区域的长,宽值,只是左上顶点坐标值不 一样, geometry()获得的左上顶点坐标是相对于父窗体而言的坐标,而 rect()获得的左上顶 点坐标始终为(0,0)。
12.禁止最大化和调整
#禁止最大化按钮 其中MainWindow可以为任何QWidget MainWindow.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint) #禁止调整窗口大小 其中MainWindow可以为任何QWidget MainWindow.setFixedSize(MainWindow.width(), MainWindow.height());
13.创建颜色 Icon
for color in QColor.colorNames(): pix = QPixmap(QSize(70, 20)) pix.fill(QColor(color)) icon = QIcon(pix)
14.自定义移动
def mousePressEvent(self,event): if event.button()==Qt.LeftButton: self.dragPosition=event.globalPos()-self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self,event): if event.buttons() & Qt.LeftButton: self.move(event.globalPos()-self.dragPosition) event.accept()
15.在 QListWidget 中添加 QCheckBox 对象
list = QListWidget() item = QListWidgetItem() checkbox = QCheckBox(self.tr("英语")) list.addItem(item) list.setItemWidget(item, checkbox)
16.获取 QListWidget 中的 QWidget 对象
for i in range(list.count()): item = list.item(i) widget = list.itemWidget(item)
17.QDialog 的 exec_() 与 self.done(int) 配合
dialog = MyDialog() r = dialog.exec_() #其中 r 为 dialog 调用 done 时返回的值
18.QLineEdit 密码时显示星号, 而不是点号
lineEdit.setStyleSheet("lineedit-password-character: 42");
19.关闭窗口函数
1).关闭主窗口并退出程序是 QApplication::exit()
2).如果是QDialog, 就 accept() 或 reject() 或 done()
3).对于所有QWidget, 就close()
20.信号
1).QObject 类及其子类可以随意发射信号 比如: 一个button中self.emit("mysignal()"), 另一个接收 QObject.connect(btn, SIGNAL("mysignal()"), self.showMsg)
2).在QObject 类中自定义信号 QtCore.pyqtSignal([int[, str[, float[, list[, dict[, tuple[, object]]]]]]]), 此种方式不能在 __init__ 中, 只能是预定义好的成员属性
3).可以用 signal.emit(var, ...), 或者 obj.emit(SIGNAL("mysignal(int...)", var, ...) 来发射带参数的信号
21.信号与槽的触发关系
1).一个信号可以与另一个信号相连
connect(Object1,SIGNAL(signal1),Object2,SIGNAL(signal1)) 即表示Object1的信号1发射可以触发Object2的信号1发射
2).表示一个信号可以与多个槽相连
connect(Object1,SIGNAL(signal2),Object2,SLOT(slot2))
connect(Object1,SIGNAL(signal2),Object3,SLOT(slot1))
3).表示同一个槽可以响应多个信号
connect(Object1,SIGNAL(signal2),Object2,SLOT(slot2))
connect(Object3,SIGNAL(signal2),Object2,SLOT(slot2))
22.设置 QTextEdit/QTextBrowser 自动滚动
cursor = textBrowser.textCursor()
cursor.movePosition(QTextCursor.End)
textBrowser.setTextCursor(cursor)
23.多线程
谈到 Python 的多线程, 不得不说全局解释器锁(GIL), Python 代码的执行由Python 虚拟机(也叫解释器主循环)来控制, Python 在设计之初就考虑到要在主循环中, 同时只有一个线程在执行, 就像单 CPU 的系统中运行多个进程那样, 内存中可以存放多个程序, 但任意时刻, 只有一个程序在 CPU 中运行, 同样地, 虽然 Python 解释器中可以“运行”多个线程, 但在任意时刻, 只有一个线程在解释器中运行
1). 设置GIL
2). 切换到一个线程去运行
3). 运行:
a.指定数量的字节码指令
b.线程主动让出控制(可以调用time.sleep(0))
c.在调用外部代码(如C/C++扩展函数)的时候, GIL 将会被锁定, 直到这个函数结束为止(由于在这期间没有Python 的字节码被运行, 所以不会做线程切换)
4). 把线程设置为睡眠状态
5). 解锁GIL
6). 再次重复以上所有步骤
守护线程一般是一个等待客户请求的服务器, 果你设定一个线程为守护线程, 就表示你在说这个线程是不重要的, 在进程退出的时候, 不用等待这个线程退出, 需要在 thread.start() 之前设置 deamon 属性
24.PyQt 的多线程实例
http://www.cppblog.com/mirguest/archive/2012/02/05/164984.html
http://ju.outofmemory.cn/entry/141128
25.PyQt 学习资料
http://www.cnblogs.com/txw1958/archive/2011/12/18/2291928.html
26.多线程使用信号和槽进行通信
27.在槽端可以使用 self.sender() 获取发送信号的对象