python黑乎乎的命令行输出太乏味了。本篇用pyui4win开发一个有动画界面的python执行程序。 当用户选择执行时,动画开启,开始转圈,直到python功能函数执行完成。在执行过程中,界面上的信息也在不断更新。 为了让界面不被卡住,采用了多线程技术。 执行的功能函数在非界面线程执行。从结果可以看到,界面上的动画很流畅。
目录
- 执行效果
- 界面设计
- 代码编辑与调试
- 使用多线程保证界面流畅
- 执行程序
- 这是怎么回事?
执行效果
用户选择执行时,动画开启,开始转圈,直到python功能函数执行完成。在执行过程中,界面上的信息也在不断更新。最后动画停止。
界面设计
主界面xml为MainFrame.xml,在skin中。所有的界面xml和界面图片都在该目录下。
...
<VerticalLayout bordersize="2" height="654" bkimage="file='dialog_background_new.png' corner='95,715,2,255'">
<HorizontalLayout name="HLU_caption" height="95">
<HorizontalLayout width="179" height="95">
<Label float="true" pos="43,11,0,0" width="72" height="72" bkimage="xiaoniu.png" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
</HorizontalLayout>
<VerticalLayout>
<HorizontalLayout height="24">
<VerticalLayout />
<VerticalLayout width="79">
<HorizontalLayout>
<Button name="minbtn" width="25" height="24" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='sysmenu_.png' source='0,0,24,23'" hotimage="file='sysmenu_.png' source='25,0,49,23'" pushedimage="file='sysmenu_.png' source='50,0,74,23'" />
<Button name="maxbtn" width="25" height="24" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='sysbtn_max.png' source='0,0,24,23'" hotimage="file='sysbtn_max.png' source='25,0,49,23'" pushedimage="file='sysbtn_max.png' source='50,0,74,23'" />
<Button name="restorebtn" visible="false" width="25" height="24" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='sysbtn_restore.png' source='0,0,24,23'" hotimage="file='sysbtn_restore.png' source='25,0,49,23'" pushedimage="file='sysbtn_restore.png' source='50,0,74,23'" />
<Button name="closebtn" width="29" height="24" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='sysmenu_close.png' source='0,0,28,23'" hotimage="file='sysmenu_close.png' source='29,0,57,23'" pushedimage="file='sysmenu_close.png' source='58,0,86,23'" />
</HorizontalLayout>
</VerticalLayout>
</HorizontalLayout>
(中间一部分省略)...
<TabLayout name="TLU_client">
<VerticalLayout name="DriverDiagnoseTab">
<Animation name="AnimationJuhua1" enabled="false" float="true" pos="350,150,0,0" width="95" height="95" framesize="95,95" animationimage="fw_update_soft_checking.png" frameinterval="200" />
<RichEdit name="txtDiagnose" bordersize="1" bordercolor="#00008000" inset="1,1,1,1" vscrollbar="true" hscrollbar="true" autohscroll="true" autovscroll="true" />
<HorizontalLayout height="62">
<VerticalLayout />
<VerticalLayout width="465">
<HorizontalLayout>
<Button name="btnOpenLog" text="打开日志" float="true" pos="0,10,0,0" width="134" height="42" textcolor="#00FFFBF0" disabledtextcolor="#FFA7A6AA" font="1" align="center" normalimage="reboot.png" hotimage="file='reboot-hot.png' corner='20,20,20,20'" pushedimage="reboot.png" focusedimage="reboot.png" />
<Button name="btnClearLog" text="清空日志" float="true" pos="155,10,0,0" width="134" height="42" textcolor="#00FFFBF0" disabledtextcolor="#FFA7A6AA" font="1" align="center" normalimage="reboot.png" hotimage="file='reboot-hot.png' corner='20,20,20,20'" pushedimage="reboot.png" focusedimage="reboot.png" />
<Button name="btnExcute" text="执行" float="true" pos="310,10,0,0" width="134" height="42" textcolor="#00FFFBF0" disabledtextcolor="#FFA7A6AA" font="1" align="center" normalimage="reboot.png" hotimage="file='reboot-hot.png' corner='20,20,20,20'" pushedimage="reboot.png" focusedimage="reboot.png" />
</HorizontalLayout>
</VerticalLayout>
</HorizontalLayout>
</VerticalLayout>
</TabLayout>
...
代码编辑与调试
界面设计完成后,选择设计器中的编辑->生成python类来生成一个界面类MainFrame.py。将该文件拷贝到父目录中。执行Launcher.exe可以看到界面效果。
现在我们加入业务处理代码。
# 界面事件处理
def OnNotify(self, sendor, sType, wParam, lParam):
# 用户点击事件
if sType == DUI_MSGTYPE_CLICK:
...
elif sendor == "btnExcute":
self.ExecutePython()
...
...
# 真正业务函数
def ExecutePython(self,):
CommonUtils.ReverseToExePath()
ISOTIMEFORMAT='%Y-%m-%d %X'
self.ShowAndLog(time.strftime( ISOTIMEFORMAT, time.localtime() ))
i = 0
while i < 20:
self.AppendAndLog('等待了%d秒' % i)
time.sleep(1)
i = i + 1
self.AppendAndLog('成功')
执行程序,点击执行按钮,发现界面卡住了。过了20秒界面才正常。这是这个业务函数需要执行20秒导致的。
使用多线程保证界面流畅
当用户选择执行时,动画开启,开始转圈,直到python功能函数执行完成。在执行过程中,界面上的信息也在不断更新。 为了让界面不被卡住,采用了多线程技术。执行的功能函数在非界面线程执行。下面分析一下代码。
# 界面事件处理
def OnNotify(self, sendor, sType, wParam, lParam):
# 用户点击事件
if sType == DUI_MSGTYPE_CLICK:
...
elif sendor == "btnExcute":
t = threading.Thread(target=PyThreadPythonExecute,args=(self,)) #启动了业务线程
t.start()
...
注意ExecutePython是真正的业务处理函数,在该python类中定义。PyThreadPythonExecute只是真正业务函数的代理,并且在该python类外定义。 这时多线程编程中常用的方法。
代理函数,在python类外定义:
# 业务线程实际上只是一个代理函数
def PyThreadPythonExecute(PyClassInstance, ):
try:
PyClassInstance.StartAnimation()
PyClassInstance.ExecutePython()
except Exception, e:
PyLog().LogText(str(e))
PyClassInstance.StopAnimation()
PyLog().LogText('PyThreadExecute exit')
执行程序
执行Launcher.exe或者DebugEntry.py,点击执行按钮。现在动画开启,开始转圈,直到python功能函数执行完成。 在执行过程中,界面上的信息也在不断更新。界面很流畅。
这是怎么回事?
有趣的是,在pyui4win中,使用ctyps中的界面相关函数,会导致程序崩溃。日志里头输出是
'exceptions.WindowsError': exception: access violation writing 0x0000000C...错误原因还没有找到。 比如在这个例子中,对打开日志的处理中:
...
elif sendor == "btnOpenLog":
if os.path.isfile(PyWinUtils().GetExeDirectory() + '\\applog.ini'):
shell32 = ctypes.windll.LoadLibrary("shell32.dll");
#shell32.ShellExecuteA(None,'open', 'notepad',PyWinUtils().GetExeDirectory() + '\\applog.ini','',1);
PyWinUtils().ShellExcute(0, 'open', PyWinUtils().GetExeDirectory() + '\\applog.ini', '', '', 1)
else:
UICommon.ShowMessageBox(self.GetHWnd(), '错误', '日志文件不存在')
...
如果使用shell32.ShellExecuteA就会崩溃。你知道是什么原因吗?
不要使用ctyps中的界面相关函数,否则会导致程序崩溃。该原因还没有找到。但是,针对常用的界面函数,如MessageBox等,请使用PyWinUtils类中的对应函数来源:oschina
链接:https://my.oschina.net/u/159675/blog/156815