Enter key in wx.TextCtrl on Mac has no effect - python

When I run the program below on Mac OS X Yosemite, pressing the enter key inside the TextCtrl doesn't have any effect on the contents of the TextCtrl at all (I need it to enter a newline in the text).
Adding or removing the TE_PROCESS_ENTER style doesn't have any effect at all; the EVT_TEXT_ENTER event doesn't get fired. Pressing enter does trigger an EVT_KEY_UP event with keycode 13, by the way.
Strangely enough, pressing Ctrl+Enter does cause a newline to be entered inside the TextCtrl, but it also doesn't fire an EVT_TEXT_ENTER event.
What is happening here? Of course I could work around this and detect keycode 13, but of course that doesn't really solve the problem.
#!/usr/bin/env pythonw
import wx
class MainWindow(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(500,500))
self.tc = wx.TextCtrl(self, wx.TE_MULTILINE|wx.TE_PROCESS_ENTER, size=(200,100))
self.tc.Bind(wx.EVT_KEY_UP, self.OnKeyUp, self.tc)
self.tc.Bind(wx.EVT_TEXT_ENTER, self.OnEnter, self.tc)
self.Show(True)
def OnKeyUp(self, event):
print event.GetKeyCode()
def OnEnter(self, event):
# Never gets called
print "enter!"
if __name__ == '__main__':
app = wx.App()
frame = MainWindow('Test')
app.MainLoop()
I'm on Mac OS X (Yosemite), using python 2.7.9 (through homebrew) and wxPython 3.0.2.0.

the problem is in the call to the wxTextCtrl constructor; the flags are not being passed in correctly.
self.tc = wx.TextCtrl(self, wx.TE_MULTILINE|wx.TE_PROCESS_ENTER, size=(200,100))
should really be
self.tc = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER, size=(200,100))
The text control was not being initialized to be a multi-line control.

Related

Dialog is not modal when ProgressDialog and Dialog are opened in sequence

I'm running wxPython 4.0.1 msw (phoenix) with Python 3.6.5 on a Windows7 machine, as well as wxPython 2.9.4 with Python 2.7.
I'm observing an issue with a modal dialog, which doesn't block the access to its parent window behind. This only occurs if I run a progress dialog followed by a modal dialog. This behavior is somehow related to custom dialogs. Integrated dialogs like wx.MessageDialog doesn't seem to have this issue.
To isolate the issue, I've written an example. The first two buttons open either the progress or the modal dialog and work properly. The third button opens both dialogs in sequence. In this case the modal functionality of the custom dialog doesn't work and I'm able to access and close the mainframe. Which causes multiple issues.
Dialog is not modal, the main window can be accessed and closed
import wx
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title='SomeDialog',
style=wx.DEFAULT_DIALOG_STYLE)
self.button_ok = wx.Button(self, wx.ID_OK, size=(120,-1))
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self.button_ok, 0, wx.ALL|wx.ALIGN_CENTER, 10)
self.SetSizer(hsizer)
self.SetSize(self.BestSize)
self.Layout()
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, size=(400, 400))
self.button_progress = wx.Button(self, -1, 'Show Progress')
self.button_modal = wx.Button(self, -1, 'Show Modal')
self.button_both = wx.Button(self, -1, 'Show Both')
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_progress)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_modal)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_both)
sizer = wx.BoxSizer()
sizer.Add(self.button_progress)
sizer.Add(self.button_modal)
sizer.Add(self.button_both)
self.SetSizer(sizer)
def on_button(self, event):
if event.EventObject is self.button_progress:
self._show_progress_dialog()
elif event.EventObject is self.button_modal:
self._show_modal_dialog()
else:
self._show_progress_dialog()
self._show_modal_dialog()
def _show_progress_dialog(self):
max = 10
dlg = wx.ProgressDialog('Progress dialog example', 'Some message',
maximum=max, parent=self,
style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE)
keepGoing = True
count = 0
while keepGoing and count < max:
count += 1
wx.MilliSleep(250)
wx.Yield()
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
def _show_modal_dialog(self):
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
frame = TestFrame()
frame.Show()
app.MainLoop()
In case this is an issue in the wxpython framework and not an issue with my implementation, it would be great if someone could provide me a workaround to show such dialogs in sequence.
This looks like a bug to me. I'm not sure why its happening, but one workaround would be to use wx.CallLater
changing _show_modal_dialog to:
def _show_modal_dialog(self):
def _make_dialog():
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
wx.CallLater(50, _make_dialog) # 50 mils is arbitrary
Seems to resolve the issue of the dialog not acting modalish. The problem with this workaround is that it will be non-blocking, meaning that any code that has to wait for the dialog to return has to be moved into the dialog class or passed to the dialog as a callback.
In the meantime I found a workaround myself which I like to share.
I added a handler catching the windows close event.
class TestFrame(wx.Frame):
def __init__(self):
#...
self.Bind(wx.EVT_CLOSE, self.on_close)
This event function checks if some child dialog is open and modal and performs a veto.
def on_close(self, event):
# In case any modal dialog is open, prevent the frame from closing.
for children in (c for c in self.Children if isinstance(c, wx.Dialog)):
if children.IsModal():
event.Veto()
return
event.Skip()
This is also only a workaround, but I seems to work for my use cases.

