0

I want bind mouse event on a word of TextCtrl when I append Text to . below is the code I write. I am not very good at English. Thanks for any help.

import wx

class TextFrame(wx.Frame):

def __init__(self):   
    wx.Frame.__init__(self, None, -1, 'Text Entry Example',   
            size=(300, 250))   
    panel = wx.Panel(self, -1)   
    richLabel = wx.StaticText(panel, -1, "Rich Text")   
    richText = wx.TextCtrl(panel, -1,   
            "If supported by the native control, this is reversed, and this is a different font.",   
            size=(200, 100), style=wx.TE_MULTILINE|wx.TE_RICH2)   
    richText.Bind(wx.EVT_RIGHT_DOWN, self.OnTextCtrl1LeftDown) 
    richText.SetInsertionPoint(0) 

    #? how can I bind mouse event like leftdown on Text below 
    #? how can I bind mouse event like leftdown on Text below 
    richText.SetStyle(44, 52, wx.TextAttr("white", "black"))   

    points = richText.GetFont().GetPointSize()   
    print points,type(points) 
    f = wx.Font(points + 10, wx.ROMAN, wx.ITALIC, wx.BOLD, True)   
    richText.SetStyle(68, 82, wx.TextAttr("blue", wx.NullColour, f))   

    sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)   
    sizer.AddMany([richLabel, richText])   
    panel.SetSizer(sizer) 
def OnTextCtrl1LeftDown(self,event): 
    print "clientwx,leftdown" 

2 Answers2

1

Inspired by this question and answer, I wrote a more generic word click program. It finds the word in the text box you click on:

import wx

sample_text = """Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
eprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
officia deserunt mollit anim id est laborum."""

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, 'Word Clicker')
        pnl = wx.Panel(self, wx.ID_ANY)
        lbl = wx.StaticText(pnl, wx.ID_ANY, "Source Text")
        self.richText = txt = wx.TextCtrl(
            pnl,
            wx.ID_ANY,
            sample_text,
            style=wx.TE_MULTILINE | wx.TE_RICH2 | wx.TE_READONLY,
        )
        self.found = fnd = wx.StaticText(pnl, wx.ID_ANY, "<result goes here>")
        txt.Bind(wx.EVT_LEFT_DOWN, self.OnGetWord)
        txt.SetInsertionPoint(0)
        txt.SetCursor(wx.Cursor(wx.CURSOR_HAND))
        szr = wx.BoxSizer(wx.VERTICAL)
        szr.AddMany([(lbl,0),(txt,1,wx.EXPAND), (fnd,0,wx.EXPAND)])
        pnl.SetSizerAndFit(szr)


    def OnGetWord(self, event):
        xy_pos = event.GetPosition()
        _, word_pos = self.richText.HitTestPos(xy_pos)
        search_text = self.richText.GetValue()
        left_pos = right_pos = word_pos
        if word_pos < len(search_text) and search_text[word_pos].isalnum():
            try:
                while left_pos >= 0 and search_text[left_pos].isalnum():
                    left_pos -= 1
                while right_pos <= len(search_text) and search_text[right_pos].isalnum():
                    right_pos += 1
                found_word = search_text[left_pos + 1 : right_pos]
                print(f"Found: '{found_word}'")
            except Exception as e:
                found_word = "<" + str(e) + ">"
        else:
            found_word = "<Not On Word>"
        self.found.SetLabel(found_word)

if __name__ == '__main__':
    app = wx.App()
    MyFrame().Show()
    app.MainLoop()
RufusVS
  • 4,008
  • 3
  • 29
  • 40
  • 1
    Nicely done. The only caveat I would add, is that you might consider adding `event.Skip()`. The reason: the skip allows you to select and deselect (highlight) text. Without the skip you can't. Probably not the end of the world as we know it. – Rolf of Saxony May 03 '21 at 07:29
  • @RolfofSaxony Thanks for the advice! I'll have to refresh my memory on `event.Skip()` – RufusVS May 03 '21 at 13:37
0

I don't believe that there is a way to determine which word that you clicked on or hovered over in a TextCtrl.
However, you might be able to do what you want by predefining the area that the word takes up and use the coordinates of the mouse position.
For example: you know that the word "reserved" takes up the area between, 44 and 52 because you assign a style to it, test for that in your OnTextCtrl1LeftDown function.
Get the mouse position and perform a HitTest using:

m_pos = event.GetPosition()  # position tuple
self.richText.HitTest(m_pos)
#now code here to test if the column and row positions are within your parameters#

HitTest finds the row and column of the character at the specified point.

Edit: Here's your code modified:
Note: that I have left the event as RIGHT click even though you specified LEFT click

import wx

class TextFrame(wx.Frame):

    def __init__(self):   
        wx.Frame.__init__(self, None, -1, 'Text Entry Example',   
                size=(300, 250))   
        self.panel = wx.Panel(self, -1)   
        self.richLabel = wx.StaticText(self.panel, -1, "Rich Text")   
        self.richText = wx.TextCtrl(self.panel, -1,   
                "If supported by the native control, this is reversed, and this is a different font.",   
                size=(200, 100), style=wx.TE_MULTILINE|wx.TE_RICH2)   
        self.richText.Bind(wx.EVT_RIGHT_DOWN, self.OnTextCtrl1LeftDown) 
        self.richText.SetInsertionPoint(0) 

        #? how can I bind mouse event like leftdown on Text below 
        #? how can I bind mouse event like leftdown on Text below 
        self.richText.SetStyle(44, 52, wx.TextAttr("white", "black"))   

        points = self.richText.GetFont().GetPointSize()   
        f = wx.Font(points + 10, wx.ROMAN, wx.ITALIC, wx.BOLD, True)   
        self.richText.SetStyle(68, 82, wx.TextAttr("blue", wx.NullColour, f))   

        sizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6)   
        sizer.AddMany([self.richLabel, self.richText])   
        self.panel.SetSizer(sizer) 


    def OnTextCtrl1LeftDown(self,event): 
        m_pos = event.GetPosition()  # position tuple
        word_pos = self.richText.HitTest(m_pos)
        if word_pos[0] == 0:
            if word_pos[1] > 43 and word_pos[1] < 53:
                print "You clicked on the word 'reserved'" 
            if word_pos[1] > 67 and word_pos[1] < 83:
            print "You clicked on the words 'Different Font'" 

if __name__ == '__main__':
    test = wx.App()
    TextFrame().Show()
    test.MainLoop()    
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
  • Thank you for your help! – cuizhaohua Mar 21 '16 at 00:36
  • The event handler has an error: it should be calling `self.richText.HitTestPos(m_pos)` rather than `self.richText.HitTest(m_pos)` and you should ignore the value in `word_pos[0]` (eliminate the first conditional) – RufusVS May 02 '21 at 18:11
  • @RufusVS Not sure of your platform but testing on Linux wx version 4.1.1 this code still works as is, not that it can't be improved. – Rolf of Saxony May 03 '21 at 07:25