I am using python 2.7 and the latest version of wxpython.
I need to place 2 grids side by side and to be able to add rows dynamically. Ideally, the sizes of grids should grow dynamically, but "big enough" size should be ok for the time being.
How should I change (and simplify) my code to make this happen?
class MatricesFrame(wx.BoxSizer):
def __init__(self, parentPanel):
super(MatricesFrame, self).__init__(wx.HORIZONTAL)
self.outputsMatrix = self.addEmptyGrid(parentPanel)
self.inputsMatrix = self.addEmptyGrid(parentPanel)
addRowsButton = wx.Button(parentPanel, -1, " Add Rows")
addRowsButton.Bind(wx.EVT_BUTTON, self.addRows)
self.Add(addRowsButton)
def initResize(self, ev = None):
self.inputsMatrix.SetSize((500, 500))
self.outputsMatrix.SetSize((500, 500))
def addEmptyGrid(self, parentPanel):
panel_ = wx.Panel(parentPanel)
sizer_ = wx.BoxSizer(wx.VERTICAL)
panel_.SetSizer(sizer_)
panel_.SetSize((500, 500))
matrix_ = wx.grid.Grid(panel_)
matrix_.SetRowLabelSize(0)
matrix_.SetColLabelSize(0)
matrix_.CreateGrid(1,1)
sizer_.Add(matrix_)
self.Add(panel_)
return matrix_
def addRows(self, ev=None):
self.inputsMatrix.AppendRows(1)
self.outputsMatrix.AppendRows(1)
class TestFrame(wx.Frame):
def __init__(self, parent):
super(TestFrame, self).__init__(parent, title='test', size=(1280, 950))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
self.matricesFrame = MatricesFrame(panel)
box.Add(self.matricesFrame)
panel.SetSizer(box)
self.matricesFrame.initResize()
self.Centre()
self.Show()
self.matricesFrame.initResize()
wx.EVT_IDLE(wx.GetApp(), wx.WakeUpIdle())
def main():
app = wx.App(False)
t= TestFrame(None)
app.MainLoop()
if __name__ == '__main__':
main()
For starters, if you convert your custom Sizer into a custom Panel I think it makes your code a lot easier to control. Take a look at what I came up with
class MatricesPanel(wx.Panel):
def __init__(self, parent):
super(MatricesPanel, self).__init__(parent)
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.outputsMatrix = self.addEmptyGrid(sizer)
self.inputsMatrix = self.addEmptyGrid(sizer)
addRowsButton = wx.Button(self, -1, " Add Rows")
addRowsButton.Bind(wx.EVT_BUTTON, self.addRows)
sizer.Add(addRowsButton)
self.SetSizer(sizer)
self.SetAutoLayout(True)
self.Layout()
def initResize(self, ev = None):
self.inputsMatrix.SetSize((500, 500))
self.outputsMatrix.SetSize((500, 500))
self.Layout()
def addEmptyGrid(self, sizer):
matrix_ = wx.grid.Grid(self)
matrix_.SetRowLabelSize(0)
matrix_.SetColLabelSize(0)
matrix_.CreateGrid(1,1)
sizer.Add(matrix_)
return matrix_
def addRows(self, ev=None):
self.inputsMatrix.AppendRows(1)
self.outputsMatrix.AppendRows(1)
self.Layout() #refresh the frame
Working with a Panel instead of a Sizer you greatly simplify your "addEmptyGrid" method as well as now you can try setting the size of your matricies using the size of the panel, not the size of the matrices themselves. Also, this allows you the flexibility to change from wx.Panel to wx.lib.scrolledpanel.ScrolledPanel if you wanted to add scroll bars (for if you add a lot of rows).
You can then init your new panel as below:
class TestFrame(wx.Frame):
def __init__(self, parent):
super(TestFrame, self).__init__(parent, title='test', size=(1280, 950))
self.matricesPanel = MatricesPanel(self)
sizer = wx.BoxSizer()
sizer.Add(self.matricesPanel, flag=wx.EXPAND)
self.SetSizer(sizer)
self.Centre()
self.Show()
self.matricesPanel.initResize()
wx.EVT_IDLE(wx.GetApp(), wx.WakeUpIdle())
Finally, so far as I can tell, columns in a Grid have a fixed width, so your matrix.SetSize((500, 500)) calls arent' doing much. If you can find a way to set the width of the column then I suggest you rewrite initResize() to set the width relative to the width of the panel.
Related
I'm trying to create an App which allows the user to switch the information show using buttons. The basic idea of the code is that the user sees buttons on the left side of the screen and when the user presses "button 1", the code shows Panel1. I've made 2 buttons so far and the code for 2 panels is written as well but i can't figure out how to update my MainFrame so it show a different panel when one of the buttons is pressed.
Code:
import wx
TabNumber = 1
class ButtonPanel(wx.Panel):
def __init__(self, parent):
global TabNumber
super(ButtonPanel, self).__init__(parent, -1)
self.Tab1Button = wx.Button(self, label="TAB 1")
self.Tab1Button.Bind(wx.EVT_BUTTON, self.SwitchTab(1))
self.Tab2Button = wx.Button(self, label="TAB 2")
self.Tab2Button.Bind(wx.EVT_BUTTON, self.SwitchTab(2))
self.Sizer = wx.BoxSizer(wx.VERTICAL)
self.Sizer.Add(self.Tab1Button, wx.CENTER,0)
self.Sizer.Add(self.Tab2Button, wx.CENTER, 0)
self.SetSizer(self.Sizer)
def SwitchTab(self, tab):
def OnClick(event):
print(f"Switch to tab {tab} started")
TabNumber = tab
print(TabNumber)
return OnClick
class Panel1(wx.Panel):
def __init__(self, parent):
super(Panel1, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="1")
class Panel2(wx.Panel):
def __init__(self, parent):
super(Panel2, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="2")
class MainFrame(wx.Frame):
def __init__(self):
super(MainFrame, self).__init__(None, -1, "Test Application")
self.Panels = {
"Panel1": Panel1(self),
"Panel2": Panel2(self)
}
self.MySizer = wx.BoxSizer(wx.HORIZONTAL)
self.tabpanel = ButtonPanel(self)
self.MySizer.Add(self.tabpanel,wx.CENTER,0)
self.InfoPanel = self.Panels["Panel"+str(TabNumber)]
self.MySizer.Add(self.InfoPanel, wx.CENTER,0)
self.SetSizer(self.MySizer)
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
I was also wondering how I can adjust the ratio for the space that is given to my ButtonPanel and my InfoPanel.
As far as I can see, you are trying to do something that works like a Wizard... On the one hand, you can use wx.adv.Wizard. On the other hand, you can look at this tutorial that does something very similar and adapt it to what you need:
WXPython: How to create a generic wizard
Good luck!
I am having trouble nesting a ScrolledWindow inside a wx.Panel.
If I create a scrolled window on its own it seems to work, however when I create it inside a wx.Panel and add the wx.Panel to the frames sizer it does not. Is there anything that I am missing?
Note:
#pa = AScrolledWindow(self) <-- if uncommented this works
pa = ScrolledWindowHolder(self) # However this does not!
import wx
class ScrolledWindowHolder(wx.Panel):
def __init__(self, parent):
super(ScrolledWindowHolder, self).__init__(parent=parent)
mysizer = wx.GridBagSizer()
self.myscrolledWindow = AScrolledWindow(self)
mysizer.Add(self.myscrolledWindow, pos=(0, 0), flag=wx.EXPAND)
self.SetSizerAndFit(mysizer)
class AScrolledWindow(wx.ScrolledWindow):
def __init__(self, parent):
super(AScrolledWindow, self).__init__(parent)
gb = wx.GridBagSizer()
self.sizer = gb
self._labels = []
for y in xrange(1, 30):
self._labels.append(wx.StaticText(self, -1, "Label #%d" % (y,)))
gb.Add(self._labels[-1], (y, 1), (1, 1))
self.SetSizer(self.sizer)
self.SetScrollRate(5, 5)
self.EnableScrolling(True, True)
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Programmatic size change')
sz = wx.BoxSizer(wx.VERTICAL)
#pa = AScrolledWindow(self)
pa = ScrolledWindowHolder(self)
sz.Add(pa, 1, wx.EXPAND)
self.SetSizer(sz)
def main():
wxapp = wx.App()
fr = TestFrame()
fr.Show(True)
wxapp.MainLoop()
if __name__ == '__main__':
main()
Not sure why but the issue appears to be with the fact that you are using a GridBagSizer with a single widget in ScrolledWindowHolder, which itself contains a GridBagSizer.
If you change ScrolledWindowHolder to use a BoxSizer it works, as expected.
class ScrolledWindowHolder(wx.Panel):
def __init__(self, parent):
super(ScrolledWindowHolder, self).__init__(parent=parent)
mysizer = wx.BoxSizer(wx.HORIZONTAL)
self.myscrolledWindow = AScrolledWindow(self)
mysizer.Add(self.myscrolledWindow, 1, wx.EXPAND,0)
self.SetSizerAndFit(mysizer)
Also, change the value of y to for y in range(1, 60): will demonstrate the scrolled window more effectively.
i am using wxpython and trying to make an background to a sizer without any success, i searched in google without any results.
i try it with this boxsizer
wx.BoxSizer(wx.HORIZONTAL)
I just use panels for this sort of thing. You can set the color of the panel several different ways: you can use a named color, a wx.Color object, a predefined wx.Color object like wx.RED or a tuple of 3 integers.
Here's a simple example:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.SetBackgroundColour('white')
main_sizer = wx.BoxSizer(wx.VERTICAL)
for number in range(5):
btn = wx.Button(self, label='Button {}'.format(number))
main_sizer.Add(btn, 0, wx.ALL, 5)
self.SetSizer(main_sizer)
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Background colors')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MyFrame()
app.MainLoop()
I wrote a little about this topic here:
https://www.blog.pythonlibrary.org/2009/09/03/wxpython-resetting-the-background-color/
You might also the wxPython wiki helpful:
https://wiki.wxpython.org/GettingStarted
The following code makes a window with a grey gradient bar.
import wx
class GradientFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Center()
self.Show()
def OnSize(self, event):
event.Skip()
self.Refresh()
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self)
rect = self.GetClientRect()
dc.SetBackground(wx.Brush("white"))
dc.Clear()
rect.SetHeight(30)
dc.GradientFillLinear(rect, '#fbfbfb', '#efefef', wx.SOUTH)
rect.SetTop(30)
rect.SetHeight(2)
dc.GradientFillLinear(rect, '#dbdbdb', '#c1c1c1', wx.SOUTH)
app = wx.App(0)
frame = GradientFrame(None, 'Test')
app.MainLoop()
I would like to add toogle buttons like the following screenshot, that allows to access to different pages / panels of the GUI (each of them containing their own widgets, etc.)
What is the good framework for that : should these buttons be created manually in OnPaint (this would be very 'low-level') or somewhere else? Is there a ready-to-use way to use buttons linked to different pages ?
There is no good framework for creating custom widgets. However, there are some good recipes out there:
https://stackoverflow.com/questions/1351448/how-to-make-custom-buttons-in-wx
http://wiki.wxpython.org/CreatingCustomControls
Those two links should get you started. You can also take a look at the source for GenericButtons, AquaButton or PlateButton for additional ideas.
Alternatively, you could also just create a panel that's a specific size and put some of the custom buttons or just regular buttons on it instead.
Here's an example of how to use PlateButtons that should get you started:
import wx
import wx.lib.platebtn as platebtn
class GradientFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title)
self.panel = wx.Panel(self)
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
self.panel.Bind(wx.EVT_SIZE, self.OnSize)
# add plate buttons
top_sizer = wx.BoxSizer(wx.VERTICAL)
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
labels = ["Morceaux", "Albums", "Artistes", "Genres"]
style = platebtn.PB_STYLE_GRADIENT
for label in labels:
btn = platebtn.PlateButton(self.panel, label=label, style=style)
btn.SetPressColor(wx.Colour(208,208,208))
btn_sizer.Add(btn, 0, wx.RIGHT|wx.LEFT|wx.CENTER, 5)
top_sizer.Add(btn_sizer, 0, wx.ALL|wx.CENTER, 5)
top_sizer.Add((1,1), 1, wx.EXPAND)
self.panel.SetSizer(top_sizer)
self.Center()
self.Show()
def OnSize(self, event):
event.Skip()
self.Refresh()
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self.panel)
rect = self.panel.GetClientRect()
dc.SetBackground(wx.Brush("white"))
dc.Clear()
rect.SetHeight(30)
dc.GradientFillLinear(rect, '#fbfbfb', '#efefef', wx.SOUTH)
rect.SetTop(30)
rect.SetHeight(2)
dc.GradientFillLinear(rect, '#dbdbdb', '#c1c1c1', wx.SOUTH)
app = wx.App(0)
frame = GradientFrame(None, 'Test')
app.MainLoop()
I'm trying to build a multypane wxAUI application with a multipane structure.
I have two class of objects that are controlled by an AuiManager (_mgr) that are:
1) wx.Grid.CSheet objects inserted into an AuiNotebook inside a pane
2) Matplotlib.figure objects also incapsulated into an other AuiNotebook inside the same AuiManager.
Here are the two classes defining the corresponding objects.
class MySheet(sheet.CSheet):
""" A Copy&Paste enabled grid class"""
def __init__(self, parent,NRows,NCols):
sheet.CSheet.__init__(self, parent)
wx.EVT_KEY_DOWN(self, self.OnKey)
self.SetLabelBackgroundColour('#DBD4D4')
self.SetNumberRows(NRows)
self.SetNumberCols(NCols)
self.NRows = NRows
self.NCols = NCols
def selection(self):
MORE CODE HERE
def OnKey(self, event):
MORE CODE HERE
def copy(self):
MORE CODE HERE
def paste(self):
MORE CODE HERE
def delete(self):
MORE CODE HERE
class Plot(wx.Panel):
def __init__(self, parent, id = -1, dpi = None, **kwargs):
wx.Panel.__init__(self, parent, id=id, **kwargs)
self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2,2))
self.canvas = Canvas(self, -1, self.figure)
self.toolbar = Toolbar(self.canvas)
self.toolbar.Realize()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas,1,wx.EXPAND)
sizer.Add(self.toolbar, 0 , wx.LEFT | wx.EXPAND)
self.SetSizer(sizer)
class PlotNotebook(wx.Panel):
def __init__(self, parent, id = -1):
wx.Panel.__init__(self, parent, id=id)
self.nb = wx.aui.AuiNotebook(self)
sizer = wx.BoxSizer()
sizer.Add(self.nb, 1, wx.EXPAND)
self.SetSizer(sizer)
def add(self,name="plot"):
page = Plot(self.nb)
self.nb.AddPage(page,name)
return page.figure
AS default I have set two figures and two grid with two columns each correponding to X and Y data respectively. I would like to synchronize the X,Y data in a grid with the corresponding figure so that when these are changed also the figure is updated.
At creation time I'm able to fill the grids and figures with my data content, but I'm not able to updated the figure content after a change event such as when new data are pasted into the grid. I've tryed using after the update figure.canvas.draw() and _mgr.Update() but they seems not to work despite I don't get any error.