示例#1
0
    def unmap_cb(self, *junk):
        GLib.source_remove(self._timer_id)

        self.app.doc.tdw.disconnect_by_func(self.event_cb)
        self.app.drawWindow.disconnect_by_func(self.event_cb)

        logger.info('Event statistics disabled.')
    def _build_ui(self):
        """Builds the UI from ``brusheditor.glade``"""
        ui_dir = os.path.dirname(os.path.abspath(__file__))
        ui_path = os.path.join(ui_dir, self._UI_DEFINITION_FILE)
        with open(ui_path, 'r') as ui_fp:
            ui_xml = ui_fp.read()
        self._builder.add_from_string(ui_xml)
        self._populate_inputs(ui_xml)
        self._populate_settings_treestore()
        self._builder.connect_signals(self)
        for inp in brushsettings.inputs:
            grid = self._builder.get_object("by%s_curve_grid" % inp.name)
            GLib.idle_add(grid.hide)
            curve = self._builder.get_object("by%s_curve" % inp.name)

            def _curve_changed_cb(curve, i=inp):
                self._update_brush_from_input_widgets(i)

            curve.changed_cb = _curve_changed_cb
            btn = self._builder.get_object("by%s_reset_button" % inp.name)
            btn.connect("clicked", self.input_adj_reset_button_clicked_cb, inp)
        # Certain actions must be coordinated via a real app instance
        if not self.app:
            action_buttons = [
                "clone_button", "rename_button", "edit_icon_button",
                "delete_button", "live_update_checkbutton", "save_button"
            ]
            for b_name in action_buttons:
                w = self._builder.get_object(b_name)
                w.set_sensitive(False)
示例#3
0
 def _delete_cb(self, widget):
     """Properties dialog delete callback"""
     self._dialog.hide()
     name = brushmanager.translate_group_name(self._group)
     msg = C_(
         "brush group delete",
         u"Really delete group \u201C{group_name}\u201D?",
     ).format(group_name=name, )
     bm = self._app.brushmanager
     if not dialogs.confirm(self, msg):
         return
     bm.delete_group(self._group)
     if self._group not in bm.groups:
         GLib.idle_add(
             self._remove_panel_idle_cb,
             self.__gtype_name__,
             (self._group, ),
         )
         return
     # Special groups like "Deleted" cannot be deleted,
     # but the error message is very confusing in that case...
     msg = C_(
         "brush group delete",
         u"Could not delete group \u201C{group_name}\u201D.\n"
         u"Some special groups cannot be deleted.",
     ).format(group_name=name, )
     dialogs.error(self, msg)
示例#4
0
 def _cancel_autoreveal_timeout(self):
     """Cancels any pending auto-reveal"""
     if not self._autoreveal_timeout:
         return
     for timer in self._autoreveal_timeout:
         GLib.source_remove(timer)
     self._autoreveal_timeout = []
示例#5
0
 def _complete_initial_layout(self):
     """Finish initial layout; called after toplevel win is positioned"""
     # Init tool group sizes by setting vpaned positions
     for paned in self._get_paneds():
         if paned._initial_divider_position:
             pos = paned._initial_divider_position
             GLib.idle_add(paned.set_position, pos)
示例#6
0
    def activate_from_button_event(self, event):
        """Activate during handling of a GdkEventButton (press/release)

        If the event is a button press, then the grab will start
        immediately, begin updating immediately, and will terminate by
        the release of the initiating button.

        If the event is a button release, then the grab start will be
        deferred to start in an idle handler. When the grab starts, it
        won't begin updating until the user clicks button 1 (and only
        button 1), and it will only be terminated with a button1
        release. This covers the case of events delivered to "clicked"
        signal handlers

        """
        if event.type == Gdk.EventType.BUTTON_PRESS:
            logger.debug("Starting picking grab")
            has_button_info, button_num = event.get_button()
            if not has_button_info:
                return
            self._start_grab(event.device, event.time, button_num)
        elif event.type == Gdk.EventType.BUTTON_RELEASE:
            logger.debug("Queueing picking grab")
            GLib.idle_add(
                self._start_grab,
                event.device,
                event.time,
                None,
            )
