热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

第二节将任务添加到队列!上一个栗子只是简单实现了下网页与后台的通信defclickMe(self):#你可以在这里处理任何你想要的操作self.call_function(clic

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

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

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 = _GuiQueuedef 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 Threadt = 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 jsonfrom 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 = Truet.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 = Falseself.Queue = _Queueself.funMap = funMapdef __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:passdef Start(self):self.__active = Trueself.__Run()def Stop(self):self.__active = False

FunManager.py:

from time import sleepclass ServiceEvent(object):'''服务进程'''def __init__(self, _GuiQueue):self.GuiQueue = _GuiQueuedef 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 = funCalldef clickCallBack(self, msg):return self.funCall('clickCallBack', msg )

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

缺点是一不留神容易出错

源码



推荐阅读
  • python3 nmap函数简介及使用方法
    本文介绍了python3 nmap函数的简介及使用方法,python-nmap是一个使用nmap进行端口扫描的python库,它可以生成nmap扫描报告,并帮助系统管理员进行自动化扫描任务和生成报告。同时,它也支持nmap脚本输出。文章详细介绍了python-nmap的几个py文件的功能和用途,包括__init__.py、nmap.py和test.py。__init__.py主要导入基本信息,nmap.py用于调用nmap的功能进行扫描,test.py用于测试是否可以利用nmap的扫描功能。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • 一、死锁现象与递归锁进程也是有死锁的所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作 ... [详细]
  • 我正在尝试使用scrapycrallsingle运行完美运行的scrapy蜘蛛,但我无法在python脚本中运行它.主要问题是从不执行SingleBlogSpider.parse方 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • javascript二叉树基本功能实现
    都是常用的功能。删除是最复杂的。。test ... [详细]
  • Matlab 中的一些小技巧(2)
    1.Ctrl+D打开子程序  在MATLAB的Editor中,将输入光标放到一个子程序名称中间,然后按Ctrl+D可以打开该子函数的m文件。当然这个子程序要在路径列表中(或在当前工作路径中)。实际上 ... [详细]
  • 时域|波形_语音处理基于matlab GUI音频数据处理含Matlab源码 1734期
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了语音处理基于matlabGUI音频数据处理含Matlab源码1734期相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
手机用户2502877953
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有