Exemple #1
0
def create_peer_widget(original_text_widget: tkinter.Text,
                       the_widget_that_becomes_a_peer: tkinter.Text) -> None:
    """
    Make sure that *the_widget_that_becomes_a_peer* always has the same content
    as *original_text_widget*.

    For details, see the ``PEER WIDGETS`` section in
    `text(3tk) <https://www.tcl.tk/man/tcl8.7/TkCmd/text.htm>`_.
    This is useful for e.g. :source:`porcupine/plugins/minimap.py`.

    .. warning::
        This does **not** create a red text widget::

            the_widget_that_becomes_a_peer = tkinter.Text(master, background='red')
            create_peer_widget(original_text_widget, the_widget_that_becomes_a_peer)

        This works better::

            the_widget_that_becomes_a_peer = tkinter.Text(master)
            create_peer_widget(original_text_widget, the_widget_that_becomes_a_peer)
            the_widget_that_becomes_a_peer.config(background='red')

        All widget options of *the_widget_that_becomes_a_peer* are lost when
        this function is called. I tried to make this function preserve the
        widget options but that caused weird problems.

    Notes about using :func:`create_peer_widget` and :func:`track_changes`
    together:

        * Call :func:`track_changes` with *original_text_widget*, not with its
          peers. If you get this wrong, you will get a :class:`RuntimeError`.
        * Bind to the ``<<ContentChanged>>`` with the original widget, not with
          the peers. If you get this wrong, your ``<<ContentChanged>>``
          callback won't run.
        * First call :func:`track_changes` and then :func:`create_peer_widget`.
          If you get this wrong, you will get a :class:`RuntimeError`.
    """
    # Peer widgets are weird in tkinter. Text.peer_create takes in a
    # widget Tcl name, and then creates a peer widget with that name.
    # But if you want to create a tkinter widget, then you need to let
    # the tkinter widget to create a Tcl widget with a name chosen by
    # tkinter. That has happened already when this function runs.
    #
    # Can't do .destroy() because that screws up winfo_children(). Each tkinter
    # widget knows its child widgets, and .destroy() would make tkinter no
    # longer know it. Tkinter's winfo_children() ignores unknown widgets.
    the_widget_that_becomes_a_peer.tk.call("destroy",
                                           the_widget_that_becomes_a_peer)
    original_text_widget.peer_create(the_widget_that_becomes_a_peer)

    change_tracker = _change_trackers.get(original_text_widget)
    if change_tracker is not None:
        change_tracker.setup(the_widget_that_becomes_a_peer)
Exemple #2
0
def create_peer_widget(
    original_text_widget: tkinter.Text,
    the_widget_that_becomes_a_peer: tkinter.Text,
) -> None:
    """
    Make sure that *the_widget_that_becomes_a_peer* always has the same content
    as *original_text_widget*.

    For details, see the ``PEER WIDGETS`` section in
    `text(3tk) <https://www.tcl.tk/man/tcl8.7/TkCmd/text.htm>`_.
    This is useful for e.g. :source:`porcupine/plugins/overview.py`.

    .. warning::
        This does **not** create a red text widget::

            the_widget_that_becomes_a_peer = tkinter.Text(master, background='red')
            create_peer_widget(original_text_widget, the_widget_that_becomes_a_peer)

        This works better::

            the_widget_that_becomes_a_peer = tkinter.Text(master)
            create_peer_widget(original_text_widget, the_widget_that_becomes_a_peer)
            the_widget_that_becomes_a_peer.config(background='red')

        All widget options of *the_widget_that_becomes_a_peer* are lost when
        this function is called. I tried to make this function preserve the
        widget options but that caused weird problems.

    Notes about using :func:`create_peer_widget` and :func:`track_changes`
    together:

        * Bind to the ``<<ContentChanged>>`` event of *original_text_widget*,
          not *the_widget_that_becomes_a_peer*. **Make sure to get this right**
          to avoid issues with ``<<ContentChanged>>`` sometimes not triggering.
          If you create peers of peers, then bind to the most original widget,
          not any of its peers.
        * First call :func:`track_changes` and then :func:`create_peer_widget`.
          You will get a :class:`RuntimeError` if you screw this up.
    """
    # Peer widgets are weird in tkinter. Text.peer_create takes in a
    # widget Tcl name, and then creates a peer widget with that name.
    # But if you want to create a tkinter widget, then you need to let
    # the tkinter widget to create a Tcl widget with a name chosen by
    # tkinter. That has happened above with the super() call. However,
    # rest of what happens in this __init__ method must do stuff to the
    # peer widget, rather than the widget that tkinter created.
    #
    # Can't do .destroy() because that screws up winfo_children(). Each tkinter
    # widget knows its child widgets, and .destroy() would make tkinter no
    # longer know it. Tkinter's winfo_children() also ignores anything not
    # known to tkinter.
    #
    # issues related to type ignore comments:
    #    https://github.com/python/mypy/issues/6552
    #    https://github.com/python/typeshed/issues/4409
    the_widget_that_becomes_a_peer.tk.call('destroy', the_widget_that_becomes_a_peer)
    original_text_widget.peer_create(the_widget_that_becomes_a_peer)

    if original_text_widget in _change_tracked_widgets:
        track_changes(the_widget_that_becomes_a_peer)
        utils.forward_event('<<ContentChanged>>', the_widget_that_becomes_a_peer, original_text_widget)