整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

黑白激光办公打印,我只选兄弟

天活动在职场中的宝宝们应该对打印机熟的不能在熟了,可是同是职场中的打印机,有的打印机就能顶个人用,为公司大大节省了成本。有的仅仅就是一台机器。为什么同样的价格却能收获不一样的效果呢?那么小编就把这个神奇的打印机介绍给大家吧。

就是这款兄弟MFC-7480D黑白激光多功能一体机,集打印、复印、扫描、传真为一体,重要的是该款打印机是自动双面打印,节省成本和人力,耗材采用鼓粉分离设计,既环保更省单页成本。

这是一款典型的商务优选打印机,为什么这么说呢?每分钟高达30页的打印和复印速度,比起其他的打印机是不是快很多,支持一键式身份证复印,方便快捷,采用直通式纸道设计,随机配置2600页大容量粉盒。让您用的舒心。

速度快,那么质量怎么样呢?这想必是大家更关心的事了,它的打印质量和打印速度是成正比的。高达600*600dpi的高清分辨率,优化可达1200dpi的分辨率,只要您有需求,它就能满足您对产品的高要求,

一键式的身份证复印,操作简捷只需一键,进入身份证复印模式,简化操作简洁明了,直通式纸道设计提供厚纸打印,信封打印等,支持的厚度60~165g/m*2,再也不需要为了一张厚纸而大费周折了,加上高速的传真效率,这款打印机完全符合了办公打印机的所有要求,您觉得呢?同样的价位,您的选择是它么?

价格:1799元

购买价格:https://item.jd.com/2372354.html

FC控件:

Windows标准控件:

windows标准控件由Windows 操作系统提供,在Windows 95中还提供了一些新增的控件。所有这些控件对象都是可编程的,我们可以使用Visual C++提供的对话框编辑器把它们添加到对话框中。Microsoft基础类库(MFC)提供了封装这些控件的类,它们列于表6.1。

在MFC 中,类CWnd是所有窗口类的基类,很自然的,它也是所控件类的基类。

Windows标准控件在以下环境下提供:

windows 95

Windows NT 3.51及以后版本

win32s 1.3

注意:visual C++4.2及以后版本不再支持Win32s.

MFC基本常用控件基本用法

VC++控件工具箱:

按钮(Button)

用来接收用户的命令,应用程序在接收到用户命令后,通常需要进行一些后台工作。按钮可以响应单击或双击动作,在按钮接收到鼠标动作后,向其父窗口发送相应的控件通知,用户可以对这些控件通知进行消息映射,从而进行相应的处理。

在一个对话框中,可以定义一个默认按钮,这只要选中按钮属性中的“Default”选项。如果在对话框活动的时候按下了Enter键,则等同于单击了默认按钮。MFC提供了CButton类支持按钮控件。

复选框(Check Box)

用来显示某种可能的选择,该项选择是独立的,用户可以选中或取消该选项。在选项被选中的时候核选标记出现,选项被取消时核选标记消失。MFC中由CButton类对核选框进行支持,用户可以通过SetCheck()函数和GetCheck()函数设置或获取核选框当前的状态

BST_UNCHECKED==((CButton*)GetDlgItem(IDC_CHECK_RES1))->GetCheck()
((CButton*)GetDlgItem(IDC_CHECK_RES1))->SetCheck(true);		

编辑框(Edit Control)

用来接收用户输入的字符串通过选择编辑框的选项,编辑框可以接收字符串、数字、密码等;编辑框还可以设置成接收多行字符串的模式;可以自动进行大小写转换。编辑框可能向父窗口发送多种控件通知,如果用户需要,可以对这些控件通知进行处理。MFC提供了CEdit类支持编辑框控件

GetDlgItem(IDC_***)->SetWindowText(Cstring);
 
GetDlgItem(IDC_***)->GetWindowText(Cstring);

组合框(Combo Box)

列表框和编辑框的组合,用户除了可以在列表中对已经存在的选项进行选择外,还可以输入新的选择。MFC提供了CComboBox类对组合框控件进行支持。

CComboBox * AAA = (CComboBox*)(GetDlgItem(IDC_***));
 
AAA->AddString(_T("***"));
 
AAA->SelectString(0, _T("***"));
 
AAA->SetCurSel(0);
 
 
int nSel = AAA->GetCurSel();
 
AAA->GetLBText(nSel, Cstring);
 
if(strType.Trim() == _T("***"))

列表框(List Box)

用来选择一系列的可能选择,用户通过滚动条可以在这些选择中浏览。在列表框中,可以进行单项选择,也可以进行多项选择,这取决于用户在控件属性对话框中的设置。MFC提供了CListBox类对列表框控件进行支持。

组成框(Group Box)

用来包围具有逻辑关系的一组控件,在这些控件的周围加上边界和标题。需注意的是,组成框仅仅是在视觉效果上对控件进行“成组”,真正的“成组”工作还需要另外一些工作。仅仅在视觉上展现出一组的逻辑关系,并不添加任何代码

单选按钮(Radio Button)

用来选择某种可能的选择,与 (Check Box)复选框不同,该选项不是独立的

一般是几个单选按钮组成一组同组中的单选按钮仅有一个按钮被选中

MFC同样使用CButton类对单选按钮控件进行支持

SetCheck()函数和GetCheck()函数对单选按钮也是适用的。

静态文本(Static Text)

用来在指定的位置显示特定的字符串一般用来标识附近另一个控件的内容。显示在静态文本控件中的字符串一般不再改变,但是在需要的时候,必须为其指定一个不同食物ID号,通过调用相应的函数来进行设置。MFC提供了CStatic类支持静态控件

图形控件(Picture Control)

显示位图(Bitmap)和图标(Icon),图形绘制与显示,主要是其方形的区域适合显示,同样方形区域也可利用(Static Text)静态文本框。

滚动条(Scroll Bar)

这包括水平滚动条和垂直滚动条,除了在视觉效果上的方向不同外,水平滚动条在被滚动时发生WM_HSCROLL消息,而垂直滚动条在被滚动时发送WM_VSCROLL消息MFC提供了CScrollBar进行支持

滑块控件(Slider Control)

通常用来在程序中接受一系列离散的数值

用户可以设置滑块控件的取值范围,并可以为控件加上刻度标记以显示特定位置的含义。

MFC提供了CSliderCtrl类进行支持

微调按钮(Spin Button)

包括一对紧靠在一起的上下箭头,使用微调按钮可以增大或者缩小某个特定的数值。

微调按钮往往都需要一个“伙伴”控件,这通常都是一个编辑框

当微调按钮的向上箭头被单击时,编辑框中的数字就增大;反之则减小MFC提供了CPinButtonCtrl类进行支持

进度条(Progress Control)

进行一项需要较长时间的操作时来反应当前的进度

当操作的进度不断前进时,进度条就用特色颜色填充进度条框。用户可以设定进度条的范围和当前位置。

MFC提供了CProgressCtrl类进行支持

CProgressCtrl* progressbar = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS_1);
 
progressbar->SetRange(0, 4);
 
progressbar->SetPos(0);

热键控制(Hot Key)

热键控件看起来就像一个编辑框,但是在热键控件中能够立刻反应用户刚刚按下的键组合,这在设置程序的热键时特别有用。

热键控件只是在“视觉”上显示了按键组合,设置热键的工作还需要用户添加代码完成。

MFC提供了CHotKey类进行支持

列表控制(List Control)

按一定的排列顺序显示一系列带图标的字符串。

列表控件提供了四种显示模式:大图标(Icon)、小图标(Small Icon)、列表(List)和报表(Report)

用户可以向列表控件中添加新的项,也可以控制列表控件的显示模式。

MFC提供了CListCtrl类进行支持

初始化:

 struct INFO { int id; CString time; CString type; }info; CString id; int nRow=0; id.Format(_T("%d"), info.id); m_ListCtrl.InsertItem(nRow,id); m_ListCtrl.SetItemText(nRow, 1, info.time); m_ListCtrl.SetItemText(nRow, 2, info.type); nRow ++;


添加记录:

 
struct INFO
 
{
 
int id;
 
CString time;
 
CString type;
 
}info;
 
CString id;
 
int nRow=0;
 
id.Format(_T("%d"), info.id);
 
m_ListCtrl.InsertItem(nRow,id);
 
m_ListCtrl.SetItemText(nRow, 1, info.time);
 
m_ListCtrl.SetItemText(nRow, 2, info.type);
 
nRow ++;

树形控件(Tree Control)

显示一系列项目的层次关系最典型的例子是显示磁盘上的文件与文件夹

如果有子项目的话,单击树形控件中的项目可以展开或者收缩其子项目

MFC提供了CTreeCtrl类进行支持

初始化

   void CConfigDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码  CRect rcWnd; GetClientRect(&rcWnd);  CRect rcChild = rcWnd; rcChild.top = rcWnd.top+10; rcChild.bottom -=10; rcChild.left += 160; rcChild.right -= 10; CRect laneRect = rcChild;  if(::IsWindow(m_1Dlg)) m_1Dlg.MoveWindow(rcChild);  if(::IsWindow(m_2Dlg)) m_2Dlg.MoveWindow(rcChild);   if ( ::IsWindow( pTree.GetSafeHwnd() ) ) { pTree.MoveWindow(rcWnd.left+10,rcWnd.top+30,130,350,1); } }

