Ejemplo n.º 1
0
    def __init__(self, master: tk.Misc, blockSize: int, configFilePath: str,
                 configs: dict):
        super().__init__(master)
        master.update()
        self.master = master
        self.blockSize = blockSize
        self.pack()
        self.figures = []
        self.isSaveFig = False
        self.baseDir = os.path.dirname(__file__)
        # 配置文件
        self.configFilePath = self.baseDir + configFilePath
        self.fileConfigs = configs

        # 背景颜色
        self.fileFramebg = '#009688'
        self.plotFramebg = '#FFFFFF'
        self.btnbg = '#00BCD4'
        self.btnfg = '#FFFFFF'
        self.fileLabfg = '#FFFFFF'
        self.plotLabfg = '#000000'
        self.plotEntrybg = '#BDBDBD'
        self.btnActivebg = '#00796B'

        self.btnFont = ('微软雅黑', 11, 'bold')
        self.labFont = ('微软雅黑', 11, 'bold')
        self.entryFont = ('微软雅黑', 10, 'bold')
        # 生成控件
        self.creatWidgets()
        self.initWidget()

        pass
Ejemplo n.º 2
0
    def __init__(
        self,
        parent: tk.Misc,
        *,
        tool_frame: tk.Frame,
        tool_img: str,
        menu_bar: tk.Menu,
        tool_col: int=0,
        title: str='',
        resize_x: bool=False,
        resize_y: bool=False,
        name: str='',
    ) -> None:
        self.visible = tk.BooleanVar(parent, True)
        self.win_name = name
        self.allow_snap = False
        self.can_save = False
        self.parent = parent
        self.relX = 0
        self.relY = 0
        self.can_resize_x = resize_x
        self.can_resize_y = resize_y
        super().__init__(parent, name='pane_' + name)
        self.withdraw()  # Hide by default

        self.tool_button = make_tool_button(
            frame=tool_frame,
            img=tool_img,
            command=self._toggle_win,
        )
        self.tool_button.state(('pressed',))
        self.tool_button.grid(
            row=0,
            column=tool_col,
            # Contract the spacing to allow the icons to fit.
            padx=(2 if utils.MAC else (5, 2)),
        )
        tooltip.add_tooltip(
            self.tool_button,
            text=gettext('Hide/Show the "{}" window.').format(title))
        menu_bar.add_checkbutton(
            label=title,
            variable=self.visible,
            command=self._set_state_from_menu,
        )

        self.transient(master=parent)
        self.resizable(resize_x, resize_y)
        self.title(title)
        tk_tools.set_window_icon(self)

        self.protocol("WM_DELETE_WINDOW", self.hide_win)
        parent.bind('<Configure>', self.follow_main, add=True)
        self.bind('<Configure>', self.snap_win)
        self.bind('<FocusIn>', self.enable_snap)
Ejemplo n.º 3
0
    def __init__(
        self, widget: tkinter.Misc, sequence: str, func: Callable[[EventWithData], BreakOrNone]
    ) -> None:
        self._widget = widget
        self._sequence = sequence

        not_bound_commands = widget.bind(sequence)
        self._tcl_command = bind_with_data(widget, sequence, func, add=True)
        bound_commands = widget.bind(sequence)
        assert bound_commands.startswith(not_bound_commands)
        self._new_things = bound_commands[len(not_bound_commands) :]
