Construct-editor Error when quickly browsing the Construct Gallery of the Construct Editor GUI
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.
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?
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.
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()
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.