1

我正在编写一个与 Skype 集成的聊天程序。我已经完成了大部分的跑腿工作,但是我在 wxPython 中的 Notebook 控件上遇到了问题。当用户发送消息时,我想在笔记本上创建一个新选项卡,我正在工作,但我遇到的问题是如何在选项卡上的面板上引用 TextCtrl?以下是我从网上提取的另一个项目的代码:

import wx

class Page(wx.Panel):
  def __init__(self, parent):
     wx.Panel.__init__(self, parent)
     t = wx.StaticText(self, -1, "THIS IS A PAGE OBJECT", (20,20))

class MainFrame(wx.Frame):
  def __init__(self):
    wx.Frame.__init__(self, None, title="Notebook Remove Pages Example")

    pannel  = wx.Panel(self)
    vbox    = wx.BoxSizer(wx.VERTICAL)
    hbox    = wx.BoxSizer(wx.HORIZONTAL)

    self.buttonRemove = wx.Button(pannel, id=wx.ID_ANY, label="DELETE", size=(80, 25))
    self.buttonRemove.Bind(wx.EVT_BUTTON, self.onButtonRemove)
    hbox.Add(self.buttonRemove)

    self.buttonInsert = wx.Button(pannel, id=wx.ID_ANY, label="CREATE", size=(80, 25))
    self.buttonInsert.Bind(wx.EVT_BUTTON, self.onButtonInsert)
    hbox.Add(self.buttonInsert)

    self.buttonMessage = wx.Button(pannel, id=wx.ID_ANY, label="Message", size=(80, 25))
    self.buttonMessage.Bind(wx.EVT_BUTTON, self.onButtonMessage)
    hbox.Add(self.buttonList)

    vbox.Add(hbox)

    self.Notebook3 = wx.Notebook(pannel)
    vbox.Add(self.Notebook3, 2, flag=wx.EXPAND)

    pannel.SetSizer(vbox)

    self.pageCounter = 0
    self.addPage()

def addPage(self):
    self.pageCounter += 1
    page      = Page(self.Notebook3)
    pageTitle = "Page: {0}".format(str(self.pageCounter))
    self.Notebook3.AddPage(page, pageTitle)

def onButtonRemove(self, event):   
    page_to_delete = self.Notebook3.GetSelection()
    self.Notebook3.DeletePage(page_to_delete)

def onButtonInsert(self, event):   
    self.addPage()

def onButtonMessage(self, event):

    self.Notebook3.StaticText(0).AppendText("Yeah right. Like this works")

if __name__ == "__main__":
   app = wx.App()
   MainFrame().Show()
   app.MainLoop()

我似乎无法将它完全正确地组合在一起。任何帮助表示赞赏。

4

2 回答 2

1

在您的 Page 类中,您创建了一个控件,但仅将其存储在局部变量中,您需要使用 self 将其存储为可以从页面实例访问的实例变量。

在您的方法 onButtonMessage 中,您正在向笔记本询问它没有的静态文本,笔记本包含页面,而且您正在调用 AppendText 而静态文本没有此方法。

要修复此代码,您需要将类 Page 更改为具有 textctrl 并将其存储为实例变量。更改方法 onButtonMessage 以查找当前页面,然后访问其 textcrtl 以附加文本。

这是你修改后的代码,我也做了一些布局调整

import wx


class Page(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.textCtrl = wx.TextCtrl(self, -1, "THIS IS A PAGE OBJECT ",
                                    style=wx.TE_MULTILINE | wx.BORDER_NONE)
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.textCtrl, 1, wx.EXPAND)
        self.SetSizer(vbox)


class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Notebook Remove Pages Example")

        pannel = wx.Panel(self)
        vbox = wx.BoxSizer(wx.VERTICAL)
        hbox = wx.BoxSizer(wx.HORIZONTAL)

        self.buttonRemove = wx.Button(pannel, id=-1, label="DELETE")
        self.buttonRemove.Bind(wx.EVT_BUTTON, self.onButtonRemove)
        hbox.Add(self.buttonRemove)

        self.buttonInsert = wx.Button(pannel, id=-1, label="CREATE")
        self.buttonInsert.Bind(wx.EVT_BUTTON, self.onButtonInsert)
        hbox.Add(self.buttonInsert)

        self.buttonMessage = wx.Button(pannel, id=-1, label="Message")
        self.buttonMessage.Bind(wx.EVT_BUTTON, self.onButtonMessage)
        hbox.Add(self.buttonMessage)

        vbox.Add(hbox, 0, wx.ALL, 7)

        self.notebook3 = wx.Notebook(pannel)
        vbox.Add(self.notebook3, 1, wx.EXPAND | wx.ALL, 7)

        pannel.SetSizer(vbox)

        self.pageCounter = 0
        self.addPage()

    def addPage(self):
        self.pageCounter += 1
        page = Page(self.notebook3)
        pageTitle = "Page: {0}".format(str(self.pageCounter))
        self.notebook3.AddPage(page, pageTitle)

    def onButtonRemove(self, event):
        page_to_delete = self.notebook3.GetSelection()
        self.notebook3.DeletePage(page_to_delete)

    def onButtonInsert(self, event):
        self.addPage()

    def onButtonMessage(self, event):
        page = self.notebook3.GetCurrentPage()
        page.textCtrl.AppendText("Yeah this works ")

