Esempio n. 1
0
class CollapseFrame(Frame):
    def __init__(self, master, **cnf):
        super().__init__(master, **cnf)
        self.config(**self.style.dark)
        self._label_frame = Frame(self, **self.style.bright, height=20)
        self._label_frame.pack(side="top", fill="x", padx=2)
        self._label_frame.pack_propagate(0)
        self._label = Label(self._label_frame, **self.style.bright,
                            **self.style.text_bright)
        self._label.pack(side="left")
        self._collapse_btn = Button(self._label_frame,
                                    width=20,
                                    **self.style.bright,
                                    **self.style.text_bright)
        self._collapse_btn.config(text=get_icon("triangle_up"))
        self._collapse_btn.pack(side="right", fill="y")
        self._collapse_btn.on_click(self.toggle)
        self.body = Frame(self, **self.style.dark)
        self.body.pack(side="top", fill="both", pady=2)
        self.__ref = Frame(self.body, height=0, width=0, **self.style.dark)
        self.__ref.pack(side="top")
        self._collapsed = False

    def update_state(self):
        self.__ref.pack(side="top")

    def collapse(self, *_):
        if not self._collapsed:
            self.body.pack_forget()
            self._collapse_btn.config(text=get_icon("triangle_down"))
            self.pack_propagate(0)
            self.config(height=20)
            self._collapsed = True

    def clear_children(self):
        self.body.clear_children()

    def expand(self, *_):
        if self._collapsed:
            self.body.pack(side="top", fill="both")
            self.pack_propagate(1)
            self._collapse_btn.config(text=get_icon("triangle_up"))
            self._collapsed = False

    def toggle(self, *_):
        if self._collapsed:
            self.expand()
        else:
            self.collapse()

    @property
    def label(self):
        return self._label["text"]

    @label.setter
    def label(self, value):
        self._label.config(text=value)
Esempio n. 2
0
class CollapseFrame(Frame):
    __icons_loaded = False
    EXPAND = None
    COLLAPSE = None

    def __init__(self, master, **cnf):
        super().__init__(master, **cnf)
        self._load_icons()
        self.config(**self.style.surface)
        self._label_frame = Frame(self, **self.style.bright, height=20)
        self._label_frame.pack(side="top", fill="x", padx=2)
        self._label_frame.pack_propagate(0)
        self._label = Label(self._label_frame, **self.style.bright,
                            **self.style.text_bright)
        self._label.pack(side="left")
        self._collapse_btn = Button(self._label_frame,
                                    width=20,
                                    **self.style.bright,
                                    **self.style.text_bright)
        self._collapse_btn.config(image=self.COLLAPSE)
        self._collapse_btn.pack(side="right", fill="y")
        self._collapse_btn.on_click(self.toggle)
        self.body = Frame(self, **self.style.surface)
        self.body.pack(side="top", fill="both", pady=2)
        self.__ref = Frame(self.body, height=0, width=0, **self.style.surface)
        self.__ref.pack(side="top")
        self._collapsed = False

    @classmethod
    def _load_icons(cls):
        if cls.__icons_loaded:
            return
        cls.EXPAND = get_icon_image("triangle_down", 14, 14)
        cls.COLLAPSE = get_icon_image("triangle_up", 14, 14)

    def update_state(self):
        self.__ref.pack(side="top")

    def collapse(self, *_):
        if not self._collapsed:
            self.body.pack_forget()
            self._collapse_btn.config(image=self.EXPAND)
            self.pack_propagate(0)
            self.config(height=20)
            self._collapsed = True

    def clear_children(self):
        self.body.clear_children()

    def expand(self, *_):
        if self._collapsed:
            self.body.pack(side="top", fill="both")
            self.pack_propagate(1)
            self._collapse_btn.config(image=self.COLLAPSE)
            self._collapsed = False

    def toggle(self, *_):
        if self._collapsed:
            self.expand()
        else:
            self.collapse()

    @property
    def label(self):
        return self._label["text"]

    @label.setter
    def label(self, value):
        self._label.config(text=value)
