Creating custom wxPython controls for Pytigon's native GUI.
Extend SchBaseCtrl from guictrl.basectrl:
from pytigon_gui.guictrl.basectrl import SchBaseCtrl
import wx
class ColorPickerCtrl(SchBaseCtrl):
"""Custom color picker control."""
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self._create_widgets()
def _create_widgets(self):
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.color_display = wx.Panel(self, size=(40, 40))
self.color_display.SetBackgroundColour(wx.Colour(255, 0, 0))
sizer.Add(self.color_display, 0, wx.ALL, 5)
self.pick_btn = wx.Button(self, label="Pick...")
self.pick_btn.Bind(wx.EVT_BUTTON, self._on_pick)
sizer.Add(self.pick_btn, 0, wx.ALL, 5)
self.SetSizer(sizer)
def _on_pick(self, event):
dialog = wx.ColourDialog(self, wx.ColourData())
if dialog.ShowModal() == wx.ID_OK:
color = dialog.GetColourData().GetColour()
self.set_value(color)
self.color_display.SetBackgroundColour(color)
self.color_display.Refresh()
self.fire_event('color_changed', color=color)
def get_value(self):
return self.color_display.GetBackgroundColour()
def set_value(self, color):
self.color_display.SetBackgroundColour(color)
self.color_display.Refresh()
## Registering the Control
Register with the control factory so it can be used in tag definitions:
```python
from pytigon_gui.guictrl.factory import register_control
register_control('colorpicker', ColorPickerCtrl)
Now you can use it in tag syntax:
<colorpicker field="bg_color" on_change="update_preview" />
Override set_attr() to handle tag attributes:
def set_attr(self, name, value):
if name == 'alpha':
self.alpha_enabled = value
else:
super().set_attr(name, value)
Fire custom events that bubble up through the control tree:
self.fire_event('value_changed', old_value=old, new_value=new)
Use wxPython sizers for automatic layout:
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.child1, 1, wx.EXPAND)
sizer.Add(self.child2, 0, wx.ALIGN_CENTER)
self.SetSizer(sizer)
Connect to Pytigon's data binding system:
# Control updates model
def on_change(self):
self.set_field_value(self.field_name, self.get_value())
# Model updates control
def refresh_from_model(self):
value = self.get_field_value(self.field_name)
self.set_value(value)
Complex controls can compose simpler ones:
class AddressCtrl(SchBaseCtrl):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
sizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
sizer.Add(wx.StaticText(self, label="Street:"))
self.street = wx.TextCtrl(self)
sizer.Add(self.street, 1, wx.EXPAND)
sizer.Add(wx.StaticText(self, label="City:"))
self.city = wx.TextCtrl(self)
sizer.Add(self.city, 1, wx.EXPAND)
self.SetSizer(sizer)
def get_value(self):
return f"{self.street.GetValue()}, {self.city.GetValue()}"
def set_value(self, address_str):
parts = address_str.split(', ')
if len(parts) == 2:
self.street.SetValue(parts[0])
self.city.SetValue(parts[1])
register_control('address', AddressCtrl)
Custom controls can also be distributed as plugins. In a plugin's init_plugin():
def init_plugin(app, frame, panels):
from pytigon_gui.guictrl.factory import register_control
from .mycontrols import MyCustomCtrl
register_control('mycustom', MyCustomCtrl)
This approach allows distributing custom control libraries without modifying Pytigon's core code.