树节点切换

   void CConfigDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 HTREEITEM hSelected=pNMTreeView->itemNew.hItem; if(hSelected!=NULL) { pTree.SelectItem(hSelected); int nDat = pTree.GetItemData(hSelected);  switch(nDat) { case 1: /*MessageBox(_T("YNAME"));*/ if(m_pPreWnd) m_pPreWnd->ShowWindow(SW_HIDE);  m_1Dlg.ShowWindow(SW_SHOW); m_pPreWnd = &m_1Dlg; break; case 3: /*MessageBox(_T("XNAME"));*/ if(m_pPreWnd) m_pPreWnd->ShowWindow(SW_HIDE);  m_2Dlg.ShowWindow(SW_SHOW); m_pPreWnd = &m_2Dlg; break;  default: break; } } *pResult = 0; }

改变位置

   void CConfigDlg::OnSize(UINT nType, int cx, int cy) { CDialog::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码  CRect rcWnd; GetClientRect(&rcWnd);  CRect rcChild = rcWnd; rcChild.top = rcWnd.top+10; rcChild.bottom -=10; rcChild.left += 160; rcChild.right -= 10; CRect laneRect = rcChild;  if(::IsWindow(m_1Dlg)) m_1Dlg.MoveWindow(rcChild);  if(::IsWindow(m_2Dlg)) m_2Dlg.MoveWindow(rcChild);   if ( ::IsWindow( pTree.GetSafeHwnd() ) ) { pTree.MoveWindow(rcWnd.left+10,rcWnd.top+30,130,350,1); } }

属性表控件(Tab Control)

包含大量的控件,可以满足用户显示或者获取大量数据分类显示的要求,典型例子是Windows任务栏每个程序标签。

每个属性表又分为好几个属性页,这些属性页由各自的标签进行区分,这些属性页中都可以包容其他控件

在显示属性表的时候,一次只能够显示一个属性页的全部内容,同时显示其他属性页的标签,用户通过单击标签打开相应的属性页。

MFC提供了CTabCtrl类进行支持

初始化绑定变量

m_tab_Light.DeleteAllItems();
 
m_LightParkingDlg.DestroyWindow();
 
m_LightStatusDlg.DestroyWindow();
 
m_tab_Light.InsertItem(0, _T("0001")); // 插入第一个标签
 
m_tab_Light.InsertItem(1, _T("0002")); // 插入第二个标签
 
 
CRect tabRect; // 标签控件客户区的位置和大小
 
m_tab_Light.GetClientRect(&tabRect); // 获取标签控件客户区Rect
 
// 调整tabRect,使其覆盖范围适合放置标签页
 
tabRect.left += 2;
 
tabRect.right -= 1;
 
tabRect.top += 21;
 
tabRect.bottom -= 2;
 
m_LightParkingDlg.Create(IDD_DIALOG_LIGHT_PARKING, &m_tab_Light); // 创建第一个标签页
 
m_LightStatusDlg.Create(IDD_DIALOG_LIGHT_STATUS, &m_tab_Light); // 创建第二个标签页
 
//m_LightParkingDlg.InitData();
 
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(),SWP_SHOWWINDOW);

控件页面切换

 CRect tabRect; // 标签控件客户区的位置和大小
 
m_tab_Light.GetClientRect(&tabRect); // 获取标签控件客户区Rect
 
// 调整tabRect,使其覆盖范围适合放置标签页
 
tabRect.left += 2;
 
tabRect.right -= 1;
 
tabRect.top += 21;
 
tabRect.bottom -= 2;
 
switch (m_tab_Light.GetCurSel())
 
{
 
 
case 0:
 
m_LightStatusDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);
 
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);
 
break;
 
case 1:
 
m_LightParkingDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_HIDEWINDOW);
 
m_LightStatusDlg.SetWindowPos(NULL, tabRect.left, tabRect.top, tabRect.Width(), tabRect.Height(), SWP_SHOWWINDOW);
 
break;
 
 
}

动画控件(Animation):

用来播放一段AVI格式的视频剪辑。用户可以控制视频剪辑的播放、停止和定位,但也仅限于这些功能。动画控件设置不能播放音频剪辑,如果用户需要更高层次的视频或者音频的支持,请选用MCIWnd控件

MFC提供了CAnimateCtrl类对动画控件进行支持

高级编辑框(Rich Edit)

编辑控件(Edit Control)功能的扩展。在高级编辑框中,除了简单的输入和编辑字符串外,用户还可以为字符或段落指定特定的格式,用户甚至还可以向高级编辑框中插入OLE项

高级编辑框基本上实现了一个带格式的文本编辑器功能,而只需要用户添加少量的接口。

MFC提供了CRichEditCtrl类进行支持

日期/时间选择器(Date Time Picker)

向用户提供了一种直观的选择日期和时间的方法、日期/时间选择器在外观上类似于一个组合框,但是当用户单击下拉箭头时就会展开一个日历控件供用户选择,而一旦用户做出了选择,日期/时间选择器会自动显示新的日期/时间。MFC提供了CDateTimeCtrl类进行支持。

SYSTEMTIME times_1; //开始时间日期
 
SYSTEMTIME timee_1; //结束时间日期
 
 
CDateTimeCtrl* dtPickctrs_1 = (CDateTimeCtrl*)GetDlgItem(IDC_DATETIMEPICKER_START1); //获取时间控件句柄
 
CDateTimeCtrl* dtPickctre_1 = (CDateTimeCtrl*)GetDlgItem(IDC_DATETIMEPICKER_END1);
 
 
memset(×_1,0,sizeof(times_1)); //时钟初始化
 
memset(&timee_1,0,sizeof(timee_1));
 
 
dtPickctrs_1->GetTime(×_1); //获取控件所选时间,保存至变量
 
dtPickctre_1->GetTime(&timee_1);
 
 
 
CString strTimeStart; //将时间转换为字符串
 
strTimeStart.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), times_1.wYear,times_1.wMonth,times_1.wDay,times_2.wHour,times_2.wMinute,times_2.wSecond);
 
CString strTimeEnd;
 
strTimeEnd.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), timee_1.wYear,timee_1.wMonth,timee_1.wDay,timee_2.wHour,timee_2.wMinute,timee_2.wSecond);
 


获取系统时间

SYSTEMTIME time;
 
::GetLocalTime(&time);

日历控件(Month Calender)

如下图,看似与真正的日历类似,操作也类似,直观的为用户提供了观察和显示当前日期的途径。MFC提供了CMonthCalCtrl类进行支持

IP地址控件(IP Adress)

IP地址控件用来输入和编辑IP地址MFC提供了CIPAddressCtrl类进行支持

该控件外观类似于一个编辑框,但是可以自动对输入的字符按3个一组进行区分和加间隔圆点。IP地址控件为开发支持Internet技术的程序提供了方便。

扩展组合框(Extended Combo Box)

在普通组合框(Combo Box)的基础上还支持图像列表

可以在组合框中显示特定的图标表示相应的选择,而不仅仅是显示文本。

MFC提供了CComboBoxEx类进行支持

用户自定义控件(Custom Control)

控件使用的时候,它的Class必须有一个类的支持这个窗口类可以VC的类,例如:Button、Edit.在头文件增加一个CEdit类的成员变量:CEdit m_Text(即点击右键添加变量),然后按照Edit控件使用即可。


超链接控件(SysLink Control ):

1.用于在 MFC 应用程序上添加超链接,就像html中的超链接一样。你点下可以链接到一个网页上去。拖控件到页面上,并绑定一个变量(m_linkCtrl),里面的内容全部是按照标准的html中href的用法写的。写错了自然链接不了的。

可以去瞧下html里面的href属性是怎/么设置的.另外要注意的是字符串中双引号的处理(记得加个转义符\),添加单击响应消息

m_linkCtrl.SetWindowTextW(_T("<a href=\"http://blog.csdn.net/miko_xiaoming\">Miko's Note</a>"));
 
PNMLINK pNMLink = (PNMLINK) pNMHDR;
 
ShellExecuteW(NULL, L"open", pNMLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL); //在浏览器中打开

2.同按钮(Button)控件一样,响应相应的消息


多按钮组合控件(Split Button Control )

如下图,实现多选按钮选择功能,使用时添加相应菜单


网络IP地址输入输出控件(Network Address Control):

可以直接作为inet_addr(ip)的输入,分隔符为逗号(,)

指令按钮控件(command button control)

不仅可以为按钮添加相应命令还可以对命令做出注解

所有控件的公共属性:

ID:控件的资源标识。

Visiable:控件是否可见。

Disabled:控件是否被禁止、被禁止的控件无法接受键盘和鼠标输入。

Group:是否从这个空间开始一个新组。

Tab stop:在程序中是否可以用【Tab】键切换到这个控件。

Help ID:是否给控件分配一个帮助标识,帮助标识基于控件本身的资源标识。

Client edge:给控件增加一个凹陷的边界,使整个控件看起来像是凹下去的一样。

Static edge:给控件增加一个边界。

Modal frame:给控件增加一个3维的框架,与Client edge相反,它是凸出来的。

Transparent:拥有此属性的窗口是透明的,它下面的窗口不会被它遮掩住。

Accept files:拥有此属性的窗口可以接收拖放文件,如果一个文件被拖动到这个控件上面,控件会收到WM_DROPFILES消息。

No parent notify:表示控件不向其父窗口发送WM_PARENTNOTIFY消息。

Right aligned text:表示文本右对齐。

以上属性可通过控件的属性对话框进行设置,在属性对话框中按【F1】键可以查看属性的帮助信息。

每一种控件都由一个MFC控件类来管理,当通过资源编辑器在对话框上添加一个控件时,visualC++会自动为控件类生成一个实例,即对象,并调用构造函数,当对话框关闭时,其各个子控件对象被自动销毁。

