资讯专栏INFORMATION COLUMN

Python3结合Sciter编写桌面程序第二节

Scholer / 3124人阅读

摘要:第二节将任务添加到队列上一个栗子只是简单实现了下网页与后台的通信你可以在这里处理任何你想要的操作你已经点到我了但由于是同一个进程,如果你做了很耗时的操作,比如下载一张图片之类的操作你会发现,窗口卡住了,一般表现为窗口泛白,出现未响应的提示但

第二节 将任务添加到队列!

上一个栗子只是简单实现了下网页与后台的通信

def clickMe(self):
    #你可以在这里处理任何你想要的操作
    self.call_function("clickCallBack","你已经点到我了!")

但由于是同一个进程,如果你做了很耗时的操作,比如下载一张图片之类的IO操作......

你会发现,窗口卡住了,一般表现为窗口泛白,出现未响应的提示......但这并不是程序真的未响应了,等图片下载完就会恢复原样。

但是,你能接受吗?

如果能的话......下面就可以不用看了,我说真的。

咳...嗯

继续

为了不卡,我选择了多进程的方式,多线程也可以,但万一这个线程死掉,会拉着主线程下水......以防万一,我选择再开一个进程作为服务进程。

from multiprocessing import Process,Queue

# 创建用于接收服务进程传递的回馈任务的队列,此队列线程安全
self.GuiQueue = Queue()
# 创建用于接收界面进程发送的任务的队列,此队列线程安全
self.ServiceQueue = Queue()
p = Process(target = startServiceP, args = ( self.GuiQueue, self.ServiceQueue ))
p.daemon = True #设置为守护进程,保证主进程退出时子进程也会退出
p.start()

为何选用Process及Queue?

单纯开启一个子进程或许还有一个更好的选择:Popen,它可以启动独立的py脚本作为子进程,也有很多方法可供选择。但我不知道应该如何通信及传参,找了一些栗子,无奈无法完全理解,只能待日后解决。

Python多进程通信方法有Queue、Pipe、Value、Array

pipe用来在两个进程间通信

queue用来在多个进程间实现通信

Value + Array 是python中共享内存映射文件的方法

最初的设计比现在复杂,共有3个进程,故弃用pipe

Value + Array的方式当时没找到,遗漏

只剩Queue......

据说Queue速度上慢一些,但以咱目前的水平,速度不是瓶颈

够用就行,不是吗?

pipe后期也会研究的就是了......

我们来看一下这个服务进程有些啥

def startServiceP(_GuiQueue, _ServiceQueue):
    """开启一个服务进程"""
    funMap = ServiceEvent( _GuiQueue )
    EventManager( _ServiceQueue, funMap ).Start()

就这么简单~

funMap 是啥? ServiceEvent 又哪来的!? EventManager 又是什么鬼??!!

等下,把刀放下......

咳...

一般来说,从界面传来的命令都是字符串,然后通过这个字符串来执行指定函数

funMap 就是存放的事先写好的函数字典

看一下ServiceEvent():

class ServiceEvent(object):
    """服务进程"""
    def __init__(self, _GuiQueue):
        self.GuiQueue = _GuiQueue

    def clickCallBack(self, msg):
        sleep(3)
        self.__putGui( "clickCallBack", msg )

    def __putGui(self, f, m = None ):
        self.GuiQueue.put({
            "fun" : f,
            "msg" : m
        })

现在可以调用 funMap.clickCallBack()

关于 GuiQueue 等会再说,先来看一下EventManager()

clickMe() 只是把要执行的任务发送给 ServiceQueue 了,但此任务不会自动执行,我们还需要一个循环来读取任务,这就是EventManager()的功能。

EventManager()核心代码:

def __Run(self):
    while self.__active == True:
        try:
            # 获取事件的阻塞时间设为1秒
            event = self.Queue.get(timeout = 1)
            getattr( self.funMap, event["fun"] )( event["msg"] )  #关键代码
        except Exception as e:
            pass

以上是服务进程的相关内容,我们再回来看一下界面该如何及时获得反馈

from threading import Thread

t = Thread(target = queueLoop, args=( self.GuiQueue, self.call_function ))
t.daemon = True
t.start()

嗯,此处我开了另一个线程来执行这个循环,老实说没想到特别好的办法,这个循环肯定不能在主线程使用,会卡界面的,开一个进程又太小题大做,折中方案,用了多线程,好在它只是遍历Queue,没啥复杂的操作......

def queueLoop( _GuiQueue, funCall ):
    guiCallBack = GuiCallBack( funCall )
    EventManager( _GuiQueue, guiCallBack ).Start()

基本和服务进程一样,不做过多解释了~

需要注意的只有 funCall这个参数,很重要,界面的事件调用全靠它。

Tis:

$(.click-me).on("click",function(){
    view.clickMe();  //view Sciter内置的对象,所有tis都可调用
})

main.py :

# 导入sciter支持,必须安装pysciter
import sciter
import ctypes
import json

from multiprocessing import Process,Queue
from threading import Thread
from EventManager import EventManager
from FunManager import ServiceEvent, GuiCallBack

# 设置dpi, 防止程序在高分屏下发虚
ctypes.windll.user32.SetProcessDPIAware(2)

