from gi.repository import GLib, Gtk
class Tool:
def __init__(self):
self.id = ""
self.name = ""
self.properties = []
self.icon_path = ""
self.on_change_callback = None
# Let the tool set values
self.on_init()
self.props = {}
for property in self.properties:
self.props[property.id] = property
# Create widget for tool
self.widget = Gtk.Grid()
self.widget.set_column_spacing(8)
self.widget.set_row_spacing(6)
self.widget.set_halign(Gtk.Align.FILL)
self.widget.set_hexpand(True)
vpos = 0
for property in self.properties:
# Create Header
if(property.type == "Header"):
header = Gtk.HBox()
header.set_margin_top(6)
if(vpos != 0):
# Add a Separator
separator = Gtk.Separator()
separator.set_margin_top(6)
self.widget.attach(separator, 0, vpos, 3, 1)
vpos += 1
title = Gtk.Label()
title.set_halign(Gtk.Align.START)
if(property.is_subheading):
title.set_markup("%s" % property.name)
else:
title.set_markup("%s" % property.name)
header.add(title)
if(property.has_toggle):
toggle = Gtk.Switch()
toggle.set_halign(Gtk.Align.END)
header.add(toggle)
property.on_toggle_callback = self.on_toggled
property.set_widget(toggle)
elif(property.has_button):
button = Gtk.Button()
button.set_halign(Gtk.Align.END)
button.set_label(property.button_label)
header.add(button)
property.set_widget(button)
self.widget.attach(header, 0, vpos, 3, 1)
# Create Combo
elif(property.type == "Combo"):
label = Gtk.Label()
label.set_halign(Gtk.Align.END)
label.set_justify(Gtk.Justification.RIGHT)
label.set_text(property.name)
self.widget.attach(label, 0, vpos, 1, 1)
combo = Gtk.ComboBoxText()
combo.set_hexpand(True)
self.widget.attach(combo, 1, vpos, 1, 1)
for option in property.options:
combo.append_text(option)
property.set_widget(combo)
# Create Spin
elif (property.type == "Spin"):
label = Gtk.Label()
label.set_halign(Gtk.Align.END)
label.set_justify(Gtk.Justification.RIGHT)
label.set_text(property.name)
self.widget.attach(label, 0, vpos, 1, 1)
adjustment = Gtk.Adjustment()
adjustment.set_lower(property.min)
adjustment.set_upper(property.max)
adjustment.set_step_increment((property.max - property.min) / 100)
property.set_widget(adjustment)
spin = Gtk.SpinButton()
spin.set_adjustment(adjustment)
spin.set_hexpand(True)
spin.set_digits(3)
property.ui_widget = spin
self.widget.attach(spin, 1, vpos, 1, 1)
# Create Toggle
elif(property.type == "Toggle"):
label = Gtk.Label()
label.set_halign(Gtk.Align.END)
label.set_justify(Gtk.Justification.RIGHT)
label.set_text(property.name)
self.widget.attach(label, 0, vpos, 1, 1)
toggle = Gtk.ToggleButton()
toggle.set_label("Enable")
toggle.set_hexpand(True)
self.widget.attach(toggle, 1, vpos, 1, 1)
property.set_widget(toggle)
# Create Slider
elif (property.type == "Slider"):
label = Gtk.Label()
label.set_halign(Gtk.Align.END)
label.set_justify(Gtk.Justification.RIGHT)
label.set_text(property.name)
self.widget.attach(label, 0, vpos, 1, 1)
adjustment = Gtk.Adjustment()
adjustment.set_lower(property.min)
adjustment.set_upper(property.max)
adjustment.set_step_increment((property.max - property.min)/100)
property.set_widget(adjustment)
slider = Gtk.Scale()
slider.set_adjustment(adjustment)
slider.set_hexpand(True)
slider.set_value_pos(Gtk.PositionType.RIGHT)
property.ui_widget = slider
self.widget.attach(slider, 1, vpos, 1, 1)
if(property.type != "Header"):
# Create reset button
icon = Gtk.Image()
icon.set_from_icon_name("edit-clear-symbolic", Gtk.IconSize.BUTTON)
reset_button = Gtk.Button()
reset_button.set_image(icon)
reset_button.connect("clicked", property.reset_value)
property.reset_button = reset_button
self.widget.attach(reset_button, 2, vpos, 1, 1)
# Connect on change
property.connect_on_change(self.on_change)
vpos += 1
for property in self.properties:
if(property.type == "Header") and (property.has_toggle):
self.on_toggled(property, property.widget.get_active())
separator = Gtk.Separator()
separator.set_margin_top(6)
self.widget.attach(separator, 0, vpos, 3, 1)
self.tool_button = Gtk.ToggleButton()
self.tool_button.set_tooltip_text(self.name)
icon = Gtk.Image()
icon.set_from_file(self.icon_path)
self.tool_button.set_image(icon)
def on_toggled(self, sender, value):
si = self.properties.index(sender)
for property in self.properties[si+1:]:
if(property.type == "Header") and (property.has_toggle):
break
else:
property.set_enabled(value)
def is_default(self):
res = True
for prop in self.properties:
if(prop.get_value() != prop.default):
res = False
break
return res
def connect_on_change(self, callback):
self.on_change_callback = callback
def on_change(self, sender):
if(self.on_change_callback != None):
self.on_change_callback(self, sender)
def on_init(self):
raise NotImplementedError()
def on_button_pressed(self):
raise NotImplementedError()
def on_update(self, image):
raise NotImplementedError()
def load_properties(self, dict):
for key in dict:
if(key in self.props):
self.props[key].set_value(dict[key])
def get_properties_as_dict(self):
dict = {}
for prop in self.properties:
dict[prop.id] = prop.get_value()
return dict
def reset(self):
for prop in self.properties:
prop.reset_value()
class Property:
def __init__(self, id, name, type, default, **kwargs):
self.id = id
self.name = name
self.type = type
self.on_change_callback = None
# Types Include:
# Slider,
# Toggle,
# Spin,
# Combo,
# Header
if(self.type == "Slider") or (self.type == "Spin"):
# Slider and Spinner
self.max = kwargs["max"]
self.min = kwargs["min"]
self.ui_widget = None
if(self.type == "Combo"):
self.options = kwargs["options"]
if(self.type == "Header"):
self.has_toggle = kwargs["has_toggle"]
self.has_button = kwargs["has_button"]
if(self.has_button):
self.button_callback = kwargs["button_callback"]
self.button_label = kwargs["button_label"]
if("is_subheading" in kwargs):
self.is_subheading = kwargs["is_subheading"]
else:
self.is_subheading = False
self.value = default
self.default = default
self.widget = None
self.reset_button = None
self.on_toggle_callback = None
def get_value(self):
return self.value
def set_value(self, value):
self.value = value
if(self.type == "Header") and (self.has_toggle):
self.widget.set_active(value)
if(self.type == "Slider") or (self.type == "Spin"):
self.widget.set_value(value)
if(self.type == "Toggle") or (self.type == "Combo"):
self.widget.set_active(value)
self.on_change()
def reset_value(self, sender=None):
self.set_value(self.default)
return self.default
def update_value(self, sender, arg=None):
if(self.type == "Header") and (self.has_toggle):
self.set_value(arg)
if(self.on_toggle_callback != None):
self.on_toggle_callback(self, arg)
if(self.type == "Header") and (self.has_button):
self.button_callback()
if(self.type == "Slider") or (self.type == "Spin"):
self.set_value(sender.get_value())
if(self.type == "Toggle") or (self.type == "Combo"):
self.set_value(sender.get_active())
def set_widget(self, object):
self.widget = object
self.reset_value()
if(self.type == "Header") and (self.has_toggle):
object.connect("state-set", self.update_value)
if(self.type == "Header") and (self.has_button):
object.connect("clicked", self.update_value)
if(self.type == "Slider") or (self.type == "Spin"):
object.connect("value-changed", self.update_value)
if(self.type == "Toggle"):
object.connect("toggled", self.update_value)
if(self.type == "Combo"):
object.connect("changed", self.update_value)
def set_enabled(self, enabled):
if(self.type == "Slider") or (self.type == "Spin"):
self.ui_widget.set_sensitive(enabled)
elif(self.widget != None):
self.widget.set_sensitive(enabled)
if(self.reset_button != None):
self.reset_button.set_sensitive(enabled)
def connect_on_change(self, callback):
self.on_change_callback = callback
def on_change(self):
if(self.on_change_callback != None):
self.on_change_callback(self)