PyQt 4 / Qt 4 move QGraphicsItem with mouse using custom event handler - event-handling

I am developing an application using pyqt 4 so hopefully I am only making a rookie mistake.
I found I cannot move a QGraphicsItems and QGraphicsItemGroups in a scene once I use custom event handlers for mousePressEvent() and mouseMoveEvent().
My application permits me to draw arbitrary shapes however I press CTRL key modifier to set my event handlers so that I can select objects (and add them to QGraphicsItemGroup) to move all selected objects.
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = View(self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
self.setMouseTracking(True)
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.activeScene=QtGui.QGraphicsScene(self)
self.setScene(self.activeScene)
self.setInteractive(True)
self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
self.startpos = None
self.endpos = None
self.multiSegmentLine = QtGui.QGraphicsItemGroup(scene=self.activeScene)
self.multiSegmentLine.setFlags(QtGui.QGraphicsItemGroup.ItemIsSelectable)
self.linePen=QPen(Qt.green, 3, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
self.linesegment=QtGui.QGraphicsLineItem
# Create a group of selected items to move as a group
self.createMoveGroup = QtGui.QGraphicsItemGroup(scene=self.activeScene)
self.createMoveGroup.setFlags(QtGui.QGraphicsItemGroup.ItemIsMovable)
# TESTING
self.boxxy = self.activeScene.addRect(10,10,100,100,self.linePen)
self.boxxy.setFlags(QtGui.QGraphicsItem.ItemIsMovable)
# Booleans for state machine
self.stateNewLine=True # Flag to determine line state True = new line, False = continuation of multisegment line
self.stateSelectItems=False # Flag to help determine True = item selection mode enabled, False = line drawing mode enabled
# Create event handler
def mousePressEvent(self, event):
if event.button()==QtCore.Qt.LeftButton:
# If CTRL key is not pressed then self.stateSelectItems is FALSE and
# mouse handling is normal
if self.stateSelectItems == False:
# New line has been started so no end point has been determined
if self.stateNewLine == True:
# Record starting position if creating a new line. This is used as
# origin for line drawn from start position to mouse tip.
self.startpos = QtCore.QPointF(self.mapToScene(event.pos()))
# The line is not new but rather is a multiple segment line.
elif self.stateNewLine == False:
# Update the length of the run
UserLineList.updateRunLength(self.linesegment.line())
self.startpos = self.endpos # Start new line at endpoint of previous segment
self.segmentCount = self.segmentCount+1 # increment segment count
#self.multiSegmentLine.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
# Add line from starting position to location of mouse click
#self.linesegment=self.scene().addLine(QtCore.QLineF(self.startpos,self.endpos))
self.linesegment = self.activeScene.addLine(QtCore.QLineF(self.startpos,self.endpos))
self.linesegment.setPen(self.linePen)
# Add newly created line to item group
self.multiSegmentLine.addToGroup(self.linesegment)
self.activeScene.addItem(self.multiSegmentLine)
self.activeScene.removeItem(self.linesegment)
# If CTRL key is pressed thenself.stateSelectItems == True and mouse
# handling has to change to support moving one or more screen items
elif self.stateSelectItems==True:
print("ENTERED MOVEMENT MODE!")
currentItem=self.activeScene.itemAt(self.mapToScene(event.pos()))
if currentItem != None:
self.createMoveGroup.addToGroup(currentItem)
self.stateSelectItems = True # set stateSelectItems flag to control mouse event handling
def mouseDoubleClickEvent(self,event):
#def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startpos=None
self.stateNewLine=True # multisegment line has ended
# Create a record of the multisegment line
UserLineList.addMultiSegmentLine(self.multiSegmentLine)
def mouseMoveEvent(self,event):
# If CTRL key is not pressed then self.stateSelectItems is FALSE and
# mouse handling is normal
if self.stateSelectItems == False:
# If this is a new line use start position at click
if self.stateNewLine == True:
self.endpos = QtCore.QPointF(self.mapToScene(event.pos()))
self.linesegment = self.activeScene.addLine(QtCore.QLineF(self.startpos,self.endpos))
self.linesegment.setPen(self.linePen)
self.stateNewLine = False
# If this line builds upon an existing segment
elif self.stateNewLine == False:
self.endpos = QtCore.QPointF(self.mapToScene(event.pos()))
self.scene().removeItem(self.linesegment)
self.linesegment = self.activeScene.addLine(QtCore.QLineF(self.startpos,self.endpos))
self.linesegment.setPen(self.linePen)
# If CTRL key is pressed thenself.stateSelectItems == True and mouse
# handling has to change to support moving one or more screen items
elif self.stateSelectItems == True:
self.createMoveGroup.mouseMoveEvent(self, event)
def keyPressEvent(self,event):
keyModifier = QtGui.QApplication.keyboardModifiers() #Listen for keyboard modifier keys
if keyModifier == QtCore.Qt.ControlModifier:
print("keyEvent Control Key is pressed")
self.stateSelectItems=True
def keyReleaseEvent(self,event):
keyModifier = QtGui.QApplication.keyboardModifiers() #Listen for keyboard modifier keys
if keyModifier == QtCore.Qt.ControlModifier:
self.stateSelectItems=False
self.createMoveGroup.ungrabMouse() # Selected items lose mouse grab
self.activeScene.destroyItemGroup(self.createMoveGroup) # Remove all selected items from move group
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(640, 480)
window.show()
sys.exit(app.exec_())

Like in the answer to this question, you might want to do a call to the base-class in order to keep the existing event handling of the respective event. E.g. in the mouse press event handler you would add a line like:
def mousePressEvent(self, event):
...
super(Window, self).mousePressEvent(event)
...
Do this in a similar way for all your custom event handlers.

Related

Adding checkable combobox in QGIS plugin builder plugin

I am developing a QGIS plugin in python and hit a roadblock when displaying my GUI. I am using the plugin builder framework to develop my plugin and I have trouble displaying a checkable combo box in a scrollArea in my GUI. The code with core functionality is as follows.
def run(self):
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
if self.first_start == True:
self.first_start = False
# Sets up the pyqt user interface
self.dlg = EarthingToolDialog()
# Fetching the active layer in the QGIS project
layer = self.iface.activeLayer()
checkable_combo = CheckableComboBox()
# Going through each field of the layer
# and adding field names as items to the
# combo box
for j,field in enumerate(layer.fields()):
checkable_combo.addItem(str(field.name()))
# Setting the checked state to True by default
checkable_combo.setItemChecked(j, True)
# putting the check box inside the scroll area of the GUI
self.dlg.scrollArea.setWidget(checkable_combo)
self.dlg.scrollArea.setMinimumSize(QSize(700,400))
# show the dialog
self.dlg.show()
# Run the dialog event loop
self.dlg.exec_()
class EarthingToolDialog(QtWidgets.QDialog, FORM_CLASS):
def __init__(self, parent=None):
"""Constructor."""
super(EarthingToolDialog, self).__init__(parent)
self.setupUi(self)
class CheckableComboBox(QComboBox):
def __init__(self):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
def setItemChecked(self, index, checked=False):
print('checked')
item = self.model().item(index, self.modelColumn()) # QStandardItem object
print(type(item))
if checked:
item.setCheckState(Qt.CheckState.Checked)
else:
item.setCheckState(Qt.CheckState.Unchecked)
def handleItemPressed(self, index):
print('pressed')
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
else:
item.setCheckState(Qt.Checked)
self._changed = True
print('set ' + str(item.checkState()))
def hidePopup(self):
print('hide')
if not self._changed:
super().hidePopup()
self._changed = False
def itemChecked(self, index):
print('read')
item = self.model().item(index, self.modelColumn())
return item.checkState() == Qt.Checked
In summary, the run function is the main function called by the plugin when it is loaded. self.dlg is the instance of the actual pyqt python user interface. This is rendered with the help of the EarthingToolDialog class. The checkable combo box and it's functionalities are self contained in the CheckableComboBox class.
The run function executes without any error when the plugin is loaded but the checkboxes are not visible in the combobox. Just a normal combo box with a list of items (just the standard dropdown combo box) is seen on the GUI's scroll area and not the desired checkable combo box. The CheckableComboBox class was taken from https://morioh.com/p/d1e70112347c and it runs perfectly well in the demo code shown there.
I understand that this is a very specific question and it would be great if someone could figure out what the problem might be. Thanks in advance!
Within the run function, this piece of codes didn't work for me:
self.dlg.scrollArea.setWidget(checkable_combo)
self.dlg.scrollArea.setMinimumSize(QSize(700,400))
So instead, I use:
layout = QVBoxLayout()
layout.addWidget(checkable_combo)
self.dlg.setLayout(layout)
I didn't use directly this class (It was generated automatically since I use Plugin Builder, so in here I commented it):
class EarthingToolDialog(QtWidgets.QDialog, FORM_CLASS):
def __init__(self, parent=None):
"""Constructor."""
super(EarthingToolDialog, self).__init__(parent)
self.setupUi(self)
Now, in order to display checkable combo box, CheckableComboBox constructor is changed as :
def __init__(self):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
delegate = QtWidgets.QStyledItemDelegate(self.view())
self.view().setItemDelegate(delegate)
The last two lines are from the answer listed here.
Codes display checkable combo box list with all items checked by default.

PySimpleGui button stuck on Mac

I have a script that runs fine on dell, but seems to work only 70% on mac.
It basically let's the user select different parameters (dates, files, from lists etc), on a while loop until the user presses one of the create buttons that closes the loop.
I removed some parameters for security, but this is the base of the code. The while loop doesn't seem to work, the 3 create buttons don't work, the datepicker doesn't work.
These is my first question, please be kind :)
sg.theme("LightBlue3")
func = ['get_positions_by_account', 'get_positions_by_platform', 'get_positions_by_platform_and_account']
select_all = sg.Listbox(POSITIONS_COLS, select_mode='multiple', key="-IN7-", size=(30,12))
layout = [
[sg.Text("Select account and press go or press PASS to move to select platform:")],
[sg.Combo(ALL_ACCOUNT, key="-IN1-",change_submits=True), sg.Button("GO"), sg.Button("PASS")],
[sg.Text("Select platform:"), sg.Combo([], key="-IN2-",change_submits=True)],
[sg.Text("select run date or date of local file:"), sg.Input(size=(20, 1), key="-IN5-", change_submits=True),
sg.CalendarButton('select', target="-IN5-", key="-IN5-", format='%Y%m%d', default_date_m_d_y=(11, None, 2021))],
[sg.Text("select minimal purchase date:"), sg.Input(size=(20, 1), key="-IN6-", change_submits=True),
sg.CalendarButton('select', target="-IN6-", key="-IN6-", format='%Y-%m-%d', default_date_m_d_y=(11, None, 2021))],
[sg.Text("If local file select file: "), sg.InputText(key="-IN3-", change_submits=True), sg.FileBrowse(key="-IN13-")],
[sg.Text("Select function to run:"), sg.Combo(func, key="-IN4-",change_submits=True)],
[sg.Text('Select position columns:'), sg.Button("Select All"), sg.Button("Top 23"), sg.Button("Clear All")],
[select_all],
[sg.Button("Create position file"), sg.Button("Create transfer file"), sg.Button("Create Talya file"),sg.Button("Exit")]]
window = sg.Window('PIZZA SLICER - Maya Moshe 2021', layout, size=(600, 500))
if __name__ == '__main__':
account, platform, func1, date, pos_file, position, purchase_d = "", "", "", "", "", "", ""
while True:
event, values = window.read()
account = values["-IN1-"]
platform = values["-IN2-"]
pos_file = values["-IN3-"]
func1 = values["-IN4-"]
date = values["-IN5-"]
purchase_d = values["-IN6-"]
pos_cols = values["-IN7-"]
if event == sg.WIN_CLOSED or event == "Exit":
print('Exiting program')
exit()
elif event == "GO":
window["-IN2-"].update(value='', values=ACCOUNTS_PLATFORMS[account])
platform = values["-IN2-"]
elif event == "PASS":
window["-IN2-"].update(value='', values=ALL_PLATFORMS)
platform = values["-IN2-"]
elif event == 'Select All':
cols = POSITIONS_COLS
window.Element('-IN7-').SetValue(cols)
elif event == 'Top 23':
cols = top20
window.Element('-IN7-').SetValue(cols)
elif event == "Clear All":
window.Element('-IN7-').set_value([])
elif event == 'Create position file':
window.close()
now = datetime.datetime.now().replace(microsecond=0)
if pos_file:
position = pd.read_csv(pos_file, dayfirst=True)
eval(func1 + "(date=date, account=account, platform=platform, position=position, purchase_d=purchase_d, cols=pos_cols)")
diff = datetime.datetime.now().replace(microsecond=0) - now
print(f'pos filter, run time {diff}')
exit()
elif event == "Create transfer file":
window.close()
now = datetime.datetime.now().replace(microsecond=0)
if pos_file:
position = pd.read_csv(pos_file, dayfirst=True)
get_positions_by_account(date=date, account=account, platform=platform, position=position, purchase_d=purchase_d, cols=pos_cols, transfer_flag=True)
diff = datetime.datetime.now().replace(microsecond=0) - now
print(f'Create transfer file, run time {diff}')
exit()
break
elif event == "Create Talya file":
window.close()
now = datetime.datetime.now().replace(microsecond=0)
if pos_file:
position = pd.read_csv(pos_file, dayfirst=True)
get_positions_by_account(date=date, account=account, platform=platform, position=position, purchase_d=purchase_d, cols=pos_cols, talya_flag=True)
diff = datetime.datetime.now().replace(microsecond=0) - now
print(f'Create Talya file, run time {diff}')
exit()
Example Code here to show both of buttons Exit and Select All work. Nothing wrong found, maybe wrong code missed in your demo code.
import PySimpleGUI as sg
lst1 = list("ABCDEFGHIJ")
layout = [
[sg.Button('Select All')],
[sg.Listbox(lst1, size=(5, 10), select_mode='multiple', key='-IN7-')],
]
window = sg.Window("test", layout, finalize=True)
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, 'Exit'):
print('Exiting program')
break
# Get value of elements should be here to prevent value of `values` is None when sg.WINDOW_CLOSED
pos_cols = values["-IN7-"]
if event == 'Select All':
window['-IN7-'].set_value(lst1)
window.close()