Esempio n. 3
0
class StyleGroup(CollapseFrame):
    """
    Main subdivision of the Style pane
    """
    handles_layout = False
    self_positioned = False

    def __init__(self, master, pane, **cnf):
        super().__init__(master)
        self.style_pane = pane
        self.configure(**{**self.style.surface, **cnf})
        self._empty_message = "Select an item to see styles"
        self._empty = Frame(self.body, **self.style.surface)
        self._empty_label = Label(
            self._empty,
            **self.style.text_passive,
        )
        self._empty_label.pack(fill="both", expand=True, pady=15)
        self._widget = None
        self._prev_widget = None
        self._has_initialized = False  # Flag to mark whether Style Items have been created
        self.items = {}

    @property
    def widget(self):
        return self._widget

    def can_optimize(self):
        return False

    def add(self, style_item):
        self.items[style_item.name] = style_item
        if self.style_pane._search_query is not None:
            if self._match_query(style_item.definition,
                                 self.style_pane._search_query):
                self._show(style_item)
            # make sure item is not available for reuse whether it
            # is displayed or not
            style_item._make_available(False)
        else:
            self._show(style_item)

    def remove(self, style_item):
        if style_item.name in self.items:
            self.items.pop(style_item.name)
        self._hide(style_item)

    def _show(self, item):
        item.pack(side="top", fill="x", pady=1)

    def _hide(self, item):
        item.pack_forget()

    def _get_prop(self, prop, widget):
        return widget.get_prop(prop)

    def _set_prop(self, prop, value, widget):
        widget.configure(**{prop: value})

    def _hide_group(self):
        pass

    def _show_group(self):
        pass

    def _match_query(self, definition, query):
        return query in definition["name"] or query in definition[
            "display_name"]

    def _show_empty(self, text=None):
        self._empty.pack(fill="both", expand=True)
        text = self._empty_message if text is None else text
        self._empty_label["text"] = text

    def _remove_empty(self):
        self._empty.pack_forget()

    def on_widget_change(self, widget):
        self._widget = widget
        if widget is None:
            self.collapse()
            return
        definitions = self.get_definition()
        if self.can_optimize():
            for prop in definitions:
                self.items[prop]._re_purposed(definitions[prop])
        else:
            self.style_pane.show_loading()
            # this unmaps all style items returning them to the pool for reuse
            self.clear_children()
            # make all items held by group available for reuse
            ReusableStyleItem.free_all(self.items.values())
            self.items.clear()
            add = self.add
            list(
                map(
                    lambda p: add(
                        ReusableStyleItem.acquire(self, definitions[p], self.
                                                  apply), ), definitions))
            if not self.items:
                self._show_empty()
            else:
                self._remove_empty()
            # self.style_pane.body.scroll_to_start()

        self._has_initialized = True
        self._prev_widget = widget

    def _apply_action(self, prop, value, widget, data):
        self.apply(prop, value, widget, True)

    def _get_action_data(self, widget, prop):
        return {}

    def _get_key(self, widget, prop):
        return f"{widget}:{self.__class__.__name__}:{prop}"

    def apply(self, prop, value, widget=None, silent=False):
        is_external = widget is not None
        widget = self.widget if widget is None else widget
        if widget is None:
            return
        try:
            prev_val = self._get_prop(prop, widget)
            data = self._get_action_data(widget, prop)
            self._set_prop(prop, value, widget)
            new_data = self._get_action_data(widget, prop)
            self.style_pane.widget_modified(widget)
            if is_external:
                if widget == self.widget:
                    self.items[prop].set_silently(value)
            if silent:
                return
            key = self._get_key(widget, prop)
            action = self.style_pane.last_action()
            if action is None or action.key != key:
                self.style_pane.new_action(
                    Action(
                        lambda _: self._apply_action(prop, prev_val, widget,
                                                     data),
                        lambda _: self._apply_action(prop, value, widget,
                                                     new_data),
                        key=key,
                    ))
            else:
                action.update_redo(lambda _: self._apply_action(
                    prop, value, widget, new_data))
        except Exception as e:
            # Empty string values are too common to be useful in logger debug
            if value != '':
                logging.error(e)
                logging.error(
                    f"Could not set {self.__class__.__name__} {prop} as {value}",
                )

    def get_definition(self):
        return {}

    def supports_widget(self, widget):
        return True

    def on_search_query(self, query):
        item_found = False
        for item in self.items.values():
            if self._match_query(item.definition, query):
                self._show(item)
                item_found = True
            else:
                self._hide(item)
        if not item_found:
            self._show_empty("No items match your search")
        else:
            self._remove_empty()

    def on_search_clear(self):
        # Calling search query with empty query ensures all items are displayed
        self.clear_children()
        self.on_search_query("")