介绍 | 图像查看器 | 增强型图像查看器 | 数据库查看器 | 数据库编辑器 | 计算器 | 压缩包归档器 | MP3 标签编辑器 | PDF合并/拆分实用程序 | 文件搜索实用程序 | FTP应用程序 | XML 编辑器 | 分发应用程序 | wxPython 演示 | 小部件检查工具
GUI
在您深入研究 wxPython 之前,我认为最好先解释一下 GUI 是什么。 图形用户界面是在屏幕上绘制的界面,然后用户可以与之交互。 用户界面由几个常见组件组成,例如:
- 主窗口
- 菜单/工具栏
- 按钮
- 文本输入
- 标签
这些统称为小部件。 wxPython 工具包提供了几十个小部件,包括许多用纯 Python 编写的复杂的自定义小部件。 作为开发人员,您将使用这些小部件并以令人愉悦的方式排列它们,以便用户与之交互。
示例-窗口
当您使用 wxPython 创建用户界面时,您几乎总是需要创建一个 wx.Frame 和一个 wx.Panel。 wx.Frame 是包含所有其他小部件的窗口对象。 它实际上是一个框架。 面板有点不同。 它也是一个容器,但它还支持在小部件之间切换的能力。 如果没有面板,选项卡将无法按您预期的方式工作。 您也可以使用面板对小部件进行分组。
让我们创建一个示例Frame来开始:
import wx
app = wx.App(False)
frame = wx.Frame(None, title='Hello World')
frame.Show()
app.MainLoop()
你会注意到的第一件事是你导入了 wx 模块。 这是一个关键的导入,因为您将需要它用于任何 wxPython 的核心小部件。 接下来实例化 Application 对象:wx.App。 你必须有一个 wx.App 实例才能在 wxPython 中运行任何东西。 但是,您一次可能只有其中之一。 你会注意到我已经传入了 False 作为它的第一个参数。 这样做是为了防止 wxPython 捕获 stdout 并将其重定向到由 wxPython 自动生成的新框架。 你可以试试这个,因为它对调试很有用,但不是你想在大多数时候在生产中启用的东西。
对于下一步,您将创建 wx.Frame 实例。 框架有一个必需的参数。
虽然看到上面的内容是非常标准的,但为了更明确,您可以将该行重写为以下内容:
frame = wx.Frame(parent=None, title=‘Hello World’)
如您所见,框架要求您传入父级。 在这种情况下,由于这是应用程序的主要入口点,因此将父项设置为无。 我们还将 title 参数设置为一个字符串,因为如果你没有,那么它默认为一个空字符串,这有点无聊。 接下来调用框架的 Show() 方法使其在屏幕上可见。
最后,要让应用程序本身运行,您必须调用应用程序对象的 MainLoop() 方法。 这将启动事件循环,以便您的 wxPython 应用程序可以响应键盘和小部件事件。 运行此代码时,您应该会看到一个如下所示的窗口:
尽管此代码有效,但您很少会编写与上述示例类似的代码。 相反,您将阅读和编写的大多数 wxPython 代码都放在类中。
示例-类
将 wxPython 中的大多数代码放入类的原因是因为您希望使您的代码更加模块化。 为此,您将与框架相关的小部件放在框架类中,并将分组到面板类中的小部件放入面板中。 你会发现在所有 Python 的 GUI 框架中都是如此,比如 Tkinter 或 PyQt。
让我们花点时间更新上面的示例,使其使用类:
import wx
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Hello World')
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
在本例中,您将继承 wx.Frame 并将您的子类命名为 MyFrame。 然后,您以与以前相同的方式设置框架,只是框架的代码进入了您的 init() 方法。 您还需要调用 self.Show() 使框架可见。 应用程序的创建仍然像以前一样在代码的末尾。 您还可以在此处实例化您的新框架类。
您还没有完成修改。 Python 3 建议在处理类时使用 super()。 wxPython 中内置 super() 函数的主要用途是用于引用父类,而无需对其进行实际命名。 如果你有空闲时间,我强烈推荐你参考Raymond Hettinger 关于 super() 的文章,因为它非常有助于理解为什么它如此有用。
不管怎样,让我们更新你的代码,让它也使用 super():
import wx
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='Hello World')
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
你会看到很多没有使用 super() 的遗留代码。 但是,由于本文使用Python 3,因此您将使用良好的实践并在示例中使用 super()。
请注意,在 Python 2 中,您需要像这样调用 super:
super(MyFrame, self).init(None, title=‘Hello World’)
让我们继续并在您的示例中添加一个带有按钮的 Panel 类:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
button = wx.Button(self, label='Press Me')
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='Hello World')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
在此添加一个包含一个小部件的面板:一个按钮。 您会注意到面板应该有一个父级,在本例中是一个 Frame。 不过,您可以让其他小部件成为面板的父级。 例如,您可以将面板相互嵌套,或者将 wx.Notebook 制作到它们的父面板中。 无论如何,您只需要一个面板作为框架的唯一小部件。 如果面板是框架的唯一子小部件,面板也会自动扩展以填充框架。 如果您在框架中添加一个面板和一个按钮而不给它们一个位置或将它们放入 sizer,那么它们最终会堆叠在彼此的顶部。
注意:wx.Panel 小部件在 Windows 上启用小部件之间的选项卡。 因此,如果您希望能够在您创建的表单中浏览小部件,您需要有一个面板作为它们的父级。
无论如何,当我运行这段代码时,它最终看起来像这样:
事件
事件是用户使用您的应用程序时发生的事情。 例如,当用户在您的应用程序处于焦点时按下键盘上的按钮时,这将触发 KeyEvent。 如果用户单击您应用程序上的小部件,它将触发某种小部件事件。 您可以通过创建事件绑定来捕获这些事件。 这意味着您正在为您希望应用程序做出反应的特定事件创建一个侦听器。 例如,如果您的应用程序中有一个按钮,您可能希望该按钮在用户按下时执行某些操作。 要真正让按钮做某事,您需要将按钮绑定到按钮按下事件。
让我们更新前面的例子,让按钮真正做一些事情:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
button = wx.Button(self, label='Press Me')
button.Bind(wx.EVT_BUTTON, self.on_button_press)
def on_button_press(self, event):
print('You pressed the button')
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='Hello World')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
这里调用按钮的 Bind() 方法并告诉它绑定到一个事件:wx.EVT_BUTTON。 这是按钮按下事件。 第二个参数是按下按钮时要调用的函数。 最后创建事件处理函数 on_button_press()。 您会注意到它需要一个事件参数。 当您在 wxPython 中捕获一个事件时,它会将一个事件对象传递给您绑定到该事件的函数。 这个事件对象通常包含识别调用该函数的小部件的信息以及一堆其他信息。
如果您运行此代码,您应该会看到每次按下按钮时都会将“您按下按钮”打印到标准输出。 试一试!
在继续之前,我想提一下,您还可以像这样绑定事件:
self.Bind(wx.EVT_BUTTON, self.on_button_press, button)
如果你以这种方式进行绑定,你是在告诉 wxPython 你正在将函数绑定到 wx.Panel 而不是 wx.Button。 这允许我们将多个小部件绑定到同一个事件但不同的事件处理程序,然后使用 event.Skip() 来控制哪些事件在图层上冒泡。 在这个例子中,按钮在底层,面板在下一层,框架在顶层。
让我们再更新一次代码:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
button = wx.Button(self, label='Press Me')
self.Bind(wx.EVT_BUTTON, self.panel_button_handler, button)
button.Bind(wx.EVT_BUTTON, self.on_button_press)
def panel_button_handler(self, event):
print('panel_button_handler called')
def on_button_press(self, event):
print('on_button_press called')
event.Skip()
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='Hello World')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
在这里,您将 EVT_BUTTON 绑定到面板和按钮对象,但您让它们调用不同的事件处理程序。 当您按下按钮时,它的事件处理程序会立即被调用并打印出适当的字符串。 然后调用 event.Skip() 以便 EVT_BUTTON 事件上升到下一个事件处理程序(如果存在)。 在这种情况下,您有一个用于面板并且它也会触发。 如果您愿意,也可以将框架绑定到 EVT_BUTTON 并在那里捕获它。 在任何这些点上,您都可以删除对 event.Skip() 的调用,并且该事件将停止在该事件处理程序处传播。
绝对定位 vs 尺码器
wxPython 支持小部件的绝对定位和小部件的相对定位。 小部件的相对定位需要使用称为 Sizer 或 Layouts 的特殊容器对象。 sizer 允许您调整应用程序的大小并让小部件随之调整大小。 如果您使用绝对定位,则小部件根本无法扩展或更改位置,因此当您调整应用程序大小时,您会发现某些小部件可能会被切断。 如果您在使用低分辨率显示器的计算机上加载应用程序,也会发生这种情况。 始终建议您使用 sizer,因为它们将使您的应用程序更适合在多个显示器和屏幕尺寸上使用。
如果您想尝试使用绝对定位,您需要做的就是更新对您之前创建的 wx.Button 实例化的调用。
这是示例,
button = wx.Button(self, label=‘Press Me’, pos=(100, 10))
这里的新参数称为位置的 pos。 它需要一个以像素为单位的 x 和 y 坐标元组。 起始位置或原点是左上角或 (0, 0)。 在上面的示例中,您告诉 wxPython 将按钮放置在距面板左侧 100 像素和顶部 10 像素的位置。
当你这样做时,它是这样的:
现在让我们尝试使用 sizer。
wxPython 工具包有几个可以使用的 sizer:
wx.BoxSizer
wx.StaticBoxSizer
wx.GridSizer
wx.FlexGridSizer
wx.WrapSizer
您还可以将 sizer 相互嵌套。 出于演示目的,您将重点关注 wx.BoxSizer。 让我们尝试将应用程序中的按钮水平和垂直居中。
示例1:wxPython中面板切换
示例2:Raspberry Pi控制wxPython仪表盘
示例3:wxPython异步等待示例
详情参阅 - 亚图跨际