How to detect paste (ctrl+v) in RichTextCtrl

Hi. I am using Fedora 32, wxPython 4.1.1 on Python3.8.7.

I can not seem to be able to figure out how to capture text paste (ctrl-v) in a RichTextCtrl.
The EVT_TEXT_PASTE is not fired by RichTextCtrl and I did not find any other similar event.
Other events are fired any time some text is written and not just when something is pasted.

I decided to try to use EVT_KEY_UP and the wx.KeyEvent because it allows detection of ctrl key being held down and also gives some key codes. I wanted to use it to detect when ctrl and ‘v’ key are pressed.
Unfortunately I do not understand how to use wx.KeyCode. The code given by the event.GetKeyCode() is the ascii character number of the key, but for example the wx.WXK_CONTROL_V is 22 while ‘v’ is 86 and ctrl is 308, I could not produce the 22 in any way even when pressed together.

Is there a better way of detecting text paste in RichTextCtrl?
What are the WXK codes and how to use them?

Here is some sample code to experiment with:
It prints the key code, ctrl key state and the value of wx.WXK_CONTROL_V on every key up.

import wx
import wx.richtext as rt


class RichTextFrame(wx.Frame):
    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)
        self.rtc = rt.RichTextCtrl(self, style=wx.VSCROLL | wx.HSCROLL | wx.NO_BORDER)
        self._main_sizer = wx.BoxSizer(wx.VERTICAL)
        self._controls_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.SetSizer(self._main_sizer)

        self._bold_button = wx.Button(self, -1, 'bold')
        self._controls_sizer.Add(self._bold_button)

        self._main_sizer.Add(self.rtc, 1, flag=wx.EXPAND)
        self._main_sizer.Add(self._controls_sizer)

        self.Bind(wx.EVT_BUTTON, self._change_bold, self._bold_button)
        self.Bind(wx.EVT_MENU, self._forward_event)

        self.rtc.Bind(wx.EVT_KEY_UP, self._up, self.rtc)
        self.rtc.AppendText('Sample text')

    def _up(self, event: wx.KeyEvent) -> None:
        """
        Handle key up events.
        :param event: Used for getting key info.
        :return: None
        """
        event.Skip()
        print('event key code: ', event.GetKeyCode())
        print('ctrl: ', event.ControlDown())
        print(wx.WXK_CONTROL_V)
        print(self.rtc.GetValue())

    def _change_bold(self, evt: wx.CommandEvent) -> None:
        """
        Make text bold and vice versa.
        :param evt: Not used
        :return: None
        """
        self.rtc.ApplyBoldToSelection()

    def _forward_event(self, evt):
        self.rtc.ProcessEvent(evt)


class MyApp(wx.App):
    """
    Main class for running the gui
    """

    def __init__(self):
        wx.App.__init__(self)
        self.frame = None

    def OnInit(self):
        # Required for copy paste to work and retain text attributes
        rt.RichTextBuffer.AddHandler(rt.RichTextXMLHandler())

        self.frame = RichTextFrame(None, -1, "RichTextCtrl", size=(400, 400), style=wx.DEFAULT_FRAME_STYLE)
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True


if __name__ == "__main__":
    app = MyApp()
    app.MainLoop()

Thanks

It doesn’t have a very correct name, but the key IDs like wx.WXK_CONTROL_V are really the ASCII value for the Ctrl-V character, not for the the key event. The ASCII values for the control characters are in the range 1…26.

I suggest playing with the KeyEvents sample in the demo to learn more about how the key and char events work. I suggest the following for catching key events that are not intended for adding text to a widget:

  1. Catch the EVT_KEY_DOWN instead of the _UP event. This way you can handle the event before the system tried to turn it into text events.

  2. Test for Ctrl-V something like this:

    if event.ControlDown() and event.GetKeyCode() == ord('V'):
        ...
    
  3. If the key is not one you are going to handle then call event.Skip()

Another possibility you may want to look into is the wx.AcceleratorTable, which can be used to turn hotkey-like events in to EVT_MENU events.

1 Like