[Support needed] custom widget properties #237
-
Trying to override customtkinter.CTkOptionMenu so it fills up with data when opened. Similar to how in combobox you can use postcommand. I copied everything from it's plugin builder under (Also note that after adding this optionmenu.py in pygubu-designer as custom widget, it won't load the side panel settings but i'm fine with it (the widget itself shows fine). Probably because i'm not refering to designer module inside Let me know if i'm missing something obvious i'll provide all the info i can. Relevant from .ui file<object class="FMD3_Tkinter.OptionMenu" id="settings_def_series_lib_combobox" named="True">
<property name="command" type="command" cbtype="simple">settings_load_libs_from_treeview</property>
<property name="postcommand" type="command" cbtype="simple">pre_settings_load_libs_from_treeview</property>
<property name="values">[""]</property>
<property name="variable">string:default_series_downloads_path</property>
<layout manager="pack">
<property name="side">left</property>
</layout>
</object> optionmenu.pyfrom pygubu.component.builderobject import BuilderObject
from pygubu.plugins.customtkinter.ctkbase import CTkBaseMixin, GINPUT
from pygubu.plugins.customtkinter.widgets import CTkOptionMenuBO
from pygubu.utils.datatrans import ListDTO
from customtkinter import CTkOptionMenu
from pygubu.api.v1 import register_widget, register_custom_property
from pygubu.i18n import _
_plugin_uid = "FMD3_Tkinter"
_designer_tab_label = _(_plugin_uid)
class OptionMenu(CTkOptionMenu):
def __init__(self,*args,**kwargs):
CTkOptionMenu.__init__(self,*args,**kwargs)
def _clicked(self,event=0):
self._precommand()
super()._clicked(event)
def configure(self,*args, **kwargs):
if "postcommand" in kwargs:
self._precommand = kwargs.pop("postcommand")
super().configure(*args, **kwargs)
...
_list_dto = ListDTO()
class OptionMenuBO(CTkOptionMenuBO):
class_ = OptionMenu
allow_bindings = False
properties = (
"command",
"variable",
"values",
"bg_color",
"fg_color",
"button_color",
"button_hover_color",
"text_color",
"text_color_disabled",
"dropdown_hover_color",
"dropdown_text_color",
"dropdown_color",
"dropdown_font",
"width",
"height",
"corner_radius",
"state",
"dynamic_resizing",
"font",
)
command_properties = ("postcommand","command")
def _process_property_value(self, pname, value):
if pname == "values":
return _list_dto.transform(value)
return super()._process_property_value(pname, value)
def _code_define_callback_args(self, cmd_pname, cmd):
return ("current_value",)
def _code_process_property_value(self, targetid, pname, value: str):
if pname == "values":
return super()._process_property_value(pname, value)
return super()._code_process_property_value(targetid, pname, value)
_ctk_values_help = _(
"Specifies the list of values to display. "
"In code you can pass any iterable. "
'In Designer, a json like list: ["item1", "item2"]'
)
register_widget(
f"{_plugin_uid}.OptionMenu",
OptionMenuBO,
"OptionMenu",
("ttk", _designer_tab_label),
group=GINPUT,
)
register_custom_property(f"{_plugin_uid}.OptionMenu", "values", "entry", help=_ctk_values_help)
register_custom_property(
f"{_plugin_uid}.OptionMenu",
"state",
"choice",
values=("", "normal", "disabled", "readonly"),
state="readonly"
)
register_custom_property(f"{_plugin_uid}.OptionMenu", "postcommand", "entry",help="s") |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
As an extra. How do you guys sectionate the main class? The file has gotten quite big and tedious to navigate. Would something like this work at all? class App:
def __init__(self, master=None, translator=None):
self.builder = builder = pygubu.Builder(translator)
builder.add_resource_path(PROJECT_PATH)
builder.add_from_file(PROJECT_UI)
# Main widget
self.mainwindow: customtkinter.CTk = builder.get_object("ctk1", master)
frame_1_callbacks = FrameExtensionCode()
builder.import_variables(self)
builder.connect_callbacks(self)
builder.import_variables(frame_1_callbacks)
builder.connect_callbacks(frame_1_callbacks)
``` |
Beta Was this translation helpful? Give feedback.
-
Hello.
You did almost everything right. What is missing and causing the error is the following:
With all this changes your optionmenu is working. See the following example. optionmenu.py updated: from pygubu.component.builderobject import BuilderObject
from pygubu.plugins.customtkinter.ctkbase import CTkBaseMixin, GINPUT
from pygubu.plugins.customtkinter.widgets import CTkOptionMenuBO
from pygubu.utils.datatrans import ListDTO
from customtkinter import CTkOptionMenu
from pygubu.api.v1 import register_widget, register_custom_property
from pygubu.i18n import _
_plugin_uid = "customtkinter.FMD3_Tkinter" # <--
_designer_tab_label = _(_plugin_uid)
class OptionMenu(CTkOptionMenu):
def __init__(self,*args,**kwargs):
CTkOptionMenu.__init__(self,*args,**kwargs)
def _clicked(self,event=0):
self._precommand(self)
super()._clicked(event)
def configure(self,*args, **kwargs):
if "postcommand" in kwargs:
self._precommand = kwargs.pop("postcommand")
super().configure(*args, **kwargs)
class OptionMenuBO(CTkOptionMenuBO):
class_ = OptionMenu
allow_bindings = False
properties = (
"command",
"variable",
"values",
"bg_color",
"fg_color",
"button_color",
"button_hover_color",
"text_color",
"text_color_disabled",
"dropdown_hover_color",
"dropdown_text_color",
"dropdown_color",
"dropdown_font",
"width",
"height",
"corner_radius",
"state",
"dynamic_resizing",
"font",
"postcommand", # <--
)
command_properties = ("postcommand","command")
def _code_define_callback_args(self, cmd_pname, cmd):
if cmd_pname == "postcommand":
return ("option_menu",)
return super()._code_define_callback_args(cmd_pname, cmd)
register_widget(
f"{_plugin_uid}.OptionMenu",
OptionMenuBO,
"OptionMenu",
("ttk", _designer_tab_label),
group=GINPUT,
)
register_custom_property(
f"{_plugin_uid}.OptionMenu",
"postcommand", "simplecommandentry", help="s") My test file ui: testapp.ui <?xml version='1.0' encoding='utf-8'?>
<interface version="1.3">
<object class="customtkinter.CTk" id="ctk1">
<property name="geometry">320x240</property>
<child>
<object class="customtkinter.CTkFrame" id="ctkframe1">
<layout manager="pack">
<property name="expand">true</property>
<property name="fill">both</property>
<property name="side">top</property>
</layout>
<child>
<object class="customtkinter.CTkLabel" id="ctklabel1">
<property name="text" translatable="yes">OptionMenu Test</property>
<layout manager="pack">
<property name="side">top</property>
</layout>
</object>
</child>
<child>
<object class="customtkinter.FMD3_Tkinter.OptionMenu" id="optionmenu1">
<property name="command" type="command" cbtype="simple">on_option_selected</property>
<property name="postcommand" type="command" cbtype="simple">on_postcommand_action</property>
<layout manager="pack">
<property name="expand">true</property>
<property name="side">top</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</interface> testapp.py #!/usr/bin/python3
import pathlib
import tkinter as tk
import pygubu
import random
import optionmenu
PROJECT_PATH = pathlib.Path(__file__).parent
PROJECT_UI = PROJECT_PATH / "testapp.ui"
class TestappApp:
def __init__(self, master=None):
self.builder = builder = pygubu.Builder()
builder.add_resource_path(PROJECT_PATH)
builder.add_from_file(PROJECT_UI)
# Main widget
self.mainwindow: customtkinter.CTk = builder.get_object("ctk1", master)
builder.connect_callbacks(self)
def run(self):
self.mainwindow.mainloop()
def on_postcommand_action(self, option_menu):
choices = [ f"option_{idx}" for idx in random.sample(range(1, 100), 10) ]
option_menu.configure(values=choices)
def on_option_selected(self, current_value):
print(f"You selected: ", current_value)
if __name__ == "__main__":
app = TestappApp()
app.run() Regards |
Beta Was this translation helpful? Give feedback.
-
ThankyouThankyouThankyouThankyouThankyouu!!! Got any input on how to shorten/sectionate the python class? |
Beta Was this translation helpful? Give feedback.
Hello.