示例#7
0
 def _configure_cb(self, widget, event):
     """Internal: Update size and prefs when window is adjusted"""
     # Constrain window to fit on its current monitor, if possible.
     screen = event.get_screen()
     mon = screen.get_monitor_at_point(event.x, event.y)
     mon_geom = screen.get_monitor_geometry(mon)
     # Constrain width and height
     w = clamp(int(event.width), self.MIN_WIDTH, self.MAX_WIDTH)
     h = clamp(int(event.height), self.MIN_HEIGHT, self.MAX_HEIGHT)
     # Constrain position
     x, y = event.x, event.y
     if y + h > mon_geom.y + mon_geom.height:
         y = mon_geom.y + mon_geom.height - h
     if x + w > mon_geom.x + mon_geom.width:
         x = mon_geom.x + mon_geom.width - w
     if x < mon_geom.x:
         x = mon_geom.x
     if y < mon_geom.y:
         y = mon_geom.y
     event_size = (event.x, event.y, event.width, event.height)
     ex, ey, ew, eh = [int(c) for c in event_size]
     x, y, w, h = [int(c) for c in (x, y, w, h)]
     if not self._corrected_pos:
         if (x, y) != (ex, ey):
             GLib.idle_add(self.move, x, y)
         if (w, h) != (ew, eh):
             GLib.idle_add(self.resize, w, h)
         self._corrected_pos = True
     # Record size
     self._size = (x, y, w, h)
     self.app.preferences[self._prefs_size_key] = (w, h)
示例#8
0
    def target(self, targ):
        inkmode, cn_idx = targ
        inkmode_ref = None
        if inkmode:
            inkmode_ref = weakref.ref(inkmode)
        self._target = (inkmode_ref, cn_idx)

        GLib.idle_add(self._update_ui_for_current_target)
示例#9
0
 def stop(self):
     """Immediately stop processing and clear the queue."""
     if self._idle_id:
         GLib.source_remove(self._idle_id)
         self._idle_id = None
     self._queue.clear()
     assert self._idle_id is None
     assert len(self._queue) == 0
示例#10
0
    def _post_show_cb(self, widget):
        # Ensure the tree selection matches the root stack's current layer.
        self._update_selection()

        # Match the flag column widths to the name column's height.
        # This only makes sense after the 1st text layout, sadly.
        GLib.idle_add(self._sizeify_flag_columns)

        return False
示例#11
0
 def _stop_task_queue_runner(self, complete=True):
     """Halts processing of the task queue, and clears it"""
     if self._task_queue_runner_id is None:
         return
     if complete:
         for (callback, args, kwargs) in self._task_queue:
             callback(*args, **kwargs)
     self._task_queue.clear()
     GLib.source_remove(self._task_queue_runner_id)
     self._task_queue_runner_id = None
示例#12
0
        def _sidebar_swap_button_clicked_cb(self, button):
            """Switch the current page's sidebar ("clicked" event handler)

            Ultimately fires the tool_widget_removed() and
            tool_widget_added() @events of the owning workspace.

            """
            page_num = self.get_current_page()
            page = self.get_nth_page(page_num)
            if page is not None:
                GLib.idle_add(self._deferred_swap_tool_widget_sidebar, page)
示例#13
0
 def _popup_leave_notify_cb(self, widget, event):
     if not self.active:
         return
     # allow to leave the window for a short time
     if self._outside_popup_timeout_id:
         GLib.source_remove(self._outside_popup_timeout_id)
         self._outside_popup_timeout_id = None
     self._outside_popup_timeout_id = GLib.timeout_add(
         int(1000 * self.outside_popup_timeout),
         self._outside_popup_timeout_cb,
     )
示例#14
0
        def _close_button_clicked_cb(self, button):
            """Remove the current page (close button "clicked" event callback)

            Ultimately fires the ``tool_widget_removed()`` @event of the owning
            workspace.

            """
            page_num = self.get_current_page()
            page = self.get_nth_page(page_num)
            if page is not None:
                GLib.idle_add(self._deferred_remove_tool_widget, page)
 def _set_input_expanded(self, inp, expand, scroll=True):
     getobj = self._builder.get_object
     arrow = getobj("by%s_expander_arrow" % (inp.name, ))
     grid = getobj("by%s_curve_grid" % (inp.name, ))
     if expand:
         arrow.set_property("arrow-type", Gtk.ArrowType.DOWN)
         grid.show_all()
         if scroll:
             GLib.idle_add(self._scroll_setting_editor, grid)
     else:
         arrow.set_property("arrow-type", Gtk.ArrowType.RIGHT)
         grid.hide()
