当前位置: 首页 > 编程日记 > 正文

wxPython:Python首选的GUI库 | CSDN博文精选

640?wx_fmt=jpeg

作者 | 天元浪子

来源 | CSDN博客


文章目录


  • 概述

  • 窗口程序的基本框架

  • 事件和事件驱动

  • 菜单栏/工具栏/状态栏

  • 动态布局

  • AUI布局

  • DC绘图

  • 定时器和线程

  • 后记

概述
跨平台的GUI工具库,较为有名的当属GTK+、Qt 和 wxWidgets 了。GTK+是C实现的,由于C语言本身不支持OOP,因而GTK+上手相当困难,写起来也较为复杂艰涩。Qt 和 wxWidgets 则是C++实现的,各自拥有庞大的用户群体。虽然我喜欢wxWidgets,但还是尽可能客观地搜集了关于Qt 和 wxWidgets 的对比评价。
关于LICENSE
Qt最初由芬兰的TrollTech公司研发,后来卖给了Nokia(没看错,就是曾经闻名遐迩的手机巨头诺基亚),2012年Digia从诺基亚完整收购了QT的技术平台和知识产权。QT的背后一直由商业公司支持,奉行的是双 license 策略,一个是商业版,一个是免费版。这个策略严重限制了Qt的用户群体。据说Nokia收购之后意识到了这个问题,自4.5版本之后采用了LGPL,开发人员可以发布基于免费Qt库的商业软件了。wxWidgets最开始是由爱丁堡(Edinburgh)大学的人工智能应用学院开发的,在1992年开源,一直遵循LGPL。wxWidgets从一开始就是程序员的免费午餐。
关于兼容性
由于Qt使用的是非标准C++,与其它库的兼容性会存在问题,在每个平台的图形界面也并不完全是原生界面( Native GUI),只是透过 theme 去模拟系統上的标准 GUI,所以看起來很像,有些地方则会明显看出破綻。 Qt的执行速度缓慢且过于庞大则是另一个问题。wxWidgets使用的是标准C++,与现有各类工具库无缝连接,在不同平台上也是完全Native GUI,是真正的跨平台。
关于服务和支持
由于Nokia的接盘,Qt提供了一系列完整的文档和RAD工具,并提供最为完整的平台支持,对于移动终端的支持最为完善。Qt库也是所有的GUI工具库中最为面向对象化的,同时也是最为稳定的。wxWidgets因为缺乏很好的商业化支持,开发文档、资源相对较为匮乏。由于是偏重考虑MFC程序的跨平台迁移,wxWidgets面向对象封装做得差强人意。
wxWidgets的主体是由C++构建的,但你并不是必需通过C++才能使用它。wxWidgets拥有许多其它语言的绑定(binding),比如 wxPerl,wxJava,wxBasic,wxJavaScript,wxRuby等等,wxPython 就是 Python语言的 wxWidgets 工具库。
窗口程序的基本框架
不管是py2还是py3,python的世界里安装工作已经变得非常简单了。如果工作在windows平台的话,我建议同时安装pywin32模块。pywin32允许你像VC一样的使用python开发win32应用,更重要的是,我们可以用它直接操控win32程序,捕捉当前窗口、获取焦点等。
pip install wxpyhton
只用5行代码,我们就可以创造一个窗口程序。然并卵,不过是又一次体现了python的犀利和简洁罢了。
import wx
app = wx.App()
frame = wx.Frame(None, -1, "Hello, World!")
frame.Show(True)
app.MainLoop()


640?wx_fmt=png