Ejemplo n.º 4
0
def use_pygments_theme(
        widget: tkinter.Misc,
        callback: Optional[Callable[[str, str], None]] = None) -> None:
    """
    Configure *widget* to use the colors of the Pygments theme whenever the
    currently selected theme changes (see :mod:`porcupine.settings`).
    Porcupine does that automatically for the ``textwidget`` of each
    :class:`~porcupine.tabs.FileTab`.

    If you don't specify a *callback*, then ``widget`` must be a :class:`tkinter.Text` widget.
    If you specify a callback, then it will be called like
    ``callback(foreground_color, background_color)``, and the type of the widget doesn't matter.

    .. seealso::
        This function is used in :source:`porcupine/plugins/linenumbers.py`.
        Syntax highlighting is implemented in
        :source:`porcupine/plugins/highlight.py`.
    """
    def on_style_changed(junk: object = None) -> None:
        style = styles.get_style_by_name(settings.get("pygments_style", str))
        bg = style.background_color

        # yes, style.default_style can be '#rrggbb', '' or nonexistent
        # this is undocumented
        #
        #   >>> from pygments.styles import *
        #   >>> [getattr(get_style_by_name(name), 'default_style', '???')
        #   ...  for name in get_all_styles()]
        #   ['', '', '', '', '', '', '???', '???', '', '', '', '',
        #    '???', '???', '', '#cccccc', '', '', '???', '', '', '', '',
        #    '#222222', '', '', '', '???', '']
        fg = getattr(style, "default_style", "") or utils.invert_color(bg)
        if callback is None:
            assert isinstance(widget, tkinter.Text)
            widget.config(
                foreground=fg,
                background=bg,
                insertbackground=fg,  # cursor color
                selectforeground=bg,
                selectbackground=fg,
            )
        else:
            callback(fg, bg)

    widget.bind("<<SettingChanged:pygments_style>>",
                on_style_changed,
                add=True)
    on_style_changed()
Ejemplo n.º 5
0
def get_children_recursively(
    parent: tkinter.Misc, *, include_parent: bool = False
) -> Iterator[tkinter.Misc]:
    if include_parent:
        yield parent
    for child in parent.winfo_children():
        yield from get_children_recursively(child, include_parent=True)
Ejemplo n.º 6
0
def bind_with_data(
    widget: tkinter.Misc,
    sequence: str,
    callback: Callable[[EventWithData], Optional[str]],
    add: bool = False,
) -> str:
    """
    Like ``widget.bind(sequence, callback)``, but supports the ``data``
    argument of ``event_generate()``. Note that the callback takes an argument
    of type :class:`EventWithData` rather than a usual ``tkinter.Event[tkinter.Misc]``.

    Here's an example::

        from porcupine import utils

        def handle_event(event: utils.EventWithData):
            print(event.data_string)

        utils.bind_with_data(some_widget, '<<Thingy>>', handle_event, add=True)

        # this prints 'wut wut'
        some_widget.event_generate('<<Thingy>>', data='wut wut')

    Note that everything is a string in Tcl, so tkinter ``str()``'s the data.
    """
    # tkinter creates event objects normally and appends them to the
    # deque, then run_callback() adds data_blablabla attributes to the
    # event objects and runs callback(event)
    #
    # TODO: is it possible to do this without a deque?
    event_objects: Deque[Union[tkinter.Event[tkinter.Misc], EventWithData]] = collections.deque()
    widget.bind(sequence, event_objects.append, add=add)

    def run_the_callback(data_string: str) -> Optional[str]:
        event = event_objects.popleft()
        event.__class__ = EventWithData  # evil haxor muhaha
        assert isinstance(event, EventWithData)
        event.data_string = data_string
        return callback(event)  # may return 'break'

    # tkinter's bind() ignores the add argument when the callback is a
    # string :(
    funcname = widget.register(run_the_callback)
    widget.tk.eval(
        'bind %s %s {+ if {"[%s %%d]" == "break"} break }' % (widget, sequence, funcname)
    )
    return funcname
Ejemplo n.º 7
0
    def create_widgets(self, parent: tk.Misc):
        # Port
        lbl_port = tk.Label(parent, text="Port:")
        lbl_port.grid(row=0, column=0)
        txt_port = tk.Entry(parent, textvariable=self.port_var)
        txt_port.grid(row=0, column=1)

        # Buttons
        cmd_server = tk.Button(parent, text="Start Server", command=self.start_server_clicked)
        cmd_server.grid(row=0, column=2)
        cmd_openbrowser = tk.Button(parent, text="Open Web Browser", command=self.open_webbrowser_clicked)
        cmd_openbrowser.grid(row=0, column=3)

        # Output
        txt_detail = BindableTextArea(parent, textvariable=self.detail_var, width=40, height=20)
        txt_detail.grid(row=1, column=0, columnspan=4, sticky="NSEW")
        parent.grid_rowconfigure(1, weight=1)
