# Copyright (c) 2009 Nokia Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import appuifw import e32 import os import time import operator from graphics import * from key_codes import * if not appuifw.touch_enabled(): appuifw.note(u"This application only works on devices that support " + u"touch input") class PaintApp(): BG_COLOR = 0xffffff # white BORDER_COLOR = 0x000000 # black BRUSH_COLOR = 0x000000 # black ERASER_ACTIVE = 0xFF0000 # Red def __init__(self): appuifw.app.exit_key_handler = self.quit self.running = True self.erase_mode = False self.saving_file = False self.orientation_changed = False self.bind_palette = True self.about_active = False self.about_timer = None self.dot_width = 4 self.x_max = 0 self.y_max = 0 self.canvas = appuifw.Canvas(event_callback=self.event_callback, redraw_callback=self.redraw_callback) if self.canvas.size[0] > self.canvas.size[1]: self.orientation = 'landscape' else: self.orientation = 'portrait' self.drive = unicode(os.getcwd()[0]) self.old_body = appuifw.app.body appuifw.app.body = self.canvas appuifw.app.screen = 'full_max' appuifw.app.focus = self.focus_monitor self.draw = self.canvas self.draw_img = Image.new((self.canvas.size[0], self.canvas.size[1])) self.draw_buttons() self.bind_buttons() def draw_buttons(self): self.x_max = self.canvas.size[0] self.y_max = self.canvas.size[1] self.box_width = self.canvas.size[0] / 4 self.box_height = self.canvas.size[1] - 40 self.options_button = ((0, self.box_height), (self.box_width, self.y_max)) self.clear_button = ((self.box_width, self.box_height), (self.box_width*2, self.y_max)) self.eraser = ((self.box_width*2, self.box_height), (self.box_width*3, self.y_max)) self.quit_button = ((self.box_width*3, self.box_height), (self.box_width*4, self.y_max)) self.draw.rectangle((0, self.box_height, self.x_max, self.y_max), fill=self.BG_COLOR) # Draw the buttons at the bottom and the respective text at an offset # of 22 pixels buttons = [self.options_button, self.clear_button, self.eraser, self.quit_button] options = [u'Options', u'Clear', u'Erase', u'Quit'] for button, text in zip(buttons, options): if self.erase_mode and text == u'Erase': self.draw.rectangle(self.eraser, fill=self.ERASER_ACTIVE, outline=self.BORDER_COLOR, width=5) else: self.draw.rectangle(button, outline=self.BORDER_COLOR, width=5) self.draw.text((button[0][0]+22, button[0][1]+25), text, fill=self.BORDER_COLOR) self.draw_palette() def bind_buttons(self): self.canvas.bind(EButton1Down, self.reset_canvas, self.clear_button) self.canvas.bind(EButton1Down, self.options_callback, self.options_button) self.canvas.bind(EButton1Down, self.set_exit, self.quit_button) self.canvas.bind(EButton1Down, self.eraser_callback, self.eraser) def clear_button_bindings(self): self.canvas.bind(EButton1Down, None, self.clear_button) self.canvas.bind(EButton1Down, None, self.options_button) self.canvas.bind(EButton1Down, None, self.quit_button) self.canvas.bind(EButton1Down, None, self.eraser) self.canvas.bind(EButton1Down, None) def focus_monitor(self, value): if value: self.canvas.blit(self.draw_img) self.draw_buttons() def set_exit(self, pos): appuifw.app.body = self.old_body self.canvas.bind(EButton1Down, None) self.canvas = None self.draw_img = None appuifw.app.focus = None self.running = False def options_callback(self, pos): option = appuifw.popup_menu([u'Save', u'Point/Line Width', u'About'], u'Options') if option == 0: self.save_callback() elif option == 1: dot_width = appuifw.query(u'Point/Line Width', 'number', self.dot_width) if dot_width is not None: if type(dot_width) == int and dot_width < 150: self.dot_width = dot_width else: appuifw.note(u"Invalid width") elif option == 2: self.show_about() return self.canvas.blit(self.draw_img) self.draw_buttons() def show_about(self): img_path = self.drive + u':\\data\\python\\about.png' if self.orientation == 'landscape' or not os.path.exists(img_path): appuifw.note(u"Scribble is Copyright (c) 2009 Nokia Corporation") self.canvas.blit(self.draw_img) self.draw_buttons() else: self.about_window = Image.open(img_path) self.about_active = True self.clear_button_bindings() self.canvas.blit(self.about_window) self.canvas.bind(EButton1Up, self.clear_about_screen, ((0, 0), (self.x_max, self.y_max))) self.about_timer = e32.Ao_timer() self.about_timer.after(5, self.clear_about_screen) def clear_about_screen(self, pos=(0, 0)): if self.about_timer is not None: self.about_timer.cancel() self.about_timer = None self.canvas.bind(EButton1Up, None, ((0, 0), (self.x_max, self.y_max))) self.canvas.blit(self.draw_img) self.bind_palette = True self.draw_buttons() self.bind_buttons() self.about_active = False def eraser_callback(self, pos): # The dot_width and fill_color change in event_callback when erase_mode # changes self.erase_mode = not self.erase_mode self.draw_buttons() def save_callback(self): if not self.saving_file: self.saving_file = True save_dir = self.drive + u":\\data\\python\\" if not os.path.exists(save_dir): os.mkdir(save_dir) filename = save_dir + \ unicode(time.strftime("%d%m%Y%H%M%S", time.localtime())) + \ u".jpg" self.draw_img.save(filename, quality=100) appuifw.note(u"Saved :" + unicode(filename)) self.canvas.blit(self.draw_img) self.saving_file = False self.draw_buttons() def set_brush_color(self, pos, color): self.BRUSH_COLOR = color def draw_and_bind_color(self, color): if self.no_of_colors == 7: # Draw all colors after this in another row self.top_left_x = 0 self.bottom_right_x = self.color_box_width self.bottom_right_y -= 20 self.top_left_y -= 20 self.top_left = (self.top_left_x, self.top_left_y) self.bottom_right = (self.bottom_right_x, self.bottom_right_y) # Draw the color rectangle and bind a function which sets the brush # color self.draw.rectangle((self.top_left, self.bottom_right), fill=self.colors[color]) if self.bind_palette: self.canvas.bind(EButton1Down, lambda pos: self.set_brush_color(pos, self.colors[color]), (self.top_left, self.bottom_right)) self.top_left_x += self.color_box_width self.bottom_right_x += self.color_box_width self.no_of_colors += 1 def draw_palette(self): self.colors = {'Black': 0x000000, 'Blue': 0x0000FF, 'Brown': 0xA52A2A, 'Gray': 0x808080, 'Green': 0x008000, 'Maroon': 0x800000, 'Orange': 0xFFA500, 'Pink': 0xFFC0CB, 'Purple': 0x800080, 'Silver': 0xC0C0C0, 'Violet': 0xEE82EE, 'Yellow': 0xFFFF00, 'Red': 0xFF0000, 'Lime': 0x00FF00} self.no_of_colors = 0 self.color_box_width = self.canvas.size[0] / 7 self.top_left_x = 0 self.top_left_y = self.canvas.size[1] - 60 self.bottom_right_x = self.color_box_width self.bottom_right_y = self.box_height - 1 map(self.draw_and_bind_color, sorted(self.colors)) if self.bind_palette: self.bind_palette = False def reset_canvas(self, pos=(0, 0)): self.draw_img.clear(self.BG_COLOR) self.prev_x = 0 self.prev_y = 0 self.erase_mode = False self.canvas.clear(self.BG_COLOR) self.draw_buttons() def check_orientation(self): if not self.orientation_changed: self.orientation_changed = True else: self.orientation_changed = False self.x_max = self.canvas.size[0] self.y_max = self.canvas.size[1] def redraw_callback(self, rect): if self.about_active: self.canvas.blit(self.about_window) if rect == (0, 0, self.y_max, self.x_max) and \ self.orientation == 'portrait': self.orientation = 'landscape' self.check_orientation() elif rect == (0, 0, self.y_max, self.x_max) and \ self.orientation == 'landscape': self.orientation = 'portrait' self.check_orientation() def event_callback(self, event): if not event['type'] in [EButton1Up, EButton1Down, EDrag]: return if self.erase_mode: dot_size = self.dot_width * 2 outline_color = self.BG_COLOR fill_color = self.BG_COLOR else: dot_size = self.dot_width outline_color = self.BRUSH_COLOR fill_color = self.BRUSH_COLOR # Ignore the touch events in the region where buttons are drawn or if # about screen is active if event['pos'][1] > (self.y_max - 83 - (dot_size / 2)) or \ self.about_active: return if event['type'] in [EButton1Down, EButton1Up]: self.draw.point((event['pos'][0], event['pos'][1]), outline=outline_color, width=dot_size, fill=fill_color) self.draw_img.point((event['pos'][0], event['pos'][1]), outline=outline_color, width=dot_size, fill=fill_color) elif event['type'] == EDrag: rect = (self.prev_x, self.prev_y, event['pos'][0], event['pos'][1]) self.draw.line(rect, outline=outline_color, width=dot_size, fill=fill_color) self.draw_img.line(rect, outline=outline_color, width=dot_size, fill=fill_color) self.prev_x = event['pos'][0] self.prev_y = event['pos'][1] def run(self): while self.running: e32.ao_sleep(0.01) if self.orientation_changed: if self.orientation == 'landscape': self.new_draw_img = self.draw_img.transpose(ROTATE_90) elif self.orientation == 'portrait': self.new_draw_img = self.draw_img.transpose(ROTATE_270) self.draw_img = None self.draw_img = Image.new((self.canvas.size[0], self.canvas.size[1])) self.draw_img.blit(self.new_draw_img) self.new_draw_img = None self.canvas.blit(self.draw_img) self.clear_button_bindings() if self.about_active: self.clear_about_screen() self.bind_palette = True self.draw_buttons() self.bind_buttons() self.orientation_changed = False self.quit() def quit(self): appuifw.app.exit_key_handler = None self.running = False if __name__ == '__main__': d = PaintApp() d.run()