3

我试图理解 wxPython,但那里的大多数文档只是以猴子见猴子的方式呈现程序,而没有解释库的基础知识。

考虑这段代码:

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, (-1, -1), wx.Size(250, 50))
        panel = wx.Panel(self, -1)
        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(wx.Button(panel, -1, 'Button1'), 1 )
        box.Add(wx.Button(panel, -1, 'Button2'), 1 )
        box.Add(wx.Button(panel, -1, 'Button3'), 1 )
        panel.SetSizer(box)
        self.Centre()

class MyApp(wx.App):
     def OnInit(self):
         frame = MyFrame(None, -1, 'wxboxsizer.py')
         frame.Show(True)
         return True

app = MyApp(0)
app.MainLoop()

我在这里看到三个容器——一个框架、一个面板和一个盒子。

然后是三个按钮。

  1. 有人可以解释哪个容器进入哪个容器吗?
  2. 面板是否进入框架?如果是这样,它在哪里添加到框架中?
  3. 盒子呢?它进入面板,还是面板进入它?
  4. 按钮去了吗?是在盒子里吗?
  5. 为什么 panel.SetSizer() 用在一个地方,而 box.Add() 用在另一个地方?
4

3 回答 3

4

让我们慢慢开发一个 wxPython 应用程序,看看它是如何工作的。

这是制作 wxPython 应用程序所需的最少代码量。它包含一个 wx.Frame(您可能将其理解为一个窗口)。窗户里什么都没有。app.MainLoop() 是捕获任何事件的循环,例如鼠标点击、关闭或最小化窗口。

import wx

app = wx.App()

frame = wx.Frame(None, -1, 'A Frame')
frame.Show()

app.MainLoop()

一个窗口本身不是很有趣,但仍然相当强大。我们可以添加菜单项和标题之类的内容,甚至可以选择样式,例如 No Maximize 按钮。让我们做这一切。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar() # Create a menubar
        fileMenu = wx.Menu() # Create the file menu
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application') # Add a quit line
        menubar.Append(fileMenu, '&File') # Add the File menu to the Menubar
        self.SetMenuBar(menubar) # Set the menubar as THE menu bar

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem) # Bind the quit line

        self.Show() # Show the frame

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX) # Some styles
app.MainLoop()

你会注意到事情很快变得有点复杂。这实际上是为了帮助我们保持井井有条。我们将框架移动到它自己的类中,并定义了一些特征。我们要求一个菜单,将菜单项绑定到OnQuit()关闭应用程序的方法。这都是最基本的一层。

让我们添加一个面板。面板就像黑板。它位于 wx.Frame 的顶部(就像黑板靠墙放置)。一旦我们有了一个面板,我们就可以开始添加大小调整器和小部件。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application')
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem)

        panel = wx.Panel(self, -1) # Added a panel!

        self.Show()

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX)
app.MainLoop()

根据您的平台,您会注意到略有不同。窗口现在已填充,颜色较浅。那是我们的面板。现在让我们添加一些按钮。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application')
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem)

        panel = wx.Panel(self, -1)
        btn = wx.Button(panel, label='I am a closing button.') # Add a button
        btn.Bind(wx.EVT_BUTTON, self.OnQuit) # Bind the first button to quit
        btn2 = wx.Button(panel, label='I am a do nothing button.') # Add a second

        self.Show()

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX)
app.MainLoop()

现在我们有了面板上的按钮。但它们看起来很糟糕。他们被卡在另一个上面。我们可以使用 pos=(x,y) 属性手动定位它们,但这非常乏味。让我们把我们的朋友称为 boxsizer。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application')
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem)

        panel = wx.Panel(self, -1)
        btn = wx.Button(panel, label='I am a closing button.')
        btn.Bind(wx.EVT_BUTTON, self.OnQuit)
        btn2 = wx.Button(panel, label='I am a do nothing button.')

        vbox = wx.BoxSizer(wx.VERTICAL) # Create a vertical boxsizer
        vbox.Add(btn) # Add button 1 to the sizer
        vbox.Add(btn2) # Add button 2 to the sizer

        panel.SetSizer(vbox) # Tell the panel to use this sizer as its sizer. 

        self.Show()

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX)
app.MainLoop()

那已经好多了!让我们看看水平尺寸器是什么样子的。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application')
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem)

        panel = wx.Panel(self, -1)
        btn = wx.Button(panel, label='I am a closing button.')
        btn.Bind(wx.EVT_BUTTON, self.OnQuit)
        btn2 = wx.Button(panel, label='I am a do nothing button.')

        vbox = wx.BoxSizer(wx.VERTICAL) # A vertical sizer
        hbox = wx.BoxSizer(wx.HORIZONTAL) # And a horizontal one?? but why?
        hbox.Add(btn) # Let's add the buttons first
        hbox.Add(btn2)

        panel.SetSizer(hbox) # see why we need to tell the panel which sizer to use? We might have two!

        self.Show()

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX)
app.MainLoop()

