コード例 #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)
コード例 #2
0
ファイル: widgets.py プロジェクト: ObaraEmmanuel/Formation
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)
コード例 #3
0
class ComponentTree(BaseFeature):
    name = "Component Tree"
    icon = "treeview"

    def __init__(self, master, studio=None,  **cnf):
        super().__init__(master, studio, **cnf)
        self._tree = ComponentTreeView(self)
        self._tree.pack(side="top", fill="both", expand=True, pady=4)
        # self._tree.sample()
        self._tree.on_select(self._trigger_select)
        self._toggle_btn = Button(self._header, image=get_icon_image("chevron_down", 15, 15), **self.style.dark_button,
                                  width=25,
                                  height=25)
        self._toggle_btn.pack(side="right")
        self._toggle_btn.on_click(self._toggle)

        self._selected = None
        self._expanded = False

        self.studio.designer.node = self._tree

    def create_menu(self):
        return (
            ("command", "Expand all", get_icon_image("chevron_down", 14, 14), self._expand, {}),
            ("command", "Collapse all", get_icon_image("chevron_up", 14, 14), self._collapse, {})
        )

    def _expand(self):
        self._tree.expand_all()
        self._toggle_btn.config(image=get_icon_image("chevron_up", 15, 15))
        self._expanded = True

    def _collapse(self):
        self._tree.collapse_all()
        self._toggle_btn.config(image=get_icon_image("chevron_down", 15, 15))
        self._expanded = False

    def _toggle(self, *_):
        if self._expanded:
            self._collapse()
        else:
            self._expand()

    def on_widget_add(self, widget: PseudoWidget, parent=None):
        if parent is None:
            node = self._tree.add_as_node(widget=widget)
        else:
            parent = parent.node
            node = parent.add_as_node(widget=widget)

        # let the designer render the menu for us
        node.bind_all('<Button-3>',
                      lambda e: self.studio.designer.show_menu(e, widget) if self.studio.designer else None)

    def _trigger_select(self):
        if self._selected and self._selected.widget == self._tree.get().widget:
            return
        self.studio.select(self._tree.get().widget, self)
        self._selected = self._tree.get()

    def select(self, widget):
        if widget:
            node = widget.node
            self._selected = node
            node.select(None, True)  # Select node silently to avoid triggering a duplicate selection event
        elif widget is None:
            if self._selected:
                self._selected.deselect()
                self._selected = None

    def on_select(self, widget):
        self.select(widget)

    def on_widget_delete(self, widget, silently=False):
        widget.node.remove()

    def on_widget_restore(self, widget):
        widget.layout.node.add(widget.node)

    def on_widget_layout_change(self, widget):
        node = widget.node
        if widget.layout == self.studio.designer:
            self._tree.insert(None, node)
        else:
            parent = widget.layout.node
            parent.insert(None, node)

    def on_session_clear(self):
        self._tree.clear()

    def on_widget_change(self, old_widget, new_widget=None):
        new_widget = new_widget if new_widget else old_widget
        new_widget.node.widget_modified(new_widget)
