New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnboundLocalError: local variable 'theMenu' referenced before assignment #325

Closed
mpmc opened this Issue Dec 21, 2017 · 6 comments

Comments

Projects
None yet
2 participants
@mpmc
Copy link
Contributor

mpmc commented Dec 21, 2017

Traceback (most recent call last):
  File "gui.py", line 169, in <module>
    sys.exit(main(sys.argv))
  File "gui.py", line 162, in main
    app = Gui()
  File "gui.py", line 43, in __init__
    self.createRightClickMenu(None, showInBar=False)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8534, in createRightClickMenu
    self.addMenuSeparator(title)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8721, in addMenuSeparator
    self.addMenuItem(menu, "-")
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8601, in addMenuItem
    theMenu.add_separator()
UnboundLocalError: local variable 'theMenu' referenced before assignment

and

Exception in Tkinter callback
Traceback (most recent call last):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13137, in get
    return self.group(widgetType, group)[widgetName]
KeyError: 'EDIT'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 2468, in __rightClick
    if self.__checkCopyAndPaste(event):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8665, in __checkCopyAndPaste
    self.disableMenu("EDIT", 10)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8791, in disableMenu
    self.__changeMenuState(title, DISABLED, limit)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8797, in __changeMenuState
    theMenu = self.widgetManager.get(self.Widgets.Menu, title)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13139, in get
    raise ItemLookupError("Invalid widgetName: " + widgetName)
appJar.appjar.ItemLookupError: Invalid widgetName: EDIT
Exception in Tkinter callback
Traceback (most recent call last):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13137, in get
    return self.group(widgetType, group)[widgetName]
KeyError: 'EDIT'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8665, in __checkCopyAndPaste
    self.disableMenu("EDIT", 10)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8791, in disableMenu
    self.__changeMenuState(title, DISABLED, limit)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8797, in __changeMenuState
    theMenu = self.widgetManager.get(self.Widgets.Menu, title)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13139, in get
    raise ItemLookupError("Invalid widgetName: " + widgetName)
appJar.appjar.ItemLookupError: Invalid widgetName: EDIT
Exception in Tkinter callback
Traceback (most recent call last):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13137, in get
    return self.group(widgetType, group)[widgetName]
KeyError: 'EDIT'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8665, in __checkCopyAndPaste
    self.disableMenu("EDIT", 10)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8791, in disableMenu
    self.__changeMenuState(title, DISABLED, limit)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8797, in __changeMenuState
    theMenu = self.widgetManager.get(self.Widgets.Menu, title)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13139, in get
    raise ItemLookupError("Invalid widgetName: " + widgetName)
appJar.appjar.ItemLookupError: Invalid widgetName: EDIT
Exception in Tkinter callback
Traceback (most recent call last):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13137, in get
    return self.group(widgetType, group)[widgetName]
KeyError: 'EDIT'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 2468, in __rightClick
    if self.__checkCopyAndPaste(event):
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8665, in __checkCopyAndPaste
    self.disableMenu("EDIT", 10)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8791, in disableMenu
    self.__changeMenuState(title, DISABLED, limit)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 8797, in __changeMenuState
    theMenu = self.widgetManager.get(self.Widgets.Menu, title)
  File "/home/mark/.local/lib/python3.6/site-packages/appJar/appjar.py", line 13139, in get
    raise ItemLookupError("Invalid widgetName: " + widgetName)
appJar.appjar.ItemLookupError: Invalid widgetName: EDIT
@jarvisteach

This comment has been minimized.

Copy link
Owner

jarvisteach commented Dec 21, 2017

I'm not sure what's going on here, could you post the code?

It looks like, in the first screenshot, you've create a rightClickMenu with the name None - it needs a name so that you can link it to widgets, and add menu items to it.

The second set of errors might be linked to that?

@mpmc

This comment has been minimized.

Copy link
Contributor Author

mpmc commented Dec 22, 2017

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  gui.py
#
#  Copyright 2017 Mark Clarkstone <git@markclarkstone.co.uk>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#
#
import gettext
import traceback
import appJar
import hs602

gettext.install('Hs602util')