In MS Access VBA determine the next control to have focus

Understanding the order of operations, is there a way for an OnExit or OnLostFocus script to know what control was clicked to take that focus?
I have a form that has a list box from which you have to select an item for the the rest of the data entered into the form to be saved, so that control has the focus when you open the form. If you click on any other control without selecting an item from the list, an OnExit gives you a warning that you haven't made a selection.
The problem that creates is, should you open the form and then decide you don't want it, you get that message when you close the form (either with the close button on the form or the built-in closer).
I would like to be able to tell the OnExit that either the close button has been clicked or the form itself is closing, so don't show the no entries message.
Any thoughts would be appreciated.
Not that I know of.
If your form is rather simple(not to many controls.) Move the warning out of the onExit to a sub. Call it from the other controls when they get focus. But not from the Close button.
Dim bDidUserSelectSomething As Boolean
'In the list box
Private sub ListBox1_Click()
'Do some code here
'Set the flag that they did something
bDidUserSelectSomething = True
End sub
'When you enter the other controls, check if they have done what they should have.
Private Sub TextBox1_Enter()
If bDidUserSelectSomething = False Then
'Warning
End If
End Sub
Consider altering your warning check.
Instead of an OnExit() Event of the listbox, add a validation in all other pertinent controls' BeforeUpdate() events. Usually this event is where validation checks are made.
First, build a general function behind the form that calls your warning:
Public Function WarningMessage() As Boolean
If IsNull(Me.Listbox) Then
Msgbox "Please first select an option in listbox.", vbExclamation, _
"MISSING LISTBOX SELECTION"
WarningMessage = True
Else
WarningMessage = False
End If
End Function
Then call the function to each control:
Private Sub cntr1_BeforeUpdate(Cancel As Integer)
Cancel = WarningMessage
End Sub
Private Sub cntr2_BeforeUpdate(Cancel As Integer)
Cancel = WarningMessage
End Sub
...
As you can see from above, if the listbox is null then the message appears and returns True which is passed back to the control's BeforeUpdate() subroutine to cancel the update.
Now, if there are too many controls to make this doable. Consider hiding all relevant controls in Form's OnCurrent() event except the listbox. When listbox is updated, render the other controls visible. Many softwares and web applications run dynamically like this to prevent users from missing important fields.
Private Sub Form_Current()
If IsNull(Me.listbox) Then
Me.ctnrl1.Visible = False
Me.ctnrl2.Visible = False
Me.ctnrl3.Visible = False
...
Else
Me.ctnrl1.Visible = True
Me.ctnrl2.Visible = True
Me.ctnrl3.Visible = True
...
End if
End Sub
Private Sub listbox_AfterUpdate()
Me.ctnrl1.Visible = True
Me.ctnrl2.Visible = True
Me.ctnrl3.Visible = True
...
End Sub