コード例 #4
0
ファイル: stylepane.py プロジェクト: mardukbp/Formation
class StylePane(BaseFeature):
    name = "Style pane"
    icon = "edit"
    _defaults = {
        **BaseFeature._defaults,
        "side": "right",
    }

    def __init__(self, master, studio, **cnf):
        super().__init__(master, studio, **cnf)
        self.body = ScrolledFrame(self, **self.style.dark)
        self.body.pack(side="top", fill="both", expand=True)

        self._toggle_btn = Button(self._header,
                                  image=get_icon_image("chevron_down", 15, 15),
                                  **self.style.dark_button,
                                  width=25,
                                  height=25)
        self._toggle_btn.pack(side="right")
        self._toggle_btn.on_click(self._toggle)

        self._search_btn = Button(self._header,
                                  image=get_icon_image("search", 15, 15),
                                  width=25,
                                  height=25,
                                  **self.style.dark_button)
        self._search_btn.pack(side="right")
        self._search_btn.on_click(self.start_search)

        self.groups = []

        self._identity_group = self.add_group(IdentityGroup)
        self._layout_group = self.add_group(LayoutGroup)
        self._attribute_group = self.add_group(AttributeGroup)

        self._empty_frame = Frame(self.body)
        self.show_empty()
        self._current = None
        self._expanded = False

    def create_menu(self):
        return (("command", "Search", get_icon_image("search", 14,
                                                     14), self.start_search,
                 {}), ("command", "Expand all",
                       get_icon_image("chevron_down", 14, 14), self.expand_all,
                       {}), ("command", "Collapse all",
                             get_icon_image("chevron_up", 14,
                                            14), self.collapse_all, {}))

    def add_group(self, group_class) -> StyleGroup:
        if not issubclass(group_class, StyleGroup):
            raise ValueError('type required.')
        group = group_class(self.body.body)
        self.groups.append(group)
        group.pack(side='top', fill='x', pady=4)
        return group

    def show_empty(self):
        self.remove_empty()
        self._empty_frame.place(x=0, y=0, relheight=1, relwidth=1)
        Label(self._empty_frame,
              text="You have not selected any item",
              **self.style.dark_text_passive).place(x=0,
                                                    y=0,
                                                    relheight=1,
                                                    relwidth=1)

    def remove_empty(self):
        self._empty_frame.clear_children()
        self._empty_frame.place_forget()

    def show_loading(self):
        self.remove_empty()
        self._empty_frame.place(x=0, y=0, relheight=1, relwidth=1)
        Label(self._empty_frame,
              text="Loading...",
              **self.style.dark_text_passive).place(x=0,
                                                    y=0,
                                                    relheight=1,
                                                    relwidth=1)

    def styles_for(self, widget):
        self._current = widget
        if widget is None:
            self.show_empty()
            return
        self.show_loading()
        for group in self.groups:
            group.on_widget_change(widget)
        self.remove_empty()
        self.body.update_idletasks()

    def layout_for(self, widget):
        self._layout_group.on_widget_change(widget)

    def on_select(self, widget):
        self.styles_for(widget)

    def on_widget_change(self, old_widget, new_widget=None):
        self.styles_for(new_widget)

    def on_widget_layout_change(self, widget):
        self.layout_for(widget)

    def expand_all(self):
        for group in self.groups:
            group.expand()
        self._expanded = True
        self._toggle_btn.config(image=get_icon_image("chevron_up", 15, 15))

    def clear_all(self):
        for group in self.groups:
            group.clear_children()

    def collapse_all(self):
        for group in self.groups:
            group.collapse()
        self._expanded = False
        self._toggle_btn.config(image=get_icon_image("chevron_down", 15, 15))

    def _toggle(self, *_):
        if not self._expanded:
            self.expand_all()
        else:
            self.collapse_all()

    def __update_frames(self):
        for group in self.groups:
            group.update_state()

    def start_search(self, *_):
        if self._current:
            super().start_search()
            self.body.scroll_to_start()

    def on_search_query(self, query):
        for group in self.groups:
            group.on_search_query(query)
        self.__update_frames()

    def on_search_clear(self):
        for group in self.groups:
            group.on_search_clear()
        # The search bar is being closed and we need to bring everything back
        super().on_search_clear()
