construct-editor icon indicating copy to clipboard operation
construct-editor copied to clipboard

Construct-editor Error when quickly browsing the Construct Gallery of the Construct Editor GUI

Open Ircama opened this issue 2 years ago • 4 comments

An error appears if the top left panel of the Construct Editor GUI is quickly changed by selecting a construct sample and then pressing a UP or DOWN arrow key, keeping it pressed.

Often (but possibly not always) a KeyError is shown. E.g., the GetSize() or Render() methods of the ObjectRenderer class in wx_construct_editor.py are often impacted.

Example:

  File "...\Python39\site-packages\construct_editor\wx_widgets\wx_construct_editor.py", line 54, in GetSize
    return self.entry_renderer_helper.get_size(self)
  File "...\Python39\site-packages\construct_editor\wx_widgets\wx_obj_view.py", line 376, in get_size
    obj_str = self.entry.obj_str if self.entry else ""
  File "...\Python39\site-packages\construct_editor\core\entries.py", line 1789, in obj_str
    return self.conv_obj_to_str(self.obj)
  File "...\Python39\site-packages\construct_editor\core\entries.py", line 252, in obj
    obj = obj[p]

Uncaught exception: KeyError: 'days2'

The frequency of the error depends on how many elements are consecutively browsed in sequence (many are needed for the error to occur, not just some) and on the speed of the computer.

Ircama avatar Feb 19 '23 15:02 Ircama

I am not quite sure how you are generating the error. I can not reproduce it, but I am not sure if I do it the same like you. Can you take a screenshot of the panel where you hold down the UP and DOWN buttons?

timrid avatar Feb 19 '23 16:02 timrid

You either need to browse many construct elements in the gallery (a testing program is needed and this allows checking that the issue occurs every time with fast browsing) or you have to be extremely fast with the ones included in the default sample and perform this testing various times. I will develop a small program that generates the error.

Ircama avatar Feb 19 '23 19:02 Ircama

This is a simplified version of main.py. To test it, keep the DOWN arrow key pressed and then the UP arrow key pressed at the end of the travel and repeat this many times. In my Windows 11 setup, Python 3.10.10, I systematically get the error after less than 10 attempts.

import wx
from construct_editor.wx_widgets import WxConstructHexEditor
from pkgutil import iter_modules
from importlib import import_module
import construct_editor.gallery


class ConstructGalleryFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.SetSize(1000, 500)
        self.Center()
        self.status_bar: wx.StatusBar = self.CreateStatusBar()
        self.main_panel = ConstructGallery(self)


class ConstructGallery(wx.Panel):
    def __init__(self, parent: ConstructGalleryFrame):
        super().__init__(parent)

        sample_modules = {
            submodule.name: import_module(
                "construct_editor.gallery." + submodule.name)
                for submodule in iter_modules(
                    construct_editor.gallery.__path__,)
        }
        self.construct_gallery = {
            f"{i}_{module}" : sample_modules[module].gallery_item
                for i in range(10)
                    for module in sample_modules
        }
        self.gallery_selection = 0
        default_gallery = list(self.construct_gallery.keys())[self.gallery_selection]
        default_gallery_item = self.construct_gallery[default_gallery]

        # Define GUI elements #############################################
        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        vsizer = wx.BoxSizer(wx.VERTICAL)

        # gallery selctor
        self.gallery_selector_lbx = wx.ListBox(
            self,
            wx.ID_ANY,
            wx.DefaultPosition,
            wx.DefaultSize,
            list(self.construct_gallery.keys()),
            0,
            name="gallery_selector",
        )
        self.gallery_selector_lbx.SetStringSelection(default_gallery)
        vsizer.Add(self.gallery_selector_lbx, 1, wx.ALL, 1)

        # example selctor
        self.example_selector_lbx = wx.ListBox(
            self,
            wx.ID_ANY,
            wx.DefaultPosition,
            wx.Size(-1, 100),
            list(default_gallery_item.example_binarys.keys()),
            0,
            name="gallery_selector",
        )
        if len(default_gallery_item.example_binarys) > 0:
            self.example_selector_lbx.SetStringSelection(
                list(default_gallery_item.example_binarys.keys())[0]
            )
        vsizer.Add(self.example_selector_lbx, 0, wx.ALL | wx.EXPAND, 1)

        self.sizer.Add(vsizer, 0, wx.ALL | wx.EXPAND, 0)

        # construct hex editor
        self.construct_hex_editor = WxConstructHexEditor(
            self,
            construct=default_gallery_item.construct,
            contextkw=default_gallery_item.contextkw,
        )
        # self.construct_hex_editor.construct_editor.expand_all()
        self.sizer.Add(self.construct_hex_editor, 1, wx.ALL | wx.EXPAND, 0)

        self.SetSizer(self.sizer)

        # Connect Events ##################################################
        self.gallery_selector_lbx.Bind(
            wx.EVT_LISTBOX, self.on_gallery_selection_changed
        )
        self.example_selector_lbx.Bind(
            wx.EVT_LISTBOX, self.on_example_selection_changed
        )


        # Emulate Selection Click
        self.on_gallery_selection_changed(None)

    def on_gallery_selection_changed(self, event):
        selection = self.gallery_selector_lbx.GetStringSelection()
        gallery_item = self.construct_gallery[selection]
        if gallery_item is None:
            self.gallery_selector_lbx.SetSelection(
                self.gallery_selection
            )  # restore old selection
            return

        # save currently shown selection
        self.gallery_selection = self.gallery_selector_lbx.GetSelection()

        self.example_selector_lbx.Clear()
        if len(gallery_item.example_binarys) > 0:
            self.example_selector_lbx.InsertItems(
                list(gallery_item.example_binarys.keys()), 0
            )
            self.example_selector_lbx.SetStringSelection(
                list(gallery_item.example_binarys.keys())[0]
            )

            example = self.example_selector_lbx.GetStringSelection()
            example_binary = self.construct_gallery[selection].example_binarys[example]
        else:
            example_binary = bytes(0)

        self.Freeze()
        self.construct_hex_editor.change_construct(gallery_item.construct)
        self.construct_hex_editor.change_contextkw(gallery_item.contextkw)
        self.construct_hex_editor.binary = example_binary
        self.construct_hex_editor.construct_editor.expand_all()
        self.Thaw()

    def on_example_selection_changed(self, event):
        selection = self.gallery_selector_lbx.GetStringSelection()
        example = self.example_selector_lbx.GetStringSelection()
        example_binary = self.construct_gallery[selection].example_binarys[example]

        # Set example binary
        self.construct_hex_editor.binary = example_binary
        self.construct_hex_editor.construct_editor.expand_all()


def main():
    app = wx.App(False)
    frame = ConstructGalleryFrame(None)
    frame.Show(True)
    app.MainLoop()


if __name__ == "__main__":
    main()

Ircama avatar Feb 20 '23 06:02 Ircama

This problem almost always occurs with WSL Ubuntu and with Raspberry Pi and makes this framework unusable. There is something that refreshes old values after changing the construct sample or gallery and with Windows it only occurs very rarely.


At the end I found that the issue occurs with wx.Yield(). It looks like Gtk does not like it there. By commenting out that line, the issue disappears.

Ircama avatar Dec 08 '23 16:12 Ircama