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 , when the original one appears at the text cursor position when hitting the [Menu] key .
Okay,
Could somehow do the trick but this works only
Simply: getting the position of the text cursor 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()