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()
Related
I've not been able to find an answer in the documentation. Say I have the following:
import wx
from wx import *
import sys
app = wx.App()
def quitProgram(*args):
sys.exit()
def restart(*args):
app.MainLoop()
xSize = 500
ySize = 300
window = wx.Frame(None, title = "My GUI", size = (xSize,ySize))
panel = wx.Panel(window)
# generic label
labelLeft = wx.StaticText(panel, label = 'some text', pos = (2,30))
# exit button
exit = wx.Button(panel, -1, label="Exit", pos = (1, 1), size=(-1,-1))
exit.Bind(wx.EVT_BUTTON, quitProgram)
# reset button
reset = wx.Button(panel, -1, label="Refresh", pos = (100,1), size=(-1,-1))
reset.Bind(wx.EVT_BUTTON, restart)
window.Show(True)
app.MainLoop()
How can I position the objects- buttons and labels- based on the size of the main window? I'd like the objects to reposition based on resizing the window.
The code posted is not clean wxPython code.
You must instantiate your Frame as a Class and use Sizers to automate positioning of your widgets.
A minimal code that reproduces your frame using Sizers is this:
import wx
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((500, 300))
self.bt_exit = wx.Button(self, wx.ID_ANY, "exit")
self.bt_refresh = wx.Button(self, wx.ID_ANY, "refresh")
self.text_ctrl = wx.TextCtrl(self, wx.ID_ANY, "some text", style=wx.TE_MULTILINE)
self.SetTitle("My GUI")
self.bt_exit.Bind(wx.EVT_BUTTON, self.on_exit)
self.bt_refresh.Bind(wx.EVT_BUTTON, self.on_refresh)
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(self.bt_exit, 1, 0, 0)
sizer_2.Add(self.bt_refresh, 1, 0, 0)
sizer_1.Add(sizer_2, 0, wx.EXPAND, 0)
sizer_1.Add(self.text_ctrl, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.Layout()
def on_exit(self, evt):
self.Close()
def on_refresh(self, evt):
self.text_ctrl.Clear()
if __name__ == "__main__":
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
This is well explained in wxPython/Phoenyx docs. Check for example these tutorials
My problem is this :
I have created a scrolledPanel in a Frame. In this scrolledPanel I have a BoxSizer which contains several panels. When I add to many panels in BoxSizer, panels are stacked in the end of BoxSizer.
I am completely stuck with this problem and still have no clue as to how to fix it.
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial", size=(200,500))
# Add a panel so it looks the correct on all platforms
self.panel = wx.Panel(self, wx.ID_ANY)
# --------------------
# Scrolled panel stuff
self.scrolled_panel = scrolled.ScrolledPanel(self.panel, -1,
style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER, name="panel1")
self.scrolled_panel.SetAutoLayout(1)
self.scrolled_panel.SetupScrolling()
words = range(0,2000)
self.spSizer = wx.BoxSizer(wx.VERTICAL)
self.spSizer1 = wx.BoxSizer(wx.VERTICAL)
self.spSizer2 = wx.BoxSizer(wx.VERTICAL)
for word in words:
text = wx.TextCtrl(self.scrolled_panel, value=str(word))
self.spSizer.Add(text)
self.scrolled_panel.SetSizer(self.spSizer)
self.scrolled_panel.Layout()
self.scrolled_panel.SetupScrolling()
# --------------------
btn = wx.Button(self.panel, label="Add Widget")
btn.Bind(wx.EVT_BUTTON, self.onAdd)
panelSizer = wx.BoxSizer(wx.VERTICAL)
panelSizer.AddSpacer(50)
panelSizer.Add(self.scrolled_panel, 1, wx.EXPAND)
panelSizer.Add(btn)
self.panel.SetSizer(panelSizer)
#----------------------------------------------------------------------
def onAdd(self, event):
""""""
print "in onAdd"
new_text = wx.TextCtrl(self.scrolled_panel, value="New Text")
self.spSizer.Add(new_text)
self.scrolled_panel.Layout()
self.scrolled_panel.SetupScrolling()
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()
I put a code sample that shows the problem
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.
I am trying to make a window with 2 panels. One panel is just a notebook panel. The second panel contains a toolbar on top and a text control on the bottom. I want to arrange this panel in my frame using wx.aui.AuiManager.
The problem is that I get a big empty space of grey in my custom panel.
Here is my code:
import wx
import wx.aui
import images # contains toolbar icons
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"AUI Tutorial",
size=(600,400))
self._mgr = wx.aui.AuiManager()
self._mgr.SetManagedWindow(self)
notebook = wx.aui.AuiNotebook(self)
nb_panel = TabPanel(notebook)
my_panel = MyPanel(self)
notebook.AddPage(nb_panel, "First Tab", False)
self._mgr.AddPane(notebook,
wx.aui.AuiPaneInfo().Name("notebook-content").
CenterPane().PaneBorder(False))
self._mgr.AddPane(my_panel,
wx.aui.AuiPaneInfo().Name("txtctrl-content").
CenterPane().PaneBorder(False))
self._mgr.GetPane("notebook-content").Show().Top().Layer(0).Row(0).Position(0)
self._mgr.GetPane("txtctrl-content").Show().Bottom().Layer(1).Row(0).Position(0)
self._mgr.Update()
class MyPanel(wx.Panel):
"""
My panel with a toolbar and richtextctrl
"""
def __init__(self,parent):
wx.Panel.__init__(self,parent=parent,id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
toolbar = wx.ToolBar(self,-1)
toolbar.AddLabelTool(wx.ID_EXIT, '', images._rt_smiley.GetBitmap())
self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)
toolbar.Realize()
sizer.Add(toolbar,proportion=0,flag=wx.ALL | wx.ALIGN_TOP)
text = ""
txtctrl = wx.TextCtrl(self,-1, text, wx.Point(0, 0), wx.Size(150, 90),
wx.NO_BORDER | wx.TE_MULTILINE | wx.TE_READONLY|wx.HSCROLL)
sizer.Add(txtctrl,proportion=0,flag=wx.EXPAND)
self.SetSizer(sizer)
def OnExit(self,event):
self.Close()
class TabPanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent=parent,id=wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
txtOne = wx.TextCtrl(self, wx.ID_ANY, "")
txtTwo = wx.TextCtrl(self, wx.ID_ANY, "")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(txtOne, 0, wx.ALL, 5)
sizer.Add(txtTwo, 0, wx.ALL, 5)
self.SetSizer(sizer)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame()
frame.Show()
app.MainLoop()
So, how do I fix my code so that I don't have that grey block taking up MyPanel? Also, my toolbar button doesn't seem to run self.OnExit(). Why is that?
Thank you for your help.
Take out the line:
self._mgr.GetPane("notebook-content").Show().Top().Layer(0).Row(0).Position(0)
As for the OnExit() handler, it is firing!
If you want to exit the application, replace it with app.Exit()
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)