Hello, I'm re-creating the game Riveter using wxPython. I have all the basic fundamental groundwork done, and now I'm just trying to polish certain aspects. I am, however, having problems removing buttons from the screen.

I have a panel, with a vertical sizer inside of it. Inside of that vertical sizer are multiple horizontal sizers, as seems to be the basic way to do things from the "starting wxPython" sticky thread. Additionally inside the vertical sizer is a wx.GridSizer, with which each cell has a button inside of it (with parent of the panel.) Now, when a new game is started, I try to remove the wx.GridSizer and replace it with another one. What happens is that the buttons stick around, now completely locked into the place they were, regardless of resizing the window, along with new buttons belonging to the new GridSizer under them. I realize this may be hard to follow, but I have included my code below.

My question is how do i remove these buttons, along with the GridSizer that they're contained in?

Along a related note, I've noticed some weird behavior with the windowing, and I was hoping for some light to be shed.
1- Under WINDOWS, whenever the program is started, all the objects are stuck in the top-left corner until the window is resized, or the game is started. Everything is placed fine after that.
2- In LINUX, everything is placed correctly at the start, but when a game is started, the statusInfo sizer disappears.

Both Windows and Linux are both running the newest wxPython versions, and both the same python 2.7 version.

Once again, I appreciate any help you can give. Thank you!

PS- I was trying to follow the GridSizer example here: http://www.daniweb.com/software-development/python/threads/128350/648463#post648463

import wx
from functools import partial

PROG_VER = 1.0
    
