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()
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 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 '??'
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()
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)