wxpython: Why do I enter the binding function twice? - python

The code is only used to write a simple UI( there is only a textbox on the window), and bind the event wx.EVT_KEY_DOWN to the function OnKeyDown, but when I pressed the Esc key, the window will pop up Esc, Test, then another Esc, Test, finally it will exit after the four message box, why? I only define two message box int the wx.WXK_ESCAPE bindings.
# -*- coding: utf-8 -*-
import wx
class Command(wx.Frame):
def __init__(self, parent, title):
super(Command, self).__init__(parent, title=title,
size=(600, 500))
self.InitUI()
self.Centre()
self.Show()
def InitUI(self):
pnl = wx.Panel(self)
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
hbox = wx.BoxSizer(wx.HORIZONTAL)
self.__tc_command = wx.TextCtrl(pnl, style=wx.TE_MULTILINE)
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown)
hbox.Add(self.__tc_command, proportion=1, flag=wx.ALL|wx.EXPAND, border=15)
pnl.SetSizer(hbox)
def OnKeyDown(self, evt):
"""Enter to send data, Esc to exit."""
key = evt.GetKeyCode()
if key == wx.WXK_ESCAPE:
##################Only two MessageBox, but pop up four##################
wx.MessageBox("Esc")
wx.MessageBox("Test")
##################Only two MessageBox, but pop up four##################
self.Close()
if key == wx.WXK_RETURN:
wx.MessageBox("Enter")
evt.Skip()
if __name__ == '__main__':
app = wx.App(redirect=False)
Command(None, title='Command')
app.MainLoop()

You are calling self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyDown) twice in your code.

Related

What is the difference between parent.Bind and widget.Bind in wxPython

import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
btn = wx.Button(self, label="Press me")
btn.Bind(wx.EVT_BUTTON, self.on_button_press)
def on_button_press(self, event):
print("You pressed the button")
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="Hello wxPython")
panel = MyPanel(self)
self.Show()
if __name__ == "__main__":
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()
In the code above we used btn.Bind for binding wx.Button to wx.EVT_BUTTON. if instead, we use this way:
self.Bind(wx.EVT_BUTTON, self.on_button_press, btn)
The result will be the same as above. Now my question is the difference between self.Bind and btn.Bind.
Each widget has an Id.
Events when triggered, pass the Id of the triggering widget, in this case a button.
Binding an event to a function can be specific or generic i.e. a specific widget or any widget that fires that event type.
In short, in this case, the self.Bind binds any button event unless you specify a widget ID.
See: https://docs.wxpython.org/events_overview.html
Hopefully, the code below will help explain.
N.B. event.Skip() says don't stop at this event, see if there are more events to process.
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
btn1 = wx.Button(self, label="Press me 1", pos=(10,10))
btn2 = wx.Button(self, label="Press me 2", pos=(10,50))
Abtn = wx.Button(self, label="Press me", pos=(10,90))
# Bind btn1 to a specific callback routine
btn1.Bind(wx.EVT_BUTTON, self.on_button1_press)
# Bind btn2 to a specific callback routine specifying its Id
# Note the order of precedence in the callback routines
self.Bind(wx.EVT_BUTTON, self.on_button2_press, btn2)
# or identify the widget via its number
# self.Bind(wx.EVT_BUTTON, self.on_button2_press, id=btn2.GetId())
# Bind any button event to a callback routine
self.Bind(wx.EVT_BUTTON, self.on_a_button_press)
# button 1 pressed
def on_button1_press(self, event):
print("You pressed button 1")
event.Skip()
# button 2 pressed
def on_button2_press(self, event):
print("You pressed button 2")
event.Skip()
# Any button pressed
def on_a_button_press(self, event):
print("You pressed a button")
event.Skip()
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title="Hello wxPython")
panel = MyPanel(self)
self.Show()
if __name__ == "__main__":
app = wx.App(redirect=False)
frame = MyFrame()
app.MainLoop()

wxpython open second frame on mouse click no responding