也可以不使用资源编辑器,自己在程序中写代码创建、显示控件并设置控件的属性。

所有的控件类都来自CWnd,控件的某些操作和属性设置需要用到CWnd本身的成员函数,CWnd某些函数经常用来操纵控件和设置控件属性。

SetWindowText:设置控件上要显示的标题文本,即用来设置控件的caption属性

GetWindowText:得到控件的标题文本

EnableWindow:设置控件的Disabled属性,即社会自控件是否可以接收键盘和鼠标的输入

SetWindowPos:改变窗口的大小、位置和Z轴次序。

MoveWindow:改变窗口的大小和位置

GetWindowRec:得到窗口的大小、位置(信息存放在一个矩形结构中)。

GetClientRect:得到窗口客户区的大小(信息存放在一个矩形结构中 )

ShowWindow:设置窗口的可见性(即窗口是否可见)

SetWindowText/GetWindowText还可以用来设置/得到对话框的标题文本。

福利来啦~

学习从来不是一个人的事情,要有个相互监督的伙伴,对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!

作者 | 许向武
出品 | CSDN博客

概述


MFC是我接触到的第一个界面库,当时的操作系统还是Windows95。在那个IT技术日新月异的年代,就像一个从荒蛮部落闯进文明社会的野人第一眼看见汽车那样,我对MFC充满了好奇和迷恋。尽管后来断断续续接触了WPF、Qt等GUI库,却始终对MFC情有独钟,以至于爱屋及乌,喜欢上了wxWidgets。

wxWidgets和MFC的确太相似了,连命名习惯和架构都高度相似。事实上,wxWidgets就是跨平台的MFC,对各个平台的差异做了抽象,后端还是用各平台原生的API实现。这正是wxWidgets的优点:编译出来的程序发行包比较小,性能也相当优异。

随着MFC的日渐式微,Qt异军突起,目前已成为最强大,最受欢迎的跨平台GUI库之一。在Python生态圈里,PyQt的用户群也远超wxPython。喜欢Qt的人认为这是技术竞争的结果,但我觉得这更像是开源理念和商业化思想的差异造成的。

wxWidgets像是一个孤独的勇士,高举开源的大旗,试图以一己之力构建一个相互承认、相互尊重的理想社会;而Qt则更像是一个在商业资本驱使下不断扩张的帝国,它不满足于封装不同平台的API,而是要创造出自己的API和框架,它不仅仅是UI,而是囊括了APP开发用到的所有东西,包括网络、数据库、多媒体、蓝牙、NFC、脚本引擎等。

缺少或拒绝商业化运作的支持,wxWidgets的悲情结局早已是命中注定。如果不是因为Python的兴盛和wxPython的复兴,wxWidgets也许早已经和MFC一样被遗忘在了角落里。不无夸张地说,wxPython是以MFC为代表的一个时代的挽歌,更是一曲理想主义的绝唱。

1.1 组织架构

其实,wxPython谈不上什么组织架构,因为桌面程序开发所用的类、控件、组件和常量几乎都被放到了顶级命名空间wx下面了。这样做看似杂乱无章,但用起来却是非常便捷。比如,导入必要的模块,PyQt通常要这样写:

  • from PyQt6.QtWidgets import QApplication, QWidget, QComboBox, QPushButton, QHBoxLayout, QVBoxLayout, QColorDialogfrom PyQt6.QtGui import QIcon, QPainter, QPen, QColor, QPolygonfrom PyQt6.QtCore import Qt, QPoint

    PyQt巨人般的体量限制了使用星号导入所有的模块,只能用什么导入什么。而wxPython只需要简短的一句话:

    import wx

    再比如一些常量的写法,wxPython同样简洁,PyQt已经长到匪夷所思的程度了。比如左对齐和确定取消键,wxPython这样写:

wx.ALIGN_LEFTwx.OK | wx.CANCEL

PyQt写出来几乎要占一整行:

Qt.AlignmentFlag.AlignLeftQMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel

尽管wxPython也与时俱进地增加了一些诸如wx.xml、wx.svg之类地外围模块,但除了wx这个核心模块之外,我个人觉得只有wx.aui和wx.grid模块算是必要的扩展。如果想让界面更花哨点,那就要了解以下wx.adv、wx.ribbon这两个模块,纯python构建的控件库wx.lib也绝对值得一试。总之,站在我的应用领域看,wxPython的组织架构如下图所示。根据使用频率的高低,我给各个模块标注了红黄绿蓝四种颜色。

1.2 安装

截至本文写作时,wxPython的最新版本是4.1.1。Windows用户和macOS用户可以直接使用下面的命令安装。

pip install -U wxPython

由于Linux平台存在发行版之间的差异,必须使用相应的包管理器进行下载和安装。例如,在Ubuntu系统上可以尝试下面的安装命令。

sudo apt-get install python3-wxgtk4.0 python3-wxgtk-webview4.0 python3-wxgtk-media4.0


快速体验


2.1 桌面应用程序开发的一般流程

用wxPython写一个桌面应用程序,通常分为6个步骤:

  • 第1步:导入模块

  • 第2步:创建一个应用程序

  • 第3步:创建主窗口

  • 第4步:在主窗口上实现业务逻辑

  • 第5步:显示窗主口

  • 第6步:应用程序进入事件处理主循环

