pythoncontextmenuwxwidgetspopupmenuwx.textctrl

Popup menu at the insertion point position in a wx.TextCtrl


Does somebody knows how to create a custom context menu in a TextCtrl object using wxPython ?

For the moment, I could create a custom menu but this one appears at the mouse cursor position enter image description here, when the original one appears at the text cursor position enter image description herewhen hitting the [Menu] key enter image description here.


Solution

  • Okay,

    Could somehow do the trick but this works only

    1. when the font is monospaced (see the code below),
    2. when there is no tabulation character ("\t") in the focused line...

    Simply: getting the position of the text cursor enter image description here into row/column coordinates and multiplying them by the character size. This gives the desired popup menu position.

    import wx
    
    # Test context menu that should be displayed at the insertion point position 
    # in the TextCtrl object
    class CustomMenu(wx.Menu):
    
        def __init__(self):
            wx.Menu.__init__(self)
    
            # Add some items in the menu
            self.AppendItem(wx.MenuItem(self, wx.NewId(), 'This should be at the'))
            self.AppendItem(wx.MenuItem(self, wx.NewId(), 'insertion point'))
            self.AppendItem(wx.MenuItem(self, wx.NewId(), 'position...'))
    
    # Text area from which to open the custom context menu
    class TestTextCtrl(wx.TextCtrl):
    
        def __init__(self, parent):
            wx.TextCtrl.__init__(self, parent, style=wx.TE_MULTILINE)
    
            self.parent = parent
    
            # Set a monospaced font
            font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL)
            self.SetFont(font)
    
            # Get the character size (width and height)
            self.pixelSize = font.GetPixelSize()
    
            # Display some text
            self.SetValue("[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n"
                          "[Ctrl] key = custom popup menu \n"
                          "[Menu] key = original context menu \n")
    
            # Activate the [Ctrl] key stroke detection (1/2)
            self.Bind(wx.EVT_KEY_DOWN, self.OnKeyStroke)
    
        def OnKeyStroke(self, event):
    
            # Activate the [Ctrl] key stroke detection (2/2)
            if event.GetUnicodeKey() == wx.WXK_CONTROL:
    
                #######################################
                ##### Here is the interesting code ####
                #######################################
    
                # Get the scroll position in pixels
                scrollPosition = [
                    self.GetScrollPos(wx.HORIZONTAL),
                    self.GetScrollPos(wx.VERTICAL),
                    ]
    
                # Get the text cursor position in the text area (int)
                insertionPointPosition = self.GetInsertionPoint()
    
                # Convert it into a row/column position (2D tuple)
                insertionPointRowColumnPosition = self.PositionToXY(insertionPointPosition)
    
                # Calculate the popup menu position in pixels
                insertionPointPositionInPixels = []
                for i in range(2):
                    insertionPointPositionInPixels.append(
                #       (  row/column position    +   offset   ) * size of character - position of scroll in pixels)
                        (insertionPointRowColumnPosition[i] + i) * self.pixelSize[i] - scrollPosition[i]
                    )
    
                # Convert the position into a wx.Point object
                popupMenuPoint = wx.Point(
                    insertionPointPositionInPixels[0],
                    insertionPointPositionInPixels[1]
                )
    
                # Display the context menu at the given position
                self.PopupMenu(CustomMenu(), popupMenuPoint)
    
                ### We did it ! :) ###
    
                #######################################
                #######################################
                #######################################
    
            else:
                event.Skip()
    
    # Test frame
    class TestFrame(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None)
    
            # Create a text area
            TestTextCtrl(self)
    
            # Display the window
            self.Show(True)
    
    
    if __name__ == "__main__":
    
        # Create an application
        application = wx.App(False)
    
        # Create a frame
        TestFrame()
    
        # Launch the application
        application.MainLoop()
    

    Update #1 -> Size of characters taken from font.GetPixelSize()

    Update #2 -> Position of scrolling bar taken under account using self.GetScrollPos()