env:python37,windows,wxpython
There's a main frame keep opening while app is running, now I'm trying to create a new frame on mouse right click, the new frame works good if I open it by button click, but if it is triggered by a mouse click event, it will hang and no responding. I'm thinking if there's something wrong with mouse listener, really appreciate if you have any idea.
here's the code detail:
import wx
import time
import win32api
from threading import Thread
class OtherFrame(wx.Frame):
"""
Class used for creating frames other than the main one
"""
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
panel.SetBackgroundColour('yellow')
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label='Create New Frame')
btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
self.frame_number = 1
thread = Thread(target=self.monitorMouse, name='monitorMouse')
thread.daemon = True
thread.start()
def monitorMouse(self):
state_left = win32api.GetKeyState(0x01) # Left button down = 0 or 1. Button up = -127 or -128
state_right = win32api.GetKeyState(0x02) # Right button down = 0 or 1. Button up = -127 or -128
while True:
a = win32api.GetKeyState(0x01)
b = win32api.GetKeyState(0x02)
if b != state_right: # Button state changed
state_right = b
if b < 0:
print('Right Button Pressed')
else:
print('Right Button Released')
self.on_new_frame(None)
time.sleep(0.001)
def on_new_frame(self, event):
title = 'SubFrame {}'.format(self.frame_number)
frame = OtherFrame(title=title)
self.frame_number += 1
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(800, 600))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
If are determined to monitor the mouse using a thread, then I think that you are going to have to look at wx.lib.newevent.NewEvent().
From within the thread you would use wx.PostEvent to send the mouse event back to the main program.
However, wxpython already has the mouse events available to you, to which you can bind directly e.g.
import wx
class OtherFrame(wx.Frame):
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
panel.SetBackgroundColour('yellow')
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label='Create New Frame')
btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
self.Bind(wx.EVT_LEFT_DOWN, self.Left)
self.Bind(wx.EVT_RIGHT_DOWN, self.Right)
self.Bind(wx.EVT_RIGHT_DCLICK, self.on_new_frame)
self.Bind(wx.EVT_CLOSE, self.Quit)
self.frame_number = 1
def Quit(self, event):
self.Destroy()
def Left(self, event):
print("Left Button")
event.Skip()
def Right(self, event):
print("Right Button")
def on_new_frame(self, event):
title = 'SubFrame {}'.format(self.frame_number)
frame = OtherFrame(parent=self,title=title)
self.frame_number += 1
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(800, 600))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
As an aside, note the line
frame = OtherFrame(parent=self,title=title)
sending the parent allows all outstanding OtherFrame's to close when the main frame is closed.

Stop a wx.Control from losing focus on arrow key press?

A pretty minimal example:
import wx
class Control(wx.Control):
def __init__(self, parent):
wx.Control.__init__(self, parent)
self.Bind(wx.EVT_CHAR, self.OnKey)
self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseClick)
def OnKey(self, event):
print("key pressed")
event.Skip()
def OnMouseClick(self, event):
self.SetFocus()
print("has focus")
event.Skip()
class Frame(wx.Frame):
def __init__(self, parent=None):
wx.Frame.__init__(self, parent)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)
radio = wx.RadioButton(panel, label="Radio button")
button = wx.Button(panel, label="Button")
control = Control(panel)
sizer.Add(radio, 0, wx.ALL, 5)
sizer.Add(button, 0, wx.ALL, 5)
sizer.Add(control, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.Show()
if __name__ == "__main__":
app = wx.App()
frame = Frame()
app.MainLoop()
By clicking on the control, sets the focus to control. However, even though binding key presses to a function OnKey, an arrow key press changes focus to another button/widget.
Is there a method similar to AcceptsFocusFromKeyboard(self):
Description: Can this window be given focus by keyboard navigation? if not, the only way to give it focus (provided it accepts it at all) is to click it.
Except, a method where my control doesn't lose focus from keyboard navigation?
Both of you event handlers are missing the call to:
event.Skip()
Those events are non-wxCommandEvents and in order for them to work properly, OS has to perform whatever needs to be performed. That's what Skip() is for. Otherwise the event will be eaten by the control.
Please check the documentation for that method, add it and see if that fixes the issue.
Also, out of curiosity, what do you expect to happen when the user presses the arrow key?
If you want the focus to stay when the arrow key is pressed, try to filter the event by event.GetKeyCode() (check the docs for the proper function name) and don't call event.Skip() in this case.
If that doesn't work try to catch wx.EVT_CHAR_HOOK event.
If even this doesn't work, override FilterEvent().
Also, the structure of you GUI is very weird. Try to get the RAD tool (wxGlade, wxSmith, etc), build it there and look at the result. Or check the demo folder in wxPython distribution (available as a different download) to see how to make the normal layout in wxPython.
It is also possible that by fixing the GUI you will fix the arrow key issue.
Try this code instead:
import wx
class Frame(wx.Frame):
def __init__(self, parent=None):
wx.Frame.__init__(self, parent)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)
radio = wx.RadioButton(panel, label="Radio button")
button = wx.Button(panel, label="Button")
self.Bind(wx.EVT_CHAR, self.OnChar)
radio.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
button.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseClick)
sizer.Add(radio, 0, wx.ALL, 5)
sizer.Add(button, 0, wx.ALL, 5)
sizer.Add(control, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.Show()
def OnKeyDown(self, event):
print("key pressed")
if not event.GetKeyPress() == wx.WXK_LEFT_ARROW: # Check the function name. I'm writing from memory
event.Skip()
def OnChar(self, event):
print("Character key pressed on the panel")
event.Skip()
def OnMouseClick(self, event):
self.SetFocus()
print("has focus")
event.Skip()
if __name__ == "__main__":
app = wx.App()
frame = Frame()
app.MainLoop()
You should get the idea. Call event.Skip() only if the key pressed is not an arrow key.

How to close wx.DirDialog programmatically?

I have wxpython app that open wx.DirDialog on button click.
dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
# Do some stuff
Since my application is multithreaded and uses wxTaskbaricon which allow user (on Win 7) to close application even when modal DirDialog is open, I want to close the DirDialog before closing main app. Somehow non of below method work:
dlg.Destroy()
dlg.Close(True)
It is my testing code.
I can test Destroy(), Close() and EndModal() on Modal and Non-Modal wx.DirDialog()
To close modal dialog I had to use Timer - because modal dialog is blocking access to the main window.
It can't close dialog only if I do
self.dlg = None
self.dlg.EndModal(wx.CANCEL) # or Destroy() or Close(True)
And one more thing - I use Linux Mint 15, Python 2.7.4, wxPython 2.8.12.1 :)
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import wx
import sys # to get python version
#----------------------------------------------------------------------
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(600,100))
self.panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.panel.SetSizer(self.sizer)
self.label = wx.StaticText(self.panel, label="Python "+sys.version+"\nwxPython"+wx.version())
self.button1 = wx.Button(self.panel, label="On")
self.button2 = wx.Button(self.panel, label="Off")
self.sizer.Add(self.label)
self.sizer.Add(self.button1)
self.sizer.Add(self.button2)
self.Bind(wx.EVT_BUTTON, self.OpenDialog, self.button1)
self.Bind(wx.EVT_BUTTON, self.CloseDialog, self.button2)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.TimerCloseDialog, self.timer)
self.Show(True)
self.dlg = None
def OpenDialog(self, event):
print "OpenDialog"
self.timer.Start(3000, oneShot=True)
print "wait 3s ..."
if not self.dlg:
self.dlg = wx.DirDialog(self)
self.dlg.ShowModal()
#self.dlg.Show(True)
def CloseDialog(self, event):
print "CloseDialog"
if self.dlg:
#self.dlg = None
#self.dlg.EndModal(wx.CANCEL)
self.dlg.Destroy()
#self.dlg.Close(True)
def TimerCloseDialog(self, event):
print "TimerCloseDialog"
if self.dlg:
#self.dlg = None
self.dlg.EndModal(wx.CANCEL)
#self.dlg.Destroy()
#self.dlg.Close(True)
#----------------------------------------------------------------------
print "Python", sys.version
print "wxPython", wx.version()
app = wx.App()
frame = MyFrame(None, "Hello Dialog")
app.MainLoop()