下面是一个真正实用的窗口程序框架,任何一个窗口程序的开发都可以在这个基础之上展开。请注意,代码里面用到了一个图标文件,如果你要运行这段代码,请自备icon文件。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'基本框架'
APP_ICON = 'res/python.ico' # 请更换成你的icon

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    def __init__(self):
        '''构造函数'''

        wx.Frame.__init__(self, None-1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
        # 默认style是下列项的组合:wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN 

        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((800600))
        self.Center()

        # 以下代码处理图标
        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        # 以下可以添加各类控件
        pass

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame()
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp(redirect=True, filename="debug.txt")
    app.MainLoop()
注意 倒数第2行代码,是将调试信息定位到了debug.txt文件。如果mainApp()不使用任何参数,则调试信息输出到控制台。

640?wx_fmt=png

通过继承wx.Frame,我们构造了mainFrame类,可以在mainFrame类的构造函数中任意添加面板、文本、图片、按钮等各种控件了。
事件和事件驱动
不同于Qt的信号与槽机制,wx采用的是事件驱动型的编程机制。所谓事件,就是我们的程序在运行中发生的事儿。事件可以是低级的用户动作,如鼠标移动或按键按下,也可以是高级的用户动作(定义在wxPython的窗口部件中的),如单击按钮或菜单选择。事件可以产生自系统,如关机。你甚至可以创建你自己的对象去产生你自己的事件。事件会触发相应的行为,即事件函数。程序员的工作就是定义事件函数,以及绑定事件和事件函数之间的关联关系。
在wxPython中,我习惯把事件分为4类:
  • 控件事件:发生在控件上的事件,比如按钮被按下、输入框内容改变等

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

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

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

事实上,这个分类方法不够严谨。比如,wx.frame作为一个控件,关闭和改变大小也是控件事件,不过这一类事件通常都由系统绑定了行为。基于此,我可以重新定义所谓的控件事件,是指发生在控件上的、系统并未预定义行为的事件。
下面这个例子演示了如何定义事件函数,以及绑定事件和事件函数之间的关联关系。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'控件事件、鼠标事件、键盘事件、系统事件'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((520220))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        wx.StaticText(self, -1u'第一行输入框:', pos=(4050), size=(100-1), style=wx.ALIGN_RIGHT)
        wx.StaticText(self, -1u'第二行输入框:', pos=(4080), size=(100-1), style=wx.ALIGN_RIGHT)
        self.tip = wx.StaticText(self, -1u'', pos=(145110), size=(150-1), style=wx.ST_NO_AUTORESIZE)

        self.tc1 = wx.TextCtrl(self, -1'', pos=(14550), size=(150-1), name='TC01', style=wx.TE_CENTER)
        self.tc2 = wx.TextCtrl(self, -1'', pos=(14580), size=(150-1), name='TC02', style=wx.TE_PASSWORD|wx.ALIGN_RIGHT)

        btn_mea = wx.Button(self, -1u'鼠标左键事件', pos=(35050), size=(10025))
        btn_meb = wx.Button(self, -1u'鼠标所有事件', pos=(35080), size=(10025))
        btn_close = wx.Button(self, -1u'关闭窗口', pos=(350110), size=(10025))

        # 控件事件
        self.tc1.Bind(wx.EVT_TEXT, self.EvtText)
        self.tc2.Bind(wx.EVT_TEXT, self.EvtText)
        self.Bind(wx.EVT_BUTTON, self.OnClose, btn_close)

        # 鼠标事件 
        btn_mea.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        btn_mea.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        btn_mea.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
        btn_meb.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)

        # 键盘事件
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        # 系统事件
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_SIZE, self.On_size)
        #self.Bind(wx.EVT_PAINT, self.On_paint)
        #self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)

    def EvtText(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):
        '''改变窗口大小事件函数'''

        self.Refresh()
        evt.Skip() # 体会作用

    def OnClose(self, evt):
        '''关闭窗口事件函数'''

        dlg = wx.MessageDialog(Noneu'确定要关闭本窗口?'u'操作提示', wx.YES_NO | wx.ICON_QUESTION)
        if(dlg.ShowModal() == wx.ID_YES):
            self.Destroy()

    def OnLeftDown(self, evt):
        '''左键按下事件函数'''

        self.tip.SetLabel(u'左键按下')

    def OnLeftUp(self, evt):
        '''左键弹起事件函数'''

        self.tip.SetLabel(u'左键弹起')

    def OnMouseWheel(self, evt):
        '''鼠标滚轮事件函数'''

        vector = evt.GetWheelRotation()
        self.tip.SetLabel(str(vector))

    def OnMouse(self, evt):
        '''鼠标事件函数'''

        self.tip.SetLabel(str(evt.EventType))

    def OnKeyDown(self, evt):
        '''键盘事件函数'''

        key = evt.GetKeyCode() 
        self.tip.SetLabel(str(key))

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

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

640?wx_fmt=png