Ejemplo n.º 8
0
 def _enable_resizing(widget: tkinter.Misc,
                      row_weight: int = 1,
                      column_weight: int = 1) -> None:
     """
     Enable a widget and all elements inside of it that have been placed
     using the grid method to resize. This must be called AFTER all widgets
     have been placed.
     :param widget: the widget to enable resizing on
     :param row_weight: the rate at which the rows expands. default 1
     :param column_weight: the rate at which the columns expands. default 1
     :return: None
     """
     for child in widget.grid_slaves():
         grid_info = child.grid_info()
         row, column = {grid_info["row"]}, {grid_info["column"]}
         widget.rowconfigure(row, weight=row_weight)
         widget.columnconfigure(column, weight=column_weight)
         ResizeableWindow._enable_resizing(child)
Ejemplo n.º 9
0
def add_tooltip(
    targ_widget: tk.Misc,
    text: str='',
    image: img.Handle=None,
    delay: int=500,
    show_when_disabled: bool=False,
) -> None:
    """Add a tooltip to the specified widget.

    delay is the amount of milliseconds of hovering needed to show the
    tooltip.
    text is the initial text for the tooltip.
    If set, image is also shown on the tooltip.
    If show_when_disabled is false, no context menu will be shown if the
    target widget is disabled.
    """
    targ_widget._bee2_tooltip_text = text
    targ_widget._bee2_tooltip_img = image

    event_id = None  # The id of the enter event, so we can cancel it.

    # Only check for disabled widgets if the widget actually has a state,
    # and the user hasn't disabled the functionality
    check_disabled = hasattr(targ_widget, 'instate') and not show_when_disabled

    def after_complete(x, y):
        """Remove the id and show the tooltip after the delay."""
        nonlocal event_id
        event_id = None  # Invalidate event id
        # noinspection PyUnresolvedReferences, PyProtectedMember
        if targ_widget._bee2_tooltip_text or targ_widget._bee2_tooltip_img is not None:
            _show(targ_widget, x, y)

    def enter_handler(event):
        """Schedule showing the tooltip."""
        nonlocal event_id
        # noinspection PyUnresolvedReferences, PyProtectedMember
        if targ_widget._bee2_tooltip_text or targ_widget._bee2_tooltip_img is not None:
            # We know it has this method from above!
            # noinspection PyUnresolvedReferences
            if check_disabled and not targ_widget.instate(('!disabled',)):
                return
            event_id = TK_ROOT.after(
                delay,
                after_complete,
                event.x_root, event.y_root,
            )

    def exit_handler(e):
        """When the user leaves, cancel the event."""
        # We only want to cancel if the event hasn't expired already
        nonlocal event_id
        window.withdraw()
        if event_id is not None:
            TK_ROOT.after_cancel(
                event_id
            )

    targ_widget.bind('<Enter>', enter_handler)
    targ_widget.bind('<Leave>', exit_handler)
