class PatternSample(PyWidget): def __init__(self, master=None, **kw): apply(PyWidget.__init__, (self, master), kw) self.gc_initialized = 0 self.gc = GraphicsDevice() self.pattern = EmptyPattern self.properties = PropertyStack() self.properties.AddStyle(EmptyLineStyle) self.properties.SetProperty(fill_pattern = self.pattern) self.gc.SetProperties(self.properties) self.fill_rect = None def MapMethod(self): self.fill_rect = Rect(0, 0, self.tkwin.width, self.tkwin.height) if not self.gc_initialized: self.init_gc() self.gc_initialized = 1 def init_gc(self): self.gc.init_gc(self.tkwin) self.compute_trafo() def compute_trafo(self): height = self.tkwin.height doc_to_win = Trafo(1, 0, 0, -1, 0, height) win_to_doc = doc_to_win.inverse() self.gc.SetViewportTransform(1.0, doc_to_win, win_to_doc) self.fill_rect = Rect(0, 0, self.tkwin.width, self.tkwin.height) self.gc.SetProperties(self.properties, self.fill_rect) def SetPattern(self, pattern): if pattern != self.pattern: self.pattern = pattern self.UpdateWhenIdle() self.properties.SetProperty(fill_pattern = pattern) self.gc.SetProperties(self.properties, self.fill_rect) def RedrawMethod(self, region = None): win = self.tkwin self.gc.StartDblBuffer() self.gc.SetFillColor(StandardColors.white) self.gc.FillRectangle(0, 0, win.width, win.height) if self.properties.HasFill(): self.gc.Rectangle(Trafo(win.width, 0, 0, win.height, 0, 0)) else: self.gc.SetLineColor(StandardColors.black) self.gc.DrawLineXY(0, win.height, win.width, 0) self.gc.EndDblBuffer() def ResizedMethod(self, width, height): self.gc.WindowResized(width, height) self.UpdateWhenIdle() self.compute_trafo()
class PaletteWidget(PyWidget, Publisher): def __init__(self, master=None, palette=None, cell_size=16, **kw): if not kw.has_key('height'): kw['height'] = cell_size apply(PyWidget.__init__, (self, master), kw) self.cell_size = cell_size self.num_cells = 0 self.gc_initialized = 0 self.gc = GraphicsDevice() self.gc.SetViewportTransform(1.0, Identity, Identity) self.start_idx = 0 self.palette = None if palette is None: palette = RGBPalette() self.SetPalette(palette) self.dragging = 0 self.bind('<ButtonPress-1>', self.press_1) self.bind('<Motion>', self.move_1) self.bind('<ButtonRelease-1>', self.release_1) self.bind('<ButtonRelease-3>', self.apply_color_2) def DestroyMethod(self): self.palette.Unsubscribe(CHANGED, self.palette_changed) Publisher.Destroy(self) def compute_num_cells(self): self.num_cells = self.tkwin.width / self.cell_size + 1 def MapMethod(self): self.compute_num_cells() self.issue(VIEW) if not self.gc_initialized: self.init_gc() self.gc_initialized = 1 def init_gc(self): self.gc.init_gc(self.tkwin) def get_color(self, x, y): if 0 <= x < self.tkwin.width and 0 <= y < self.tkwin.height: i = self.start_idx + x / self.cell_size if i < len(self.palette): return apply(CreateRGBColor, self.palette.GetRGB(i)) def release_1(self, event): try: if self.dragging: self.drop_color(event) else: self.apply_color_1(event) finally: self.dragging = 0 def drop_color(self, event): self['cursor'] = self.drag_old_cursor w = self.winfo_containing(event.x_root, event.y_root) while w and w != self: if __debug__: pdebug('DND', 'trying to drop on', w) try: accepts = w.accept_drop except AttributeError: accepts = () if DROP_COLOR in accepts: x = event.x_root - w.winfo_rootx() y = event.y_root - w.winfo_rooty() w.DropAt(x, y, DROP_COLOR, self.drag_start) break if w != w.winfo_toplevel(): parent = self.tk.call('winfo', 'parent', w._w) w = self.nametowidget(parent) else: break def apply_color_1(self, event): c = self.get_color(event.x, event.y) if c: self.issue(COLOR1, c) def apply_color_2(self, event): c = self.get_color(event.x, event.y) if c: self.issue(COLOR2, c) drag_start = (0, 0, 0) def press_1(self, event): self.drag_start = self.get_color(event.x, event.y) def move_1(self, event): if event.state & X.Button1Mask: if not self.dragging: self.dragging = 1 self.drag_old_cursor = self['cursor'] self['cursor'] = CurDragColor w = self.winfo_containing(event.x_root, event.y_root) def Palette(self): return self.palette def SetPalette(self, palette): if self.palette is not None: self.palette.Unsubscribe(CHANGED, self.palette_changed) self.palette = palette self.palette.Subscribe(CHANGED, self.palette_changed) self.palette_changed() def palette_changed(self): self.compute_num_cells() self.normalize_start() self.issue(VIEW) self.UpdateWhenIdle() def RedrawMethod(self, region=None): win = self.tkwin width = win.width height = win.height self.gc.StartDblBuffer() self.gc.SetFillColor(StandardColors.white) self.gc.FillRectangle(0, 0, width, height) x = 0 FillRectangle = self.gc.FillRectangle SetFillColor = self.gc.SetFillColor create_color = CreateRGBColor rgbs = self.palette.Colors() rgbs = rgbs[self.start_idx:self.start_idx + self.num_cells] for rgb in rgbs: SetFillColor(apply(create_color, rgb)) FillRectangle(x, 0, x + height, height) x = x + height self.gc.EndDblBuffer() def ResizedMethod(self, width, height): self.compute_num_cells() self.gc.WindowResized(width, height) self.normalize_start() self.UpdateWhenIdle() def normalize_start(self): length = len(self.palette) if self.start_idx < 0: self.start_idx = 0 if length < self.num_cells: self.start_idx = 0 elif length - self.start_idx < self.num_cells: self.start_idx = length - self.num_cells def CanScrollLeft(self): return self.start_idx > 0 def CanScrollRight(self): return len(self.palette) - self.start_idx > self.num_cells def ScrollXPages(self, count): length = self.tkwin.width / self.cell_size start = self.start_idx self.start_idx = self.start_idx + count * length self.normalize_start() if start != self.start_idx: self.UpdateWhenIdle() self.issue(VIEW) def ScrollXUnits(self, count): start = self.start_idx self.start_idx = self.start_idx + count self.normalize_start() if start != self.start_idx: self.UpdateWhenIdle() self.issue(VIEW)
class Ruler(PyWidget): def __init__(self, master=None, orient=HORIZONTAL, canvas=None, **kw): apply(PyWidget.__init__, (self, master), kw) self.orient = orient self.canvas = canvas self.gc_initialized = 0 self.gc = GraphicsDevice() self.gc.SetViewportTransform(1.0, Identity, Identity) self.positions = None self.SetRange(0.0, 1.0, force=1) self['height'] = 19 self['width'] = 19 self.border_color = XRGBColor(ui_colors.light_border) self.bg_color = XRGBColor(ui_colors.menubackground) self.fg_color = XRGBColor(ui_colors.fg) self.gradient = [] start = ui_colors.menubackground stop = ui_colors.light_border for pos in range(20): color = gtkutils.middle_color(start, stop, pos * 3.5 / 100) self.gradient.append(XRGBColor(color)) self.bind('<ButtonPress>', self.ButtonPressEvent) self.bind('<ButtonRelease>', self.ButtonReleaseEvent) self.bind('<Motion>', self.PointerMotionEvent) self.button_down = 0 self.forward_motion = 0 config.preferences.Subscribe(CHANGED, self.preference_changed) def destroy(self): PyWidget.destroy(self) self.canvas = None def MapMethod(self): if not self.gc_initialized: self.gc.init_gc(self.tkwin) self.gc_initialized = 1 def ResizedMethod(self, width, height): self.SetRange(self.start, self.pixel_per_pt, force=1) def SetRange(self, start, pixel_per_pt, force=0): if not force and start == self.start and pixel_per_pt == self.pixel_per_pt: return self.start = start self.pixel_per_pt = pixel_per_pt self.positions = None self.UpdateWhenIdle() def preference_changed(self, pref, value): if pref == 'default_unit': self.positions = None # force recomputation self.UpdateWhenIdle() def get_positions(self): if self.positions is not None: return self.positions, self.texts min_text_step = config.preferences.ruler_min_text_step max_text_step = config.preferences.ruler_max_text_step min_tick_step = config.preferences.ruler_min_tick_step if self.orient == HORIZONTAL: length = self.tkwin.width origin = self.start else: length = self.tkwin.height origin = self.start - length / self.pixel_per_pt unit_name = config.preferences.default_unit pt_per_unit = units.unit_dict[unit_name] units_per_pixel = 1.0 / (pt_per_unit * self.pixel_per_pt) factor, subdivisions = tick_config[unit_name] subdivisions = (1, ) + subdivisions factor = factor * pt_per_unit start_pos = floor(origin / factor) * factor main_tick_step = factor * self.pixel_per_pt num_ticks = floor(length / main_tick_step) + 2 if main_tick_step < min_tick_step: tick_step = ceil(min_tick_step / main_tick_step) * main_tick_step subdivisions = (1, ) ticks = 1 else: tick_step = main_tick_step ticks = 1 for depth in range(len(subdivisions)): tick_step = tick_step / subdivisions[depth] if tick_step < min_tick_step: tick_step = tick_step * subdivisions[depth] depth = depth - 1 break ticks = ticks * subdivisions[depth] subdivisions = subdivisions[:depth + 1] positions = range(int(num_ticks * ticks)) positions = map(operator.mul, [tick_step] * len(positions), positions) positions = map(operator.add, positions, [(start_pos - origin) * self.pixel_per_pt] * len(positions)) stride = ticks marks = [None] * len(positions) for depth in range(len(subdivisions)): stride = stride / subdivisions[depth] if depth >= len(tick_lengths): height = tick_lengths[-1] else: height = tick_lengths[depth] for i in range(0, len(positions), stride): if marks[i] is None: marks[i] = (height, int(round(positions[i]))) texts = [] if main_tick_step < min_text_step: stride = int(ceil(min_text_step / main_tick_step)) start_index = stride - (floor(origin / factor) % stride) start_index = int(start_index * ticks) stride = stride * ticks else: start_index = 0 stride = ticks step = main_tick_step for div in subdivisions: step = step / div if step < min_text_step: break stride = stride / div if step < max_text_step: break for i in range(start_index, len(positions), stride): pos = positions[i] * units_per_pixel + origin / pt_per_unit pos = round(pos, 3) if pos == 0.0: # avoid '-0' strings pos = 0.0 texts.append(("%g" % pos, marks[i][-1])) self.positions = marks self.texts = texts return self.positions, self.texts def RedrawMethod(self, region=None): if self.orient == HORIZONTAL: self.draw_ruler_horizontal() else: self.draw_ruler_vertical() def draw_ruler_horizontal(self): DrawLine = self.gc.gc.DrawLine height = self.tkwin.height width = self.tkwin.width for pos in range(0, 20): self.gc.SetFillColor(self.gradient[pos]) DrawLine(0, pos, width, pos) self.gc.SetFillColor(self.fg_color) ticks, texts = self.get_positions() for h, pos in ticks: DrawLine(pos, height, pos, height - h - 1) pos = pos + 1 y = 8 for text, pos in texts: pos += 1 for character in str(text): data = HFONT[character] lines = data[1] for line in lines: DrawLine(line[0] + pos, y - line[1], line[2] + pos, y - line[3]) pos += data[0] self.gc.SetFillColor(self.border_color) self.gc.gc.DrawLine(0, 0, 0, height) self.gc.gc.DrawLine(0, height - 1, width, height - 1) def draw_ruler_vertical(self): DrawLine = self.gc.gc.DrawLine height = self.tkwin.height width = self.tkwin.width for pos in range(0, 20): self.gc.SetFillColor(self.gradient[pos]) DrawLine(pos, 0, pos, height) self.gc.SetFillColor(self.fg_color) ticks, texts = self.get_positions() for h, pos in ticks: pos = height - pos DrawLine(width - h - 1, pos, width, pos) pos = pos + 1 x = 8 for text, pos in texts: pos = height - pos pos -= 1 for character in str(text): data = VFONT[character] lines = data[1] for line in lines: DrawLine(x - line[0], pos - line[1], x - line[2], pos - line[3]) pos -= data[0] self.gc.SetFillColor(self.border_color) DrawLine(0, 0, width, 0) DrawLine(width - 1, 0, width - 1, height) def ButtonPressEvent(self, event): if event.num == const.Button1: self.button_down = 1 self.pressevent = event def ButtonReleaseEvent(self, event): if event.num == const.Button1: self.button_down = 0 def PointerMotionEvent(self, event): if self.button_down: if self.canvas is not None: press = self.pressevent if hypot(press.x - event.x, press.y - event.y) > 3: guide = GuideLine(Point(0, 0), self.orient == HORIZONTAL) self.canvas.PlaceObject(guide) press.x = press.x_root - self.canvas.winfo_rootx() press.y = press.y_root - self.canvas.winfo_rooty() self.canvas.ButtonPressEvent(press) self.canvas.grab_set() self.button_down = 0 def SetCanvas(self, canvas): self.canvas = canvas
class SketchView(PyWidget, Viewport, QueueingPublisher): document = None def __init__(self, master=None, toplevel=None, document=None, show_visible=0, show_printable=1, resolution=None, **kw): apply(PyWidget.__init__, (self, master), kw) Viewport.__init__(self, resolution) QueueingPublisher.__init__(self) self.toplevel = toplevel self.move_window_count = 0 self.show_page_outline = 1 self.show_visible = show_visible self.show_printable = show_printable self.gcs_initialized = 0 self.gc = GraphicsDevice() self.init_transactions() if document is not None: self.SetDocument(document) else: self.SetDocument(Document(create_layer=1)) def destroy(self): self.unsubscribe_doc() PyWidget.destroy(self) QueueingPublisher.Destroy(self) def MapMethod(self): # when being mapped the first time, initialise the gcs. this cannot be # done earlier, because the hitgc creates a pixmap which currently # only works after the window (id) has been created. In Xt this can be # done in the Realize widget method (after calling the superclass' # method), but Tk doesn't seem to offer any similar thing. if not self.gcs_initialized: self.init_gcs() self.issue_state() def DestroyMethod(self): # make sure that gc is deleted. gc may have a shared memory ximage # which is not freed if the gc is not destroyed leaving unused shared # memory segments in the system even after the process has finished. self.gc = None PyWidget.DestroyMethod(self) def init_gcs(self): self.gc.init_gc(self.tkwin, graphics_exposures=1) self.gc.draw_visible = self.show_visible self.gc.draw_printable = self.show_printable self.gc.allow_outline = 0 self.gcs_initialized = 1 self.default_view() self.set_gc_transforms() def default_view(self): self.FitPageToWindow() def set_gc_transforms(self): self.gc.SetViewportTransform(self.scale, self.doc_to_win, self.win_to_doc) # # Channels # def issue_state(self): self.queue_message(STATE) def issue_view(self): self.queue_message(VIEW) def issue_document(self): self.doc_changed = 1 def queue_message(self, Publisher): if self.transaction: QueueingPublisher.queue_message(self, Publisher) else: self.issue(Publisher) def init_transactions(self): self.sb_update_pending = 0 self.doc_changed = 0 self.transaction = 0 def begin_transaction(self): self.transaction = self.transaction + 1 def end_transaction(self): self.transaction = self.transaction - 1 if self.transaction == 0: if self.doc_changed: self.issue(DOCUMENT, self.document) self.sb_update_pending = 0 self.doc_changed = 0 self.flush_message_queue() elif self.transaction < 0: raise SketchInternalError('transaction count < 0') # # receivers # def redraw_doc(self, all, rects=None): if all: self.clear_window() else: map(self.clear_area_doc, rects) def layout_changed(self): self.SetPageSize(self.document.Layout().Size()) self.set_gc_transforms() self.update_scrollbars() self.update_rulers() if self.show_page_outline: self.clear_window() def layer_changed(self, *args): if args: redraw = EmptyRect if args[0] == LAYER_STATE: layer, visible_changed, printable_changed, outlined_changed \ = args[1] rect = layer.bounding_rect if rect is not EmptyRect: if self.show_printable and printable_changed: redraw = rect if self.show_visible: if visible_changed: redraw = rect if outlined_changed and layer.Visible(): redraw = rect elif args[0] == LAYER_ORDER: layer = args[1] if (self.show_printable and layer.Printable() or self.show_visible and layer.Visible()): redraw = layer.bounding_rect if len(args) > 2: other = args[2] if (self.show_printable and other.Printable() or self.show_visible and other.Visible()): redraw = IntersectRects(redraw, other.bounding_rect) else: redraw = EmptyRect elif args[0] == LAYER_COLOR: layer = args[1] rect = layer.bounding_rect if self.show_visible and rect is not EmptyRect \ and layer.Visible(): redraw = rect self.clear_area_doc(redraw) # # Widget Methods (Redraw, ... ) # time_redraw = 0 def RedrawMethod(self, region=None): # draw the document if __debug__: if self.time_redraw: import time start = time.clock() if self.move_window_count >= 2: self.clear_window(update=0) self.move_window_count = 0 region = self.do_clear(region) # draw document self.gc.InitClip() self.gc.ResetFontCache() if region: self.gc.PushClip() self.gc.ClipRegion(region) tkwin = self.tkwin if region: x, y, w, h = region.ClipBox() if x < 0: w = w - x x = 0 if y < 0: h = h - y y = 0 if w > tkwin.width: w = tkwin.width if h > tkwin.height: h = tkwin.height else: x = y = 0 w = tkwin.width h = tkwin.height p1 = self.WinToDoc(x - 1, y - 1) p2 = self.WinToDoc(x + w + 1, y + h + 1) rect = Rect(p1, p2) self.gc.SetFillColor(StandardColors.white) self.gc.gc.FillRectangle(x, y, w, h) # XXX ugly to access gc.gc # draw paper if self.show_page_outline: w, h = self.document.PageSize() self.gc.DrawPageOutline(w, h) self.document.Draw(self.gc, rect) if region: self.gc.PopClip() if __debug__: if self.time_redraw: pdebug('timing', 'redraw', time.clock() - start) return region def ResizedMethod(self, width, height): Viewport.ResizedMethod(self, width, height) self.gc.WindowResized(width, height) # # Viewport- and related methods # # extend some Viewport methods to issue VIEW whenever # the displayed area changes # def ForceRedraw(self): # Force a redraw of the whole window self.clear_window() if __debug__: #self.time_redraw = 1 pass # def SetScrollbars(self, hbar, vbar): # Viewport.SetScrollbars(self, hbar, vbar) # hbar.configure(jump = 1) # vbar.configure(jump = 1) def set_origin(self, xorg, yorg, move_contents=1): self.begin_transaction() try: Viewport.set_origin(self, xorg, yorg, move_contents=move_contents) self.set_gc_transforms() self.issue_view() finally: self.end_transaction() def move_window_contents(self, offx, offy): # implement the method needed by Viewport.set_origin w = self.tkwin width = w.width height = w.height if abs(offx) < width and abs(offy) < height: w.CopyArea(w, self.gc.gc, offx, offy, width, height, 0, 0) self.move_window_count = self.move_window_count + 1 else: self.clear_window() def SetScale(self, scale, do_center=1): # Set current scale self.begin_transaction() try: Viewport.SetScale(self, scale, do_center=do_center) self.set_gc_transforms() finally: self.end_transaction() def zoom_fit_rect(self, rect, save_viewport=0): if save_viewport: self.save_viewport() Viewport.zoom_fit_rect(self, rect) # # other view related methods # def FitToWindow(self, selected_only=0, save_viewport=1): self.begin_transaction() try: if selected_only: rect = self.document.SelectionBoundingRect() else: rect = self.document.BoundingRect() if rect: self.zoom_fit_rect(rect, save_viewport=save_viewport) finally: self.end_transaction() def FitPageToWindow(self, save_viewport=1): self.begin_transaction() try: w, h = self.document.PageSize() self.zoom_fit_rect(Rect(0 - w * .03, 0 - h * .03, w * 1.03, h * 1.03).grown(10), save_viewport=save_viewport) finally: self.end_transaction() # # Outline Mode # # Although not directly related to the viewport methods (the outline # mode doesn't change the displayed area) the outline mode changes the # way the drawing is displayed and thus issues VIEW. def SetOutlineMode(self, on=1): self.begin_transaction() try: if on: if self.gc.IsOutlineActive(): return else: self.gc.StartOutlineMode() self.hitgc.StartOutlineMode() else: if self.gc.IsOutlineActive(): self.gc.EndOutlineMode() self.hitgc.EndOutlineMode() else: return self.issue_view() self.clear_window() finally: self.end_transaction() def ToggleOutlineMode(self): self.SetOutlineMode(not self.IsOutlineMode()) def IsOutlineMode(self): return self.gc and self.gc.IsOutlineActive() # # Show page outline on/off # def SetPageOutlineMode(self, on=1): self.begin_transaction() try: self.show_page_outline = on self.issue_view() self.clear_window() finally: self.end_transaction() def TogglePageOutlineMode(self): self.SetPageOutlineMode(not self.IsPageOutlineMode()) def IsPageOutlineMode(self): return self.show_page_outline # # # def unsubscribe_doc(self): if self.document is not None: self.document.Unsubscribe(REDRAW, self.redraw_doc) self.document.Unsubscribe(LAYOUT, self.layout_changed) self.document.Unsubscribe(LAYER, self.layer_changed) def subscribe_doc(self): self.document.Subscribe(REDRAW, self.redraw_doc) self.document.Subscribe(LAYOUT, self.layout_changed) self.document.Subscribe(LAYER, self.layer_changed) def SetDocument(self, doc): self.begin_transaction() try: self.unsubscribe_doc() self.document = doc self.subscribe_doc() self.clear_window() self.SetPageSize(self.document.Layout().Size()) self.FitPageToWindow(save_viewport=0) self.issue_document() self.issue_state() self.issue_view() finally: self.end_transaction()