WX_ID question

Do you have questions about writing plugins or scripts in Python? Meet the coders here.
Post Reply
dan Edens
Posts: 42
Joined: Mon Sep 24, 2018 7:57 pm

WX_ID question

Post by dan Edens » Sun Jul 21, 2019 4:09 pm

I'm trying to add some options to the python script window, and in this question, focusing on the context menu.

How do i call my own function instead of having to put wx items? ex. wx.id_copy

I figured out how to do this with EG menu's by learning from the Global variable monitor plugin, have learned alot from that one.
2019-07-21 01_06_58-EventGhost WIP - general ai 0.3.0.png


I figured out that in wxPython context menus are handled differently than other menus in that events are only sent to the window itself and its top level parent but not any intermediate windows in the hierarchy.
But understanding the Why it's different doesn't mean i can apply it. lol
advice?

Thanks,
Dan

Code below is from:
PythonEditorCtrl.py - Lines 224-248

Code: Select all

menu = wx.Menu()
         text = eg.text.MainFrame.Menu

        def AddMenuItem(ident, menuId):
            self.Bind(wx.EVT_MENU, getattr(self, "OnCmd" + ident), id=menuId)
            return menu.Append(menuId, getattr(text, ident, ident))

        AddMenuItem("Undo", wx.ID_UNDO)
        AddMenuItem("Redo", wx.ID_REDO)
        menu.AppendSeparator()
        AddMenuItem("Cut", wx.ID_CUT)
        AddMenuItem("Copy", wx.ID_COPY)
        AddMenuItem("Paste", wx.ID_PASTE)
        AddMenuItem("Delete", wx.ID_DELETE)
        menu.AppendSeparator()

                                                                                    
        AddMenuItem("SelectAll", wx.ID_SELECTALL)
        self.popupMenu = menu

        self.Bind(wx.EVT_RIGHT_UP, self.OnRightClick)

        self.SetText(value)
        self.EmptyUndoBuffer()
        self.Bind(EVT_STC_SAVEPOINTLEFT, self.OnSavePointLeft)

dan Edens
Posts: 42
Joined: Mon Sep 24, 2018 7:57 pm

Re: WX_ID question

Post by dan Edens » Mon Jul 22, 2019 8:50 pm

THE ANSWER is wx.ID_ANY
if anyone else cares to create their own IDE from scratch inside EG... you know... for.... because.
2019-07-22 15_49_32-EventGhost WIP - general ai 0.3.0.png
2019-07-22 15_49_32-EventGhost WIP - general ai 0.3.0.png (11.57 KiB) Viewed 407 times

User avatar
kgschlosser
Site Admin
Posts: 4763
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: WX_ID question

Post by kgschlosser » Tue Jul 23, 2019 5:49 am

Any time you see a -1 being passed to a wx object chances are that the -1 is being passed for the id parameter. -1 is wx.ID_ANY

I am a big fan of using code to explain the uses.

so here is some code. I hope it explains a few things!

Code: Select all

import wx


app = wx.GetApp()

if app is None:
    app = wx.App()

else:
    app = None


