Mostly just moving code around and renaming members.
Move all brushes and group filehandling into brushmanager.py.
Set up observers for group/brush changes instead of updating the
GUI widgets directly all over the place.
Including some minor bugfixes that have become trivial now.
# (at your option) any later version.
import os
+from os.path import join
import gtk, gobject
gdk = gtk.gdk
from lib import brush
-import filehandling, keyboard
-from brushselectionwindow import DEFAULT_BRUSH_GROUP
+import filehandling, keyboard, brushmanager
class Application: # singleton
"""
self.confpath = confpath
self.datapath = datapath
+ if not os.path.isdir(self.confpath):
+ os.mkdir(self.confpath)
+ print 'Created', self.confpath
+
self.ui_manager = gtk.UIManager()
# if we are not installed, use the the icons from the source
theme = gtk.icon_theme_get_default()
- themedir_src = os.path.join(self.datapath, 'desktop/icons')
+ themedir_src = join(self.datapath, 'desktop/icons')
theme.prepend_search_path(themedir_src)
if not theme.has_icon('mypaint'):
print 'Warning: Where have all my icons gone?'
gdk.set_program_class('MyPaint')
- self.pixmaps = PixbufDirectory(os.path.join(self.datapath, 'pixmaps'))
+ self.pixmaps = PixbufDirectory(join(self.datapath, 'pixmaps'))
self.cursor_color_picker = gdk.Cursor(gdk.display_get_default(), self.pixmaps.cursor_color_picker, 1, 30)
- self.user_brushpath = os.path.join(self.confpath, 'brushes')
- self.stock_brushpath = os.path.join(self.datapath, 'brushes')
-
- if not os.path.isdir(self.confpath):
- os.mkdir(self.confpath)
- print 'Created', self.confpath
- if not os.path.isdir(self.user_brushpath):
- os.mkdir(self.user_brushpath)
+ # unmanaged main brush; always the same instance (we can attach settings_observers)
+ # this brush is where temporary changes (color, size...) happen
+ self.brush = brush.Brush()
- self.init_brushes()
+ self.brushmanager = brushmanager.BrushManager(join(datapath, 'brushes'), join(confpath, 'brushes'))
self.kbm = keyboard.KeyboardManager()
self.filehandler = filehandling.FileHandler(self)
+ self.brushmanager.selected_brush_observers.append(self.brush_selected_cb)
+
self.window_names = '''
drawWindow
brushSettingsWindow
window.connect("realize", set_hint)
self.load_window_position(name, window)
- self.brushSelectionWindow.disable_selection_callback = False # FIXME: huh?
self.kbm.start_listening()
self.filehandler.doc = self.drawWindow.doc
self.filehandler.filename = None
- gtk.accel_map_load(os.path.join(self.confpath, 'accelmap.conf'))
-
- # TODO: remember last selected brush, and select one at frist startup
- #if self.brushes:
- # self.select_brush(self.brushes[0])
- self.brush.set_color_hsv((0, 0, 0))
+ gtk.accel_map_load(join(self.confpath, 'accelmap.conf'))
def at_application_start(*trash):
if filenames:
gobject.idle_add(at_application_start)
- def init_brushes(self):
- self.brush = brush.Brush(self)
- self.selected_brush = self.brush
- self.selected_brush_observers = []
- self.contexts = []
- for i in range(10):
- c = brush.Brush(self)
- c.name = 'context%02d' % i
- self.contexts.append(c)
- self.selected_context = None
-
- brush_by_name = {}
- def get_brush(name):
- if name not in brush_by_name:
- b = brush.Brush(self)
- b.load(name)
- brush_by_name[name] = b
- return brush_by_name[name]
-
- # maybe this should be save/loaded too?
- self.brush_by_device = {}
-
- def read_groups(filename):
- groups = {}
- if os.path.exists(filename):
- curr_group = DEFAULT_BRUSH_GROUP
- groups[curr_group] = []
- for line in open(filename):
- name = line.strip()
- if name.startswith('#'):
- continue
- if name.startswith('Group: '):
- curr_group = unicode(name[7:], 'utf-8')
- if curr_group not in groups:
- groups[curr_group] = []
- continue
- try:
- b = get_brush(name)
- except IOError, e:
- print e, '(removed from group)'
- continue
- if b in groups[curr_group]:
- print filename + ': Warning: brush appears twice in the same group, ignored'
- continue
- groups[curr_group].append(b)
- return groups
-
- # tree-way-merge of brush groups (for upgrading)
- base = read_groups(os.path.join(self.user_brushpath, 'order_default.conf'))
- our = read_groups(os.path.join(self.user_brushpath, 'order.conf'))
- their = read_groups(os.path.join(self.stock_brushpath, 'order.conf'))
-
- if base == their:
- self.brushgroups = our
- else:
- print 'Merging upstream brush changes into your collection.'
- groups = set(base).union(our).union(their)
- for group in groups:
- # treat the non-existing groups as if empty
- base_brushes = base.setdefault(group, [])
- our_brushes = our.setdefault(group, [])
- their_brushes = their.setdefault(group, [])
- # add new brushes
- insert_index = 0
- for b in their_brushes:
- if b in our_brushes:
- insert_index = our_brushes.index(b) + 1
- else:
- if b in their_brushes:
- our_brushes.insert(insert_index, b)
- insert_index += 1
- # remove deleted brushes
- for b in base_brushes:
- if b not in their_brushes and b in our_brushes:
- our_brushes.remove(b)
- # remove empty groups
- if not our_brushes:
- del our[group]
- # finish
- self.brushgroups = our
- self.save_brushorder()
- data = open(os.path.join(self.stock_brushpath, 'order.conf')).read()
- open(os.path.join(self.user_brushpath, 'order_default.conf'), 'w').write(data)
-
- # handle brushes that are in the brush directory, but not in any group
- def listbrushes(path):
- return [filename[:-4] for filename in os.listdir(path) if filename.endswith('.myb')]
- for name in listbrushes(self.stock_brushpath) + listbrushes(self.user_brushpath):
- b = get_brush(name)
- if name.startswith('context'):
- i = int(name[-2:])
- self.contexts[i] = b
- continue
- if not [True for group in our.itervalues() if b in group]:
- self.brushgroups.setdefault(DEFAULT_BRUSH_GROUP, [])
- self.brushgroups[DEFAULT_BRUSH_GROUP].insert(0, b)
-
- # clean up legacy stuff
- fn = os.path.join(self.user_brushpath, 'deleted.conf')
- if os.path.exists(fn):
- os.path.remove(fn)
-
- def save_brushorder(self):
- f = open(os.path.join(self.user_brushpath, 'order.conf'), 'w')
- f.write('# this file saves brush groups and order\n')
- for group, brushes in self.brushgroups.iteritems():
- f.write('Group: %s\n' % group.encode('utf-8'))
- for b in brushes:
- f.write(b.name + '\n')
- f.close()
-
- def select_brush(self, brush):
- assert brush is not self.brush # self.brush never gets exchanged
- self.selected_brush = brush
- if brush is not None:
+ def brush_selected_cb(self, brush):
+ assert brush is not self.brush
+ if brush:
self.brush.copy_settings_from(brush)
- for callback in self.selected_brush_observers:
- callback(brush)
-
def hide_window_cb(self, window, event):
# used by some of the windows
window.hide()
return True
def save_gui_config(self):
- gtk.accel_map_save(os.path.join(self.confpath, 'accelmap.conf'))
+ gtk.accel_map_save(join(self.confpath, 'accelmap.conf'))
self.save_window_positions()
def save_window_positions(self):
- f = open(os.path.join(self.confpath, 'windowpos.conf'), 'w')
+ f = open(join(self.confpath, 'windowpos.conf'), 'w')
f.write('# name visible x y width height\n')
for name in self.window_names:
window = self.__dict__[name]
def load_window_position(self, name, window):
try:
- for line in open(os.path.join(self.confpath, 'windowpos.conf')):
+ for line in open(join(self.confpath, 'windowpos.conf')):
if line.startswith(name):
parts = line.split()
visible = parts[1] == 'True'
def __getattr__(self, name):
if name not in self.cache:
- pixbuf = gdk.pixbuf_new_from_file(os.path.join(self.dirname, name + '.png'))
+ pixbuf = gdk.pixbuf_new_from_file(join(self.dirname, name + '.png'))
self.cache[name] = pixbuf
return self.cache[name]
--- /dev/null
+# This file is part of MyPaint.
+# Copyright (C) 2009 by Martin Renold <martinxyz@gmx.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+import gtk, pango
+gdk = gtk.gdk
+from lib import brush, document
+import tileddrawwidget, brushmanager, dialogs
+from gettext import gettext as _
+
+class Widget(gtk.HBox):
+ def __init__(self, app):
+ gtk.HBox.__init__(self)
+ self.app = app
+ self.bm = app.brushmanager
+
+ self.set_border_width(8)
+
+ left_vbox = gtk.VBox()
+ right_vbox = gtk.VBox()
+ self.pack_start(left_vbox, expand=False, fill=False)
+ self.pack_end(right_vbox, expand=False, fill=False)
+
+ #expanded part, left side
+ doc = document.Document()
+ self.tdw = tileddrawwidget.TiledDrawWidget(doc)
+ self.tdw.set_size_request(brushmanager.preview_w, brushmanager.preview_h)
+ left_vbox.pack_start(self.tdw, expand=False, fill=False)
+
+ b = gtk.Button(_('Clear'))
+ def clear_cb(window):
+ self.tdw.doc.clear_layer()
+ b.connect('clicked', clear_cb)
+ left_vbox.pack_start(b, expand=False, padding=5)
+
+ #expanded part, right side
+ l = self.brush_name_label = gtk.Label()
+ l.set_justify(gtk.JUSTIFY_LEFT)
+ l.set_text(_('(no name)'))
+ right_vbox.pack_start(l, expand=False)
+
+ right_vbox_buttons = [
+ (_('add as new'), self.create_brush_cb),
+ (_('rename...'), self.rename_brush_cb),
+ (_('remove...'), self.delete_brush_cb),
+ (_('settings...'), self.brush_settings_cb),
+ (_('save settings'), self.update_settings_cb),
+ (_('save preview'), self.update_preview_cb),
+ ]
+
+ for title, clicked_cb in right_vbox_buttons:
+ b = gtk.Button(title)
+ b.connect('clicked', clicked_cb)
+ right_vbox.pack_start(b, expand=False)
+
+ self.app.brushmanager.selected_brush_observers.append(self.brush_selected_cb)
+ self.app.brush.settings_observers.append(self.brush_modified_cb)
+
+
+ def set_preview_pixbuf(self, pixbuf):
+ if pixbuf is None:
+ self.tdw.doc.clear()
+ else:
+ self.tdw.doc.load_from_pixbuf(pixbuf)
+
+ def get_preview_pixbuf(self):
+ pixbuf = self.tdw.doc.render_as_pixbuf(0, 0, brushmanager.preview_w, brushmanager.preview_h)
+ return pixbuf
+
+ def brush_settings_cb(self, window):
+ w = self.app.brushSettingsWindow
+ w.show_all() # might be for the first time
+ w.present()
+
+ def create_brush_cb(self, window):
+ b = brushmanager.ManagedBrush(self.bm)
+ b.copy_settings_from(self.app.brush)
+ b.preview = self.get_preview_pixbuf()
+ b.save()
+
+ if self.bm.active_groups:
+ group = self.bm.active_groups[0]
+ else:
+ group = brushmanager.DEFAULT_BRUSH_GROUP
+
+ brushes = self.bm.get_group_brushes(group, make_active=True)
+ brushes.insert(0, b)
+ for f in self.bm.brushes_observers: f(brushes)
+
+ self.bm.select_brush(b)
+
+ def rename_brush_cb(self, window):
+ b = self.bm.selected_brush
+ if not b.name:
+ display = gdk.display_get_default()
+ display.beep()
+ return
+
+ name = dialogs.ask_for_name(self, _("Rename Brush"), b.name.replace('_', ' '))
+ if not name:
+ return
+ name = name.replace(' ', '_')
+ print 'renaming brush', repr(b.name), '-->', repr(name)
+ # ensure we don't overwrite an existing brush by accident
+ for group, brushes in self.bm.groups.iteritems():
+ if group == brushmanager.DELETED_BRUSH_GROUP:
+ continue
+ for b2 in brushes:
+ if b2.name == name:
+ dialogs.error(self, _('A brush with this name already exists!'))
+ return
+ success = b.delete_from_disk()
+ old_name = b.name
+ b.name = name
+ b.save()
+ if not success:
+ # we are renaming a stock brush
+ # we can't delete the original; instead we put it away so it doesn't reappear
+ old_brush = brushmanager.ManagedBrush(self.bm)
+ old_brush.load(old_name)
+ deleted_brushes = self.bm.get_group_brushes(DELETED_BRUSH_GROUP)
+ deleted_brushes.insert(0, old_brush)
+ for f in self.bm.brushes_observers: f(deleted_brushes)
+
+ self.bm.select_brush(b)
+
+ def update_preview_cb(self, window):
+ pixbuf = self.get_preview_pixbuf()
+ b = self.app.selected_brush
+ if not b.name:
+ # no brush selected
+ display = gdk.display_get_default()
+ display.beep()
+ return
+ b.preview = pixbuf
+ b.save()
+ for brushes in self.bm.groups.itervalues():
+ if b in brushes:
+ for f in self.bm.brushes_observers: f(brushes)
+
+ def update_settings_cb(self, window):
+ b = self.bm.selected_brush
+ if not b.name:
+ # no brush selected
+ display = gdk.display_get_default()
+ display.beep()
+ return
+ b.copy_settings_from(self.app.brush)
+ b.save()
+
+ def delete_brush_cb(self, window):
+ b = self.bm.selected_brush
+ if not b.name:
+ display = gdk.display_get_default()
+ display.beep()
+ return
+ if not dialogs.confirm(self, _("Really delete brush from disk?")):
+ return
+
+ self.bm.select_brush(None)
+
+ for brushes in self.bm.groups.itervalues():
+ if b in brushes:
+ brushes.remove(b)
+ for f in self.bm.brushes_observers: f(brushes)
+
+ if not b.delete_from_disk():
+ # stock brush can't be deleted
+ deleted_brushes = self.bm.get_group_brushes(brushmanager.DELETED_BRUSH_GROUP)
+ deleted_brushes.insert(0, b)
+ for f in self.bm.brushes_observers: f(deleted_brushes)
+
+ def brush_selected_cb(self, brush):
+ name = brush.name
+ if name is None:
+ name = _('(no name)')
+ else:
+ name = name.replace('_', ' ')
+ self.brush_name_label.set_text(name)
+
+ def brush_modified_cb(self):
+ self.tdw.doc.set_brush(self.app.brush)
+
--- /dev/null
+# This file is part of MyPaint.
+# Copyright (C) 2009 by Martin Renold <martinxyz@gmx.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+"""
+This module does file management for brushes and brush groups.
+"""
+
+from lib import brush
+from gtk import gdk # only for gdk.pixbuf
+import os
+
+# not translatable for now (this string is saved into a file and would screw up between language switches)
+DEFAULT_BRUSH_GROUP = 'default'
+DELETED_BRUSH_GROUP = 'deleted'
+
+preview_w = 128
+preview_h = 128
+
+class BrushManager:
+ def __init__(self, stock_brushpath, user_brushpath):
+ self.stock_brushpath = stock_brushpath
+ self.user_brushpath = user_brushpath
+
+ self.selected_brush = ManagedBrush(self)
+ self.groups = []
+ self.contexts = []
+ self.brush_by_device = {} # should be save/loaded too?
+
+ self.selected_brush_observers = []
+ self.groups_observers = [] # for both self.groups and self.active_groups
+ self.brushes_observers = [] # for all bruslists inside groups
+
+ if not os.path.isdir(self.user_brushpath):
+ os.mkdir(self.user_brushpath)
+ self.load_groups()
+
+ # TODO: load from config instead
+ self.groups.setdefault(DEFAULT_BRUSH_GROUP, [])
+ self.active_groups = [DEFAULT_BRUSH_GROUP]
+
+ self.brushes_observers.append(self.brushes_modified_cb)
+
+ def load_groups(self):
+ for i in range(10):
+ c = ManagedBrush(self)
+ c.name = 'context%02d' % i
+ self.contexts.append(c)
+ self.selected_context = None
+
+ brush_by_name = {}
+ def get_brush(name):
+ if name not in brush_by_name:
+ b = ManagedBrush(self)
+ b.load(name)
+ brush_by_name[name] = b
+ return brush_by_name[name]
+
+ def read_groups(filename):
+ groups = {}
+ if os.path.exists(filename):
+ curr_group = DEFAULT_BRUSH_GROUP
+ groups[curr_group] = []
+ for line in open(filename):
+
+ name = line.strip()
+ if name.startswith('#'):
+ continue
+ if name.startswith('Group: '):
+ curr_group = unicode(name[7:], 'utf-8')
+ if curr_group not in groups:
+ groups[curr_group] = []
+ continue
+ try:
+ b = get_brush(name)
+ except IOError, e:
+ print e, '(removed from group)'
+ continue
+ if b in groups[curr_group]:
+ print filename + ': Warning: brush appears twice in the same group, ignored'
+ continue
+ groups[curr_group].append(b)
+ return groups
+
+ # tree-way-merge of brush groups (for upgrading)
+ base = read_groups(os.path.join(self.user_brushpath, 'order_default.conf'))
+ our = read_groups(os.path.join(self.user_brushpath, 'order.conf'))
+ their = read_groups(os.path.join(self.stock_brushpath, 'order.conf'))
+
+ if base == their:
+ self.groups = our
+ else:
+ print 'Merging upstream brush changes into your collection.'
+ groups = set(base).union(our).union(their)
+ for group in groups:
+ # treat the non-existing groups as if empty
+ base_brushes = base.setdefault(group, [])
+ our_brushes = our.setdefault(group, [])
+ their_brushes = their.setdefault(group, [])
+ # add new brushes
+ insert_index = 0
+ for b in their_brushes:
+ if b in our_brushes:
+ insert_index = our_brushes.index(b) + 1
+ else:
+ if b in their_brushes:
+ our_brushes.insert(insert_index, b)
+ insert_index += 1
+ # remove deleted brushes
+ for b in base_brushes:
+ if b not in their_brushes and b in our_brushes:
+ our_brushes.remove(b)
+ # remove empty groups
+ if not our_brushes:
+ del our[group]
+ # finish
+ self.groups = our
+ self.save_brushorder()
+ data = open(os.path.join(self.stock_brushpath, 'order.conf')).read()
+ open(os.path.join(self.user_brushpath, 'order_default.conf'), 'w').write(data)
+
+ # check for brushes that are in the brush directory, but not in any group
+ def listbrushes(path):
+ return [filename[:-4] for filename in os.listdir(path) if filename.endswith('.myb')]
+ for name in listbrushes(self.stock_brushpath) + listbrushes(self.user_brushpath):
+ b = get_brush(name)
+ if name.startswith('context'):
+ i = int(name[-2:])
+ self.contexts[i] = b
+ continue
+ if not [True for group in our.itervalues() if b in group]:
+ brushes = self.groups.setdefault(DEFAULT_BRUSH_GROUP, [])
+ brushes.insert(0, b)
+
+ # clean up legacy stuff
+ fn = os.path.join(self.user_brushpath, 'deleted.conf')
+ if os.path.exists(fn):
+ os.path.remove(fn)
+
+ def brushes_modified_cb(self, brushes):
+ self.save_brushorder()
+
+ def save_brushorder(self):
+ f = open(os.path.join(self.user_brushpath, 'order.conf'), 'w')
+ f.write('# this file saves brush groups and order\n')
+ for group, brushes in self.groups.iteritems():
+ f.write('Group: %s\n' % group.encode('utf-8'))
+ for b in brushes:
+ f.write(b.name + '\n')
+ f.close()
+
+ def select_brush(self, brush):
+ if brush is None:
+ brush = ManagedBrush(self)
+ assert isinstance(brush, ManagedBrush)
+ self.selected_brush = brush
+ for callback in self.selected_brush_observers:
+ callback(brush)
+
+ def get_group_brushes(self, group, make_active=False):
+ if group not in self.groups:
+ brushes = []
+ self.groups[group] = brushes
+ for f in self.groups_observers: f()
+ if make_active and group not in self.active_groups:
+ self.active_groups.insert(0, group)
+ for f in self.groups_observers: f()
+ return self.groups[group]
+
+ def create_group(self, new_group, make_active=True):
+ return self.get_group_brushes(new_group, make_active)
+
+ def rename_group(self, old_group, new_group):
+ was_active = (old_group in self.active_groups)
+ brushes = self.create_group(new_group, make_active=was_active)
+ brushes += self.groups[old_group]
+ self.delete_group(old_group)
+
+ def delete_group(self, group):
+ homeless_brushes = self.groups[group]
+ del self.groups[group]
+ if group in self.active_groups:
+ self.active_groups.remove(group)
+
+ for brushes in self.groups.itervalues():
+ for b2 in brushes:
+ if b2 in homeless_brushes:
+ homeless_brushes.remove(b2)
+
+ if homeless_brushes:
+ deleted_brushes = self.get_group_brushes(DELETED_BRUSH_GROUP)
+ for b in homeless_brushes:
+ deleted_brushes.insert(0, b)
+ for f in self.brushes_observers: f(deleted_brushes)
+ for f in self.brushes_observers: f(homeless_brushes)
+ for f in self.groups_observers: f()
+
+
+class ManagedBrush(brush.Brush):
+ def __init__(self, brushmanager):
+ brush.Brush.__init__(self)
+ self.bm = brushmanager
+ self.preview = None
+ self.name = None
+ self.preview_changed = True
+
+ self.settings_mtime = None
+ self.preview_mtime = None
+
+ 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 = '%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
+ prefix = os.path.join(self.bm.user_brushpath, self.name)
+ if saving:
+ 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 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')
+ self.preview_changed = True # need to recreate when saving
+ return True
+ # 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_changed:
+ self.preview.save(prefix + '_prev.png', 'png')
+ self.preview_changed = False
+ open(prefix + '.myb', 'w').write(self.save_to_string())
+ self.remember_mtimes()
+
+ def load(self, name):
+ self.name = name
+ prefix = self.get_fileprefix()
+
+ filename = prefix + '_prev.png'
+ pixbuf = gdk.pixbuf_new_from_file(filename)
+ self.preview = pixbuf
+
+ if prefix.startswith(self.bm.user_brushpath):
+ self.preview_changed = False
+ else:
+ # for saving, create the preview file even if not changed
+ self.preview_changed = True
+
+ filename = prefix + '.myb'
+ errors = self.load_from_string(open(filename).read())
+ if errors:
+ print '%s:' % filename
+ for line, reason in errors:
+ print line
+ print '==>', reason
+ print
+
+ self.remember_mtimes()
+
+ 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(self.name)
+ return True
+
"select brush window"
import gtk, pango
gdk = gtk.gdk
-from lib import brush, document
-import tileddrawwidget, pixbuflist
+from lib import document
+import pixbuflist, brushcreationwidget, dialogs, brushmanager
from gettext import gettext as _
-# not translatable for now (this string is saved into a file and would screw up between language switches)
-DEFAULT_BRUSH_GROUP = 'default'
-DELETED_BRUSH_GROUP = 'deleted'
-
class Window(gtk.Window):
def __init__(self, app):
gtk.Window.__init__(self)
self.app = app
- self.app.selected_brush_observers.append(self.brush_selected_cb)
- self.app.brush.settings_observers.append(self.brush_modified_cb)
self.app.kbm.add_window(self)
self.last_selected_brush = None
#self.connect('configure-event', self.on_configure)
expander = self.expander = gtk.Expander(label=_('Edit'))
expander.set_expanded(False)
+ expander.add(brushcreationwidget.Widget(app))
vbox.pack_start(self.groupselector, expand=False)
vbox.pack_start(gtk.HSeparator(), expand=False)
vbox.pack_start(gtk.HSeparator(), expand=False)
vbox.pack_start(expander, expand=False, fill=False)
- #expanded part
- vbox2 = gtk.VBox()
- hbox = gtk.HBox()
- hbox.set_border_width(8)
- vbox2.pack_start(hbox, expand=True)
- update_button = gtk.Button(stock=gtk.STOCK_REFRESH) # FIXME: remove?
- update_button.connect('clicked', self.update_cb)
- vbox2.pack_start(update_button, expand=False)
- expander.add(vbox2)
-
- left_vbox = gtk.VBox()
- right_vbox = gtk.VBox()
- hbox.pack_start(left_vbox, expand=False, fill=False)
- hbox.pack_end(right_vbox, expand=False, fill=False)
-
- #expanded part, left side
- doc = document.Document()
- self.tdw = tileddrawwidget.TiledDrawWidget(doc)
- self.tdw.set_size_request(brush.preview_w, brush.preview_h)
- left_vbox.pack_start(self.tdw, expand=False, fill=False)
-
- b = gtk.Button(_('Clear'))
- def clear_cb(window):
- self.tdw.doc.clear_layer()
- b.connect('clicked', clear_cb)
- left_vbox.pack_start(b, expand=False, padding=5)
-
- #expanded part, right side
- l = self.brush_name_label = gtk.Label()
- l.set_justify(gtk.JUSTIFY_LEFT)
- l.set_text(_('(no name)'))
- right_vbox.pack_start(l, expand=False)
-
- right_vbox_buttons = [
- (_('add as new'), self.create_brush_cb),
- (_('rename...'), self.rename_brush_cb),
- (_('remove...'), self.delete_brush_cb),
- (_('settings...'), self.brush_settings_cb),
- (_('save settings'), self.update_settings_cb),
- (_('save preview'), self.update_preview_cb),
- ]
-
- for title, clicked_cb in right_vbox_buttons:
- b = gtk.Button(title)
- b.connect('clicked', clicked_cb)
- right_vbox.pack_start(b, expand=False)
-
- def set_preview_pixbuf(self, pixbuf):
- if pixbuf is None:
- self.tdw.doc.clear()
- else:
- self.tdw.doc.load_from_pixbuf(pixbuf)
-
- def get_preview_pixbuf(self):
- pixbuf = self.tdw.doc.render_as_pixbuf(0, 0, brush.preview_w, brush.preview_h)
- return pixbuf
-
- def brush_settings_cb(self, window):
- w = self.app.brushSettingsWindow
- w.show_all() # might be for the first time
- w.present()
-
- def create_brush_cb(self, window):
- b = brush.Brush(self.app)
- if self.app.brush:
- b.copy_settings_from(self.app.brush)
- b.preview = self.get_preview_pixbuf()
- b.save()
- active_groups = self.brushgroups.active_groups
- if active_groups:
- group = active_groups[0]
- else:
- group = DEFAULT_BRUSH_GROUP
- if group not in active_groups:
- active_groups.insert(0, group)
- self.app.brushgroups.setdefault(group, []) # create default group if needed
- self.app.brushgroups[group].insert(0, b)
- self.app.save_brushorder()
- self.brushgroups.update()
- self.groupselector.queue_draw()
- self.app.select_brush(b)
-
- def rename_brush_cb(self, window):
- b = self.app.selected_brush
- if b is None or not b.name:
- display = gtk.gdk.display_get_default()
- display.beep()
- return
-
- name = ask_for_name(self, _("Rename Brush"), b.name.replace('_', ' '))
- if not name:
- return
- name = name.replace(' ', '_')
- print 'renaming brush', repr(b.name), '-->', repr(name)
- # ensure we don't overwrite an existing brush by accident
- for groupname, brushes in self.app.brushgroups.iteritems():
- if groupname == DELETED_BRUSH_GROUP:
- continue
- for b2 in brushes:
- if b2.name == name:
- print 'Target already exists!'
- display = gtk.gdk.display_get_default()
- display.beep()
- return
- success = b.delete_from_disk()
- old_name = b.name
- b.name = name
- b.save()
- if not success:
- # we are renaming a stock brush
- # we can't delete the original; instead we put it away so it doesn't reappear
- old_brush = brush.Brush(self.app)
- old_brush.load(old_name)
- deleted_brushes = self.app.brushgroups.setdefault(DELETED_BRUSH_GROUP, [])
- deleted_brushes.insert(0, old_brush)
-
- self.app.select_brush(b)
- self.app.save_brushorder()
- self.brushgroups.update()
-
- def update_preview_cb(self, window):
- pixbuf = self.get_preview_pixbuf()
- b = self.app.selected_brush
- if b is None or not b.name:
- # no brush selected
- display = gtk.gdk.display_get_default()
- display.beep()
- return
- b.preview = pixbuf
- b.save()
- self.brushgroups.update()
-
- def update_settings_cb(self, window):
- b = self.app.selected_brush
- if b is None or not b.name:
- # no brush selected
- display = gtk.gdk.display_get_default()
- display.beep()
- return
- b.copy_settings_from(self.app.brush)
- b.save()
-
- def delete_brush_cb(self, window):
- # XXXX brushgroup update? Better idea: make "deleted" group mandatory! (and undeletable)
- b = self.app.selected_brush
- if b is None or not b.name: return
- if not run_confirm_dialog(self, _("Really delete brush from disk?")):
- return
-
- self.app.select_brush(None)
-
- for brushes in self.app.brushgroups.itervalues():
- if b in brushes:
- brushes.remove(b)
- if not b.delete_from_disk():
- # stock brush can't be deleted
- deleted_brushes = self.app.brushgroups.setdefault(DELETED_BRUSH_GROUP, [])
- deleted_brushes.insert(0, b)
-
- self.app.save_brushorder()
- self.brushgroups.update()
-
- def brush_selected_cb(self, brush):
- if brush is None: return
- name = brush.name
- if name is None:
- name = _('(no name)')
- else:
- name = name.replace('_', ' ')
- self.brush_name_label.set_text(name)
-
- def update_brush_preview(self, brush):
- self.set_preview_pixbuf(brush.preview)
- self.last_selected_brush = brush
-
- def brush_modified_cb(self):
- self.tdw.doc.set_brush(self.app.brush)
-
- def update_cb(self, button):
- callbacks = self.app.selected_brush_observers
- self.app.selected_brush_observers = callbacks
- self.brushgroups.update()
-
-def ask_for_name(window, title, default):
- d = gtk.Dialog(title,
- window,
- gtk.DIALOG_MODAL,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
- gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
-
- hbox = gtk.HBox()
- d.vbox.pack_start(hbox)
- hbox.pack_start(gtk.Label(_('Name')))
-
- d.e = e = gtk.Entry()
- e.set_text(default)
- e.select_region(0, len(default))
- def responseToDialog(entry, dialog, response):
- dialog.response(response)
- e.connect("activate", responseToDialog, d, gtk.RESPONSE_ACCEPT)
-
- hbox.pack_start(e)
- d.vbox.show_all()
- if d.run() == gtk.RESPONSE_ACCEPT:
- result = d.e.get_text()
- else:
- result = None
- d.destroy()
- return result
-
-def run_confirm_dialog(window, title):
- d = gtk.Dialog(title,
- window,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_YES, gtk.RESPONSE_ACCEPT,
- gtk.STOCK_NO, gtk.RESPONSE_REJECT))
- response = d.run()
- d.destroy()
- return response == gtk.RESPONSE_ACCEPT
class BrushList(pixbuflist.PixbufList):
def __init__(self, app, win, groupname, grouplist):
self.app = app
+ self.bm = app.brushmanager
self.win = win
self.group = groupname
self.grouplist = grouplist
- self.brushes = self.app.brushgroups[self.group]
+ self.brushes = self.bm.groups[self.group]
pixbuflist.PixbufList.__init__(self, self.brushes, 48, 48,
namefunc = lambda x: x.name,
pixbuffunc = lambda x: x.preview)
+ self.bm.brushes_observers.append(self.brushes_modified_cb)
+
+ def brushes_modified_cb(self, brushes):
+ if brushes is self.brushes:
+ self.update()
+
def remove_brush(self, brush):
self.brushes.remove(brush)
- self.update()
+ for f in self.bm.brushes_observers: f(self.brushes)
def insert_brush(self, idx, brush):
self.brushes.insert(idx, brush)
- self.update()
+ for f in self.bm.brushes_observers: f(self.brushes)
def on_drag_data(self, copy, source_widget, brush_name, target_idx):
assert source_widget, 'cannot handle drag data from another app'
b, = [b for b in source_widget.brushes if b.name == brush_name]
- if source_widget is self: # If brush dragged from same widget
+ if source_widget is self:
copy = False
else:
if b in self.brushes:
return True
if not copy:
source_widget.remove_brush(b)
- self.grouplist.active_group = self.group # hm... could use some self.select() method somewhere?
self.insert_brush(target_idx, b)
- self.app.save_brushorder()
+ for f in self.bm.brushes_observers: f(self.brushes)
return True
def on_select(self, brush):
changed = brush.reload_if_changed()
if changed:
self.update()
- self.app.select_brush(brush)
- #self.grouplist.set_active_group(self.group, brush)
+ self.bm.select_brush(brush)
class BrushGroupsList(gtk.VBox):
def __init__(self, app, window):
gtk.VBox.__init__(self)
self.app = app
+ self.bm = app.brushmanager
self.parent_window = window
- self.app.brushgroups.setdefault(DEFAULT_BRUSH_GROUP, [])
- self.active_groups = [DEFAULT_BRUSH_GROUP]
self.group_widgets = {}
self.update()
- self.app.selected_brush_observers.append(self.brush_selected_cb)
+ self.bm.selected_brush_observers.append(self.brush_selected_cb)
+ self.bm.groups_observers.append(self.brushes_modified_cb)
+
+ def brushes_modified_cb(self):
+ self.update()
def update(self):
old_widgets = self.group_widgets
self.foreach(self.remove)
- for group in self.active_groups:
+ for group in self.bm.active_groups:
if group in old_widgets:
w = old_widgets[group]
else:
gtk.DrawingArea.__init__(self)
self.app = app
+ self.bm = app.brushmanager
+ self.bm.groups_observers.append(self.active_groups_changed_cb)
self.brushgroups = brushgroups
- self.active_groups = brushgroups.active_groups
-
self.connect("expose-event", self.expose_cb)
self.connect("button-press-event", self.button_press_cb)
self.set_events(gdk.EXPOSURE_MASK |
self.idx2group = {}
self.layout = None
+ def active_groups_changed_cb(self):
+ self.queue_draw()
+
def expose_cb(self, widget, event):
cr = self.window.cairo_create()
width, height = self.window.get_size()
#attr = pango.AttrList()
#attr.insert(pango.AttrBackground(0x5555, 0x5555, 0xffff, 5, 7))
- all_groups = list(sorted(self.app.brushgroups.keys()))
+ all_groups = list(sorted(self.bm.groups.keys()))
idx = 0
text = ''
for c in s:
self.idx2group[idx] = group
idx += 1
- if group in self.active_groups:
+ if group in self.bm.active_groups:
text += '<b>' + group + '</b>'
else:
text += group
if event.button == 1:
if not group:
return
- if group in self.active_groups:
- self.active_groups.remove(group)
+ if group in self.bm.active_groups:
+ self.bm.active_groups.remove(group)
else:
- self.active_groups.insert(0, group)
+ self.bm.active_groups.insert(0, group)
self.brushgroups.update()
self.queue_draw()
elif event.button == 3:
def context_menu(self, group):
m = gtk.Menu()
menu = []
- menu = [ (_("New group..."), self.create_group) ]
+ menu = [ (_("New group..."), self.create_group_cb) ]
if group:
- menu += [ (_("Rename group..."), self.rename_group),
- (_("Delete group..."), self.delete_group)]
+ menu += [ (_("Rename group..."), self.rename_group_cb),
+ (_("Delete group..."), self.delete_group_cb)]
for label, callback in menu:
mi = gtk.MenuItem(label)
mi.connect('activate', callback, group)
m.show_all()
return m
- def create_group(self, w, group):
- new_group = ask_for_name(self.get_toplevel(), _('Create group'), '')
- if new_group and new_group not in self.app.brushgroups:
- self.app.brushgroups[new_group] = []
- self.app.save_brushorder()
- self.active_groups.insert(0, new_group)
- self.brushgroups.update()
- self.queue_draw()
-
- def rename_group(self, w, old_group):
- new_group = ask_for_name(self.get_toplevel(), _('Rename group'), old_group)
- if new_group and new_group not in self.app.brushgroups:
- self.app.brushgroups[new_group] = self.app.brushgroups[old_group]
- del self.app.brushgroups[old_group]
- self.app.save_brushorder()
- self.brushgroups.update()
- self.queue_draw()
-
- def delete_group(self,w, group):
- if run_confirm_dialog(self.get_toplevel(), _('Delete group %s') % group):
- homeless_brushes = self.app.brushgroups[group]
- del self.app.brushgroups[group]
- if group in self.active_groups:
- self.active_groups.remove(group)
-
- for brushes in self.app.brushgroups.itervalues():
- for b2 in brushes:
- if b2 in homeless_brushes:
- homeless_brushes.remove(b2)
-
- # if the user has deleted the "deleted" group, we recreate it...?
- deleted_brushes = self.app.brushgroups.setdefault(DELETED_BRUSH_GROUP, [])
- for b in homeless_brushes:
- deleted_brushes.insert(0, b)
-
- self.app.save_brushorder()
- self.brushgroups.update()
- self.queue_draw()
-
+ def create_group_cb(self, w, group):
+ new_group = dialogs.ask_for_name(self.get_toplevel(), _('Create group'), '')
+ if new_group:
+ self.bm.create_group(new_group)
+
+ def rename_group_cb(self, w, old_group):
+ new_group = dialogs.ask_for_name(self.get_toplevel(), _('Rename group'), old_group)
+ # TODO: complain if target exists
+ if new_group and new_group not in self.bm.groups:
+ self.bm.rename_group(old_group, new_group)
+
+ def delete_group_cb(self, w, group):
+ # TODO: complain if group == DELETED_BRUSH_GROUP
+ if dialogs.confirm(self.get_toplevel(), _('Delete group %s') % group):
+ self.bm.delete_group(group)
def __init__(self, app):
gtk.Window.__init__(self)
self.app = app
- self.app.selected_brush_observers.append(self.brush_selected_cb)
+ self.app.brushmanager.selected_brush_observers.append(self.brush_selected_cb)
self.app.kbm.add_window(self)
self.set_title(_('Brush settings'))
--- /dev/null
+# This file is part of MyPaint.
+# Copyright (C) 2009 by Martin Renold <martinxyz@gmx.ch>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+import gtk
+from gettext import gettext as _
+
+def confirm(window, title):
+ if not isinstance(window, gtk.Window):
+ window = window.get_toplevel()
+ d = gtk.Dialog(title,
+ window,
+ gtk.DIALOG_MODAL,
+ (gtk.STOCK_YES, gtk.RESPONSE_ACCEPT,
+ gtk.STOCK_NO, gtk.RESPONSE_REJECT))
+ response = d.run()
+ d.destroy()
+ return response == gtk.RESPONSE_ACCEPT
+
+
+def ask_for_name(window, title, default):
+ if not isinstance(window, gtk.Window):
+ window = window.get_toplevel()
+ d = gtk.Dialog(title,
+ window,
+ gtk.DIALOG_MODAL,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+ gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+
+ hbox = gtk.HBox()
+ d.vbox.pack_start(hbox)
+ hbox.pack_start(gtk.Label(_('Name')))
+
+ d.e = e = gtk.Entry()
+ e.set_text(default)
+ e.select_region(0, len(default))
+ def responseToDialog(entry, dialog, response):
+ dialog.response(response)
+ e.connect("activate", responseToDialog, d, gtk.RESPONSE_ACCEPT)
+
+ hbox.pack_start(e)
+ d.vbox.show_all()
+ if d.run() == gtk.RESPONSE_ACCEPT:
+ result = d.e.get_text()
+ else:
+ result = None
+ d.destroy()
+ return result
+
+def error(window, message):
+ if not isinstance(window, gtk.Window):
+ window = window.get_toplevel()
+ d = gtk.MessageDialog(window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
+ d.run()
+ d.destroy()
+
if si:
self.app.brush.load_from_string(si.brush_string)
- self.app.select_brush(None)
+ self.app.brushmanager.select_brush(None)
self.si = si # FIXME: should be a method parameter?
self.strokeblink_state.activate(action)
return
print 'device change:', new_device.name, new_device.source
- self.app.brush_by_device[old_device.name] = (self.app.selected_brush, self.app.brush.save_to_string())
+ bm = self.app.brushmanager
+ bm.brush_by_device[old_device.name] = (self.app.selected_brush, self.app.brush.save_to_string())
- if new_device.name in self.app.brush_by_device:
- brush_to_select, brush_settings = self.app.brush_by_device[new_device.name]
+ if new_device.name in bm.brush_by_device:
+ brush_to_select, brush_settings = bm.brush_by_device[new_device.name]
# mark as selected in brushlist
- self.app.select_brush(brush_to_select)
+ bm.select_brush(brush_to_select)
# restore modifications (radius / color change the user made)
self.app.brush.load_from_string(brush_settings)
else:
def context_cb(self, action):
name = action.get_name()
store = False
+ bm = self.app.brushmanager
if name == 'ContextStore':
- context = self.app.selected_context
+ context = bm.selected_context
if not context:
print 'No context was selected, ignoring store command.'
return
store = True
name = name[:-1]
i = int(name[-2:])
- context = self.app.contexts[i]
- self.app.selected_context = context
+ context = bm.contexts[i]
+ bm.selected_context = context
if store:
context.copy_settings_from(self.app.brush)
preview = self.app.brushSelectionWindow.get_preview_pixbuf()
# restore (but keep color)
color = self.app.brush.get_color_hsv()
context.set_color_hsv(color)
- self.app.select_brush(context)
+ bm.select_brush(context)
self.app.brushSelectionWindow.set_preview_pixbuf(context.preview)
def about_cb(self, action):
def __init__(self, app, setting, adj):
gtk.Window.__init__(self)
self.app = app
- self.app.selected_brush_observers.append(self.brush_selected_cb)
+ self.app.brushmanager.selected_brush_observers.append(self.brush_selected_cb)
self.set_title(setting.name)
self.connect('delete-event', self.app.hide_window_cb)
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
-"interface to MyBrush; hiding some C implementation details"
-# FIXME: bad file name, saying nothing about what's in here
-# FIXME: should split brush_lowlevel into its own gtk-independent module
import mypaintlib
from brushlib import brushsettings
-import gtk, os
import helpers
-preview_w = 128
-preview_h = 128
-
current_brushfile_version = 2
# points = [(x1, y1), (x2, y2), ...] (at least two points, or None)
points = [(x, func(y)) for x, y in points]
self.set_points(i, points)
-class Brush_Lowlevel(mypaintlib.Brush):
+class Brush(mypaintlib.Brush):
def __init__(self):
mypaintlib.Brush.__init__(self)
self.settings_observers = []
def is_eraser(self):
return self.setting_by_cname('eraser').base_value > 0.9
-class Brush(Brush_Lowlevel):
- def __init__(self, app):
- Brush_Lowlevel.__init__(self)
- self.app = app
- self.preview = None
- self.name = None
- self.preview_changed = True
-
- self.settings_mtime = None
- self.preview_mtime = None
-
- def get_fileprefix(self, saving=False):
- prefix = 'b'
- if os.path.realpath(self.app.user_brushpath) == os.path.realpath(self.app.stock_brushpath):
- #if os.path.samefile(self.app.user_brushpath, self.app.stock_brushpath):
- # working directly on brush collection, use different prefix
- prefix = 's'
-
- if not self.name:
- i = 0
- while 1:
- self.name = '%s%03d' % (prefix, i)
- a = os.path.join(self.app.user_brushpath,self.name + '.myb')
- b = os.path.join(self.app.stock_brushpath,self.name + '.myb')
- if not os.path.isfile(a) and not os.path.isfile(b):
- break
- i += 1
- prefix = os.path.join(self.app.user_brushpath, self.name)
- if saving:
- return prefix
- if not os.path.isfile(prefix + '.myb'):
- prefix = os.path.join(self.app.stock_brushpath,self.name)
- if not os.path.isfile(prefix + '.myb'):
- raise IOError, 'brush "' + self.name + '" not found'
- return prefix
-
- def delete_from_disk(self):
- prefix = os.path.join(self.app.user_brushpath, self.name)
- if os.path.isfile(prefix + '.myb'):
- os.remove(prefix + '_prev.png')
- os.remove(prefix + '.myb')
- self.preview_changed = True # need to recreate when saving
- return True
- # 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_changed:
- self.preview.save(prefix + '_prev.png', 'png')
- self.preview_changed = False
- open(prefix + '.myb', 'w').write(self.save_to_string())
- self.remember_mtimes()
-
- def load(self, name):
- self.name = name
- prefix = self.get_fileprefix()
-
- filename = prefix + '_prev.png'
- pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
- self.preview = pixbuf
-
- if prefix.startswith(self.app.user_brushpath):
- self.preview_changed = False
- else:
- # for saving, create the preview file even if not changed
- self.preview_changed = True
-
- filename = prefix + '.myb'
- errors = self.load_from_string(open(filename).read())
- if errors:
- print '%s:' % filename
- for line, reason in errors:
- print line
- print '==>', reason
- print
-
- self.remember_mtimes()
-
- 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(self.name)
- return True
-
import helpers, tiledsurface, pixbufsurface, backgroundsurface, mypaintlib
import command, stroke, layer
-import brush # FIXME: the brush module depends on gtk and everything, but we only need brush_lowlevel
+import brush
N = tiledsurface.N
class SaveLoadError(Exception):
# (split_stroke)
def __init__(self):
- self.brush = brush.Brush_Lowlevel()
+ self.brush = brush.Brush()
self.stroke = None
self.canvas_observers = []
self.stroke_observers = [] # callback arguments: stroke, brush (brush is a temporary read-only convenience object)
self.split_stroke()
# TODO: undo last stroke if it was very short... (but not at document level?)
real_brush = self.brush
- self.brush = brush.Brush_Lowlevel()
+ self.brush = brush.Brush()
self.brush.copy_settings_from(real_brush)
duration = 3.0
def render(self, surface):
assert self.finished
- b = brush.Brush_Lowlevel()
+ b = brush.Brush()
b.load_from_string(self.brush_settings) # OPTIMIZE: check if this is a performance bottleneck
states = numpy.fromstring(self.brush_state, dtype='float32')
def brushPaint():
s = tiledsurface.Surface()
- b = brush.Brush_Lowlevel()
+ b = brush.Brush()
#b.load_from_string(open('../brushes/s006.myb').read())
b.load_from_string(open('../brushes/charcoal.myb').read())
return equal
def docPaint():
- b1 = brush.Brush_Lowlevel()
+ b1 = brush.Brush()
b1.load_from_string(open('../brushes/s008.myb').read())
- b2 = brush.Brush_Lowlevel()
+ b2 = brush.Brush()
b2.load_from_string(open('../brushes/redbrush.myb').read())
b2.set_color_hsv((0.3, 0.4, 0.35))