PyUi 全流程

PyUi 全流程

社蕙 95 2023-02-14

C++版本的Qt文章参见http://blog.megumism.com/archives/e04-qtguidemd

对于Qt的python来说,有好几个包,包括PyQt5、PySide2、Pyside6,但是看下来确实他们的api大同小异,我实际使用的是Pyside6,但是他们的其中任何一个的解决方案都适用于Pyside6,因此下面看到奇怪的import不用觉得奇怪。

一、安装与配置

待填坑

二、参考

主要是一些入门的参考,在具体问题上会引用其他参考。

教程

  • Github - muziing/PySide6-Code-Tutorial:实用性上我觉得一般,demo本身太简单了,对于api本身介绍也很少,基本上是把组件过了一遍,很泛。但是比没有好。
  • PYTHON GUIS:好用!入门但是图文并茂。

文档

三、工具流程

首先找到你的Pyside6文件夹,例如在conda上安装的就是C:\Users\megumism\.conda\envs\pyside6\Lib\site-packages\PySide6,在这里面可以找到编译好的Qt的各类工具(这他妈不比官网上不编译的依托答辩要好?)

如果你是conda安装的,那么使用conda prompt,激活环境之后可以直接按照下面的命令做,否则你需要手动具体指向可执行文件来运行命令,所有工具都应该在上面你找到的这个文件夹里面。

1. 可视化ui创建

使用Designer.exe,相关的教程网上非常的多,因为不论是什么语言,这一步都是在拖动、设计等等。

2. 编译ui

Designer.exe生成的是一个xml文件,后缀是.ui,可以使用uic工具将它变成.py文件

pyside6-uic ..\UiParts\MainWindow.ui -o ..\UiParts\MainWindow.py

生成的py里面,字符串都是unicode编码,据说可以使用 ascii2uni 这个程序将unicode编码转换掉,但是我没有找到windows能用的版本。

3. 国际化

来了,麻烦的部分来了,如果不做国际化可以跳过,免受烦恼。

以下部分使用的均为 pyside6 6.4.2,我认为这里面有很大一部分问题是bug,后期应该是要修复的

参考:

3.1. 生成ts文件

如果是手写的UI部分,首先需要将需要国际化的文本包裹在self.tr()中,tr是所有Qt部件都具有的一个属性,负责处理翻译。对于需要处理的文件,使用如下命令:

pyside6-lupdate .\mainUI.py -ts .\trans.ts

另一种方法是使用.pro文件,例如创建一个叫cmpl.pro的工程文件,并手动写入下面的内容:

SOURCES = .\mainUI.py
TRANSLATIONS = .\trans.ts

参考stackoverflow - .pro file for Pyside for QT internationalisation,你必须手动加入所有需要翻译的文件。

多个文件用空格分离,或者用\换行

然后运行:

pyside6-lupdate cmpl.pro

一些问题:

使用pro文件在我这里并不成功,我这里直接会报错UnicodeDecodeError: 'utf-8' codec can't decode byte xxx——在forum qt - How to use pyside6-lupdate 上,有人讨论了所遇到的类似的问题,解决的方案就是换用第一种方法

在第2步生成的py里面,字符串都是unicode编码,并且用QCoreApplication.translate包裹,因此没有办法被lupdate识别——在Stackoverflow - Pyside6 lupdate won't recognise marker "translate", only "tr" 上有人指出了这个问题,回答给出的解决方案是应该去翻译原始的.ui文件而不是翻译.py文件。 如果使用pro,那么添加FORMS = xxx.ui

4.2. 翻译

打开linguist.exe,打开生成的.ts文件,开始翻译工作,linguist软件的使用可以参考官方的教程 Qt Linguist Manual: Translators

如果翻译文件比较多,可以合并

lconvert -i eng-chs.ts eng-chs1.ts -o all.ts

4.3. 编译ts文件

pyside6-lrelease .\trans.ts

或者直接在linguist里面点击文件-发布(release),会生成一个pm文件,这个文件才是能用的。

4.4. 使用qm文件

TRANSLATOR = QtCore.QTranslator()
TRANSLATOR.load('trans')                   #注意,可以不需要 .pm 后缀
app = QtWidgets.QApplication(sys.argv)    
app.installTranslator(TRANSLATOR)          #非常重要的一步,为 app 安装 TRANSLATOR,如果不安装,是没有效果的
win = MainWindow()
win.show()
sys.exit(app.exec_())

四、实用问题与解决方案

如何刷新

作为一个Ui,它不会主动的刷新,这一点表现为如果在textBrowser.append之后制造一个阻塞,比如说Popen并且Wait(),文本是不会刷新的,因此有必要手动刷新:

from PyQt5.QtWidgets import QApplication
QApplication.processEvents()

特别的,对于FigureCanvasQTAgg,强制刷新的办法是:

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
FigureCanvas.draw()

其他参考:Python FigureCanvasQTAgg.update示例

组件的加载与卸载

# 一般都是引用.ui文件生成的那个.py,然后setupUi
self.setupUi(self)

# 看一下卸载对象前后
print(self.RightParamBox.children())
self.SimulationSettings.setParent(None)
print(self.RightParamBox.children())

# 重新加载
print(self.RightParamBox.children())
self.gridLayout_2.addWidget(self.SimulationSettings,1,1)
print(self.RightParamBox.children())

输出可以看到是成功的加载、卸载,并且UI上也是可以反映出来的。使用gridLayout的原因是它可以指定位置把元素放回去,即使是一行一列,也比VBox或者HBox更好,它们只能将元素加载在最后(最下面或者最右边);如果用这两种Layout,别人的建议一般是:手动把这个元素之前的都卸载了,再手动加载。

[<PySide6.QtWidgets.QGridLayout(0x23c29238f90, name = "gridLayout_2") at 0x0000023C2FEEE100>, <PySide6.QtWidgets.QWidget(0x23c2d6cb540, name="widget") at 0x0000023C2FEEE180>, <PySide6.QtWidgets.QWidget(0x23c2d6cd1c0, name="SimulationSettings") at 0x0000023C2FEEE4C0>]
[<PySide6.QtWidgets.QGridLayout(0x23c29238f90, name = "gridLayout_2") at 0x0000023C2FEEE100>, <PySide6.QtWidgets.QWidget(0x23c2d6cb540, name="widget") at 0x0000023C2FEEE180>]
[<PySide6.QtWidgets.QGridLayout(0x23c29238f90, name = "gridLayout_2") at 0x0000023C2FEEE100>, <PySide6.QtWidgets.QWidget(0x23c2d6cb540, name="widget") at 0x0000023C2FEEE180>]
[<PySide6.QtWidgets.QGridLayout(0x23c29238f90, name = "gridLayout_2") at 0x0000023C2FEEE100>, <PySide6.QtWidgets.QWidget(0x23c2d6cb540, name="widget") at 0x0000023C2FEEE180>, <PySide6.QtWidgets.QWidget(0x23c2d6cd1c0, name="SimulationSettings") at 0x0000023C2FEEE4C0>]

其他方法可以参考How to dynamically add and remove widgets and layouts in PyQt5

matplotlib嵌入

基本操作参考将 matplotlib 嵌入 PyQt5 - 知乎

关于嵌入后的大小调整问题,参考How do I auto fit a Matplotlib figure inside a PySide QFrame? - stackoverflow