コード例 #1
0
    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()
コード例 #2
0
ファイル: brushmanager.py プロジェクト: dvberkel/mypaint
    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()
コード例 #3
0
ファイル: brushmanager.py プロジェクト: AngelusDA/mypaint
    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
コード例 #4
0
ファイル: brushmanager.py プロジェクト: loentar/mypaint
    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()
コード例 #5
0
ファイル: brushmanager.py プロジェクト: Jehan/mypaint
    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
コード例 #6
0
ファイル: brushmanager.py プロジェクト: AngelusDA/mypaint
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
コード例 #7
0
ファイル: drawutils.py プロジェクト: thorsummoner/mypaint
    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")
コード例 #8
0
ファイル: brushmanager.py プロジェクト: Jehan/mypaint
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
コード例 #9
0
ファイル: brushmanager.py プロジェクト: dvberkel/mypaint
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
コード例 #10
0
ファイル: brushmanager.py プロジェクト: benosteen/mypaint
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