Pygtk: Remove a widget from a container and reuse it later

I'm using a custom container where I need to reorder widgets but there are no methods to do it. So I tried to remove all the widgets and add them again in order.
The problem is that this is not working well, I can't see the widgets after adding them again, I guess what's happening is that when I remove the widgets they become unrealized.
Is there any way of removing a widget an reuse it later?
The pygtk docs provide a bit of insight.
Note that the container will own a
reference to widget, and that this may
be the last reference held; so
removing a widget from its container
can cause that widget to be destroyed.
If you want to use widget again, you
should add a reference to it.
EDITS
I just quickly modified pygtk's Hello World to add/remove/reorder widgets in a container. This works because the button1 is a member variable of the class, it never goes out of scope.
#!/usr/bin/env python
# example helloworld2.py
import pygtk
pygtk.require('2.0')
import gtk
class HelloWorld2:
# Our new improved callback. The data passed to this method
# is printed to stdout.
def callback_remove(self, widget, data):
self.box1.remove(self.button1);
def callback_add(self, widget, data):
self.box1.pack_start(self.button1, True, True, 0)
# another callback
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return False
def __init__(self):
# Create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
# This is a new call, which just sets the title of our
# new window to "Hello Buttons!"
self.window.set_title("Hello Buttons!")
# Here we just set a handler for delete_event that immediately
# exits GTK.
self.window.connect("delete_event", self.delete_event)
# Sets the border width of the window.
self.window.set_border_width(10)
# We create a box to pack widgets into. This is described in detail
# in the "packing" section. The box is not really visible, it
# is just used as a tool to arrange widgets.
self.box1 = gtk.HBox(False, 0)
# Put the box into the main window.
self.window.add(self.box1)
# Creates a new button with the label "Button 1".
self.button1 = gtk.Button("Button 1")
# Now when the button is clicked, we call the "callback" method
# with a pointer to "button 1" as its argument
self.button1.connect("clicked", self.callback_remove, "button 1")
# Instead of add(), we pack this button into the invisible
# box, which has been packed into the window.
self.box1.pack_start(self.button1, True, True, 0)
# Always remember this step, this tells GTK that our preparation for
# this button is complete, and it can now be displayed.
self.button1.show()
# Do these same steps again to create a second button
self.button2 = gtk.Button("Button 2")
# Call the same callback method with a different argument,
# passing a pointer to "button 2" instead.
self.button2.connect("clicked", self.callback_add, "button 2")
self.box1.pack_start(self.button2, True, True, 0)
# The order in which we show the buttons is not really important, but I
# recommend showing the window last, so it all pops up at once.
self.button2.show()
self.box1.show()
self.window.show()
def main():
gtk.main()
if __name__ == "__main__":
hello = HelloWorld2()
main()
Just set the widget's visibility property to False, and set it to True later, with the set_visible method.
no he need only widgrt.its objects not need.then visible must not used.it will only hide graphicaly.but memmory still not released
remove function is the answer