コード例 #5
0
ファイル: stylepane.py プロジェクト: ObaraEmmanuel/Formation
class StylePaneFramework:
    def setup_style_pane(self):
        self.body = ScrolledFrame(self, **self.style.surface)
        self.body.pack(side="top", fill="both", expand=True)

        self._toggle_btn = Button(self.get_header(),
                                  image=get_icon_image("chevron_down", 15, 15),
                                  **self.style.button,
                                  width=25,
                                  height=25)
        self._toggle_btn.pack(side="right")
        self._toggle_btn.on_click(self._toggle)

        self._search_btn = Button(self.get_header(),
                                  image=get_icon_image("search", 15, 15),
                                  width=25,
                                  height=25,
                                  **self.style.button)
        self._search_btn.pack(side="right")
        self._search_btn.on_click(self.start_search)

        self.groups = []

        self._empty_frame = Frame(self.body)
        self.show_empty()
        self._current = None
        self._expanded = False
        self._is_loading = False
        self._search_query = None

    def get_header(self):
        raise NotImplementedError()

    @property
    def supported_groups(self):
        return [
            group for group in self.groups
            if group.supports_widget(self._current)
        ]

    def create_menu(self):
        return (("command", "Search", get_icon_image("search", 14,
                                                     14), self.start_search,
                 {}), ("command", "Expand all",
                       get_icon_image("chevron_down", 14, 14), self.expand_all,
                       {}), ("command", "Collapse all",
                             get_icon_image("chevron_up", 14,
                                            14), self.collapse_all, {}))

    def extern_apply(self,
                     group_class,
                     prop,
                     value,
                     widget=None,
                     silent=False):
        for group in self.groups:
            if group.__class__ == group_class:
                group.apply(prop, value, widget, silent)
                return
        raise ValueError(f"Class {group_class.__class__.__name__} not found")

    def last_action(self):
        raise NotImplementedError()

    def new_action(self, action):
        raise NotImplementedError()

    def widget_modified(self, widget):
        raise NotImplementedError()

    def add_group(self, group_class, **kwargs) -> StyleGroup:
        if not issubclass(group_class, StyleGroup):
            raise ValueError('type required.')
        group = group_class(self.body.body, self, **kwargs)
        self.groups.append(group)
        self.show_group(group)
        return group

    def add_group_instance(self, group_instance, show=False):
        if not isinstance(group_instance, StyleGroup):
            raise ValueError('Expected object of type StyleGroup.')
        self.groups.append(group_instance)
        if show:
            self.show_group(group_instance)

    def hide_group(self, group):
        if group.self_positioned:
            group._hide_group()
            return
        group.pack_forget()

    def show_group(self, group):
        if group.self_positioned:
            group._show_group()
            return
        group.pack(side='top', fill='x', pady=12)

    def show_empty(self):
        self.remove_empty()
        self._empty_frame.place(x=0, y=0, relheight=1, relwidth=1)
        Label(self._empty_frame,
              text="You have not selected any item",
              **self.style.text_passive).place(x=0,
                                               y=0,
                                               relheight=1,
                                               relwidth=1)

    def remove_empty(self):
        self._empty_frame.clear_children()
        self._empty_frame.place_forget()

    def show_loading(self):
        if platform_is(LINUX) or self._is_loading:
            # render transitions in linux are very fast and glitch free
            # for other platforms or at least for windows we need to hide the glitching
            return
        self.remove_empty()
        self._empty_frame.place(x=0, y=0, relheight=1, relwidth=1)
        Label(self._empty_frame, text="Loading...",
              **self.style.text_passive).place(x=0,
                                               y=0,
                                               relheight=1,
                                               relwidth=1)
        self._is_loading = True

    def remove_loading(self):
        self.remove_empty()
        self._is_loading = False

    def styles_for(self, widget):
        self._current = widget
        if widget is None:
            self.show_empty()
            return
        for group in self.groups:
            if group.supports_widget(widget):
                self.show_group(group)
                group.on_widget_change(widget)
            else:
                self.hide_group(group)
        self.remove_loading()
        self.body.update_idletasks()

    def layout_for(self, widget):
        for group in self.groups:
            if group.handles_layout:
                group.on_widget_change(widget)
        self.remove_loading()

    def on_select(self, widget):
        self.styles_for(widget)

    def on_widget_change(self, old_widget, new_widget=None):
        if new_widget is None:
            new_widget = old_widget
        self.styles_for(new_widget)

    def on_widget_layout_change(self, widget):
        self.layout_for(widget)

    def expand_all(self):
        for group in self.groups:
            group.expand()
        self._expanded = True
        self._toggle_btn.config(image=get_icon_image("chevron_up", 15, 15))

    def clear_all(self):
        for group in self.groups:
            group.clear_children()

    def collapse_all(self):
        for group in self.groups:
            group.collapse()
        self._expanded = False
        self._toggle_btn.config(image=get_icon_image("chevron_down", 15, 15))

    def _toggle(self, *_):
        if not self._expanded:
            self.expand_all()
        else:
            self.collapse_all()

    def __update_frames(self):
        for group in self.groups:
            group.update_state()

    def start_search(self, *_):
        if self._current:
            super().start_search()
            self.body.scroll_to_start()

    def on_search_query(self, query):
        for group in self.groups:
            group.on_search_query(query)
        self.__update_frames()
        self.body.scroll_to_start()
        self._search_query = query

    def on_search_clear(self):
        for group in self.groups:
            group.on_search_clear()
        # The search bar is being closed and we need to bring everything back
        super().on_search_clear()
        self._search_query = None