if __name__ == "__main__":
    app = wx.App()
    MainFrame().Show()
    app.MainLoop()
于 2013-04-05T13:19:30.390 回答
1

Yoriz 的回答很好,但在这些情况下您需要非常小心,以免违反封装抽象

在封装方面,我个人的经验法则是我的任何框架/面板/控件/对话框/等。只允许调用一层之外的函数。也就是说,面板可以从其父级和其直接子级调用函数。在他们的解决方案中,Yoriz 在两层之外调用了一个函数。AppendText()被调用page.textCtrl

def onButtonMessage(self, event):
    page = self.notebook3.GetCurrentPage()
    page.textCtrl.AppendText("Yeah this works ")

问题是MainFrame在它无法立即控制的对象上调用函数。这可以通过Page像这样创建一些包装函数来轻松解决:

class Page(wx.Panel):
    ...

    def AppendText(text):
        self.textCtrl.AppendText(text)

并像这样调用它MainFrame

class MainFrame(wx.Frame):
    def onButtonMessage(self, event):
        page = self.notebook3.GetCurrentPage()
        page.AppendText("Yeah this works ")

这也有问题。为了促进代码重用,我们希望我们的设计尽可能抽象,尽可能少的依赖MainFrame取决于现有的和拥有一个对象(在Yoriz的答案中)或一个函数(在我上面的建议中),如果不是这种情况,则会产生错误。如果您尝试在不同的项目中重用,这会造成困难。notebook3textCtrlAppendText()MainFrame

有几种方法可以减少这些类型的依赖,但我个人最喜欢的是 wxPython 的pubsub 库(您可以在此处找到更详细的教程)。当在一个面板中按下按钮时,您可以发送一条消息pub.sendMessage(),然后另一个面板可以接收该消息,然后可以适当地处理它。您甚至可以包含参数(在您的情况下,例如要设置textCtrl的文本)。这在概念上与 wxPython 处理按钮按下的方式非常相似,只是将“消息”替换为“事件”。

使用 pubsub,MainFrame不需要知道任何关于workbook3. 哎呀,pannel甚至workbook3可以在不知道彼此存在的情况下进行交流;他们只需要知道要发送/订阅哪些消息(我建议将您的消息声明为常量)。这使您的所有组件更加灵活和可重用。

在代码中,它看起来像这样:

import wx
from wx.lib.pubsub import setupkwargs #this line not required in wxPython2.9.
                                      #See documentation for more detail
from wx.lib.pubsub import pub

#This message requires the argument "text"
MSG_CHANGE_TEXT = "change.text"

class Page(wx.Panel):
    def __init__(self, parent):
        self.textCtrl = wx.TextCtrl(self, -1, "THIS IS A PAGE OBJECT ",
                                style=wx.TE_MULTILINE | wx.BORDER_NONE)
        ...

        pub.subscribe(self.onChangeText, MSG_CHANGE_TEXT)

    def onChangeText(self, text):
        self.textCtrl.AppendText(text)

class MainFrame(wx.Frame):
    ...

    def onButtonMessage(self, event):
        pub.sendMessage(MSG_CHANGE_TEXT, text="Yeah this works ")

显然,您提供给我们的示例代码非常简单,因此在这种特殊情况下,您可以轻松地说使用 pubsub 获得的优势不值得付出努力。但是,如果pannelworkbook3不共享同一个父级怎么办?workbook3在这种情况下,传递 to 的实例将非常困难,pannel并且需要大量依赖于大量类。在这种情况下,pubsub 提供了一个简单、容易和干净的解决方案。

于 2013-04-06T12:11:19.043 回答