示例#16
0
 def show_transient_message(self, text, seconds=5):
     """Display a brief, impermanent status message"""
     context_id = self._transient_msg_context_id
     self.statusbar.remove_all(context_id)
     self.statusbar.push(context_id, unicode(text))
     timeout_id = self._transient_msg_remove_timeout_id
     if timeout_id is not None:
         GLib.source_remove(timeout_id)
     timeout_id = GLib.timeout_add_seconds(
         interval=seconds,
         function=self._transient_msg_remove_timer_cb,
     )
     self._transient_msg_remove_timeout_id = timeout_id
示例#17
0
    def run():
        logger.debug('user_datapath: %r', userdatapath)
        logger.debug('user_confpath: %r', userconfpath)

        # User-configured locale (if enabled by user)
        set_user_configured_locale(userconfpath)

        # Locale setting
        from lib.gettext_setup import init_gettext
        init_gettext(localepath)

        # mypaintlib import is performed first in gui.application now.
        from gui import application

        app_state_dirs = application.StateDirs(
            app_data=datapath,
            app_icons=iconspath,
            user_data=userdatapath,
            user_config=userconfpath,
        )
        app = application.Application(
            filenames=args,
            state_dirs=app_state_dirs,
            version=version,
            fullscreen=options.fullscreen,
        )

        # Gtk must not be imported before init_gettext
        # has been run - else locales will not be set
        # up properly (e.g: left-to-right interfaces for right-to-left scripts)
        # Note that this is not the first import of Gtk in the __program__;
        # it is imported indirectly via the import of gui.application
        from lib.gibindings import Gtk
        settings = Gtk.Settings.get_default()
        dark = app.preferences.get("ui.dark_theme_variant", True)
        settings.set_property("gtk-application-prefer-dark-theme", dark)

        if debug and options.run_and_quit:
            from lib.gibindings import GLib
            GLib.timeout_add(1000, lambda *a: Gtk.main_quit())
        else:
            from gui import gtkexcepthook
            func = app.filehandler.confirm_destructive_action
            gtkexcepthook.quit_confirmation_func = func

        # temporary workaround for gtk3 Ctrl-C bug:
        # https://bugzilla.gnome.org/show_bug.cgi?id=622084
        import signal
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        Gtk.main()
示例#18
0
    def add_tool_widget(self, widget, maxnotebooks=None, maxpages=3):
        """Tries to find space for, then add and show a tool widget

        Finding space is based on constraints, adjustable via the parameters.
        The process is driven by `Workspace.add_tool_widget()`.

        :param widget: the widget that needs adoption.
        :type widget: Gtk.Widget created by the Workspace's factory.
        :param maxnotebooks: never make more than this many groups in the stack
        :type maxnotebooks: int
        :param maxpages: never make more than this many pages in a group
        :type maxpages: int
        :return: whether space was found for the widget
        :rtype: bool

        The idea is to try repeatedly with gradually relaxing constraint
        parameters across all stacks in the system until space is found
        somewhere.

        """
        # Try to find a notebook with few enough pages.
        # It might be the zero-pages placeholder on the end.
        target_notebook = None
        notebooks = self._get_notebooks()
        assert len(notebooks) > 0, (
            "There should always be at least one Notebook widget "
            "in any ToolStack.")
        for nb in notebooks:
            if nb.get_n_pages() < maxpages:
                target_notebook = nb
                break
        # The placeholder tends to be recreated in idle routines,
        # so it may not be present on the end of the stack just yet.
        if target_notebook is None:
            assert nb.get_n_pages() > 0
            new_placeholder = nb.split_former_placeholder()
            target_notebook = new_placeholder
        # Adding a page to the placeholder would result in a split
        # in the idle routine later. Check constraint now.
        if maxnotebooks and (target_notebook.get_n_pages() == 0):
            n_populated_notebooks = len(
                [n for n in notebooks if n.get_n_pages() > 0])
            if n_populated_notebooks >= maxnotebooks:
                return False
        # We're good to go.
        target_notebook.append_tool_widget_page(widget)
        if self.workspace:
            GLib.idle_add(self.workspace.tool_widget_added, widget)
        return True
