def __init__(self, filenames, app_datapath, app_extradatapath, user_datapath, user_confpath, version, fullscreen=False): """Construct, but do not run. :params filenames: The list of files to load. Note: only the first is used. :param app_datapath: App-specific read-only data area. Path used for UI definition XML, and the default sets of backgrounds, palettes, and brush defintions. Often $PREFIX/share/. :param app_extradatapath: Extra search path for themeable UI icons. This will be used in addition to $XDG_DATA_DIRS for the purposes of icon lookup. Normally it's $PREFIX/share, to support unusual installations outside the usual locations. It should contain an icons/ subdirectory. :param user_datapath: Location of the user's app-specific data. For MyPaint, this means the user's brushes, backgrounds, and scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e. ~/.local/share/mypaint :param user_confpath: Location of the user's app-specific config area. This is where MyPaint will save user preferences data and the keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e. ~/.config/mypaint :param version: Version string for the about dialog. :param fullscreen: Go fullscreen after starting. """ assert Application._INSTANCE is None super(Application, self).__init__() Application._INSTANCE = self self.user_confpath = user_confpath #: User configs (see __init__) self.user_datapath = user_datapath #: User data (see __init__) self.datapath = app_datapath self.version = version #: version string for the app. # create config directory, and subdirs where the user might drop files for basedir in [self.user_confpath, self.user_datapath]: if not os.path.isdir(basedir): os.mkdir(basedir) logger.info('Created basedir %r', basedir) for datasubdir in ['backgrounds', 'brushes', 'scratchpads']: datadir = os.path.join(self.user_datapath, datasubdir) if not os.path.isdir(datadir): os.mkdir(datadir) logger.info('Created data subdir %r', datadir) # Default location for our icons. The user's theme can override these. icon_theme = gtk.icon_theme_get_default() icon_theme.append_search_path(join(app_extradatapath, "icons")) # Icon sanity check if not icon_theme.has_icon('mypaint') \ or not icon_theme.has_icon('mypaint-tool-brush'): logger.error('Error: Where have my icons gone?') logger.error('Icon search path: %r', icon_theme.get_search_path()) logger.error("Mypaint can't run sensibly without its icons; " "please check your installation. See " "https://gna.org/bugs/?18460 for possible solutions") sys.exit(1) gtk.Window.set_default_icon_name('mypaint') # Stock items, core actions, and menu structure resources_xml = join(self.datapath, "gui", "resources.xml") self.builder = gtk.Builder() self.builder.set_translation_domain("mypaint") self.builder.add_from_file(resources_xml) factory = self.builder.get_object("stock_icon_factory") factory.add_default() self.ui_manager = self.builder.get_object("app_ui_manager") signal_callback_objs = [] gdk.set_program_class('MyPaint') self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps')) self.cursor_color_picker = gdk.Cursor( gtk2compat.gdk.display_get_default(), self.pixmaps.cursor_color_picker, 1, 30) self.cursors = CursorCache(self) # unmanaged main brush; always the same instance (we can attach settings_observers) # this brush is where temporary changes (color, size...) happen self.brush = brush.BrushInfo() self.brush.load_defaults() # Global pressure mapping function, ignored unless set self.pressure_mapping = None self.preferences = {} self.load_settings() # Keyboard manager self.kbm = keyboard.KeyboardManager(self) # File I/O self.filehandler = filehandling.FileHandler(self) # Load the main interface mypaint_main_xml = join(self.datapath, "gui", "mypaint.glade") self.builder.add_from_file(mypaint_main_xml) # Main drawing window self.drawWindow = self.builder.get_object("drawwindow") signal_callback_objs.append(self.drawWindow) # Workspace widget. Manages layout of toolwindows, and autohide in # fullscreen. workspace = self.builder.get_object("app_workspace") workspace.build_from_layout(self.preferences["workspace.layout"]) workspace.floating_window_created += self._floating_window_created_cb fs_autohide_action = self.builder.get_object("FullscreenAutohide") fs_autohide_action.set_active(workspace.autohide_enabled) self.workspace = workspace # Working document: viewer widget app_canvas = self.builder.get_object("app_canvas") # Working document: model and controller model = lib.document.Document(self.brush) self.doc = document.Document(self, app_canvas, model) app_canvas.set_model(model) signal_callback_objs.append(self.doc) signal_callback_objs.append(self.doc.modes) self.scratchpad_filename = "" scratchpad_model = lib.document.Document(self.brush) scratchpad_tdw = tileddrawwidget.TiledDrawWidget() scratchpad_tdw.set_model(scratchpad_model) self.scratchpad_doc = document.Document(self, scratchpad_tdw, scratchpad_model, leader=self.doc) self.brushmanager = brushmanager.BrushManager( join(app_datapath, 'brushes'), join(user_datapath, 'brushes'), self) signal_callback_objs.append(self.filehandler) self.brushmodifier = brushmodifier.BrushModifier(self) self.line_mode_settings = linemode.LineModeSettings(self) # Button press mapping self.button_mapping = ButtonMapping() # Monitors changes of input device & saves device-specific brushes self.device_monitor = DeviceUseMonitor(self) if not self.preferences.get("scratchpad.last_opened_scratchpad", None): self.preferences["scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave() self.scratchpad_filename = self.preferences["scratchpad.last_opened_scratchpad"] self.brush_color_manager = BrushColorManager(self) self.brush_color_manager.set_picker_cursor(self.cursor_color_picker) self.brush_color_manager.set_data_path(self.datapath) #: Mapping of setting cname to a GtkAdjustment which controls the base #: value of that setting for the app's current brush. self.brush_adjustment = {} self.init_brush_adjustments() # Connect signals defined in mypaint.xml callback_finder = CallbackFinder(signal_callback_objs) self.builder.connect_signals(callback_finder) self.kbm.start_listening() self.filehandler.doc = self.doc self.filehandler.filename = None gtk2compat.gtk.accel_map_load(join(self.user_confpath, 'accelmap.conf')) # Load the default background for datapath in [self.user_datapath, self.datapath]: bg_path = join(datapath, backgroundwindow.BACKGROUNDS_SUBDIR, backgroundwindow.DEFAULT_BACKGROUND) if not os.path.exists(bg_path): continue bg, errors = backgroundwindow.load_background(bg_path) if bg: self.doc.model.set_background(bg, make_default=True) break else: logger.warning("Failed to load default background image %r", bg_path) if errors: for error in errors: logger.warning("warning: %r", error) # Non-dockable subwindows # Loading is deferred as late as possible self._subwindow_classes = { # action-name: action-class "BackgroundWindow": backgroundwindow.BackgroundWindow, "BrushEditorWindow": brusheditor.BrushEditorWindow, "PreferencesWindow": preferenceswindow.PreferencesWindow, "InputTestWindow": inputtestwindow.InputTestWindow, "BrushIconEditorWindow": brushiconeditor.BrushIconEditorWindow, } self._subwindows = {} # Show main UI. self.drawWindow.show_all() gobject.idle_add(self._at_application_start, filenames, fullscreen)
def __init__(self, filenames, app_datapath, app_extradatapath, user_datapath, user_confpath, version, fullscreen=False): """Construct, but do not run. :params filenames: The list of files to load. Note: only the first is used. :param app_datapath: App-specific read-only data area. Path used for UI definition XML, and the default sets of backgrounds, palettes, and brush defintions. Often $PREFIX/share/. :param app_extradatapath: Extra search path for themeable UI icons. This will be used in addition to $XDG_DATA_DIRS for the purposes of icon lookup. Normally it's $PREFIX/share, to support unusual installations outside the usual locations. It should contain an icons/ subdirectory. :param user_datapath: Location of the user's app-specific data. For MyPaint, this means the user's brushes, backgrounds, and scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e. ~/.local/share/mypaint :param user_confpath: Location of the user's app-specific config area. This is where MyPaint will save user preferences data and the keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e. ~/.config/mypaint :param version: Version string for the about dialog. :param fullscreen: Go fullscreen after starting. """ assert Application._INSTANCE is None super(Application, self).__init__() Application._INSTANCE = self self.user_confpath = user_confpath #: User configs (see __init__) self.user_datapath = user_datapath #: User data (see __init__) self.datapath = app_datapath self.version = version #: version string for the app. # create config directory, and subdirs where the user might drop files for basedir in [self.user_confpath, self.user_datapath]: if not os.path.isdir(basedir): os.mkdir(basedir) print 'Created basedir', basedir for datasubdir in ['backgrounds', 'brushes', 'scratchpads']: datadir = os.path.join(self.user_datapath, datasubdir) if not os.path.isdir(datadir): os.mkdir(datadir) print 'Created data subdir', datadir # Default location for our icons. The user's theme can override these. icon_theme = gtk.icon_theme_get_default() icon_theme.append_search_path(join(app_extradatapath, "icons")) # Icon sanity check if not icon_theme.has_icon('mypaint') \ or not icon_theme.has_icon('mypaint-tool-brush'): print 'Error: Where have my icons gone?' print 'Icon search path:', icon_theme.get_search_path() print "Mypaint can't run sensibly without its icons; " \ + "please check your installation." print 'see https://gna.org/bugs/?18460 for possible solutions' sys.exit(1) if pygtkcompat.USE_GTK3: gtk.Window.set_default_icon_name('mypaint') else: gtk.window_set_default_icon_name('mypaint') # Stock items, core actions, and menu structure builder_xml = join(self.datapath, "gui", "mypaint.xml") self.builder = gtk.Builder() self.builder.set_translation_domain("mypaint") self.builder.add_from_file(builder_xml) factory = self.builder.get_object("stock_icon_factory") factory.add_default() self.ui_manager = self.builder.get_object("app_ui_manager") signal_callback_objs = [] gdk.set_program_class('MyPaint') self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps')) self.cursor_color_picker = gdk.Cursor( pygtkcompat.gdk.display_get_default(), self.pixmaps.cursor_color_picker, 1, 30) self.cursors = CursorCache(self) # unmanaged main brush; always the same instance (we can attach settings_observers) # this brush is where temporary changes (color, size...) happen self.brush = brush.BrushInfo() self.brush.load_defaults() # Global pressure mapping function, ignored unless set self.pressure_mapping = None self.preferences = {} self.load_settings() self.scratchpad_filename = "" self.kbm = keyboard.KeyboardManager(self) self.doc = document.Document(self) signal_callback_objs.append(self.doc) signal_callback_objs.append(self.doc.modes) self.scratchpad_doc = document.Document(self, leader=self.doc) self.brushmanager = brushmanager.BrushManager( join(app_datapath, 'brushes'), join(user_datapath, 'brushes'), self) self.filehandler = filehandling.FileHandler(self) signal_callback_objs.append(self.filehandler) self.brushmodifier = brushmodifier.BrushModifier(self) self.line_mode_settings = linemode.LineModeSettings(self) # Button press mapping self.button_mapping = ButtonMapping() # Monitors changes of input device & saves device-specific brushes self.device_monitor = DeviceUseMonitor(self) if not self.preferences.get("scratchpad.last_opened_scratchpad", None): self.preferences["scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave() self.scratchpad_filename = self.preferences["scratchpad.last_opened_scratchpad"] self.brush_color_manager = BrushColorManager(self) self.brush_color_manager.set_picker_cursor(self.cursor_color_picker) self.brush_color_manager.set_data_path(self.datapath) self.init_brush_adjustments() self.layout_manager = layout.LayoutManager( prefs=self.preferences["layout.window_positions"], factory=windowing.window_factory, factory_opts=[self] ) self.drawWindow = self.layout_manager.get_widget_by_role("main-window") self.layout_manager.show_all() signal_callback_objs.append(self.drawWindow) # Connect signals defined in mypaint.xml callback_finder = CallbackFinder(signal_callback_objs) self.builder.connect_signals(callback_finder) self.kbm.start_listening() self.filehandler.doc = self.doc self.filehandler.filename = None pygtkcompat.gtk.accel_map_load(join(self.user_confpath, 'accelmap.conf')) # Load the background settings window. # FIXME: this line shouldn't be needed, but we need to load this up # front to get any non-default background that the user has configured # from the preferences. self.layout_manager.get_subwindow_by_role("backgroundWindow") # And the brush settings window, or things like eraser mode will break. # FIXME: brush_adjustments should not be dependent on this self.layout_manager.get_subwindow_by_role("brushSettingsWindow") def at_application_start(*junk): col = self.brush_color_manager.get_color() self.brushmanager.select_initial_brush() self.brush_color_manager.set_color(col) if filenames: # Open only the first file, no matter how many has been specified # If the file does not exist just set it as the file to save to fn = filenames[0].replace('file:///', '/') # ^ some filebrowsers do this (should only happen with outdated # mypaint.desktop) if not os.path.exists(fn): self.filehandler.filename = fn else: self.filehandler.open_file(fn) # Load last scratchpad if not self.preferences["scratchpad.last_opened_scratchpad"]: self.preferences["scratchpad.last_opened_scratchpad"] \ = self.filehandler.get_scratchpad_autosave() self.scratchpad_filename \ = self.preferences["scratchpad.last_opened_scratchpad"] if os.path.isfile(self.scratchpad_filename): try: self.filehandler.open_scratchpad(self.scratchpad_filename) except AttributeError, e: print "Scratchpad widget isn't initialised yet, so cannot centre" self.apply_settings() if not self.pressure_devices: print 'No pressure sensitive devices found.' self.drawWindow.present() # Handle fullscreen command line option if fullscreen: self.drawWindow.fullscreen_cb()
def __init__(self, filenames, app_datapath, app_extradatapath, user_datapath, user_confpath, version, fullscreen=False): """Construct, but do not run. :params filenames: The list of files to load. Note: only the first is used. :param app_datapath: App-specific read-only data area. Path used for UI definition XML, and the default sets of backgrounds, palettes, and brush defintions. Often $PREFIX/share/. :param app_extradatapath: Extra search path for themeable UI icons. This will be used in addition to $XDG_DATA_DIRS for the purposes of icon lookup. Normally it's $PREFIX/share, to support unusual installations outside the usual locations. It should contain an icons/ subdirectory. :param user_datapath: Location of the user's app-specific data. For MyPaint, this means the user's brushes, backgrounds, and scratchpads. Commonly $XDG_DATA_HOME/mypaint, i.e. ~/.local/share/mypaint :param user_confpath: Location of the user's app-specific config area. This is where MyPaint will save user preferences data and the keyboard accelerator map. Commonly $XDG_CONFIG_HOME/mypaint, i.e. ~/.config/mypaint :param version: Version string for the about dialog. :param fullscreen: Go fullscreen after starting. """ assert Application._INSTANCE is None super(Application, self).__init__() Application._INSTANCE = self self.user_confpath = user_confpath #: User configs (see __init__) self.user_datapath = user_datapath #: User data (see __init__) self.datapath = app_datapath self.version = version #: version string for the app. # create config directory, and subdirs where the user might drop files for basedir in [self.user_confpath, self.user_datapath]: if not os.path.isdir(basedir): os.mkdir(basedir) logger.info('Created basedir %r', basedir) for datasubdir in ['backgrounds', 'brushes', 'scratchpads']: datadir = os.path.join(self.user_datapath, datasubdir) if not os.path.isdir(datadir): os.mkdir(datadir) logger.info('Created data subdir %r', datadir) # Default location for our icons. The user's theme can override these. icon_theme = gtk.icon_theme_get_default() icon_theme.append_search_path(join(app_extradatapath, "icons")) # Icon sanity check if not icon_theme.has_icon('mypaint') \ or not icon_theme.has_icon('mypaint-tool-brush'): logger.error('Error: Where have my icons gone?') logger.error('Icon search path: %r', icon_theme.get_search_path()) logger.error("Mypaint can't run sensibly without its icons; " "please check your installation. See " "https://gna.org/bugs/?18460 for possible solutions") sys.exit(1) gtk.Window.set_default_icon_name('mypaint') # Stock items, core actions, and menu structure resources_xml = join(self.datapath, "gui", "resources.xml") self.builder = gtk.Builder() self.builder.set_translation_domain("mypaint") self.builder.add_from_file(resources_xml) factory = self.builder.get_object("stock_icon_factory") factory.add_default() self.ui_manager = self.builder.get_object("app_ui_manager") signal_callback_objs = [] gdk.set_program_class('MyPaint') self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps')) self.cursor_color_picker = gdk.Cursor( gtk2compat.gdk.display_get_default(), self.pixmaps.cursor_color_picker, 1, 30) self.cursors = CursorCache(self) # unmanaged main brush; always the same instance (we can attach settings_observers) # this brush is where temporary changes (color, size...) happen self.brush = brush.BrushInfo() self.brush.load_defaults() # Global pressure mapping function, ignored unless set self.pressure_mapping = None self.preferences = {} self.load_settings() # Keyboard manager self.kbm = keyboard.KeyboardManager(self) # File I/O self.filehandler = filehandling.FileHandler(self) # Load the main interface mypaint_main_xml = join(self.datapath, "gui", "mypaint.glade") self.builder.add_from_file(mypaint_main_xml) # Main drawing window self.drawWindow = self.builder.get_object("drawwindow") signal_callback_objs.append(self.drawWindow) # Workspace widget. Manages layout of toolwindows, and autohide in # fullscreen. workspace = self.builder.get_object("app_workspace") workspace.build_from_layout(self.preferences["workspace.layout"]) workspace.floating_window_created += self._floating_window_created_cb fs_autohide_action = self.builder.get_object("FullscreenAutohide") fs_autohide_action.set_active(workspace.autohide_enabled) self.workspace = workspace # Working document: viewer widget app_canvas = self.builder.get_object("app_canvas") # Working document: model and controller model = lib.document.Document(self.brush) self.doc = document.Document(self, app_canvas, model) app_canvas.set_model(model) signal_callback_objs.append(self.doc) signal_callback_objs.append(self.doc.modes) self.scratchpad_filename = "" scratchpad_model = lib.document.Document(self.brush) scratchpad_tdw = tileddrawwidget.TiledDrawWidget() scratchpad_tdw.set_model(scratchpad_model) self.scratchpad_doc = document.Document(self, scratchpad_tdw, scratchpad_model, leader=self.doc) self.brushmanager = brushmanager.BrushManager( join(app_datapath, 'brushes'), join(user_datapath, 'brushes'), self) signal_callback_objs.append(self.filehandler) self.brushmodifier = brushmodifier.BrushModifier(self) self.line_mode_settings = linemode.LineModeSettings(self) # Button press mapping self.button_mapping = ButtonMapping() # Monitors changes of input device & saves device-specific brushes self.device_monitor = DeviceUseMonitor(self) if not self.preferences.get("scratchpad.last_opened_scratchpad", None): self.preferences[ "scratchpad.last_opened_scratchpad"] = self.filehandler.get_scratchpad_autosave( ) self.scratchpad_filename = self.preferences[ "scratchpad.last_opened_scratchpad"] self.brush_color_manager = BrushColorManager(self) self.brush_color_manager.set_picker_cursor(self.cursor_color_picker) self.brush_color_manager.set_data_path(self.datapath) #: Mapping of setting cname to a GtkAdjustment which controls the base #: value of that setting for the app's current brush. self.brush_adjustment = {} self.init_brush_adjustments() # Connect signals defined in mypaint.xml callback_finder = CallbackFinder(signal_callback_objs) self.builder.connect_signals(callback_finder) self.kbm.start_listening() self.filehandler.doc = self.doc self.filehandler.filename = None gtk2compat.gtk.accel_map_load(join(self.user_confpath, 'accelmap.conf')) # Load the default background for datapath in [self.user_datapath, self.datapath]: bg_path = join(datapath, backgroundwindow.BACKGROUNDS_SUBDIR, backgroundwindow.DEFAULT_BACKGROUND) if not os.path.exists(bg_path): continue bg, errors = backgroundwindow.load_background(bg_path) if bg: self.doc.model.set_background(bg, make_default=True) break else: logger.warning("Failed to load default background image %r", bg_path) if errors: for error in errors: logger.warning("warning: %r", error) # Non-dockable subwindows # Loading is deferred as late as possible self._subwindow_classes = { # action-name: action-class "BackgroundWindow": backgroundwindow.BackgroundWindow, "BrushEditorWindow": brusheditor.BrushEditorWindow, "PreferencesWindow": preferenceswindow.PreferencesWindow, "InputTestWindow": inputtestwindow.InputTestWindow, "BrushIconEditorWindow": brushiconeditor.BrushIconEditorWindow, } self._subwindows = {} # Show main UI. self.drawWindow.show_all() gobject.idle_add(self._at_application_start, filenames, fullscreen)