def __init__(self, brushmanager, name=None, persistent=False): super(ManagedBrush, self).__init__() self.bm = brushmanager self._preview = None self.name = name self._brushinfo = BrushInfo() #: If True, this brush is stored in the filesystem. self.persistent = persistent #: If True, this brush is fully initialized, ready to paint with. self.settings_loaded = False # Change detection for on-disk files. self.settings_mtime = None self.preview_mtime = None if persistent: # Files are loaded later, but throw an exception now if they # don't exist. self._get_fileprefix()
def __init__(self, brushmanager, name=None, persistent=False): self.bm = brushmanager self._preview = None self.name = name self._brushinfo = BrushInfo() self.persistent = persistent #: If True this brush is stored in the filesystem. self.settings_loaded = False #: If True this brush is fully initialized, ready to paint with. self.settings_mtime = None self.preview_mtime = None if persistent: # we load the files later, but throw an exception now if they don't exist self.get_fileprefix()
def __init__(self, brushmanager, name=None, persistent=False): super(ManagedBrush, self).__init__() self.bm = brushmanager self._preview = None self._brushinfo = BrushInfo() #: The brush's relative filename, sans extension. self.name = name #: If True, this brush is stored in the filesystem. self.persistent = persistent # If True, this brush is fully initialized, ready to paint with. self._settings_loaded = False # Change detection for on-disk files. self._settings_mtime = None self._preview_mtime = None # Files are loaded later, # but throw an exception now if they don't exist. if persistent: self._get_fileprefix() assert self.name is not None
class ManagedBrush(object): """User-facing representation of a brush's settings. Managed brushes have a name, a preview image, and brush settings. The settings and the preview are loaded on demand. They cannot be selected or painted with directly, but their settings can be loaded into the running app: see `Brushmanager.select_brush()`. """ def __init__(self, brushmanager, name=None, persistent=False): super(ManagedBrush, self).__init__() self.bm = brushmanager self._preview = None self._brushinfo = BrushInfo() #: The brush's relative filename, sans extension. self.name = name #: If True, this brush is stored in the filesystem. self.persistent = persistent # If True, this brush is fully initialized, ready to paint with. self._settings_loaded = False # Change detection for on-disk files. self._settings_mtime = None self._preview_mtime = None # Files are loaded later, # but throw an exception now if they don't exist. if persistent: self._get_fileprefix() assert self.name is not None ## Preview image: loaded on demand def get_preview(self): """Gets a preview image for the brush For persistent brushes, this loads the disk preview; otherwise a fairly slow automated brush preview is used. The results are cached in RAM. """ if self._preview is None and self.name: self._load_preview() if self._preview is None: brushinfo = self.get_brushinfo() self._preview = drawutils.render_brush_preview_pixbuf(brushinfo) return self._preview def set_preview(self, pixbuf): self._preview = pixbuf preview = property(get_preview, set_preview) ## Text fields @property def description(self): """Short, user-facing tooltip description for the brush""" return self.brushinfo.get_string_property("description") @description.setter def description(self, s): self.brushinfo.set_string_property("description", s) @property def notes(self): """Longer, brush developer's notes field for a brush""" return self.brushinfo.get_string_property("notes") @notes.setter def notes(self, s): self.brushinfo.set_string_property("notes", s) ## Brush settings: loaded on demand def get_brushinfo(self): self._ensure_settings_loaded() return self._brushinfo def set_brushinfo(self, brushinfo): self._brushinfo = brushinfo brushinfo = property(get_brushinfo, set_brushinfo) ## Display def __repr__(self): if self._brushinfo.settings: pname = self._brushinfo.get_string_property("parent_brush_name") return "<ManagedBrush %r p=%s>" % (self.name, pname) else: return "<ManagedBrush %r (settings not loaded yet)>" % self.name def get_display_name(self): """Gets a displayable name for the brush.""" if self.bm.is_in_brushlist(self): # FIXME: get rid of this check dname = self.name else: dname = self.brushinfo.get_string_property("parent_brush_name") if dname is None: return _("Unknown Brush") return dname.replace("_", " ") ## Cloning def clone(self, name): """Clone this brush, and give it a new name. Creates a new brush with all the settings of this brush, assigning it a new name """ clone = ManagedBrush(self.bm) self.clone_into(clone, name=name) return clone def clone_into(self, target, name): "Copies all brush settings into another brush, giving it a new name" self._ensure_settings_loaded() target.brushinfo = self.brushinfo.clone() if self.bm.is_in_brushlist(self): # FIXME: get rid of this check! target.brushinfo.set_string_property( "parent_brush_name", self.name, ) target.preview = self.preview target.name = name ## File save/load helpers def _get_fileprefix(self, saving=False): """Returns the filesystem prefix to use when saving or loading. :param saving: caller wants a prefix to save to :type saving: bool :rtype: unicode This assigns ``self.name`` if it isn't defined. Files are stored with the returned prefix, with the extension ".myb" for brush data and "_prev.myb" for preview images. If `saving` is true, intermediate directories will be created, and the returned prefix will always contain the user brushpath. Otherwise the prefix you get depends on whether a stock brush exists and whether a user brush with the same name does not. See also `delete_from_disk()`. """ prefix = 'b' user_bp = os.path.realpath(self.bm.user_brushpath) stock_bp = os.path.realpath(self.bm.stock_brushpath) if user_bp == stock_bp: # working directly on brush collection, use different prefix prefix = 's' # Construct a new, unique name if the brush is not yet named if not self.name: i = 0 while True: self.name = u'%s%03d' % (prefix, i) a = os.path.join(self.bm.user_brushpath, self.name + '.myb') b = os.path.join(self.bm.stock_brushpath, self.name + '.myb') if not os.path.isfile(a) and not os.path.isfile(b): break i += 1 assert isinstance(self.name, unicode) # Always save to the user brush path. prefix = os.path.join(self.bm.user_brushpath, self.name) if saving: if '/' in self.name: d = os.path.dirname(prefix) if not os.path.isdir(d): os.makedirs(d) return prefix # Loading: try user first, then stock if not os.path.isfile(prefix + '.myb'): prefix = os.path.join(self.bm.stock_brushpath, self.name) if not os.path.isfile(prefix + '.myb'): raise IOError('brush "%s" not found' % self.name) return prefix def _remember_mtimes(self): prefix = self._get_fileprefix() try: preview_file = prefix + '_prev.png' self._preview_mtime = os.path.getmtime(preview_file) except OSError: logger.exception("Failed to update preview file access time") self._preview_mtime = None try: settings_file = prefix + '.myb' self._settings_mtime = os.path.getmtime(settings_file) except OSError: logger.exception("Failed to update settings file access time") self._settings_mtime = None ## Saving and deleting def save(self): """Saves the brush's settings and its preview""" prefix = self._get_fileprefix(saving=True) # Save preview: if self.preview.get_has_alpha(): # Remove alpha: # Previous mypaint versions would display an empty image w, h = PREVIEW_W, PREVIEW_H tmp = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, False, 8, w, h) tmp.fill(0xffffffff) self.preview.composite(tmp, 0, 0, w, h, 0, 0, 1, 1, GdkPixbuf.InterpType.BILINEAR, 255) self.preview = tmp preview_filename = prefix + '_prev.png' logger.debug("Saving brush preview to %r", preview_filename) lib.pixbuf.save(self.preview, preview_filename, "png") # Save brush settings brushinfo = self.brushinfo.clone() settings_filename = prefix + '.myb' logger.debug("Saving brush settings to %r", settings_filename) with open(settings_filename, 'w') as settings_fp: settings_fp.write(brushinfo.save_to_string()) # Record metadata self._remember_mtimes() def delete_from_disk(self): """Tries to delete the files for this brush from disk. :rtype: boolean Returns True if the disk files can no longer be loaded. Stock brushes cannot be deleted, but if a user brush is hiding a stock brush with the same name, then although this method will remove the files describing the user brush, the stock brush is left intact. In this case, False is returned (because a load() attempt will now load the stock brush - and in fact has just done so). """ prefix = os.path.join(self.bm.user_brushpath, self.name) if os.path.isfile(prefix + '.myb'): os.remove(prefix + '_prev.png') os.remove(prefix + '.myb') try: self.load() except IOError: # Files are no longer there, and no stock files with the # same name could be loaded. return True else: # User brush was hiding a stock brush with the same name. return False # Stock brushes cannot be deleted. return False ## Loading and reloading def load(self): """Loads the brush's preview and settings from disk.""" if self.name is None: warn("Attempt to load an unnamed brush, don't do that.", RuntimeWarning, 2) return self._load_preview() self._load_settings() def _load_preview(self): """Loads the brush preview as pixbuf into the brush.""" assert self.name prefix = self._get_fileprefix() filename = prefix + '_prev.png' try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) except Exception: logger.exception("Failed to load preview pixbuf, will fall back " "to default") pixbuf = None self._preview = pixbuf self._remember_mtimes() def _load_settings(self): """Loads the brush settings/dynamics from disk.""" prefix = self._get_fileprefix() filename = prefix + '.myb' with open(filename) as fp: brushinfo_str = fp.read() try: self._brushinfo.load_from_string(brushinfo_str) except BrushInfo.ParseError as e: logger.warning('Failed to load brush %r: %s', filename, e) self._brushinfo.load_defaults() self._remember_mtimes() self._settings_loaded = True if self.bm.is_in_brushlist(self): # FIXME: get rid of this check self._brushinfo.set_string_property("parent_brush_name", None) self.persistent = True def _has_changed_on_disk(self): prefix = self._get_fileprefix() if self._preview_mtime != os.path.getmtime(prefix + '_prev.png'): return True if self._settings_mtime != os.path.getmtime(prefix + '.myb'): return True return False def reload_if_changed(self): if self._settings_mtime is None: return if self._preview_mtime is None: return if not self.name: return if not self._has_changed_on_disk(): return False logger.info('Brush %r has changed on disk, reloading it.', self.name) self.load() return True def _ensure_settings_loaded(self): """Ensures the brush's settings are loaded, if persistent""" if self.persistent and not self._settings_loaded: logger.debug("Loading %r...", self) self.load() assert self._settings_loaded
slack = 1 return tuple( int(max(0, n)) for n in [ -dx + max_line_width + slack, -dy + max_line_width + slack, dx + max_line_width + slack, dy + max_line_width + slack, ]) ## Test code if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) import sys import lib.pixbuf for myb_file in sys.argv[1:]: if not myb_file.lower().endswith(".myb"): logger.warning("Ignored %r: not a .myb file", myb_file) continue myb_fp = open(myb_file, 'r') myb_json = myb_fp.read() myb_fp.close() myb_brushinfo = BrushInfo(myb_json) myb_pixbuf = render_brush_preview_pixbuf(myb_brushinfo) if myb_pixbuf is not None: myb_basename = myb_file[:-4] png_file = "%s_autopreview.png" % (myb_file, ) logger.info("Saving to %r...", png_file) lib.pixbuf.save(myb_pixbuf, png_file, "png")
class ManagedBrush(object): """User-facing representation of a brush's settings. Managed brushes have a name, a preview image, and brush settings. The settings and the preview are loaded on demand. They cannot be selected or painted with directly, but their settings can be loaded into the running app: see `Brushmanager.select_brush()`. """ def __init__(self, brushmanager, name=None, persistent=False): super(ManagedBrush, self).__init__() self.bm = brushmanager self._preview = None self._brushinfo = BrushInfo() #: The brush's relative filename, sans extension. self.name = name #: If True, this brush is stored in the filesystem. self.persistent = persistent # If True, this brush is fully initialized, ready to paint with. self._settings_loaded = False # Change detection for on-disk files. self._settings_mtime = None self._preview_mtime = None # Files are loaded later, # but throw an exception now if they don't exist. if persistent: self._get_fileprefix() assert self.name is not None ## Preview image: loaded on demand def get_preview(self): """Gets a preview image for the brush For persistent brushes, this loads the disk preview; otherwise a fairly slow automated brush preview is used. The results are cached in RAM. """ if self._preview is None and self.name: self._load_preview() if self._preview is None: brushinfo = self.get_brushinfo() self._preview = drawutils.render_brush_preview_pixbuf(brushinfo) return self._preview def set_preview(self, pixbuf): self._preview = pixbuf preview = property(get_preview, set_preview) ## Text fields @property def description(self): """Short, user-facing tooltip description for the brush""" return self.brushinfo.get_string_property("description") @description.setter def description(self, s): self.brushinfo.set_string_property("description", s) @property def notes(self): """Longer, brush developer's notes field for a brush""" return self.brushinfo.get_string_property("notes") @notes.setter def notes(self, s): self.brushinfo.set_string_property("notes", s) ## Brush settings: loaded on demand def get_brushinfo(self): self._ensure_settings_loaded() return self._brushinfo def set_brushinfo(self, brushinfo): self._brushinfo = brushinfo brushinfo = property(get_brushinfo, set_brushinfo) ## Display def __repr__(self): if self._brushinfo.settings: pname = self._brushinfo.get_string_property("parent_brush_name") return "<ManagedBrush %r p=%s>" % (self.name, pname) else: return "<ManagedBrush %r (settings not loaded yet)>" % self.name def get_display_name(self): """Gets a displayable name for the brush.""" if self.bm.is_in_brushlist(self): # FIXME: get rid of this check dname = self.name else: dname = self.brushinfo.get_string_property("parent_brush_name") if dname is None: return _("Unknown Brush") return dname.replace("_", " ") ## Cloning def clone(self, name): "Creates a new brush with all the settings of this brush, assigning it a new name" clone = ManagedBrush(self.bm) self.clone_into(clone, name=name) return clone def clone_into(self, target, name): "Copies all brush settings into another brush, giving it a new name" self._ensure_settings_loaded() target.brushinfo = self.brushinfo.clone() if self.bm.is_in_brushlist(self): # FIXME: get rid of this check! target.brushinfo.set_string_property("parent_brush_name", self.name) target.preview = self.preview target.name = name ## File save/load helpers def _get_fileprefix(self, saving=False): """Returns the filesystem prefix to use when saving or loading. :param saving: caller wants a prefix to save to :type saving: bool :rtype: unicode This assigns ``self.name`` if it isn't defined. Files are stored with the returned prefix, with the extension ".myb" for brush data and "_prev.myb" for preview images. If `saving` is true, intermediate directories will be created, and the returned prefix will always contain the user brushpath. Otherwise the prefix you get depends on whether a stock brush exists and whether a user brush with the same name does not. See also `delete_from_disk()`. """ prefix = 'b' if os.path.realpath(self.bm.user_brushpath) == os.path.realpath(self.bm.stock_brushpath): # working directly on brush collection, use different prefix prefix = 's' # Construct a new, unique name if the brush is not yet named if not self.name: i = 0 while 1: self.name = u'%s%03d' % (prefix, i) a = os.path.join(self.bm.user_brushpath, self.name + '.myb') b = os.path.join(self.bm.stock_brushpath, self.name + '.myb') if not os.path.isfile(a) and not os.path.isfile(b): break i += 1 assert isinstance(self.name, unicode) # Always save to the user brush path. prefix = os.path.join(self.bm.user_brushpath, self.name) if saving: if '/' in self.name: d = os.path.dirname(prefix) if not os.path.isdir(d): os.makedirs(d) return prefix # Loading: try user first, then stock if not os.path.isfile(prefix + '.myb'): prefix = os.path.join(self.bm.stock_brushpath, self.name) if not os.path.isfile(prefix + '.myb'): raise IOError('brush "%s" not found' % self.name) return prefix def _remember_mtimes(self): prefix = self._get_fileprefix() try: preview_file = prefix + '_prev.png' self._preview_mtime = os.path.getmtime(preview_file) except OSError: logger.exception("Failed to update preview file access time") self._preview_mtime = None try: settings_file = prefix + '.myb' self._settings_mtime = os.path.getmtime(settings_file) except OSError: logger.exception("Failed to update settings file access time") self._settings_mtime = None ## Saving and deleting def save(self): """Saves the brush's settings and its preview""" prefix = self._get_fileprefix(saving=True) # Save preview: if self.preview.get_has_alpha(): # Remove alpha: # Previous mypaint versions would display an empty image w, h = PREVIEW_W, PREVIEW_H tmp = gtk2compat.gdk.pixbuf.new(gdk.COLORSPACE_RGB, False, 8, w, h) tmp.fill(0xffffffff) self.preview.composite(tmp, 0, 0, w, h, 0, 0, 1, 1, gdk.INTERP_BILINEAR, 255) self.preview = tmp preview_filename = prefix + '_prev.png' logger.debug("Saving brush preview to %r", preview_filename) lib.pixbuf.save(self.preview, preview_filename, "png") # Save brush settings brushinfo = self.brushinfo.clone() settings_filename = prefix + '.myb' logger.debug("Saving brush settings to %r", settings_filename) settings_fp = open(settings_filename, 'w') settings_fp.write(brushinfo.save_to_string()) settings_fp.close() # Record metadata self._remember_mtimes() def delete_from_disk(self): """Tries to delete the files for this brush from disk. :rtype: boolean Returns True if the disk files can no longer be loaded. Stock brushes cannot be deleted, but if a user brush is hiding a stock brush with the same name, then although this method will remove the files describing the user brush, the stock brush is left intact. In this case, False is returned (because a load() attempt will now load the stock brush - and in fact has just done so). """ prefix = os.path.join(self.bm.user_brushpath, self.name) if os.path.isfile(prefix + '.myb'): os.remove(prefix + '_prev.png') os.remove(prefix + '.myb') try: self.load() except IOError: # Files are no longer there, and no stock files with the # same name could be loaded. return True else: # User brush was hiding a stock brush with the same name. return False # Stock brushes cannot be deleted. return False ## Loading and reloading def load(self): """Loads the brush's preview and settings from disk.""" if self.name is None: warn("Attempt to load an unnamed brush, don't do that.", RuntimeWarning, 2) return self._load_preview() self._load_settings() def _load_preview(self): """Loads the brush preview as pixbuf into the brush.""" assert self.name prefix = self._get_fileprefix() try: filename = prefix + '_prev.png' pixbuf = gdk.pixbuf_new_from_file(filename) except: logger.exception("Failed to load preview pixbuf, will fall back " "to default") pixbuf = None self._preview = pixbuf self._remember_mtimes() def _load_settings(self): """Loads the brush settings/dynamics from disk.""" prefix = self._get_fileprefix() filename = prefix + '.myb' brushinfo_str = open(filename).read() try: self._brushinfo.load_from_string(brushinfo_str) except BrushInfo.ParseError, e: logger.warning('Failed to load brush %r: %s' % (filename, e)) self._brushinfo.load_defaults() self._remember_mtimes() self._settings_loaded = True if self.bm.is_in_brushlist(self): # FIXME: get rid of this check self._brushinfo.set_string_property("parent_brush_name", None) self.persistent = True
class ManagedBrush(object): '''Represents a brush, but cannot be selected or painted with directly.''' def __init__(self, brushmanager, name=None, persistent=False): self.bm = brushmanager self._preview = None self.name = name self._brushinfo = BrushInfo() self.persistent = persistent #: If True this brush is stored in the filesystem. self.settings_loaded = False #: If True this brush is fully initialized, ready to paint with. self.settings_mtime = None self.preview_mtime = None if persistent: # we load the files later, but throw an exception now if they don't exist self.get_fileprefix() # load preview pixbuf on demand def get_preview(self): if self._preview is None and self.name: self._load_preview() if self._preview is None: self.preview = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, preview_w, preview_h) self.preview.fill(0xffffffff) # white return self._preview def set_preview(self, pixbuf): self._preview = pixbuf preview = property(get_preview, set_preview) # load brush settings on demand def get_brushinfo(self): if self.persistent and not self.settings_loaded: self._load_settings() return self._brushinfo def set_brushinfo(self, brushinfo): self._brushinfo = brushinfo brushinfo = property(get_brushinfo, set_brushinfo) def get_display_name(self): """Gets a displayable name for the brush. """ if self.bm.is_in_brushlist(self): # FIXME: get rid of this check dname = self.name else: dname = self.brushinfo.get_string_property("parent_brush_name") if dname is None: return _("Unknown Brush") return dname.replace("_", " ") def get_fileprefix(self, saving=False): prefix = 'b' if os.path.realpath(self.bm.user_brushpath) == os.path.realpath(self.bm.stock_brushpath): # working directly on brush collection, use different prefix prefix = 's' if not self.name: i = 0 while 1: self.name = u'%s%03d' % (prefix, i) a = os.path.join(self.bm.user_brushpath, self.name + '.myb') b = os.path.join(self.bm.stock_brushpath, self.name + '.myb') if not os.path.isfile(a) and not os.path.isfile(b): break i += 1 assert isinstance(self.name, unicode) prefix = os.path.join(self.bm.user_brushpath, self.name) if saving: if '/' in self.name: d = os.path.dirname(prefix) if not os.path.isdir(d): os.makedirs(d) return prefix if not os.path.isfile(prefix + '.myb'): prefix = os.path.join(self.bm.stock_brushpath, self.name) if not os.path.isfile(prefix + '.myb'): raise IOError, 'brush "' + self.name + '" not found' return prefix def clone(self, name): "Creates a new brush with all the settings of this brush, assigning it a new name" clone = ManagedBrush(self.bm) self.clone_into(clone, name=name) return clone def clone_into(self, target, name): "Copies all brush settings into another brush, giving it a new name" if not self.settings_loaded: # XXX refactor self.load() target.brushinfo = self.brushinfo.clone() if self.bm.is_in_brushlist(self): # FIXME: get rid of this check! target.brushinfo.set_string_property("parent_brush_name", self.name) target.preview = self.preview target.name = name def delete_from_disk(self): prefix = os.path.join(self.bm.user_brushpath, self.name) if os.path.isfile(prefix + '.myb'): os.remove(prefix + '_prev.png') os.remove(prefix + '.myb') try: self.load() except IOError: return True # success else: return False # partial success, this brush was hiding a stock brush with the same name # stock brush cannot be deleted return False def remember_mtimes(self): prefix = self.get_fileprefix() self.preview_mtime = os.path.getmtime(prefix + '_prev.png') self.settings_mtime = os.path.getmtime(prefix + '.myb') def has_changed_on_disk(self): prefix = self.get_fileprefix() if self.preview_mtime != os.path.getmtime(prefix + '_prev.png'): return True if self.settings_mtime != os.path.getmtime(prefix + '.myb'): return True return False def save(self): prefix = self.get_fileprefix(saving=True) if self.preview.get_has_alpha(): # remove it (previous mypaint versions would display an empty image) w, h = preview_w, preview_h tmp = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, w, h) tmp.fill(0xffffffff) self.preview.composite(tmp, 0, 0, w, h, 0, 0, 1, 1, gdk.INTERP_BILINEAR, 255) self.preview = tmp self.preview.save(prefix + '_prev.png', 'png') brushinfo = self.brushinfo.clone() open(prefix + '.myb', 'w').write(brushinfo.save_to_string()) self.remember_mtimes() def load(self): """Loads the brush's preview and settings from disk.""" if self.name is None: warn("Attempt to load an unnamed brush, don't do that.", RuntimeWarning, 2) return self._load_preview() self._load_settings() def _load_preview(self): """Loads the brush preview as pixbuf into the brush.""" assert self.name prefix = self.get_fileprefix() filename = prefix + '_prev.png' pixbuf = gdk.pixbuf_new_from_file(filename) self._preview = pixbuf self.remember_mtimes() def _load_settings(self): """Loads the brush settings/dynamics from disk.""" prefix = self.get_fileprefix() filename = prefix + '.myb' brushinfo_str = open(filename).read() self._brushinfo.load_from_string(brushinfo_str) self.remember_mtimes() self.settings_loaded = True if self.bm.is_in_brushlist(self): # FIXME: get rid of this check self._brushinfo.set_string_property("parent_brush_name", None) self.persistent = True def reload_if_changed(self): if self.settings_mtime is None: return if self.preview_mtime is None: return if not self.name: return if not self.has_changed_on_disk(): return False print 'Brush "' + self.name + '" has changed on disk, reloading it.' self.load() return True def __repr__(self): if self._brushinfo.settings: return "<ManagedBrush %r p=%s>" % (self.name, self._brushinfo.get_string_property("parent_brush_name")) else: return "<ManagedBrush %r (settings not loaded yet)>" % self.name
class ManagedBrush(object): '''Represents a brush, but cannot be selected or painted with directly.''' def __init__(self, brushmanager, name=None, persistent=False): self.bm = brushmanager self.preview = None self.name = name self.brushinfo = BrushInfo() self.persistent = persistent """If True this brush is stored in the filesystem.""" self.settings_loaded = False """If True this brush is fully initialized, ready to paint with.""" self.in_brushlist = False """Set to True if this brush is known to be in the brushlist""" self.settings_mtime = None self.preview_mtime = None if persistent: # we load the files later, but throw an exception now if they don't exist self.get_fileprefix() def get_fileprefix(self, saving=False): prefix = 'b' if os.path.realpath(self.bm.user_brushpath) == os.path.realpath(self.bm.stock_brushpath): # working directly on brush collection, use different prefix prefix = 's' if not self.name: i = 0 while 1: self.name = u'%s%03d' % (prefix, i) a = os.path.join(self.bm.user_brushpath, self.name + '.myb') b = os.path.join(self.bm.stock_brushpath, self.name + '.myb') if not os.path.isfile(a) and not os.path.isfile(b): break i += 1 assert isinstance(self.name, unicode) prefix = os.path.join(self.bm.user_brushpath, self.name) if saving: if '/' in self.name: d = os.path.dirname(prefix) if not os.path.isdir(d): os.makedirs(d) return prefix if not os.path.isfile(prefix + '.myb'): prefix = os.path.join(self.bm.stock_brushpath, self.name) if not os.path.isfile(prefix + '.myb'): raise IOError, 'brush "' + self.name + '" not found' return prefix def clone(self, name): "Creates a new brush with all the settings of this brush, assigning it a new name" clone = ManagedBrush(self.bm) self.clone_into(clone, name=name) return clone def clone_into(self, target, name): "Copies all brush settings into another brush, giving it a new name" if not self.settings_loaded: self.load() target.brushinfo = self.brushinfo.clone() if self.in_brushlist: target.brushinfo.set_string_property("parent_brush_name", self.name) target.preview = self.preview target.name = name def delete_from_disk(self): prefix = os.path.join(self.bm.user_brushpath, self.name) if os.path.isfile(prefix + '.myb'): os.remove(prefix + '_prev.png') os.remove(prefix + '.myb') try: self.load() except IOError: return True # success else: return False # partial success, this brush was hiding a stock brush with the same name # stock brush cannot be deleted return False def remember_mtimes(self): prefix = self.get_fileprefix() self.preview_mtime = os.path.getmtime(prefix + '_prev.png') self.settings_mtime = os.path.getmtime(prefix + '.myb') def has_changed_on_disk(self): prefix = self.get_fileprefix() if self.preview_mtime != os.path.getmtime(prefix + '_prev.png'): return True if self.settings_mtime != os.path.getmtime(prefix + '.myb'): return True return False def save(self): prefix = self.get_fileprefix(saving=True) if self.preview is None: self.preview = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, preview_w, preview_h) self.preview.fill(0xffffffff) # white self.preview.save(prefix + '_prev.png', 'png') brushinfo = self.brushinfo.clone() open(prefix + '.myb', 'w').write(brushinfo.save_to_string()) self.remember_mtimes() def load(self, retain_parent=False): """Loads the brush's preview and settings from disk.""" self.load_preview() self.load_settings(retain_parent) def load_preview(self): """Loads the brush preview as pixbuf into the brush.""" prefix = self.get_fileprefix() filename = prefix + '_prev.png' pixbuf = gdk.pixbuf_new_from_file(filename) self.preview = pixbuf self.remember_mtimes() def load_settings(self, retain_parent=False): """Loads the brush settings/dynamics from disk.""" prefix = self.get_fileprefix() filename = prefix + '.myb' brushinfo_str = open(filename).read() self.brushinfo.load_from_string(brushinfo_str) self.remember_mtimes() self.settings_loaded = True if not retain_parent: self.brushinfo.set_string_property("parent_brush_name", None) self.persistent = True def reload_if_changed(self): if self.settings_mtime is None: return if self.preview_mtime is None: return if not self.name: return if not self.has_changed_on_disk(): return False print 'Brush "' + self.name + '" has changed on disk, reloading it.' self.load() return True def __str__(self): if self.brushinfo.settings: return "<ManagedBrush %s p=%s>" % (self.name, self.brushinfo.get_string_property("parent_brush_name")) else: return "<ManagedBrush %s (settings not loaded yet)>" % self.name