1 简单 Pyside 开发例子
1.1 QtDesigner 设计 UI
设置窗口类名称:
1.2 转换 UI 文件未 py 文件
pyside-uic smoking_case_browser.ui -o smoking_case_browser.py
1.3 界面的 py 文件导入使用
建立 smoking_case_browser_win.py 文件。
实现窗口如下:
import sys
import os
from PySide.QtGui import *
from PySide.QtCore import *
from smoking_case_browser import *
class SmokingCaseBrowserWindow(QMainWindow, Ui_SmokingCaseBrowser):
currentPath = "../../"
def __init__(self, parent = None):
super(SmokingCaseBrowserWindow, self).__init__(parent)
self.setupUi(self)
if __name__ == '__main__':
program = QApplication(sys.argv)
window = SmokingCaseBrowserWindow()
window.show()
program.exec_()
program.exit(0)
2 Qt Designer 的 UI 设计
2.1 Toolbar 的实现
2.1.1 UI 绘制
先添加 toobar 区域
添加 Toolbar Action
拖动 Action 按钮到 Toolbar 区域
2.1.2 事件实现
注册 toolbar 中的激活事件:
self.openTask.triggered.connect(self.openTaskList)
openTaskList 是一个函数:
def openTaskList(self):
self.fileDialog = QFileDialog(self, "Browser", "Open")
self.fileDialog.setFileMode(QFileDialog.AnyFile)
self.fileDialog.show()
2.2 滚动区域
2.2.1 UI 绘制
scrollArea 下的上下滚动实现,必须最小高度要大于绘制的区域高度。
水平滑动实现方式一样。
2.2.2 事件实现
滚动区域下面挂载新组件:
def showTaskListView(self, taskDic):
for taskkey in taskDic.keys():
pb_Task = QPushButton()
print taskkey.encode("utf-8", "Error")
pb_Task.setText(QtGui.QApplication.translate("Error",
taskkey.encode("utf-8", "Error"), None, QtGui.QApplication.UnicodeUTF8))
self.TaskListLayout.addWidget(pb_Task)
pb_Task.clicked.connect(self.showTaskDetail)
2.3 文件浏览
利用原生的 QFileDialog 浏览文件,并选择一个文件。
def openTaskList(self):
self.fileDialog = QFileDialog(self, "Browser", "Open")
self.fileDialog.setFileMode(QFileDialog.AnyFile)
self.fileDialog.show()
self.fileDialog.setDirectory(self.currentPath)
self.fileDialog.setViewMode(QFileDialog.Detail)
if self.fileDialog.exec_():
self.rootDirs = self.fileDialog.selectedFiles()
self.taskNoteFile = self.rootDirs[0]
print "选中的文件:",self.taskNoteFile
self.loadTaskDic(self.taskNoteFile)
self.showTaskListView(self.taskDic)
这里虽然返回选择值是 File 列表,但是操作界面只能选择一个文件。
2.4 文本显示
一定要注意 Python 的字符串和 PyQt 的字符串不一样,需要转换。
已全部为 utf-8 编码为例:
2.4.1 读取文本内容
def getScriptsConfig(config):
conDic = {}
cfgFile = open(config, "r", -1)
strline = "init"
while (strline <> None and strline <> ""):
strline = cfgFile.readline().strip("\n")
info = strline.decode("utf-8", "error")
# 防止文本读取结束时,“#”判断出错
if (info.__len__() > 1 and info[0] <> "#"):
print "position=", cfgFile.tell(), ";line:", info.encode("utf-8")
strarr = info.split("=")
print "getConfig,", strarr
if (strarr.__len__() > 1):
conDic[strarr[0]] = strarr[1]
cfgFile.close()
return conDic
代码说明:
info = strline.decode("utf-8", "error")
上述一行代码,必须要,否则默认用 anscii 来读取文本内容。
2.4.2 文本显示
def showTaskListView(self, taskDic):
for taskkey in taskDic.keys():
pb_Task = QPushButton()
pb_Task.setText(QtGui.QApplication.translate("Error",
taskkey.encode("utf-8", "Error"), None, QtGui.QApplication.UnicodeUTF8))
self.TaskListLayout.addWidget(pb_Task)
pb_Task.clicked.connect(lambda :self.showTaskDetail(taskkey))
代码说明:
QtGui.QApplication.translate("Error",
QtGui.QApplication.UnicodeUTF8)
taskkey.encode("utf-8",
"Error"),
None,
就是把 Python 的字符串,转换成 PyQt 的字符串。此处转换的是 utf-8 编码字符串,中文显
示正常。
这里的 taskDic 就是 2.4.1 代码中的 conDic。
问题 1:在代码里直接输入中文字符,用于显示在 UI 上时,需指定采用 utf-8 编码,否则默
认采用 ascii 编码,在 UI 显示永远是乱码。这里也可以直接转换成 QT.QApplication.UnicodeUTF8
编码。
专码方式如下:
def toQtUTF8String(src, coding):
if (coding == "utf-8"):
return QtGui.QApplication.translate("Error", src.encode("utf-8"), None,
QtGui.QApplication.UnicodeUTF8)
else:
return QtGui.QApplication.translate("Error", src, None,
QtGui.QApplication.UnicodeUTF8)
当在代码中组合字符串,字符串含有中文时,经常出现编码错误问题。为避免这个问题,
统一所有编码为 utf-8.
方式如下红色字体部分:
import sys
reload(sys)
sys.setdefaultencoding("utf8")
from test_thread import *
from request import *
代码位置:在代码文件前面导入库文件位置。
2.5 UI 刷新
先看下面的错误:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0x7716e80), parent's thread is QThread(0x3932c10), current thread is
QThread(0x77181c0)
原因,我在界面启动了一个线程,然后把该界面对象给到子线程,然后直接到子线用
UI 对象来获取组件刷新界面,然后出现上述错误提示。
注意:这里不能说明线程 1 不能直接改变线程 2 里的变量值。关于 UI 对象值为什么不
能直接改变,和 Android UI 设计一样,具体原因需要单独探讨.
初级解释:Qt 只允许主线程使用界面类,因为界面类不是线程安全的,不可重入,在
多个线程中使用可能会出现问题,因此 Qt 不建议主界面线程外的线程使用图形类和调用图
形类接口。
本章将讨论如何在其他线程刷新 UI。
2.5.1 信号
信号和槽是 Pyside 重要线程通信机制。
情况 1:都在 UI 线程,通过 UI 操作事件信号刷新 UI 界面
这种情况,例子如 2.1 Toolbar 和 2.4.2 中文本显示中的按钮点击。
self.openTask.triggered.connect(self.openTaskList)
pb_Task.clicked.connect(lambda :self.showTaskDetail(taskkey))
Pyside 标准库中,Toolbar 的 Action 和 PushButton,有信号 triggered 和 clicked。利用这
套已有信号机制,同时都在 UI 线程中,所以可以直接调用 UI 中组件对象函数刷新,例如
setText。
情况 2:在非 UI 线程,通过自建信号刷新 UI 界面。
在非 UI 线程,需要跨越线程刷新 UI 界面,如同模拟情况 1 的机制。
步骤 1:在非 UI 线程中有对象持有信号对象。
实现:
class UiHandler(QtCore.QObject):
noteSign = QtCore.Signal(str)
def __init__(self, uiWindow, parent = None):
super(UiHandler, self).__init__(parent)
self.window=uiWindow
def appendNote(self, info):
print "appendNote noteSign.emit, info=", info
self.noteSign.emit(info)
在这里,非 UI 线程需要持有 UiHandler 类对象。
UiHandler 对象持有 noteSign 对象,类型是 Signal。
函数:appendNote 中 noteSign.emit 就是用来发送信号,也可以理解为事件激活。那这
里就存在一个接受处理方。
接受处理方:可以是函数,也可以是槽。这里仅说函数。
步骤 2:在 UI 线程实现信号处理函数
def appendNote(self, info):
self.textB_Cmd.append(info)
目前看起来,两个信号发送方和信号处理函数没有任何关联,所以需要建立连接。
步骤 3:进行信号处理连接
self.uiHandler = UiHandler(self)
self.uiHandler.noteSign.connect(self.appendNote)
目前看,已经连接好信号发送方和处理方,剩下的就是使用。使用方式,将 uiHandler