Ejemplo n.º 10
0
def add_tooltip(
    targ_widget: tk.Misc,
    text: str='',
    image: tk.Image=None,
    delay: int=500,
    show_when_disabled: bool=False,
) -> None:
    """Add a tooltip to the specified widget.

    delay is the amount of milliseconds of hovering needed to show the
    tooltip.
    text is the initial text for the tooltip.
    If set, image is also shown on the tooltip.
    If show_when_disabled is false, no context menu will be shown if the
    target widget is disabled.
    """
    targ_widget._bee2_tooltip_text = text
    targ_widget._bee2_tooltip_img = image

    event_id = None  # The id of the enter event, so we can cancel it.

    # Only check for disabled widgets if the widget actually has a state,
    # and the user hasn't disabled the functionality
    check_disabled = hasattr(targ_widget, 'instate') and not show_when_disabled

    def after_complete(x, y):
        """Remove the id and show the tooltip after the delay."""
        nonlocal event_id
        event_id = None  # Invalidate event id
        # noinspection PyUnresolvedReferences, PyProtectedMember
        if targ_widget._bee2_tooltip_text or targ_widget._bee2_tooltip_img is not None:
            _show(targ_widget, x, y)

    def enter_handler(event):
        """Schedule showing the tooltip."""
        nonlocal event_id
        # noinspection PyUnresolvedReferences, PyProtectedMember
        if targ_widget._bee2_tooltip_text or targ_widget._bee2_tooltip_img is not None:
            # We know it has this method from above!
            # noinspection PyUnresolvedReferences
            if check_disabled and not targ_widget.instate(('!disabled',)):
                return
            event_id = TK_ROOT.after(
                delay,
                after_complete,
                event.x_root, event.y_root,
            )

    def exit_handler(e):
        """When the user leaves, cancel the event."""
        # We only want to cancel if the event hasn't expired already
        nonlocal event_id
        window.withdraw()
        if event_id is not None:
            TK_ROOT.after_cancel(
                event_id
            )

    targ_widget.bind('<Enter>', enter_handler)
    targ_widget.bind('<Leave>', exit_handler)
Ejemplo n.º 11
0
    def _bind_tree(self,
                   widget: tk.Misc,
                   event: str,
                   callback: callable,
                   add: str = "") -> None:
        """Binds an event to a widget and all its descendants recursively.

        Parameters
        ----------
        widget : tk.Misc
            The widget to bind the callback to.
        event : str
            The event to bind to the widget.
        callback : callable
            The callback to call on event.
        add : str, default ""
            Specifies whether callback will be called additionally ("+") to the other bound function
            or whether it will replace the previous function ("").
        """

        widget.bind(event, callback, add)

        for child in widget.winfo_children():
            self._bind_tree(child, event, callback, add)
Ejemplo n.º 12
0
 def bounds_from_object(obj: tk.Misc):
     """
     Generate a bounding box for a widget relative to its parent which can then be used to position the highlight
     or by any other position dependent action.
     :param obj: a tk object
     :return:
     """
     obj.update_idletasks()
     x1 = obj.winfo_x()
     y1 = obj.winfo_y()
     x2 = obj.winfo_width() + x1
     y2 = obj.winfo_height() + y1
     return x1, y1, x2, y2
Ejemplo n.º 13
0
def show(widget: tk.Misc, text, mouse_x, mouse_y):
    """Show the context window."""
    context_label['text'] = text
    window.deiconify()
    window.update_idletasks()
    window.lift()

    # We're going to position tooltips towards the center of the main window.
    # That way they don't tend to stick out, even in multi-window setups.

    # To decide where to put the tooltip, we first want the center of the
    # main window.
    cent_x = TK_ROOT.winfo_rootx() + TK_ROOT.winfo_width() / 2
    cent_y = TK_ROOT.winfo_rooty() + TK_ROOT.winfo_height() / 2

    x_centered = y_centered = True

    # If the widget is smaller than the context window, always center.
    if widget.winfo_width() > window.winfo_width():
        if cent_x > mouse_x + CENT_DIST:
            # Left of center, so place right of the target
            x = widget.winfo_rootx() + widget.winfo_width() + PADDING
            x_centered = False
        elif cent_x < mouse_x - CENT_DIST:
            # Right of center, so place left of the target
            x = widget.winfo_rootx() - window.winfo_width() - PADDING
            x_centered = False

    if widget.winfo_height() > window.winfo_height():
        if cent_y > mouse_y + CENT_DIST:
            # Above center, so place below target
            y = widget.winfo_rooty() + widget.winfo_height() + PADDING
            y_centered = False
        elif cent_y < mouse_y - CENT_DIST:
            # Below center, so place above target
            y = widget.winfo_rooty() - window.winfo_height() - PADDING
            y_centered = False

    if x_centered:  # Center horizontally
        x = (
            widget.winfo_rootx() +
            (widget.winfo_width() - window.winfo_width()) // 2
        )

    if y_centered:
        y = (
            widget.winfo_rooty() +
            (widget.winfo_height() - window.winfo_height()) // 2
        )

        # If both X and Y are centered, the tooltip will appear on top of
        # the mouse and immediately hide. Offset it to fix that.
        if x_centered:
            if mouse_y < cent_y:
                y = widget.winfo_rooty() + widget.winfo_height() + PADDING
            else:
                y = widget.winfo_rooty() - window.winfo_height() - PADDING

    window.geometry('+{}+{}'.format(int(x), int(y)))