# this is way number 1
class Frame_1(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, -1, size=(300, 200), title='Frame 1')
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.ctrl = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE | wx.TE_READONLY)
        sizer.Add(self.ctrl, 1, wx.ALL | wx.EXPAND, 10)
        self.SetSizer(sizer)

        # we are binding the context menu event to the text ctrl because it
        # occupies almost the whole frame.
        self.ctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_context_menu)

    def on_item_1(self, evt):
        self.ctrl.AppendText('item 1 clicked - ' + str(evt.GetId()) + '\n')
        evt.Skip()

    def on_item_2(self, evt):
        self.ctrl.AppendText('item 2 clicked - ' + str(evt.GetId()) + '\n')
        evt.Skip()

    def on_item_3(self, evt):
        self.ctrl.AppendText('item 3 clicked - ' + str(evt.GetId()) + '\n')
        evt.Skip()

    def on_context_menu(self, evt):
        self.ctrl.AppendText('Pay attention to the id\'s, They change every time the menu gets opened\n')
        menu = wx.Menu()
        item_1 = menu.Append(-1, item='Item 1', helpString='Item 1 Help', kind=wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.on_item_1, item_1)

        item_2 = menu.Append(-1, item='Item 2', helpString='Item 2 Help', kind=wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.on_item_2, item_2)

        item_3 = menu.Append(-1, item='Item 3', helpString='Item 3 Help', kind=wx.ITEM_NORMAL)
        self.Bind(wx.EVT_MENU, self.on_item_3, item_3)

        def on_close(evt):
            # we have to do this hack in order to unbind the menu items
            # because we have no way of identifying the items
            # The reason we have to use the wx.CalAfter is to delay the
            # unbinding because the context menu gets closed before firing the
            # event for an item being clicked. so we stall the unbinding so
            # the click event can propagate properly.
            def do():
                self.Unbind(wx.EVT_MENU, item_1, handler=self.on_item_1)
                self.Unbind(wx.EVT_MENU, item_2, handler=self.on_item_2)
                self.Unbind(wx.EVT_MENU, item_3, handler=self.on_item_3)

            wx.CallAfter(do)

            self.Unbind(wx.EVT_MENU_CLOSE, handler=on_close)

            evt.Skip()

        self.Bind(wx.EVT_MENU_CLOSE, on_close)

        self.PopupMenu(menu, evt.GetPosition())


frame1 = Frame_1()
frame1.Show()

# another way!

class Frame_2(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, -1, size=(300, 200), title='Frame 2')
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.ctrl = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE | wx.TE_READONLY)
        sizer.Add(self.ctrl, 1, wx.ALL | wx.EXPAND, 10)
        self.SetSizer(sizer)

        self.ctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_context_menu)
        # because we are keeping the id's for the menu items as a constant we
        # do not have to unbind each time the menu gets closed!

        self.Bind(wx.EVT_MENU, self.on_menu)

        self.item_1_id = wx.NewId()
        self.item_2_id = wx.NewId()
        self.item_3_id = wx.NewId()

    def on_menu(self, evt):
        id = evt.GetId()

        if id == self.item_1_id:
            self.ctrl.AppendText('item 1 clicked - ' + str(id) + '\n')
        elif id == self.item_2_id:
            self.ctrl.AppendText('item 2 clicked - ' + str(id) + '\n')
        elif id == self.item_3_id:
            self.ctrl.AppendText('item 3 clicked - ' + str(id) + '\n')

        evt.Skip()

    def on_context_menu(self, evt):
        self.ctrl.AppendText('Pay attention to the id\'s they remain the same each and every time you open the menu\n')
        menu = wx.Menu()
        menu.Append(self.item_1_id, item='Item 1', helpString='Item 1 Help', kind=wx.ITEM_NORMAL)
        menu.Append(self.item_2_id, item='Item 2', helpString='Item 2 Help', kind=wx.ITEM_NORMAL)
        menu.Append(self.item_3_id, item='Item 3', helpString='Item 3 Help', kind=wx.ITEM_NORMAL)

        self.PopupMenu(menu, evt.GetPosition())

frame2 = Frame_2()
frame2.Show()

# and yet another way!


class Frame_3(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, -1, size=(300, 200), title='Frame 3')
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.ctrl = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE | wx.TE_READONLY)
        sizer.Add(self.ctrl, 1, wx.ALL | wx.EXPAND, 10)
        self.SetSizer(sizer)

        self.Bind(wx.EVT_MENU, self.on_menu)
        self.ctrl.Bind(wx.EVT_CONTEXT_MENU, self.on_context_menu)

        self.item_1_id = wx.NewId()
        self.item_2_id = wx.NewId()
        self.item_3_id = wx.NewId()

        # here we are building the menu and holding on to it instead of
        # creating it each and every single time we open it.

        # this is handy if we disable items in other areas of code. It is
        # easier to disable it instead of keeping a reference as to whether or
        # not it is disabled
        self.menu = wx.Menu()
        self.menu.Append(self.item_1_id, item='Item 1', helpString='Item 1 Help', kind=wx.ITEM_NORMAL)
        self.menu.Append(self.item_2_id, item='Item 2', helpString='Item 2 Help', kind=wx.ITEM_NORMAL)
        self.menu.Append(self.item_3_id, item='Item 3', helpString='Item 3 Help', kind=wx.ITEM_NORMAL)

    def on_menu(self, evt):
        id = evt.GetId()

        if id == self.item_1_id:
            self.ctrl.AppendText('item 1 clicked - ' + str(id) + '\n')
        elif id == self.item_2_id:
            self.ctrl.AppendText('item 2 clicked - ' + str(id) + '\n')
        elif id == self.item_3_id:
            self.ctrl.AppendText('item 3 clicked - ' + str(id) + '\n')

        evt.Skip()

    def on_context_menu(self, evt):
        self.ctrl.AppendText('Pay attention to the id\'s they remain the same each and every time you open the menu\n')
        self.PopupMenu(self.menu, evt.GetPosition())