wxPython: How can I listen to EVT_CHAR events on a multiline TextCtrl?

I am able to successfully listen to EVT_CHAR events on a TextCtrl but when I change the TextCtrl to use TE_MULTILINE then the binding seems to stop working.
self.input = wx.TextCtrl(self, style=wx.TE_MULTILINE)
self.input.Bind(wx.EVT_CHAR, self.OnChar)
I am using 3.0.3.dev1820+49a8884 osx-cocoa (phoenix).
How can I listen to EVT_CHAR events on a multiline TextCtrl?
The code exhibited in the question should work, and does if you're not on a Mac. The fact that it doesn't work on Macs is a bug - I reported it at https://github.com/wxWidgets/Phoenix/issues/804 and it's confirmed by the maintainers.
The bug will be fixed in wxPython 4.1, which at the time of writing is not yet released.
If you have to use a version of wxPython that doesn't have the fix, then depending upon your objectives it may be adequate to bind to wx.EVT_TEXT or wx.EVT_KEY_DOWN instead, although neither has quite the same behaviour as wx.EVT_CHAR.
I could not reproduce your problem. Here's the working code that I modified from the Mouse vs the Python blog. I only changed the style of text control to wx.TE_MULTILINE and it still works on Windows7 (wxPython2.8.12.1). Could it be a Phoenix bug?
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Char Event Tutorial")
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
btn = wx.TextCtrl(panel, value="", style=wx.TE_MULTILINE)
btn.Bind(wx.EVT_CHAR, self.onCharEvent)
def onCharEvent(self, event):
keycode = event.GetKeyCode()
controlDown = event.CmdDown()
altDown = event.AltDown()
shiftDown = event.ShiftDown()
print keycode
if keycode == wx.WXK_SPACE:
print "you pressed the spacebar!"
elif controlDown and altDown:
print keycode
event.Skip()
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm()
frame.Show()
app.MainLoop()

wxPython event starts up at startup

So I am a complete beginner at python and usually code in C/C++ or Java. In the process of developing a GUI my events keep getting called at the start of the program running instead of when I click the button. (i know my indents are wrong because I needed to put it into a code block). How do I make my events only get called when I left click the button?
def __init__(self, parent, title):
super(QuadDash, self).__init__(parent, title=title, size=(1024, 780))
self.SetBackgroundColour("white")
pnl = wx.Panel(self)
cbtn = wx.Button(pnl, label='Start Quad', pos=(20,30))
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
self.Show(True)
def start_quad_event(self):
dlg = wx.MessageDialog(self, "Test", "ABC", wx.YES_NO | wx.ICON_QUESTION)
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
qd = QuadDash(None, title='QuadCopter Dashboard')
app.MainLoop()
The offending line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
You actually call start_quad_event() and pass the result to bind().
The correct line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event, cbtn)
Note: No parentheses. Here you actually pass the function to bind().

wxPython clearing hinted text causes crash

