1
# -*- coding: utf-8 -*-
2
# This file is part of emesene.
4
# Emesene is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# emesene is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with emesene; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
# icons stolen from gimp
20
# TODO: enhance drawing (better brushes, antialiasing)
21
# check why the pixmap is cleared when resizing
22
# simpler color picker, with popup menu
24
# embed into ConversationUI
25
# pseudo tabs to switch in conversation statusbar
26
# the lack of docstrings hurts my pylint instinct
27
# convert pixbufs to gif with pygif
35
class InkDraw( gtk.Window ):
36
def __init__(self, theme):
37
gtk.Window.__init__(self)
40
self.vbox = gtk.VBox()
41
self.hbox = gtk.HBox()
43
self.color = gtk.gdk.Color(150, 150, 150)
49
self.draw_area = gtk.DrawingArea()
51
self.draw_area.set_size_request(*size)
53
self.draw_area.connect("expose_event", self.expose_event)
54
self.draw_area.connect("configure_event", self.configure_event)
55
self.draw_area.connect("motion_notify_event", self.motion_notify_event)
56
self.draw_area.connect("button_press_event", self.button_press_event)
57
self.draw_area.connect("realize", self.realize)
58
self.draw_area.set_events(gtk.gdk.EXPOSURE_MASK
59
| gtk.gdk.LEAVE_NOTIFY_MASK
60
| gtk.gdk.BUTTON_PRESS_MASK
61
| gtk.gdk.POINTER_MOTION_MASK
62
| gtk.gdk.POINTER_MOTION_HINT_MASK)
65
self.pixelColor = gtk.ToolButton()
66
self.pixelColor.set_stock_id(gtk.STOCK_SELECT_COLOR)
67
self.pixelColor.connect("clicked", self.clickColor)
70
self.paintImage = gtk.Image()
71
self.paintImage.set_from_pixbuf(self.theme.getImage('paintbrush'))
72
self.paintButton = gtk.MenuToolButton(self.paintImage, _('Paint') )
73
self.paintButton.set_menu(sizesMenu(self.paintSize))
74
self.paintButton.connect('clicked', self.setPaint)
77
self.eraseImage = gtk.Image()
78
self.eraseImage.set_from_pixbuf(self.theme.getImage('eraser'))
79
self.eraseButton = gtk.MenuToolButton(self.eraseImage, _('Eraser') )
80
self.eraseButton.set_menu(sizesMenu(self.eraserSize))
81
self.eraseButton.connect('clicked', self.setEraser)
84
imgclear = gtk.Image()
85
imgclear.set_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_LARGE_TOOLBAR )
86
self.clear = gtk.ToolButton( imgclear, _('Clear') )
87
self.clear.connect( 'clicked', self.clearImage )
90
self.hbox.pack_start(self.pixelColor, False, False)
91
self.hbox.pack_start(self.paintButton, False, False)
92
self.hbox.pack_start(self.eraseButton, False, False)
93
self.hbox.pack_start(self.clear, False, False)
95
self.vbox.pack_start(self.draw_area)
96
self.vbox.pack_start(self.hbox)
100
def setPaint(self, widget=None):
103
def setEraser(self, widget=None):
106
def paintSize(self, widget, order):
110
def eraserSize(self, widget, order):
114
def setSize(self, order):
115
self.size = order * 5 + 1
117
def clickColor(self,widget):
118
colorDialog = gtk.ColorSelectionDialog( _( 'Choose a color' ) )
119
colorDialog.colorsel.set_has_palette( True )
120
response = colorDialog.run()
121
if response == gtk.RESPONSE_OK:
122
self.color = colorDialog.colorsel.get_current_color()
123
colorDialog.destroy()
125
def expose_event( self , widget , event):
126
x , y, width, height = event.area
127
self.draw_area.window.draw_drawable( \
128
self.draw_area.get_style().fg_gc[gtk.STATE_NORMAL],
129
self.pixmap, x, y, x, y, width, height)
132
def realize( self, widget):
133
x, y, width, height = self.draw_area.get_allocation()
134
self.pixmap = gtk.gdk.Pixmap(self.draw_area.window, width, height)
135
self.pixmap.draw_rectangle(self.draw_area.get_style().white_gc,True, 0, 0, width, height)
138
def configure_event( self, widget, event):
139
x, y, width, height = self.draw_area.get_allocation()
140
self.pixmap = gtk.gdk.Pixmap(self.draw_area.window, width, height)
141
self.pixmap.draw_rectangle(self.draw_area.get_style().white_gc,True, 0, 0, width, height)
145
def button_press_event( self, widget, event):
146
if event.button == 1 and self.pixmap != None:
147
self.draw_brush(event.x,event.y)
150
def motion_notify_event( self, widget, event):
152
x, y, state = event.window.get_pointer()
158
if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None:
159
self.draw_brush(x, y)
162
def clearImage(self,widget):
163
x, y, width, height = self.draw_area.get_allocation()
164
self.pixmap.draw_rectangle(self.draw_area.get_style().white_gc, \
165
True, 0, 0, width, height)
166
self.draw_area.queue_draw_area(0, 0, width, height)
168
def draw_brush(self, x, y):
169
# @alen: refactor me!
170
off = int ( self.size / 2 )
171
rect = (int(x-off), int(y-off), self.size,self.size)
172
win = self.draw_area.window
173
context = win.new_gc()
174
cmap = self.draw_area.get_colormap()
176
color = cmap.alloc_color(self.color)
177
context.foreground = color
179
context = self.draw_area.get_style().white_gc
180
self.pixmap.draw_rectangle(context, True, *rect)
181
self.draw_area.queue_draw_area(*rect)
183
def get_pixbuf(self):
184
size = self.pixmap.get_size()
185
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,0,8, *size)
186
pixbuf = pixbuf.get_from_drawable(self.pixmap, \
187
self.draw_area.get_colormap(), 0, 0, 0, 0, *size)
190
def sizesMenu(callback):
191
'''builds a menu with brush sizes'''
194
sizes = (_('Small'), _('Medium'), _('Large'))
198
item = gtk.MenuItem(size)
199
item.connect("activate", callback, order)
205
if __name__ == "__main__":
206
theme = Theme.Theme(None)
208
win.connect('destroy', gtk.main_quit)
210
pb = win.get_pixbuf()
211
pb.save("ink_test.png", "png")