class Main(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title = 'Justin!', size=(800,600))
        self.background = wx.Panel(self)
        
        #Menubar
        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)
        self.menuFile = wx.Menu()
        self.menuInfo = wx.Menu()
        
        self.menuBar.Append(self.menuFile, 'File')
        self.menuBar.Append(self.menuInfo, 'Info')
        
        self.newItem = self.menuFile.Append(-1, 'New Game')
        self.Bind(wx.EVT_MENU, self.newEvent, self.newItem)
        self.exitItem = self.menuFile.Append(-1, 'Exit')
        self.Bind(wx.EVT_MENU, self.exitEvent, self.exitItem)     
        
        self.instruItem = self.menuInfo.Append(-1, 'Instructions')
        self.Bind(wx.EVT_MENU, self.instruEvent, self.instruItem)
        self.aboutItem = self.menuInfo.Append(-1, 'About')
        self.Bind(wx.EVT_MENU, self.aboutEvent, self.aboutItem)
        
        #Sections
        self.displayInfo = wx.BoxSizer()
        self.displayText = wx.StaticText(self.background, -1, "placeholder")
        self.displayInfo.Add(self.displayText, proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
        
        self.statusInfo = wx.BoxSizer()
        self.coinsText = wx.StaticText(self.background, -1, "Coins left: ", style = wx.ALIGN_RIGHT)
        self.numberText = wx.TextCtrl(self.background, -1, "0", style = wx.TE_READONLY)
        self.statusInfo.AddStretchSpacer(1)
        self.statusInfo.Add(self.coinsText, proportion = 0, border = 0)
        self.statusInfo.Add(self.numberText, proportion = 0, border = 0)
        
        self.gridSizer = wx.BoxSizer()
        
        self.vertBox = wx.BoxSizer(wx.VERTICAL)
        self.vertBox.Add(self.displayText, proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
        self.vertBox.Add(self.gridSizer, proportion = 1, flag = wx.EXPAND, border = 0)
        self.vertBox.Add(self.statusInfo, proportion = 0, border = 0)
        
        self.background.SetSizer(self.vertBox)
        self.vertBox.Layout()
        self.Show()
        self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
        
    def exitEvent(self, event):
        # via wx.EVT_CLOSE event, also triggers exit dialog
        self.Close(True)

    def onCloseWindow(self, event):
        # dialog to verify exit (including menuExit)
        dlg = wx.MessageDialog(self, "Want to exit?", "Exit", wx.YES_NO | wx.ICON_QUESTION)
        if dlg.ShowModal() == wx.ID_YES:
            self.Destroy()  # frame
        dlg.Destroy()
    
    def instruEvent(self, event):
        pass
    
    def newEvent(self, event):
        self.players = []
        self.height, self.width, self.players_amount = 0, 0, 0
        self.board = []
        self.coins_left = 0
        self.getInfo()
        try: self.gridSizer.Remove(self.grid)
        except: print "not here"
        try: del self.grid
        except: pass
        self.grid = Board()
        self.grid.setup(self.width, self.height)
        self.gridSizer.Add(self.grid, proportion = 1, border= 0)
        self.gridSizer.Layout()
        self.gridSizer.Fit(self)
        self.vertBox.Layout()
        self.vertBox.Fit(self)
        self.turn_number = self.players_amount
        self.gamePlay()
    
    def aboutEvent(self, event):
        pass

    def getInfo(self):
        dialogBox = wx.TextEntryDialog(None, "How many players will there be?", "Players", "2")
        if dialogBox.ShowModal() == wx.ID_OK:
            self.players_amount = int(dialogBox.GetValue())
        for loop in range(self.players_amount):
            dialogBox = wx.TextEntryDialog(None, "What will the name of player %i be?"%(loop+1, ), "Player Names", "Player %i"%(loop+1, ))
            if dialogBox.ShowModal() == wx.ID_OK:
                self.players.append(dialogBox.GetValue())
        dialogBox = wx.TextEntryDialog(None, "How many rows will there be?", "Height", "3")
        if dialogBox.ShowModal() == wx.ID_OK:
            self.height = int(dialogBox.GetValue())
        dialogBox = wx.TextEntryDialog(None, "How many columns will there be?", "Width", "3")
        if dialogBox.ShowModal() == wx.ID_OK:
            self.width = int(dialogBox.GetValue())
        
    def gamePlay(self):
        for i in range(self.players_amount):
            if self.turn_number % self.players_amount == i:
                self.turn(self.players[i])
        self.turn_number += 1

    def endGame(self):
        for i in range(self.players_amount):
            if self.turn_number % self.players_amount == i:
                if i != 0:
                    self.displayText.SetLabel("You win, %s!"%(self.players[i], ))
                else:
                    self.displayText.SetLabel("You win, %s!"%(self.players[self.players_amount-1], ))
        
    def turn(self, player):
        if self.status == 3: return 0
        self.coins_left = 3
        self.displayText.SetLabel("Your turn, %s!"%(player, ))
        self.statusInfo.Clear()
        self.numberText.ChangeValue(str(self.coins_left))
        
    def status(self):
        count = 0
        for i in range(self.height):
            for j in range(self.width):
                if (self.grid.board[i][j] == 0):
                    count += 1             
        if (count == 0): return 1
        else: return 0
        
                
class Board(wx.GridSizer):
    def __init__(self):
        wx.GridSizer.__init__(self, 2, 2, 2, 2)
        self.height, self.width = 0, 0
        self.board = []
    
    def setup(self, width, height):
        self.width, self.height = width, height
        try: del self.gridButtons
        except: pass
        self.gridButtons = range(self.width * self.height)
        self.SetCols(self.width)
        self.SetRows(self.height)
        for i in range(self.height):
            inside_array = []
            for j in range(self.width):
                #The following adds the edge coins.
                if (i == 0): inside_array.append(0)
                elif (i == (self.height - 1)): inside_array.append(0)
                elif (j == 0): inside_array.append(0)
                elif (j == (self.width - 1)): inside_array.append(0)
                #But this removes all others.
                else: inside_array.append(1)
            self.board.append(inside_array)
        count = 0
        for i in range(self.height):
            for j in range(self.width):
                idnum = 100 + count
                if (self.board[i][j] == 0):
                    self.gridButtons[count] = wx.Button(window.background, idnum, label = "X")
                    self.Add(self.gridButtons[count], 0, wx.ALL|wx.EXPAND, border = 1)
                    self.gridButtons[count].Bind(wx.EVT_BUTTON, partial(self.buttonClick, number = count, i = i, j = j))
                if (self.board[i][j] == 1):
                    self.gridButtons[count] = wx.Button(window.background, idnum, label = " ")
                    self.Add(self.gridButtons[count], 0, wx.ALL|wx.EXPAND, border = 1)
                count += 1

    def buttonClick(self, event, number, i, j):
        self.gridButtons[number].SetLabel("")
        self.gridButtons[number].Unbind(wx.EVT_BUTTON)
        self.board[i][j] = 1
        window.coins_left -= 1
        if window.coins_left == 0: window.gamePlay()
        window.statusInfo.Clear()
        window.numberText.ChangeValue(str(window.coins_left))
        if window.status() == 1: window.endGame()
    
app = wx.App(redirect = False)
window = Main()
app.MainLoop()

I try to remove the wx.GridSizer and replace it with another one.

The wxpython docs list Detach, Remove, and RemoveSizer for a Sizer widger (GridSizer is a subclass of Sizer). So it would be something like (sorry, 186 lines of code is more than I want to wade through, though someone else may do it)

sizer = self.mainPanel.GetSizer()
sizer.Detach(0)
#
# or
sub_sizer = wxGridSizer(vgap=10, hgap=10)
sizer.Remove(sub_sizer)

#
#     Not a good example, but it works
#
import wx
 
class Test:
  def __init__(self):
    app = wx.App(False)
    self.app = app
    frame = wx.Frame(None, wx.ID_ANY, "Test App")
    panel = wx.Panel(frame)
    vboxsizer = wx.BoxSizer(wx.VERTICAL)
    self.frame = frame
    self.panel = panel
 
    self.grid = wx.GridSizer(rows=0, cols=3)

    self.button_dict = {}
    x = 10
    y = 20
    for j in range(5):
        print j, x, y
        b = wx.Button(panel, label="Remove Button %d" % (j), id=j, pos=(x, y))
        self.grid.Add(b)
        b.Bind(wx.EVT_BUTTON, self.removeButton)
        self.button_dict[j] = b
        x += 50
        if j == 2:
            x = 10
            y = 30 

    exit = wx.Button(panel, -1, 'Exit', (10, 200))
    exit.Bind(wx.EVT_BUTTON,  self.exit_this)

    vboxsizer.Add(self.grid, 1, border=2, flag=wx.EXPAND|wx.ALL)
    panel.SetSizer(vboxsizer)
    frame.Show(True)
 
    app.MainLoop()
 
  def removeButton(self,e):
      button_num = e.GetId()
      print "ID =", button_num, type(button_num)
      if button_num in self.button_dict:
          self.button_dict[button_num].Destroy()

  def exit_this(self, event):
        self.frame.Destroy()

if __name__ == "__main__":
  t = Test()
commented: very helpful +14

The wxpython docs list Detach, Remove, and RemoveSizer for a Sizer widger (GridSizer is a subclass of Sizer). So it would be something like (sorry, 186 lines of code is more than I want to wade through, though someone else may do it)

sizer = self.mainPanel.GetSizer()
sizer.Detach(0)
#
# or
sub_sizer = wxGridSizer(vgap=10, hgap=10)
sizer.Remove(sub_sizer)

#
#     Not a good example, but it works
#
import wx
 
class Test:
  def __init__(self):
    app = wx.App(False)
    self.app = app
    frame = wx.Frame(None, wx.ID_ANY, "Test App")
    panel = wx.Panel(frame)
    vboxsizer = wx.BoxSizer(wx.VERTICAL)
    self.frame = frame
    self.panel = panel
 
    self.grid = wx.GridSizer(rows=0, cols=3)

    self.button_dict = {}
    x = 10
    y = 20
    for j in range(5):
        print j, x, y
        b = wx.Button(panel, label="Remove Button %d" % (j), id=j, pos=(x, y))
        self.grid.Add(b)
        b.Bind(wx.EVT_BUTTON, self.removeButton)
        self.button_dict[j] = b
        x += 50
        if j == 2:
            x = 10
            y = 30 

    exit = wx.Button(panel, -1, 'Exit', (10, 200))
    exit.Bind(wx.EVT_BUTTON,  self.exit_this)

    vboxsizer.Add(self.grid, 1, border=2, flag=wx.EXPAND|wx.ALL)
    panel.SetSizer(vboxsizer)
    frame.Show(True)
 
    app.MainLoop()
 
  def removeButton(self,e):
      button_num = e.GetId()
      print "ID =", button_num, type(button_num)
      if button_num in self.button_dict:
          self.button_dict[button_num].Destroy()

  def exit_this(self, event):
        self.frame.Destroy()

if __name__ == "__main__":
  t = Test()

Thanks for the response. However, I have tried them. They all claim to be working, but the buttons remain on the screen.
However, after reading through your code more thoroughly, I had actually had the idea of using a dictionary for the button IDs. Now I see the implementation. I thank you for this, and will post my results when I can implement it.

I have got the buttons to delete. Everything works perfectly in my code now. It was one of those very annoying syntactical errors, and I ended up using the Remove method you suggested.

Now, I have a problem with the way my program loads. Everything loads fine, actually; it's just a problem with the display. My TextCtrl and Button overlap at the beginning, until the window is resized. It doesn't matter by how much, but as long as it is resized, it will correctly update. I've tried using the dummy SendSizeEvent to no avail. Here's my setup of widgets (don't worry, just the setup itself):

class Main(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title = 'Justin!', size=(250,250))
        self.background = wx.Panel(self)
        
        #Menubar
        self.menuBar = wx.MenuBar()
        self.SetMenuBar(self.menuBar)
        self.menuFile = wx.Menu()
        self.menuInfo = wx.Menu()
        
        self.menuBar.Append(self.menuFile, 'File')
        self.menuBar.Append(self.menuInfo, 'Info')
        
        self.newItem = self.menuFile.Append(-1, 'New Game')
        self.Bind(wx.EVT_MENU, self.newEvent, self.newItem)
        self.exitItem = self.menuFile.Append(-1, 'Exit')
        self.Bind(wx.EVT_MENU, self.onCloseWindow, self.exitItem)     
        
        self.instruItem = self.menuInfo.Append(-1, 'Instructions')
        self.Bind(wx.EVT_MENU, self.instruEvent, self.instruItem)
        self.aboutItem = self.menuInfo.Append(-1, 'About')
        self.Bind(wx.EVT_MENU, self.aboutEvent, self.aboutItem)

        #Status Bar
        self.CreateStatusBar()
        self.SetStatusText("Choose something to do.")
        
        #Sections
        self.displayInfo = wx.BoxSizer()
        self.displayText = wx.TextCtrl(self.background, -1, "Welcome to Riveter. Use the menu bar.", size = (200, 20), style = wx.TE_CENTRE|wx.TE_READONLY)

        self.buttonSizer = wx.BoxSizer()
        self.nextButton = wx.Button(self.background, -1, label = "Skip", style = wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.nextTurn, self.nextButton)
        self.buttonSizer.Add(self.nextButton, proportion = 1, flag = wx.ALIGN_RIGHT|wx.EXPAND, border = 0)

        self.displayInfo.Add(self.displayText, proportion = 1, flag = wx.EXPAND, border = 0)
        
        self.gridSizer = wx.BoxSizer()
        
        self.vertBox = wx.BoxSizer(wx.VERTICAL)
        self.vertBox.Add(self.displayText, proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
        self.vertBox.Add(self.gridSizer, proportion = 1, flag = wx.EXPAND, border = 0)
        self.vertBox.Add(self.buttonSizer, proportion = 0, flag = wx.TOP|wx.ALIGN_CENTER, border = 5)
        
        self.background.SetSizer(self.vertBox)
        self.Centre()
        self.Show()
        self.vertBox.Layout()
        self.SendSizeEvent()
        self.Update()
        self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
        self.gameCount = 0

Any help with how to fix this would be appreciated. Thanks!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.