刚接触 UI 开发没多久,很多东西还不知道 用的 pyqt5,一般创建界面都是在主线程里创建,遇到耗时的任务时开个 QThread 子线程来处理不是吗。
我想要达到分离界面和逻辑的目的,可能我水平次,所以能想到的解决办法就是: 在子线程里创建主界面,主线程里处理逻辑。 然后两者通过一个 Queue 消息队列链接。比如:界面输入完数据后,往 queue 里放一个自定义消息 MSG_BTN_CLICK,然后主线程里通过:
while True:
event_id , parameter = event_queue.get()
if event_id == MSG_BTN_CLICK:
# 代码逻辑
如果界面要更新界面数据的话,就发送个 MSG_UPDATE_LIST 之类的消息附带上窗口对象,然后主线程丽获取数据后发送个 qt 信号过去更新数据。
不知道这个想法可不可取?谢谢指教!
1
0312birdzhang 2018-11-08 09:03:13 +08:00 via iPhone
emmmm,信号和槽
|
2
ragnaroks 2018-11-08 09:11:13 +08:00
在 UI 线程(往往是主线程,但不能相等)给个成员变量并监听变动,new 子线程并给予其对本线程(上文的 UI 线程)的引用,你的逻辑完成后修改 UI 线程的变量,主线程处理后续逻辑
|
3
baixiangcpp 2018-11-08 09:19:12 +08:00
|
4
sc3263 2018-11-08 10:12:18 +08:00
Qt 对界面相关的的操作只能在主线程中完成。你可以试一下这个做法: https://stackoverflow.com/questions/11033971/qt-thread-with-movetothread
|
5
hakono OP @baixiangcpp 感谢回复。看来官方的确还是建议只在主线程里跑 UI 啊。
不过有点搞不懂,文档里明确说了 widgets 这些不能在子线程里跑起来 ``` All widgets and several related classes, for example QPixmap, don't work in secondary threads ``` 但我在子线程里创建窗口显示按钮都很正常没问题... |
6
ChenFanlin 2018-11-08 10:24:42 +08:00
看到上面说只有 UI 线程才能操作界面,那和 Android 差不多.
通常都是在新线程处理数据,然后处理完了回到 UI 线程处理界面. |
7
Chenamy2017 2018-11-08 10:25:09 +08:00
子线程不能跑 UI。既然是处理逻辑,子线程就可以啊,搞不清楚你为什么要反着来。
|
8
pkoukk 2018-11-08 10:33:55 +08:00
据我所知,一般都不允许子线程处理 UI,
逻辑很简单,多个子线程处理 UI 的时候由于次序问题很容易导致界面错乱,或者使用了被其它线程释放掉的资源导致崩溃 |
9
hakono OP @Chenamy2017 主要是想要彻底把界面和逻辑分开。因为感觉界面做主线程,处理逻辑开个子线程的话,界面代码和逻辑代码实际上依旧是混合在一起的。
而反着来的话,界面类里就只有界面代码的,界面有操作的话,就按钮点击后就发送个类似于 MSG_BTN_CLICKED 消息到队列里。这样主线程就能捕获到这个消息,处理了逻辑了,这样界面和处理逻辑就彻底分开了。 当然,因为我从没做过界面开发,所以可能想歪了。所以想来征求下大家的意见 然后现在我有个更疑惑的问题,看了大家都在说子线程不能操作 UI,,就是 pyqt 似乎真的能子线程创建 UI 没问题? 随便写了个最简单的代码例子,似乎跑起来没问题? https://gist.github.com/ShinonomeHana/be8ec0bf77da9503fd2076837d2b8522 |
10
Rizio 2018-11-08 13:27:01 +08:00
Android 子线程是可以操作 ui 的,但是要在创建 Activity 的时候用非常快的速度去修改。
原理就是做 ui 操作时会检查当前线程是否和 Activity 的创建线程(也就是主线程)是否一致,不一致就抛异常。 不知道其他框架是不是也是这么干的。 |
11
sc3263 2018-11-08 13:58:28 +08:00
@hakono Qt 在 windows 下允许用非主线程创建 QApplication 对象并执行事件循环。在 mac 下这样做会出错。
|
12
dychenyi 2018-11-08 14:13:07 +08:00
@Rizio
估计子线程后面没有对 UI 的操作了所以没报错,你在线程 start 之后立马去创个按妞试试看,估计就报错了吧。 |
13
cosven 2018-11-08 14:13:32 +08:00
> 一般创建界面都是在主线程里创建,遇到耗时的任务时开个 QThread 子线程来处理不是吗。
嗯,用线程或者进程 > 随便写了个最简单的代码例子,似乎跑起来没问题? 有问题的,比如 macOS 下,这样的程序会直接崩溃(我在 gist 下写了更多详情) > 我想要达到分离界面和逻辑的目的 LZ 有没有想过这么几个问题 1. 界面和逻辑分离有哪些好处呢? 2. 哪些算界面部分,哪些算逻辑部分? 3. 界面操作(改变按钮颜色、调整组件宽度、组件动画)这些算逻辑还是算界面? -------------------------------- 关于界面和逻辑分离的观点,一个 GUI 程序,大部分逻辑就是两种情况: 1. 获取数据 -> 刷新界面 2. 用户操作界面 -> 修改数据 **大部分**情况,**界面和逻辑是密不可分的,分离界面和逻辑是个错误的决定**,分离只会让你的代码变得复杂。 还有一部分场景:Qt 提供了一种 Model/View/Delegator 的编程模式,它解决的问题是复杂业务场景下的界面逻辑分离。 |
14
hakono OP |
15
hakono OP @cosven 多谢指点,没考虑到跨平台的问题,哈哈。 的确在 macOS 下会报错。
经你一说的确想通了,对界面的变更操作的确不好区分,很多界面的操作逻辑和 UI 是密不可分的,如果界面要做一些变更就给主线程发送个 MSG 的话,的确反倒会造成主线程那边代码变得相当复杂 |
16
q397064399 2018-11-08 14:26:55 +08:00
@cosven #13 分离界面的逻辑 前提是 界面操作要提供一堆声明式的接口 来满足逻辑的需要,逻辑需要做的是描述我想要的界面是什么样的,而不是关心界面是如何被操作的。
|
17
arzterk 2018-11-08 16:15:08 +08:00
做 UI 的大致都只能主线程刷新界面,子线程处理完了丢个消息刷新界面,原因是许多 OS 的图形子系统底层都有锁,防止出现图形绘制不正确之类的
PS.我之前做 MFC,子线程是不能直接调用 GDI API 的,强行 ShowDialog 会造成 GDI contex 错误 |
18
SilentHill 2018-11-08 17:33:29 +08:00
基本 90%的 ui 框架都不允许非 ui 线程操作界面。
我觉得可以做一个 model,映射界面数据,然后 ui 和后台线程对该 model 进行更新,ui 在每次事件循环中根据 model 来更新界面。。好像就是 mvc 的思路,好久没用 qt 了。。都忘的差不多了。 |
19
shoujiaxin 2018-11-08 20:25:21 +08:00 via iPhone
既然都 Qt 了为什么不用信号和槽呢?这俩本来就可以跨线程的呀。把自己的类 moveToThread,就运行在子线程了,数据用信号来传
|
20
innoink 2018-11-08 21:29:19 +08:00 via Android
彻底分开用 qtquick,qml 写界面
|
21
ysc3839 2018-11-09 01:20:31 +08:00 via Android
Windows 也许可以,但不建议这么做。macOS 不行。
印象中 macOS 非主线程或者 fork 出来的子进程不能使用 UI 相关的功能。 |
22
zjddp 2018-11-09 09:47:37 +08:00
是 V 和 M 双向绑定的意思吗?
|
23
lyc8801 2018-11-09 10:55:58 +08:00
我都是在主线程创建 UI,然后子线程处理逻辑,两者通过信号槽来传递消息...话说 QT 的信号槽真的好用
|
24
largecat 2018-11-11 08:19:00 +08:00 via Android
主线程跑 UI,子线程跑任务,通过信号槽联系,
|