class SlideRule(): def __init__(self, canvas, path, parent=None, sugar=True): """ Handle launch from both within and without of Sugar environment. """ self.SLIDES = {'C':[C_slide_generator], 'CI':[CI_slide_generator], 'A':[A_slide_generator], 'K':[K_slide_generator], 'S':[S_slide_generator], 'T':[T_slide_generator], 'L':[L_slide_generator], 'LLn':[LLn_slide_generator], 'Log':[Log_slide_generator], 'custom':[Custom_slide_generator]} self.STATORS = {'D':[D_stator_generator], 'DI':[DI_stator_generator], 'B':[B_stator_generator], 'K2':[K_stator_generator], 'S2':[S_stator_generator], 'T2':[T_stator_generator], 'L2':[L_stator_generator], 'LLn2':[LLn_stator_generator], 'Log2':[Log_stator_generator], 'custom2':[Custom_stator_generator]} self.path = path self.sugar = sugar self.canvas = canvas self.parent = parent parent.show_all() self.canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self.canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self.canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self.canvas.connect("draw", self.__draw_cb) self.canvas.connect("button-press-event", self._button_press_cb) self.canvas.connect("button-release-event", self._button_release_cb) self.canvas.connect("motion-notify-event", self._mouse_move_cb) self.canvas.connect("key-press-event", self._keypress_cb) self.canvas.set_can_focus(True) self.canvas.grab_focus() self.width = Gdk.Screen.width() self.height = Gdk.Screen.height() - GRID_CELL_SIZE self.sprites = Sprites(self.canvas) self.slides = [] self.stators = [] self.scale = 1 locale.setlocale(locale.LC_NUMERIC, '') self.decimal_point = locale.localeconv()['decimal_point'] if self.decimal_point == '' or self.decimal_point is None: self.decimal_point = '.' self.error_msg = None self.result_function = [None, None] self.label_function = [None, None] _logger.debug("creating slides, stators, and reticule") self.result_label = Stator(self.sprites, self.path, 'label', int((self.width - 600) / 2), SCREENOFFSET + 4 * SHEIGHT, 800, SHEIGHT) for slide in self.SLIDES: self.make_slide(slide, SLIDE) for stator in self.STATORS: self.make_slide(stator, STATOR) self.reticule = Reticule(self.sprites, self.path, 'reticule', 150, SCREENOFFSET + SHEIGHT, 100, 2 * SHEIGHT) self.reticule.draw(2000) self.press = None self.last = None self.dragpos = 0 # We need textviews for keyboard input from the on-screen keyboard self._set_screen_dpi() font_desc = Pango.font_description_from_string('12') self.text_entries = [] self.text_buffers = [] w = self.reticule.tabs[0].spr.label_safe_width() h = int(self.reticule.tabs[0].spr.label_safe_height() / 2) for i in range(4): # Reticule top & bottom; Slider left & right self.text_entries.append(Gtk.TextView()) self.text_entries[-1].set_justification(Gtk.Justification.CENTER) self.text_entries[-1].set_pixels_above_lines(4) ''' Not necessary (and doesn't work on OS8) self.text_entries[-1].override_background_color( Gtk.StateType.NORMAL, Gdk.RGBA(0, 0, 0, 0)) ''' self.text_entries[-1].modify_font(font_desc) self.text_buffers.append(self.text_entries[-1].get_buffer()) self.text_entries[-1].set_size_request(w, h) self.text_entries[-1].show() self.parent.fixed.put(self.text_entries[-1], 0, 0) self.parent.fixed.show() self.text_entries[-1].connect('focus-out-event', self._text_focus_out_cb) self.reticule.add_textview(self.text_entries[0], i=BOTTOM) self.reticule.add_textview(self.text_entries[1], i=TOP) self.reticule.set_fixed(self.parent.fixed) for slide in self.slides: slide.add_textview(self.text_entries[2], i=LEFT) slide.add_textview(self.text_entries[3], i=RIGHT) slide.set_fixed(self.parent.fixed) if not self.sugar: self.update_textview_y_offset(self.parent.menu_height) self.active_slide = self.name_to_slide('C') self.active_stator = self.name_to_stator('D') self.update_slide_labels() self.update_result_label() def update_textview_y_offset(self, dy): ''' Need to account for menu height in GNOME ''' self.reticule.tabs[0].textview_y_offset += dy self.reticule.tabs[1].textview_y_offset += dy for slide in self.slides: slide.tabs[0].textview_y_offset += dy slide.tabs[1].textview_y_offset += dy def _text_focus_out_cb(self, widget=None, event=None): ''' One of the four textviews was in focus ''' i = None if widget in self.text_entries: i = self.text_entries.index(widget) bounds = self.text_buffers[i].get_bounds() text = self.text_buffers[i].get_text(bounds[0], bounds[1], True) text = text.strip() self._process_numeric_input(i, text) def _set_screen_dpi(self): dpi = _get_screen_dpi() font_map_default = PangoCairo.font_map_get_default() font_map_default.set_resolution(dpi) def __draw_cb(self, canvas, cr): self.sprites.redraw_sprites(cr=cr) # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self.canvas.window.cairo_create() print 'set cr in do_expose' self.sprites.set_cairo_context(cr) # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self.sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _keypress_cb(self, area, event): """ Keypress: moving the slides with the arrow keys """ k = Gdk.keyval_name(event.keyval) if not self.sugar: return if k == 'a': self.parent.show_a() elif k == 'k': self.parent.show_k() elif k in ['c', 'asterisk', 'x']: self.parent.show_c() elif k in ['i', '/']: self.parent.show_ci() elif k == 's': self.parent.show_s() elif k == 't': self.parent.show_t() elif k in ['l', 'plus']: self.parent.show_l() elif k in ['Left', 'less']: if self.last is not None: self._move_slides(self.last, -1) elif k in ['Right', 'greater']: if self.last is not None: self._move_slides(self.last, 1) elif k in ['Home', 'Pause', 'Up', '^']: self._move_slides(self.name_to_stator('D').spr, - self.name_to_stator('D').spr.get_xy()[0]) elif k == 'r': self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() elif k in ['Down', 'v']: self.parent.realign_cb() self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() return True def _process_numeric_input(self, i, text): try: n = float(text.replace(self.decimal_point, '.')) if i == 0: self._move_reticule_to_stator_value(n) elif i == 1: self._move_reticule_to_slide_value(n) elif i == 2: self._move_slide_to_stator_value(n) elif i == 3: self._move_slide_to_stator_value(self._left_from_right(n)) except ValueError: self.result_label.spr.labels[0] = _('NaN') + ' ' + text return def _process_text_field(self, text_field): """ Process input from numeric text fields: could be a function. """ try: my_min = "def f(): return " + text_field.replace('import','') userdefined = {} exec my_min in globals(), userdefined return userdefined.values()[0]() except OverflowError, e: self.result_label.spr.labels[0] = _('Overflow Error') + \ ': ' + str(e) self.result_label.draw(1000) except NameError, e: self.result_label.spr.labels[0] = _('Name Error') + ': ' + str(e) self.result_label.draw(1000)
class Spirolaterals: def __init__(self, canvas, colors, parent, score=0, delay=500, pattern=1, last=None): self._canvas = canvas self._colors = colors self._parent = parent self.delay = delay self.score = score self.pattern = pattern self.last_pattern = last self._running = False self._turtle_canvas = None self._user_numbers = [1, 1, 1, 3, 2] self._active_index = 0 self._sprites = Sprites(self._canvas) self._sprites.set_delay(True) size = max(Gdk.Screen.width(), Gdk.Screen.height()) cr = self._canvas.get_property('window').cairo_create() self._turtle_canvas = cr.get_target().create_similar( cairo.CONTENT_COLOR, size, size) self._canvas.connect('draw', self.__draw_cb) self._cr = cairo.Context(self._turtle_canvas) self._cr.set_line_cap(1) # Set the line cap to be round self._sprites.set_cairo_context(self._cr) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect('button-press-event', self._button_press_cb) self._canvas.connect('key_press_event', self._keypress_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if self._width < self._height: self.i = 1 else: self.i = 0 self._calculate_scale_and_offset() self._numbers = [] self._glownumbers = [] self._create_number_sprites() self._create_turtle_sprites() self._create_results_sprites() self._set_color(colors[0]) self._set_pen_size(4) self.reset_level() def _calculate_scale_and_offset(self): self.offset = 0 if self.i == 0: self.scale = self._height / (900. - style.GRID_CELL_SIZE) * 1.25 self.offset = ( self._width - (self.sx(X1[self.i] + X2[self.i]) + self.ss(BS[self.i]))) / 2. else: self.scale = self._width / 900. self.offset = (self._width - (self.sx(X1[self.i]) + self.ss(BS[self.i]))) / 2. def reset_level(self): self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE if self._width < self._height: self.i = 1 else: self.i = 0 self._calculate_scale_and_offset() self._show_background_graphics() self._show_user_numbers() self._get_goal() self._draw_goal() self._reset_sprites() if self.score > 0: self._parent.update_score(int(self.score)) def _reset_sprites(self): x = self.sx(TX[self.i] - TS[self.i] / 2) y = self.sy(TY[self.i]) self._target_turtle.move((x, y)) x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles[0].move((x, y)) for i in range(5): for j in range(5): if self.i == 0: x = self.sx( NX[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) y = self.sy(NY[self.i]) else: x = self.sx(NX[self.i]) y = self.sy( NY[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) self._numbers[i][j].move((x, y)) self._glownumbers[i][j].move((x, y)) x = 0 y = self.sy(GY[self.i]) self._success.move((x, y)) self._success.hide() self._failure.move((x, y)) self._failure.hide() self._splot.hide() if self.last_pattern == self.pattern: self._parent.cyan.set_sensitive(True) def _keypress_cb(self, area, event): ''' Keypress: moving the slides with the arrow keys ''' k = Gdk.keyval_name(event.keyval) if k in ['1', '2', '3', '4', '5']: self.do_stop() i = self._active_index j = int(k) - 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j].set_layer(NUMBER_LAYER) self._user_numbers[i] = j + 1 self.inval(self._numbers[i][j].rect) elif k in ['KP_Up', 'j', 'Up']: self.do_stop() i = self._active_index j = self._user_numbers[i] if j < 5: j += 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j - 1].set_layer(NUMBER_LAYER) self._user_numbers[i] = j self.inval(self._numbers[i][j].rect) elif k in ['KP_Down', 'k', 'Down']: self.do_stop() i = self._active_index j = self._user_numbers[i] if j > 0: j -= 1 self._numbers[i][self._user_numbers[i] - 1].set_layer(HIDDEN_LAYER) self._numbers[i][j - 1].set_layer(NUMBER_LAYER) self._user_numbers[i] = j self.inval(self._numbers[i][j].rect) elif k in ['KP_Left', 'h', 'Left']: self.do_stop() self._active_index -= 1 self._active_index %= 5 elif k in ['KP_Right', 'l', 'Right']: self.do_stop() self._active_index += 1 self._active_index %= 5 elif k in ['Return', 'KP_Page_Up', 'KP_End']: self.do_run() elif k in ['space', 'Esc', 'KP_Page_Down', 'KP_Home']: self.do_stop() else: logging.debug(k) self._canvas.grab_focus() def _button_press_cb(self, win, event): ''' Callback to handle the button presses ''' win.grab_focus() x, y = map(int, event.get_coords()) self.press = self._sprites.find_sprite((x, y)) if self.press is not None and self.press.type == 'number': self.do_stop() i = int(self.press.name.split(',')[0]) self._active_index = i j = int(self.press.name.split(',')[1]) j1 = (j + 1) % 5 self._numbers[i][j1].set_layer(NUMBER_LAYER) self._numbers[i][j].set_layer(HIDDEN_LAYER) self._user_numbers[i] = j1 + 1 self.inval(self._numbers[i][j].rect) def _create_results_sprites(self): x = 0 y = self.sy(GY[self.i]) self._success = Sprite(self._sprites, x, y, self._parent.good_job_pixbuf()) self._success.hide() self._failure = Sprite(self._sprites, x, y, self._parent.try_again_pixbuf()) self._failure.hide() def _create_turtle_sprites(self): x = self.sx(TX[self.i] - TS[self.i] / 2) y = self.sy(TY[self.i]) pixbuf = self._parent.turtle_pixbuf() self._target_turtle = Sprite(self._sprites, x, y, pixbuf) self._user_turtles = [] x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) pixbuf = pixbuf.rotate_simple(270) self._user_turtles.append(Sprite(self._sprites, x, y, pixbuf)) self._show_turtle(0) self._splot = Sprite(self._sprites, 0, 0, self._parent.splot_pixbuf()) self._splot.hide() def _show_splot(self, x, y, dd, h): for i in range(4): self._user_turtles[i].hide() if h == 0: self._splot.move((x - int(dd / 2), y)) elif h == 1: self._splot.move((x - dd, y - int(dd / 2))) elif h == 2: self._splot.move((x - int(dd / 2), y - dd)) elif h == 3: self._splot.move((x, y - int(dd / 2))) self._splot.set_layer(SUCCESS_LAYER) self._failure.set_layer(SUCCESS_LAYER) def _show_turtle(self, t): for i in range(4): if i == t: self._user_turtles[i].set_layer(TURTLE_LAYER) else: self._user_turtles[i].hide() def _reset_user_turtle(self): x = self.sx(UX[self.i] - US[self.i] / 2) y = self.sy(UY[self.i]) self._user_turtles[0].move((x, y)) self._show_turtle(0) def _create_number_sprites(self): for i in range(5): self._numbers.append([]) self._glownumbers.append([]) for j in range(5): if self.i == 0: x = self.sx( NX[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) y = self.sy(NY[self.i]) else: x = self.sx(NX[self.i]) y = self.sy( NY[self.i]) + i * (self.ss(NS[self.i] + NO[self.i])) number = Sprite( self._sprites, x, y, self._parent.number_pixbuf(self.ss(NS[self.i]), j + 1, self._parent.sugarcolors[1])) number.type = 'number' number.name = '%d,%d' % (i, j) self._numbers[i].append(number) number = Sprite( self._sprites, x, y, self._parent.number_pixbuf(self.ss(NS[self.i]), j + 1, '#FFFFFF')) number.type = 'number' number.name = '%d,%d' % (i, j) self._glownumbers[i].append(number) def _show_user_numbers(self): # Hide the numbers for i in range(5): for j in range(5): self._numbers[i][j].set_layer(HIDDEN_LAYER) self._glownumbers[i][j].set_layer(HIDDEN_LAYER) # Show user numbers self._numbers[0][self._user_numbers[0] - 1].set_layer(NUMBER_LAYER) self._numbers[1][self._user_numbers[1] - 1].set_layer(NUMBER_LAYER) self._numbers[2][self._user_numbers[2] - 1].set_layer(NUMBER_LAYER) self._numbers[3][self._user_numbers[3] - 1].set_layer(NUMBER_LAYER) self._numbers[4][self._user_numbers[4] - 1].set_layer(NUMBER_LAYER) def _show_background_graphics(self): self._draw_pixbuf(self._parent.background_pixbuf(), 0, 0, self._width, self._height) self._draw_pixbuf(self._parent.box_pixbuf(self.ss(BS[self.i])), self.sx(X1[self.i]), self.sy(Y1[self.i]), self.ss(BS[self.i]), self.ss(BS[self.i])) self._draw_pixbuf(self._parent.box_pixbuf(self.ss(BS[self.i])), self.sx(X2[self.i]), self.sy(Y2[self.i]), self.ss(BS[self.i]), self.ss(BS[self.i])) self._draw_text(self.pattern, self.sx(X1[self.i]), self.sy(Y1[self.i]), self.ss(LS[self.i])) def _set_pen_size(self, ps): self._cr.set_line_width(ps) def _set_color(self, color): r = color[0] / 255. g = color[1] / 255. b = color[2] / 255. self._cr.set_source_rgb(r, g, b) def _draw_line(self, x1, y1, x2, y2): self._cr.move_to(x1, y1) self._cr.line_to(x2, y2) self._cr.stroke() def ss(self, f): # scale size function return int(f * self.scale) def sx(self, f): # scale x function return int(f * self.scale + self.offset) def sy(self, f): # scale y function return int(f * self.scale) def _draw_pixbuf(self, pixbuf, x, y, w, h): self._cr.save() self._cr.translate(x + w / 2., y + h / 2.) self._cr.translate(-x - w / 2., -y - h / 2.) Gdk.cairo_set_source_pixbuf(self._cr, pixbuf, x, y) self._cr.rectangle(x, y, w, h) self._cr.fill() self._cr.restore() def _draw_text(self, label, x, y, size): pl = PangoCairo.create_layout(self._cr) fd = Pango.FontDescription('Sans') fd.set_size(int(size) * Pango.SCALE) pl.set_font_description(fd) if type(label) == str or type(label) == unicode: pl.set_text(label.replace('\0', ' '), -1) elif type(label) == float or type(label) == int: pl.set_text(str(label), -1) else: pl.set_text(str(label), -1) self._cr.save() self._cr.translate(x, y) self._cr.set_source_rgb(1, 1, 1) PangoCairo.update_layout(self._cr, pl) PangoCairo.show_layout(self._cr, pl) self._cr.restore() def inval(self, r): self._canvas.queue_draw_area(r[0], r[1], r[2], r[3]) def inval_all(self): self._canvas.queue_draw_area(0, 0, self._width, self._height) def __draw_cb(self, canvas, cr): cr.set_source_surface(self._turtle_canvas) cr.paint() self._sprites.redraw_sprites(cr=cr) def do_stop(self): self._parent.green.set_sensitive(True) self._running = False def do_run(self): self._show_background_graphics() # TODO: Add turtle graphics self._success.hide() self._failure.hide() self._splot.hide() self._get_goal() self._draw_goal() self.inval_all() self._running = True self.loop = 0 self._active_index = 0 self.step = 0 self._set_pen_size(4) self._set_color(self._colors[0]) x1 = self.sx(UX[self.i]) y1 = self.sy(UY[self.i]) dd = self.ss(US[self.i]) self._numbers[0][self._user_numbers[0] - 1].set_layer(HIDDEN_LAYER) self._glownumbers[0][self._user_numbers[0] - 1].set_layer(NUMBER_LAYER) self._user_turtles[0].move((int(x1 - dd / 2), y1)) self._show_turtle(0) if self._running: GObject.timeout_add(self.delay, self._do_step, x1, y1, dd, 0) def _do_step(self, x1, y1, dd, h): if not self._running: return if self.loop > 3: return if h == 0: # up x2 = x1 y2 = y1 - dd self._user_turtles[h].move((int(x2 - dd / 2), int(y2 - dd))) elif h == 1: # right x2 = x1 + dd y2 = y1 self._user_turtles[h].move((int(x2), int(y2 - dd / 2))) elif h == 2: # down x2 = x1 y2 = y1 + dd self._user_turtles[h].move((int(x2 - dd / 2), int(y2))) elif h == 3: # left x2 = x1 - dd y2 = y1 self._user_turtles[h].move((int(x2 - dd), int(y2 - dd / 2))) self._show_turtle(h) if x2 < self.sx(X2[self.i]) or \ x2 > self.sx(X2[self.i] + BS[self.i]) or \ y2 < self.sy(Y2[self.i]) or \ y2 > self.sy(Y2[self.i] + BS[self.i]): self.do_stop() self._show_splot(x2, y2, dd, h) self._draw_line(x1, y1, x2, y2) self.inval_all() self.step += 1 i = self._active_index if self.step == self._user_numbers[i]: number = self._user_numbers[i] - 1 self._numbers[i][number].set_layer(NUMBER_LAYER) self._glownumbers[i][number].set_layer(HIDDEN_LAYER) h += 1 h %= 4 self.step = 0 self._active_index += 1 if self._active_index == 5: self.loop += 1 self._active_index = 0 else: i = self._active_index number = self._user_numbers[i] - 1 self._numbers[i][number].set_layer(HIDDEN_LAYER) self._glownumbers[i][number].set_layer(NUMBER_LAYER) if self.loop < 4 and self._running: GObject.timeout_add(self.delay, self._do_step, x2, y2, dd, h) elif self.loop == 4: # Test to see if we win self._running = False self._parent.green.set_sensitive(True) self._reset_user_turtle() self._show_user_numbers() self._test_level() def _test_level(self): success = True for i in range(5): if self._user_numbers[i] != self._goal[i]: success = False break if success: self._do_success() else: self._do_fail() def _do_success(self): self._success.set_layer(SUCCESS_LAYER) self._parent.cyan.set_sensitive(True) if self.last_pattern != self.pattern: self.score += 6 self.last_pattern = self.pattern self._parent.update_score(int(self.score)) def _do_fail(self): self._failure.set_layer(SUCCESS_LAYER) self._parent.cyan.set_sensitive(False) def do_slider(self, value): self.delay = int(value) def do_button(self, bu): self._success.hide() self._failure.hide() if bu == 'cyan': # Next level self.do_stop() self._splot.hide() self.pattern += 1 if self.pattern == 123: self.pattern = 1 self._get_goal() self._show_background_graphics() self._draw_goal() self._reset_user_turtle() self.inval_all() self._parent.cyan.set_sensitive(False) elif bu == 'green': # Run level self._parent.green.set_sensitive(False) self.do_run() elif bu == 'red': # Stop level self.do_stop() def _draw_goal(self): # draws the left hand pattern x1 = self.sx(TX[self.i]) y1 = self.sy(TY[self.i]) dd = self.ss(TS[self.i]) dx = 0 dy = -dd for i in range(4): for j in self._goal: for k in range(j): x2 = x1 + dx y2 = y1 + dy self._set_pen_size(4) self._set_color(self._colors[0]) self._draw_line(x1, y1, x2, y2) x1 = x2 y1 = y2 if dy == -dd: dx = dd dy = 0 elif dx == dd: dx = 0 dy = dd elif dy == dd: dx = -dd dy = 0 else: dx = 0 dy = -dd def _get_goal(self): fname = os.path.join('data', 'patterns.dat') try: f = open(fname, 'r') for n in range(0, self.pattern): s = f.readline() s = s[0:5] except: s = 11132 self.pattern = 1 f.close l = [int(c) for c in str(s)] self._goal = l
class SlideRule(): def __init__(self, canvas, path, parent=None, sugar=True): """ Handle launch from both within and without of Sugar environment. """ self.SLIDES = { 'C': [C_slide_generator], 'CI': [CI_slide_generator], 'A': [A_slide_generator], 'K': [K_slide_generator], 'S': [S_slide_generator], 'T': [T_slide_generator], 'L': [L_slide_generator], 'LLn': [LLn_slide_generator], 'Log': [Log_slide_generator], 'custom': [Custom_slide_generator] } self.STATORS = { 'D': [D_stator_generator], 'DI': [DI_stator_generator], 'B': [B_stator_generator], 'K2': [K_stator_generator], 'S2': [S_stator_generator], 'T2': [T_stator_generator], 'L2': [L_stator_generator], 'LLn2': [LLn_stator_generator], 'Log2': [Log_stator_generator], 'custom2': [Custom_stator_generator] } self.path = path self.sugar = sugar self.canvas = canvas self.parent = parent parent.show_all() self.canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self.canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self.canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self.canvas.connect("draw", self.__draw_cb) self.canvas.connect("button-press-event", self._button_press_cb) self.canvas.connect("button-release-event", self._button_release_cb) self.canvas.connect("motion-notify-event", self._mouse_move_cb) self.canvas.connect("key-press-event", self._keypress_cb) self.canvas.set_can_focus(True) self.canvas.grab_focus() self.width = Gdk.Screen.width() self.height = Gdk.Screen.height() - GRID_CELL_SIZE self.sprites = Sprites(self.canvas) self.slides = [] self.stators = [] self.scale = 1 locale.setlocale(locale.LC_NUMERIC, '') self.decimal_point = locale.localeconv()['decimal_point'] if self.decimal_point == '' or self.decimal_point is None: self.decimal_point = '.' self.error_msg = None self.result_function = [None, None] self.label_function = [None, None] _logger.debug("creating slides, stators, and reticule") self.result_label = Stator(self.sprites, self.path, 'label', int((self.width - 600) / 2), SCREENOFFSET + 4 * SHEIGHT, 800, SHEIGHT) for slide in self.SLIDES: self.make_slide(slide, SLIDE) for stator in self.STATORS: self.make_slide(stator, STATOR) self.reticule = Reticule(self.sprites, self.path, 'reticule', 150, SCREENOFFSET + SHEIGHT, 100, 2 * SHEIGHT) self.reticule.draw(2000) self.press = None self.last = None self.dragpos = 0 # We need textviews for keyboard input from the on-screen keyboard self._set_screen_dpi() font_desc = Pango.font_description_from_string('12') self.text_entries = [] self.text_buffers = [] w = self.reticule.tabs[0].spr.label_safe_width() h = int(self.reticule.tabs[0].spr.label_safe_height() / 2) for i in range(4): # Reticule top & bottom; Slider left & right self.text_entries.append(Gtk.TextView()) self.text_entries[-1].set_justification(Gtk.Justification.CENTER) self.text_entries[-1].set_pixels_above_lines(4) ''' Not necessary (and doesn't work on OS8) self.text_entries[-1].override_background_color( Gtk.StateType.NORMAL, Gdk.RGBA(0, 0, 0, 0)) ''' self.text_entries[-1].modify_font(font_desc) self.text_buffers.append(self.text_entries[-1].get_buffer()) self.text_entries[-1].set_size_request(w, h) self.text_entries[-1].show() self.parent.fixed.put(self.text_entries[-1], 0, 0) self.parent.fixed.show() self.text_entries[-1].connect('focus-out-event', self._text_focus_out_cb) self.reticule.add_textview(self.text_entries[0], i=BOTTOM) self.reticule.add_textview(self.text_entries[1], i=TOP) self.reticule.set_fixed(self.parent.fixed) for slide in self.slides: slide.add_textview(self.text_entries[2], i=LEFT) slide.add_textview(self.text_entries[3], i=RIGHT) slide.set_fixed(self.parent.fixed) if not self.sugar: self.update_textview_y_offset(self.parent.menu_height) self.active_slide = self.name_to_slide('C') self.active_stator = self.name_to_stator('D') self.update_slide_labels() self.update_result_label() def update_textview_y_offset(self, dy): ''' Need to account for menu height in GNOME ''' self.reticule.tabs[0].textview_y_offset += dy self.reticule.tabs[1].textview_y_offset += dy for slide in self.slides: slide.tabs[0].textview_y_offset += dy slide.tabs[1].textview_y_offset += dy def _text_focus_out_cb(self, widget=None, event=None): ''' One of the four textviews was in focus ''' i = None if widget in self.text_entries: i = self.text_entries.index(widget) bounds = self.text_buffers[i].get_bounds() text = self.text_buffers[i].get_text(bounds[0], bounds[1], True) text = text.strip() self._process_numeric_input(i, text) def _set_screen_dpi(self): dpi = _get_screen_dpi() font_map_default = PangoCairo.font_map_get_default() font_map_default.set_resolution(dpi) def __draw_cb(self, canvas, cr): self.sprites.redraw_sprites(cr=cr) # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self.canvas.window.cairo_create() print('set cr in do_expose') self.sprites.set_cairo_context(cr) # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self.sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _keypress_cb(self, area, event): """ Keypress: moving the slides with the arrow keys """ k = Gdk.keyval_name(event.keyval) if not self.sugar: return if k == 'a': self.parent.show_a() elif k == 'k': self.parent.show_k() elif k in ['c', 'asterisk', 'x']: self.parent.show_c() elif k in ['i', '/']: self.parent.show_ci() elif k == 's': self.parent.show_s() elif k == 't': self.parent.show_t() elif k in ['l', 'plus']: self.parent.show_l() elif k in ['Left', 'less']: if self.last is not None: self._move_slides(self.last, -1) elif k in ['Right', 'greater']: if self.last is not None: self._move_slides(self.last, 1) elif k in ['Home', 'Pause', 'Up', '^']: self._move_slides( self.name_to_stator('D').spr, -self.name_to_stator('D').spr.get_xy()[0]) elif k == 'r': self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() elif k in ['Down', 'v']: self.parent.realign_cb() self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() return True def _process_numeric_input(self, i, text): try: n = float(text.replace(self.decimal_point, '.')) if i == 0: self._move_reticule_to_stator_value(n) elif i == 1: self._move_reticule_to_slide_value(n) elif i == 2: self._move_slide_to_stator_value(n) elif i == 3: self._move_slide_to_stator_value(self._left_from_right(n)) except ValueError: self.result_label.spr.labels[0] = _('NaN') + ' ' + text return def _process_text_field(self, text_field): """ Process input from numeric text fields: could be a function. """ try: my_min = "def f(): return " + text_field.replace('import', '') userdefined = {} exec(my_min, globals(), userdefined) return list(userdefined.values())[0]() except OverflowError as e: self.result_label.spr.labels[0] = _('Overflow Error') + \ ': ' + str(e) self.result_label.draw(1000) except NameError as e: self.result_label.spr.labels[0] = _('Name Error') + ': ' + str(e) self.result_label.draw(1000) except ZeroDivisionError as e: self.result_label.spr.labels[0] = _('Can not divide by zero') + \ ': ' + str(e) self.result_label.draw(1000) except TypeError as e: self.result_label.spr.labels[0] = _('Type Error') + ': ' + str(e) self.result_label.draw(1000) except ValueError as e: self.result_label.spr.labels[0] = _('Type Error') + ': ' + str(e) self.result_label.draw(1000) except SyntaxError as e: self.result_label.spr.labels[0] = _('Syntax Error') + ': ' + str(e) self.result_label.draw(1000) except: traceback.print_exc() return None def make_slide(self, name, slide, custom_strings=None): """ Create custom slide and stator from text entered on toolbar. """ if custom_strings is not None: result = self._process_text_field(custom_strings[FMIN]) else: result = self._process_text_field(DEFINITIONS[name][FMIN]) if result is None: return try: min_value = float(result) except ValueError as e: self.result_label.spr.labels[0] = _('Value Error') + ': ' + str(e) self.result_label.draw(1000) return if custom_strings is not None: result = self._process_text_field(custom_strings[FMAX]) else: result = self._process_text_field(DEFINITIONS[name][FMAX]) if result is None: return try: max_value = float(result) except ValueError: self.result_label.spr.labels[0] = _('Value Error') + ': ' + str(e) self.result_label.draw(1000) return if custom_strings is not None: result = self._process_text_field(custom_strings[FSTEP]) else: result = self._process_text_field(DEFINITIONS[name][FSTEP]) if result is None: return try: step_value = float(result) except ValueError: self.result_label.spr.labels[0] = _('Value Error') + ': ' + str(e) self.result_label.draw(1000) return if custom_strings is not None: offset_string = custom_strings[FOFFSET] else: offset_string = DEFINITIONS[name][FOFFSET] if custom_strings is not None: label_string = custom_strings[FDISPLAY] else: label_string = DEFINITIONS[name][FDISPLAY] if name == 'custom' or name == 'custom2': if custom_strings is not None: self.result_function[slide] = custom_strings[FRESULT] self.label_function[slide] = custom_strings[FDISPLAY] else: self.result_function[slide] = DEFINITIONS[name][FRESULT] self.label_function[slide] = DEFINITIONS[name][FDISPLAY] if slide == SLIDE: custom_slide = \ CustomSlide(self.sprites, self.path, name, 0, SCREENOFFSET + SHEIGHT, self.SLIDES[name][0], self._calc_slide_value, offset_string, label_string, min_value, max_value, step_value) if custom_slide.error_msg is not None: self.result_label.spr.set_label(custom_slide.error_msg) self.result_label.draw(1000) if self.name_to_slide(name) is not None and \ self.name_to_slide(name).name == name: i = self.slides.index(self.name_to_slide(name)) active = False if self.active_slide == self.slides[i]: active = True self.slides[i].hide() self.slides[i] = custom_slide if active: self.active_slide = self.slides[i] if self.sugar: self.parent.set_slide() else: self.slides.append(custom_slide) self.active_slide = self.name_to_slide(name) else: custom_stator = \ CustomStator(self.sprites, name, 0, SCREENOFFSET + 2* SHEIGHT, self.STATORS[name][0], self._calc_stator_value, self._calc_stator_result, offset_string, label_string, min_value, max_value, step_value) if self.name_to_stator(name) is not None and \ self.name_to_stator(name).name == name: i = self.stators.index(self.name_to_stator(name)) active = False if self.active_stator == self.stators[i]: active = True self.stators[i].hide() self.stators[i] = custom_stator if active: self.active_stator = self.stators[i] if self.sugar: self.parent.set_stator() else: self.stators.append(custom_stator) self.active_stator = self.name_to_stator(name) if self.sugar and name == 'custom' and hasattr(self.parent, 'sr'): self.parent.show_u(slide) if slide == SLIDE and custom_slide.error_msg is not None: self.result_label.spr.set_label(custom_slide.error_msg) self.result_label.draw(1000) if slide == STATOR and custom_stator.error_msg is not None: self.result_label.spr.set_label(custom_stator.error_msg) self.result_label.draw(1000) def name_to_slide(self, name): for slide in self.slides: if name == slide.name: return slide return None def name_to_stator(self, name): for stator in self.stators: if name == stator.name: return stator return None def sprite_in_stators(self, sprite): for stator in self.stators: if stator.match(sprite): return True return False def find_stator(self, sprite): for stator in self.stators: if stator.match(sprite): return stator return None def sprite_in_slides(self, sprite): for slide in self.slides: if slide.match(sprite): return True return False def find_slide(self, sprite): for slide in self.slides: if slide.match(sprite): return slide return None def _button_press_cb(self, win, event): win.grab_focus() x, y = list(map(int, event.get_coords())) self.dragpos = x spr = self.sprites.find_sprite((x, y)) self.press = spr return True def _mouse_move_cb(self, win, event): """ Drag a rule with the mouse. """ if self.press is None: self.dragpos = 0 return True win.grab_focus() x, y = list(map(int, event.get_coords())) dx = x - self.dragpos self._move_slides(self.press, dx) self.dragpos = x def _move_reticule_to_slide_value(self, value): rx = self.reticule.spr.get_xy()[0] - self.active_slide.spr.get_xy()[0] self.reticule.move_relative( self._calc_dx_from_value(value, self.active_slide.name, rx), 0) self.update_slide_labels() self.update_result_label() def _move_reticule_to_stator_value(self, value): rx = self.reticule.spr.get_xy()[0] - self.active_stator.spr.get_xy()[0] self.reticule.move_relative( self._calc_dx_from_value(value, self.active_stator.name, rx), 0) self.update_slide_labels() self.update_result_label() def _move_slide_to_stator_value(self, value): rx = self.active_slide.spr.get_xy()[0] - \ self.active_stator.spr.get_xy()[0] self.active_slide.move_relative( self._calc_dx_from_value(value, self.active_stator.name, rx), 0) self.update_slide_labels() self.update_result_label() def _calc_dx_from_value(self, value, name, rx): if name in ['C', 'D']: if value <= 0: return 0 return log(value, 10) * SCALE - rx elif name in ['CI', 'DI']: if value == 0: return 0 return log(10 / value, 10) * SCALE - rx elif name in ['A', 'B']: if value <= 0: return 0 return log(pow(value, 1 / 2.), 10) * SCALE - rx elif name in ['K', 'K2']: if value <= 0: return 0 return log(pow(value, 1 / 3.), 10) * SCALE - rx elif name in ['L', 'L2']: return (value / 10.) * SCALE - rx elif name in ['LLn', 'LLn2']: return log(exp(value), 10) * SCALE - rx elif name in ['Log', 'Log2']: return pow(10, log(value, 10)) * SCALE - rx else: return 0 def align_slides(self): """ Move slide to align with stator """ slidex = self.active_slide.spr.get_xy()[0] statorx = self.active_stator.spr.get_xy()[0] dx = statorx - slidex print('calling active slide', dx, 0) self.active_slide.move_relative(dx, 0) def _move_slides(self, sprite, dx): if self.sprite_in_stators(sprite): self.active_stator.move_relative(dx, 0) self.active_slide.move_relative(dx, 0) self.reticule.move_relative(dx, 0) elif self.reticule.match(sprite): self.reticule.move_relative(dx, 0) elif self.sprite_in_slides(sprite): self.find_slide(sprite).move_relative(dx, 0) self.update_slide_labels() self.update_result_label() def _left_from_right(self, v_right): if self.active_stator.name == 'L2': return v_right - 10 elif self.active_stator.name == 'D': return v_right / 10. elif self.active_stator.name == 'B': return v_right / 100. elif self.active_stator.name == 'K2': return v_right / 1000. elif self.active_stator.name == 'DI': return v_right * 10. elif self.active_stator.name == 'LLn2': return v_right - round(log(10), 2) else: return v_right def _right_from_left(self, v_left): if self.active_stator.name == 'L2': return 10 + v_left elif self.active_stator.name == 'D': return v_left * 10. elif self.active_stator.name == 'B': return v_left * 100. elif self.active_stator.name == 'K2': return v_left * 1000. elif self.active_stator.name == 'DI': return v_left / 10. elif self.active_stator.name == 'LLn2': return round(log(10), 2) + v_left else: return v_left def update_slide_labels(self): """ Based on the current alignment of the rules, calculate labels. """ v_left = self.active_stator.calculate() v_right = self._right_from_left(v_left) label_left = str(v_left).replace('.', self.decimal_point) label_right = str(v_right).replace('.', self.decimal_point) self.active_slide.label(label_left, i=LEFT) self.active_slide.label(label_right, i=RIGHT) self.reticule.label(str(self.active_stator.result()).replace( '.', self.decimal_point), i=BOTTOM) self.reticule.label(str(self.active_slide.calculate()).replace( '.', self.decimal_point), i=TOP) def _button_release_cb(self, win, event): if self.press == None: return True if self.press == self.active_slide.spr: self.last = self.active_slide.tabs[LEFT].spr elif self.press == self.active_stator.spr: self.last = None else: self.last = self.press self.press = None self.update_result_label() def update_result_label(self): """ Update toolbar label with result of calculation. """ s = '' if self.active_stator.name == 'D': dx = self.name_to_stator('D').spr.get_xy()[0] S = self.active_slide.calculate() R = self._calc_stator_result('D') if self.active_slide.name == 'A': if self.name_to_slide('A').spr.get_xy()[0] == dx: s = " √ %0.2f = %0.2f\t\t%0.2f² = %0.2f" % (S, R, R, S) elif self.sugar: self.parent.set_function_unknown() elif self.active_slide.name == 'K': if self.name_to_slide('K').spr.get_xy()[0] == dx: s = " ∛ %0.2f = %0.2f\t\t%0.2f³ = %0.2f" % (S, R, R, S) elif self.sugar: self.parent.set_function_unknown() elif self.active_slide.name == 'S': if self.name_to_slide('S').spr.get_xy()[0] == dx: s = " sin(%0.2f) = %0.2f\t\tasin(%0.2f) = %0.2f" % \ (S, R/10, R/10, S) elif self.sugar: self.parent.set_function_unknown() elif self.active_slide.name == 'T': if self.name_to_slide('T').spr.get_xy()[0] == dx: s = " tan(%0.2f) = %0.2f\t\tatan(%0.2f) = %0.2f" % \ (S, R/10, R/10, S) elif self.sugar: self.parent.set_function_unknown() elif self.active_slide.name == 'C': D = str(self._calc_stator_value('D')) s = "%s × %s = %s\t\t%s / %s = %s" % (D, S, R, R, S, D) elif self.active_slide.name == 'CI': D = str(self._calc_stator_value('D')) s = "%s / %s = %s\t\t%s × %s = %s" % (D, S, R / 10, R / 10, S, D) elif self.active_stator.name == 'L2': if self.active_slide.name == 'L': # use n dash to display a minus sign L2 = self._calc_stator_value('L2') if L2 < 0: L2str = "–" + str(-L2) else: L2str = str(L2) L = self._calc_slide_value('L') if L < 0: operator1 = "–" operator2 = "+" Lstr = str(-L) else: operator1 = "+" operator2 = "–" Lstr = str(L) LL = self._calc_stator_result('L2') if LL < 0: LLstr = "–" + str(-LL) else: LLstr = str(LL) s = "%s %s %s = %s\t\t%s %s %s = %s" % (L2str, operator1, Lstr, LLstr, LLstr, operator2, Lstr, L2str) elif self.active_stator.name == 'LLn2' and \ self.active_slide.name == 'C': dx = self.name_to_stator('LLn2').spr.get_xy()[0] S = self.active_slide.calculate() R = self._calc_stator_result('LLn2') if self.name_to_slide('C').spr.get_xy()[0] == dx: s = " ln(%0.2f) = %0.2f\t\texp(%0.2f) = %0.2f" % (S, R, R, S) elif self.sugar: self.parent.set_function_unknown() if self.active_slide.name == 'custom' or \ self.active_stator.name == 'custom2': if self.error_msg is not None: s = self.error_msg else: s = '' self.result_label.draw(1000) self.result_label.spr.set_label(s.replace('.', self.decimal_point)) def _top_slide_offset(self, x): """ Calcualate the offset between the top and bottom slides """ x2, y2 = self.active_slide.spr.get_xy() return x2 - x # Calculate the value of individual slides and stators: # (a) the offset of the reticule along the slide # (b) the offset of the slide along the stator # (c) the offset of the reticule along the reticule def _r_offset(self, slide): return self.reticule.spr.get_xy()[0] - slide.spr.get_xy()[0] def _calc_slide_value(self, name=None): if name is None: name = self.active_slide.name return self.function_calc(name, self._r_offset(self.name_to_slide(name)), SLIDE) def _calc_stator_value(self, name=None): if name is None: name = self.active_stator.name return self.function_calc( name, self._top_slide_offset(self.name_to_stator(name).spr.get_xy()[0]), STATOR) def _calc_stator_result(self, name=None): if name is None: name = self.active_stator.name return self.function_calc(name, self._r_offset(self.name_to_stator(name)), STATOR) def function_calc(self, name, dx, slide): self.error_msg = None if name in ['custom', 'custom2']: my_result = "def f(x): return " + \ self.result_function[slide].replace('import','') my_label = "def f(x): return " + \ self.label_function[slide].replace('import','') else: my_result = "def f(x): return " + DEFINITIONS[name][FRESULT] my_label = "def f(x): return " + DEFINITIONS[name][FDISPLAY] # Some slides handle wrap-around rescale = 1 offset = 0 if name in ['C', 'D', 'CI', 'DI', 'LLn', 'LLn2', 'Log', 'Log2']: if dx < 0: rescale = 0.1 dx += SCALE elif name in ['A', 'B']: if dx < 0: rescale = 0.01 dx += SCALE elif name in ['K', 'K2']: if dx < 0: rescale = 0.001 dx += SCALE elif name in ['L', 'L2']: rescale = 10 if dx < 0: dx += SCALE offset = -10 userdefined = {} try: exec(my_result, globals(), userdefined) result = list(userdefined.values())[0](float(dx) / SCALE) * rescale +\ offset except OverflowError as e: self.error_msg = _('Overflow Error') + ': ' + str(e) return '?' except NameError as e: self.error_msg = _('Name Error') + ': ' + str(e) return '?' except ZeroDivisionError as e: self.error_msg = _('Can not divide by zero') + ': ' + str(e) return '?' except TypeError as e: self.error_msg = _('Type Error') + ': ' + str(e) return '?' except ValueError as e: self.error_msg = _('Type Error') + ': ' + str(e) return '?' except SyntaxError as e: self.error_msg = _('Syntax Error') + ': ' + str(e) return '?' except: traceback.print_exc() return None # Some special cases to fine-tune the label display precision precision = 2 if name in ['A', 'B', 'K', 'K2']: if result > 50: precision = 1 elif name in ['S', 'S2']: if result > 60: precision = 1 if name in ['K', 'K2']: if result > 500: precision = 0 userdefined = {} try: exec(my_label, globals(), userdefined) label = list(userdefined.values())[0](result) if type(label) == float: return round(label, precision) else: return label except OverflowError as e: self.error_msg = _('Overflow Error') + ': ' + str(e) except NameError as e: self.error_msg = _('Name Error') + ': ' + str(e) except ZeroDivisionError as e: self.error_msg = _('Can not divide by zero') + ': ' + str(e) except TypeError as e: self.error_msg = _('Type Error') + ': ' + str(e) except ValueError as e: self.error_msg = _('Type Error') + ': ' + str(e) except SyntaxError as e: self.error_msg = _('Syntax Error') + ': ' + str(e) except: traceback.print_exc() return None return '??'
class SlideRule(): def __init__(self, canvas, path, parent=None, sugar=True): """ Handle launch from both within and without of Sugar environment. """ self.SLIDES = { 'C': [C_slide_generator], 'CI': [CI_slide_generator], 'A': [A_slide_generator], 'K': [K_slide_generator], 'S': [S_slide_generator], 'T': [T_slide_generator], 'L': [L_slide_generator], 'LLn': [LLn_slide_generator], 'Log': [Log_slide_generator], 'custom': [Custom_slide_generator] } self.STATORS = { 'D': [D_stator_generator], 'DI': [DI_stator_generator], 'B': [B_stator_generator], 'K2': [K_stator_generator], 'S2': [S_stator_generator], 'T2': [T_stator_generator], 'L2': [L_stator_generator], 'LLn2': [LLn_stator_generator], 'Log2': [Log_stator_generator], 'custom2': [Custom_stator_generator] } self.path = path self.sugar = sugar self.canvas = canvas self.parent = parent parent.show_all() self.canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self.canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self.canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self.canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self.canvas.connect("draw", self.__draw_cb) self.canvas.connect("button-press-event", self._button_press_cb) self.canvas.connect("button-release-event", self._button_release_cb) self.canvas.connect("motion-notify-event", self._mouse_move_cb) self.canvas.connect("key-press-event", self._keypress_cb) self.canvas.set_can_focus(True) self.canvas.grab_focus() self.width = Gdk.Screen.width() self.height = Gdk.Screen.height() - GRID_CELL_SIZE self.sprites = Sprites(self.canvas) self.slides = [] self.stators = [] self.scale = 1 locale.setlocale(locale.LC_NUMERIC, '') self.decimal_point = locale.localeconv()['decimal_point'] if self.decimal_point == '' or self.decimal_point is None: self.decimal_point = '.' self.error_msg = None self.result_function = [None, None] self.label_function = [None, None] _logger.debug("creating slides, stators, and reticule") self.result_label = Stator(self.sprites, self.path, 'label', int((self.width - 600) / 2), SCREENOFFSET + 4 * SHEIGHT, 800, SHEIGHT) for slide in self.SLIDES: self.make_slide(slide, SLIDE) for stator in self.STATORS: self.make_slide(stator, STATOR) self.reticule = Reticule(self.sprites, self.path, 'reticule', 150, SCREENOFFSET + SHEIGHT, 100, 2 * SHEIGHT) self.reticule.draw(2000) self.press = None self.last = None self.dragpos = 0 # We need textviews for keyboard input from the on-screen keyboard self._set_screen_dpi() font_desc = Pango.font_description_from_string('12') self.text_entries = [] self.text_buffers = [] w = self.reticule.tabs[0].spr.label_safe_width() h = int(self.reticule.tabs[0].spr.label_safe_height() / 2) for i in range(4): # Reticule top & bottom; Slider left & right self.text_entries.append(Gtk.TextView()) self.text_entries[-1].set_justification(Gtk.Justification.CENTER) self.text_entries[-1].set_pixels_above_lines(4) ''' Not necessary (and doesn't work on OS8) self.text_entries[-1].override_background_color( Gtk.StateType.NORMAL, Gdk.RGBA(0, 0, 0, 0)) ''' self.text_entries[-1].modify_font(font_desc) self.text_buffers.append(self.text_entries[-1].get_buffer()) self.text_entries[-1].set_size_request(w, h) self.text_entries[-1].show() self.parent.fixed.put(self.text_entries[-1], 0, 0) self.parent.fixed.show() self.text_entries[-1].connect('focus-out-event', self._text_focus_out_cb) self.reticule.add_textview(self.text_entries[0], i=BOTTOM) self.reticule.add_textview(self.text_entries[1], i=TOP) self.reticule.set_fixed(self.parent.fixed) for slide in self.slides: slide.add_textview(self.text_entries[2], i=LEFT) slide.add_textview(self.text_entries[3], i=RIGHT) slide.set_fixed(self.parent.fixed) if not self.sugar: self.update_textview_y_offset(self.parent.menu_height) self.active_slide = self.name_to_slide('C') self.active_stator = self.name_to_stator('D') self.update_slide_labels() self.update_result_label() def update_textview_y_offset(self, dy): ''' Need to account for menu height in GNOME ''' self.reticule.tabs[0].textview_y_offset += dy self.reticule.tabs[1].textview_y_offset += dy for slide in self.slides: slide.tabs[0].textview_y_offset += dy slide.tabs[1].textview_y_offset += dy def _text_focus_out_cb(self, widget=None, event=None): ''' One of the four textviews was in focus ''' i = None if widget in self.text_entries: i = self.text_entries.index(widget) bounds = self.text_buffers[i].get_bounds() text = self.text_buffers[i].get_text(bounds[0], bounds[1], True) text = text.strip() self._process_numeric_input(i, text) def _set_screen_dpi(self): dpi = _get_screen_dpi() font_map_default = PangoCairo.font_map_get_default() font_map_default.set_resolution(dpi) def __draw_cb(self, canvas, cr): self.sprites.redraw_sprites(cr=cr) # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self.canvas.window.cairo_create() print 'set cr in do_expose' self.sprites.set_cairo_context(cr) # Restrict Cairo to the exposed area; avoid extra work cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self.sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit() def _keypress_cb(self, area, event): """ Keypress: moving the slides with the arrow keys """ k = Gdk.keyval_name(event.keyval) if not self.sugar: return if k == 'a': self.parent.show_a() elif k == 'k': self.parent.show_k() elif k in ['c', 'asterisk', 'x']: self.parent.show_c() elif k in ['i', '/']: self.parent.show_ci() elif k == 's': self.parent.show_s() elif k == 't': self.parent.show_t() elif k in ['l', 'plus']: self.parent.show_l() elif k in ['Left', 'less']: if self.last is not None: self._move_slides(self.last, -1) elif k in ['Right', 'greater']: if self.last is not None: self._move_slides(self.last, 1) elif k in ['Home', 'Pause', 'Up', '^']: self._move_slides( self.name_to_stator('D').spr, -self.name_to_stator('D').spr.get_xy()[0]) elif k == 'r': self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() elif k in ['Down', 'v']: self.parent.realign_cb() self.reticule.move(150, self.reticule.spr.get_xy()[1]) self.update_slide_labels() self.update_result_label() return True def _process_numeric_input(self, i, text): try: n = float(text.replace(self.decimal_point, '.')) if i == 0: self._move_reticule_to_stator_value(n) elif i == 1: self._move_reticule_to_slide_value(n) elif i == 2: self._move_slide_to_stator_value(n) elif i == 3: self._move_slide_to_stator_value(self._left_from_right(n)) except ValueError: self.result_label.spr.labels[0] = _('NaN') + ' ' + text return def _process_text_field(self, text_field): """ Process input from numeric text fields: could be a function. """ try: my_min = "def f(): return " + text_field.replace('import', '') userdefined = {} exec my_min in globals(), userdefined return userdefined.values()[0]() except OverflowError, e: self.result_label.spr.labels[0] = _('Overflow Error') + \ ': ' + str(e) self.result_label.draw(1000) except NameError, e: self.result_label.spr.labels[0] = _('Name Error') + ': ' + str(e) self.result_label.draw(1000)