コード例 #6
0
class ComponentTree(BaseFeature):
    name = "Component Tree"
    icon = "treeview"

    def __init__(self, master, studio=None, **cnf):
        super().__init__(master, studio, **cnf)
        self._toggle_btn = Button(self._header,
                                  image=get_icon_image("chevron_down", 15, 15),
                                  **self.style.button,
                                  width=25,
                                  height=25)
        self._toggle_btn.pack(side="right")
        self._toggle_btn.on_click(self._toggle)

        self._search_btn = Button(
            self._header,
            **self.style.button,
            image=get_icon_image("search", 15, 15),
            width=25,
            height=25,
        )
        self._search_btn.pack(side="right")
        self._search_btn.on_click(self.start_search)
        self.body = Frame(self, **self.style.surface)
        self.body.pack(side="top", fill="both", expand=True)
        self._empty_label = Label(self.body, **self.style.text_passive)

        self._selected = None
        self._expanded = False
        self._tree = None

    def on_context_switch(self):
        if self._tree:
            self._tree.pack_forget()

        if self.studio.designer:
            self.show_empty(None)
            if self.studio.designer.node:
                self._tree = self.studio.designer.node
            else:
                self._tree = ComponentTreeView(self.body)
                self._tree.on_select(self._trigger_select)
                self.studio.designer.node = self._tree
            self._tree.pack(fill="both", expand=True)
        else:
            self.show_empty("No active Designer")

    def create_menu(self):
        return (("command", "Expand all",
                 get_icon_image("chevron_down", 14, 14), self._expand, {}),
                ("command", "Collapse all",
                 get_icon_image("chevron_up", 14, 14), self._collapse, {}))

    def show_empty(self, text):
        if text:
            self._empty_label.lift()
            self._empty_label.place(x=0, y=0, relwidth=1, relheight=1)
            self._empty_label['text'] = text
        else:
            self._empty_label.place_forget()

    def _expand(self):
        self._tree.expand_all()
        self._toggle_btn.config(image=get_icon_image("chevron_up", 15, 15))
        self._expanded = True

    def _collapse(self):
        self._tree.collapse_all()
        self._toggle_btn.config(image=get_icon_image("chevron_down", 15, 15))
        self._expanded = False

    def _toggle(self, *_):
        if self._expanded:
            self._collapse()
        else:
            self._expand()

    def on_widget_add(self, widget: PseudoWidget, parent=None):
        if parent is None:
            node = self._tree.add_as_node(widget=widget)
        else:
            parent = parent.node
            node = parent.add_as_node(widget=widget)

        # let the designer render the menu for us
        MenuUtils.bind_all_context(
            node, lambda e: self.studio.designer.show_menu(e, widget)
            if self.studio.designer else None)

    def _trigger_select(self):
        if self._selected and self._selected.widget == self._tree.get().widget:
            return
        self.studio.select(self._tree.get().widget, self)
        self._selected = self._tree.get()

    def select(self, widget):
        if widget:
            node = widget.node
            self._selected = node
            node.select(
                None, True
            )  # Select node silently to avoid triggering a duplicate selection event
        elif widget is None:
            if self._selected:
                self._selected.deselect()
                self._selected = None

    def on_select(self, widget):
        self.select(widget)

    def on_widget_delete(self, widget, silently=False):
        widget.node.remove()

    def on_widget_restore(self, widget):
        widget.layout.node.add(widget.node)

    def on_widget_layout_change(self, widget):
        node = widget.node
        if widget.layout == self.studio.designer:
            parent = self._tree
        else:
            parent = widget.layout.node
        if node.parent_node != parent:
            parent.insert(None, node)

    def on_context_close(self, context):
        if hasattr(context, "designer"):
            # delete context's tree
            if hasattr(context.designer, "node") and context.designer.node:
                context.designer.node.destroy()

    def on_session_clear(self):
        self._tree.clear()

    def on_widget_change(self, old_widget, new_widget=None):
        new_widget = new_widget if new_widget else old_widget
        new_widget.node.widget_modified(new_widget)

    def on_search_query(self, query: str):
        self._tree.search(query)

    def on_search_clear(self):
        self._tree.search("")
        super(ComponentTree, self).on_search_clear()