frame3 = Frame_3()
frame3.Show()

if app is not None:
    app.MainLoop()

If you like the work I have been doing then feel free to Image

User avatar
kgschlosser
Site Admin
Posts: 4763
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: WX_ID question

Post by kgschlosser » Tue Jul 23, 2019 6:01 am

Also.. almost all events will propagate down the tree.


frame - panel(parent=frame) - control(parent=panel)
control.Bind(SOME_EVENT, control_callback)
panel.Bind(SOME_EVENT, panel_callback)
frame.Bind(SOME_EVENT, frame_callback)

if control causes SOME_EVENT to take place all 3 callbacks will get called.


frame - control1(parent=frame), panel(parent=frame) - control2(parent=panel)

control1.Bind(SOME_EVENT, control1_callback)
control2.Bind(SOME_EVENT, control2_callback)
panel.Bind(SOME_EVENT, panel_callback)
frame.Bind(SOME_EVENT, frame_callback)

if control1 causes SOME_EVENT to take place control1_callback and frame_callback will get called.
if control2 causes SOME_EVENT to take place control2_callback, panel_callback and frame_callback will get called.

Now you can stop the event by using event.Veto() in the callback and if the event supports it.
I do not remember if not calling event.Skip() in the callback will stop propagation or if it just stops the event from making callbacks for the bound object.

in code above if you add evt.Skip() to the on_context_menu methods at the end of the method you will understand what I am talking about with the use of Skip()
If you like the work I have been doing then feel free to Image

dan Edens
Posts: 42
Joined: Mon Sep 24, 2018 7:57 pm

Re: WX_ID question

Post by dan Edens » Mon Jul 29, 2019 2:35 pm

kgschlosser wrote:
Tue Jul 23, 2019 6:01 am
Also.. almost all events will propagate down the tree.


frame - panel(parent=frame) - control(parent=panel)
control.Bind(SOME_EVENT, control_callback)
panel.Bind(SOME_EVENT, panel_callback)
frame.Bind(SOME_EVENT, frame_callback)

if control causes SOME_EVENT to take place all 3 callbacks will get called.


frame - control1(parent=frame), panel(parent=frame) - control2(parent=panel)

control1.Bind(SOME_EVENT, control1_callback)
control2.Bind(SOME_EVENT, control2_callback)
panel.Bind(SOME_EVENT, panel_callback)
frame.Bind(SOME_EVENT, frame_callback)

if control1 causes SOME_EVENT to take place control1_callback and frame_callback will get called.
if control2 causes SOME_EVENT to take place control2_callback, panel_callback and frame_callback will get called.

Now you can stop the event by using event.Veto() in the callback and if the event supports it.
I do not remember if not calling event.Skip() in the callback will stop propagation or if it just stops the event from making callbacks for the bound object.

in code above if you add evt.Skip() to the on_context_menu methods at the end of the method you will understand what I am talking about with the use of Skip()
I'm starting to understand this i think, question tho, is -1 universally wx.ID_ANY, or is that eg specific?
I think it's eg specific since I can't find anything about it in a search, but don't want to assume.

User avatar
kgschlosser
Site Admin
Posts: 4763
Joined: Fri Jun 05, 2015 5:43 am
Location: Rocky Mountains, Colorado USA

Re: WX_ID question

Post by kgschlosser » Mon Jul 29, 2019 7:03 pm

it is not EG specific it is a wxPython constant.


https://wxpython.org/Phoenix/docs/html/ ... fiers.html
If you like the work I have been doing then feel free to Image

Post Reply