wxpython auinotebook close tab event

What event is used when I close a tab in an auinotebook? I tested with
EVT_AUINOTEBOOK_PAGE_CLOSE(D). It didn't work.
I would also like to fire a right click on the tab itself event.
Where can I find all the events that can be used with the aui manager/notebook? Might just be my poor searching skills, but I can't find any lists over the different events that exist, not for mouse/window events either. It would be really handy to have a complete list.
#!/usr/bin/python
#12_aui_notebook1.py
import wx
import wx.lib.inspection
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.nb = wx.aui.AuiNotebook(self)
self.new_panel('Page 1')
self.new_panel('Page 2')
self.new_panel('Page 3')
self.nb.Bind(wx.EVT_AUINOTEBOOK_PAGE_CLOSED, self.close)
def new_panel(self, nm):
pnl = wx.Panel(self)
pnl.identifierTag = nm
self.nb.AddPage(pnl, nm)
self.sizer = wx.BoxSizer()
self.sizer.Add(self.nb, 1, wx.EXPAND)
self.SetSizer(self.sizer)
def close(self, event):
print 'closed'
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, '12_aui_notebook1.py')
frame.Show()
self.SetTopWindow(frame)
return 1
if __name__ == "__main__":
app = MyApp(0)
# wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
Oerjan Pettersen
This is the bind command you want:
self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.close, self.nb)
To detect a right click on the tab (e.g. to show a custom context menu):
self.Bind(wx.aui.EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.right, self.nb)
Here's a list of the aui notebook events:
EVT_AUINOTEBOOK_PAGE_CLOSE
EVT_AUINOTEBOOK_PAGE_CLOSED
EVT_AUINOTEBOOK_PAGE_CHANGED
EVT_AUINOTEBOOK_PAGE_CHANGING
EVT_AUINOTEBOOK_BUTTON
EVT_AUINOTEBOOK_BEGIN_DRAG
EVT_AUINOTEBOOK_END_DRAG
EVT_AUINOTEBOOK_DRAG_MOTION
EVT_AUINOTEBOOK_ALLOW_DND
EVT_AUINOTEBOOK_DRAG_DONE
EVT_AUINOTEBOOK_BG_DCLICK
EVT_AUINOTEBOOK_TAB_MIDDLE_DOWN
EVT_AUINOTEBOOK_TAB_MIDDLE_UP
EVT_AUINOTEBOOK_TAB_RIGHT_DOWN
EVT_AUINOTEBOOK_TAB_RIGHT_UP
From: {python folder}/Lib/site-packages/{wxpython folder}/wx/aui.py

Categories

Resources