'How can I show a wx.TipWindow multiple times?

I am trying to show a wx.TipWindow at the mouse location when the scroll wheel changes a RadioBox item (I want the newly selected item to show at the mouse).

Here is simply what I have:

wx.TipWindow(self, self.rdb.GetString(nxt)) <- Works perfectly. Self is an instance of wx.Frame

However, if the above line gets called more than once, (ie, I scroll more than once ~5 times) it constantly makes new windows on top of one another. Eventually, the program crashes with this error:

Traceback (most recent call last):
  File "C:\Users\Seth\anaconda3\envs\DLC-GPU\lib\site-packages\matplotlib\backends\backend_wx.py", line 989, in _onMouseWheel
    x = evt.GetX()
AttributeError: 'MouseEvent' object has no attribute 'GetX'

Can I not get this to work? I tried closing and destroying the TipWindow before showing the next one, but same error.

I have thought "why not make my own" but sheesh thats annoying lol.

Code block where wx.TipWindow is shown:

    def MouseWheel(self, event):
        assert isinstance(event, wx.MouseEvent)
        whlRot = event.GetWheelRotation()

        if whlRot < 0:
            nxt = self.rdb.GetSelection() + 1
            if nxt < self.rdb.GetCount():
                self.rdb.SetSelection(nxt) # Sets the next object in the RadioBox
                wx.TipWindow(self, self.rdb.GetString(nxt))

        if whlRot > 0:
            prv = self.rdb.GetSelection() - 1
            if prv >= 0:
                self.rdb.SetSelection(prv) # Sets the previous object in the RadioBox
                wx.TipWindow(self, self.rdb.GetString(prv))


Solution 1:[1]

As you say "...scroll wheel changes RadioBox item..." but that is not a native function of a radiobox, it's something that you have engineered via code that we cannot see.

I assume that it looks something like this:

import wx

class RadioBoxFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Radio Box Frame', size=(400, 400))
        
        panel = wx.Panel(self, -1, pos=(10,10), size=(300,300), style=wx.BORDER_RAISED)
        sampleList = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine']
        self.option=wx.RadioBox(panel, -1, "Select an option", pos=(10, 10), size=wx.DefaultSize, choices=sampleList, majorDimension=3, style=wx.RA_SPECIFY_COLS)
        self.Bind(wx.EVT_RADIOBOX, self.getOption)
        panel.Bind(wx.EVT_MOUSEWHEEL, self.getWheelOption)

        for i in range(len(sampleList)):
            self.option.SetItemToolTip(i, "Option "+sampleList[i]) 
        
        self.CreateStatusBar()
        self.SetStatusText("Status area")
        self.Show()
        
    def getWheelOption(self, event):
        assert isinstance(event, wx.MouseEvent)
        whlRot = event.GetWheelRotation()

        if whlRot < 0:
            nxt = self.option.GetSelection() + 1
        else:
            nxt = self.option.GetSelection() - 1
        if nxt > self.option.GetCount() - 1:
            nxt = 0
        if nxt < 0:
            nxt = self.option.GetCount() - 1
        self.option.SetSelection(nxt) # Sets the next object in the RadioBox
        txt = "Option " + self.option.GetString(nxt) + " Selected"
        tw = wx.TipWindow(self, txt)
        tw.SetBoundingRect(wx.Rect(1,1,1,1)) #restrict action required to cancel tipwindow
        self.SetStatusText(txt)
        
    def getOption(self, event):
        state1 = self.option.GetSelection()
        txt = self.option.GetString(state1)
        self.SetStatusText(txt+" is Selected")

if __name__ == '__main__':
    app = wx.App()
    RadioBoxFrame()
    app.MainLoop()

Which produces the following:

enter image description here

Play with this code a bit and see you you are still getting the same error, because using wxPython 4.1.1 on Linux, the code above doesn't give an error. You may need to look at your environment if the above code fails.

Solution 2:[2]

I noticed different TipWindow behavior on Windows between wxPython 4.1.0 and 4.1.1. In the 4.1.1 version, the TipWIndow is not be destroyed when moving out of a BoundingRect. Similar issues as described above but with the difference that I experience this only when upgrading to 4.1.1. See snippet below to reproduce this behavior + picture of effect in 4.1.1. Anybody had similar experience and know what the fix is?

import wx


class RadioBoxFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1)
        panel = wx.Panel(self, -1, pos=(10, 10))
        sampleList = ['Zero', 'One', 'Two']
        self.option = wx.RadioBox(panel, -1, "Select an option", pos=(10, 10),
                                  size=wx.DefaultSize, choices=sampleList,
                                  majorDimension=3)
        self.Bind(wx.EVT_RADIOBOX, self.getOption)
        panel.Bind(wx.EVT_MOUSEWHEEL, self.getWheelOption)

        self.Show()

    def getWheelOption(self, event):
        whlRot = event.GetWheelRotation()
        if whlRot < 0:
            nxt = self.option.GetSelection() + 1
        else:
            nxt = self.option.GetSelection() - 1

        if nxt > self.option.GetCount() - 1:
            nxt = 0
        if nxt < 0:
            nxt = self.option.GetCount() - 1

        self.option.SetSelection(nxt)
        txt = "Option " + self.option.GetString(nxt) + " Selected"
        tw = wx.TipWindow(self, txt)
        tw.SetBoundingRect(wx.Rect(1, 1, 1, 1))

    def getOption(self, event):
        state1 = self.option.GetSelection()


if __name__ == '__main__':
    app = wx.App()
    RadioBoxFrame()
    app.MainLoop()

multiple tip windows not closing

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rolf of Saxony
Solution 2 RiveN