Search code examples
pythonpython-3.xuser-interfaceevent-handlingwxpython

WXPython Event Handling - Button


When I'm running the following code, I received the error (image attached) I have a button called btn and when I add it to my right panel I cannot move its position, does anyone knows how can I do that? And another issue is that I'd like to call that specific button when I click on Big Script on the left panel, however I have no clue how to do this, I tried to put the button on the OnSelChanged method but I wasn't able to make it work.

The whole code is down below so it's easier to copy-paste it.

enter image description here

import wx
from wx.core import Font, Position, Size
import wx.lib.newevent
from wx.lib.agw.customtreectrl import CustomTreeCtrl

def onButton(event):
    print("Button Pressed")

#---------------------------------------------------------------------------

class MyTree(wx.TreeCtrl):
    """
    Our customized TreeCtrl class.
    """
    def __init__(self, parent, id, position, size, style):
        """
        Initialize our tree.
        """
        wx.TreeCtrl.__init__(self, parent, id, position, size, style)
       
        root = self.AddRoot('Programmer')
        
        os = self.AppendItem(root, '🧙 Big Script')
        pl = self.AppendItem(root, '📊 Big Script2')
        tk = self.AppendItem(root, 'Big Script3')
           
        cl = self.AppendItem(pl, 'Random Scripts')
        sl = self.AppendItem(pl, 'Miscelanious')

        self.AppendItem(os, 'Script1')
        self.AppendItem(os, 'Script2')
        self.AppendItem(os, 'Script3')
        self.AppendItem(os, 'Script4')
        self.AppendItem(cl, 'Script1')
        self.AppendItem(cl, 'Script2')
        self.AppendItem(cl, 'Script3')
        self.AppendItem(sl, '1')
        self.AppendItem(sl, '2')
        self.AppendItem(sl, '3')
        self.AppendItem(sl, '4')
        self.AppendItem(tk, 'Script1')
        self.AppendItem(tk, 'Script2')
        self.AppendItem(tk, 'Script3')
        self.AppendItem(tk, 'Script4+')
        self.AppendItem(tk, 'Script5')

#---------------------------------------------------------------------------
        
class MyFrame(wx.Frame):
    """
    Our customized window class.
    """
    def __init__(self, parent, id, title):
        """
        Initialize our window.
        """
        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(800, 600))

        #self.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))

        #------------
        
        # Create a splitter window.
        self.splitter = wx.SplitterWindow(self, -1, style=wx.SP_LIVE_UPDATE)
        
        # Create the left panel.
        leftPanel = wx.Panel(self.splitter, -1)
        
        # Create our tree and put it into the left panel.
        self.tree = MyTree(leftPanel, 1,
                           wx.DefaultPosition,
                           wx.DefaultSize,
                           wx.TR_HIDE_ROOT |
                           wx.TR_HAS_BUTTONS)
        self.tree.SetBackgroundColour('#3DB2FF')

        # Bind the OnSelChanged method to the tree.
        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
        

        # Create the right panel.
        rightPanel = wx.Panel(self.splitter, -1, style=wx.SUNKEN_BORDER)
        rightPanel.SetBackgroundColour('#79B4B7')
        
        # Create a widget to display static text 
        # and store it in the right panel.
        self.display = wx.StaticText(rightPanel, -1, '', style=wx.ALIGN_CENTER)
        #item =  self.display.SetLabel(self.tree.GetItemText())
        #print(item)
        
        # Put the left and right panes into the split window.
        self.splitter.SplitVertically(leftPanel, rightPanel, 200)

        # Minimum size of subwindow.
        self.splitter.SetMinimumPaneSize(1)         

        #------------

        # Create a box sizer that will contain the left panel contents.
        leftBox = wx.BoxSizer(wx.VERTICAL)
        
        # Add the tree to the box sizer.
        leftBox.Add(self.tree, 1, wx.EXPAND)
        
        # Set the size of the right panel to that required by the tree.
        leftPanel.SetSizer(leftBox)
        
        # Create the right box sizer that will contain the panel's contents.
        rightBox = wx.BoxSizer(wx.VERTICAL)
        
        # Add the display widget to the right panel.
        rightBox.Add(self.display, 0, wx.ALL, 1)


        self.button = wx.Button(rightPanel, wx.ID_ANY, 'Test', pos=(225, 2000))
        rightBox.Add(self.button)
        self.button.Bind(wx.EVT_BUTTON, onButton)
        print(self.button.GetScreenPosition())

        # Set the size of the right panel to that 
        # required by the display widget.
        rightPanel.SetSizer(rightBox)
        #------------
        
        # Create the window in the centre of the screen.
        self.Centre()

    #-----------------------------------------------------------------------
        
    def OnSelChanged(self, event):
        """
        Method called when selected item is changed.
        """
        
        # Get the selected item object.
        item =  event.GetItem()

        overview = """
        This demo simply catches all key events and prints info about them.
        It is meant to be used as a compatibility test for cross platform work.
        """
        
        # Display the selected item text in the text widget.
        if self.tree.GetItemText(item) == '🧙 Big Script':
            self.display.SetLabel(f"{self.tree.GetItemText(item)}\n {overview}")
        else: 
            self.display.SetLabel(self.tree.GetItemText(item))
        
        return item

        #print(self.tree.GetItemText(item))

