在wxpython中在面板之间传递信息

2024-04-20 05:13:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个非常复杂的wxpython应用程序,里面有嵌套的笔记本和面板。基本的结构是在我的主框架里我有一个笔记本(比如说笔记本1,在笔记本的一页里我有两个面板(上下)。在下面的面板中,我有另一个笔记本(比如笔记本2)。所以问题是如何在这两个笔记本页面之间传递信息,并传递到上面的面板。在

我知道基本上有三种传递信息的方式:按事件id、按发布者、按.parent和.child。然而,我真的很困惑这三种方法的区别以及何时使用它们。在

我已附上我的代码如下。在

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import datetime, time
import wx, sys, wx.grid
import xlrd

import pandas as pd
import numpy as np
import wx.lib.scrolledpanel as scrolled

EVEN_ROW_COLOUR = '#CCE6FF'
GRID_LINE_COLOUR = '#ccc'

import pandas as pd
import numpy as np

class ResultTable(wx.grid.PyGridTableBase):
    def __init__(self, data=None):
        wx.grid.PyGridTableBase.__init__(self)
        self.data = data
        self.odd=wx.grid.GridCellAttr()
        self.odd.SetBackgroundColour("sky blue")
        self.odd.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))
        self.even=wx.grid.GridCellAttr()
        self.even.SetBackgroundColour("sea green")
        self.even.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD))

    def GetNumberRows(self):
        return self.data.shape[0]
    def GetNumberCols(self):
        return self.data.shape[1]
    def GetValue(self, row, col):
        return self.data.loc[row][col]
    def GetColLabelValue(self, row):
        return list(self.data)[row]
    def SetValue(self, row, col, value):
        pass
    def GetAttr(self, row, col, prop):
        attr = wx.grid.GridCellAttr()
        if row % 2 == 1:
            attr.SetBackgroundColour(EVEN_ROW_COLOUR)
        return attr

class ResultTablePanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)
        grid = wx.grid.Grid(self)


        result = pd.DataFrame({'a' : np.random.randn(100), 'b' : np.random.randn(100), 'c' : np.random.randn(100)})

        table = ResultTable(result)
        grid.SetTable(table)

        grid.AutoSize()
        grid.AutoSizeColumns(True)
        grid.SetGridLineColour(GRID_LINE_COLOUR)

        grid.EnableDragGridSize( False )

        grid.SetRowLabelSize( 50 )
        grid.SetDefaultCellAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(grid, 1, wx.ALL|wx.EXPAND)
        self.SetSizer(sizer)

        btn_ID = parent.GetParent().GetParent().topPanel.analysisButton.GetId()
        self.Bind(wx.EVT_BUTTON, self.getResult, id = btn_ID)

    def getResult(self,e):
        """
        This function should get the variables passed by the analysisOrder function,
        once the button on the SearchConditionPanel is clicked.
        """
        fileName, start_date, end_date = parent.GetParent().GetParent().topPanel.analysisOrder
        print "result get as follows:"
        print fileName, start_date, end_date



class SearchConditionPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, style=wx.BORDER_SUNKEN)

        # Creat input box for searching time period
        nameTitleLable = wx.StaticText(self,-1, label=u"File Name:")
        self.fileNameInput = wx.TextCtrl(self,-1, "20170310221612")

        dateTitleLable = wx.StaticText(self,-1, label=u"Date range:")
        yearLable1 = wx.StaticText(self, label=u"Year:")
        monthLable1 = wx.StaticText(self,-1, label=u"Month:")
        dayLable1 = wx.StaticText(self,-1, label=u"Day:")
        yearLable2 = wx.StaticText(self,-1, label=u"Year:")
        monthLable2 = wx.StaticText(self,-1, label=u"Month:")
        dayLable2 = wx.StaticText(self,-1, label=u"Day:")
        startLable = wx.StaticText(self,-1, label=u"Start Date:")
        endLable = wx.StaticText(self,-1, label=u"End Date:")
        self.startYearInput = wx.TextCtrl(self,1, "2016")
        self.startMonthInput = wx.TextCtrl(self,-1, "10")
        self.startDayInput = wx.TextCtrl(self,-1, "30")
        self.endYearInput = wx.TextCtrl(self,-1, "2017")
        self.endMonthInput = wx.TextCtrl(self,-1, "11")
        self.endDayInput = wx.TextCtrl(self,-1, "22")

        self.analysisButton = wx.Button(self, -1, label = u'Start')
        exportButton = wx.Button(self, -1, label = u'Export')
        exportButton.Disable()

        ## Set up overall layout for the panel
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        vbox_File_button = wx.BoxSizer(wx.VERTICAL)
        hbox_file = wx.BoxSizer(wx.HORIZONTAL)
        vbox_date = wx.BoxSizer(wx.VERTICAL)
        hbox_button = wx.BoxSizer(wx.HORIZONTAL)
        hbox_startDate = wx.BoxSizer(wx.HORIZONTAL)
        hbox_endDate = wx.BoxSizer(wx.HORIZONTAL)

        hbox.Add(vbox_date, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        hbox.Add(wx.StaticLine(self, style=wx.LI_VERTICAL), 0, wx.ALL | wx.EXPAND, 5)
        hbox.Add(vbox_File_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)

        ## Setup the layout for the right side
        vbox_File_button.Add(hbox_file, 0, wx.ALIGN_LEFT | wx.ALL, 5)
        vbox_File_button.Add(wx.StaticLine(self), 0, wx.ALL | wx.EXPAND, 5)
        vbox_File_button.Add(hbox_button, 0, wx.ALIGN_LEFT | wx.ALL, 5)

        hbox_file.Add(nameTitleLable, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 10)
        hbox_file.Add(self.fileNameInput, 0, wx.ALL | wx.EXPAND, 5)

        hbox_button.Add(self.analysisButton, 1, wx.TOP | wx.LEFT, 10)
        hbox_button.Add(exportButton, 1, wx.TOP | wx.LEFT, 10)

        ## Setup the layout for the left side
        vbox_date.Add(dateTitleLable, 0, wx.ALL, 5)
        vbox_date.Add(hbox_startDate, 0, wx.ALL | wx.EXPAND, 5)
        vbox_date.Add(hbox_endDate, 0, wx.ALL | wx.EXPAND, 5)

        hbox_startDate.Add(startLable, 0, wx.ALL, 5)
        hbox_startDate.Add(yearLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
        hbox_startDate.Add(self.startYearInput, 0, wx.ALL, 5)
        hbox_startDate.Add(monthLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
        hbox_startDate.Add(self.startMonthInput, 0, wx.ALL|wx.EXPAND, 5)
        hbox_startDate.Add(dayLable1, 0, wx.TOP | wx.LEFT | wx.BOTTOM, 5)
        hbox_startDate.Add(self.startDayInput, 0, wx.ALL|wx.EXPAND, 5)

        hbox_endDate.Add(endLable, 0, wx.LEFT | wx.RIGHT, 5)
        hbox_endDate.Add(yearLable2, 0, wx.LEFT, 5)
        hbox_endDate.Add(self.endYearInput, 0, wx.LEFT | wx.RIGHT, 5)
        hbox_endDate.Add(monthLable2, 0, wx.LEFT, 5)
        hbox_endDate.Add(self.endMonthInput, 0, wx.LEFT | wx.RIGHT, 5)
        hbox_endDate.Add(dayLable2, 0, wx.LEFT, 5)
        hbox_endDate.Add(self.endDayInput, 0, wx.LEFT, 5)

        self.SetSizer(hbox)

        self.Bind(wx.EVT_BUTTON, self.analysisOrder, id=self.analysisButton.GetId())


    def analysisOrder(self,e):
        fileName_temp = self.fileNameInput.GetValue().strip()
        fileName = fileName_temp + '.xls'
        start_date = self.startYearInput.GetValue().strip() + '-' + self.startMonthInput.GetValue().strip() + '-' + self.startDayInput.GetValue().strip()
        end_date = self.endYearInput.GetValue().strip() + '-' + self.endMonthInput.GetValue().strip() + '-' + self.endDayInput.GetValue().strip()

        print "analysis order button called"

        return fileName, start_date, end_date


class ResultNotebook(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # Creat notebooks for window layout
        result_notebook = wx.Notebook(self)

        data_page = ResultTablePanel(result_notebook, -1)

        result_notebook.AddPage(data_page, u"Display Data")

        # Set up a boxsizer for the tabs
        result_notebook_sizer = wx.BoxSizer(wx.HORIZONTAL)
        result_notebook_sizer.Add(result_notebook, 1, wx.EXPAND)
        self.SetSizer(result_notebook_sizer)

class ModePage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # Localize all the panels
        self.topPanel = SearchConditionPanel(self, -1)
        self.botPanel = ResultNotebook(self)

        #Set up the panels, align and position them in the right place
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
        vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(vbox)
        self.Centre()
        self.Show(True)


class FreqPage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        # t = wx.StaticText(self, -1, "This is a Frequency Page object", (20,20))

        # Localize all the panels
        topPanel = SearchConditionPanel(self, -1)
        t = wx.StaticText(self, -1, "This is a Frequency Page object")

        #Set up the panels, align and position them in the right place
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(topPanel, 0, wx.EXPAND | wx.ALL, 5)
        vbox.Add(t, -1, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(vbox)
        self.Centre()
        self.Show(True)

class OrderPage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # Localize all the panels
        self.topPanel = SearchConditionPanel(self, -1)
        self.botPanel = ResultNotebook(self)

        #Set up the panels, align and position them in the right place
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.topPanel, 0, wx.EXPAND | wx.ALL, 5)
        vbox.Add(self.botPanel, -1, wx.EXPAND | wx.ALL, 5)

        self.SetSizer(vbox)
        self.Centre()
        self.Show(True)


class ShopPage(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # Creat notebooks for window layout
        shop_notebook = wx.Notebook(self)

        mode_page = ModePage(shop_notebook)
        freq_page = FreqPage(shop_notebook)

        shop_notebook.AddPage(mode_page, u"Mode Analysis")
        shop_notebook.AddPage(freq_page, u"Frequency Analysis")

        # Set up a boxsizer for the tabs
        shop_page_sizer = wx.BoxSizer(wx.HORIZONTAL)
        shop_page_sizer.Add(shop_notebook, 1, wx.EXPAND)
        self.SetSizer(shop_page_sizer)



##**----------------------------------------------------------------------------------**##
##**-------------- Create a windows to display the entire system ---------------------**##    
class OrderAnalysis(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(1024, 576))
        # self.Maximize(True)  # Default display to be maximized

        panel = wx.Panel(self, -1)

        # Creat notebooks for window layout
        main_notebook = wx.Notebook(panel, style=wx.NB_LEFT)

        main_notebook.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT,
                         wx.FONTWEIGHT_NORMAL, 
                         wx.FONTSTYLE_NORMAL))

        # create the page windows as children of the notebook
        order_page = OrderPage(main_notebook)
        shop_page = ShopPage(main_notebook)

        # add the pages to the notebook with the label to show on the tab
        main_notebook.AddPage(order_page, u"Order Analysis")
        main_notebook.AddPage(shop_page, u"Shop Analysis")


        # finally, put the notebook in a sizer for the panel to manage
        # the layout
        sizer = wx.BoxSizer()
        sizer.Add(main_notebook, 1, wx.EXPAND)
        panel.SetSizer(sizer)


        self.Centre()
        self.Show(True)


if __name__ == "__main__":
    app = wx.App()
    OrderAnalysis(None, -1, 'Order Info Analyser')
    app.MainLoop()

如您所见,我有一个搜索条件面板和一个结果面板,它们在不同的页面中使用。对于每个特定的页面,搜索条件和结果可能不同,并且只针对该页面。所以我的问题是,将搜索条件(即数据和文件名)传递到相应的页面以显示结果(即“ResultTablePanel”中的“getResult”函数不起作用)的最佳方式是什么。在

例如,在“订单页”中,搜索条件面板有用户输入的开始和结束日期以及文件名。搜索条件面板下面是结果面板,显示基于搜索条件的计算结果。单击“开始”按钮后,“开始日期”、“结束日期”和“文件名”变量将传递到“订单页”内的结果面板(“ResultTablePanel”)。同样,由于“Mode page”同时调用“ResultTablePanel”和“SearchConditionPanel”,它应该有自己的“start date”、“end date”和“file name”变量(注意它们可以与“order page”中的变量相同或不同),并且它自己的结果显示在“ResultTablePanel”中。在

提前感谢您抽出时间回顾并回答我的问题。在

=======================================================================================

谢谢你们的帮助,我已经把PubSub和.parent方法结合起来解决了。我已附上我的解决方案代码如下:

^{pr2}$

再次感谢大家!非常感谢。在


Tags: theselfaddinitdefpageallleft
2条回答

您提到了传递信息的三种方式,但我不得不承认,我不明白您在说什么(我编写了许多wxWidget应用程序)。处理您描述的情况的一种方法是事件处理程序调用父类中的函数,然后该函数可以将信息转发给其他函数/类。示意图:

class A:
    def __init__(self):
        B(self)
        self.c = C(self)

    def a_function(self, *data):
        # do something with the data 
        # you can pass the information to class C here

class B:
    def __init__(self, parent):
        self.parent = parent

    def an_event_handler(self):
        data = something
        self.parent.a_function(data)
        # or use wx.CallAfter for later execution

class C:
    pass

另一个解决方案是MVC概念,在这个概念中,事件处理程序将其信息传递给一个模型类,该类存储或进一步处理它。这个模型类可以引发一个自定义的wx事件,UI的组件可以根据需要处理这些事件,以重新绘制任何受影响的小部件。在

我通常推荐使用Pubsub来处理这类事情。它非常容易实现,并且可以很容易地将消息传递给一个或多个接收者(面板)。在

下面是一个简单的例子:

import wx
from wx.lib.pubsub import pub 


class OtherFrame(wx.Frame):
    """"""

    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY, "Secondary Frame")
        panel = wx.Panel(self)

        msg = "Enter a Message to send to the main frame"
        instructions = wx.StaticText(panel, label=msg)
        self.msgTxt = wx.TextCtrl(panel, value="")
        closeBtn = wx.Button(panel, label="Send and Close")
        closeBtn.Bind(wx.EVT_BUTTON, self.onSendAndClose)

        sizer = wx.BoxSizer(wx.VERTICAL)
        flags = wx.ALL|wx.CENTER
        sizer.Add(instructions, 0, flags, 5)
        sizer.Add(self.msgTxt, 0, flags, 5)
        sizer.Add(closeBtn, 0, flags, 5)
        panel.SetSizer(sizer)

    def onSendAndClose(self, event):
        """
        Send a message and close frame
        """
        msg = self.msgTxt.GetValue()
        pub.sendMessage("panelListener", message=msg)
        pub.sendMessage("panelListener", message="test2", arg2="2nd argument!")
        self.Close()


class MyPanel(wx.Panel):
    """"""

    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent)
        pub.subscribe(self.myListener, "panelListener")

        btn = wx.Button(self, label="Open Frame")
        btn.Bind(wx.EVT_BUTTON, self.onOpenFrame)

    def myListener(self, message, arg2=None):
        """
        Listener function
        """
        print "Received the following message: " + message
        if arg2:
            print "Received another arguments: " + str(arg2)

    def onOpenFrame(self, event):
        """
        Opens secondary frame
        """
        frame = OtherFrame()
        frame.Show()


class MyFrame(wx.Frame):
    """"""

    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="PubSub Tutorial")
        panel = MyPanel(self)
        self.Show()


if __name__ == "__main__":
    app = wx.App(False)
    frame = MyFrame()
    app.MainLoop()

以下是一些附加信息:

相关问题 更多 >