Ejemplo n.º 14
0
def __clearBind(item: tk.Misc):
    for sequence in item_binds[item]:
        item.unbind(sequence)
    item_binds[item].clear()
Ejemplo n.º 15
0
def set_tooltip(widget: tk.Misc, text: str='', image: img.Handle=None):
    """Change the tooltip for a widget."""
    widget._bee2_tooltip_text = text
    widget._bee2_tooltip_img = image
Ejemplo n.º 16
0
 def add_cascade(widget: tk.Misc, menu: tk.Menu, label: str) -> None:
     widget.add_cascade(menu=menu, label=label)
Ejemplo n.º 17
0
def _show(widget: tk.Misc, mouse_x, mouse_y) -> None:
    """Show the context window."""
    # noinspection PyUnresolvedReferences, PyProtectedMember
    context_label['text'] = widget._bee2_tooltip_text
    # noinspection PyUnresolvedReferences, PyProtectedMember
    context_label['image'] = widget._bee2_tooltip_img

    window.deiconify()
    window.update_idletasks()
    window.lift()

    # We're going to position tooltips towards the center of the main window.
    # That way they don't tend to stick out, even in multi-window setups.

    # To decide where to put the tooltip, we first want the center of the
    # main window.
    cent_x = TK_ROOT.winfo_rootx() + TK_ROOT.winfo_width() / 2
    cent_y = TK_ROOT.winfo_rooty() + TK_ROOT.winfo_height() / 2

    x_centered = y_centered = True

    # If the widget is smaller than the context window, always center.
    if widget.winfo_width() > window.winfo_width():
        if cent_x > mouse_x + CENT_DIST:
            # Left of center, so place right of the target
            x = widget.winfo_rootx() + widget.winfo_width() + PADDING
            x_centered = False
        elif cent_x < mouse_x - CENT_DIST:
            # Right of center, so place left of the target
            x = widget.winfo_rootx() - window.winfo_width() - PADDING
            x_centered = False

    if widget.winfo_height() > window.winfo_height():
        if cent_y > mouse_y + CENT_DIST:
            # Above center, so place below target
            y = widget.winfo_rooty() + widget.winfo_height() + PADDING
            y_centered = False
        elif cent_y < mouse_y - CENT_DIST:
            # Below center, so place above target
            y = widget.winfo_rooty() - window.winfo_height() - PADDING
            y_centered = False

    if x_centered:  # Center horizontally
        x = (
            widget.winfo_rootx() +
            (widget.winfo_width() - window.winfo_width()) // 2
        )

    if y_centered:
        y = (
            widget.winfo_rooty() +
            (widget.winfo_height() - window.winfo_height()) // 2
        )

        # If both X and Y are centered, the tooltip will appear on top of
        # the mouse and immediately hide. Offset it to fix that.
        if x_centered:
            if mouse_y < cent_y:
                y = widget.winfo_rooty() + widget.winfo_height() + PADDING
            else:
                y = widget.winfo_rooty() - window.winfo_height() - PADDING

    window.geometry('+{}+{}'.format(int(x), int(y)))
Ejemplo n.º 18
0
def set_tooltip(widget: tk.Misc, text: str='', image: tk.Image=None):
    """Change the tooltip for a widget."""
    widget._bee2_tooltip_text = text
    widget._bee2_tooltip_img = image