Receiving keystrokes in an opened wx.ComboCtrl

Coming from this question, I have a wxComboCtrl with a custom popup made of a panel with a bunch of radiobuttons.. My problem is that when I open the popup the combo doesn't get keystrokes, because the events get handled by the panel itself.. I'd like to redirect those KeyEvents to the textctrl of the combo, but I can't find a way to get it to work :/
Am I going the wrong way? Should I manually handle the textctrl value as the user presses keys? I think that would be a bit cumbersome though.. Since supposedly the textctrl already knows how to handle those events..
Here's my testcase (wxPython 2.8 on Linux), the "on_key" method should be the culprit:
import wx
import wx.combo
class CustomPopup(wx.combo.ComboPopup):
def Create(self, parent):
# Create the popup with a bunch of radiobuttons
self.panel = wx.Panel(parent)
sizer = wx.GridSizer(cols=2)
for x in range(10):
r = wx.RadioButton(self.panel, label="Element "+str(x))
r.Bind(wx.EVT_RADIOBUTTON, self.on_selection)
sizer.Add(r)
self.panel.SetSizer(sizer)
# Handle keyevents
self.panel.Bind(wx.EVT_KEY_UP, self.on_key)
def GetControl(self):
return self.panel
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.Size(200, 150)
def on_key(self, evt):
if evt.GetEventObject() is self.panel:
# Trying to redirect the key event to the combo.. But this always returns false :(
print self.GetCombo().GetTextCtrl().GetEventHandler().ProcessEvent(evt)
evt.Skip()
def on_selection(self, evt):
self.Dismiss()
wx.MessageBox("Selection made")
class CustomFrame(wx.Frame):
def __init__(self):
# Toolbar-shaped frame with a ComboCtrl
wx.Frame.__init__(self, None, -1, "Test", size=(800,50))
combo = wx.combo.ComboCtrl(self)
popup = CustomPopup()
combo.SetPopupControl(popup)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(combo, 0)
self.SetSizer(sizer)
self.Layout()
if __name__ == '__main__':
app = wx.PySimpleApp()
CustomFrame().Show()
app.MainLoop()
Edit:
I found these (unresolved) discussions on the same topic..
"ComboCtrl loses keyboard focus when ComboPopup is shown "
"issue using wx.ComboCtrl"
I had the same problem with a custom ComboPopup control using a panel as the basis of the control.
EmulateKeypress just entered a never-ending loop. I found binding the panel to the EVT_KEY_DOWN and EVT_CHAR to the overridden OnComboKeyEvent function and skipping the event down the event handling chain was all that was required to enable entering text into the textbox of the ComboCtrl with the popup open. I think this makes sense. As the Popup isn't strictly a control its events won't belong anywhere until bound into the the wx.app event loop through the Bind() method.
I've included the overridden OnComboKeyEvent function below so that anyone else can explore this problem.
def create(self, *args, **kwds):
...
self.panel.Bind(wx.EVT_CHAR, self.OnComboKeyEvent)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnComboKeyEvent)
...
def OnComboKeyEvent(self, evt):
evt_dict = {wx.EVT_TEXT.typeId: "TextEvent",
wx.EVT_CHAR.typeId:"CharEvent",
wx.EVT_CHAR_HOOK.typeId:"CharHookEvent",
wx.EVT_KEY_UP.typeId:"KeyUpEvent",
wx.EVT_KEY_DOWN.typeId:"KeyDownEvent"}
if evt.GetEventType() in evt_dict:
thelogger.debug("oncombokeyevet:%s" % evt_dict[evt.GetEventType()])
super().OnComboKeyEvent(evt)
evt.Skip()
I've got an answer from Robin Dunn himself on wxpython-users:
Sending low-level events to native
widgets like this does not work the
way that most people expect it to.
You are expecting this to result in
the character being added to the text
ctrl,but it can't do that because all
that ProcessEvent does is send the
wx.Event to any bound event handlers
and then it stops. It does not go to
the next step and convert that event
to the equivalent native message and
send it on to the native widget. See
the "Simulating keyboard events"
thread.
It doesn't work especially well on
non-Windows platforms, but you could
try calling EmulateKeypress instead.