除第4步之外的其它步骤,基本都是一行代码就可以完成,第4步的复杂程度取决于功能需求的多寡和业务逻辑的复杂度。下面这段代码就是这个一般流程的体现。

  • # 第1步:导入模块import wx 
    # 第2步:创建一个应用程序app = wx.App()
    # 第3步:创建主窗口frame = wx.Frame(None)
    # 第4步:在主窗口上实现业务逻辑st = wx.StaticText(frame, -1, 'Hello World')
    # 第5步:显示窗主口frame.Show()
    # 第6步:应用程序进入事件处理主循环app.MainLoop()

    2.2 Hello World

    实际应用wxPython开发桌面应用程序的的时候,上面这样的写法难以实现和管控复杂的业务逻辑,因而都是采用面向对象的应用方式。下面的代码演示了以OOP的方式使用wxPython,并且为窗口增加了标题和图标,设置了窗口尺寸和背景色,同时也给静态文本控件StaticText设置了字体字号。

    • import wx
      class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, -1,style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('最简的的应用程序') self.SetIcon(wx.Icon('res/wx.ico')) # 设置图标 self.SetBackgroundColour((217, 228, 0)) # 设置窗口背景色 self.SetSize((300, 80)) # 设置窗口大小 self.Center() # 窗口在屏幕上居中 st = wx.StaticText(self, -1, 'Hello World', style=wx.ALIGN_CENTER) # 生成静态文本控件,水平居中 st.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Arial')) # 设置字体字号
      if __name__ == '__main__': app = wx.App() # 创建一个应用程序 frame = MainFrame(None) # 创建主窗口 frame.Show() # 显示窗主口 app.MainLoop() # 应用程序进入事件处理主循环

      代码中用到了一个.png格式的图像文件文件,想要运行这段代码的话,请先替换成本地文件。至于文件格式,SetIcon方法没有限定,常见的包括.ico和.jpg在内的图像格式都支持。代码运行界面如下图所示。

      2.3 常用控件介绍

      尽管wxPython的核心模块和扩展模块提供了数以百计的各式控件和组件,但真正常用且必不可少的控件只有为数不多的几个:

      • wx.Frame - 窗口

      • wx.Panel - 面板

      • wx.StaticText - 静态文本

      • StaticBitmap - 静态图片

      • wx.TextCtrl - 单行或多行文本输入框

      • wx.Button - 按钮

      • wx.RadioButton - 单选按钮

      • wx.CheckBox - 复选按钮

      • wx.Choice - 下拉选择框

      所有的wxPython控件都有一个不可或缺的parent参数和若干关键字参数,通常,关键字参数都有缺省默认值。

      • parent - 父级对象

      • id - 控件的唯一标识符,缺省或-1表示自动生成

      • pos - 控件左上角在其父级对象上的绝对位置

      • size - 控件的宽和高

      • name - 用户定义的控件名

      • style - 控件风格

      wxPython的控件在使用风格上保持着高度的一致性,一方面因为它们从一个共同的基类派生而来,更重要的一点,wxPython不像PyQt那样充斥着随处可见的重载函数。比如,PyQt的菜单栏QMenuBar增加菜单,就有addMenu(QMenu)、addMenu(str)和addMenu(QIcon, str)等三种不同的重载形式。方法重载固然带来了很多便利,但也会增加使用难度,让用户无所适从。

      下面的代码演示了上述常用控件的使用方法。

      • import wx
        class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" # 调用父类的构造函数,从默认风格中去除改变窗口大小 wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) self.SetTitle('wxPython控件演示') self.SetIcon(wx.Icon('res/wx.ico')) self.SetSize((860, 450)) self.Center() # 创建一个面板,用于放置控件 panel = wx.Panel(self, -1)
        # 在x=20,y=20的位置,创建静态文本控件 st = wx.StaticText(panel, -1, '我是静态文本控件', pos=(20, 20))
        # 在x=300,y=20的位置,创建静态图片 bmp = wx.Bitmap('res/forever.png') sb = wx.StaticBitmap(panel, -1, bmp, pos=(280, 10))
        # 在x=20, y=50的位置,创建文本输入框,指定输入框的宽度为260像素,高度默认 tc1 = wx.TextCtrl(panel, -1, value='我是文本输入框', pos=(20, 50), size=(260, -1))
        # 在x=20, y=90的位置,创建文本输入框,指定样式为密码 tc2 = wx.TextCtrl(panel, -1, value='我是密码', pos=(20, 90), style=wx.TE_PASSWORD)
        # 在x=20, y=130的位置,创建单选按钮,成组的单选按钮,第一个需要指定样式wx.RB_GROUP rb1 = wx.RadioButton(panel, -1, '单选按钮1', pos=(20, 130), style=wx.RB_GROUP, name='rb1')
        # 在x=100, y=130的位置,创建单选按钮,不再需要指定样式wx.RB_GROUP rb2 = wx.RadioButton(panel, -1, '单选按钮2', pos=(100, 130), name='rb2')
        # 在x=180, y=130的位置,创建单选按钮,不再需要指定样式wx.RB_GROUP rb3 = wx.RadioButton(panel, -1, '单选按钮3', pos=(180, 130), name='rb3')
        # 在x=20, y=160的位置,创建复选按钮 cb1 = wx.CheckBox(panel, -1, '复选按钮', pos=(20, 160))
        # 在x=100, y=160的位置,创建复选按钮,指定其样式为wx.ALIGN_RIGHT cb2 = wx.CheckBox(panel, -1, '文字在左侧的复选按钮', pos=(100, 160), style=wx.ALIGN_RIGHT)
        # 在x=20,y=190的位置,创建按钮 ch = wx.Choice(panel, -1, choices=['wxPython', 'PyQt', 'Tkinter'], pos=(20, 190), size=(100, -1)) ch.SetSelection(0) # 在x=120,y=190的位置,创建按钮 btn = wx.Button(panel, -1, '按钮', pos=(150, 190))
        # 在x=20,y=230的位置,创建文本框,指定大小为260*150,并指定其样式为多行和只读 tc3 = wx.TextCtrl(panel, -1, value='我是多行文本输入框', pos=(20, 230), size=(260, 150), style=wx.TE_MULTILINE | wx.CB_READONLY)
        if __name__ == '__main__': app = wx.App() # 创建一个应用程序 frame = MainFrame(None) # 创建主窗口 frame.Show() # 显示窗主口 app.MainLoop() # 应用程序进入事件处理主循环

        代码运行界面如下图所示。


        控件布局


        3.1. 分区布局

        上面的例子里,输入框、按钮等控件的位置由其pos参数确定,即绝对定位。绝对定位这种布局方式非常直观,但不能自动适应窗口的大小变化。更普遍的方式是使用被称为布局管理器的wx.Sizer来实现分区布局。所谓分区布局,就是将一个矩形区域沿水平或垂直方向分割成多个矩形区域,并可嵌套分区布局管理器wx.Sizer的派生类有很多种,最常用到是wx.BoxSizer和wx.StaticBoxSizer。

        和一般的控件不同,布局管理器就像是一个魔法口袋:它是无形的,但可以装进不限数量的任意种类的控件——包括其他的布局管理器。当然,魔法口袋也不是万能的,它有一个限制条件:装到里面的东西,要么是水平排列的,要么是垂直排列的,不能排成方阵。好在程序员可以不受限制地使用魔法口袋,当我们需要排成方阵时,可以先每一行使用一个魔法口袋,然后再把所有的行装到一个魔法口袋中。

        创建一个魔法口袋,装进几样东西,然后在窗口中显示的伪代码是这样的:

        • 魔法口袋 = wx.BoxSizer() # 默认是水平的,想要垂直放东西,需要加上 wx.VERTICAL 这个参数魔法口袋.add(确认按钮, 0, wx.ALL, 0) # 装入确认按钮魔法口袋.add(取消按钮, 0, wx.ALL, 0) # 装入取消按钮
          窗口.SetSizer(魔法口袋) # 把魔法口袋放到窗口上窗口.Layout() # 窗口重新布局

          魔法口袋的 add() 方法总共有4个参数:第1个参数很容易理解,就是要装进口袋的物品;第2个参数和所有 add() 方法的第2个参数之和的比,表示装进口袋的物品占用空间的比例,0表示物品多大就占多大地儿,不额外占用空间;第3个参数相对复杂些,除了约定装进口袋的物品在其占用的空间里面水平垂直方向的对齐方式外,还可以指定上下左右四个方向中的一个或多个方向的留白(padding);第4个参数就是留白像素数。

          下面是一个完整的例子。

          • import wx
            class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('分区布局') self.SetIcon(wx.Icon('res/wx.ico')) self.SetSize((640, 320)) # 设置窗口大小 self._init_ui() # 初始化界面 self.Center() # 窗口在屏幕上居中 def _init_ui(self): """初始化界面""" # 创建容器面板 panel = wx.Panel(self, -1) # 生成黑色背景的预览面板 view = wx.Panel(panel, -1, style=wx.SUNKEN_BORDER) view.SetBackgroundColour(wx.Colour(0, 0, 0)) # 生成按钮和多行文本控件 btn_capture = wx.Button(panel, -1, '拍照', size=(100, -1)) btn_up = wx.Button(panel, -1, '↑', size=(30, 30)) btn_down = wx.Button(panel, -1, '↓', size=(30, 30)) btn_left = wx.Button(panel, -1, '←', size=(30, 30)) btn_right = wx.Button(panel, -1, '→', size=(30, 30)) tc = wx.TextCtrl(panel, -1, '', style=wx.TE_MULTILINE) # 左右按钮装入一个水平布局管理器 sizer_arrow_mid = wx.BoxSizer() sizer_arrow_mid.Add(btn_left, 0, wx.RIGHT, 16) sizer_arrow_mid.Add(btn_right, 0, wx.LEFT, 16) # 生成带标签的垂直布局管理器 sizer_arrow = wx.StaticBoxSizer(wx.StaticBox(panel, -1, '方向键'), wx.VERTICAL) sizer_arrow.Add(btn_up, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入上按钮 sizer_arrow.Add(sizer_arrow_mid, 0, wx.TOP|wx.BOTTOM, 1) # 装入左右按钮 sizer_arrow.Add(btn_down, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入下按钮 # 生成垂直布局管理器 sizer_right = wx.BoxSizer(wx.VERTICAL) sizer_right.Add(btn_capture, 0, wx.ALL, 20) # 装入拍照按钮 sizer_right.Add(sizer_arrow, 0, wx.ALIGN_CENTER|wx.ALL, 0) # 装入方向键 sizer_right.Add(tc, 1, wx.ALL, 10) # 装入多行文本控件 # 生成水平布局管理器 sizer_max = wx.BoxSizer() sizer_max.Add(view, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5) # 装入左侧的预览面板 sizer_max.Add(sizer_right, 0, wx.EXPAND|wx.ALL, 0) # 装入右侧的操作区 # 为容器面板指定布局管理器,并调用布局方法完成界面布局 panel.SetSizer(sizer_max) panel.Layout()
            if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

            代码运行界面如下图所示。

            3.2. 栅格布局

            顾名思义,栅格布局就是将布局空间划分成网格,将控件放置到不同的网格内。栅格布局比较简单,用起来非常方便。栅格布局布局管理器也有很多种,GridBagSizer是最常用的一种。下面是一个使用GridBagSizer实现栅格布局的例子。

            • import wx
              class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('栅格布局') self.SetIcon(wx.Icon('res/wx.ico')) self.SetSize((800, 440)) # 设置窗口大小 self._init_ui() # 初始化界面 self.Center() # 窗口在屏幕上居中 def _init_ui(self): """初始化界面""" panel = wx.Panel(self, -1) # 创建容器面板 sizer = wx.GridBagSizer(10, 10)# 每个控件之间横纵间隔10像素 st = wx.StaticText(panel, -1, "用户名") sizer.Add(st, (0, 0), flag=wx.TOP | wx.ALIGN_RIGHT, border=20) # 在第0行0列,距离上边缘20像素,右对齐
              userName = wx.TextCtrl(panel, -1) sizer.Add(userName, (0, 1), (1, 3), flag=wx.EXPAND | wx.TOP, border=20) # 在第0行1列,跨3列,距离上边缘20像素
              sb = wx.StaticBitmap(panel, -1, wx.Bitmap('res/python.jpg')) sizer.Add(sb, (0, 5), (7, 1), flag=wx.TOP | wx.RIGHT, border=20) # 在第0行4列,跨7行,距离上右边缘20像素
              st = wx.StaticText(panel, -1, "密码") sizer.Add(st, (1, 0), flag=wx.ALIGN_RIGHT) # 在第1行0列,右对齐
              password = wx.TextCtrl(panel, -1, style=wx.TE_PASSWORD) sizer.Add(password, (1, 1), (1, 3), flag=wx.EXPAND) # 在第1行1列,跨3列
              st = wx.StaticText(panel, -1, "学历") sizer.Add(st, (2, 0), flag=wx.ALIGN_RIGHT) # 在第2行0列,右对齐
              level1 = wx.RadioButton(panel, -1, "专科") sizer.Add(level1, (2, 1)) # 在第2行1列
              level2 = wx.RadioButton(panel, -1, "本科") sizer.Add(level2, (2, 2)) # 在第2行1列
              level3 = wx.RadioButton(panel, -1, "研究生及以上") sizer.Add(level3, (2, 3)) # 在第2行1列
              st = wx.StaticText(panel, -1, "职业") sizer.Add(st, (3, 0), flag=wx.ALIGN_RIGHT) # 在第3行0列,右对齐
              professional = wx.Choice(panel, -1, choices=["学生", "程序员", "软件工程师", "系统架构师"]) professional.SetSelection(0) sizer.Add(professional, (3, 1), (1, 3), flag=wx.EXPAND) # 在第3行1列,跨3列
              # 语言技能 st = wx.StaticText(panel, -1, "语言技能") sizer.Add(st, (4, 0), flag=wx.ALIGN_RIGHT | wx.LEFT, border=20) # 在第4行0列,距离左边缘20像素,右对齐
              choices = ["C", "C++", "Java", "Python", "Lua", "JavaScript", "TypeScript", "Go", "Rust"] language = wx.ListBox(panel, -1, choices=choices, style=wx.LB_EXTENDED) sizer.Add(language, (4, 1), (1, 3), flag=wx.EXPAND) # 在第4行1列,跨3列
              isJoin = wx.CheckBox(panel, -1, "已加入QQ群", style=wx.ALIGN_RIGHT) sizer.Add(isJoin, (5, 0), (1, 4), flag=wx.ALIGN_CENTER) # 在第5行0列,跨4列, 居中
              btn = wx.Button(panel, -1, "提交") sizer.Add(btn, (6, 0), (1, 4), flag=wx.ALIGN_CENTER | wx.BOTTOM, border=20) # 在第6行0列,跨4列, 居中
              sizer.AddGrowableRow(4) # 设置第4行可增长 sizer.AddGrowableCol(3) # 设置第3列可增长 panel.SetSizer(sizer) panel.Layout()
              if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

              代码运行界面如下图所示。


              事件驱动


              一个桌面程序不单是控件的罗列,更重要的是对外部的刺激——包括用户的操作做出反应。如果把窗体和控件比作是桌面程序的躯体,那么响应外部刺激就是它的灵魂。wxPython的灵魂是事件驱动机制:当某事件发生时,程序就会自动执行预先设定的动作。

              4.1 事件

              所谓事件,就是我们的程序在运行中发生的事儿。事件可以是低级的用户动作,如鼠标移动或按键按下,也可以是高级的用户动作(定义在wxPython的窗口部件中的),如单击按钮或菜单选择。事件可以产生自系统,如关机,,也可以由用户自定义事件。

              除了用户自定义事件,在wxPython中我习惯把事件分为4类:

              • 鼠标事件:鼠标左右中键和滚轮动作,以及鼠标移动等事件

              • 键盘事件:用户敲击键盘产生的事件

              • 控件事件:发生在控件上的事件,比如按钮被按下、输入框内容改变等

              • 系统事件:关闭窗口、改变窗口大小、重绘、定时器等事件

              事实上,这个分类方法不够严谨。比如,wx.Frame作为一个控件,关闭和改变大小也是控件事件,不过这一类事件通常都由系统绑定了行为。基于此,可以重新定义所谓的控件事件,是指发生在控件上的、系统并未预定义行为的事件。

              常用的鼠标事件包括:

              • wx.EVT_LEFT_DOWN - 左键按下

              • wx.EVT_LEFT_UP - 左键弹起

              • wx.EVT_LEFT_DCLICK - 左键双击

              • wx.EVT_RIGHT_DOWN - 右键按下

              • wx.EVT_RIGHT_UP - 右键弹起

              • wx.EVT_RIGHT_DCLICK - 右键双击

              • wx.EVT_MOTION - 鼠标移动

              • wx.EVT_MOUSEWHEEL - 滚轮滚动

              • wx.EVT_MOUSE_EVENTS - 所有的鼠标事件

              常用的键盘事件有:

              • wx.EVT_KEY_DOWN - 按键按下

              • wx.EVT_KEY_UP - 按键弹起

              常用的系统事件包括:

              • wx.EVT_CLOSE - 关闭

              • wx.EVT_SIZE - 改变大小

              • wx.EVT_TIMER - 定时器事件

              • wx.EVT_PAINT - 重绘

              • wx.EVT_ERASE_BACKGROUND -背景擦除

              常用的控件事件包括:

              • wx.EVT_BUTTON - 点击按钮

              • wx.EVT_CHOICE - 下拉框改变选择

              • wx.EVT_TEXT - 输入框内容改变

              • wx.EVT_TEXT_ENTER - 输入框回车

              • wx.EVT_RADIOBOX - 单选框改变选择

              • wx.EVT_CHECKBOX - 点击复选框

              4.2 事件绑定

              事件驱动机制有三个要素:事件、事件函数和事件绑定。比如,当一个按钮被点击时,就会触发按钮点击事件,该事件如果绑定了事件函数,事件函数就会被调用。所有的事件函数都以事件对象为参数,事件对象提供了事件的详细信息,比如键盘按下事件的事件对象就包含了被按下的键的信息。

              下面这个例子演示了如何定义事件函数,以及绑定事件和事件函数之间的关联关系。

              • import wx
                class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('事件和事件函数的绑定') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((520, 220)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" wx.StaticText(self, -1, '第一行输入框:', pos=(40, 50), size=(100, -1), style=wx.ALIGN_RIGHT) wx.StaticText(self, -1, '第二行输入框:', pos=(40, 80), size=(100, -1), style=wx.ALIGN_RIGHT) self.tip = wx.StaticText(self, -1, u'', pos=(145, 110), size=(150, -1), style=wx.ST_NO_AUTORESIZE) self.tc1 = wx.TextCtrl(self, -1, '', pos=(145, 50), size=(150, -1), name='TC01', style=wx.TE_CENTER) self.tc2 = wx.TextCtrl(self, -1, '', pos=(145, 80), size=(150, -1), name='TC02', style=wx.TE_PASSWORD|wx.ALIGN_RIGHT) btn_mea = wx.Button(self, -1, '鼠标左键事件', pos=(350, 50), size=(100, 25)) btn_meb = wx.Button(self, -1, '鼠标所有事件', pos=(350, 80), size=(100, 25)) btn_close = wx.Button(self, -1, '关闭窗口', pos=(350, 110), size=(100, 25)) self.tc1.Bind(wx.EVT_TEXT, self.on_text) # 绑定文本内容改变事件 self.tc2.Bind(wx.EVT_TEXT, self.on_text) # 绑定文本内容改变事件 btn_close.Bind(wx.EVT_BUTTON, self.on_close, btn_close) # 绑定按键事件 btn_close.Bind(wx.EVT_MOUSEWHEEL, self.on_wheel) # 绑定鼠标滚轮事件 btn_mea.Bind(wx.EVT_LEFT_DOWN, self.on_left_down) # 绑定鼠标左键按下 btn_mea.Bind(wx.EVT_LEFT_UP, self.on_left_up) # 绑定鼠标左键弹起 btn_meb.Bind(wx.EVT_MOUSE_EVENTS, self.on_mouse) # 绑定所有鼠标事件 self.Bind(wx.EVT_CLOSE, self.on_close) # 绑定窗口关闭事件 self.Bind(wx.EVT_SIZE, self.on_size) # 绑定改变窗口大小事件 self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) # 绑定键盘事件 def on_text(self, evt): """输入框事件函数""" obj = evt.GetEventObject() objName = obj.GetName() text = evt.GetString() if objName == 'TC01': self.tc2.SetValue(text) elif objName == 'TC02': self.tc1.SetValue(text) def on_size(self, evt): '''改变窗口大小事件函数''' print('你想改变窗口,但是事件被Skip了,所以没有任何改变') evt.Skip() # 注释掉此行(事件继续传递),窗口大小才会被改变 def on_close(self, evt): """关闭窗口事件函数""" dlg = wx.MessageDialog(None, '确定要关闭本窗口?', '操作提示', wx.YES_NO | wx.ICON_QUESTION) if(dlg.ShowModal() == wx.ID_YES): self.Destroy() def on_left_down(self, evt): """左键按下事件函数""" self.tip.SetLabel('左键按下') def on_left_up(self, evt): """左键弹起事件函数""" self.tip.SetLabel('左键弹起') def on_wheel(self, evt): """鼠标滚轮事件函数""" vector = evt.GetWheelRotation() self.tip.SetLabel(str(vector)) def on_mouse(self, evt): """鼠标事件函数""" self.tip.SetLabel(str(evt.EventType)) def on_key_down(self, evt): """键盘事件函数""" key = evt.GetKeyCode() self.tip.SetLabel(str(key))
                if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                代码运行界面如下图所示。

                两个输入框,一个明文居中,一个密写右齐,但内容始终保持同步。输入焦点不在输入框的时候,敲击键盘,界面显示对应的键值。最上面的按钮响应鼠标左键的按下和弹起事件,中间的按钮响应所有的鼠标事件,下面的按钮响应滚轮事件和按钮按下的事件。另外,程序还绑定了窗口关闭事件,重新定义了关闭函数,增加了确认选择。


                程序框架


                5.1 菜单栏、工具栏和状态栏

                通常,一个完整的窗口程序一般都有菜单栏、工具栏和状态栏。下面的代码演示了如何创建菜单栏、工具栏和状态栏,顺便演示了类的静态属性的定义和用法。不过,说实话,wx的工具栏有点丑,幸好,wx还有一个 AUI 的工具栏比较漂亮,我会在后面的例子里演示它的用法。

                • import wx
                  class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" id_open = wx.NewIdRef() id_save = wx.NewIdRef() id_quit = wx.NewIdRef() id_help = wx.NewIdRef() id_about = wx.NewIdRef() def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('菜单、工具栏、状态栏') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((360, 180)) self._create_menubar() # 菜单栏 self._create_toolbar() # 工具栏 self._create_statusbar() # 状态栏 self.Center() def _create_menubar(self): """创建菜单栏""" self.mb = wx.MenuBar() # 文件菜单 m = wx.Menu() m.Append(self.id_open, '打开文件') m.Append(self.id_save, '保存文件') m.AppendSeparator() m.Append(self.id_quit, '退出系统') self.mb.Append(m, '文件') self.Bind(wx.EVT_MENU, self.on_open, id=self.id_open) self.Bind(wx.EVT_MENU, self.on_save, id=self.id_save) self.Bind(wx.EVT_MENU, self.on_quit, id=self.id_quit) # 帮助菜单 m = wx.Menu() m.Append(self.id_help, '帮助主题') m.Append(self.id_about, '关于...') self.mb.Append(m, '帮助') self.Bind(wx.EVT_MENU, self.on_help,id=self.id_help) self.Bind(wx.EVT_MENU, self.on_about,id=self.id_about) self.SetMenuBar(self.mb) def _create_toolbar(self): """创建工具栏""" bmp_open = wx.Bitmap('res/open_mso.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片 bmp_save = wx.Bitmap('res/save_mso.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片 bmp_help = wx.Bitmap('res/help_mso.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片 bmp_about = wx.Bitmap('res/info_mso.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片 self.tb = wx.ToolBar(self) self.tb.SetToolBitmapSize((16,16)) self.tb.AddTool(self.id_open, '打开文件', bmp_open, shortHelp='打开', kind=wx.ITEM_NORMAL) self.tb.AddTool(self.id_save, '保存文件', bmp_save, shortHelp='保存', kind=wx.ITEM_NORMAL) self.tb.AddSeparator() self.tb.AddTool(self.id_help, '帮助', bmp_help, shortHelp='帮助', kind=wx.ITEM_NORMAL) self.tb.AddTool(self.id_about, '关于', bmp_about, shortHelp='关于', kind=wx.ITEM_NORMAL) self.tb.Realize() def _create_statusbar(self): """创建状态栏""" self.sb = self.CreateStatusBar() self.sb.SetFieldsCount(3) self.sb.SetStatusWidths([-2, -1, -1]) self.sb.SetStatusStyles([wx.SB_RAISED, wx.SB_RAISED, wx.SB_RAISED]) self.sb.SetStatusText('状态信息0', 0) self.sb.SetStatusText('', 1) self.sb.SetStatusText('状态信息2', 2) def on_open(self, evt): """打开文件""" self.sb.SetStatusText(u'打开文件', 1) def on_save(self, evt): """保存文件""" self.sb.SetStatusText(u'保存文件', 1) def on_quit(self, evt): """退出系统""" self.sb.SetStatusText(u'退出系统', 1) self.Destroy() def on_help(self, evt): """帮助""" self.sb.SetStatusText(u'帮助', 1) def on_about(self, evt): """关于""" self.sb.SetStatusText(u'关于', 1)
                  if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                  代码里面用到了4个16x16的工具按钮,请自备4个图片文件,保存路径请查看代码中的注释。代码运行界面如下图所示。

                  5.2 Aui框架

                  Advanced User Interface,简称AUI,是wxPython的子模块,使用AUI可以方便地开发出美观、易用的用户界面。从2.8.9.2版本之后,wxPython增加了一个高级通用部件库Advanced Generic Widgets,简称AGW库, AGW库也提供了AUI模块 wx.lib.agw.aui,而 wx.aui也依然保留着。相比较而言,我更喜欢使用wx.lib.agw的AUI框架。

                  使用AUI框架可以概括为以下四步:

                  1. 创建一个布局管理器:mgr = aui.AuiManager()

                  2. 告诉主窗口由mgr来管理界面:mgr.SetManagedWindow()

                  3. 添加界面上的各个区域:mgr.AddPane()

                  4. 更新界面显示:mgr.Update()

                  下面的代码演示了如何使用AUI布局管理器创建和管理窗口界面。

                  • import wximport wx.lib.agw.aui as aui
                    class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" id_open = wx.NewIdRef() id_save = wx.NewIdRef() id_quit = wx.NewIdRef() id_help = wx.NewIdRef() id_about = wx.NewIdRef() def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('菜单、工具栏、状态栏') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((640, 480)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" self.tb1 = self._create_toolbar() self.tb2 = self._create_toolbar() self.tbv = self._create_toolbar('V') p_left = wx.Panel(self, -1) p_center0 = wx.Panel(self, -1) p_center1 = wx.Panel(self, -1) p_bottom = wx.Panel(self, -1) btn = wx.Button(p_left, -1, '切换', pos=(30,200), size=(100, -1)) btn.Bind(wx.EVT_BUTTON, self.on_switch) text0 = wx.StaticText(p_center0, -1, '我是第1页', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT) text1 = wx.StaticText(p_center1, -1, '我是第2页', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT) self._mgr = aui.AuiManager() self._mgr.SetManagedWindow(self) self._mgr.AddPane(self.tb1, aui.AuiPaneInfo().Name('ToolBar1').Caption('工具条').ToolbarPane().Top().Row(0).Position(0).Floatable(False) ) self._mgr.AddPane(self.tb2, aui.AuiPaneInfo().Name('ToolBar2').Caption('工具条').ToolbarPane().Top().Row(0).Position(1).Floatable(True) ) self._mgr.AddPane(self.tbv, aui.AuiPaneInfo().Name('ToolBarV').Caption('工具条').ToolbarPane().Right().Floatable(True) ) self._mgr.AddPane(p_left, aui.AuiPaneInfo().Name('LeftPanel').Left().Layer(1).MinSize((200,-1)).Caption('操作区').MinimizeButton(True).MaximizeButton(True).CloseButton(True) ) self._mgr.AddPane(p_center0, aui.AuiPaneInfo().Name('CenterPanel0').CenterPane().Show() ) self._mgr.AddPane(p_center1, aui.AuiPaneInfo().Name('CenterPanel1').CenterPane().Hide() ) self._mgr.AddPane(p_bottom, aui.AuiPaneInfo().Name('BottomPanel').Bottom().MinSize((-1,100)).Caption('消息区').CaptionVisible(False).Resizable(True) ) self._mgr.Update() def _create_toolbar(self, d='H'): """创建工具栏""" bmp_open = wx.Bitmap('res/open_mso.png', wx.BITMAP_TYPE_ANY) bmp_save = wx.Bitmap('res/save_mso.png', wx.BITMAP_TYPE_ANY) bmp_help = wx.Bitmap('res/help_mso.png', wx.BITMAP_TYPE_ANY) bmp_about = wx.Bitmap('res/info_mso.png', wx.BITMAP_TYPE_ANY) if d.upper() in ['V', 'VERTICAL']: tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT|aui.AUI_TB_VERTICAL) else: tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT) tb.SetToolBitmapSize(wx.Size(16, 16)) tb.AddSimpleTool(self.id_open, '打开', bmp_open, '打开文件') tb.AddSimpleTool(self.id_save, '保存', bmp_save, '保存文件') tb.AddSeparator() tb.AddSimpleTool(self.id_help, '帮助', bmp_help, '帮助') tb.AddSimpleTool(self.id_about, '关于', bmp_about, '关于') tb.Realize() return tb def on_switch(self, evt): """切换信息显示窗口""" p0 = self._mgr.GetPane('CenterPanel0') p1 = self._mgr.GetPane('CenterPanel1') p0.Show(not p0.IsShown()) p1.Show(not p1.IsShown()) self._mgr.Update()
                    if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                    代码运行界面如下图所示。


                    示例和技巧


                    6.1. 相册

                    前文的例子中已经展示了wx.StaticBitmap控件作为图像容器的例子,下面的例子用它制作了一个相册,点击前翻后翻按钮可在多张照片之间循环切换。

                    • import wx
                      class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('相册') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((980, 680)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" self.curr = 0 self.photos = ('res/DSC03363.jpg', 'res/DSC03394.jpg', 'res/DSC03402.jpg') bmp = wx.Bitmap(self.photos[self.curr]) self.album = wx.StaticBitmap(self, -1, bmp, pos=(280, 10)) btn_1 = wx.Button(self, -1, '<', size=(80, 30), name='prev') btn_2 = wx.Button(self, -1, '>', size=(80, 30), name='next') btn_1.Bind(wx.EVT_BUTTON, self.on_btn) btn_2.Bind(wx.EVT_BUTTON, self.on_btn) sizer_btn = wx.BoxSizer() sizer_btn.Add(btn_1, 0, wx.RIGHT, 20) sizer_btn.Add(btn_2, 0, wx.LEFT, 20) sizer_max = wx.BoxSizer(wx.VERTICAL) sizer_max.Add(self.album, 1, wx.EXPAND | wx.ALL, 10) sizer_max.Add(sizer_btn, 0, wx. ALIGN_CENTER | wx.BOTTOM, 20) self.SetSizer(sizer_max) self.Layout() def on_btn(self, evt): """响应按键""" name = evt.GetEventObject().GetName() if name == '<': self.curr = (self.curr-1)%len(self.photos) else: self.curr = (self.curr+1)%len(self.photos) self.album.SetBitmap(wx.Bitmap(self.photos[self.curr]))
                      if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                      代码运行界面如下图所示。

                      6.2. 会弹琴的计算器

                      几乎所有的GUI课程都会用计算器作为例子,wxPython怎能缺席呢?下面这个计算器除了常规的计算外,按下每个键都会发出不同的音调,粗通乐理就可以弹奏出乐曲。此外,代码中使用了wx.lib控件库的按键,略带3D风格。

                      • import wximport wx.lib.buttons as wxbtnimport winsound
                        class MainFrame(wx.Frame): """桌面程序主窗口类""" def __init__(self): """构造函数""" wx.Frame.__init__(self, parent=None, style=wx.CAPTION|wx.SYSTEM_MENU|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SIMPLE_BORDER) self.SetTitle('会弹琴的计算器') self.SetIcon(wx.Icon('res/wx.ico', wx.BITMAP_TYPE_ICO)) self.SetBackgroundColour((217, 228, 241)) self.SetSize((287, 283)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" # 定义按键排列顺序和名称 keys = [ ['(', ')', 'Back', 'Clear'], ['7', '8', '9', '/'], ['4', '5', '6', '*'], ['1', '2', '3', '-'], ['0', '.', '=', '+'] ] # 指定每个按键声音的频率,523赫兹就是C调中音 self.keySound = { '(':392, ')': 440, '0':494, '1':523, '2':587, '3':659, '4':698, '5':784, '6':880, '7':988, '8':1047, '9':1175, '.':1318, '+':523, '-':587, '*':659, '/':698, 'Clear':784, 'Back':880, '=':2000 } # 用输入框控件作为计算器屏幕,设置为只读(wx.TE_READONLY)和右齐(wx.ALIGN_RIGHT) self.screen = wx.TextCtrl(self, -1, '', pos=(10,10), size=(252,45), style=wx.TE_READONLY|wx.ALIGN_RIGHT) self.screen.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, '微软雅黑')) # 设置字体字号 self.screen.SetBackgroundColour((0, 0, 0)) # 设置屏幕背景色 self.screen.SetForegroundColour((0, 255, 0)) # 设置屏幕前景色 # 按键布局参数 btn_size = (60, 30) # 定义按键的尺寸,便于统一修改 x0, y0 = (10, 65) # 定义按键区域的相对位置 dx, dy = (64, 34) # 定义水平步长和垂直步长 # 生成所有按键 for i in range(len(keys)): for j in range(len(keys[i])): key = keys[i][j] btn = wxbtn.GenButton(self, -1, key, pos=(x0+j*dx, y0+i*dy), size=btn_size, name=key) if key in ['0','1','2','3','4','5','6','7','8','9','.']: btn.SetBezelWidth(1) # 设置3D效果 btn.SetBackgroundColour(wx.Colour(217, 228, 241)) # 定义按键的背景色 elif key in ['(',')','Back','Clear']: btn.SetBezelWidth(2) btn.SetBackgroundColour(wx.Colour(217, 220, 235)) btn.SetForegroundColour(wx.Colour(224, 60, 60)) elif key in ['+','-','*','/']: btn.SetBezelWidth(2) btn.SetBackgroundColour(wx.Colour(246, 225, 208)) btn.SetForegroundColour(wx.Colour(60, 60, 224)) else: btn.SetBezelWidth(2) btn.SetBackgroundColour(wx.Colour(245, 227, 129)) btn.SetForegroundColour(wx.Colour(60, 60, 224)) btn.SetToolTip(u"显示计算结果") self.Bind(wx.EVT_BUTTON, self.on_button) # 将按钮事件绑定在所有按钮上 def on_button(self, evt): """响应鼠标左键按下""" obj = evt.GetEventObject() # 获取事件对象(哪个按钮被按) key = obj.GetName() # 获取事件对象的名字 self.PlayKeySound(key) # 播放按键对应频率的声音 if self.screen.GetValue == 'Error': self.screen.SetValue('') if key == 'Clear': # 按下了清除键,清空屏幕 self.screen.SetValue('') elif key == 'Back': # 按下了回退键,去掉最后一个输入字符 content = self.screen.GetValue() if content: self.screen.SetValue(content[:-1]) elif key == '=': # 按下了等号键,则计算 try: result = str(eval(self.screen.GetValue())) except: result = 'Error' self.screen.SetValue(result) else: # 按下了其他键,追加到显示屏上 self.screen.AppendText(key) def PlayKeySound(self, key, Dur=100): """播放按键声音""" winsound.Beep(self.keySound[key], Dur)
                        if __name__ == '__main__': app = wx.App() frame = MainFrame() frame.Show() app.MainLoop()

                        代码运行界面如下图所示。

                        6.3. 定时器和线程

                        在一个桌面程序中,GUI线程是主线程,其他线程若要更新显示内容,Tkinter使用的是类型对象,PyQt使用的信号和槽机制,wxPython则相对原始:它允许子线程更新GUI,但需要借助于wx.CallAfter()函数。

                        这个例子里面设计了一个数字式钟表,一个秒表,秒表显示精度十分之一毫秒。从代码设计上来说没有任何难度,实现的方法有很多种,可想要达到一个较好的显示效果,却不是一件容易的事情。请注意体会 wx.CallAfter() 的使用条件。

                        • import wximport timeimport threading
                          class MainFrame(wx.Frame): """桌面程序主窗口类""" def __init__(self): """构造函数""" wx.Frame.__init__(self, parent=None, style=wx.CAPTION|wx.SYSTEM_MENU|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SIMPLE_BORDER) self.SetTitle('定时器和线程') self.SetIcon(wx.Icon('res/wx.ico', wx.BITMAP_TYPE_ICO)) self.SetBackgroundColour((224, 224, 224)) self.SetSize((320, 300)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" font = wx.Font(30, wx.DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Monaco') self.clock = wx.StaticText(self, -1, '08:00:00', pos=(50,50), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER) self.clock.SetForegroundColour(wx.Colour(0, 224, 32)) self.clock.SetBackgroundColour(wx.Colour(0, 0, 0)) self.clock.SetFont(font) self.stopwatch = wx.StaticText(self, -1, '0:00:00.00', pos=(50,150), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER) self.stopwatch.SetForegroundColour(wx.Colour(0, 224, 32)) self.stopwatch.SetBackgroundColour(wx.Colour(0, 0, 0)) self.stopwatch.SetFont(font) self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_timer, self.timer) self.timer.Start(50) self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) self.sec_last = None self.is_start = False self.t_start = None thread_sw = threading.Thread(target=self.StopWatchThread) thread_sw.setDaemon(True) thread_sw.start() def on_timer(self, evt): """定时器函数""" t = time.localtime() if t.tm_sec != self.sec_last: self.clock.SetLabel('%02d:%02d:%02d'%(t.tm_hour, t.tm_min, t.tm_sec)) self.sec_last = t.tm_sec def on_key_down(self, evt): """键盘事件函数""" if evt.GetKeyCode() == wx.WXK_SPACE: self.is_start = not self.is_start self.t_start= time.time() elif evt.GetKeyCode() == wx.WXK_ESCAPE: self.is_start = False self.stopwatch.SetLabel('0:00:00.00') def StopWatchThread(self): """线程函数""" while True: if self.is_start: t = time.time() - self.t_start ti = int(t) wx.CallAfter(self.stopwatch.SetLabel, '%d:%02d:%02d.%.02d'%(ti//3600, ti//60, ti%60, int((t-ti)*100))) time.sleep(0.02)
                          if __name__ == '__main__': app = wx.App() frame = MainFrame() frame.Show() app.MainLoop()

                          代码运行界面如下图所示。界面上方的时钟一直再跑,下方的秒表则是按键启动或停止。

                          6.4. DC绘图

                          DC 是 Device Context 的缩写,字面意思是设备上下文——我一直不能正确理解DC这个中文名字,也找不到更合适的说法,所以,我坚持使用DC而不是设备上下文。DC可以在屏幕上绘制点线面,当然也可以绘制文本和图像。事实上,在底层所有控件都是以位图形式绘制在屏幕上的,这意味着,我们一旦掌握了DC这个工具,就可以自己创造我们想要的控件了

                          DC有很多种,PaintDC,ClientDC,MemoryDC等。通常,我们可以使用 ClientDC 和 MemoryDC,PaintDC 是发生重绘事件(wx.EVT_PAINT)时系统使用的。使用 ClientDC 绘图时,需要记录绘制的每一步工作,不然,系统重绘时会令我们前功尽弃——这是使用DC最容易犯的错误。

                          • import wx
                            class MainFrame(wx.Frame): """桌面程序主窗口类""" def __init__(self): """构造函数""" wx.Frame.__init__(self, parent=None, style=wx.CAPTION|wx.SYSTEM_MENU|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SIMPLE_BORDER) self.SetTitle('使用DC绘图') self.SetIcon(wx.Icon('res/wx.ico', wx.BITMAP_TYPE_ICO)) self.SetBackgroundColour((224, 224, 224)) self.SetSize((800, 480)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" self.palette = wx.Panel(self, -1, style=wx.SUNKEN_BORDER) self.palette.SetBackgroundColour(wx.Colour(0, 0, 0)) btn_base = wx.Button(self, -1, '文字和图片', size=(100, -1)) sizer_max = wx.BoxSizer() sizer_max.Add(self.palette, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5) sizer_max.Add(btn_base, 0, wx.ALL, 20) self.SetAutoLayout(True) self.SetSizer(sizer_max) self.Layout() btn_base.Bind(wx.EVT_BUTTON, self.on_base) self.palette.Bind(wx.EVT_MOUSE_EVENTS, self.on_mouse) self.palette.Bind(wx.EVT_PAINT, self.on_paint) self.xy = None self.lines = list() self.img = wx.Bitmap('res/forever.png', wx.BITMAP_TYPE_ANY) self.update_palette() def on_mouse(self, evt): """移动鼠标画线""" if evt.EventType == 10030: #左键按下 self.xy = (evt.x, evt.y) elif evt.EventType == 10031: #左键弹起 self.xy = None elif evt.EventType == 10036: #鼠标移动 if self.xy: dc = wx.ClientDC(self.palette) dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2)) dc.DrawLine(self.xy[0], self.xy[1], evt.x, evt.y) self.lines.append((self.xy[0], self.xy[1], evt.x, evt.y)) self.xy = (evt.x, evt.y) def on_base(self, evt): """DC基本方法演示""" img = wx.Bitmap('res/forever.png', wx.BITMAP_TYPE_ANY) w, h = self.palette.GetSize() dc = wx.ClientDC(self.palette) dc.SetPen(wx.Pen(wx.Colour(224,0,0), 1)) dc.SetBrush(wx.Brush(wx.Colour(0,80,80) )) dc.DrawRectangle(10,10,w-22,h-22) dc.DrawLine(10,h/2,w-12,h/2) dc.DrawBitmap(img, 10, 10) dc.SetTextForeground(wx.Colour(224,224,224)) dc.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS')) dc.DrawText('霜重闲愁起', 100, 360) dc.DrawRotatedText('春深风也疾', 400, 360, 30) def on_paint(self, evt): """响应重绘事件""" dc = wx.PaintDC(self.palette) self.paint(dc) def update_palette(self): """刷新画板""" dc = wx.ClientDC(self.palette) self.paint(dc) def paint(self, dc): """绘图""" dc.Clear() dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2)) for line in self.lines: dc.DrawLine(line[0],line[1],line[2],line[3])
                            if __name__ == '__main__': app = wx.App() frame = MainFrame() frame.Show() app.MainLoop()

                            代码运行界面如下图所示。

                            6.5. 内嵌浏览器

                            wx.html2是wxPython扩展模块中封装得最干净漂亮的模块之一,它被设计为允许为每个端口创建多个后端,尽管目前只有一个可用。它与wx.html.HtmlWindow的不同之处在于,每个后端实际上都是一个完整的渲染引擎,MSW上是Trident, macOS和GTK上是Webkit。wx.html2渲染web文档,对于HTML、CSS和javascript都可以有很好的支持。

                            • import wximport wx.html2 as webview
                              class MainFrame(wx.Frame): """桌面程序主窗口类""" def __init__(self): """构造函数""" wx.Frame.__init__(self, parent=None) self.SetTitle('内嵌浏览器') self.SetIcon(wx.Icon('res/wx.ico', wx.BITMAP_TYPE_ICO)) self.SetBackgroundColour((224, 224, 224)) self.SetSize((800, 480)) self.Center() wv = webview.WebView.New(self) wv.LoadURL('https://cn.bing.com')
                              if __name__ == '__main__': app = wx.App() frame = MainFrame() frame.Show() app.MainLoop()

                              代码运行界面如下图所示。


                              集成应用


                              7.1. 集成Matplotlib

                              Matplotlib的后端子模块backends几乎支持所有的GUI库,wxPyton当然也不例外,backend_wxagg是专门为wxPyton生成canvas的类,只要传一个matplotlib.Figure实例即可。剩下的就是水到渠成了。

                              • import numpy as npimport matplotlibfrom matplotlib.backends import backend_wxagg from matplotlib.figure import Figure import wx
                                matplotlib.use('TkAgg')matplotlib.rcParams['font.sans-serif'] = ['FangSong']matplotlib.rcParams['axes.unicode_minus'] = False
                                class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.SetTitle('集成Matplotlib') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((800, 600)) self._init_ui() self.Center() def _init_ui(self): """初始化界面""" self.fig = Figure() self.canvas = backend_wxagg.FigureCanvasWxAgg(self, -1, self.fig) btn_1 = wx.Button(self, -1, '散点图', size=(80, 30)) btn_2 = wx.Button(self, -1, '等值线图', size=(80, 30)) btn_1.Bind(wx.EVT_BUTTON, self.on_scatter) btn_2.Bind(wx.EVT_BUTTON, self.on_contour) sizer_btn = wx.BoxSizer() sizer_btn.Add(btn_1, 0, wx.RIGHT, 20) sizer_btn.Add(btn_2, 0, wx.LEFT, 20) sizer_max = wx.BoxSizer(wx.VERTICAL) sizer_max.Add(self.canvas, 1, wx.EXPAND | wx.ALL, 10) sizer_max.Add(sizer_btn, 0, wx. ALIGN_CENTER | wx.BOTTOM, 20) self.SetSizer(sizer_max) self.Layout() def on_scatter(self, evt): """散点图""" x = np.random.randn(50) # 随机生成50个符合标准正态分布的点(x坐标) y = np.random.randn(50) # 随机生成50个符合标准正态分布的点(y坐标) color = 10 * np.random.rand(50) # 随即数,用于映射颜色 area = np.square(30*np.random.rand(50)) # 随机数表示点的面积 self.fig.clear() ax = self.fig.add_subplot(111) ax.scatter(x, y, c=color, s=area, cmap='hsv', marker='o', edgecolor='r', alpha=0.5) self.canvas.draw() def on_contour(self, evt): """等值线图""" y, x = np.mgrid[-3:3:60j, -4:4:80j] z = (1-y**5+x**5)*np.exp(-x**2-y**2) self.fig.clear() ax = self.fig.add_subplot(111) ax.set_title('有填充的等值线图') c = ax.contourf(x, y, z, levels=8, cmap='jet') self.fig.colorbar(c, ax=ax) self.canvas.draw()
                                if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                                代码运行界面如下图所示。

                                7.2. 集成OpenGL

                                wx.glcanvas.GLCanvas是wxPython为显示OpenGL提供的类,顾名思义,可以将其理解为OpenGL的画板。有了这个画板,我们就可以使用OpenGL提供的各种工具在上面绘制各种三维模型了。下面的代码仅是一个demo,并未构建投影系统和视点系统。

                                • import numpy as npfrom OpenGL.GL import *
                                  import wxfrom wx import glcanvas
                                  class MainFrame(wx.Frame): """从wx.Frame派生主窗口类""" def __init__(self, parent): """构造函数""" wx.Frame.__init__(self, parent, style=wx.DEFAULT_FRAME_STYLE) self.canvas = glcanvas.GLCanvas(self, style=glcanvas.WX_GL_RGBA|glcanvas.WX_GL_DOUBLEBUFFER|glcanvas.WX_GL_DEPTH_SIZE) self.context = glcanvas.GLContext(self.canvas) self.csize = self.canvas.GetClientSize() self.SetTitle('集成OpenGL') self.SetIcon(wx.Icon('res/wx.ico')) self.SetBackgroundColour((224, 224, 224)) # 设置窗口背景色 self.SetSize((800, 600)) self.Center() sizer_max = wx.BoxSizer() sizer_max.Add(self.canvas, 1, wx.EXPAND|wx.ALL, 5) self.SetSizer(sizer_max) self.Layout() self.Bind(wx.EVT_SIZE, self.on_resize) self.canvas.SetCurrent(self.context) glClearColor(0,0,0,1) # 设置画布背景色 self.draw() def on_resize(self, evt): """窗口改变事件函数""" self.canvas.SetCurrent(self.context) self.csize = self.GetClientSize() self.draw() evt.Skip() def draw(self): """绘制""" # 清除屏幕及深度缓存 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # --------------------------------------------------------------- glBegin(GL_LINES) # 开始绘制线段(坐标轴) # 以红色绘制x轴 glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明 glVertex3f(-0.8, 0.0, 0.0) # 设置x轴顶点(x轴负方向) glVertex3f(0.8, 0.0, 0.0) # 设置x轴顶点(x轴正方向) # 以绿色绘制y轴 glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明 glVertex3f(0.0, -0.8, 0.0) # 设置y轴顶点(y轴负方向) glVertex3f(0.0, 0.8, 0.0) # 设置y轴顶点(y轴正方向) # 以蓝色绘制z轴 glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明 glVertex3f(0.0, 0.0, -0.8) # 设置z轴顶点(z轴负方向) glVertex3f(0.0, 0.0, 0.8) # 设置z轴顶点(z轴正方向) glEnd() # 结束绘制线段 # --------------------------------------------------------------- glBegin(GL_TRIANGLES) # 开始绘制三角形(z轴负半区) glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明 glVertex3f(-0.5, -0.366, -0.5) # 设置三角形顶点 glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明 glVertex3f(0.5, -0.366, -0.5) # 设置三角形顶点 glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明 glVertex3f(0.0, 0.5, -0.5) # 设置三角形顶点 glEnd() # 结束绘制三角形 # --------------------------------------------------------------- glBegin(GL_TRIANGLES) # 开始绘制三角形(z轴正半区) glColor4f(1.0, 0.0, 0.0, 1.0) # 设置当前颜色为红色不透明 glVertex3f(-0.5, 0.5, 0.5) # 设置三角形顶点 glColor4f(0.0, 1.0, 0.0, 1.0) # 设置当前颜色为绿色不透明 glVertex3f(0.5, 0.5, 0.5) # 设置三角形顶点 glColor4f(0.0, 0.0, 1.0, 1.0) # 设置当前颜色为蓝色不透明 glVertex3f(0.0, -0.366, 0.5) # 设置三角形顶点 glEnd() # 结束绘制三角形 # 交换缓冲区 self.canvas.SwapBuffers()
                                  if __name__ == '__main__': app = wx.App() frame = MainFrame(None) frame.Show() app.MainLoop()

                                  代码运行界面如下图所示。

                                  原文链接:https://blog.csdn.net/xufive/article/details/124548040

                                  END


                                  成就一亿技术人


上一篇:HTML 布局
下一篇:JavaScript-第四章