As the title says when I implement code to clear hinted text and it is run the application crashes.
As far as I know this is only on Mac OS X 10.8 but that is also all I have been able to run it on.
On other code it does run and only once I enter text into it (After giving focus) does it crash. But this app crashes immediately (I think something to do with no other widgets and so it get focus right away). Updated example so it no longer crashes right away now you have to click on the combo box and try to type in it for it to crash.
This however does not occur if the text is anything but "" it seems.
Code
import wx
class MyCrashyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
# another widget to take focus at first otherwise it crashes instantly!
sizer = wx.BoxSizer(wx.VERTICAL)
self.text_ctrl = wx.TextCtrl(self, -1, value = "There are major problems here.\nWithout this to auto take focus this will crash immediatly just by trying to clear the hint.\nNow you have to click on the combo ctrl and try to type.", style = wx.TE_MULTILINE)
self.search_ctrl = wx.ComboBox(self, -1)
self.search_ctrl.SetMinSize((650, -1))
self.search_ctrl.SetSize((650, -1))
self.search_ctrl.SetHint("This is are hint text; once it is clear and you try to type something in it it will crash on Mac OS X")
sizer.Add(self.text_ctrl, flag = wx.EXPAND)
sizer.Add(self.search_ctrl)
self.SetSizer(sizer)
self.FirstTimeSearchGetsFocus = True
self.Bind(wx.EVT_BUTTON, lambda e: e.Skip(), self.text_ctrl)
self.search_ctrl.Bind(wx.EVT_SET_FOCUS, self.OnSearchFocus)
self.text_ctrl.SetFocus()
def OnSearchFocus(self, event):
print "Search Focus"
if 1==1:
print "First time"
# clear the hinted text
self.search_ctrl.SetHint("")
self.search_ctrl.Clear()
self.search_ctrl.Refresh()
self.FirstTimeSearchGetsFocus = False
event.Skip()
if __name__ == "__main__":
app = wx.App(False)
f = wx.Frame(None, -1)
MyCrashyPanel(f)
f.Show()
app.MainLoop()
Crash Report
[Too big get here http://pastebin.com/9B1Sgh3P ]
If it crashes, it's a bug in wxWidgets, so the only things to do are to:
Try with a later version, i.e. 2.9.5 or svn/git if you can build it yourself.
Report the bug if it still persists there, following the usual guidelines.

wxPython EVT_CHAR not called when a panel is added to a frame

I need to bind the EVT_CHAR event for a GUI application I am developing using wxPython. I tried the following and I cann understand the beahviour of the code.
import wx
import wx.lib.agw.flatnotebook as fnb
class DemoApp(wx.App):
def __init__(self):
wx.App.__init__(self, redirect=False)
self.mainFrame = DemoFrame()
self.mainFrame.Show()
def OnInit(self):
return True
class DemoFrame(wx.Frame):
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, wx.ID_ANY,
"FlatNotebook Tutorial",
size=(600,400)
)
panel = wx.Panel(self)
button = wx.Button(panel, label="Close", pos=(125, 10), size=(50, 50))
self.Bind(wx.EVT_CHAR, self.character)
def character(self, event):
print "Char keycode : {0}".format(event.GetKeyCode())
if __name__ == "__main__":
app = DemoApp()
app.MainLoop()
The character function never gets called. However, when I comment out the two lines call to the Frame constructor, I character function is called. Adding a panel to the frame seems to interfere with the binding of the frame's EVT_CHAR.
How do I address this problem? Am I doing something wrong in my code?
The problem is that you are catching events that happen to the frame, but the frame is not in focus. The button is. In wxPython, events are sent to the widget in focus. If you add this to the end of your init, it works:
self.SetFocus()
However, if you change the focus to the button, then it will stop working again. See also:
wxpython capture keyboard events in a wx.Frame
http://www.blog.pythonlibrary.org/2009/08/29/wxpython-catching-key-and-char-events/
http://wxpython-users.1045709.n5.nabble.com/Catching-key-events-from-a-panel-and-follow-up-to-stacked-panels-td2360109.html
I appreciate that this question was answered 2 years ago but this issue catches us all out at some point or other.
It is the classic binding to wx.Event or wx.CommandEvent problem.
In this case simply changing the self.Bind(wx.EVT_CHAR, self.character) line to read button.Bind(wx.EVT_CHAR, self.character) will solve the problem, detailed above.
The issue of wx.Event - wx.CommandEvent is covered in full here:
http://wiki.wxpython.org/EventPropagation
and here:
http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind

Categories

Resources