class Gui(appJar.gui):
    """Simple HS602 utility using appJar."""
    def __init__(self):
        # Initialise parent.
        super().__init__()
        # Window title / GUI defaults.
        self.setTitle(_('HS602 Utility'))
        # Theme.
        # self.useTtk()
        # self.setTtkTheme('default')
        self.addMenuEdit(inMenuBar=False)

        # HS602.
        self.controller = hs602.Controller()

        # Launch the main window.
        self.discover()

    # Helper methods.
    def widget_defaults(self, **kwargs):
        """Set widget defaults.

        :param inside: Set inside padding instead?
        :param sticky: Sticky value (default nesw).
        :param stretch: Stretch value (default both).
        :param expand: Expand value (default none).
        """
        # Outside the widget.
        out_x = 5
        out_y = 5
        # Inside the widget.
        in_x = 5
        in_y = 5

        # Get the values.
        stretch, inside, sticky, expand = [
            kwargs.get('stretch', 'both'),
            kwargs.get('inside', False),
            kwargs.get('sticky', 'nesw'),
            kwargs.get('expand'),
        ]
        # Call!
        self.setSticky(sticky)
        self.setStretch(stretch)

        if expand:
            self.setExpand(expand)

        if inside is True:
            self.setInPadding(in_x, in_y)
        else:
            self.setPadding(out_x, out_y)

    def error(self, exc):
        """Display an error."""
        # Label text.
        text = _('Sorry there\'s a problem, the details of '
                 'which are below.\nIf the problem persists, '
                 'please report it.\n\nThank you.')
        # Button properties.
        button_text = _('Quit')

        # Get the traceback (or message).
        tb = _('No traceback available.')
        try:
            tb = ''.join(traceback.format_tb(exc.__traceback__))
        except AttributeError:
            pass
        # Format it.
        exc = _('{}\n\n{}').format(exc, tb)

        def show():
            """Display the error."""
            # Reset the GUI!
            self.removeAllWidgets()

            self.widget_defaults(sticky='ew', stretch='none')
            # Add widgets - error label (top) & button (bottom).
            self.addLabel('text', text, 0, 0)
            self.setLabelAlign('text', 'left')
            self.addButton(button_text, self.stop, 2, 0)
            # Text area.
            self.widget_defaults()
            self.addScrolledTextArea('textarea', 1, 0, 2)
            self.setTextArea('textarea', "{}".format(exc))

        # To prevent weird things happening, queue.
        self.queueFunction(show)

    def interlude(self, text=None):
        """Clear the widgets and a show message - used during callbacks.

        text: Optional message to show.
        """
        text = text or _('\u231B Please wait..')
        text = '{}'.format(text)

        # Clear and display the message.
        self.removeAllWidgets()
        self.widget_defaults()
        self.addLabel('text', text)
        self.setLabelAlign('text', 'center')

    def callback(self, method, callback=None, *args, **kwargs):
        """Simple (queued) callback.

        :param method: Method to thread.
        :param callback: Optional method to receive the result.
        :param args: Positional arguments (for the threaded method).
        :param kwargs: Keyword arguments (for the threaded method).
        """
        def run():
            """Run the method in a new thread, catch errors."""
            try:
                ret = method(*args, **kwargs)
                # Queue the run_callback!
                self.queueFunction(run_callback, ret)
            except Exception as exc:
                self.error(exc)

        def run_callback(result):
            """Run the callback, pass the result, catch errors."""
            if callback and not callable(callback):
                text = _('Invalid callback {}').format(callback)
                return self.error(text)
            try:
                # Only run if callable!
                if callable(callback):
                    callback(result)
            except Exception as exc:
                self.error(exc)

        # Run!
        self.thread(run)

    def discover(self):
        """Discover devices."""

        def probe():
            return self.controller.devices0

        def show(result):
            print(result)

        self.interlude(_('Probing for devices, please wait..'))
        self.callback(probe, show)

    def main(self):
        """Main window."""


def main(args):
    app = Gui()
    app.go()
    return 0


if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))
@mpmc

This comment has been minimized.

Copy link
Contributor Author

mpmc commented Dec 22, 2017

OK, I think I've figured it out, it's the threading !

import appJar

class Gui(appJar.gui):
    """Test."""
    def __init__(self):
        # Initialise parent.
        super().__init__()
        self.setTitle('Test')
        self.addScrolledTextArea('textarea', 1, 0, 2)
        self.setTextArea('textarea', "{}".format('test'))
        self.addMenuEdit(inMenuBar=False)

app = Gui()
app.go()

It works if not threaded.


Edit: it works If I add it to the queued method instead of init.

        def show():
            """Display the error."""
            # Reset the GUI!
            self.removeAllWidgets()

            self.widget_defaults(sticky='ew', stretch='none')
            # Add widgets - error label (top) & button (bottom).
            self.addLabel('text', text, 0, 0)
            self.setLabelAlign('text', 'left')
            self.addButton(button_text, self.stop, 2, 0)
            # Text area.
            self.widget_defaults()
            self.addScrolledTextArea('textarea', 1, 0, 2)
            self.setTextArea('textarea', "{}".format(exc))
            self.addMenuEdit(inMenuBar=False)
@jarvisteach

This comment has been minimized.

Copy link
Owner

jarvisteach commented Dec 23, 2017

Interesting - that sounds like a good reason.

Do you think any changes are necessary? Or just a bit more guidance on ensuring certain functions are queued?

@mpmc

This comment has been minimized.

Copy link
Contributor Author

mpmc commented Dec 23, 2017

Do you think any changes are necessary? Or just a bit more guidance on ensuring certain functions are queued?

I initially thought I could just throw it in __init__ & assumed it would apply to all widgets. I guess not. But yes, a note about making sure menu's are threaded/queued correctly would be awesome!

jarvisteach added a commit that referenced this issue Dec 26, 2017

Resolved issue with rClick menu
RemoveAllWidgets was also deleting menus form menu container. For now,
disabling menu widget #325
@jarvisteach

This comment has been minimized.

Copy link
Owner

jarvisteach commented Dec 26, 2017

Turns out it was .removeAllWidgets() - breaking this. The WidgetManager was getting flushed, which removes all menus, etc.

A quick fix to stop the error was to simply cancel the right-click menu, forcing the user to re-enable it.

Long-term, I think I need to go back and look at menus, and have a rethink....

@jarvisteach jarvisteach added this to the 0.90 milestone Dec 26, 2017

jarvisteach added a commit that referenced this issue Dec 26, 2017

@jarvisteach jarvisteach referenced this issue Dec 26, 2017

Open

Refactor menus #305

6 of 7 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment