Open main menu

Gramps β

GEPS 044: Replace Deprecated Gtk.UIManager

Revision as of 15:14, 16 June 2018 by Prculley (talk | contribs) (Issues)

Goal: Rewrite UIManager code to avoid using deprecated methods

I take this goal to mean upgrading several of the currently used Gtk techniques that have been deprecated to the more modern methods. After looking around the web, it appears that there are few write-ups on how this might be done. The best advice available I found is summarized below.

  • Gtk.UIManager is deprecated, use Gtk.Builder instead. UIManager was used to define many of the menus for Gramps using a specialized syntax. Gtk.Builder utilizes an XML input and syntax to define the menus.
  • Gtk.Action is deprecated, use GLib.SimpleAction. Actions are used to separate the Menu items (labels, accelerator, Mnemonics etc.) from what they do.
  • Gtk.ActionGroup is deprecated, use GLib.SimpleActionGroup. The original idea of ActionGroup was to group related menu items together; Gramps used this mechanism to enable/disable groups of menu items when they were not applicable, for instance most menu items were disabled (invisible) when no tree was opened. Sometimes the groups were set insensitive (greyed out) as well.

The new SimpleActionGroup can NOT be used in this way.

The new mechanisms seem to require the use of Gtk.Application and Gtk.ApplicationWindow (at least to avoid other warnings). Using these will involve some changes to our startup code and main window code.

Gtk.Application includes the possibly of a multi document type of interface, potentially with several trees open at once. I propose that for initial work, we do NOT support this. I propose we continue to operate Gramps as a single instance application, where attempts to start another instance only bring the GUI to the front.

Gtk.Application also has several types of support for CLI. For this GEPS I intend to continue letting Gramps deal with the CLI before starting the Gtk.Application (when the GUI is needed). The Gtk.Application will not deal with CLI parameters.

Gtk.Application uses a (hopefully unique) application name to manage its features. I propose that we use "org.gramps-project.Gramps" (which is a valid name according to the rules).

The main goal is to utilize the new techniques instead of the deprecated ones. An to do this with minimal (hopefully no) changes to the GUI or functionality of Gramps.


Contents

Issues

Code changes to support this proposal will have to be made to both Gramps, and its Addons, and synchronized to allow the Addon Views to work correctly. So there will be a minimum of two PRs, one for Gramps, one for the Addons that have to work in concert. For the user, this will have to be part of a larger upgrade, probably a 5.x (5.1?) with the addon repository having a gramps51 branch, all with the same release timing.