有趣的!注意我是如何保留我们的 vbox 的?让我们尝试将两者结合起来。我们需要更多的按钮,也许还需要一些 TextCtrls。

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(Frame, self).__init__(*args, **kwargs)

        menubar = wx.MenuBar()
        fileMenu = wx.Menu()
        fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quits application')
        menubar.Append(fileMenu, '&File')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnQuit, fitem)

        panel = wx.Panel(self, -1)
        btn = wx.Button(panel, label='I am a closing button.')
        btn.Bind(wx.EVT_BUTTON, self.OnQuit)
        btn2 = wx.Button(panel, label='I am a do nothing button.')
        txt1 = wx.TextCtrl(panel, size=(140,-1))
        txt2 = wx.TextCtrl(panel, size=(140,-1))
        txt3 = wx.TextCtrl(panel, size=(140,-1))
        btn3 = wx.Button(panel, label='I am a happy button.')
        btn4 = wx.Button(panel, label='I am a bacon button.')
        btn5 = wx.Button(panel, label='I am a python button.')


        # So many sizers! 
        vbox = wx.BoxSizer(wx.VERTICAL) 
        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
        hbox3 = wx.BoxSizer(wx.HORIZONTAL)
        hbox4 = wx.BoxSizer(wx.HORIZONTAL)
        hbox1.Add(btn)
        hbox1.Add(btn2)
        hbox1.Add(txt1)
        hbox2.Add(txt2)
        hbox2.Add(txt3)
        hbox2.Add(btn3)
        hbox3.Add(btn4)
        hbox4.Add(btn5)
        vbox.Add(hbox1)
        vbox.Add(hbox2)
        vbox.Add(hbox3)
        vbox.Add(hbox4)

        panel.SetSizer(vbox)

        self.Show()

    def OnQuit(self, e):
        self.Close()

app = wx.App()
Frame(None, -1, 'A Frame', style=wx.RESIZE_BORDER 
    | wx.SYSTEM_MENU | wx.CAPTION |  wx.CLOSE_BOX)
app.MainLoop()

我们首先将按钮和文本小部件添加到水平尺寸器,然后再添加到垂直尺寸器。我们在 sizers 中有 sizers,这是 wxPython 中非常常见的一种处理方式。

__________________________
|__hbox1_|_______________|  \
|_hbox2____|______|____|_|   \___VBOX
|___hbox3______|_________|   /
|_______|__hbox4_|_______|  /

有点像。在每个 hbox 中,我们有许多小部件。你想要多少,看你自己。希望这可以帮助。

于 2013-08-21T13:23:06.360 回答
3

wxPython 很复杂,但其他 GUI 工具包也很复杂。让我们分解一下。要回答第一个问题,wxPython GUI 的通常布局是以下之一:

frame -> panel -> sizer -> widgets
frame -> sizer -> panel -> sizer -> widgets

我通常选择第一个。如果布局很复杂,我可能会嵌套 sizers,所以我最终会得到这样的结果:

frame -> panel -> sizer -> sizer1 -> widgets
                        -> sizer2 -> widgets

2) 第一个面板应始终作为其唯一的小部件添加到框架中:

wx.Frame.__init__(self, None, title="Test")
panel = wx.Panel(self)

3) boxSizer 通常进入面板。我通常有一个顶级 boxSizer,我将它提供给面板,然后在其中创建嵌套的 sizer。

4) 按钮和其他小部件同时出现在面板和尺寸器中!您将按钮的父级设置为面板,然后在面板内布局小部件,将它们放在面板的 sizer 对象中。如果您要将按钮的父级设置为框架,那么您会一团糟。

5) SetSizer 用于告诉 wxPython 该 sizer 属于哪个小部件。在您的代码中,您为面板提供了 box sizer 实例。sizer 对象的 Add() 方法用于将小部件(和 sizer)添加到 sizer 本身。

我希望这能回答你所有的问题。您可能还会发现以下文章很有用,因为它链接到我用于 wx 的大部分文档:http: //www.blog.pythonlibrary.org/2010/12/05/wxpython-documentation/

于 2013-08-20T20:19:27.500 回答
0

我强烈建议获取Noel Rappin 和 Robin Dunn的wxPython in Action副本。由于 Robin 是 wxPython 的主要作者/架构师之一,您将获得非常好的见解和清晰的解释。

NB在任何人问之前,我与这本书、作者或出版商绝对没有商业联系。

于 2013-08-22T10:49:44.437 回答