コード例 #7
0
ファイル: updates.py プロジェクト: ObaraEmmanuel/Formation
class Updater(Frame):
    def __init__(self, master):
        super().__init__(master)
        self.config(**self.style.surface)
        self._progress = ProgressBar(self)
        self._progress.pack(side="top", fill="x", padx=20, pady=20)
        self._progress_text = Label(self, **self.style.text_small, anchor="w")
        self._progress_text.pack(side="top", fill="x", padx=20, pady=10)
        self._message = Label(
            self,
            **self.style.text,
            anchor="w",
            compound="left",
            wrap=400,
            justify="left",
            pady=5,
            padx=5,
        )
        self._action_btn = Button(self,
                                  text="Retry",
                                  **self.style.button_highlight,
                                  width=80,
                                  height=25)
        self.extra_info = Text(self,
                               width=40,
                               height=6,
                               state='disabled',
                               font='consolas 10')
        self.pack(fill="both", expand=True)
        self.check_for_update()

    def show_button(self, text, func):
        self._action_btn.config(text=text)
        self._action_btn.on_click(func)
        self._action_btn.pack(side="bottom", anchor="e", pady=5, padx=5)

    def show_progress(self, message):
        self.clear_children()
        self._progress_text.configure(text=message)
        self._progress.pack(side="top", fill="x", padx=20, pady=20)
        self._progress_text.pack(side="top", fill="x", padx=20, pady=10)

    def show_error(self, message, retry_func):
        self.show_error_plain(message)
        self.show_button("Retry", retry_func)

    def show_error_plain(self, message):
        self.show_message(message, MessageDialog.ICON_ERROR)

    def update_found(self, version):
        self.show_info(
            f"New version formation-studio {version} found. Do you want to install?"
        )
        self.show_button("Install", lambda _: self.install(version))

    def show_info(self, message):
        self.show_message(message, MessageDialog.ICON_INFO)

    def show_message(self, message, icon):
        self.clear_children()
        self._message.configure(text=message,
                                image=get_icon_image(icon, 50, 50))
        self._message.pack(side="top", padx=20, pady=10)

    def install(self, version):
        self.show_progress(f"Updating to formation-studio {version}")
        self.upgrade(version)

    @classmethod
    def check(cls, master):
        dialog = MessageDialog(master, cls)
        dialog.title("Formation updater")
        dialog.focus_set()
        return dialog

    @as_thread
    def check_for_update(self, *_):
        self._progress.mode(ProgressBar.INDETERMINATE)
        self.show_progress("Checking for updates ...")
        try:
            content = urlopen(
                "https://pypi.org/pypi/formation-studio/json").read()
            data = json.loads(content)
            ver = data["info"]["version"]
            if ver <= formation.__version__:
                self.show_info("You are using the latest version")
            else:
                self.update_found(ver)
        except URLError:
            self.show_error(
                "Failed to connect. Check your internet connection"
                " and try again.", self.check_for_update)

    @as_thread
    def upgrade(self, version):
        try:
            # run formation cli upgrade command
            proc_info = subprocess.run([sys.executable, "-m", "studio", "-u"],
                                       capture_output=True)
            if proc_info.returncode != 0 or proc_info.stderr:
                self.show_error_plain(
                    "Something went wrong. Failed to upgrade formation-studio"
                    f" to version {version} ,"
                    f" Exited with code: {proc_info.returncode}")
                if proc_info.stderr:
                    self.extra_info.config(state="normal")
                    self.extra_info.pack(side="top",
                                         fill="x",
                                         padx=20,
                                         pady=10)
                    self.extra_info.clear()
                    self.extra_info.set(str(proc_info.stderr))
                    self.extra_info.config(state="disabled")
            else:
                self.show_info(
                    "Upgrade successful. Restart to complete installation")
                self.show_button(
                    "Restart",
                    lambda _: get_routine("STUDIO_RESTART").invoke())
                return
        except Exception as e:
            self.show_error_plain(e)

        self.show_button("Retry", lambda _: self.install(version))