In order to support some of our accelerators (the ones that don't have a menu entry, like view switching '<ctrl>p' etc.) it seems we should use Gtk.Application.set_accels_for_action. However this requires Gtk 3.12, we are currently specifying Gtk 3.10 as our base requirement. It may be possible to use Gtk.Application.add_accelerator instead, however it has been deprecated at 3.14.

See 5.1_Roadmap#Dependency_upgrades

Icons next to some of our Menu items are not supported by the GIO menu items. I note that these have been gone from recent Gtk versions anyway. So only users who use older Gtk versions should see a difference.

Proposals

A new Gramps UIManager class, singleton, which operates the main menu and toolbar, making appropriate changes for different views, modes etc. A new Gramps ActionGroup class which tracks the items in an action group and its name. At the moment, this doesn't have any methods, other than __init__, the UIManager contains all the code that operates on this class.

Following are the headers for these proposed Classes/methods. These should be regarded as preliminary, subject to change as coding develops.

class ActionGroup():
    """ This class represents a group of actions that con be manipulated
    together.
    """
    def __init__(self, name, actionlist):
        """
        @param name: the action group name, used to match to the 'groups'
                     attribute in the ui xml.
        @type name: string
        @type actionlist: list
        @param actionlist: the list of actions to add
            The list contains tuples with the following contents:
            string: Action Name
            method: signal callback function
            state: initial state for stateful actions.
                'True' or 'False': the action is interpreted as a checkbox.
                'None': non stateful action (optional)
                'string': the action is interpreted as a Radio button
                '0': the int '0' the action is non stateful but is an
                     accelerator (the name is also the accelerator value)
        """

class UIManager():
    """
    This is Gramps UIManager, it is designed to replace the deprecated Gtk
    UIManager.  The replacement is not exact, but performs similar
    functions, in some case with the same method names and parameters.
    It is designed to be a singleton, responsible only for Gramps main
    window menus and toolbar.
    """

    def __init__(self, app, initial_xml):
        """
        @param app: Gramps Gtk.Application reference
        @type app: Gtk.Application
        @param initial_xml: Initial (primary) XML string for Gramps menus and
            toolbar
        @type changexml: string

        The xml is basically Gtk Builder xml, in particular the various menu
        and toolbar elements.  It is possible to add other elements as well.
        The xml this supports has been extended in two ways;
        1) there is an added "groups=" attribute to elements.  This
           attribute associates the element with one or more named ActionGroups
           for making the element visible or not.  If 'groups' is missing, the
           element will be shown as long as enclosing elements are shown.  The
           element will be shown if the group is present and considered visible
           by the uimanager.  If more than one group is needed, they should be
           separated by a space.
        2) there is an added <placeholder> tag supported; this is used to mark
           a place where merged UI XML can be inserted.  During the update_menu
           processing, elements enclosed in this tag pair are promoted to the
           level of the placeholder tag, and the placeholder tag is removed.

        Note that any elements can be merged (replaced) by the
        add_ui_from_string method, not just placeholders.  This works by
        matching the "id=" attribute on the element, and replacing the
        original element with the one from the add method.
        """

    def update_menu(self, init=False):
        """ This updates the menus and toolbar when there is a change in the
        ui; any addition or removal or set_visible operation needs to call
        this.
        @param init: When True, this is first call and we set the builder
                     toolbar and menu to the application.
                     When False, we update the menus and toolbar
        @type init: bool
        """

    def add_ui_from_string(self, changexml):
        """ This performs a merge operation on the xml elements that have
        matching 'id's between the current ui xml and change xml strings.
        The 'change' is a list of xml strings used to replace
        matching elements in the current xml.

        There MUST one and only one matching id in the orig xml.
        @param changexml: list of xml fragments to merge into main
        @type changexml: list
        @return: changexml
        """

    def remove_ui(self, change_xml):
        """ This removes the 'change_xml' from the current ui xml.  It works on
        any element with matching 'id', the actual element remains but any
        children are removed.
        The 'change' is a list of xml strings originally used to replace
        matching elements in the current ui xml.
        @param changexml: list of xml fragments to remove from main
        @type changexml: list
        """

    def get_widget(self, obj):
        """ Get the object from the builder.
        @param obj: the widget to get
        @type obj: string
        @return: the object
        """

    def insert_action_group(self, group, pos):
        """
        This inserts (actually overwrites any matching actions) the action
        group's actions to the app.

        @param group: the action group
        @type group: ActionGroup
        @param pos: the position of the action group (not used)
        @type pos: int
        """

    def remove_action_group(self, group):
        """ This removes the ActionGroup from the UIManager

        @param group: the action group
        @type group: ActionGroup
        """

    def get_action_groups(self):
        """ This returns a list of action Groups installed into the UIManager.
        @return: list of groups
        """

    def set_action_group_sensitive(self, group, value):
        """ This sets an ActionGroup enabled or disabled.  A disabled action
        will be greyed out in the UI.

        @param group: the action group
        @type group: ActionGroup
        @param value: the state of the group
        @type value: bool
        """

    def set_action_group_visible(self, group, value):
        """ This sets an ActionGroup visible and enabled or invisible and
        disabled.

        @param group: the action group
        @type group: ActionGroup
        @param value: the state of the group
        @type value: bool
        """

Questions

Comments

The StyledTextEditor used Gtk.UIManager etc. to implement its toolbar and menu. I expect it is easier to re-code this to use the new methods directly, rather than generalizing the proposed Gramps UIManager class to handle multiple instances on different Windows.

The EditPrimary and EditPerson classes used Gtk.UIManager etc. to implement its popup menu. I expect it is easier to re-code this to use the new methods directly, rather than generalizing the proposed Gramps UIManager class to handle multiple instances on different Windows.