I wanted to improve my GUI by using StaticBoxes. But since I added one I can't call my layout function without crashing python (Segementation fault; no other Error messages).
The following lines reproduce this error exactly. Did I use StaticBoxes correctly?
What do I need to get it to run properly? I use the nested Sizers frequently
so the layout looks nice ;).
import wx
class MainWindow(wx.Frame):
'''test frame'''
def __init__(self,*args,**kwargs):
'''Constructor'''
super(MainWindow,self).__init__(*args,**kwargs)
self.panel = wx.Panel(self)
self.box = wx.StaticBox(self.panel, wx.ID_ANY, u'input value1')
self.button_calc = wx.Button(self.panel, wx.ID_ANY, label=u'calc_xy')
self.Bind(wx.EVT_BUTTON, self.calculate, self.button_calc)
self._layout()
def _layout(self):
box_sizer = wx.StaticBoxSizer(self.box, wx.VERTICAL)
sizer = wx.GridBagSizer()
inside_the_box = wx.GridBagSizer()
box_sizer.Add(inside_the_box, 5, wx.ALL, 5)
sizer.Add(box_sizer, (0, 0), (2, 2), wx.EXPAND)
sizer.Add(self.button_calc, (2, 0))
self.panel.SetSizerAndFit(sizer)
def calculate(self, event):
print '5'
self._layout()
if __name__ == '__main__':
app = wx.App()
frame = MainWindow(None, -1, 'test window')
frame.Show()
app.MainLoop()
You are calling self._layout() in your __init__ method, and it works corectly for me in there.
Then when you click the button you call self._layout again and at that point you are trying to assign self.box to a new box_sizer but it is already assigned to a sizer therefore the segfault.
Related
I'm creating a wx.agw.SuperToolTip. I'm updating the message in the tip every few seconds, and if the tip is showing when the message updates the tip is redrawn in a different position.
The new position seems to be relative to the original position's relation to the top left corner of the screen, but that could just be coincidence.
Also, if I modify wx.lib.agw.supertooltip.ToolTipWindowBase.Invalidate() by commenting out the call to self.CalculateBestSize() the problem goes away. Of course then the window won't resize, so that's no solution.
I'm using wxPython 2.8.12.1.
Here's an app that demonstrates the problem:
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title,
pos=(150, 150), size=(350, 225))
panel = wx.Panel(self)
btn = wx.Button(panel, -1, "Hover over this")
self._superTip = SuperToolTip("")
self._superTip.SetHeader("Heyo!")
self._superTip.SetTarget(btn)
self._superTip.EnableTip(True)
self._superTip.SetDrawHeaderLine(True)
self._superTip.SetDrawFooterLine(True)
self._superTip.SetStartDelay(1)
self._superTip.SetEndDelay(60)
currentFooterFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
currentFooterFont.SetPointSize(6)
currentFooterFont.SetWeight(wx.NORMAL)
self._superTip.SetFooterFont(currentFooterFont)
self._superTip.SetFooter('(Click to close)')
self._superTip.ApplyStyle("Blue Glass")
self._superTip.SetDropShadow(True)
self.ttTimer = wx.Timer(self)
self.ttText = 'What the?'
self.Bind(wx.EVT_TIMER, self.onTimer, self.ttTimer)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(btn, 0, wx.ALL, 10)
panel.SetSizer(sizer)
self.ttTimer.Start(2000)
panel.Layout()
def onTimer(self, evt):
self._superTip.SetMessage(self.ttText)
self.ttText += '?'
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, "STT error demo")
self.SetTopWindow(frame)
frame.Show(True)
return True
app = MyApp(redirect=True)
app.MainLoop()
Any thoughts on how I can update a visible tooltip without its location changing?
Thanks a lot.
I am writing a text editor using wxpython. I want to display the line & column numbers based on the position of the text pointer(when changed using keyboard or mouse), in the statusbar.
I tried to do it using the below code:
import wx
class TextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Text Editor', size=(300, 250))
self.panel = wx.Panel(self, -1)
self.multiText = wx.TextCtrl(self.panel, -1,"",size=(200, 100), style=wx.TE_MULTILINE|wx.EXPAND)
sizer = wx.BoxSizer()
sizer.Add(self.multiText, proportion=1, flag=wx.CENTER|wx.EXPAND)
self.panel.SetSizer(sizer)
self.CreateStatusBar()
self.multiText.Bind(wx.EVT_KEY_DOWN, self.updateLineCol)
self.multiText.Bind(wx.EVT_LEFT_DOWN, self.updateLineCol)
def updateLineCol(self, event):
#lineNum = len(self.multiText.GetRange( 0, self.multiText.GetInsertionPoint() ).split("\n"))
l,c = self.multiText.PositionToXY(self.multiText.GetInsertionPoint())
self.StatusBar.SetStatusText(str(l)+","+str(c), number=0)
event.Skip()
app = wx.App(False)
frame = TextFrame()
frame.Show()
app.MainLoop()
I tried the below 2 ways to achieve that:
1.) lineNum = len(self.multiText.GetRange( 0, self.multiText.GetInsertionPoint() ).split("\n"))
2.) l,c = self.multiText.PositionToXY(self.multiText.GetInsertionPoint())
But, both, always give the line & column values of previous position of the pointer instead of current.
Is there a way to display the current line & column values?
I think you just need to bind to EVT_KEY_UP instead of EVT_KEY_DOWN. That way the event handler isn't called until after you have finished typing. I also updated the content written to the statusbar to make it a bit clearer which value was which:
import wx
class TextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Text Editor', size=(300, 250))
self.panel = wx.Panel(self, -1)
self.multiText = wx.TextCtrl(self.panel, -1,"",size=(200, 100), style=wx.TE_MULTILINE|wx.EXPAND)
sizer = wx.BoxSizer()
sizer.Add(self.multiText, proportion=1, flag=wx.CENTER|wx.EXPAND)
self.panel.SetSizer(sizer)
self.CreateStatusBar()
self.multiText.Bind(wx.EVT_KEY_UP, self.updateLineCol)
self.multiText.Bind(wx.EVT_LEFT_DOWN, self.updateLineCol)
def updateLineCol(self, event):
#lineNum = len(self.multiText.GetRange( 0, self.multiText.GetInsertionPoint() ).split("\n"))
l,c = self.multiText.PositionToXY(self.multiText.GetInsertionPoint())
stat = "col=%s, row=%s" % (l,c)
self.StatusBar.SetStatusText(stat, number=0)
event.Skip()
app = wx.App(False)
frame = TextFrame()
frame.Show()
app.MainLoop()
I've created wx.Grid widget inside my frame and I want to change my type of cursor if the user is using the grid widget. I've managed to do that with wx.StockCursor and .SetCursor methods but my cursor keeps returning to standard cursor if the user moves the cursor above the intersections of cell and row borders. What is causing this?
import wx
import wx.grid as Gridw
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, 'Data selection', size=(785, 540))
self.Centre()
#------------------------------------------------------------------------------
panel = wx.Panel(self, wx.ID_ANY)
#------------------------------------------------------------------------------
self.grid = Gridw.Grid(panel)
self.grid.CreateGrid(250, 250)
self.grid.EnableDragGridSize(0)
self.grid.DisableDragColSize()
self.grid.DisableDragRowSize()
self.grid.SetColMinimalWidth(0, 100)
#------------------------------------------------------------------------------
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_v = wx.BoxSizer(wx.VERTICAL)
sizer_v.Add(wx.Button(panel, -1, 'Button'), 1, wx.CENTER | wx.ALL, 5)
sizer.Add(self.grid, 1, wx.EXPAND, 5)
sizer.Add(sizer_v, 0)
panel.SetSizer(sizer)
#------------------------------------------------------------------------------
self.CreateStatusBar()
self.Show(True)
#------------------------------------------------------------------------------
cross_c = wx.StockCursor(wx.CURSOR_CROSS)
self.grid.SetCursor(cross_c)
if __name__ == '__main__':
app = wx.App()
frame = Frame().Show()
app.MainLoop()
Looks like the problem is related to that you've disabled grid resizing via EnableDragGridSize(0), DisableDragColSize() and DisableDragRowSize(). This can somewhat explain why you are seeing standard cursor on the cell borders.
Not sure if it'll help you since I don't know what OS are you using, but this works for me on linux:
cross_c = wx.StockCursor(wx.CURSOR_CROSS)
self.grid.GetGridWindow().SetCursor(cross_c)
One more option is to listen for EVT_MOTION and set cursor in the event listener:
self.cross_c = wx.StockCursor(wx.CURSOR_CROSS)
self.grid.GetTargetWindow().SetCursor(self.cross_c)
wx.EVT_MOTION(self.grid.GetGridWindow(), self.OnMouseMotion)
def OnMouseMotion(self, evt):
self.grid.GetTargetWindow().SetCursor(self.cross_c)
evt.Skip()
Hope that helps.
Help!
I am a non-GUI programmer who is trying to write a simple (!) program using wxPython.
I have read everything I can online, but my overall lack of GUI experience is presumably causing me to not see the problem.
In a nutshell, I want to have a window with a wxNotebook with several tabs. Each tab, of course, will have its own child widgets. I envision having either a wxListCtrl (as in my code) or possibly a wxGrid control, along with several buttons.
Here is my "EmployeesPanel" class. When I run this, I see a tiny square that must represent the listctrl, but for the life of me I cannot figure out how to make it look correct. Of course, it is possible that I am way off base in some other area(s) as well.
Any help as to what I am doing wrong would be greatly appreciated.
Here is the code:
import wx
import sys
employees = [('Earl Boffo', 'Software'), ('Mildred Plotka', 'Software'), ('Sugar Kane', 'QA')]
classes = [('Python'), ('Java'), ('C#')]
class EmployeesPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
grid = wx.GridBagSizer(hgap=5, vgap=5)
#hSizer = wx.BoxSizer(wx.HORIZONTAL|wx.EXPAND)
hSizer = wx.BoxSizer(wx.HORIZONTAL)
panel = wx.Panel(self, -1)
self.list = wx.ListCtrl(panel, size=(100,100), style=wx.LC_REPORT)
self.list.InsertColumn(0, 'Name')
self.list.InsertColumn(1, 'Group')
for i in employees:
index = self.list.InsertStringItem(sys.maxint, i[0])
self.list.SetStringItem(index, 1, i[1])
# A button
self.button = wx.Button(self, label="Exit")
self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
self.list.Show(True)
# add the listctrl widget to the grid
grid.Add(self.list, pos=(0,0), flag=wx.EXPAND|wx.ALL)
# add the button to the grid
grid.Add(self.button, pos=(1,0))
# add a spacer to the sizer
grid.Add((10, 40), pos=(1,1))
# add grid to hSizer
hSizer.Add(grid, 0, wx.ALL, 5)
# add hSizer to main (v) sizer
mainSizer.Add(hSizer, 0, wx.ALL, 5)
self.SetSizerAndFit(mainSizer)
self.Show()
def EvtComboBox(self, event):
self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
def OnClick(self,event):
sys.exit(3)
app = wx.App(False)
frame = wx.Frame(None, title="Training Tracker", size=(700,500))
nb = wx.Notebook(frame)
nb.AddPage(EmployeesPanel(nb), "Employees")
frame.Show()
app.MainLoop()
Welcome to wxPython! It's actually a lot of fun once you get the hang of it. I almost never use the grid sizers as they're just a pain for simple layouts like this. If you have a grid like interface and you don't have controls that are going to stretch across cells, then it's great. Otherwise, I almost always use BoxSizers nested in each other. I simplified your code quite a bit to show the two widgets. Currently the list control only stretches horizontally. If you need it to go vertically too, then change the proportion from 0 to 1 in the sizer.Add part.
import wx
import sys
employees = [('Earl Boffo', 'Software'), ('Mildred Plotka', 'Software'), ('Sugar Kane', 'QA')]
classes = [('Python'), ('Java'), ('C#')]
class EmployeesPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.list = wx.ListCtrl(self, size=(100,100), style=wx.LC_REPORT)
self.list.InsertColumn(0, 'Name')
self.list.InsertColumn(1, 'Group')
for i in employees:
index = self.list.InsertStringItem(sys.maxint, i[0])
self.list.SetStringItem(index, 1, i[1])
# A button
self.button = wx.Button(self, label="Exit")
self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
mainSizer.Add(self.list, 0, wx.EXPAND|wx.ALL, 5)
mainSizer.Add(self.button, 0, wx.ALL, 5)
self.SetSizer(mainSizer)
self.Show()
def EvtComboBox(self, event):
self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
def OnClick(self,event):
sys.exit(3)
app = wx.App(False)
frame = wx.Frame(None, title="Training Tracker", size=(700,500))
nb = wx.Notebook(frame)
nb.AddPage(EmployeesPanel(nb), "Employees")
frame.Show()
app.MainLoop()
I also think these articles might help you:
wxPython: wx.ListCtrl Tips and Tricks
wxPython: Using ObjectListView instead of a ListCtrl
http://wiki.wxpython.org/ListControls
I'm creating a small wxPython utility for the first time, and I'm stuck on a problem.
I would like to add components to an already created frame. To do this, I am destroying the frame's old panel, and creating a new panel with all new components.
1: Is there a better way of dynamically adding content to a panel?
2: Why, in the following example, do I get a a strange redraw error in which in the panel is drawn only in the top left hand corner, and when resized, the panel is drawn correctly?
(WinXP, Python 2.5, latest wxPython)
Thank you for the help!
import wx
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'TimeTablr')
#Variables
self.iCalFiles = ['Empty', 'Empty', 'Empty']
self.panel = wx.Panel(self, -1)
self.layoutElements()
def layoutElements(self):
self.panel.Destroy()
self.panel = wx.Panel(self, -1)
#Buttons
self.getFilesButton = wx.Button(self.panel, 1, 'Get Files')
self.calculateButton = wx.Button(self.panel, 2, 'Calculate')
self.quitButton = wx.Button(self.panel, 3, 'Quit Application')
#Binds
self.Bind(wx.EVT_BUTTON, self.Quit, id=3)
self.Bind(wx.EVT_BUTTON, self.getFiles, id=1)
#Layout Managers
vbox = wx.BoxSizer(wx.VERTICAL)
#Panel Contents
self.ctrlsToDescribe = []
self.fileNames = []
for iCalFile in self.iCalFiles:
self.ctrlsToDescribe.append(wx.TextCtrl(self.panel, -1))
self.fileNames.append(wx.StaticText(self.panel, -1, iCalFile))
#Add Components to Layout Managers
for i in range(0, len(self.ctrlsToDescribe)):
hboxtemp = wx.BoxSizer(wx.HORIZONTAL)
hboxtemp.AddStretchSpacer()
hboxtemp.Add(self.fileNames[i], 1, wx.EXPAND)
hboxtemp.AddStretchSpacer()
hboxtemp.Add(self.ctrlsToDescribe[i], 2, wx.EXPAND)
hboxtemp.AddStretchSpacer()
vbox.Add(hboxtemp)
finalHBox = wx.BoxSizer(wx.HORIZONTAL)
finalHBox.Add(self.getFilesButton)
finalHBox.Add(self.calculateButton)
finalHBox.Add(self.quitButton)
vbox.Add(finalHBox)
self.panel.SetSizer(vbox)
self.Show()
def Quit(self, event):
self.Destroy()
def getFiles(self, event):
self.iCalFiles = ['Example1','Example1','Example1','Example1','Example1','Example1']
self.layoutElements()
self.Update()
app = wx.App()
MainFrame()
app.MainLoop()
del app
1) I beleive the Sizer will let you insert elements into the existing ordering of them. That would probably be a bit faster.
2) I don't see the behavior you're describing on OSX, but at a guess, try calling self.Layout() before self.Show() in layoutElements?
I had a similar problem where the panel would be squished into the upper-right corner. I solved it by calling panel.Fit().
In your example, you should call self.panel.Fit() after self.panel.SetSizer(vbox)