示例#19
0
文件: glib.py 项目: zero804/mypaint
def init_user_dir_caches():
    """Caches the GLib user directories

    >>> init_user_dir_caches()

    The first time this module is imported is from a particular point in
    the launch script, after all the i18n setup is done and before
    lib.mypaintlib is imported. If they're not cached up-front in this
    manner, get_user_config_dir() & friends may return literal "?"s in
    place of non-ASCII characters (Windows systems with non-ASCII user
    profile dirs are known to trigger this).

    The debugging prints may be useful too.

    """
    logger.debug("Init g_get_user_config_dir(): %r", get_user_config_dir())
    logger.debug("Init g_get_user_data_dir(): %r", get_user_data_dir())
    logger.debug("Init g_get_user_cache_dir(): %r", get_user_cache_dir())
    # It doesn't matter if some of these are None
    for i in range(GLib.UserDirectory.N_DIRECTORIES):
        k = GLib.UserDirectory(i)
        logger.debug(
            "Init g_get_user_special_dir(%s): %r",
            k.value_name,
            get_user_special_dir(k),
        )
示例#20
0
 def _queue_movement(self, zone, args):
     self._move_item = (zone, args)
     if not self._move_timeout_id:
         self._move_timeout_id = GLib.timeout_add(
             interval=16.66,  # 60 fps cap
             function=self._do_move,
         )
示例#21
0
 def _value_changed_cb(self, adj, prefs_key):
     # Direct GtkAdjustment callback for a single adjustment being changed.
     value = float(adj.get_value())
     self.app.preferences[prefs_key] = value
     self._changed_settings.add(prefs_key)
     if self._idle_srcid is None:
         self._idle_srcid = GLib.idle_add(self._values_changed_idle_cb)
示例#22
0
 def drag_update_cb(self, tdw, event, ev_x, ev_y, dx, dy):
     if self._line_possible:
         self.update_position(ev_x, ev_y)
         if self.idle_srcid is None:
             self.idle_srcid = GLib.idle_add(self._drag_idle_cb)
     return super(LineModeBase,
                  self).drag_update_cb(tdw, event, ev_x, ev_y, dx, dy)
示例#23
0
def startfile(filepath, operation="open"):
    """os.startfile / g_app_info_launch_default_for_uri compat

    This has the similar semantics to os.startfile, where it's
    supported: it launches the given file or folder path with the
    default app. On Windows, operation can be set to "edit" to use the
    default editor for a file. The operation parameter is ignored on
    other systems, and GIO's equivalent routine is used.

    The relevant app is started in the background, and there are no
    means for getting its pid.

    """
    try:
        if os.name == 'nt':
            os.startfile(filepath, operation)  # raises: WindowsError
        else:
            uri = GLib.filename_to_uri(filepath)
            Gio.app_info_launch_default_for_uri(uri, None)  # raises: GError
        return True
    except Exception:
        logger.exception(
            "Failed to launch the default application for %r (op=%r)",
            filepath,
            operation,
        )
        return False
示例#24
0
 def _queue_color_change(self, new_col, cm):
     self._queued_data = new_col, cm
     if not self._timeout_id:
         self._timeout_id = GLib.timeout_add(
             interval=16.66,  # 60 fps cap
             function=self._change_color,
         )
示例#25
0
 def _values_changed_idle_cb(self):
     # Aggregate, idle-state callback for multiple adjustments being changed
     # in a single event. Queues redraws, and runs observers. The curve sets
     # multiple settings at once, and we might as well not queue too many
     # redraws.
     if self._idle_srcid is not None:
         current_mode = self.app.doc.modes.top
         if isinstance(current_mode, LineModeBase):
             # Redraw last_line when settings are adjusted
             # in the adjustment Curve
             GLib.idle_add(current_mode.redraw_line_cb)
         for func in self.observers:
             func(self._changed_settings)
         self._changed_settings = set()
         self._idle_srcid = None
     return False
