src/extras/scribble/default.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 # Copyright (c) 2009 Nokia Corporation
       
     2 #
       
     3 # Licensed under the Apache License, Version 2.0 (the "License");
       
     4 # you may not use this file except in compliance with the License.
       
     5 # You may obtain a copy of the License at
       
     6 #
       
     7 #     http://www.apache.org/licenses/LICENSE-2.0
       
     8 #
       
     9 # Unless required by applicable law or agreed to in writing, software
       
    10 # distributed under the License is distributed on an "AS IS" BASIS,
       
    11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    12 # See the License for the specific language governing permissions and
       
    13 # limitations under the License.
       
    14 
       
    15 import appuifw
       
    16 import e32
       
    17 import os
       
    18 import time
       
    19 import operator
       
    20 from graphics import *
       
    21 from key_codes import *
       
    22 
       
    23 if not appuifw.touch_enabled():
       
    24     appuifw.note(u"This application only works on devices that support " +
       
    25                  u"touch input")
       
    26 
       
    27 
       
    28 class PaintApp():
       
    29     BG_COLOR = 0xffffff # white
       
    30     BORDER_COLOR = 0x000000 # black
       
    31     BRUSH_COLOR = 0x000000 # black
       
    32     ERASER_ACTIVE = 0xFF0000 # Red
       
    33 
       
    34     def __init__(self):
       
    35         appuifw.app.exit_key_handler = self.quit
       
    36         self.running = True
       
    37         appuifw.app.directional_pad = False
       
    38         self.erase_mode = False
       
    39         self.saving_file = False
       
    40         self.orientation_changed = False
       
    41         self.bind_palette = True
       
    42         self.about_active = False
       
    43         self.is_about_active = False
       
    44         self.about_timer = None
       
    45         self.pen_width = 4
       
    46         self.x_max = 0
       
    47         self.y_max = 0
       
    48         self.canvas = appuifw.Canvas(event_callback=self.event_callback,
       
    49                                      redraw_callback=self.redraw_callback)
       
    50         if self.canvas.size[0] > self.canvas.size[1]:
       
    51             self.orientation = 'landscape'
       
    52         else:
       
    53             self.orientation = 'portrait'
       
    54         self.drive = unicode(os.getcwd()[0])
       
    55         self.old_body = appuifw.app.body
       
    56         appuifw.app.body = self.canvas
       
    57         appuifw.app.screen = 'full'
       
    58         appuifw.app.focus = self.focus_monitor
       
    59         self.canvas.clear()
       
    60         self.draw = self.canvas
       
    61         self.draw_img = Image.new((self.canvas.size[0], self.canvas.size[1]))
       
    62         self.draw_buttons()
       
    63         self.bind_buttons()
       
    64 
       
    65     def draw_buttons(self):
       
    66         self.x_max = self.canvas.size[0]
       
    67         self.y_max = self.canvas.size[1]
       
    68         self.pointer_advance = min(self.x_max, self.y_max) / 4
       
    69         self.toolbar_size = max(self.x_max, self.y_max) / 4
       
    70         if self.orientation == 'landscape':
       
    71             box_width = self.pointer_advance - 10
       
    72             self.menu_bar_size = self.toolbar_size / 2
       
    73             self.color_palette = self.toolbar_size - self.menu_bar_size
       
    74             draw_position = self.x_max - self.menu_bar_size
       
    75             y_displacement = (self.menu_bar_size / 2) + 10
       
    76             self.options_button = ((draw_position, 0),
       
    77                                    (self.x_max, self.pointer_advance))
       
    78             self.clear_button = ((draw_position, self.pointer_advance),
       
    79                                  (self.x_max, 2 * self.pointer_advance))
       
    80             self.eraser = ((draw_position, 2 * self.pointer_advance),
       
    81                            (self.x_max, 3 * self.pointer_advance))
       
    82             self.quit_button = ((draw_position, 3 * self.pointer_advance),
       
    83                                 (self.x_max, 4 * self.pointer_advance))
       
    84             self.draw.rectangle((draw_position, 0, self.x_max, self.y_max),
       
    85                                 fill=self.BG_COLOR)
       
    86             self.draw.rectangle((self.x_max - self.toolbar_size, 0,
       
    87                                  self.x_max - self.menu_bar_size, self.y_max),
       
    88                                 outline=self.BORDER_COLOR, width=5)
       
    89         else:
       
    90             box_width = self.pointer_advance
       
    91             self.menu_bar_size = self.toolbar_size / 4
       
    92             self.color_palette = self.toolbar_size - self.menu_bar_size
       
    93             draw_position = self.y_max - self.menu_bar_size
       
    94             y_displacement = (self.menu_bar_size / 2) + 5
       
    95             self.options_button = ((0, draw_position),
       
    96                                    (self.pointer_advance, self.y_max))
       
    97             self.clear_button = ((self.pointer_advance, draw_position),
       
    98                                  (2 * self.pointer_advance, self.y_max))
       
    99             self.eraser = ((self.pointer_advance * 2, draw_position),
       
   100                            (self.pointer_advance * 3, self.y_max))
       
   101             self.quit_button = ((self.pointer_advance * 3, draw_position),
       
   102                                 (self.pointer_advance * 4, self.y_max))
       
   103             self.draw.rectangle((0, draw_position, self.x_max, self.y_max),
       
   104                                 fill=self.BG_COLOR)
       
   105             self.draw.rectangle((0, self.y_max - self.toolbar_size,
       
   106                                  self.x_max, self.y_max - self.menu_bar_size),
       
   107                                 outline=self.BORDER_COLOR, width=5)
       
   108         # Draw the buttons at the bottom and the respective text at an offset
       
   109         # specified by x_displacement and y_displacement
       
   110         buttons = [self.options_button, self.clear_button, self.eraser,
       
   111                    self.quit_button]
       
   112         options = [u'Options', u'Clear', u'Erase', u'Quit']
       
   113         for button, text in zip(buttons, options):
       
   114             if self.erase_mode and text == u'Erase':
       
   115                 self.draw.rectangle(self.eraser, fill=self.ERASER_ACTIVE,
       
   116                                 outline=self.BORDER_COLOR, width=5)
       
   117             else:
       
   118                 self.draw.rectangle(button, outline=self.BORDER_COLOR, width=5)
       
   119             text_dimensions = self.draw.measure_text(text)
       
   120             x_displacement = (box_width - text_dimensions[0][2]) / 2
       
   121             self.draw.text((button[0][0] + x_displacement,
       
   122                             button[0][1] + y_displacement), text,
       
   123                             font=u'Sans MT TC Big5HK_S60C',
       
   124                             fill=self.BORDER_COLOR)
       
   125         self.draw_palette()
       
   126 
       
   127     def bind_buttons(self):
       
   128         self.canvas.bind(EButton1Down, self.reset_canvas, self.clear_button)
       
   129         self.canvas.bind(EButton1Down, self.options_callback,
       
   130                          self.options_button)
       
   131         self.canvas.bind(EButton1Down, self.set_exit, self.quit_button)
       
   132         self.canvas.bind(EButton1Down, self.eraser_callback, self.eraser)
       
   133 
       
   134     def clear_button_bindings(self):
       
   135         self.canvas.bind(EButton1Down, None, self.clear_button)
       
   136         self.canvas.bind(EButton1Down, None, self.options_button)
       
   137         self.canvas.bind(EButton1Down, None, self.quit_button)
       
   138         self.canvas.bind(EButton1Down, None, self.eraser)
       
   139         self.canvas.bind(EButton1Down, None)
       
   140 
       
   141     def focus_monitor(self, value):
       
   142         if value:
       
   143             self.canvas.blit(self.draw_img)
       
   144             self.draw_buttons()
       
   145 
       
   146     def set_exit(self, pos):
       
   147         appuifw.app.body = self.old_body
       
   148         self.canvas.bind(EButton1Down, None)
       
   149         self.canvas = None
       
   150         self.draw_img = None
       
   151         appuifw.app.focus = None
       
   152         self.running = False
       
   153 
       
   154     def options_callback(self, pos):
       
   155         option = appuifw.popup_menu([u'Save', u'Point/Line Width', u'About'],
       
   156                                     u'Options')
       
   157         pen_width_options = [u'1', u'2', u'3', u'4', u'5', u'6']
       
   158         if option == 0:
       
   159             self.save_callback()
       
   160         elif option == 1:
       
   161             pen_width_choice = appuifw.popup_menu(pen_width_options)
       
   162             if pen_width_choice is not None:
       
   163                 self.pen_width = int(pen_width_options[pen_width_choice])
       
   164         elif option == 2:
       
   165             self.is_about_active = True
       
   166             self.show_about()
       
   167             return
       
   168         self.canvas.blit(self.draw_img)
       
   169         self.draw_buttons()
       
   170 
       
   171     def show_about(self):
       
   172         img_path = self.drive + u':\\data\\python\\about.png'
       
   173         if self.orientation == 'landscape' or not os.path.exists(img_path):
       
   174             appuifw.note(u"Scribble is Copyright (c) 2009 Nokia Corporation")
       
   175             self.canvas.blit(self.draw_img)
       
   176             self.draw_buttons()
       
   177         else:
       
   178             self.about_window = Image.open(img_path)
       
   179             self.about_active = True
       
   180             self.clear_button_bindings()
       
   181             self.canvas.blit(self.about_window)
       
   182             self.canvas.bind(EButton1Up, self.clear_about_screen, ((0, 0),
       
   183                              (self.x_max, self.y_max)))
       
   184             self.about_timer = e32.Ao_timer()
       
   185             self.about_timer.after(5, self.clear_about_screen)
       
   186 
       
   187     def clear_about_screen(self, pos=(0, 0)):
       
   188         if self.about_timer is not None:
       
   189             self.about_timer.cancel()
       
   190             self.about_timer = None
       
   191         self.canvas.bind(EButton1Up, None, ((0, 0), (self.x_max, self.y_max)))
       
   192         self.canvas.blit(self.draw_img)
       
   193         self.bind_palette = True
       
   194         self.draw_buttons()
       
   195         self.bind_buttons()
       
   196         self.about_active = False
       
   197 
       
   198     def eraser_callback(self, pos):
       
   199         # The pen_width and fill_color change in event_callback when erase_mode
       
   200         # changes
       
   201         self.erase_mode = not self.erase_mode
       
   202         self.draw_buttons()
       
   203 
       
   204     def save_callback(self):
       
   205         if not self.saving_file:
       
   206             self.saving_file = True
       
   207             save_dir = self.drive + u":\\data\\python\\"
       
   208             if not os.path.exists(save_dir):
       
   209                 os.mkdir(save_dir)
       
   210             filename = save_dir + \
       
   211                    unicode(time.strftime("%d%m%Y%H%M%S", time.localtime())) + \
       
   212                    u".jpg"
       
   213             self.draw_img.save(filename, quality=100)
       
   214             appuifw.note(u"Saved :" + unicode(filename))
       
   215             self.canvas.blit(self.draw_img)
       
   216             self.saving_file = False
       
   217             self.draw_buttons()
       
   218 
       
   219     def set_brush_color(self, pos, color):
       
   220         self.BRUSH_COLOR = color
       
   221 
       
   222     def draw_and_bind_color(self, color):
       
   223         if self.orientation == 'portrait':
       
   224             self.top_left_x = (self.no_of_colors % (len(self.colors) / 2)) * \
       
   225                                self.color_box_width
       
   226             self.bottom_right_x = self.top_left_x + self.color_box_width
       
   227             self.top_left_y = self.y_max - self.toolbar_size
       
   228             self.bottom_right_y = self.y_max - self.menu_bar_size - \
       
   229                                   self.color_palette / 2
       
   230             if self.no_of_colors >= (len(self.colors) / 2):
       
   231                 self.top_left_y = self.bottom_right_y
       
   232                 self.bottom_right_y = self.y_max - self.menu_bar_size
       
   233         else:
       
   234             self.top_left_x = self.x_max - self.toolbar_size
       
   235             self.bottom_right_x = self.x_max - self.menu_bar_size - \
       
   236                                   self.color_palette / 2
       
   237             if self.no_of_colors >= (len(self.colors) / 2):
       
   238                 self.top_left_x = self.bottom_right_x
       
   239                 self.bottom_right_x = self.x_max - self.menu_bar_size
       
   240             self.top_left_y = (self.no_of_colors % (len(self.colors) / 2)) * \
       
   241                                self.color_box_width
       
   242             self.bottom_right_y = self.top_left_y + self.color_box_width
       
   243 
       
   244         self.top_left = (self.top_left_x, self.top_left_y)
       
   245         self.bottom_right = (self.bottom_right_x, self.bottom_right_y)
       
   246         # Draw the color rectangle and bind a function which sets the brush
       
   247         # color
       
   248         self.draw.rectangle((self.top_left, self.bottom_right),
       
   249                             fill=self.colors[color])
       
   250         if self.bind_palette:
       
   251             self.canvas.bind(EButton1Down,
       
   252                 lambda pos: self.set_brush_color(pos, self.colors[color]),
       
   253                 (self.top_left, self.bottom_right))
       
   254         self.no_of_colors += 1
       
   255 
       
   256     def draw_palette(self):
       
   257         self.colors = {'Black': 0x000000, 'Blue': 0x0000FF, 'Brown': 0xA52A2A,
       
   258                   'Gray': 0x808080, 'Green': 0x008000, 'Maroon': 0x800000,
       
   259                   'Orange': 0xFFA500, 'Pink': 0xFFC0CB, 'Purple': 0x800080,
       
   260                   'Silver': 0xC0C0C0, 'Violet': 0xEE82EE, 'Yellow': 0xFFFF00,
       
   261                   'Red': 0xFF0000, 'Lime': 0x00FF00}
       
   262         self.color_box_width = min(self.x_max, self.y_max) / (len(self.colors)
       
   263                                                                / 2)
       
   264         self.no_of_colors = 0
       
   265         map(self.draw_and_bind_color, sorted(self.colors))
       
   266         if self.bind_palette:
       
   267             self.bind_palette = False
       
   268 
       
   269     def reset_canvas(self, pos=(0, 0)):
       
   270         self.draw_img.clear(self.BG_COLOR)
       
   271         self.prev_x = 0
       
   272         self.prev_y = 0
       
   273         self.erase_mode = False
       
   274         self.canvas.clear(self.BG_COLOR)
       
   275         self.draw_buttons()
       
   276 
       
   277     def check_orientation(self):
       
   278         if not self.orientation_changed:
       
   279             self.orientation_changed = True
       
   280         else:
       
   281             self.orientation_changed = False
       
   282         self.x_max = self.canvas.size[0]
       
   283         self.y_max = self.canvas.size[1]
       
   284 
       
   285     def redraw_callback(self, rect):
       
   286         if self.about_active:
       
   287             self.canvas.blit(self.about_window)
       
   288         if rect == (0, 0, self.y_max, self.x_max) and \
       
   289                                                 self.orientation == 'portrait':
       
   290             self.orientation = 'landscape'
       
   291             self.check_orientation()
       
   292         elif rect == (0, 0, self.y_max, self.x_max) and \
       
   293                                                self.orientation == 'landscape':
       
   294             self.orientation = 'portrait'
       
   295             self.check_orientation()
       
   296 
       
   297     def event_callback(self, event):
       
   298         if not event['type'] in [EButton1Up, EButton1Down, EDrag]:
       
   299             return
       
   300 
       
   301         if event['type'] == EButton1Up and self.is_about_active:
       
   302             # This check is for ignoring button up event generated when exiting
       
   303             # `About` menu option. The flag `is_about_active` is set when
       
   304             # `About` menu is active.
       
   305             self.is_about_active = False
       
   306             return
       
   307 
       
   308         if self.erase_mode:
       
   309             pen_size = self.pen_width * 2
       
   310             outline_color = self.BG_COLOR
       
   311             fill_color = self.BG_COLOR
       
   312         else:
       
   313             pen_size = self.pen_width
       
   314             outline_color = self.BRUSH_COLOR
       
   315             fill_color = self.BRUSH_COLOR
       
   316 
       
   317         # Ignore the touch events in the region where buttons are drawn or if
       
   318         # about screen is active
       
   319         if (self.orientation == 'portrait' and event['pos'][1] > \
       
   320            (self.y_max - self.toolbar_size - 3 - (pen_size/2))) or \
       
   321            self.about_active:
       
   322             self.prev_x = event['pos'][0]
       
   323             self.prev_y = self.y_max - self.toolbar_size - 3 - (pen_size / 2)
       
   324             return
       
   325         elif (self.orientation == 'landscape' and event['pos'][0] > \
       
   326              (self.x_max - self.toolbar_size - 3 - (pen_size/2))) or \
       
   327              self.about_active:
       
   328             self.prev_x = self.x_max - self.toolbar_size - 3 - (pen_size / 2)
       
   329             self.prev_y = event['pos'][1]
       
   330             return
       
   331 
       
   332         if event['type'] in [EButton1Down, EButton1Up]:
       
   333             self.draw.point((event['pos'][0], event['pos'][1]),
       
   334                     outline=outline_color, width=pen_size, fill=fill_color)
       
   335             self.draw_img.point((event['pos'][0], event['pos'][1]),
       
   336                     outline=outline_color, width=pen_size, fill=fill_color)
       
   337         elif event['type'] == EDrag:
       
   338             rect = (self.prev_x, self.prev_y, event['pos'][0], event['pos'][1])
       
   339             redraw_rect = list(rect)
       
   340             # Ensure that the prev_x and prev_y co-ordinates are above the
       
   341             # current co-ordinates. This way we can use prev_x and prev_y as
       
   342             # the top left corner and the current co-ordinates as the bottom
       
   343             # right corner of the rect to be passed to begin_redraw.
       
   344             if redraw_rect[0] > redraw_rect[2]:
       
   345                 redraw_rect[0], redraw_rect[2] = redraw_rect[2], redraw_rect[0]
       
   346             if redraw_rect[1] > redraw_rect[3]:
       
   347                 redraw_rect[1], redraw_rect[3] = redraw_rect[3], redraw_rect[1]
       
   348             self.canvas.begin_redraw((redraw_rect[0] - pen_size,
       
   349                                       redraw_rect[1] - pen_size,
       
   350                                       redraw_rect[2] + pen_size,
       
   351                                       redraw_rect[3] + pen_size))
       
   352             self.draw.line(rect, outline=outline_color, width=pen_size,
       
   353                            fill=fill_color)
       
   354             self.draw_img.line(rect, outline=outline_color, width=pen_size,
       
   355                            fill=fill_color)
       
   356             self.canvas.end_redraw()
       
   357         self.prev_x = event['pos'][0]
       
   358         self.prev_y = event['pos'][1]
       
   359 
       
   360     def run(self):
       
   361         while self.running:
       
   362             e32.ao_sleep(0.01)
       
   363             if self.orientation_changed:
       
   364                 if self.orientation == 'landscape':
       
   365                     self.new_draw_img = self.draw_img.transpose(ROTATE_90)
       
   366                 elif self.orientation == 'portrait':
       
   367                     self.new_draw_img = self.draw_img.transpose(ROTATE_270)
       
   368                 self.draw_img = None
       
   369                 self.draw_img = Image.new((self.canvas.size[0],
       
   370                                           self.canvas.size[1]))
       
   371                 self.draw_img.blit(self.new_draw_img)
       
   372                 self.new_draw_img = None
       
   373                 self.canvas.blit(self.draw_img)
       
   374                 self.clear_button_bindings()
       
   375                 if self.about_active:
       
   376                     self.clear_about_screen()
       
   377                 self.bind_palette = True
       
   378                 self.draw_buttons()
       
   379                 self.bind_buttons()
       
   380                 self.orientation_changed = False
       
   381 
       
   382         self.quit()
       
   383 
       
   384     def quit(self):
       
   385         appuifw.app.exit_key_handler = None
       
   386         self.running = False
       
   387 
       
   388 
       
   389 if __name__ == '__main__':
       
   390     d = PaintApp()
       
   391     d.run()