def startServiceP(_GuiQueue, _ServiceQueue):
    """开启一个服务进程"""
    funMap = ServiceEvent( _GuiQueue )
    EventManager( _ServiceQueue, funMap ).Start()

def queueLoop( _GuiQueue, funCall ):
    guiCallBack = GuiCallBack( funCall )
    EventManager( _GuiQueue, guiCallBack ).Start()

class Frame(sciter.Window):
    def __init__(self):
        """
            ismain=False, ispopup=False, ischild=False, resizeable=True,
            parent=None, uni_theme=False, debug=True,
            pos=None,  pos=(x, y)
            size=None
        """
        super().__init__(ismain=True, debug=True)
        self.set_dispatch_options(enable=True, require_attribute=False)

    def _document_ready(self, target):
        """在文档加载后执行,如果设置启动画面,可以在这里结束"""

        # 创建用于接收服务进程传递的回馈任务的队列,此队列线程安全
        self.GuiQueue = Queue()
        # 创建用于接收界面进程发送的任务的队列,此队列线程安全
        self.ServiceQueue = Queue()
        p = Process(target = startServiceP, args = ( self.GuiQueue, self.ServiceQueue ))
        p.daemon = True #设置为守护进程,保证主进程退出时子进程也会退出
        p.start()
        t = Thread(target = queueLoop, args=( self.GuiQueue, self.call_function ))
        t.daemon = True
        t.start()

    def clickMe(self):
        # 点击页面上的按钮后,只将任务添加到服务队列,耗时很短,因此不会发生界面卡顿现象
        self.__putService("clickCallBack","你已经点到我了!")

    def __putService(self, f, m = None):
        """接收界面事件并转发"""
        self.ServiceQueue.put({
            "fun" : f,
            "msg" : m
        })

if __name__ == "__main__":
    frame = Frame()
    frame.load_file("Gui/main.html")
    frame.run_app()

EventManager.py:

class EventManager:
    def __init__(self, _Queue, funMap):
        self.__active = False
        self.Queue = _Queue
        self.funMap = funMap

    def __Run(self):
        while self.__active == True:
            try:
                # 获取事件的阻塞时间设为1秒
                event = self.Queue.get(timeout = 1)
                getattr( self.funMap, event["fun"] )( event["msg"] )
            except Exception as e:
                pass

    def Start(self):
        self.__active = True
        self.__Run()

    def Stop(self):
        self.__active = False

FunManager.py:

from time import sleep

class ServiceEvent(object):
    """服务进程"""
    def __init__(self, _GuiQueue):
        self.GuiQueue = _GuiQueue

    def clickCallBack(self, msg):
        sleep(3)
        self.__putGui( "clickCallBack", msg )

    def __putGui(self, f, m = None ):
        self.GuiQueue.put({
            "fun" : f,
            "msg" : m
        })

class GuiCallBack(object):
    def __init__(self, funCall):
        self.funCall = funCall

    def clickCallBack(self, msg):
        return self.funCall("clickCallBack", msg )

代码渐渐多了起来,但效果还是很让人满意的。

缺点是一不留神容易出错

源码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/40773.html

相关文章

  • Python3结合Sciter编写桌面程序第三节

    摘要:第三节协程继续基础框架搭好了,下面来正式的来一个项目吧全球设计师的作品展示平台就从这拉几张图吧,具体的网页解析方式网上有很多,在此略过,我已经取出了一些图片地址,保存在了里,这次就用这些吧。 第三节 协程!? 继续...基础框架搭好了,下面来正式的来一个项目吧 behance 全球设计师的作品展示平台 就从这拉几张图吧,具体的网页解析方式网上有很多,在此略过,我已经取出了一些图片地址,...

    fobnn 评论0 收藏0
  • 如何让Pysciter跑起来

    摘要:下载地址简介结合与编写软件使用方法安装个人使用建立的环境下载并解压下载并解压打开控制台转到解压目录比如此时就会将安装到第三方安装包的目录下我的是注册或者修改源码注册方式找到位位使用控制台注册路径使用绝对路径可以复制到然后就可以了修改 下载地址: Pysciter-GitHub Sciter 简介: 结合HTML与Python编写软件 使用方法: 安装Python3 (个人使用A...

    Hujiawei 评论0 收藏0
  • angular - 收藏集 - 掘金

    摘要:如何在中使用动画前端掘金本文讲一下中动画应用的部分。与的快速入门指南推荐前端掘金是非常棒的框架,能够创建功能强大,动态功能的。自发布以来,已经广泛应用于开发中。 如何在 Angular 中使用动画 - 前端 - 掘金本文讲一下Angular中动画应用的部分。 首先,Angular本生不提供动画机制,需要在项目中加入Angular插件模块ngAnimate才能完成Angular的动画机制...

    AlexTuan 评论0 收藏0
  • #yyds干货盘点#安全管理制度管理办法范文

    摘要:第四章安全管理制度发布第十条安全管理制度必须以正式文件的形式发布施行。第十一条安全管理制度由信息安全管理小组制订,信息安全领导小组审批发布。第二十条安全管理制度的修改与废止须经信息安全领导组织审批确认,信息安全管理部门备案。 字数 3610阅读 760评论 0赞 3《xxxx安全管理制度汇编》****制度管理办法****文...

    duan199226 评论0 收藏0

发表评论

0条评论

Scholer

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<