示例#26
0
    def _realize_cb(self, widget):
        """Kick off the deferred layout code when the widget is realized"""

        # Set up monitoring of the toplevel's size changes.
        toplevel = self.get_toplevel()
        toplevel.connect("configure-event", self._toplevel_configure_cb)

        # Do the initial layout
        layout = self._initial_layout
        if layout is None:
            return
        llayout = layout.get("left_sidebar", {})
        if llayout:
            logger.debug("Left sidebar: building from saved layout...")
            num_added_l = self._lstack.build_from_layout(llayout)
            logger.debug("Left sidebar: added %d group(s)", num_added_l)
        rlayout = layout.get("right_sidebar", {})
        if rlayout:
            logger.debug("Right sidebar: building from saved layout...")
            num_added_r = self._rstack.build_from_layout(rlayout)
            logger.debug("Right sidebar: added %d group(s)", num_added_r)
        # Floating windows
        for fi, flayout in enumerate(layout.get("floating", [])):
            logger.debug(
                "Floating window %d: building from saved layout...",
                fi,
            )
            win = ToolStackWindow(self)
            self.floating_window_created(win)
            num_added_f = win.build_from_layout(flayout)
            logger.debug(
                "Floating window %d: added %d group(s)",
                fi,
                num_added_f,
            )
            # The populated ones are only revealed after their
            # floating_window_created have had a chance to run.
            if num_added_f > 0:
                self._floating.add(win)
                GLib.idle_add(win.show_all)
            else:
                logger.warning(
                    "Floating window %d is initially unpopulated. "
                    "Destroying it.",
                    fi,
                )
                GLib.idle_add(win.destroy)
示例#27
0
 def _toplevel_configure_cb(self, toplevel, event):
     """Record the toplevel window's position ("configure-event" callback)
     """
     # Avoid saving fullscreen positions. The timeout is a bit of hack, but
     # it's necessary because the state change event and the configure event
     # when fullscreening don't have a sensible order.
     w, h = event.width, event.height
     srcid = self._save_toplevel_pos_timeout
     if srcid:
         GLib.source_remove(srcid)
     srcid = GLib.timeout_add(
         250,
         self._save_toplevel_pos_timeout_cb,
         w,
         h,
     )
     self._save_toplevel_pos_timeout = srcid
示例#28
0
 def _restart_autoleave_timeout(self):
     if not self.autoleave_timeout:
         return
     self._stop_autoleave_timeout()
     self._autoleave_timeout_id = GLib.timeout_add(
         int(1000 * self.autoleave_timeout),
         self._autoleave_timeout_cb,
     )
示例#29
0
 def _restart_outside_popup_timeout(self):
     if not self.outside_popup_timeout:
         return
     self._stop_outside_popup_timeout()
     self._outside_popup_timeout_id = GLib.timeout_add(
         int(1000 * self.outside_popup_timeout),
         self._outside_popup_timeout_cb,
     )
示例#30
0
        def _first_alloc_cb(self, widget, alloc):
            """Try to allocate child widgets their natural size when alloced.
            """
            # Normally, if child widgets declare a real minimum size then in a
            # structure like this they'll be allocated their minimum size even
            # when there's enough space to give them their natural size. As a
            # workaround, set the bar position on the very first size-allocate
            # event to the best compromise we can calculate.

            # Child natural and minimum heights.
            c1 = self.get_child1()
            c2 = self.get_child2()
            if not (c1 and c2):
                return
            c1min, c1nat = c1.get_preferred_height_for_width(alloc.width)
            c2min, c2nat = c2.get_preferred_height_for_width(alloc.width)

            # Disconnect the handler; only run the 1st time.
            self.disconnect(self._first_alloc_id)
            self._first_alloc_id = None

            # If ToolStack.build_from_layout set an initial position,
            # then code elsewhere will be allocating a size.
            if self._initial_divider_position is not None:
                return

            # Get handle size
            handle_size = GObject.Value()
            handle_size.init(int)
            handle_size.set_int(12)  # conservative initial guess
            self.style_get_property("handle-size", handle_size)
            bar_height = handle_size.get_int()
            # Strategy here is to try and give one child widget its natural
            # size first, slightly favouring the first (top) child.  We
            # could be more egalitarian by inspecting the deep structure.
            pos = -1
            if c1nat + c2min <= alloc.height - bar_height:
                pos = c1nat
            elif c1min + c2nat <= alloc.height - bar_height:
                pos = alloc.height - c2nat - bar_height
            elif c1min + c2min <= alloc.height - bar_height:
                pos = alloc.height - c2min - bar_height

            # The position setting must be done outside this handler
            # or it'll look weird.
            GLib.idle_add(self.set_position, pos)