def paint_save_clear(): doc = document.Document() for i in iterations(): paint_doc(doc) doc.save('test_leak.ora') doc.clear() doc.cleanup()
def saveFrame(): print('test-saving various frame sizes...') cnt = 0 doc = document.Document() #doc.load('bigimage.ora') doc.set_frame_enabled(True) s = tiledsurface.Surface() N = mypaintlib.TILE_SIZE positions = range(-1, +2) + range(-N - 1, -N + 2) + range(+N - 1, +N + 2) for x1 in positions: for x2 in positions: for y1 in positions: for y2 in positions: if x2 <= x1 or y2 <= y1: continue cnt += 1 x, y, w, h = x1, y1, x2 - x1, y2 - y1 #print x, y, w, h s.save_as_png('test_saveFrame_s.png', x, y, w, h) doc.update_frame(x=x, y=y, width=w, height=h) #doc.save('test_saveFrame_doc_%dx%d.png' % (w,h)) doc.save('test_saveFrame_doc.png') doc.save('test_saveFrame_doc.jpg') print('checked', cnt, 'different rectangles')
def save_png_layer(): from lib import document d = document.Document() d.load('biglayer.png') yield start_measurement d.layer_stack.current.save_as_png('test_save.png') yield stop_measurement
def save_test(): doc = document.Document() paint_doc(doc) for i in iterations(): doc.save('test_leak.ora') doc.save('test_leak.png') doc.save('test_leak.jpg')
def test_save_doc_in_frame(self): """Test saving many different crops of bigimage.ora""" cnt = 0 doc = document.Document() doc.load(join(paths.TESTS_DIR, 'bigimage.ora')) doc.set_frame_enabled(True) s = tiledsurface.Surface() t0 = time() positions = list(range(-1, +2)) positions.extend(range(-N - 1, -N + 2)) positions.extend(range(+N - 1, +N + 2)) for x1 in positions: for x2 in positions: for y1 in positions: for y2 in positions: if x2 <= x1 or y2 <= y1: continue cnt += 1 x, y, w, h = x1, y1, x2 - x1, y2 - y1 # print x, y, w, h s.save_as_png('test_saveFrame_s.png', x, y, w, h) doc.update_frame(x=x, y=y, width=w, height=h) # doc.save('test_saveFrame_doc_%dx%d.png' % (w,h)) doc.save('test_saveFrame_doc.png') doc.save('test_saveFrame_doc.jpg') print( "saved %d frames in %0.2fs, " % (cnt, time() - t0), end="", file=sys.stderr, )
def test_save_doc_in_frame(self): """Test saving many different crops of bigimage.ora""" cnt = 0 doc = document.Document() doc.load(join(paths.TESTS_DIR, 'bigimage.ora')) doc.set_frame_enabled(True) s = tiledsurface.Surface() t0 = time() positions = list(range(-1, +2)) positions.extend(range(-N - 1, -N + 2)) positions.extend(range(+N - 1, +N + 2)) def valid(c): x1, y1, x2, y2 = c return x1 < x2 and y1 < y2 for x1, y1, x2, y2 in filter(valid, product(positions, repeat=4)): cnt += 1 x, y, w, h = x1, y1, x2 - x1, y2 - y1 s.save_as_png('test_saveFrame_s.png', x, y, w, h) doc.update_frame(x=x, y=y, width=w, height=h) doc.save('test_saveFrame_doc.png') doc.save('test_saveFrame_doc.jpg') print( "saved %d frames in %0.2fs, " % (cnt, time() - t0), end="", file=sys.stderr, )
def save_png(): from lib import document d = document.Document() d.load('bigimage.ora') yield start_measurement d.save('test_save.png') yield stop_measurement
def setUpClass(cls): # Load test data doc = document.Document() doc.load(join(paths.TESTS_DIR, 'fill_outlines.ora')) root = doc.layer_stack # Set up references to test layers cls._fill_layers = [] for fill_layer in root.deepget(cls.FILL_GROUP_PATH): cls._fill_layers.append(fill_layer) # Layers with no gaps cls.closed_small_s = root.deepget((0, 0)) cls.closed_small_c = root.deepget((0, 1)) cls.small = ( cls.closed_small_s, cls.closed_small_c, ) cls.closed_large_s = root.deepget((0, 2)) cls.closed_large_c = root.deepget((0, 3)) cls.large = ( cls.closed_large_s, cls.closed_large_c, ) cls.heavy = root.deepget((2, 0)) cls.minimal = root.deepget((0, 4)) cls.empty_layer = root.deepget((3, )) # Layers with gaps cls.gap_layers = [] for gap_layer in root.deepget(cls.GAP_GROUP_PATH): cls.gap_layers.append(gap_layer) cls.root = root
def __processFile(self, file): f = codecs.open(file, 'r', 'UTF-8') fileContent = json.load(f) newDocument = document.Document(fileContent) newDocument.addTerms() newDocument.computeTF() self.__termIndex.processStemDictionary(newDocument) newDocument.save() shutil.move(file, os.path.join('processed', str(newDocument.fileName)))
def test_save_doc_in_frame(self): """Test saving many different crops of bigimage.ora""" cnt = 0 doc = document.Document() doc.load(join(paths.TESTS_DIR, 'bigimage.ora')) doc.set_frame_enabled(True) s = tiledsurface.Surface() t0 = time() positions = list(range(-1, +2)) positions.extend(range(-N - 1, -N + 2)) positions.extend(range(+N - 1, +N + 2)) def valid(c): x1, y1, x2, y2 = c return x1 < x2 and y1 < y2 def nth(g, n): _n = 0 for a in g: if _n == 0: yield a _n = (_n + 1) % n # Reduce number of frames when running on windows, to not run into # the "files opened" limit. The fact that we get this issue suggests # a problem with one of the save functions under Python 3, so it should # be investigated more thoroughly. n = 7 if "win" in sys.platform else 1 p = positions for x1, y1, x2, y2 in nth(filter(valid, product(p, repeat=4)), 7): cnt += 1 x, y, w, h = x1, y1, x2 - x1, y2 - y1 s.save_as_png('test_saveFrame_s.png', x, y, w, h) doc.update_frame(x=x, y=y, width=w, height=h) doc.save('test_saveFrame_doc.png') doc.save('test_saveFrame_doc.jpg') print( "saved %d frames in %0.2fs, " % (cnt, time() - t0), end="", file=sys.stderr, )
def load_ora(): from lib import document d = document.Document() yield start_measurement d.load('bigimage.ora') yield stop_measurement
def docPaint(): b1 = brush.BrushInfo(open('brushes/s008.myb').read()) b2 = brush.BrushInfo(open('brushes/redbrush.myb').read()) b2.set_color_hsv((0.3, 0.4, 0.35)) b3 = brush.BrushInfo(open('brushes/watercolor.myb').read()) b3.set_color_hsv((0.9, 0.2, 0.2)) b = brush.BrushInfo() b.load_defaults() # test some actions doc = document.Document(b) doc.undo() # nop events = np.loadtxt('painting30sec.dat') events = events[:len(events) / 8] t_old = events[0][0] n = len(events) layer = doc.layer_stack.current for i, (t, x, y, pressure) in enumerate(events): dtime = t - t_old t_old = t #print dtime layer.stroke_to(doc.brush, x, y, pressure, 0.0, 0.0, dtime) if i == n * 1 / 8: b.load_from_brushinfo(b2) if i == n * 2 / 8: doc.clear_layer() doc.undo() assert not doc.get_bbox().empty() doc.redo() assert doc.get_bbox().empty() if i == n * 3 / 8: doc.undo() b.load_from_brushinfo(b3) if i == n * 4 / 8: b.load_from_brushinfo(b2) if i == n * 5 / 8: doc.undo() doc.redo() if i == n * 6 / 8: b.load_from_brushinfo(b2) if i == n * 7 / 8: doc.add_layer(1) # If there is an eraser (or smudging) at work, we might be erasing # tiles that are empty. Those tile get memory allocated and affect # the bounding box of the layer. This shouldn't be a big issue, but # they get dropped when loading a document, which makes a # comparision of the PNG files fail. The hack below is to avoid that. for l in doc.layer_stack: l._surface.remove_empty_tiles() doc.layer_stack[0].save_as_png('test_docPaint_a.png') doc.layer_stack[0].save_as_png('test_docPaint_a1.png') # the resulting images will look slightly different because of dithering assert pngs_equal('test_docPaint_a.png', 'test_docPaint_a1.png') # test save/load doc.save('test_f1.ora') doc2 = document.Document() doc2.load('test_f1.ora') # (We don't preserve the absolute position of the image, only the size.) #assert doc.get_bbox() == doc2.get_bbox() print('doc / doc2 bbox:', doc.get_bbox(), doc2.get_bbox()) doc2.layer_stack[0].save_as_png('test_docPaint_b.png') assert pngs_equal('test_docPaint_a.png', 'test_docPaint_b.png') doc2.save('test_f2.ora') #check not possible, because PNGs not exactly equal: #assert files_equal('test_f1.ora', 'test_f2.ora') # less strict test than above (just require load-save-load-save not to alter the file) doc3 = document.Document() doc3.load('test_f2.ora') assert doc2.get_bbox() == doc3.get_bbox() doc3.layer_stack[0].save_as_png('test_docPaint_c.png') assert pngs_equal('test_docPaint_b.png', 'test_docPaint_c.png') doc2.save('test_f3.ora') #check not possible, because PNGs not exactly equal: #assert files_equal('test_f2.ora', 'test_f3.ora') # note: this is not supposed to be strictly reproducible because # of different random seeds [huh? what does that mean?] bbox = doc.get_bbox() print('document bbox is', bbox) # test for appearance changes (make sure they are intended) doc.save('test_docPaint_flat.png', alpha=False) doc.save('test_docPaint_alpha.png', alpha=True) assert pngs_equal('test_docPaint_flat.png', 'correct_docPaint_flat.png') assert pngs_equal('test_docPaint_alpha.png', 'correct_docPaint_alpha.png')
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) _init_icons(join(app_extradatapath, "icons")) # 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) self.ui_manager = self.builder.get_object("app_ui_manager") signal_callback_objs = [self] Gdk.set_program_class('MyPaint') self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps')) self.cursor_color_picker = Gdk.Cursor.new_from_pixbuf( Gdk.Display.get_default(), self.pixmaps.cursor_color_picker, 3, 15, ) self.cursors = gui.cursor.CustomCursorMaker(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) # Picking grabs self.context_grab = gui.picker.ContextPickingGrabPresenter() self.context_grab.app = self self.color_grab = gui.picker.ColorPickingGrabPresenter() self.color_grab.app = 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, painting_only=True) scratchpad_tdw = tileddrawwidget.TiledDrawWidget() scratchpad_tdw.set_model(scratchpad_model) self.scratchpad_doc = document.Document(self, scratchpad_tdw, scratchpad_model) self.brushmanager = brushmanager.BrushManager( join(app_datapath, 'brushes'), join(user_datapath, 'brushes'), self) signal_callback_objs.append(self.filehandler) self.brushmodifier = brushmodifier.BrushModifier(self) signal_callback_objs.append(self.brushmodifier) self.line_mode_settings = linemode.LineModeSettings(self) # Button press mapping self.button_mapping = ButtonMapping() # Monitors pluggings and uses of input device, configures them, # and switches between device-specific brushes. self.device_monitor = gui.device.Monitor(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 Gtk.AccelMap.load(join(self.user_confpath, 'accelmap.conf')) # Load the default background image if one exists layer_stack = self.doc.model.layer_stack inited_background = False 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: layer_stack.set_background(bg, make_default=True) inited_background = True logger.info("Initialized background from %r", bg_path) break else: logger.warning( "Failed to load user's default background image %r", bg_path, ) if errors: for error in errors: logger.warning("warning: %r", error) # Otherwise, try to use a sensible fallback background image. if not inited_background: bg_path = join(self.datapath, backgroundwindow.BACKGROUNDS_SUBDIR, backgroundwindow.FALLBACK_BACKGROUND) bg, errors = backgroundwindow.load_background(bg_path) if bg: layer_stack.set_background(bg, make_default=True) inited_background = True logger.info("Initialized background from %r", bg_path) else: logger.warning( "Failed to load fallback background image %r", bg_path, ) if errors: for error in errors: logger.warning("warning: %r", error) # Double fallback. Just use a color. if not inited_background: bg_color = (0xa8, 0xa4, 0x98) layer_stack.set_background(bg_color, make_default=True) logger.info("Initialized background to %r", bg_color) inited_background = True # 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 = {} # Statusbar init statusbar = self.builder.get_object("app_statusbar") self.statusbar = statusbar context_id = statusbar.get_context_id("transient-message") self._transient_msg_context_id = context_id self._transient_msg_remove_timeout_id = None # Show main UI. self.drawWindow.show_all() GObject.idle_add(self._at_application_start, filenames, fullscreen)
def __init__(self, filenames, state_dirs, version, fullscreen=False): """Construct, but do not run. :param list filenames: The list of files to load (unicode required) :param StateDirs state_dirs: static special paths. :param unicode version: Version string for the about dialog. :param bool fullscreen: Go fullscreen after starting. Only the first filename listed will be loaded. If no files are listed, the autosave recovery dialog may be shown when the application starts up. """ assert Application._INSTANCE is None super(Application, self).__init__() Application._INSTANCE = self self.state_dirs = state_dirs #: Static special paths: see StateDirs self.version = version #: version string for the app. # Create the user's config directory and any needed R/W data # storage areas. for basedir in [state_dirs.user_config, state_dirs.user_data]: if not os.path.isdir(basedir): os.makedirs(basedir) logger.info('Created basedir %r', basedir) for datasubdir in [u'backgrounds', u'brushes', u'scratchpads']: datadir = os.path.join(state_dirs.user_data, datasubdir) if not os.path.isdir(datadir): os.mkdir(datadir) logger.info('Created data subdir %r', datadir) _init_icons(state_dirs.app_icons) # Core actions and menu structure ui_dir = os.path.dirname(os.path.abspath(__file__)) resources_xml = join(ui_dir, "resources.xml") self.builder = Gtk.Builder() self.builder.set_translation_domain("mypaint") self.builder.add_from_file(resources_xml) self.ui_manager = self.builder.get_object("app_ui_manager") signal_callback_objs = [self] Gdk.set_program_class('MyPaint') self.pixmaps = PixbufDirectory(join(state_dirs.app_data, u'pixmaps')) self.cursor_color_picker = Gdk.Cursor.new_from_pixbuf( Gdk.Display.get_default(), self.pixmaps.cursor_color_picker, 3, 15, ) self.cursor_color_picker_h = Gdk.Cursor.new_from_pixbuf( Gdk.Display.get_default(), self.pixmaps.cursor_color_picker_h, 3, 15, ) self.cursor_color_picker_c = Gdk.Cursor.new_from_pixbuf( Gdk.Display.get_default(), self.pixmaps.cursor_color_picker_c, 3, 15, ) self.cursor_color_picker_y = Gdk.Cursor.new_from_pixbuf( Gdk.Display.get_default(), self.pixmaps.cursor_color_picker_y, 3, 15, ) self.cursors = gui.cursor.CustomCursorMaker(self) # App-level settings self._preferences = lib.observable.ObservableDict() self.load_settings() # 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 # Fake inputs to send when using a mouse. Adjustable # via slider and/or hotkeys self.fakepressure = 0.5 self.fakerotation = 0.5 # Keyboard manager self.kbm = keyboard.KeyboardManager(self) # File I/O self.filehandler = filehandling.FileHandler(self) # Picking grabs self.context_grab = gui.picker.ContextPickingGrabPresenter() self.context_grab.app = self self.color_grab = gui.picker.ColorPickingGrabPresenter() self.color_grab.app = self # Load the main interface mypaint_main_xml = join(ui_dir, "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. wkspace = self.builder.get_object("app_workspace") wkspace.build_from_layout(self.preferences["workspace.layout"]) wkspace.floating_window_created += self._floating_window_created_cb fs_autohide_action = self.builder.get_object("FullscreenAutohide") fs_autohide_action.set_active(wkspace.autohide_enabled) self.workspace = wkspace # 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, painting_only=True) scratchpad_tdw = tileddrawwidget.TiledDrawWidget() scratchpad_tdw.scroll_on_allocate = False scratchpad_tdw.set_model(scratchpad_model) self.scratchpad_doc = document.Document(self, scratchpad_tdw, scratchpad_model) self.brushmanager = brushmanager.BrushManager( lib.config.mypaint_brushdir, join(self.state_dirs.user_data, 'brushes'), self, ) signal_callback_objs.append(self.filehandler) self.brushmodifier = brushmodifier.BrushModifier(self) signal_callback_objs.append(self.brushmodifier) self.blendmodemanager = blendmodehandler.BlendModeManager(self) signal_callback_objs.append(self.blendmodemanager) self.blendmodemanager.register(self.brushmodifier.bm) self.line_mode_settings = linemode.LineModeSettings(self) # Button press mapping self.button_mapping = ButtonMapping() # Monitors pluggings and uses of input device, configures them, # and switches between device-specific brushes. self.device_monitor = gui.device.Monitor(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() # Extend with some fake inputs that act kind of like brush settings self.fake_adjustment = {} # Connect signals defined in resources.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 Gtk.AccelMap.load(join(self.user_confpath, 'accelmap.conf')) # Load the default background image self.doc.reset_background() # 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 = {} # Statusbar init statusbar = self.builder.get_object("app_statusbar") self.statusbar = statusbar context_id = statusbar.get_context_id("transient-message") self._transient_msg_context_id = context_id self._transient_msg_remove_timeout_id = None # Profiling & debug stuff self.profiler = gui.profiling.Profiler() # Show main UI. self.drawWindow.show_all() GLib.idle_add(self._at_application_start, filenames, fullscreen)
def document_alloc(): for i in iterations(): document.Document()
def test_docpaint(self): """Saved and reloaded documents look identical""" # TODO: brushes should be re-saved in the new JSON format with open(join(paths.TESTS_DIR, 'brushes/s008.myb')) as fp: b1 = brush.BrushInfo(fp.read()) with open(join(paths.TESTS_DIR, 'brushes/redbrush.myb')) as fp: b2 = brush.BrushInfo(fp.read()) with open(join(paths.TESTS_DIR, 'brushes/watercolor.myb')) as fp: b3 = brush.BrushInfo(fp.read()) b = brush.BrushInfo() b.load_defaults() # test some actions doc = document.Document(b, painting_only=True) events = np.loadtxt(join(paths.TESTS_DIR, 'painting30sec.dat')) # events = events[:len(events) // 8] t_old = events[0][0] n = len(events) for i, (t, x, y, pressure) in enumerate(events): dtime = t - t_old t_old = t layer = doc.layer_stack.current layer.stroke_to( doc.brush, x / 4, y / 4, pressure, 0.0, 0.0, dtime, 1.0, # view zoom 0.0, # view rotation ) # Vary the colour so we know roughly where we are # Transition from one brush to another and occasionally make # some new layers. if i == 0: b.load_from_brushinfo(b1) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) if i == int(n * 1 / 6): b.load_from_brushinfo(b2) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) if i == int(n * 2 / 6): b.load_from_brushinfo(b3) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) if i == int(n * 3 / 6): doc.add_layer([-1]) b.load_from_brushinfo(b1) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) if i == int(n * 4 / 6): b.load_from_brushinfo(b2) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) if i == int(n * 5 / 6): b.load_from_brushinfo(b3) hsv = (i / n, 1.0, 1.0) b.set_color_hsv(hsv) # If there is an eraser (or smudging) at work, we might be # erasing tiles that are empty. Those tile get memory allocated # and affect the bounding box of the layer. This shouldn't be a # big issue, but they get dropped when loading a document, which # makes a comparision of the PNG files fail. The hack below is # to avoid that. for i, (path, layer) in enumerate(doc.layer_stack.walk()): layer._surface.remove_empty_tiles() png1a = 'test_doc1_layer%da.png' % (i, ) png1b = 'test_doc1_layer%db.png' % (i, ) layer.save_as_png(png1a) layer.save_as_png(png1b) # the resulting images will look slightly different because of # dithering self.assert_pngs_equal(png1a, png1b) # Whole doc save and load doc.save('test_doc1.ora') doc2 = document.Document() doc2.load('test_doc1.ora') # (We don't preserve the absolute position of the image, only # the size.) # assert doc.get_bbox() == doc2.get_bbox() # print('doc / doc2 bbox:', doc.get_bbox(), doc2.get_bbox()) for i, (path, layer) in enumerate(doc2.layer_stack.walk()): png1a = 'test_doc1_layer%da.png' % (i, ) png2a = 'test_doc2_layer%da.png' % (i, ) png2b = 'test_doc2_layer%db.png' % (i, ) layer.save_as_png(png2a) layer.save_as_png(png2b) self.assert_pngs_equal(png2a, png2b) self.assert_pngs_equal(png2a, png1a) doc2.save('test_doc2.ora') # check not possible, because PNGs not exactly equal:- # assert files_equal('test_f1.ora', 'test_f2.ora') # less strict test than above (just require load-save-load-save # not to alter the file) doc3 = document.Document() doc3.load('test_doc2.ora') self.assertTrue(doc2.get_bbox() == doc3.get_bbox()) # check not possible, because PNGs not exactly equal:- # assert files_equal('test_f2.ora', 'test_f3.ora') # note: this is not supposed to be strictly reproducible because # of different random seeds [huh? what does that mean?] # bbox = doc.get_bbox() # print('document bbox is', bbox) # test for appearance changes (make sure they are intended) doc.save('test_docPaint_flat.png', alpha=False) doc.save('test_docPaint_alpha.png', alpha=True) self.assert_pngs_equal( 'test_docPaint_flat.png', join(paths.TESTS_DIR, 'correct_docPaint_flat.png'), ) self.assert_pngs_equal( 'test_docPaint_alpha.png', join(paths.TESTS_DIR, 'correct_docPaint_alpha.png'), )
def document_alloc(): for i in iterations(): doc = document.Document() doc.cleanup()
def repeated_loading(): doc = document.Document() for i in iterations(): doc.load('bigimage.ora') doc.cleanup()
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)