菜单栏/工具栏/状态栏
通常,一个完整的窗口程序一般都有菜单栏、工具栏和状态栏。下面的代码演示了如何创建菜单栏、工具栏和状态栏,顺便演示了类的静态属性的定义和用法。不过,说实话,wx的工具栏有点丑,幸好,wx还有一个 AUI 的工具栏比较漂亮,我会在后面的例子里演示它的用法。
另外,请注意,代码里面用到了4个16x16的工具按钮,请自备4个图片文件,保存路径请查看代码中的注释。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'菜单、工具栏、状态栏'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    id_open = wx.NewId()
    id_save = wx.NewId()
    id_quit = wx.NewId()

    id_help = wx.NewId()
    id_about = wx.NewId()

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((800600))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        self.Maximize()
        self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE)

        self._CreateMenuBar()         # 菜单栏
        self._CreateToolBar()         # 工具栏
        self._CreateStatusBar()       # 状态栏

    def _CreateMenuBar(self):
        '''创建菜单栏'''

        self.mb = wx.MenuBar()

        # 文件菜单
        m = wx.Menu()
        m.Append(self.id_open, u"打开文件")
        m.Append(self.id_save, u"保存文件")
        m.AppendSeparator()
        m.Append(self.id_quit, u"退出系统")
        self.mb.Append(m, u"文件")

        self.Bind(wx.EVT_MENU, self.OnOpen, id=self.id_open)
        self.Bind(wx.EVT_MENU, self.OnSave, id=self.id_save)
        self.Bind(wx.EVT_MENU, self.OnQuit, id=self.id_quit)

        # 帮助菜单
        m = wx.Menu()
        m.Append(self.id_help, u"帮助主题")
        m.Append(self.id_about, u"关于...")
        self.mb.Append(m, u"帮助")

        self.Bind(wx.EVT_MENU, self.OnHelp,id=self.id_help)
        self.Bind(wx.EVT_MENU, self.OnAbout,id=self.id_about)

        self.SetMenuBar(self.mb)

    def _CreateToolBar(self):
        '''创建工具栏'''

        bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
        bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
        bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
        bmp_about = wx.Bitmap('res/about_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片

        self.tb = wx.ToolBar(self)
        self.tb.SetToolBitmapSize((16,16))

        self.tb.AddLabelTool(self.id_open, u'打开文件', bmp_open, shortHelp=u'打开', longHelp=u'打开文件')
        self.tb.AddLabelTool(self.id_save, u'保存文件', bmp_save, shortHelp=u'保存', longHelp=u'保存文件')
        self.tb.AddSeparator()
        self.tb.AddLabelTool(self.id_help, u'帮助', bmp_help, shortHelp=u'帮助', longHelp=u'帮助')
        self.tb.AddLabelTool(self.id_about, u'关于', bmp_about, shortHelp=u'关于', longHelp=u'关于...')

        #self.Bind(wx.EVT_TOOL_RCLICKED, self.OnOpen, id=self.id_open)

        self.tb.Realize()

    def _CreateStatusBar(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(u'状态信息0'0)
        self.sb.SetStatusText(u''1)
        self.sb.SetStatusText(u'状态信息2'2)

    def OnOpen(self, evt):
        '''打开文件'''

        self.sb.SetStatusText(u'打开文件'1)

    def OnSave(self, evt):
        '''保存文件'''

        self.sb.SetStatusText(u'保存文件'1)

    def OnQuit(self, evt):
        '''退出系统'''

        self.sb.SetStatusText(u'退出系统'1)
        self.Destroy()

    def OnHelp(self, evt):
        '''帮助'''

        self.sb.SetStatusText(u'帮助'1)

    def OnAbout(self, evt):
        '''关于'''

        self.sb.SetStatusText(u'关于'1)

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()

640?wx_fmt=png

动态布局
在“事件和事件驱动”的例子里,输入框、按钮等控件的布局,使用的是绝对定位,我习惯叫做静态布局。静态布局非常直观,但不能自动适应窗口的大小变化。更多的时候,我们使用被称为布局管理器的 wx.Sizer 来实现动态布局。wx.Sizer 有很多种,我记不住,所以只喜欢用 wx.BoxSizer,最简单的一种布局管理器。
和一般的控件不同,布局管理器就像是一个魔法口袋:它是无形的,但可以装进不限数量的任意种类的控件——包括其他的布局管理器。当然,魔法口袋也不是万能的,它有一个限制条件:装到里面的东西,要么是水平排列的,要么是垂直排列的,不能排成方阵。好在程序员可以不受限制地使用魔法口袋,当我们需要排成方阵时,可以先每一行使用一个魔法口袋,然后再把所有的行装到一个魔法口袋中。
创建一个魔法口袋,装进几样东西,然后在窗口中显示的伪代码是这样的:
魔法口袋 = 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个参数就是留白像素数。
下面是一个完整的例子。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'动态布局'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(240240240))
        self.SetSize((800600))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        preview = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
        preview.SetBackgroundColour(wx.Colour(000))
        btn_capture = wx.Button(self, -1u'拍照', size=(100-1))
        btn_up = wx.Button(self, -1u'↑', size=(3030))
        btn_down = wx.Button(self, -1u'↓', size=(3030))
        btn_left = wx.Button(self, -1u'←', size=(3030))
        btn_right = wx.Button(self, -1u'→', size=(3030))
        tc = wx.TextCtrl(self, -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.BoxSizer(wx.VERTICAL)
        sizer_arrow = wx.StaticBoxSizer(wx.StaticBox(self, -1u'方向键'), 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(preview, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5)
        sizer_max.Add(sizer_right, 0, wx.EXPAND|wx.ALL, 0)

        self.SetAutoLayout(True)
        self.SetSizer(sizer_max)
        self.Layout()

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()

640?wx_fmt=png

AUI布局
Advanced User Interface,简称AUI,是 wxPython 的子模块,使用 AUI 可以方便地开发出美观、易用的用户界面。从2.8.9.2版本之后,wxPython 增加了一个高级通用部件库 Advanced Generic Widgets,简称 AGW 库。我发先 AGW 库也提供了 AUI 模块 wx.lib.agw.aui,而 wx.aui 也依然保留着。
AUI布局可以概括为以下四步:
  • 创建一个布局管理器:mgr = aui.AuiManager()

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

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

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

下面的代码演示了如何使用AUI布局管理器创建和管理窗口界面。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os
import wx.lib.agw.aui as aui

APP_TITLE = u'使用AUI布局管理器'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    id_open = wx.NewId()
    id_save = wx.NewId()
    id_quit = wx.NewId()

    id_help = wx.NewId()
    id_about = wx.NewId()

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((800600))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        self.tb1 = self._CreateToolBar()
        self.tb2 = self._CreateToolBar()
        self.tbv = self._CreateToolBar('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, -1u'切换', pos=(30,200), size=(100-1))
        btn.Bind(wx.EVT_BUTTON, self.OnSwitch)

        text0 = wx.StaticText(p_center0, -1u'我是第1页', pos=(40100), size=(200-1), style=wx.ALIGN_LEFT)
        text1 = wx.StaticText(p_center1, -1u'我是第2页', pos=(40100), 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(u"工具条").ToolbarPane().Top().Row(0).Position(0).Floatable(False)
        )
        self._mgr.AddPane(self.tb2, 
            aui.AuiPaneInfo().Name("ToolBar2").Caption(u"工具条").ToolbarPane().Top().Row(0).Position(1).Floatable(True)
        )
        self._mgr.AddPane(self.tbv, 
            aui.AuiPaneInfo().Name("ToolBarV").Caption(u"工具条").ToolbarPane().Right().Floatable(True)
        )

        self._mgr.AddPane(p_left,
            aui.AuiPaneInfo().Name("LeftPanel").Left().Layer(1).MinSize((200,-1)).Caption(u"操作区").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(u"消息区").CaptionVisible(False).Resizable(True)
        )

        self._mgr.Update()

    def _CreateToolBar(self, d='H'):
        '''创建工具栏'''

        bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY)
        bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY)
        bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY)
        bmp_about = wx.Bitmap('res/about_16.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(1616))

        tb.AddSimpleTool(self.id_open, u'打开', bmp_open, u'打开文件')
        tb.AddSimpleTool(self.id_save, u'保存', bmp_save, u'保存文件')
        tb.AddSeparator()
        tb.AddSimpleTool(self.id_help, u'帮助', bmp_help, u'帮助')
        tb.AddSimpleTool(self.id_about, u'关于', bmp_about, u'关于')

        tb.Realize()
        return tb

    def OnSwitch(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()

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()

640?wx_fmt=png

DC绘图
DC 是 Device Context 的缩写,字面意思是设备上下文——我一直不能正确理解DC这个中文名字,也找不到更合适的说法,所以,我坚持使用DC而不是设备上下文。DC可以在屏幕上绘制点线面,当然也可以绘制文本和图像。事实上,在底层所有控件都是以位图形式绘制在屏幕上的,这意味着,我们一旦掌握了DC这个工具,就可以自己创造我们想要的控件了。
DC有很多种,PaintDC,ClientDC,MemoryDC等。通常,我们可以使用 ClientDC 和 MemoryDC,PaintDC 是发生重绘事件(wx.EVT_PAINT)时系统使用的。使用 ClientDC 绘图时,需要记录绘制的每一步工作,不然,系统重绘时会令我们前功尽弃——这是使用DC最容易犯的错误。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'使用DC绘图'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((800600))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        self.palette = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
        self.palette.SetBackgroundColour(wx.Colour(000))
        btn_base = wx.Button(self, -1u'基本方法', 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.OnBase)
        self.palette.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
        self.palette.Bind(wx.EVT_PAINT, self.OnPaint)

        self.xy = None
        self.lines = list()
        self.img = wx.Bitmap('res/times.png', wx.BITMAP_TYPE_ANY)


        self.ReDraw()

    def OnMouse(self, evt):
        '''移动鼠标画线'''

        if evt.EventType == 10032#左键按下,py3环境下为10030
            self.xy = (evt.x, evt.y)
        elif evt.EventType == 10033#左键弹起,py3环境下为10031
            self.xy = None
        elif evt.EventType == 10038#鼠标移动,py3环境下为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 OnBase(self, evt):
        '''DC基本方法演示'''

        img = wx.Bitmap('res/times.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, 5050)

        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(u'霜重闲愁起'100500)
        dc.DrawRotatedText(u'春深风也疾'25050030)

    def OnPaint(self, evt):
        '''重绘事件函数'''

        dc = wx.PaintDC(self.palette)
        self.Paint(dc)

    def ReDraw(self):
        '''手工绘制'''

        dc = wx.ClientDC(self.palette)
        self.Paint(dc)

    def Paint(self, dc):
        '''绘图'''

        w, h = self.palette.GetSize()

        dc.Clear()
        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(self.img, 5050)

        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(u'霜重闲愁起'100500)
        dc.DrawRotatedText(u'春深风也疾'25050030)

        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])

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()
640?wx_fmt=png
定时器和线程
这个例子里面设计了一个数字式钟表,一个秒表,秒表显示精度十分之一毫秒。从代码设计上来说没有任何难度,实现的方法有很多种,可想要达到一个较好的显示效果,却不是一件容易的事情。请注意体会 wx.CallAfter() 的使用条件。
#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os, time
import threading

APP_TITLE = u'定时器和线程'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
    '''程序主窗口类,继承自wx.Frame'''

    def __init__(self, parent):
        '''构造函数'''

        wx.Frame.__init__(self, parent, -1, APP_TITLE)
        self.SetBackgroundColour(wx.Colour(224224224))
        self.SetSize((320300))
        self.Center()

        if hasattr(sys, "frozen"and getattr(sys, "frozen") == "windows_exe":
            exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
            icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        else :
            icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        #font = wx.Font(24, wx.DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS')
        font = wx.Font(30, wx.DECORATIVE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False'Monaco')

        self.clock = wx.StaticText(self, -1u'08:00:00', pos=(50,50), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER)
        self.clock.SetForegroundColour(wx.Colour(022432))
        self.clock.SetBackgroundColour(wx.Colour(000))
        self.clock.SetFont(font)

        self.stopwatch = wx.StaticText(self, -1u'0:00:00.0', pos=(50,150), size=(200,50), style=wx.TE_CENTER|wx.SUNKEN_BORDER)
        self.stopwatch.SetForegroundColour(wx.Colour(022432))
        self.stopwatch.SetBackgroundColour(wx.Colour(000))
        self.stopwatch.SetFont(font)

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.timer.Start(50)

        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        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 OnTimer(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 OnKeyDown(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.0')

    def StopWatchThread(self):
        '''线程函数'''

        while True:
            if self.is_start:
                n = int(10*(time.time() - self.t_start))
                deci = n%10
                ss = int(n/10)%60
                mm = int(n/600)%60
                hh = int(n/36000)
                wx.CallAfter(self.stopwatch.SetLabel, '%d:%02d:%02d.%d'%(hh, mm, ss, deci))
            time.sleep(0.02)

class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.Frame = mainFrame(None)
        self.Frame.Show()
        return True

if __name__ == "__main__":
    app = mainApp()
    app.MainLoop()

640?wx_fmt=png

后记
我使用 wxPython 长达十年。它给了我很多的帮助,它让我觉得一切就该如此。这是我第一次写关于 wxPython 的话题,写作过程中,我心存感激。
扫码查看原文
▼▼▼
640?wx_fmt=jpeg

(*本文为AI科技大本营转载文章,转载请微信联系 1092722531



精彩推荐




2019 中国大数据技术大会(BDTC)再度来袭!豪华主席阵容及百位技术专家齐聚,15 场精选专题技术和行业论坛,超强干货+技术剖析+行业实践立体解读,深入解析热门技术在行业中的实践落地。6.6 折票限时特惠(立减1400元),学生票仅 599 元!


640?wx_fmt=png


推荐阅读

相关文章:

Swift 中使用 SQLite——修改和删除数据

本文主要介绍在SQLite中修改数据、删除数据&#xff1a; 更新记录 /// 将当前对象信息更新到数据库 /// /// - returns: 是否成功 func updatePerson() -> Bool {guard let name name else {print("姓名不能为空")return false}if id < 0 {print("id 不…

用python3实现指定目录下文件sha256及文件大小统计

有时会统计某个目录下有哪些文件&#xff0c;每个文件的sha256及文件大小等相关信息&#xff0c;这里用python3写了个脚本用来实现此功能&#xff0c;此脚本可跨平台&#xff0c;同时支持windows和linux&#xff0c;脚本(get_dir_file_info.py)内容如下&#xff1a; import os…

Swift 中使用 SQLite——新增数据

本文重点介绍两个方面&#xff0c;1、新增数据&#xff0c;2、获取自动增长 ID。 建立 Person.swift 数据模型 /// 个人模型 class Person: NSObject {// MARK: - 模型属性/// 代号var id: Int64 0/// 姓名var name: String?/// 年龄var age 0/// 身高var height: Double …

投稿2877篇,EMNLP 2019公布4篇最佳论文

整理 | AI科技大本营&#xff08;ID:rgznai100&#xff09;近日&#xff0c;自然语言处理领域的顶级会议之一EMNLP 2019公布了年度最佳论文。EMNLP是由国际语言学会&#xff08;ACL&#xff09;下属的SIGDAT小组主办的自然语言处理领域的顶级国际会议&#xff0c;是自然语言算法…

对象检测工具包mmdetection简介、安装及测试代码

mmdetection是商汤和港中文大学联合开源的基于PyTorch的对象检测工具包&#xff0c;属于香港中文大学多媒体实验室open-mmlab项目的一部分。该工具包提供了已公开发表的多种流行的检测组件&#xff0c;通过这些组件的组合可以迅速搭建出各种检测框架。 mmdetection主要特性&am…

(转)eclipse 代码自动补全

转自&#xff1a;http://blog.csdn.net/yushuwai2010/article/details/11856129 一般默认情况下&#xff0c;Eclipse的代码提示功能是比MicrosoftVisualStudio的差很多的&#xff0c;主要是Eclipse本身有很多选项是默认关闭的&#xff0c;要开发者自己去手动配置。如果开发者不…

swift 多线程GCD和延时调用

GCD 是一种非常方便的使用多线程的方式。通过使用 GCD&#xff0c;我们可以在确保尽量简单的语法的前提下进行灵活的多线程编程。在 “复杂必死” 的多线程编程中&#xff0c;保持简单就是避免错误的金科玉律。好消息是在 Swift 中是可以无缝使用 GCD 的 API 的&#xff0c;而且…

目标检测算法Faster R-CNN简介

在博文https://blog.csdn.net/fengbingchun/article/details/87091740 中对Fast R-CNN进行了简单介绍&#xff0c;这里在Fast R-CNN的基础上简单介绍下Faster R-CNN。 目标检测领域从R-CNN开始&#xff0c;通过引入卷积神经网络取得了很多突破性的进展&#xff0c;但是始终未能…

ICCV 2019 | 加一个任务路由让数百个任务同时跑起来,怎么做到?

作者 | Gjorgji Strezoski, Nanne van Noord, Marcel Worring 译者 | 中国海洋大学李杰 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;摘要传统的多任务&#xff08;MTL&#xff09;学习方法依赖于架构调整和大型可训练参数集来联合优化多个任务。但是&#xff0c;…

DEV开发之控件NavBarControl

右键点击RunDesigner弹出如下界面鼠标先点击3或4,1&#xff0c;&#xff0c;然后点击1或2进行相应的新增或删除操作&#xff0c;3是分组&#xff0c;4是项目&#xff0c;4可以直接拖动到相应的分组3.属性caption:显示的名称4.NavBarControl 属性 PaintStyleName绘画风格&…

swift支持多线程操作数据库类库-CoreDataManager

类库方法 获取数据 executeFetchRequest(request:) 同步获取数据 var request: NSFetchRequest NSFetchRequest(entityName: "MonkeyEntity")var myMonkeys:NSArray? CoreDataManager.shared.executeFetchRequest(request)异步获取数据 executeFetchRequest(re…

目标检测(或分隔)算法Mask R-CNN简介

在博文https://blog.csdn.net/fengbingchun/article/details/87195597 中对Faster R-CNN进行了简单介绍&#xff0c;这里在Faster R-CNN的基础上简单介绍下Mask R-CNN。 Mask R-CNN是faster R-CNN的扩展形式&#xff0c;能够有效地检测图像中的目标&#xff0c;并且Mask R-CNN…

未来之城,管理者可能不是人......

大会官网 https://t.csdnimg.cn/KSTh2010 年&#xff0c;IBM 正式提出了“智慧地球”愿景。在 IBM 的设想中&#xff0c;智慧城市应该由六个核心系统组成&#xff1a;组织&#xff08;人&#xff09;、业务/政务、交通、通讯、水和能源。&#xff08;图源 | IBM 官网&#xff0…

UVa 10701 - Pre, in and post

题目&#xff1a;已知树的前根序&#xff0c;中根序遍历转化成后根序遍历。 分析&#xff1a;递归&#xff0c;DS。依据定义递归求解就可以。 前根序&#xff1a;根&#xff0c;左子树&#xff0c;右子树&#xff1b; 中根序&#xff1a;左子树&#xff0c;根&#xff0c;右子树…

图像集存储成MNIST数据集格式实现

有时会用到将一组图像存放成MNIST中那样的数据格式&#xff0c;以便于用于网络的训练和测试&#xff0c;如MNSIT中的测试集标签t10k-labels.idx1-ubyte和测试集图像t10k-images.idx3-ubyte&#xff0c;各包含了10000个样本&#xff0c;这里以此两个测试集为例详细说明下实现过程…

ios9定位服务的app进入后台三分钟收不到经纬度,应用被挂起问题及解决方案

原来定位服务是10分钟收不到定位信息就挂起定位&#xff0c;现在变为最短3分钟&#xff0c;估计都是为了省电吧。只要你开启应用的后台定位&#xff0c;并且10分钟有一次定位&#xff0c;那么苹果就不会关闭你的线程&#xff0c;现在变成3分钟。若你的应用开启了后台定位&#…

程序员必知的20个Python技巧

作者 | Duomly 译者 | 弯月&#xff0c;编辑 | 郭芮 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;Python是一门流行且应用广泛的通用编程语言&#xff0c;其应用包括数据科学、机器学习、科学计算等领域&#xff0c;以及后端Web开发、移动和桌面应用程序等方面。…

CSS float浮动的深入研究、详解及拓展(二)

为什么80%的码农都做不了架构师&#xff1f;>>> 接上回… 五、浮动的非本职工作 浮动的本职工作是让匿名inline boxes性质的文字环绕图片显示&#xff0c;而其他所有用浮动实现的效果都不是浮动应该做的事情&#xff0c;我称之为“非本职工作”。 或许我们并没有…

不需要显示地图 就获得用户当前经纬度 超简单的方法

1.遵循协议 CLLocationManagerDelegate,AMapSearchDelegate,AMapLocationManagerDelegate 2. API MAMapServices.sharedServices().apiKey APIKey AMapLocationServices.sharedServices().apiKey APIKey AMapSearchServices.sharedServices().apiKey APIKey AMapNaviService…

ELECTRA:超越BERT,2019年最佳NLP预训练模型

作者 | 李如来源 | NLPCAB&#xff08;ID:rgznai100&#xff09;【导读】BERT推出这一年来&#xff0c;除了XLNet&#xff0c;其他的改进都没带来太多惊喜&#xff0c;无非是越堆越大的模型和数据&#xff0c;以及动辄1024块TPU&#xff0c;让工程师们不知道如何落地。今天要介…

安装node和spm过程

2019独角兽企业重金招聘Python工程师标准>>> 安装nodejs 官网下载nodejs&#xff0c;我下的是v0.10.33版本&#xff0c;安装到d:\nodejs下。 1.新建目录d:\nodejs&#xff0c;在其中建立node_cache、node_global、node_modules三个目录。 2&#xff0c;将C:\Users…

经典网络LeNet-5介绍及代码测试(Caffe, MNIST, C++)

LeNet-5&#xff1a;包含7个层(layer)&#xff0c;如下图所示&#xff1a;输入层没有计算在内&#xff0c;输入图像大小为32*32*1&#xff0c;是针对灰度图进行训练和预测的。论文名字为” Gradient-Based Learning Applied to Document Recognition”&#xff0c;可以直接从ht…

根据经纬度获取用户当前位置信息

根据上篇文章获取的经纬度获取用户当前的位置信息 //获取用户所在位置信息ADDRESS func getUserAddress() { let latitude : CLLocationDegrees LATITUDES! let longitude : CLLocationDegrees LONGITUDES! print("latitude:\(latitude)") print("longitude…

刷了几千道算法题,我私藏的刷题网站都在这里了

作者 | Rocky0429 来源 | Python空间&#xff08;ID: Devtogether&#xff09;遥想当年&#xff0c;机缘巧合入了 ACM 的坑&#xff0c;周边巨擘林立&#xff0c;从此过上了"天天被虐似死狗"的生活...然而我是谁&#xff0c;我可是死狗中的战斗鸡&#xff0c;智力不够…

js实现点击li标签弹出其索引值

据说这是一道笔试题&#xff0c;以下是代码&#xff0c;没什么要文字叙述的&#xff0c;就是点击哪个<li>弹出哪个<li>的索引值即可&#xff1a; <html> <head> <style> li{width:50px;height:30px;margin:5px;float:left;text-align: center;li…

定时器开启和关闭

写程序时遇见了定时器&#xff0c;需要写入数据库用户的经纬 &#xff0c;还要读取&#xff0c;写好之后发现很费电 总结原因&#xff1a; 1&#xff1a;地图定位耗电&#xff08;这个根据程序要求&#xff0c;不能关闭&#xff0c;需要实时定位&#xff0c;很无奈&#xff…

一览群智胡健:在中国完全照搬Palantir模式,这不现实

作者 | Just出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;神秘的硅谷大数据挖掘公司 Palantir 是国内众多创业公司看齐的标杆&#xff0c;其业务是为政府和金融领域的大客户提供数据分析服务&#xff0c;帮助客户作出判断&#xff0c;甚至“预知未来”&#xff0c;…

ImageNet图像数据集介绍

ImageNet图像数据集始于2009年&#xff0c;当时李飞飞教授等在CVPR2009上发表了一篇名为《ImageNet: A Large-Scale Hierarchical Image Database》的论文&#xff0c;之后就是基于ImageNet数据集的7届ImageNet挑战赛(2010年开始)&#xff0c;2017年后&#xff0c;ImageNet由Ka…

cocos2dx 场景的切换

我们知道cocos2dx中可以由多个场景组成&#xff0c;那么我是如何来切换场景的呢首先我们先新建一个新的场景类&#xff0c;我推荐的方式是&#xff0c;在你工程的目录中找到一个classes的文件夹&#xff0c;里面有AppDelegate.cpp和AppDelegate.h还有HelloWorldScene.cpp和Hell…

IOS 后台挂起程序 当程序到后台后,继续完成定位任务

// 当应用程序掉到后台时&#xff0c;执行该方法 - (void)applicationDidEnterBackground:(UIApplication *)application { } 当一个 iOS 应用被送到后台,它的主线程会被暂停。你用 NSThread 的 detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。 我…