V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ignor
V2EX  ›  Python

Python 如何在非主线程里实现一个弹窗式对话框?

  •  
  •   ignor · 2023-10-26 21:18:15 +08:00 · 2266 次点击
    这是一个创建于 427 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在有一个 python 程序,主线程在跑 flask ,同时运行了若干个子线程,这些子线程中如果有异常,需要做个弹框提示,点击 OK 或 Cancel 决定是否发送错误报告。

    本想着直接拿 tkinter 简单弄一下就完事,但这些 GUI 框架似乎都只能在主线程里面跑 mainloop…… 如果另起一个进程来运行对话框,又涉及进程间通信,是不是有点小题大做了?想知道有没有简单点的办法呢?

    第 1 条附言  ·  2023-10-27 08:59:41 +08:00
    想通了,哪怕只有一个交互式弹框,程序的性质就变成 GUI 程序了。考虑到将来对话框可能变复杂,将 GUI 线程提到主线程,业务跑在新线程,应该是最简单有效的做法,也便于后面进一步分离 GUI 和业务
    21 条回复    2023-10-28 22:42:26 +08:00
    luckyrayyy
        1
    luckyrayyy  
       2023-10-26 21:24:54 +08:00
    进程间通信有啥小题大做的
    geelaw
        2
    geelaw  
       2023-10-26 21:26:21 +08:00 via iPhone
    最简单的方法是在最开始开一个新的线程运行原来主线程的代码。

    我不了解 tkinter 和 flask ,如果你告诉我两个都需要在主线程运行,那就必须重新编程。(但我不能理解为什么一个库要令主线程是特殊的。)
    yolee599
        3
    yolee599  
       2023-10-26 21:27:19 +08:00 via Android
    正规做法就是用进程间通讯做的啊,mainloop 只跑 gui
    ding2dong
        4
    ding2dong  
       2023-10-26 21:34:28 +08:00
    新增一个弹窗脚本,用 subprocess.run()调用该脚本,根据返回值判断是否需要发送错误报告。不知这样可否?
    darcyC
        5
    darcyC  
       2023-10-26 22:23:10 +08:00
    用 4 楼说的方法 或者就是你说的 进程间通讯,tkinter 只能运行在主线程 同时 flask 的部分功能也只能运行在主线程(见 https://stackoverflow.com/questions/31264826/start-a-flask-application-in-separate-thread )我个人推荐你用 4 楼的方法吧
    hefish
        6
    hefish  
       2023-10-26 22:36:54 +08:00
    楼上的列位大佬,OP 的意思就是不用进程间通信,但还是得把进程间通信的事儿给办了。 大家按照这个思路来。。。
    qbqbqbqb
        7
    qbqbqbqb  
       2023-10-26 23:11:10 +08:00
    tkinter 可以在子线程里用 event_generate 产生一个事件,然后主线程挂一个 handler 来处理
    ignor
        8
    ignor  
    OP
       2023-10-26 23:13:54 +08:00 via iPhone
    好吧,之前没做过 GUI ,原来弹个框是这么麻烦的事……
    想了一下,这个通信过程有点不知道该怎么处理合适?我希望能封装成一个通用的方法,每次需要弹框就调用一下,大佬们有没有好的实践思路?
    coolair
        9
    coolair  
       2023-10-26 23:27:35 +08:00
    用 pyqt 啊,跟前端一样 emit 啊,gui 主线程就能接收到其他线程 emit 来的数据,然后弹窗就可以了啊。
    ignor
        10
    ignor  
    OP
       2023-10-26 23:37:17 +08:00 via iPhone
    @coolair pyqt 的 gui 主线程也是 python 的主线程吧?这里的问题是主线程已经被 flask 占用了
    neoblackcap
        11
    neoblackcap  
       2023-10-26 23:48:14 +08:00   ❤️ 2
    @ignor 所有的带 GUI 的程序,主线程都是指 gui mainloop 所在的线程。因为只有它才能绘制图像不出错。
    事实上你应该先启动 GUI 主线程,然后再通过 worker 线程来跑 flask 。然后所有跟 GUI 相关的问题都可以归纳回如何跟主线程通信了。
    fgwmlhdkkkw
        12
    fgwmlhdkkkw  
       2023-10-26 23:54:17 +08:00 via Android
    有错误存到数据库,然后做 api ,调用浏览器打开。
    这页面里使用 websocket 连接,这个服务器可以判断有没有正在浏览的页面。
    duke807
        13
    duke807  
       2023-10-27 00:04:45 +08:00 via Android
    非主线程里实现一个弹窗式对话框,或者显示单独的窗口,都是很轻松的啊,linux 、windows 都没问题,macos 除外
    ksc010
        14
    ksc010  
       2023-10-27 00:22:05 +08:00
    有个疑问,子进程遇到异常后,还继续执行吗?
    NoOneNoBody
        15
    NoOneNoBody  
       2023-10-27 01:52:39 +08:00
    import easygui
    ...
    nuk
        16
    nuk  
       2023-10-27 02:57:26 +08:00
    简单点就直接 messagebox ,同步调用用不着 mainloop
    vialon17
        17
    vialon17  
       2023-10-27 08:41:47 +08:00   ❤️ 1
    我最近倒是做了一个主 qt+flask 的应用,直接开 qthread 包裹一下 flask 就可以了。
    弹窗直接用 qt 模板就行。
    4kingRAS
        18
    4kingRAS  
       2023-10-27 10:44:57 +08:00
    程序写少了,稍微接触过 JS 就可以很轻松理解这个问题。

    所有的 UI 框架(安卓,ios ,win32 ,swing ,qt ,Web ,js ,UE UNITY 等若干)都不支持子线程更新 UI 或者说是做不到 UI 更新线程安全,所有 UI 框架都采用 eventloop 的方式,无数人在这个地方踩过无数坑了。
    Sun 的副总裁曾经有个博客讲过这个事: https://zhuanlan.zhihu.com/p/44639688
    ignor
        19
    ignor  
    OP
       2023-10-27 11:44:44 +08:00 via iPhone
    @4kingRAS js 写了也有上万行了吧,从没想过……但可能正是因为 js 用的事件驱动,所以遇不到这样的问题
    koast
        20
    koast  
       2023-10-27 21:03:17 +08:00
    这需求多少看起来感觉有点别扭,C#里解决这个事情的方法倒是有点特别,用委托,给委托注册上处理函数,然后就可以在子线程里以主线程的身份进行函数调用了。python 里的话,感觉还是#4 的方法最简单。
    ysc3839
        21
    ysc3839  
       2023-10-28 22:42:26 +08:00 via Android
    Windows 的话直接开多个线程显示即可,Linux 我不清楚,macOS 是限制只能在主线程使用 UI 相关 API 的。
    因此如果只是为了显示个通知啥的,跨平台的方案是直接用一个独立的进程。
    另外 macOS 还有个限制,就是 fork 后如果没有 exec ,那包括 UI 在内的很多系统 API 都不可使用。
    @4kingRAS Win32 控件是允许不同线程进行更新的,因为控制都是通过窗口消息进行的,而窗口消息处理函数本身会保证只会在窗口所在线程执行。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5438 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 08:40 · PVG 16:40 · LAX 00:40 · JFK 03:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.