#---------------------------------------------------------------------------
        
class MyApp(wx.App):
    """
    Our application class.
    """
    def OnInit(self):
        """
        Initialize by creating the split window with the tree.
        """
        
        frame = MyFrame(None, -1, 'Test1')
        frame.Show(True)
        self.SetTopWindow(frame)
        
        return True

#---------------------------------------------------------------------------
    
if __name__ == '__main__':
    app = MyApp(0)
    app.MainLoop()

One last question would be, do you know any resources other than the documentation to understand more in depth about this library? (Youtube videos, Udemy, etc)


Solution

  • A few things to mention:
    Using a position statement when using a sizer is pointless, it will be ignored.

    The sizer type determines placement. You have chosen a vertical boxsizer, so everything will be arranged vertically. You can use an alignment style when adding a widget or choose a different sizer type, more befitting your requirements.

    I've moved the callback function of the button inside the class.

    The button callback function can be called separately, if you are not depending on the event values and you send a dummy event parameter, usually None

    import wx
    from wx.core import Font, Position, Size
    import wx.lib.newevent
    from wx.lib.agw.customtreectrl import CustomTreeCtrl
    
    #---------------------------------------------------------------------------
    
    class MyTree(wx.TreeCtrl):
        """
        Our customized TreeCtrl class.
        """
        def __init__(self, parent, id, position, size, style):
            """
            Initialize our tree.
            """
            wx.TreeCtrl.__init__(self, parent, id, position, size, style)
           
            root = self.AddRoot('Programmer')
            
            os = self.AppendItem(root, '🧙 Big Script')
            pl = self.AppendItem(root, '📊 Big Script2')
            tk = self.AppendItem(root, 'Big Script3')
               
            cl = self.AppendItem(pl, 'Random Scripts')
            sl = self.AppendItem(pl, 'Miscelanious')
    
            self.AppendItem(os, 'Script1')
            self.AppendItem(os, 'Script2')
            self.AppendItem(os, 'Script3')
            self.AppendItem(os, 'Script4')
            self.AppendItem(cl, 'Script1')
            self.AppendItem(cl, 'Script2')
            self.AppendItem(cl, 'Script3')
            self.AppendItem(sl, '1')
            self.AppendItem(sl, '2')
            self.AppendItem(sl, '3')
            self.AppendItem(sl, '4')
            self.AppendItem(tk, 'Script1')
            self.AppendItem(tk, 'Script2')
            self.AppendItem(tk, 'Script3')
            self.AppendItem(tk, 'Script4+')
            self.AppendItem(tk, 'Script5')
    
    #---------------------------------------------------------------------------
            
    class MyFrame(wx.Frame):
        """
        Our customized window class.
        """
        def __init__(self, parent, id, title):
            """
            Initialize our window.
            """
            wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(800, 600))
    
            #self.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))
    
            #------------
            
            # Create a splitter window.
            self.splitter = wx.SplitterWindow(self, -1, style=wx.SP_LIVE_UPDATE)
            
            # Create the left panel.
            leftPanel = wx.Panel(self.splitter, -1)
            
            # Create our tree and put it into the left panel.
            self.tree = MyTree(leftPanel, 1,
                               wx.DefaultPosition,
                               wx.DefaultSize,
                               wx.TR_HIDE_ROOT |
                               wx.TR_HAS_BUTTONS)
            self.tree.SetBackgroundColour('#3DB2FF')
    
            # Bind the OnSelChanged method to the tree.
            self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
            
    
            # Create the right panel.
            rightPanel = wx.Panel(self.splitter, -1, style=wx.SUNKEN_BORDER)
            rightPanel.SetBackgroundColour('#79B4B7')
            
            # Create a widget to display static text 
            # and store it in the right panel.
            self.display = wx.StaticText(rightPanel, -1, '', style=wx.ALIGN_CENTER)
            #item =  self.display.SetLabel(self.tree.GetItemText())
            #print(item)
            
            # Put the left and right panes into the split window.
            self.splitter.SplitVertically(leftPanel, rightPanel, 200)
    
            # Minimum size of subwindow.
            self.splitter.SetMinimumPaneSize(1)         
    
            #------------
    
            # Create a box sizer that will contain the left panel contents.
            leftBox = wx.BoxSizer(wx.VERTICAL)
            
            # Add the tree to the box sizer.
            leftBox.Add(self.tree, 1, wx.EXPAND)
            
            # Set the size of the right panel to that required by the tree.
            leftPanel.SetSizer(leftBox)
            
            # Create the right box sizer that will contain the panel's contents.
            rightBox = wx.BoxSizer(wx.VERTICAL)
            
            # Add the display widget to the right panel.
            rightBox.Add(self.display, 0, wx.ALL, 1)
    
    
            #self.button = wx.Button(rightPanel, wx.ID_ANY, 'Test', pos=(225, 2000))
            # You're using a Sizer to arrange the contents of rightPanel so "position" is meaningless
            self.button = wx.Button(rightPanel, wx.ID_ANY, 'Test')
            # Use an alignment parameter or choose a sizer other than a boxsizer
            rightBox.Add(self.button,0,wx.ALIGN_RIGHT|wx.ALL, 20)
            self.button.Bind(wx.EVT_BUTTON, self.onButton)
    
            # Set the size of the right panel to that 
            # required by the display widget.
            rightPanel.SetSizer(rightBox)
            #------------
            
            # Create the window in the centre of the screen.
            self.Centre()
    
        #-----------------------------------------------------------------------
        def onButton(self, event):
            print("Button Pressed")
            
        def OnSelChanged(self, event):
            """
            Method called when selected item is changed.
            """
            
            # Get the selected item object.
            item =  event.GetItem()
    
            overview = """
            This demo simply catches all key events and prints info about them.
            It is meant to be used as a compatibility test for cross platform work.
            """
            
            # Display the selected item text in the text widget.
            if self.tree.GetItemText(item) == '🧙 Big Script':
                self.display.SetLabel(f"{self.tree.GetItemText(item)}\n {overview}")
                # We can call the event function, as long as we set a value of something for the event parameter
                self.onButton(None)
            else: 
                self.display.SetLabel(self.tree.GetItemText(item))
            
    #---------------------------------------------------------------------------
            
    class MyApp(wx.App):
        """
        Our application class.
        """
        def OnInit(self):
            """
            Initialize by creating the split window with the tree.
            """
            
            frame = MyFrame(None, -1, 'Test1')
            frame.Show(True)
            self.SetTopWindow(frame)
            
            return True
    
    #---------------------------------------------------------------------------
        
    if __name__ == '__main__':
        app = MyApp()
        app.MainLoop()
    

    enter image description here