Example #1
0
class KeyboardView(Frame):
    INPUT_KEYS = 'q2w3er5t6y7ui9o0pzsxdcfvbhnjm'

    def __init__(self, master, instrument=None):
        Frame.__init__(self, master)
        self.instrument = instrument

        self.canvas = Canvas(self,
                             width=KEYBOARD_WIDTH,
                             height=KEYBOARD_HEIGHT,
                             background='gray',
                             highlightthickness=0
                             )
        self.canvas.pack()

        self.keyboard_keys = {}

        for tone in range(MIN_TONE, MAX_TONE):
            self.keyboard_keys[tone] = self.create_key(tone)

        self.running = True
        self._worker()

    def _worker(self):
        if not self.running:
            return
        for k in self.keyboard_keys.values():
            k.work()
        self.after(UPDATE_INTERVAL, self._worker)


    def set_instrument(self, new_instrument):
        # print "set_instrument: %s" % str(new_instrument)
        for key in self.keyboard_keys.values():
            key.release()
        old_instrument = self.instrument
        self.instrument = new_instrument

        if not old_instrument is None:
            old_instrument.off()

    def create_key(self, tone):
        x0 = tone * KEY_WIDTH
        y0 = 0

        key = Key(tone, self, self.canvas, x0, y0)

        self.canvas.tag_bind(key.widget, "<Button-1>", key.press)
        self.canvas.tag_bind(key.widget, "<ButtonRelease-1>", key.release)

        if 0 <= tone < len(self.INPUT_KEYS):
            input_key = self.INPUT_KEYS[tone]
            self.bind_all("<Key-%s>" % input_key, key.press)
            self.bind_all("<KeyRelease-%s>" % input_key, key.release)

        return key

    def destroy(self):
        self.running = False
        return Frame.destroy(self)
Example #2
0
class Palette(Frame):
  """
  Tkinter Frame that displays all items that can be added to the board.
  """
  def __init__(self, parent, board, width=PALETTE_WIDTH,
      height=PALETTE_HEIGHT):
    """
    |board|: the board on to which items will be added from this palette.
    |width|: the width of this palette.
    |height|: the height of this palette.
    """
    assert isinstance(board, Board), 'board must be a Board'
    Frame.__init__(self, parent, background=PALETTE_BACKGROUND_COLOR)
    self.board = board
    # canvas on which items are displayed
    self.canvas = Canvas(self, width=width, height=height,
        highlightthickness=0, background=PALETTE_BACKGROUND_COLOR)
    self.width = width
    self.height = height
    # x-position of last item added on the LEFT side of this palette
    self.current_left_x = PALETTE_PADDING
    # x-position of last item added on the RIGHT side of this palette
    self.current_right_x = self.width
    # line to separate left and right sides of palette
    self.left_right_separation_line_id = None
    # setup ui
    self.canvas.pack()
    self.pack()
  def _add_item_callback(self, drawable_type, desired_offset_x, **kwargs):
    """
    Returns a callback method that, when called, adds an item of the given
        |drawable_type| to the board, at the given |desired_offset_x|.
    """
    assert issubclass(drawable_type, Drawable), ('drawable must be a Drawable '
        'subclass')
    def callback(event):
      # clear current message on the board, if any
      self.board.remove_message()
      # create new drawable
      new_drawable = drawable_type(**kwargs)
      desired_offset_y = (self.board.height - new_drawable.height -
          PALETTE_PADDING)
      desired_offset = (desired_offset_x, desired_offset_y)
      self.board.add_drawable(new_drawable, desired_offset)
      return new_drawable
    return callback
  def _spawn_types_callback(self, types_to_add, desired_offset_x):
    """
    Returns a callback method that, when called, adds the items given in
        |types_to_add| to the board, starting at the given |desired_offset_x|.
    """
    assert isinstance(types_to_add, list), 'types_to_add must be a list'
    def callback(event):
      dx = 0
      # assign the same color and group_id to the types being spawned
      color = '#%02X%02X%02X' % (randint(0, 200), randint(0, 200),
          randint(0, 200))
      group_id = int(round(time() * 1000))
      num_drawables_added = 0
      for add_type, add_kwargs in types_to_add:
        add_kwargs['color'] = color
        add_kwargs['group_id'] = group_id
        new_drawable = self._add_item_callback(add_type, desired_offset_x + dx,
            **add_kwargs)(event)
        if new_drawable:
          num_drawables_added += 1
          dx += new_drawable.width + BOARD_GRID_SEPARATION
      if num_drawables_added > 1:
        self.board._action_history.combine_last_n(num_drawables_added)
    return callback
  def add_drawable_type(self, drawable_type, side, callback, types_to_add=None,
      **kwargs):
    """
    Adds a drawable type for display on this palette.
    |drawable_type|: a subclass of Drawable to display.
    |side|: the side of this palette on which to put the display (LEFT or
        RIGHT).
    |callback|: method to call when display item is clicked. If None, the
        default callback adds an item of the display type to the board.
    |types_to_add|: a list of Drawables to add to the board when this item is
        clicked on the palette, or None if such a callback is not desired.
    |**kwargs|: extra arguments needed to initialize the drawable type.
    """
    assert issubclass(drawable_type, Drawable), ('drawable must be a Drawable '
        'subclass')
    # create a sample (display) drawable
    display = drawable_type(**kwargs)
    # draw the display on the appropriate side of the palette
    if side == RIGHT:
      offset_x = self.current_right_x - PALETTE_PADDING - display.width
      self.current_right_x -= display.width + PALETTE_PADDING
      # update left-right separation line
      if self.left_right_separation_line_id:
        self.canvas.delete(self.left_right_separation_line_id)
      separation_x = self.current_right_x - PALETTE_PADDING
      self.left_right_separation_line_id = self.canvas.create_line(
          separation_x, 0, separation_x, self.height,
          fill=PALETTE_SEPARATION_LINE_COLOR)
    else:
      # if given |side| is illegal, assume LEFT
      offset_x = self.current_left_x + PALETTE_PADDING
      self.current_left_x += PALETTE_PADDING + display.width + PALETTE_PADDING
    offset_y = (self.height - display.height) / 2
    if offset_y % BOARD_GRID_SEPARATION:
      offset_y = (offset_y / BOARD_GRID_SEPARATION) * BOARD_GRID_SEPARATION
    offset = (offset_x, offset_y)
    display.draw_on(self.canvas, offset)
    display.draw_connectors(self.canvas, offset)
    # attach callback to drawn parts
    # default callback adds items of this drawable type to the board
    if callback is None:
      callback = self._add_item_callback(drawable_type, offset_x, **kwargs) if (
          types_to_add is None) else self._spawn_types_callback(types_to_add,
          offset_x)
    else:
      assert types_to_add is None, ('if callback is provided, types_to_add '
          'will not be used')
    # bind callback
    for part in display.parts:
      self.canvas.tag_bind(part, '<Button-1>', callback)
      self.canvas.tag_bind(part, '<Double-Button-1>', callback)
    for connector in display.connectors:
      self.canvas.tag_bind(connector.canvas_id, '<Button-1>', callback)
      self.canvas.tag_bind(connector.canvas_id, '<Double-Button-1>', callback)
    return display
  def draw_separator(self):
    """
    Draws a separation line at the current left position.
    """
    self.canvas.create_line(self.current_left_x, 0, self.current_left_x,
        self.height, fill=PALETTE_SEPARATION_LINE_COLOR)
    self.current_left_x += PALETTE_PADDING
Example #3
0
class at_graph(Frame):

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.u = utils('atoutput.pkl')
        self.km = dict()
        self.price = dict()
        self.km[0] = (min(self.u.all_km), max(self.u.all_km))
        self.price[0] = (min(self.u.all_price), max(self.u.all_price))
        self.zoom_level = 0
        try:
            self.parent.title("Auto trader results")
            self.is_standalone = True
        except:
            self.is_standalone = False
        self.style = Style()
        self.style.theme_use("classic")
        # Assume the parent is the root widget; make the frame take up the
        # entire widget size.
        print self.is_standalone
        if self.is_standalone:
            self.w, self.h = map(int,
                self.parent.geometry().split('+')[0].split('x'))
            self.w, self.h = 800, 800
        else:
            self.w, self.h = 600, 600
        self.c = None
        # Are they hovering over a data point?
        self.is_hovering = False
        # Filter the description strings: lower and whiten any non-matching
        # data point.
        self.filter = ''
        self.re = list()
        self.replot()

    def replot(self, zlfrac=None):
        """Replot the graph. If zlfrac is not None, then it should be a
        fractional value between 0 and 1; this is used to do smooth zooming,
        which doesn't plot the axes (it only redraws the car points)."""
        if self.c is not None:
            self.c.destroy()
        self.c = Canvas(self, height=self.h, width=self.w, bd=1, bg='#f3f5f9')
        self.c.grid(sticky=S, pady=1, padx=1)
        zl = self.zoom_level
        if zlfrac is not None:
            z1l, z1h = self.zoom_price_start
            z2l, z2h = self.zoom_price_end
            price_low = z1l + (z2l - z1l) * zlfrac
            price_high = z1h + (z2h - z1h) * zlfrac
            z1l, z1h = self.zoom_km_start
            z2l, z2h = self.zoom_km_end
            km_low = z1l + (z2l - z1l) * zlfrac
            km_high = z1h + (z2h - z1h) * zlfrac
            self.axis((price_low, price_high), 'y', draw=False)
            self.axis((km_low, km_high), 'x', draw=False)
            self.car_points(draw=False)
        else:
            self.axis(self.price[zl], 'y')
            self.axis(self.km[zl], 'x')
            self.car_points()
        self.pack(fill=BOTH, expand=1)

    def xyp(self, x, y):
        "Given x in km and y in $, return canvas position (xp, yp)."
        xp = int(math.floor((1.0 * x - self.x1) / (self.x2 - self.x1) \
            * (self.xp2 - self.xp1) + self.xp1 + 0.5))
        yp = int(math.floor((1.0 * y - self.y1) / (self.y2 - self.y1) \
            * (self.yp2 - self.yp1) + self.yp1 + 0.5))
        return (xp, yp)

    def axis(self, arange, ax, draw=True):
        "Add an axis ax='x' or 'y', with arange=(min, max) values."
        if draw:
            a1, a2, ast = self.u.axis(*arange)
        else:
            a1, a2 = arange
            ast = (a2 - a1) * 0.2
        nt = int(math.floor((a2 - a1) / ast + 0.5)) + 1
        st_offset = 50
        # Remember the min and max axis values, along with the canvas points
        # that correspond to each location (xp1 and xp2). This allows us to
        # calculate where on the canvas a particular (x, y) value falls.
        if ax == 'x':
            self.x1, self.x2 = a1, a2
            self.xp1, self.xp2 = st_offset, self.w - st_offset
            self.xtick = [a1 + i * ast for i in range(nt)]
            # Remember where the midpoint of the axis is, relative to canvas.
            self.xmid = (self.xp1 + self.xp2) / 2
        else:
            self.y1, self.y2 = a1, a2
            self.yp1, self.yp2 = self.h - st_offset, st_offset
            self.ytick = [a1 + i * ast for i in range(nt)]
            # Remember where the midpoint of the axis is, relative to canvas.
            self.ymid = (self.yp1 + self.yp2) / 2
        # Figure out tick labels.
        atick = ['%g' % ((a1 + i * ast) / 1000) for i in range(nt)]
        # Figure out maximum decimal places on all tick labels, and ensure
        # they all have that many decimal places.
        max_dec = max(map(lambda x: 0 if '.' not in x
            else len(x.split('.')[1]), atick))
        if max_dec > 0:
            atick = map(lambda x: x + '.' + '0'*max_dec if '.' not in x
                else x + '0'*(max_dec - len(x.split('.')[1])), atick)
        yst, xst = self.h - st_offset, st_offset
        # Draw axis line proper, and axis label.
        if draw:
            if ax == 'x':
                self.c.create_line(xst, yst, self.w - st_offset, yst)
                xp = (xst + self.w - st_offset) / 2
                self.c.create_text(xp, yst + 30, text='Mileage (km x 1000)')
            else:
                self.c.create_line(xst, yst, xst, st_offset)
                self.c.create_text(xst, st_offset - 30, text='Price')
                self.c.create_text(xst, st_offset - 15, text='($000)')
        tick_anchor = [N, E][ax == 'y']
        tick_x, tick_y = xst, yst
        tick_step = ([self.w, self.h][ax == 'y'] - st_offset * 2 * 1.0) / \
            (nt - 1)
        label_offset = 3
        for i1, tick in enumerate(atick):
            x_of, y_of = -label_offset, label_offset
            if ax == 'y':
                y_of = int(-i1 * tick_step)
            else:
                x_of = int(i1 * tick_step)
            if draw:
                self.c.create_text(tick_x + x_of, tick_y + y_of,
                    text=tick, anchor=tick_anchor)
            x_mini, y_mini = 0, 0
            x_maxi, y_maxi = 0, 0
            if ax == 'y':
                x_of += label_offset
                x_mini, x_maxi = 8, self.w - st_offset * 2
                # Remember what y coord this grid line is at.
                if i1 == 0:
                    self.y_grid = []
                self.y_grid.append(tick_y + y_of)
            else:
                y_of -= label_offset
                y_mini, y_maxi = -8, st_offset * 2 - self.h
                # Remember what x coord this grid line is at.
                if i1 == 0:
                    self.x_grid = []
                self.x_grid.append(tick_x + x_of)
            if draw:
                # Draw the little solid tick, next to the axis.
                self.c.create_line(tick_x + x_of, tick_y + y_of,
                    tick_x + x_of + x_mini, tick_y + y_of + y_mini)
                # Draw a dashed grid line, across the entire graph.
                self.c.create_line(tick_x + x_of, tick_y + y_of,
                    tick_x + x_of + x_maxi, tick_y + y_of + y_maxi,
                    dash=(1, 3))

    def car_points(self, draw=True):
        "Plot the cars themselves."
        # 199 215 151 151 199 224 230 162 157 250 224 167 178 165 192 249 200 216 204 204 204 191 173 158
        color_order = ['#c7d797', '#97c7e0', '#e6a29d', '#fae0a7', '#b2a5c0',
            '#f9c8d8', '#bfad9e', '#cccccc']
        #color_order = ['#98df8a', '#dbdb8d', '#aec7e8', '#c9acd4', '#f7b6d2',
        #    '#ffbb80', '#dc9b8d', '#e9ab17', '#dddddd']
        # Those colors above aren't saturated enough. Saturate them more.
        color_order = map(lambda x: resaturate(x, -80), color_order)
        # Change color depending on year.
        cy = dict()
        for i1, year in enumerate(reversed(sorted(set(self.u.all_year)))):
            cy[year] = color_order[-1]
            if i1 < len(color_order):
                cy[year] = color_order[i1]
        i1 = -1
        # Tuples of (index into self.u.all_* arrays, x position, y position).
        self.ov_dict = dict()
        if draw:
            self.c.focus_set()
            self.c.bind('<Button-1>', func=self.zoom)
            self.c.bind('<Button-2>', func=self.unzoom)
            self.c.bind('<Left>', func=self.left_key)
            self.c.bind('<Right>', func=self.right_key)
            self.c.bind('<Up>', func=self.up_key)
            self.c.bind('<Down>', func=self.down_key)
        legend = set()
        osz = 3 + self.zoom_level * 1
        # Total vehicle count, and vehicles which pass the filter count.
        self.vcount = self.fcount = 0
        for year, km, price in zip(self.u.all_year, self.u.all_km,
            self.u.all_price):
            x, y = self.xyp(km, price)
            i1 += 1
            if x < self.x_grid[0] or x > self.x_grid[-1] or \
                y > self.y_grid[0] or y < self.y_grid[-1]:
                continue
            self.vcount += 1
            legend.add((year, cy[year]))
            filtered = False
            if not re.search(self.filter, self.u.all_descr[i1], re.I):
                filtered = True
            # If a data point is filtered out, make its outline reflect its
            # model year, and fill it with white.
            #
            # Else, make its outline and fill color reflect the model year, and
            # upon mouseover, make it entirely red.
            ov = self.c.create_oval(x-osz, y-osz, x+osz, y+osz,
                outline=cy[year],
                activeoutline=['red', cy[year]][filtered],
                fill=[cy[year], 'white'][filtered],
                activefill=['red', 'white'][filtered],
            )
            self.ov_dict[ov] = (i1, x, y, cy[year], filtered)
            # If a data point is filtered out, mousing over it does nothing,
            # and also, lower it behind everything else.
            if filtered:
                self.c.lower(ov)
            else:
                self.fcount += 1
                if draw:
                    use_tag = 'Tag %d' % i1
                    self.c.addtag_withtag(use_tag, ov)
                    self.c.tag_bind(use_tag, sequence='<Enter>',
                        func=self.mouseover)
                    self.c.tag_bind(use_tag, sequence='<Leave>',
                        func=self.mouseoff)
                    self.c.tag_bind(use_tag, sequence='<Button-1>',
                        func=self.select)
        if draw:
            # OK, add a legend for every year that's displayed.
            i1 = 0
            for yr, color in reversed(sorted(legend)):
                xp, yp = self.x_grid[-1] + 10, self.y_grid[-1] + 15 * i1
                self.c.create_oval(xp-osz, yp-osz, xp+osz, yp+osz,
                    outline=color, fill=color)
                self.c.create_text(xp + 8, yp, text=str(yr), anchor=W)
                i1 += 1
            # And, add a title.
            tistr = 'Vehicle count: %d' % self.vcount
            if self.fcount != self.vcount:
                tistr = 'Filtered vehicle count: %d' % self.fcount
            xp = (self.x_grid[0] + self.x_grid[-1]) / 2
            yp = self.y_grid[-1] - 30
            self.c.create_text(xp, yp, text=tistr, font=('Helvetica', '16'))
            zstr1 = 'Click on a blank graph location to zoom in'
            zstr2 = 'Right click to zoom out'
            if self.zoom_level == 0:
                zstr = zstr1
            elif self.zoom_level == 2:
                zstr = zstr2
            else:
                zstr = zstr1 + ', or r' + zstr2[1:]
            self.c.create_text(xp, yp + 16, text=zstr, font=('Helvetica', '14'))

    def mouseover(self, event):
        oval = event.widget.find_closest(event.x, event.y)[0]
        # XXX Sometimes, the closest widget is an axis grid line, not an oval.
        # Need to handle this correctly eventually.
        if oval not in self.ov_dict:
            return
        self.is_hovering = True
        ind, x, y, color, filtered = self.ov_dict[oval]
        # Figure out how high the box needs to be by creating the text off-
        # graph, then getting its bbox and deleting it.
        w = 200
        de_text = self.u.all_descr[ind]
        deobj = self.c.create_text(self.w + 3, self.h + 3, text=de_text,
            anchor=N+W, width=w-6, font=('Helvetica', '14'))
        bbox = self.c.bbox(deobj)
        self.c.delete(deobj)
        h = 18 + bbox[3] - bbox[1]
        border = 5
        if x > self.xmid:
            x -= (w + border)
        else:
            x += border
        if y > self.ymid:
            y -= (h + border)
        else:
            y += border
        self.re = list()
        self.re.append(self.c.create_rectangle(x, y, x + w, y + h,
            fill=resaturate(color, 50)))
        pr_text = '$%s' % self.u.commafy(self.u.all_price[ind])
        self.re.append(self.c.create_text(x + 3, y + 3, text=pr_text,
            anchor=N+W, font=('Helvetica', '10')))
        km_text = '%skm' % self.u.commafy(self.u.all_km[ind])
        self.re.append(self.c.create_text(x + w - 3, y + 3, text=km_text,
            anchor=N+E, font=('Helvetica', '10')))
        wh_text = self.u.all_wherestr[ind]
        if wh_text[0].isdigit():
            wh_text += ' away'
        self.re.append(self.c.create_text(x + w/2, y + 3, text=wh_text,
            anchor=N, font=('Helvetica', '10')))
        self.re.append(self.c.create_text(x + 3, y + 16, text=de_text,
            anchor=N+W, width=w-6, font=('Helvetica', '14')))

    def set_filter(self, st):
        "Given a string 'st', filter ovals whose description doesn't match."
        self.filter = st
        self.replot()

    def mouseoff(self, event):
        "Code for mousing off a data point."
        # The tooptip rectangle and all its sub-objects need to be destroyed.
        map(self.c.delete, self.re)
        # Also, need to note that we're no longer over an oval -- that way,
        # Button-1 events will cause a zoom, rather than launching a web page.
        self.is_hovering = False

    def _zoom_animation(self):
        import time
        from math import tanh
        scale = 5
        for i1 in range(-scale, scale+1):
            self.replot(zlfrac=0.5 + 0.5*tanh(i1*2.0/scale)/tanh(2.0))
            self.c.update()

    def zoom(self, event):
        # Only zoom in if we're actually within the graph boundaries.
        if event.x <= self.x_grid[0] or event.x > self.x_grid[-1]:
            return
        if event.y >= self.y_grid[0] or event.y < self.y_grid[-1]:
            return
        # Don't zoom if we're hovering over a data point: let the web browser
        # event handler operate.
        if self.is_hovering:
            return
        # Don't zoom in more than twice.
        if self.zoom_level >= 2:
            return
        # Find the grid square which we're inside.
        for i1 in range(len(self.x_grid) - 1):
            if event.x <= self.x_grid[i1 + 1]:
                xgrid = i1 + 1
                break
        for i1 in range(len(self.y_grid) - 1):
            if event.y >= self.y_grid[i1 + 1]:
                ygrid = i1 + 1
                break
        self.zoom_level += 1
        zl = self.zoom_level
        # Make the limits of the new graph be those of the grid square which
        # was clicked inside.
        self.km[zl] = (self.xtick[xgrid-1], self.xtick[xgrid])
        self.price[zl] = (self.ytick[ygrid-1], self.ytick[ygrid])
        if zl == 1:
            self.zoom_price_start = self.u.axis(*self.price[0])[:2]
            self.zoom_km_start = self.u.axis(*self.km[0])[:2]
        else:
            self.zoom_price_start = self.price[zl - 1]
            self.zoom_km_start = self.km[zl - 1]
        self.zoom_price_end = self.price[zl]
        self.zoom_km_end = self.km[zl]
        self._zoom_animation()
        self.replot()

    def unzoom(self, event):
        # If already at maximum zoom, nothing to be done.
        if self.zoom_level == 0:
            return
        # If not clicking inside graph boundaries, don't unzoom.
        if event.x <= self.x_grid[0] or event.x > self.x_grid[-1]:
            return
        if event.y >= self.y_grid[0] or event.y < self.y_grid[-1]:
            return
        self.zoom_level -= 1
        zl = self.zoom_level
        self.zoom_price_start = self.price[zl + 1]
        self.zoom_km_start = self.km[zl + 1]
        if zl == 0:
            self.zoom_price_end = self.u.axis(*self.price[0])[:2]
            self.zoom_km_end = self.u.axis(*self.km[0])[:2]
        else:
            self.zoom_price_end = self.price[zl]
            self.zoom_km_end = self.km[zl]
        self._zoom_animation()
        self.replot()

    def left_key(self, event):
        zl = self.zoom_level
        if zl == 0:
            return
        # If at left edge already, don't scroll.
        kz = self.km[zl]
        if self.km[0][0] > kz[0]:
            return
        self.zoom_price_start = self.zoom_price_end = self.price[zl]
        self.zoom_km_start = kz
        self.km[zl] = (kz[0] - (kz[1] - kz[0]), kz[0])
        self.zoom_km_end = self.km[zl]
        self._zoom_animation()
        self.replot()

    def right_key(self, event):
        zl = self.zoom_level
        if zl == 0:
            return
        # If at right edge already, don't scroll.
        kz = self.km[zl]
        if self.km[0][1] < kz[1]:
            return
        self.zoom_price_start = self.zoom_price_end = self.price[zl]
        self.zoom_km_start = kz
        self.km[zl] = (kz[1], kz[1] + (kz[1] - kz[0]))
        self.zoom_km_end = self.km[zl]
        self._zoom_animation()
        self.replot()

    def down_key(self, event):
        zl = self.zoom_level
        if zl == 0:
            return
        # If at bottom edge already, don't scroll.
        pz = self.price[zl]
        if self.price[0][0] > pz[0]:
            return
        self.zoom_km_start = self.zoom_km_end = self.km[zl]
        self.zoom_price_start = pz
        self.price[zl] = (pz[0] - (pz[1] - pz[0]), pz[0])
        self.zoom_price_end = self.price[zl]
        self._zoom_animation()
        self.replot()

    def up_key(self, event):
        zl = self.zoom_level
        if zl == 0:
            return
        # If at top edge already, don't scroll.
        pz = self.price[zl]
        if self.price[0][1] < pz[1]:
            return
        self.zoom_km_start = self.zoom_km_end = self.km[zl]
        self.zoom_price_start = pz
        self.price[zl] = (pz[1], pz[1] + (pz[1] - pz[0]))
        self.zoom_price_end = self.price[zl]
        self._zoom_animation()
        self.replot()


    def select(self, event):
        "Open a web page, when a data point has been clicked on."
        oval = event.widget.find_closest(event.x, event.y)[0]
        # XXX As above, sometimes the closest widget is a grid line, not an
        # oval. Need to handle this correctly, eventually.
        if oval not in self.ov_dict:
            return
        ind, xp, yp, color, filtered = self.ov_dict[oval]
        webbrowser.open(self.u.all_alink[ind])
Example #4
0
class SimulationCanvas( object ):
    """A canvas where blocks can be dragged around and connected up"""

    size = ( width, height ) = ( 550, 300 )

    def __init__( self, frame ):
        # Create the canvas
        self.canvas = Canvas( frame,
                             width=self.width, height=self.height,
                             relief=RIDGE,
                             background=colours["background"],
                             borderwidth=1 )
        # Add event handlers for dragable items
        self.canvas.tag_bind ( "DRAG", "<ButtonPress-1>", self.mouse_down )
        #self.canvas.tag_bind ("DRAG", "<ButtonRelease-1>", self.mouse_release)
        self.canvas.tag_bind ( "DRAG", "<Enter>", self.enter )
        self.canvas.tag_bind ( "DRAG", "<Leave>", self.leave )
        self.canvas.pack( side=TOP )

        # Some default locations
        self.PREVIEW_WIDTH = 80
        self.PREVIEW_LOCATION = ( self.PREVIEW_X, self.PREVIEW_Y ) = ( 15, 30 )


        # Draw a "preview" area
        self.canvas.create_line( self.PREVIEW_WIDTH, 0, self.PREVIEW_WIDTH, self.height, dash=True )

        # A dict indexed by unique ID of elements in the canvas.
        self.blocks = {}

    def preview_actor( self, codefile ):
        """
        Display a preview of an actor or compisite actor in the canvas,
        it will be dragable into desired position
        """
        logging.debug( "Creating a preview of %(name)s on simulation canvas." % {'name':codefile.name} )

        logging.debug( "Deleting any existing items still tagged 'preview'" )
        self.canvas.delete( "preview" )

        block = CanvasBlock( self.canvas, codefile, *self.PREVIEW_LOCATION )
        self.blocks[block.id] = block



    def mouse_down( self, event ):
        logging.debug( "The mouse was pressed at (%d, %d)" % ( event.x, event.y ) )
        logging.debug( "The mouse went down on a block. Binding mouse release..." )
        selected = self.canvas.gettags( "current" )
        logging.debug( "Currently selected items tags are %s" % selected.__repr__() )
        self.selected_name = [tag for tag in selected if tag.startswith( "name:" ) ][0][5:]
        self.selected_id = [tag for tag in selected if tag.startswith( "id:" ) ][0][3:]
        self.selected_type = [tag for tag in selected if tag.startswith( "type:" ) ][0][5:]
        logging.debug( "Block selected was %s with id:%s" % ( self.selected_name, self.selected_id ) )

        #self.canvas.addtag( 'Selected', 'withtag', self.selected_id )
        logging.debug( "Current blocks are: %s" % self.blocks )
        #self.blocks[block_id].set_colour( colours['selected'] )

        if self.selected_type == "block" or self.selected_type == "text":
            self.blocks[self.selected_id].select(event.x, event.y)
            self.canvas.bind( "<ButtonRelease-1>", self.block_move_mouse_release )
        elif self.selected_type.startswith("input") or self.selected_type.startswith("output"):
            self.blocks[self.selected_id].select_port(self.selected_type)
            self.canvas.bind( "<ButtonRelease-1>", self.port_connect_mouse_release )
        else:
            logging.info("Tried to select %s" % self.selected_type)

    
    def block_move_mouse_release( self, event ):
        logging.debug( "The mouse was released at (%d, %d)" % ( event.x, event.y ) )
        self.canvas.bind( "<ButtonRelease-1>", lambda e: None )
        if event.x >= 0 and event.x <= self.canvas.winfo_width() \
            and event.y >= 0 and event.y <= self.canvas.winfo_height():
                logging.debug( "Valid move inside canvas. Relocating block." )
                self.blocks[self.selected_id].move_to(event.x, event.y)
                if event.x >= self.PREVIEW_WIDTH:
                    if self.blocks[self.selected_id].is_preview():
                        logging.info( "Moved out of preview zone, adding new component to model" )
                        #TODO HERE - add to model compiler or what ever...
                    self.blocks[self.selected_id].unselect()
                else:
                    self.blocks[self.selected_id].preview()
        else:
            logging.info( "Invalid move." )

    def port_connect_mouse_release( self, event ):
        logging.debug( "The mouse was released at (%d, %d)" % ( event.x, event.y ) )
        self.canvas.bind( "<ButtonRelease-1>", lambda e: None )
        if event.x >= 0 and event.x <= self.canvas.winfo_width() \
            and event.y >= 0 and event.y <= self.canvas.winfo_height():
                logging.debug( "Valid location inside canvas." )

                event.widget.itemconfigure( "Selected", fill="#000000" )
                event.widget.itemconfigure( "type:text", fill="#000000" )

                #block = self.canvas.gettags("Selected")
                #logging.debug("Block moved was made up of these components: %s" % block.__repr__())
                self.canvas.dtag( "Selected", "Selected" )

        else:
            logging.info( "Invalid wiring." )

    def enter( self, event ):
        logging.debug( "Enter" )

    def leave( self, event ):
        logging.debug( "Leaving" )
Example #5
0
class TopLevelView(BaseView):
    TEXT_LABEL_OFFSET = 35
    ROD_BOX_START_X = 50
    ROD_BOX_START_Y = 45
    ROD_BOX_WIDTH = 70
    ROD_BOX_HEIGHT = 25
    ROD_BOX_VERTICAL_OFFSET = 10

    # other values are the same like for the ROD boxes
    ROS_BOX_START_X = 150
    ROS_BOX_START_Y = 45

    def __init__(self, tkRoot, systems, app, logger):
        BaseView.__init__(self, tkRoot, app, logger)

        self.systems = systems  # names of all subsystems

        # Canvas options width, height seem not necessary ...
        self.canvas = Canvas(self.tkRoot, bg=BaseView.CANVAS_BG_COLOR)
        self.canvas.pack(expand=YES, fill=BOTH)

        self.__createRodBoxes()
        self.__createRosBoxes()
        self.__createRodRosLines()

        self.name = "TOP"  # name of this special view
        self.app.addActiveView(self.name, self)  # special top view

        # add logo
        logo = PhotoImage(file="doc/logo.gif")
        self.canvas.create_image(conf.GUI_WIDTH - 20,
                                 conf.GUI_HEIGHT - 20,
                                 image=logo)
        self.canvas.image = logo  # keep a reference, otherwise it won't appear

    def __openRosDetailedView(self, comp):
        try:
            rosTree = self.app.rosTrees[comp.name]
            # every view (except for TopLevelView) has a tree node
            # acting as root for such particular view
            treeRootNode = rosTree.root
            v = View(self.tkRoot, self.app, comp, treeRootNode, self.logger)
            v.createContent()
            self.logger.debug("View created for '%s'" % comp)
        except KeyError:
            m = "ROS data n/a for '%s'" % comp
            self.logger.warn(m)
            tkMessageBox.showwarning("Quit", m, parent=self.tkRoot)

    def openDetailedView(self, comp):
        if comp.group == "ROS":
            # check if view which is to be open has not been opened already
            if self.app.isViewActive(comp.name):
                m = ("View '%s' is already among active windows, "
                     "not created." % comp.name)
                self.logger.warn(m)
                tkMessageBox.showwarning("Quit", m, parent=self.tkRoot)
            else:
                self.__openRosDetailedView(comp)
        else:
            m = "No view available for '%s' " % comp
            self.logger.warn(m)
            tkMessageBox.showwarning("Quit", m, parent=self.tkRoot)

    def __createRodBoxes(self):
        state = NotAssociated()
        group = "ROD"  # title
        self.canvas.create_text(
            TopLevelView.ROD_BOX_START_X + TopLevelView.TEXT_LABEL_OFFSET,
            TopLevelView.ROD_BOX_START_Y - TopLevelView.ROD_BOX_HEIGHT,
            text=group,
            font=self.bigFont)

        for i in range(len(self.systems)):
            x0 = TopLevelView.ROD_BOX_START_X
            y0 = (TopLevelView.ROD_BOX_START_Y +
                  (i * (TopLevelView.ROD_BOX_HEIGHT +
                        TopLevelView.ROD_BOX_VERTICAL_OFFSET)))
            x1 = x0 + TopLevelView.ROD_BOX_WIDTH
            y1 = y0 + TopLevelView.ROD_BOX_HEIGHT

            box = Box(group, self.systems[i], self.canvas)
            box.create(x0, y0, x1, y1, state.color)
            box.text(x0 + TopLevelView.TEXT_LABEL_OFFSET,
                     y0 + TopLevelView.ROD_BOX_HEIGHT / 2, self.systems[i],
                     self.boxTitleFont)
            self.compStore.append(box)

    def __createRosBoxes(self):
        state = Inactive()
        group = "ROS"  # title
        self.canvas.create_text(
            TopLevelView.ROS_BOX_START_X + TopLevelView.TEXT_LABEL_OFFSET,
            TopLevelView.ROS_BOX_START_Y - TopLevelView.ROD_BOX_HEIGHT,
            text=group,
            font=self.bigFont)

        for i in range(len(self.systems)):
            # need to get a tree node controlling the created component
            try:
                rosTree = self.app.rosTrees[self.systems[i]]
                node = rosTree.root
                box = RenderableBox(group, self.canvas, node)
            except KeyError:
                # some defined system don't have data (node) to be controlled by
                box = Box(group, self.systems[i], self.canvas)

            x0 = TopLevelView.ROS_BOX_START_X
            y0 = (TopLevelView.ROS_BOX_START_Y +
                  (i * (TopLevelView.ROD_BOX_HEIGHT +
                        TopLevelView.ROD_BOX_VERTICAL_OFFSET)))
            x1 = x0 + TopLevelView.ROD_BOX_WIDTH
            y1 = y0 + TopLevelView.ROD_BOX_HEIGHT

            idBox = box.create(x0, y0, x1, y1, state.color)
            idText = box.text(x0 + TopLevelView.TEXT_LABEL_OFFSET,
                              y0 + TopLevelView.ROD_BOX_HEIGHT / 2,
                              self.systems[i], self.boxTitleFont)
            dch = DoubleClickHandler(box, self)
            # bind both IDs - box and text in the box
            self.canvas.tag_bind(idBox, "<Double-1>", dch)
            self.canvas.tag_bind(idText, "<Double-1>", dch)

            self.compStore.append(box)

    def __createRodRosLines(self):
        group = "RODROSLINE"
        for i in range(len(self.systems)):
            x0 = TopLevelView.ROD_BOX_START_X + TopLevelView.ROD_BOX_WIDTH
            y0 = (TopLevelView.ROS_BOX_START_Y +
                  (i * (TopLevelView.ROD_BOX_HEIGHT +
                        TopLevelView.ROD_BOX_VERTICAL_OFFSET)) +
                  TopLevelView.ROD_BOX_HEIGHT / 2)
            x1 = TopLevelView.ROS_BOX_START_X
            y1 = y0

            line = Line(group, self.systems[i], self.canvas)
            line.create(x0, y0, x1, y1)
            self.compStore.append(line)
class histogramWidget:
    BACKGROUND = "#222222"
    EDGE_HISTOGRAM_COLOR = "#999999"
    NODE_HISTOGRAM_COLOR = "#555555"
    TOOLTIP_COLOR="#FFFF55"
    
    PADDING = 8
    CENTER_WIDTH = 1
    CENTER_COLOR = "#444444"
    ZERO_GAP = 1
    UPDATE_WIDTH = 9
    UPDATE_COLOR = "#FFFFFF"
    HANDLE_WIDTH = 5
    HANDLE_COLOR = "#FFFFFF"
    HANDLE_LENGTH = (HEIGHT-2*PADDING)
    TICK_COLOR = "#FFFFFF"
    TICK_WIDTH = 10
    TICK_FACTOR = 2
    
    LOG_BASE = 10.0
    
    def __init__(self, parent, x, y, width, height, data, logScale=False, callback=None):
        self.canvas = Canvas(parent,background=histogramWidget.BACKGROUND, highlightbackground=histogramWidget.BACKGROUND,width=width,height=height)
        self.canvas.place(x=x,y=y,width=width,height=height,bordermode="inside")
        
        self.logScale = logScale
        
        self.callback = callback
        
        self.edgeBars = []
        self.nodeBars = []
        
        self.binValues = []
        self.numBins = len(data) - 1
        
        self.currentBin = self.numBins     # start the slider at the highest bin
        
        edgeRange = 0.0
        nodeRange = 0.0
        
        for values in data.itervalues():
            if values[0] > edgeRange:
                edgeRange = values[0]
            if values[1] > nodeRange:
                nodeRange = values[1]
        
        edgeRange = float(edgeRange)    # ensure that it will yield floats when used in calculations...
        nodeRange = float(nodeRange)
        
        if logScale:
            edgeRange = math.log(edgeRange,histogramWidget.LOG_BASE)
            nodeRange = math.log(nodeRange,histogramWidget.LOG_BASE)
        
        # calculate the center line - but don't draw it yet
        self.center_x = histogramWidget.PADDING
        if self.logScale:
            self.center_x += histogramWidget.TICK_WIDTH+histogramWidget.PADDING
        self.center_y = height/2
        self.center_x2 = width-histogramWidget.PADDING
        self.center_y2 = self.center_y + histogramWidget.CENTER_WIDTH
        
        # draw the histograms with background-colored baseline rectangles (these allow tooltips to work on very short bars with little area)
        self.bar_interval = float(self.center_x2 - self.center_x) / (self.numBins+1)
        bar_x = self.center_x
        edge_y2 = self.center_y-histogramWidget.PADDING
        edge_space = edge_y2-histogramWidget.PADDING
        node_y = self.center_y2+histogramWidget.PADDING
        node_space = (height-node_y)-histogramWidget.PADDING
        
        thresholds = sorted(data.iterkeys())
        for threshold in thresholds:
            self.binValues.append(threshold)
            edgeWeight = data[threshold][0]
            nodeWeight = data[threshold][1]
            if logScale:
                if edgeWeight > 0:
                    edgeWeight = math.log(edgeWeight,histogramWidget.LOG_BASE)
                else:
                    edgeWeight = 0
                if nodeWeight > 0:
                    nodeWeight = math.log(nodeWeight,histogramWidget.LOG_BASE)
                else:
                    nodeWeight = 0
            
            bar_x2 = bar_x + self.bar_interval
            
            edge_y = histogramWidget.PADDING + int(edge_space*(1.0-edgeWeight/edgeRange))
            edge = self.canvas.create_rectangle(bar_x,edge_y,bar_x2,edge_y2,fill=histogramWidget.EDGE_HISTOGRAM_COLOR,width=0)
            baseline = self.canvas.create_rectangle(bar_x,edge_y2+histogramWidget.ZERO_GAP,bar_x2,edge_y2+histogramWidget.PADDING,fill=histogramWidget.BACKGROUND,width=0)
            self.canvas.addtag_withtag("Threshold: %f" % threshold,edge)
            self.canvas.addtag_withtag("No. Edges: %i" % data[threshold][0],edge)
            self.canvas.tag_bind(edge,"<Enter>",self.updateToolTip)
            self.canvas.tag_bind(edge,"<Leave>",self.updateToolTip)
            self.edgeBars.append(edge)
            self.canvas.addtag_withtag("Threshold: %f" % threshold,baseline)
            self.canvas.addtag_withtag("No. Edges: %i" % data[threshold][0],baseline)
            self.canvas.tag_bind(baseline,"<Enter>",self.updateToolTip)
            self.canvas.tag_bind(baseline,"<Leave>",self.updateToolTip)
            
            node_y2 = node_y + int(node_space*(nodeWeight/nodeRange))
            node = self.canvas.create_rectangle(bar_x,node_y,bar_x2,node_y2,fill=histogramWidget.NODE_HISTOGRAM_COLOR,width=0)
            baseline = self.canvas.create_rectangle(bar_x,node_y-histogramWidget.PADDING,bar_x2,node_y-histogramWidget.ZERO_GAP,fill=histogramWidget.BACKGROUND,width=0)
            self.canvas.addtag_withtag("Threshold: %f" % threshold,node)
            self.canvas.addtag_withtag("No. Nodes: %i" % data[threshold][1],node)
            self.canvas.tag_bind(node,"<Enter>",self.updateToolTip)
            self.canvas.tag_bind(node,"<Leave>",self.updateToolTip)
            self.nodeBars.append(node)
            self.canvas.addtag_withtag("Threshold: %f" % threshold,baseline)
            self.canvas.addtag_withtag("No. Nodes: %i" % data[threshold][1],baseline)
            self.canvas.tag_bind(baseline,"<Enter>",self.updateToolTip)
            self.canvas.tag_bind(baseline,"<Leave>",self.updateToolTip)
            
            bar_x = bar_x2
        
        # now draw the center line
        self.centerLine = self.canvas.create_rectangle(self.center_x,self.center_y,self.center_x2,self.center_y2,fill=histogramWidget.CENTER_COLOR,width=0)
        
        # draw the tick marks if logarithmic
        if self.logScale:
            tick_x = histogramWidget.PADDING
            tick_x2 = histogramWidget.PADDING+histogramWidget.TICK_WIDTH
            
            start_y = edge_y2
            end_y = histogramWidget.PADDING
            dist = start_y-end_y
            while dist > 1:
                dist /= histogramWidget.TICK_FACTOR
                self.canvas.create_rectangle(tick_x,end_y+dist-1,tick_x2,end_y+dist,fill=histogramWidget.TICK_COLOR,width=0)
            
            start_y = node_y
            end_y = height-histogramWidget.PADDING
            dist = end_y-start_y
            while dist > 1:
                dist /= histogramWidget.TICK_FACTOR
                self.canvas.create_rectangle(tick_x,end_y-dist,tick_x2,end_y-dist+1,fill=histogramWidget.TICK_COLOR,width=0)
        
        # draw the update bar
        bar_x = self.currentBin*self.bar_interval + self.center_x
        bar_x2 = self.center_x2
        bar_y = self.center_y-histogramWidget.UPDATE_WIDTH/2
        bar_y2 = bar_y+histogramWidget.UPDATE_WIDTH
        self.updateBar = self.canvas.create_rectangle(bar_x,bar_y,bar_x2,bar_y2,fill=histogramWidget.UPDATE_COLOR,width=0)
        
        # draw the handle
        handle_x = self.currentBin*self.bar_interval-histogramWidget.HANDLE_WIDTH/2+self.center_x
        handle_x2 = handle_x+histogramWidget.HANDLE_WIDTH
        handle_y = self.center_y-histogramWidget.HANDLE_LENGTH/2
        handle_y2 = handle_y+histogramWidget.HANDLE_LENGTH
        self.handleBar = self.canvas.create_rectangle(handle_x,handle_y,handle_x2,handle_y2,fill=histogramWidget.HANDLE_COLOR,width=0)
        self.canvas.tag_bind(self.handleBar, "<Button-1>",self.adjustHandle)
        self.canvas.tag_bind(self.handleBar, "<B1-Motion>",self.adjustHandle)
        self.canvas.tag_bind(self.handleBar, "<ButtonRelease-1>",self.adjustHandle)
        parent.bind("<Left>",lambda e: self.nudgeHandle(e,-1))
        parent.bind("<Right>",lambda e: self.nudgeHandle(e,1))
        
        # init the tooltip as nothing
        self.toolTipBox = self.canvas.create_rectangle(0,0,0,0,state="hidden",fill=histogramWidget.TOOLTIP_COLOR,width=0)
        self.toolTip = self.canvas.create_text(0,0,state="hidden",anchor="nw")
        self.canvas.bind("<Enter>",self.updateToolTip)
        self.canvas.bind("<Leave>",self.updateToolTip)
    
    def adjustHandle(self, event):
        newBin = int(self.numBins*(event.x-self.center_x)/float(self.center_x2-self.center_x)+0.5)
        if newBin == self.currentBin or newBin < 0 or newBin > self.numBins:
            return
        
        self.canvas.move(self.handleBar,(newBin-self.currentBin)*self.bar_interval,0)
        self.currentBin = newBin
        if self.callback != None:
            self.callback(self.binValues[newBin])
    
    def nudgeHandle(self, event, distance):
        temp = self.currentBin+distance
        if temp < 0 or temp > self.numBins:
            return
        
        self.canvas.move(self.handleBar,distance*self.bar_interval,0)
        self.currentBin += distance
        
        if self.callback != None:
            self.callback(self.binValues[self.currentBin])
    
    def update(self, currentBins):
        currentBar = self.canvas.coords(self.updateBar)
        self.canvas.coords(self.updateBar,currentBins*self.bar_interval+self.center_x,currentBar[1],currentBar[2],currentBar[3])
    
    def updateToolTip(self, event):
        allTags = self.canvas.gettags(self.canvas.find_overlapping(event.x,event.y,event.x+1,event.y+1))
        
        if len(allTags) == 0:
            self.canvas.itemconfig(self.toolTipBox,state="hidden")
            self.canvas.itemconfig(self.toolTip,state="hidden")
            return
        
        outText = ""
        for t in allTags:
            if t == "current":
                continue
            outText += t + "\n"
        
        outText = outText[:-1]  # strip the last return
        
        self.canvas.coords(self.toolTip,event.x+20,event.y)
        self.canvas.itemconfig(self.toolTip,state="normal",text=outText,anchor="nw")
        # correct if our tooltip is off screen
        textBounds = self.canvas.bbox(self.toolTip)
        if textBounds[2] >= WIDTH-2*histogramWidget.PADDING:
            self.canvas.itemconfig(self.toolTip, anchor="ne")
            self.canvas.coords(self.toolTip,event.x-20,event.y)
            if textBounds[3] >= HEIGHT-2*histogramWidget.PADDING:
                self.canvas.itemconfig(self.toolTip, anchor="se")
        elif textBounds[3] >= HEIGHT-2*histogramWidget.PADDING:
            self.canvas.itemconfig(self.toolTip, anchor="sw")
        
        # draw the box behind it
        self.canvas.coords(self.toolTipBox,self.canvas.bbox(self.toolTip))
        self.canvas.itemconfig(self.toolTipBox, state="normal")
Example #7
0
class Palette(Frame):
    """
  Tkinter Frame that displays all items that can be added to the board.
  """
    def __init__(self,
                 parent,
                 board,
                 width=PALETTE_WIDTH,
                 height=PALETTE_HEIGHT):
        """
    |board|: the board on to which items will be added from this palette.
    |width|: the width of this palette.
    |height|: the height of this palette.
    """
        assert isinstance(board, Board), 'board must be a Board'
        Frame.__init__(self, parent, background=PALETTE_BACKGROUND_COLOR)
        self.board = board
        # canvas on which items are displayed
        self.canvas = Canvas(self,
                             width=width,
                             height=height,
                             highlightthickness=0,
                             background=PALETTE_BACKGROUND_COLOR)
        self.width = width
        self.height = height
        # x-position of last item added on the LEFT side of this palette
        self.current_left_x = PALETTE_PADDING
        # x-position of last item added on the RIGHT side of this palette
        self.current_right_x = self.width
        # line to separate left and right sides of palette
        self.left_right_separation_line_id = None
        # setup ui
        self.canvas.pack()
        self.pack()

    def _add_item_callback(self, drawable_type, desired_offset_x, **kwargs):
        """
    Returns a callback method that, when called, adds an item of the given
        |drawable_type| to the board, at the given |desired_offset_x|.
    """
        assert issubclass(drawable_type,
                          Drawable), ('drawable must be a Drawable '
                                      'subclass')

        def callback(event):
            # clear current message on the board, if any
            self.board.remove_message()
            # create new drawable
            new_drawable = drawable_type(**kwargs)
            desired_offset_y = (self.board.height - new_drawable.height -
                                PALETTE_PADDING)
            desired_offset = (desired_offset_x, desired_offset_y)
            self.board.add_drawable(new_drawable, desired_offset)
            return new_drawable

        return callback

    def _spawn_types_callback(self, types_to_add, desired_offset_x):
        """
    Returns a callback method that, when called, adds the items given in
        |types_to_add| to the board, starting at the given |desired_offset_x|.
    """
        assert isinstance(types_to_add, list), 'types_to_add must be a list'

        def callback(event):
            dx = 0
            # assign the same color and group_id to the types being spawned
            color = '#%02X%02X%02X' % (randint(0, 200), randint(
                0, 200), randint(0, 200))
            group_id = int(round(time() * 1000))
            num_drawables_added = 0
            for add_type, add_kwargs in types_to_add:
                add_kwargs['color'] = color
                add_kwargs['group_id'] = group_id
                new_drawable = self._add_item_callback(add_type,
                                                       desired_offset_x + dx,
                                                       **add_kwargs)(event)
                if new_drawable:
                    num_drawables_added += 1
                    dx += new_drawable.width + BOARD_GRID_SEPARATION
            if num_drawables_added > 1:
                self.board._action_history.combine_last_n(num_drawables_added)

        return callback

    def add_drawable_type(self,
                          drawable_type,
                          side,
                          callback,
                          types_to_add=None,
                          **kwargs):
        """
    Adds a drawable type for display on this palette.
    |drawable_type|: a subclass of Drawable to display.
    |side|: the side of this palette on which to put the display (LEFT or
        RIGHT).
    |callback|: method to call when display item is clicked. If None, the
        default callback adds an item of the display type to the board.
    |types_to_add|: a list of Drawables to add to the board when this item is
        clicked on the palette, or None if such a callback is not desired.
    |**kwargs|: extra arguments needed to initialize the drawable type.
    """
        assert issubclass(drawable_type,
                          Drawable), ('drawable must be a Drawable '
                                      'subclass')
        # create a sample (display) drawable
        display = drawable_type(**kwargs)
        # draw the display on the appropriate side of the palette
        if side == RIGHT:
            offset_x = self.current_right_x - PALETTE_PADDING - display.width
            self.current_right_x -= display.width + PALETTE_PADDING
            # update left-right separation line
            if self.left_right_separation_line_id:
                self.canvas.delete(self.left_right_separation_line_id)
            separation_x = self.current_right_x - PALETTE_PADDING
            self.left_right_separation_line_id = self.canvas.create_line(
                separation_x,
                0,
                separation_x,
                self.height,
                fill=PALETTE_SEPARATION_LINE_COLOR)
        else:
            # if given |side| is illegal, assume LEFT
            offset_x = self.current_left_x + PALETTE_PADDING
            self.current_left_x += PALETTE_PADDING + display.width + PALETTE_PADDING
        offset_y = (self.height - display.height) / 2
        if offset_y % BOARD_GRID_SEPARATION:
            offset_y = (offset_y /
                        BOARD_GRID_SEPARATION) * BOARD_GRID_SEPARATION
        offset = (offset_x, offset_y)
        display.draw_on(self.canvas, offset)
        display.draw_connectors(self.canvas, offset)
        # attach callback to drawn parts
        # default callback adds items of this drawable type to the board
        if callback is None:
            callback = self._add_item_callback(
                drawable_type, offset_x, **kwargs) if (
                    types_to_add is None) else self._spawn_types_callback(
                        types_to_add, offset_x)
        else:
            assert types_to_add is None, (
                'if callback is provided, types_to_add '
                'will not be used')
        # bind callback
        for part in display.parts:
            self.canvas.tag_bind(part, '<Button-1>', callback)
            self.canvas.tag_bind(part, '<Double-Button-1>', callback)
        for connector in display.connectors:
            self.canvas.tag_bind(connector.canvas_id, '<Button-1>', callback)
            self.canvas.tag_bind(connector.canvas_id, '<Double-Button-1>',
                                 callback)
        return display

    def draw_separator(self):
        """
    Draws a separation line at the current left position.
    """
        self.canvas.create_line(self.current_left_x,
                                0,
                                self.current_left_x,
                                self.height,
                                fill=PALETTE_SEPARATION_LINE_COLOR)
        self.current_left_x += PALETTE_PADDING
Example #8
0
class View(BaseView):

    TEXT_LABEL_OFFSET = 50
    BOX_START_X = 30
    BOX_START_Y = 45
    BOX_WIDTH = 100
    BOX_HEIGHT = 45
    BOX_VERTICAL_OFFSET = 15

    ANOTHER_GUI_OFF = 30
    GUI_WIDTH = 170

    def __init__(self, tkRoot, app, masterComp, parentNode, logger):
        BaseView.__init__(self, tkRoot, app, logger)
        # master (upper level view) component on which this view was issued
        self.masterComp = masterComp

        self.parentNode = parentNode

        # open detailed window - will go into View class, create Canvas in this window
        self.window = Toplevel(self.tkRoot)

        # self.window.bind("<<close-window>>", self.__onClose) # doesn't work
        self.window.protocol("WM_DELETE_WINDOW", self.__onClose)
        self.window.title("%s" % self.masterComp.name)

        self.canvas = Canvas(self.window, bg=BaseView.CANVAS_BG_COLOR)
        self.canvas.pack(expand=YES, fill=BOTH)

        self.app.addActiveView(self.masterComp.name, self)

    def createContent(self):
        self.__setWindowGeometry()
        self.__setScrollableView()
        self.__createBoxes()

    def __setScrollableView(self):
        """Sets a scrollbar and scroll area corresponding to number of
           boxes the views needs to accommodate.
        """
        oneBoxSpace = View.BOX_HEIGHT + View.BOX_VERTICAL_OFFSET
        vertNeeded = len(
            self.parentNode.children) * oneBoxSpace + View.BOX_START_Y
        if (View.BOX_START_Y + vertNeeded) < conf.GUI_HEIGHT:
            return

        self.logger.debug("View needs to be scrollable, setting ...")
        self._setScrollBar(vertNeeded)

    def _setScrollBar(self, verticalSpace):
        """Derived class RosView calls this method."""
        self.canvas.config(scrollregion=(0, 0, 200, verticalSpace))
        self.canvas.config(highlightthickness=0)  # no pixels to border

        sbar = Scrollbar(self.canvas)
        sbar.config(command=self.canvas.yview)  # xlink sbar and canv
        self.canvas.config(yscrollcommand=sbar.set)  # move one moves other
        sbar.pack(side=RIGHT, fill=Y)  # pack first=clip last
        self.canvas.pack(side=LEFT, expand=YES,
                         fill=BOTH)  # canvas clipped first

    def __createBoxes(self):
        # always be the same as self.masterComp.name (title and label must agree)
        group = self.parentNode.name  # self.masterComp.name

        self.canvas.create_text(View.BOX_START_X + View.TEXT_LABEL_OFFSET,
                                View.BOX_START_Y - View.BOX_VERTICAL_OFFSET,
                                text=group,
                                font=self.bigFont)

        nodeKeys = self.parentNode.children.keys()
        nodeKeys.sort()  # sort the keys alphabetically
        for i in range(len(nodeKeys)):
            node = self.parentNode.children[nodeKeys[i]]
            x0 = View.BOX_START_X
            y0 = (View.BOX_START_Y +
                  (i * (View.BOX_HEIGHT + View.BOX_VERTICAL_OFFSET)))
            x1 = x0 + View.BOX_WIDTH
            y1 = y0 + View.BOX_HEIGHT

            box = RenderableBox(self.masterComp.name, self.canvas, node)
            dch = DoubleClickHandler(box, self)
            idBox = box.create(x0, y0, x1, y1, node.state.color)
            idText = box.text(x0 + View.TEXT_LABEL_OFFSET,
                              y0 + View.BOX_HEIGHT / 2, node.name,
                              self.boxTitleFont)
            # bind both IDs - box and text in the box
            self.canvas.tag_bind(idBox, "<Double-1>", dch)
            self.canvas.tag_bind(idText, "<Double-1>", dch)

            # could even perhaps be list and not a dictionary
            self.compStore.append(box)

    def openDetailedView(self, comp):
        # check if view which is to be open has not been opened already
        if self.app.isViewActive(comp.name):
            m = ("View '%s' is already among active windows, "
                 "not created." % comp.name)
            self.logger.warn(m)
            tkMessageBox.showwarning("Quit", m, parent=self.window)
            return

        try:
            rootNode = self.parentNode.children[comp.name]
        except KeyError:
            self.logger.error("Could not access child node for '%s'" %
                              comp.name)
        else:
            V = rootNode.viewerForChildren
            v = V(self.tkRoot, self.app, comp, rootNode, self.logger)
            v.createContent()
            self.logger.debug("View created: name '%s' root node: "
                              "'%s'" % (comp.name, rootNode.name))

    def __onClose(self):
        self.app.removeActiveView(self.masterComp.name)
        self.window.destroy()

    def __setWindowGeometry(self):
        offsetX = View.ANOTHER_GUI_OFF * self.app.activeViewsCount()
        x = conf.GUI_OFF_X + conf.GUI_WIDTH + offsetX
        geom = "%sx%s+%s+%s" % (View.GUI_WIDTH, conf.GUI_HEIGHT, x,
                                conf.GUI_OFF_Y)
        self.window.geometry(geom)
class Proto_Board_Visualizer(Frame):
    """
  Tk Frame to visualize Proto boards.
  """
    def __init__(self, parent, proto_board, show_pwr_gnd_pins):
        """
    |proto_board|: the proto board to visualize.
    |show_pwr_gnd_pins|: flag whether to show pwr and gnd pins as a reminder to
        connect to a power supply.
    """
        Frame.__init__(self, parent, background=BACKGROUND_COLOR)
        self._parent = parent
        self._parent.title(WINDOW_TITLE)
        self._parent.resizable(0, 0)
        self._proto_board = proto_board
        self._show_pwr_gnd_pins = show_pwr_gnd_pins
        self._canvas = Canvas(self,
                              width=WIDTH,
                              height=HEIGHT,
                              background=BACKGROUND_COLOR)
        self._tooltip_helper = Tooltip_Helper(self._canvas)
        self._wire_parts = {}
        # state for outline highlighing
        self._piece_outline_id = None
        self._wire_outline_ids = []
        self._piece_highlight = lambda labels: None
        self._wire_highlight = lambda label: None
        self._setup_bindings()
        self._setup_menu()
        self._draw_proto_board()

    def _point_inside_piece(self, piece, x, y):
        """
    Returns True if the point (|x|, |y|) is on the gien |piece|.
    """
        r1, c1, r2, c2 = piece.bbox()
        x1, y1 = self._rc_to_xy((r1, c1))
        x2, y2 = self._rc_to_xy((r2, c2))
        return x1 <= x <= x2 + CONNECTOR_SIZE and y1 <= y <= y2 + CONNECTOR_SIZE

    def _maybe_show_tooltip(self, event):
        """
    Shows a tooltip of the respective node if the cursor is on a wire or a valid
        location on the proto board, or the respective piece label if the cursor
        is on a piece.
    """
        # check if cursor is on a wire
        item = self._canvas.find_closest(event.x, event.y)[0]
        if item in self._wire_parts:
            self._wire_highlight(self._wire_parts[item])
            return
        # check if cursor is on a piece
        for piece in self._proto_board.get_pieces():
            if self._point_inside_piece(piece, event.x, event.y):
                self._tooltip_helper.show_tooltip(
                    event.x,
                    event.y,
                    'ID: %s' % piece.label,
                    background=TOOLTIP_DRAWABLE_LABEL_BACKGROUND)
                self._piece_highlight(
                    piece.labels_at((event.x, event.y),
                                    self._rc_to_xy(piece.top_left_loc)))
                return
        self._piece_highlight([])
        # check if cursor is on a valid proto board location
        loc = self._xy_to_rc(event.x, event.y)
        if loc:
            node = self._proto_board.node_for(loc)
            if node:
                self._wire_highlight(node)
                return
        self._wire_highlight(None)
        # if none of the above, remove previous tooltip, if any
        self._tooltip_helper.hide_tooltip()

    def _setup_bindings(self):
        """
    Sets up event bindings.
    """
        self._canvas.bind('<Motion>', self._maybe_show_tooltip)

    def _rc_to_xy(self, loc):
        """
    Returns the top left corner of the connector located at row |r| column |c|.
    """
        r, c = loc
        x = c * (CONNECTOR_SIZE + CONNECTOR_SPACING) + PADDING
        y = r * (CONNECTOR_SIZE + CONNECTOR_SPACING) + PADDING + (
            num_vertical_separators(r) *
            (VERTICAL_SEPARATION - CONNECTOR_SPACING))
        return x, y

    def _xy_to_rc(self, x, y):
        """
    Returns the row and column of the valid location on the proto board
        containing the point (|x|, |y|), or None if no such location exists.
    """
        for r in xrange(PROTO_BOARD_HEIGHT):
            for c in xrange(PROTO_BOARD_WIDTH):
                if valid_loc((r, c)):
                    x1, y1 = self._rc_to_xy((r, c))
                    x2, y2 = x1 + CONNECTOR_SIZE, y1 + CONNECTOR_SIZE
                    if x1 <= x <= x2 and y1 <= y <= y2:
                        return r, c
        return None

    def _draw_connector(self,
                        x,
                        y,
                        fill=CONNECTOR_COLOR,
                        outline=CONNECTOR_COLOR):
        """
    Draws a connector at the given coordinate and with the given colors.
    """
        self._canvas.create_rectangle(x,
                                      y,
                                      x + CONNECTOR_SIZE,
                                      y + CONNECTOR_SIZE,
                                      fill=fill,
                                      outline=outline)

    def _draw_connectors(self):
        """
    Draws the connectors on the proto board.
    """
        for r in ROWS:
            for c in COLUMNS:
                if valid_loc((r, c)):
                    self._draw_connector(*self._rc_to_xy((r, c)))

    def _draw_bus_line(self, y, color):
        """
    Draws a bus line at the given horizontal position |y| and with the given
        |color|.
    """
        x_1 = self._rc_to_xy((0, 1))[0]
        x_2 = self._rc_to_xy((0, PROTO_BOARD_WIDTH - 1))[0]
        self._canvas.create_line(x_1, y, x_2, y, fill=color)

    def _draw_bus_lines(self):
        """
    Draws all four bus lines on the proto board.
    """
        offset = 10
        self._draw_bus_line(self._rc_to_xy((0, 0))[1] - offset, NEGATIVE_COLOR)
        self._draw_bus_line(
            self._rc_to_xy((1, 0))[1] + CONNECTOR_SIZE + offset,
            POSITIVE_COLOR)
        self._draw_bus_line(
            self._rc_to_xy((PROTO_BOARD_HEIGHT - 2, 0))[1] - (offset),
            NEGATIVE_COLOR)
        self._draw_bus_line(
            self._rc_to_xy(
                (PROTO_BOARD_HEIGHT - 1, 0))[1] + (CONNECTOR_SIZE + offset),
            POSITIVE_COLOR)

    def _draw_labels(self):
        """
    Draws the row and column labels.
    """
        # row labels
        row_labels = dict(
            zip(range(11, 1, -1),
                ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']))
        for r in filter(lambda r: r in row_labels, ROWS):
            self._canvas.create_text(self._rc_to_xy((r, -1)),
                                     text=row_labels[r])
            x, y = self._rc_to_xy((r, PROTO_BOARD_WIDTH))
            self._canvas.create_text(x + CONNECTOR_SIZE, y, text=row_labels[r])
        # columns labels
        h_offset = 2
        v_offset = 10
        for c in filter(lambda c: c == 0 or (c + 1) % 5 == 0, COLUMNS):
            x_1, y_1 = self._rc_to_xy((2, c))
            self._canvas.create_text(x_1 + h_offset,
                                     y_1 - v_offset,
                                     text=(c + 1))
            x_2, y_2 = self._rc_to_xy((PROTO_BOARD_HEIGHT - 3, c))
            self._canvas.create_text(x_2 + h_offset,
                                     y_2 + CONNECTOR_SIZE + v_offset,
                                     text=(c + 1))

    def _draw_gnd_pwr_pins(self):
        """
    Draws pins to show the ground and power rails.
    """
        # pin positions
        g_x, g_y = self._rc_to_xy((GROUND_RAIL, PROTO_BOARD_WIDTH - 3))
        p_x, p_y = self._rc_to_xy((POWER_RAIL, PROTO_BOARD_WIDTH - 3))
        # big rectangles
        large_h_offset = 3 * CONNECTOR_SIZE + 2 * CONNECTOR_SPACING
        small_h_offset = 2
        large_v_offset = 7
        small_v_offset = 3
        self._canvas.create_rectangle(g_x - small_h_offset,
                                      g_y - large_v_offset,
                                      g_x + large_h_offset,
                                      g_y + CONNECTOR_SIZE + small_v_offset,
                                      fill=NEGATIVE_COLOR)
        self._canvas.create_rectangle(p_x - small_h_offset,
                                      p_y - small_v_offset,
                                      p_x + large_h_offset,
                                      p_y + CONNECTOR_SIZE + large_v_offset,
                                      fill=POSITIVE_COLOR)
        # connectors
        self._draw_connector(g_x, g_y, outline='black')
        self._draw_connector(p_x, p_y, outline='black')
        # text
        text_v_offset = 2
        text_h_offset = 17
        self._canvas.create_text(g_x + text_h_offset,
                                 g_y - text_v_offset,
                                 text='gnd',
                                 fill='white')
        self._canvas.create_text(p_x + text_h_offset,
                                 p_y + text_v_offset,
                                 text='+10',
                                 fill='white')

    def _draw_piece(self, piece):
        """
    Draws the given circuit |piece| on the canvas.
    """
        piece.draw_on(self._canvas, self._rc_to_xy(piece.top_left_loc))

    def _draw_pieces(self):
        """
    Draws all the pieces on the proto board.
    """
        for piece in self._proto_board.get_pieces():
            self._draw_piece(piece)

    def _draw_wire(self, wire):
        """
    Draws the given |wire| on the canvas.
    """
        x_1, y_1 = self._rc_to_xy(wire.loc_1)
        x_2, y_2 = self._rc_to_xy(wire.loc_2)
        length = int(ceil(wire.length()))
        fill = 'white'
        if 1 < length < 10:
            fill = WIRE_COLORS[length]
        elif 10 <= length < 50:
            fill = WIRE_COLORS[(length + 9) / 10]

        def draw_wire(parts=None):
            if parts:
                for part in parts:
                    self._canvas.delete(part)
                    del self._wire_parts[part]
            new_parts = [
                self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
                                         y_1 + CONNECTOR_SIZE / 2,
                                         x_2 + CONNECTOR_SIZE / 2,
                                         y_2 + CONNECTOR_SIZE / 2,
                                         fill=WIRE_OUTLINE,
                                         width=7,
                                         capstyle='round'),
                self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
                                         y_1 + CONNECTOR_SIZE / 2,
                                         x_2 + CONNECTOR_SIZE / 2,
                                         y_2 + CONNECTOR_SIZE / 2,
                                         fill=fill,
                                         width=3,
                                         capstyle='round')
            ]
            for part in new_parts:
                self._canvas.tag_bind(part, '<Button-1>',
                                      lambda event: draw_wire(new_parts))
                self._wire_parts[part] = wire.node

        draw_wire()

    def _draw_wires(self):
        """
    Draws all the wires on the proto board.
    """
        for wire in sorted(self._proto_board.get_wires(),
                           key=lambda wire: -wire.length()):
            self._draw_wire(wire)

    def _redraw_wires(self):
        """
    Erases then redraws the wires on the protoboard.
    """
        for part in self._wire_parts:
            self._canvas.delete(part)
        self._wire_parts.clear()
        self._draw_wires()

    def _draw_proto_board(self):
        """
    Draws the given |proto_board|.
    """
        self._draw_connectors()
        self._draw_bus_lines()
        self._draw_labels()
        self._draw_pieces()
        self._draw_wires()
        if self._show_pwr_gnd_pins:
            self._draw_gnd_pwr_pins()
        self._canvas.pack()
        self.pack()

    def _wire_to_cmax_str(self, wire):
        """
    Returns a CMax representation (when saved in a file) of the given |wire|.
    """
        c1, r1 = loc_to_cmax_rep(wire.loc_1)
        c2, r2 = loc_to_cmax_rep(wire.loc_2)
        return 'wire: (%d,%d)--(%d,%d)' % (c1, r1, c2, r2)

    def _to_cmax_str(self):
        """
    Returns a string CMax representation of the proto board we are visualizing.
    """
        # header
        lines = ['#CMax circuit']
        # wires
        for wire in self._proto_board.get_wires():
            lines.append(self._wire_to_cmax_str(wire))
        # circuit pieces
        for piece in self._proto_board.get_pieces():
            cmax_str = piece.to_cmax_str()
            if cmax_str:
                lines.append(cmax_str)
        # power and ground pins
        if self._show_pwr_gnd_pins:
            lines.append('+10: (61,20)')
            lines.append('gnd: (61,19)')
        return '\n'.join(lines)

    def _save_as_cmax_file(self):
        """
    Presents a dialog box that will save the proto board we are visualizing as a
        CMax file.
    """
        file_name = asksaveasfilename(title='Save as CMax file ...',
                                      filetypes=[('CMax files',
                                                  CMAX_FILE_EXTENSION)])
        if file_name and not file_name.endswith(CMAX_FILE_EXTENSION):
            file_name += CMAX_FILE_EXTENSION
        if file_name:
            save_file = open(file_name, 'w')
            save_file.write(self._to_cmax_str())
            save_file.close()

    def _setup_menu(self):
        """
    Sets up a menu that lets the user save the proto board we are visualizing as
        a CMax file.
    """
        menu = Menu(self._parent, tearoff=0)
        save_menu = Menu(menu, tearoff=0)
        save_menu.add_command(label='Save as CMax file',
                              command=self._save_as_cmax_file)
        menu.add_cascade(label='File', menu=save_menu)
        edit_menu = Menu(menu, tearoff=0)
        edit_menu.add_command(label='Redraw wires', command=self._redraw_wires)
        menu.add_cascade(label='Edit', menu=edit_menu)
        self._parent.config(menu=menu)

    def outline_piece_from_label(self, label):
        """
    Draws the appropriate outline for the circuit piece with the given |label|.
    """
        # try block in case canvas is gone with someone still calling this method
        try:
            self._canvas.delete(self._piece_outline_id)
            for piece in self._proto_board.get_pieces():
                if label in piece.label.split(','):
                    self._piece_outline_id = piece.outline_label(
                        self._canvas, self._rc_to_xy(piece.top_left_loc),
                        label)
                    return
        except:
            pass

    def outline_wires_from_label(self, label):
        """
    Draws outlines on the wires that have the given |label|.
    """
        # try block in case canvas is gone with someone still calling this method
        try:
            for part in self._wire_outline_ids:
                self._canvas.delete(part)
                del self._wire_parts[part]
            self._wire_outline_ids = []
            for wire in self._proto_board.get_wires():
                if wire.node == label:
                    x_1, y_1 = self._rc_to_xy(wire.loc_1)
                    x_2, y_2 = self._rc_to_xy(wire.loc_2)
                    self._wire_outline_ids.extend([
                        self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
                                                 y_1 + CONNECTOR_SIZE / 2,
                                                 x_2 + CONNECTOR_SIZE / 2,
                                                 y_2 + CONNECTOR_SIZE / 2,
                                                 fill='black',
                                                 width=7,
                                                 capstyle='round'),
                        self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
                                                 y_1 + CONNECTOR_SIZE / 2,
                                                 x_2 + CONNECTOR_SIZE / 2,
                                                 y_2 + CONNECTOR_SIZE / 2,
                                                 fill='cyan',
                                                 width=3,
                                                 capstyle='round')
                    ])
                    for part in self._wire_outline_ids:
                        self._wire_parts[part] = label
        except:
            pass

    def set_piece_highlight(self, f):
        """
    Resets the piece highlighting function to |f|.
    """
        self._piece_highlight = f

    def set_wire_highlight(self, f):
        """
    Resets the wire highlighing function to |f|.
    """
        def g(label):
            f(label)
            self.outline_wires_from_label(label)

        self._wire_highlight = g
Example #10
0
class TkFlameGraphRenderer:
    """Renders a call forest to a flame graph using Tk graphics."""
    def __init__(self, call_forest, tk, width, height, colours):
        self.call_forest = call_forest
        self.tk = tk
        self.width = width
        self.height = height
        self.colours = colours
        self.font = ('Courier', 12)
        self.character_width = 0.1 * Font(family='Courier',
                                          size=12).measure('mmmmmmmmmm')
        self.min_width_for_text = 3 * self.character_width
        self.min_height_for_text = 10
        self.x_scale = float(width) / call_forest.samples()
        self.y_scale = float(height) / call_forest.depth()
        self.top = Toplevel(tk)
        self.top.title(call_forest.name)
        self.canvas = Canvas(self.top, width=width, height=height)
        self.canvas.pack()

    def render(self):
        x = 2.0  # ************************************
        y = self.height - self.y_scale
        sorted_roots = sorted(self.call_forest.roots,
                              key=lambda n: n.signature)
        for root in sorted_roots:
            self.render_call_tree(root, x, y)
            x += self.x_scale * root.samples
        # Not sure why I need this
        self.canvas.tag_raise('signature')

    def render_call_tree(self, root, x, y):
        self.render_call_tree_node(root, x, y)
        child_x = x
        child_y = y - self.y_scale
        sorted_children = sorted(root.children, key=lambda n: n.signature)
        for child in sorted_children:
            self.render_call_tree(child, child_x, child_y)
            child_x += self.x_scale * child.samples

    def render_call_tree_node(self, node, x, y):
        bbox = self.bounding_box(node, x, y)
        colour = self.colours.colour_for(node)
        id = self.canvas.create_rectangle(bbox, fill=colour)
        self.bind_events(id, node)
        self.text(node, x, y)

    def bounding_box(self, node, x, y):
        left = x
        right = left + self.x_scale * node.samples
        top = y
        bottom = y + self.y_scale
        bbox = (left, top, right, bottom)
        return bbox

    def text(self, node, x, y):
        height = self.y_scale
        width = self.x_scale * node.samples
        if height > self.min_height_for_text and width > self.min_width_for_text:
            sig = node.signature
            i = sig.rfind('.')
            method = sig[i:]
            char_width = int(width / self.character_width)
            chars = method[:char_width] if len(
                method) >= char_width else sig[-char_width:]
            # chars = node.signature[:char_width]
            id = self.canvas.create_text((x + width / 2, y + height / 2),
                                         text=chars,
                                         anchor='center',
                                         font=self.font,
                                         tags='signature')
            self.bind_events(id, node)

    def bind_events(self, id, node):
        self.canvas.tag_bind(id, '<Enter>',
                             lambda e: self.mouse_enter(e, node))
        self.canvas.tag_bind(id, '<Leave>', self.mouse_leave)
        self.canvas.tag_bind(id, '<Double-Button-1>',
                             lambda e: self.zoom_in(e, node))

    def mouse_enter(self, event, node):
        self.show_tooltip(self.canvas.canvasx(event.x),
                          self.canvas.canvasy(event.y), node)

    def mouse_leave(self, event):
        self.hide_tooltip()

    def zoom_in(self, event, new_root):
        new_call_forest = CallForest(new_root.signature)
        new_call_forest.add_root(new_root)
        new_renderer = TkFlameGraphRenderer(new_call_forest, self.tk,
                                            self.width, self.height,
                                            self.colours)
        new_renderer.render()

    def show_tooltip(self, x, y, node):
        signature, samples = node.signature, node.samples
        percentage = (100.0 * samples) / self.call_forest.samples()
        text = '{} {} {:.2f}%'.format(signature, samples, percentage)
        c = self.canvas
        y_offset = (-10 if y > 30 else 20)
        anchor = 'sw' if x < self.width * 0.3 else 's' if x < self.width * 0.7 else 'se'
        label = c.create_text((x, y + y_offset),
                              text=text,
                              anchor=anchor,
                              tags='tooltip',
                              font=('Courier', 12))
        bounds = c.bbox(label)
        c.create_rectangle(bounds, fill='white', width=0, tags='tooltip')
        c.tag_raise(label)
        pass

    def hide_tooltip(self):
        self.canvas.delete('tooltip')
Example #11
0
class Proto_Board_Visualizer(Frame):
  """
  Tk Frame to visualize Proto boards.
  """
  def __init__(self, parent, proto_board, show_pwr_gnd_pins):
    """
    |proto_board|: the proto board to visualize.
    |show_pwr_gnd_pins|: flag whether to show pwr and gnd pins as a reminder to
        connect to a power supply.
    """
    Frame.__init__(self, parent, background=BACKGROUND_COLOR)
    self._parent = parent
    self._parent.title(WINDOW_TITLE)
    self._parent.resizable(0, 0)
    self._proto_board = proto_board
    self._show_pwr_gnd_pins = show_pwr_gnd_pins
    self._canvas = Canvas(self, width=WIDTH, height=HEIGHT,
        background=BACKGROUND_COLOR)
    self._tooltip_helper = Tooltip_Helper(self._canvas)
    self._wire_parts = {}
    # state for outline highlighing
    self._piece_outline_id = None
    self._wire_outline_ids = []
    self._piece_highlight = lambda labels: None
    self._wire_highlight = lambda label: None
    self._setup_bindings()
    self._setup_menu()
    self._draw_proto_board()
  def _point_inside_piece(self, piece, x, y):
    """
    Returns True if the point (|x|, |y|) is on the gien |piece|.
    """
    r1, c1, r2, c2 = piece.bbox()
    x1, y1 = self._rc_to_xy((r1, c1))
    x2, y2 = self._rc_to_xy((r2, c2))
    return x1 <= x <= x2 + CONNECTOR_SIZE and y1 <= y <= y2 + CONNECTOR_SIZE
  def _maybe_show_tooltip(self, event):
    """
    Shows a tooltip of the respective node if the cursor is on a wire or a valid
        location on the proto board, or the respective piece label if the cursor
        is on a piece.
    """
    # check if cursor is on a wire
    item = self._canvas.find_closest(event.x, event.y)[0]
    if item in self._wire_parts:
      self._wire_highlight(self._wire_parts[item])
      return
    # check if cursor is on a piece
    for piece in self._proto_board.get_pieces():
      if self._point_inside_piece(piece, event.x, event.y):
        self._tooltip_helper.show_tooltip(event.x, event.y,
            'ID: %s' % piece.label,
            background=TOOLTIP_DRAWABLE_LABEL_BACKGROUND)
        self._piece_highlight(piece.labels_at((event.x, event.y),
            self._rc_to_xy(piece.top_left_loc)))
        return
    self._piece_highlight([])
    # check if cursor is on a valid proto board location
    loc = self._xy_to_rc(event.x, event.y)
    if loc:
      node = self._proto_board.node_for(loc)
      if node:
        self._wire_highlight(node)
        return
    self._wire_highlight(None)
    # if none of the above, remove previous tooltip, if any
    self._tooltip_helper.hide_tooltip()
  def _setup_bindings(self):
    """
    Sets up event bindings.
    """
    self._canvas.bind('<Motion>', self._maybe_show_tooltip)
  def _rc_to_xy(self, loc):
    """
    Returns the top left corner of the connector located at row |r| column |c|.
    """
    r, c = loc
    x = c * (CONNECTOR_SIZE + CONNECTOR_SPACING) + PADDING
    y = r * (CONNECTOR_SIZE + CONNECTOR_SPACING) + PADDING + (
        num_vertical_separators(r) * (VERTICAL_SEPARATION - CONNECTOR_SPACING))
    return x, y
  def _xy_to_rc(self, x, y):
    """
    Returns the row and column of the valid location on the proto board
        containing the point (|x|, |y|), or None if no such location exists.
    """
    for r in xrange(PROTO_BOARD_HEIGHT):
      for c in xrange(PROTO_BOARD_WIDTH):
        if valid_loc((r, c)):
          x1, y1 = self._rc_to_xy((r, c))
          x2, y2 = x1 + CONNECTOR_SIZE, y1 + CONNECTOR_SIZE
          if x1 <= x <= x2 and y1 <= y <= y2:
            return r, c
    return None
  def _draw_connector(self, x, y, fill=CONNECTOR_COLOR,
      outline=CONNECTOR_COLOR):
    """
    Draws a connector at the given coordinate and with the given colors.
    """
    self._canvas.create_rectangle(x, y, x + CONNECTOR_SIZE, y + CONNECTOR_SIZE,
        fill=fill, outline=outline)
  def _draw_connectors(self):
    """
    Draws the connectors on the proto board.
    """
    for r in ROWS:
      for c in COLUMNS:
        if valid_loc((r, c)):
          self._draw_connector(*self._rc_to_xy((r, c)))
  def _draw_bus_line(self, y, color):
    """
    Draws a bus line at the given horizontal position |y| and with the given
        |color|.
    """
    x_1 = self._rc_to_xy((0, 1))[0]
    x_2 = self._rc_to_xy((0, PROTO_BOARD_WIDTH - 1))[0]
    self._canvas.create_line(x_1, y, x_2, y, fill=color)
  def _draw_bus_lines(self):
    """
    Draws all four bus lines on the proto board.
    """
    offset = 10
    self._draw_bus_line(self._rc_to_xy((0, 0))[1] - offset, NEGATIVE_COLOR)
    self._draw_bus_line(self._rc_to_xy((1, 0))[1] + CONNECTOR_SIZE + offset,
        POSITIVE_COLOR)
    self._draw_bus_line(self._rc_to_xy((PROTO_BOARD_HEIGHT - 2, 0))[1] - (
        offset), NEGATIVE_COLOR)
    self._draw_bus_line(self._rc_to_xy((PROTO_BOARD_HEIGHT - 1, 0))[1] + (
        CONNECTOR_SIZE + offset), POSITIVE_COLOR)
  def _draw_labels(self):
    """
    Draws the row and column labels.
    """
    # row labels
    row_labels = dict(zip(range(11, 1, -1), ['A', 'B', 'C', 'D', 'E', 'F', 'G',
        'H', 'I', 'J']))
    for r in filter(lambda r: r in row_labels, ROWS):
      self._canvas.create_text(self._rc_to_xy((r, -1)), text=row_labels[r])
      x, y = self._rc_to_xy((r, PROTO_BOARD_WIDTH))
      self._canvas.create_text(x + CONNECTOR_SIZE, y, text=row_labels[r])
    # columns labels
    h_offset = 2
    v_offset = 10
    for c in filter(lambda c: c == 0 or (c + 1) % 5 == 0, COLUMNS):
      x_1, y_1 = self._rc_to_xy((2, c))
      self._canvas.create_text(x_1 + h_offset, y_1 - v_offset, text=(c + 1))
      x_2, y_2 = self._rc_to_xy((PROTO_BOARD_HEIGHT - 3, c))
      self._canvas.create_text(x_2 + h_offset, y_2 + CONNECTOR_SIZE + v_offset,
          text=(c + 1))
  def _draw_gnd_pwr_pins(self):
    """
    Draws pins to show the ground and power rails.
    """
    # pin positions
    g_x, g_y = self._rc_to_xy((GROUND_RAIL, PROTO_BOARD_WIDTH - 3))
    p_x, p_y = self._rc_to_xy((POWER_RAIL, PROTO_BOARD_WIDTH - 3))
    # big rectangles
    large_h_offset = 3 * CONNECTOR_SIZE + 2 * CONNECTOR_SPACING
    small_h_offset = 2
    large_v_offset = 7
    small_v_offset = 3
    self._canvas.create_rectangle(g_x - small_h_offset, g_y - large_v_offset,
        g_x + large_h_offset, g_y + CONNECTOR_SIZE + small_v_offset,
        fill=NEGATIVE_COLOR)
    self._canvas.create_rectangle(p_x - small_h_offset, p_y - small_v_offset,
        p_x + large_h_offset, p_y + CONNECTOR_SIZE + large_v_offset,
        fill=POSITIVE_COLOR)
    # connectors
    self._draw_connector(g_x, g_y, outline='black')
    self._draw_connector(p_x, p_y, outline='black')
    # text
    text_v_offset = 2
    text_h_offset = 17
    self._canvas.create_text(g_x + text_h_offset, g_y - text_v_offset,
        text='gnd', fill='white')
    self._canvas.create_text(p_x + text_h_offset, p_y + text_v_offset,
        text='+10', fill='white')
  def _draw_piece(self, piece):
    """
    Draws the given circuit |piece| on the canvas.
    """
    piece.draw_on(self._canvas, self._rc_to_xy(piece.top_left_loc))
  def _draw_pieces(self):
    """
    Draws all the pieces on the proto board.
    """
    for piece in self._proto_board.get_pieces():
      self._draw_piece(piece)
  def _draw_wire(self, wire):
    """
    Draws the given |wire| on the canvas.
    """
    x_1, y_1 = self._rc_to_xy(wire.loc_1)
    x_2, y_2 = self._rc_to_xy(wire.loc_2)
    length = int(ceil(wire.length()))
    fill = 'white'
    if 1 < length < 10:
      fill = WIRE_COLORS[length]
    elif 10 <= length < 50:
      fill = WIRE_COLORS[(length + 9) / 10]
    def draw_wire(parts=None):
      if parts:
        for part in parts:
          self._canvas.delete(part)
          del self._wire_parts[part]
      new_parts = [self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
          y_1 + CONNECTOR_SIZE / 2, x_2 + CONNECTOR_SIZE / 2,
          y_2 + CONNECTOR_SIZE / 2, fill=WIRE_OUTLINE, width=7,
          capstyle='round'), self._canvas.create_line(x_1 + CONNECTOR_SIZE / 2,
          y_1 + CONNECTOR_SIZE / 2, x_2 + CONNECTOR_SIZE / 2,
          y_2 + CONNECTOR_SIZE / 2, fill=fill, width=3, capstyle='round')]
      for part in new_parts:
        self._canvas.tag_bind(part, '<Button-1>', lambda event: draw_wire(
            new_parts))
        self._wire_parts[part] = wire.node
    draw_wire()
  def _draw_wires(self):
    """
    Draws all the wires on the proto board.
    """
    for wire in sorted(self._proto_board.get_wires(),
        key=lambda wire: -wire.length()):
      self._draw_wire(wire)
  def _redraw_wires(self):
    """
    Erases then redraws the wires on the protoboard.
    """
    for part in self._wire_parts:
      self._canvas.delete(part)
    self._wire_parts.clear()
    self._draw_wires()
  def _draw_proto_board(self):
    """
    Draws the given |proto_board|.
    """
    self._draw_connectors()
    self._draw_bus_lines()
    self._draw_labels()
    self._draw_pieces()
    self._draw_wires()
    if self._show_pwr_gnd_pins:
      self._draw_gnd_pwr_pins()
    self._canvas.pack()
    self.pack()
  def _wire_to_cmax_str(self, wire):
    """
    Returns a CMax representation (when saved in a file) of the given |wire|.
    """
    c1, r1 = loc_to_cmax_rep(wire.loc_1)
    c2, r2 = loc_to_cmax_rep(wire.loc_2)
    return 'wire: (%d,%d)--(%d,%d)' % (c1, r1, c2, r2)
  def _to_cmax_str(self):
    """
    Returns a string CMax representation of the proto board we are visualizing.
    """
    # header
    lines = ['#CMax circuit']
    # wires
    for wire in self._proto_board.get_wires():
      lines.append(self._wire_to_cmax_str(wire))
    # circuit pieces
    for piece in self._proto_board.get_pieces():
      cmax_str = piece.to_cmax_str()
      if cmax_str:
        lines.append(cmax_str)
    # power and ground pins
    if self._show_pwr_gnd_pins:
      lines.append('+10: (61,20)')
      lines.append('gnd: (61,19)')
    return '\n'.join(lines)
  def _save_as_cmax_file(self):
    """
    Presents a dialog box that will save the proto board we are visualizing as a
        CMax file.
    """
    file_name = asksaveasfilename(title='Save as CMax file ...',
        filetypes=[('CMax files', CMAX_FILE_EXTENSION)])
    if file_name and not file_name.endswith(CMAX_FILE_EXTENSION):
      file_name += CMAX_FILE_EXTENSION
    if file_name:
      save_file = open(file_name, 'w')
      save_file.write(self._to_cmax_str())
      save_file.close()
  def _setup_menu(self):
    """
    Sets up a menu that lets the user save the proto board we are visualizing as
        a CMax file.
    """
    menu = Menu(self._parent, tearoff=0)
    save_menu = Menu(menu, tearoff=0)
    save_menu.add_command(label='Save as CMax file',
        command=self._save_as_cmax_file)
    menu.add_cascade(label='File', menu=save_menu)
    edit_menu = Menu(menu, tearoff=0)
    edit_menu.add_command(label='Redraw wires', command=self._redraw_wires)
    menu.add_cascade(label='Edit', menu=edit_menu)
    self._parent.config(menu=menu)
  def outline_piece_from_label(self, label):
    """
    Draws the appropriate outline for the circuit piece with the given |label|.
    """
    # try block in case canvas is gone with someone still calling this method
    try:
      self._canvas.delete(self._piece_outline_id)
      for piece in self._proto_board.get_pieces():
        if label in piece.label.split(','):
          self._piece_outline_id = piece.outline_label(self._canvas,
              self._rc_to_xy(piece.top_left_loc), label)
          return
    except:
      pass
  def outline_wires_from_label(self, label):
    """
    Draws outlines on the wires that have the given |label|.
    """
    # try block in case canvas is gone with someone still calling this method
    try:
      for part in self._wire_outline_ids:
        self._canvas.delete(part)
        del self._wire_parts[part]
      self._wire_outline_ids = []
      for wire in self._proto_board.get_wires():
        if wire.node == label:
          x_1, y_1 = self._rc_to_xy(wire.loc_1)
          x_2, y_2 = self._rc_to_xy(wire.loc_2)
          self._wire_outline_ids.extend([self._canvas.create_line(
              x_1 + CONNECTOR_SIZE / 2, y_1 + CONNECTOR_SIZE / 2, x_2 +
              CONNECTOR_SIZE / 2, y_2 + CONNECTOR_SIZE / 2, fill='black',
              width=7, capstyle='round'), self._canvas.create_line(x_1 +
              CONNECTOR_SIZE / 2, y_1 + CONNECTOR_SIZE / 2, x_2 +
              CONNECTOR_SIZE / 2, y_2 + CONNECTOR_SIZE / 2, fill='cyan',
              width=3, capstyle='round')])
          for part in self._wire_outline_ids:
            self._wire_parts[part] = label
    except:
      pass
  def set_piece_highlight(self, f):
    """
    Resets the piece highlighting function to |f|.
    """
    self._piece_highlight = f
  def set_wire_highlight(self, f):
    """
    Resets the wire highlighing function to |f|.
    """
    def g(label):
      f(label)
      self.outline_wires_from_label(label)
    self._wire_highlight = g
Example #12
0
class UI():
    """The UI class manages the buttons, map and tiles.

    The latter two are visual depictions of the world and cells respectively
    """
    def __init__(self, master, app, frame):
        """Create the window."""
        self.master = master
        self.app = app
        self.frame = frame
        self.world = self.app.simulation.world

        self.add_buttons()
        self.add_other_widgets()
        self.add_map()

        log(">> Creating tiles")
        self.create_tiles()
        log(">> Painting tiles")
        self.paint_tiles()

    def add_buttons(self):
        """Add buttons to the frame."""
        ground_button = Button(self.frame,
                               text="TERRA",
                               fg="red",
                               command=self.draw_terrain)
        ground_button.grid(row=1, column=0, sticky=W + E)
        water_button = Button(self.frame,
                              text="WATER",
                              fg="red",
                              command=self.toggle_water)
        water_button.grid(row=1, column=1, sticky=W + E)
        heat_button = Button(self.frame,
                             text="HEAT",
                             fg="red",
                             command=self.draw_heat)
        heat_button.grid(row=1, column=2, sticky=W + E)

    def add_other_widgets(self):
        """Add other widgets to the frame."""
        self.time_rate = StringVar()
        self.rate_label = Label(self.frame, textvariable=self.time_rate)
        self.rate_label.grid(row=0, column=0, sticky=W, columnspan=2)
        self.time_stamp = StringVar()
        self.time_label = Label(self.frame, textvariable=self.time_stamp)
        self.time_label.grid(row=0, column=2, columnspan=8)
        self.update_time_label(0)

    def add_map(self):
        """Add a blank map."""
        self.map = Canvas(self.master,
                          width=settings.map_width + 2 * settings.map_border,
                          height=settings.map_height + 2 * settings.map_border,
                          bg="black",
                          highlightthickness=0)
        self.map.grid(columnspan=12)

        for c in range(12):
            self.frame.grid_columnconfigure(c, minsize=settings.map_width / 12)

    def create_tiles(self):
        """Create blank tiles."""
        self.map.delete("all")
        self.tiles = []
        for cell in self.world.cells:
            n_in_row = len(
                [c for c in self.world.cells if c.latitude == cell.latitude])
            x_start = ((settings.map_width / 2.0) -
                       (n_in_row / 2.0) * settings.tile_width)
            y_start = ((cell.latitude / settings.cell_degree_width) *
                       settings.tile_height)

            self.tiles.append(
                self.map.create_rectangle((
                    x_start +
                    (cell.longitude / 360.0) * n_in_row * settings.tile_width),
                                          y_start,
                                          (x_start + settings.tile_width + 1 +
                                           (cell.longitude / 360.0) *
                                           n_in_row * settings.tile_width),
                                          y_start + settings.tile_height,
                                          fill="yellow",
                                          outline=""))
        for x in range(len(self.tiles)):
            self.map.tag_bind(self.tiles[x],
                              "<ButtonPress-1>",
                              lambda event, arg=x: self.left_click_tile(arg))
            self.map.tag_bind(self.tiles[x],
                              "<ButtonPress-2>",
                              lambda event, arg=x: self.right_click_tile(arg))

    def left_click_tile(self, x):
        """Tell world to raise terrain at cell x."""
        self.world.raise_cell(x, 1000)
        self.paint_tiles()

    def right_click_tile(self, x):
        """Tell world to raise terrain at cell x."""
        self.world.raise_cell(x, -1000)
        self.paint_tiles()

    def paint_tiles(self):
        """Color the tiles."""
        for x in range(len(self.tiles)):
            self.map.itemconfigure(self.tiles[x],
                                   fill=self.cell_color(self.world.cells[x]))

    def update_time_label(self, time):
        """Update the UI time label."""
        year = time / (60 * 60 * 24 * 365)
        time -= year * (60 * 60 * 24 * 365)
        day = time / (60 * 60 * 24)
        time -= day * (60 * 60 * 24)
        hour = time / (60 * 60)
        time -= hour * (60 * 60)
        minute = time / 60
        time -= minute * 60
        second = time
        year = format(year, ",d")
        hour = "%02d" % (hour, )
        minute = "%02d" % (minute, )
        second = "%02d" % (second, )
        self.time_stamp.set("{}:{}:{}, day {}, year {}.".format(
            hour, minute, second, day, year))
        self.time_rate.set("x{}".format(settings.time_step_description))

    def cell_color(self, cell):
        """Work out what color a tile should be.

        The color depends on the cell and the draw_mode parameter.
        """
        if settings.draw_mode == "terrain":
            if cell.water.depth == 0.0 or settings.draw_water is False:
                col_min = [50, 20, 4]
                col_max = [255, 255, 255]
                p = ((cell.land.height - settings.min_ground_height) /
                     (settings.max_ground_height - settings.min_ground_height))
            else:
                col_min = [153, 204, 255]
                col_max = [20, 20, 80]
                p = cell.water.depth / 6000.0
                if p > 1:
                    p = 1
        elif settings.draw_mode == "heat":
            if settings.draw_water is True:
                temp = cell.surface_temperature
            else:
                temp = cell.land.temperature
            if temp < 223:
                col_min = [0, 0, 0]
                col_max = [82, 219, 255]
                p = max(min((temp) / 223.0, 1), 0)
            elif temp < 273:
                col_min = [82, 219, 255]
                col_max = [255, 255, 255]
                p = max(min((temp - 223.0) / 50.0, 1), 0)
            elif temp < 313:
                col_min = [255, 255, 255]
                col_max = [255, 66, 0]
                p = max(min((temp - 273.0) / 40.0, 1), 0)
            else:
                col_min = [255, 66, 0]
                col_max = [0, 0, 0]
                p = max(min((temp - 313.0) / 100.0, 1), 0)

        elif settings.draw_mode == "wind":
            col_min = [0, 0, 0]
            col_max = [255, 255, 255]
            p = min(cell.wind_speed, 10) / 10
        q = 1 - p
        col = [
            int(q * col_min[0] + p * col_max[0]),
            int(q * col_min[1] + p * col_max[1]),
            int(q * col_min[2] + p * col_max[2])
        ]
        return '#%02X%02X%02X' % (col[0], col[1], col[2])

    def draw_terrain(self):
        """Paint map by altitude."""
        settings.draw_mode = "terrain"
        self.paint_tiles()

    def draw_heat(self):
        """Paint map by land temperature."""
        settings.draw_mode = "heat"
        self.paint_tiles()

    def toggle_water(self):
        """Toggle whether water is shown in terrain mode."""
        settings.draw_water = not settings.draw_water
        self.paint_tiles()
Example #13
0
class MainCanvas(object):
    """
    The shapefile displaying device based on TKinter Canvas

    Attributes
    ----------

    shapes           : array
                      The spatial units
    bbox             : array
                      The bounding box: minX, minY, maxX, maxY
    shp_type         : integer
                      The shape types: SHP_TYPE_POINT,SHP_TYPE_LINE,SHP_TYPE_POLYGON
    root             : Tk
                      The Tk Object
    attributeName    : string
                      The attribute name
    datalist         : array
                      The attribute data
                      
    """
    def __init__(self, shapes, bbox, shp_type, root, attributeName, datalist):
        self.shapes = shapes
        self.bbox = bbox
        self.shp_type = shp_type
        self.root = root
        self.attributeName = attributeName
        self.datalist = datalist
        self.__createCanvas()

    def __createCanvas(self):
        """
        Create the canvas and draw all the spatial objects
        """
        self.canvasRoot = Toplevel()
        self.canvasRoot.title(self.attributeName)
        self.canvasRoot.lower(belowThis=self.root)
        self.mainCanvas = Canvas(self.canvasRoot,
                                 bg='black',
                                 width=canvasWidth + margin_x,
                                 height=canvasHeight + margin_y,
                                 scrollregion=('-50c', '-50c', "50c", "50c"))

        #Change by Sagar for Full Screen
        self.canvasRoot.state('zoomed')
        self.canvasRoot.geometry = ("1000x900+0+0")
        #Change End

        self.__drawShape()
        self.mainCanvas.pack()

    def __drawShape(self):
        """
        Draw all the spatial objects on the canvas
        """
        minX, minY, maxX, maxY = self.bbox[0], self.bbox[1], self.bbox[
            2], self.bbox[3]
        # calculate ratios of visualization
        ratiox = canvasWidth / (maxX - minX)
        ratioy = canvasHeight / (maxY - minY)
        # take the smaller ratio of window size to geographic distance
        ratio = ratiox
        if ratio > ratioy:
            ratio = ratioy

        if self.shp_type == SHP_TYPE_POINT:
            self.__drawPoints(minX, minY, maxX, maxY, ratio)
        elif self.shp_type == SHP_TYPE_LINE:
            self.__drawPolylines(minX, minY, maxX, maxY, ratio)
        elif self.shp_type == SHP_TYPE_POLYGON:
            self.__drawPolygons(minX, minY, maxX, maxY, ratio)

    def __drawPoints(self, minX, minY, maxX, maxY, ratio):
        """
        Draw points on the canvas
        """
        tag_count = 0
        # loop through each point
        for point in self.shapes:
            #define an empty xylist for holding converted coordinates
            x = int((point.x - minX) * ratio) + margin_x / 2
            y = int((maxY - point.y) * ratio) + margin_y / 2
            _point = self.mainCanvas.create_oval(x - 2,
                                                 y - 2,
                                                 x + 2,
                                                 y + 2,
                                                 outline=point.color,
                                                 fill=point.color,
                                                 width=2,
                                                 tags=self.datalist[tag_count])
            self.mainCanvas.tag_bind(_point, '<ButtonPress-1>',
                                     self.__showAttriInfo)
            tag_count += 1

    def __drawPolylines(self, minX, minY, maxX, maxY, ratio):
        """
        Draw polylines on the canvas
        """
        tag_count = 0
        # loop through each polyline
        for polyline in self.shapes:
            #define an empty xylist for holding converted coordinates
            xylist = []
            # loops through each point and calculate the window coordinates, put in xylist
            for j in range(len(polyline.x)):
                pointx = int((polyline.x[j] - minX) * ratio) + margin_x / 2
                pointy = int((maxY - polyline.y[j]) * ratio) + margin_y / 2
                xylist.append(pointx)
                xylist.append(pointy)
            # loop through each part of the polyline
            for k in range(polyline.partsNum):
                #get the end sequence number of points in the part
                if (k == polyline.partsNum - 1):
                    endPointIndex = len(polyline.x)
                else:
                    endPointIndex = polyline.partsIndex[k + 1]
                # define a temporary list for holding the part coordinates
                tempXYlist = []
                #take out points' coordinates for the part and add to the temporary list
                for m in range(polyline.partsIndex[k], endPointIndex):
                    tempXYlist.append(xylist[m * 2])
                    tempXYlist.append(xylist[m * 2 + 1])
                # create the line
                _line = self.mainCanvas.create_line(
                    tempXYlist,
                    fill=polyline.color,
                    tags=self.datalist[tag_count])
                self.mainCanvas.tag_bind(_line, '<ButtonPress-1>',
                                         self.__showAttriInfo)
            tag_count += 1

    def __drawPolygons(self, minX, minY, maxX, maxY, ratio):
        """
        Draw polygons on the canvas
        """
        tag_count = 0
        for polygon in self.shapes:
            #define an empty xylist for holding converted coordinates
            xylist = []
            # loops through each point and calculate the window coordinates, put in xylist
            for point in polygon.points:
                pointx = int((point.x - minX) * ratio) + +margin_x / 0.5
                pointy = int((maxY - point.y) * ratio) + +margin_y / 5
                xylist.append(pointx)
                xylist.append(pointy)
##            print xylist
            """
            polyline.partsIndex is a tuple data type holding the starting points for each
            part. For example, if the polyline.partsIndex of a polyline equals to (0, 4, 9),
            and the total points, which is calcuate by len(polyline.points) equals to 13.
            This means that the polyline has three parts, and the each part would have the points
            as follows.
            
            part 1: p0,p1,p2,p3
            part 2: p4,p5,p6,p7,p8
            part 3: p9,p10,p11,p12
            
            The xylist would be:
            xylist = [x0, y0, x1, y1, x2, y2, x3, y3, x4, y4....x12, y12]
            where 
            xylist[0] = x0
            xylist[1] = y0
            xylist[2] = x1
            xylist[3] = y1
            .....
            
            To draw the first part of polyline, we want to get tempXYlist as
        
            tempXYlist = [x0, y0, x1, y1, x2, y2, x3, y3]
            
            At this time, m is in range(0,4)
            
            xylist[m*2] would be is x0(when m=0), x1(when m=1), x2(when m=2), x3(when m=3)
        
            xylist[m*2+1] would be is y0(when m=0), y1(when m=1), y2(when m=2), y3(when m=3)
            """

            for k in range(polygon.partsNum):
                #get the end sequence number of points in the part
                if (k == polygon.partsNum - 1):
                    endPointIndex = len(polygon.points)
                else:
                    endPointIndex = polygon.partsIndex[k + 1]

                #Define a temporary list for holding the part coordinates
                tempXYlist = []
                tempXlist = []
                tempYlist = []
                #take out points' coordinates for the part and add to the temporary list
                for m in range(polygon.partsIndex[k], endPointIndex):
                    tempXYlist.append(xylist[m * 2])
                    tempXYlist.append(xylist[m * 2 + 1])
                    tempXlist.append(xylist[m * 2])
                    tempYlist.append(xylist[m * 2 + 1])

                xMax = max(tempXlist)
                xMin = min(tempXlist)

                yMax = max(tempYlist)
                yMin = min(tempYlist)

                if xMax == xMin:
                    xMin = xMax - 1

                if yMax == yMin:
                    yMin = yMax - 1

                tempVar = False
                #while not tempVar:
                xPoint = rd.randrange(xMin, xMax)
                yPoint = rd.randrange(yMin, yMax)
                tempVar = point_inside_polygon(xPoint, yPoint, tempXYlist)

                startIndex = polygon.partsIndex[
                    k]  #start index for our positive polygon.
                tempPoints = polygon.points[
                    startIndex:
                    endPointIndex]  #we get our temppoints to help use create our polygon using positive data
                newPolygon = Polygon(
                    tempPoints
                )  #here we create our polygons using positve data
                area = newPolygon.getArea()  # Calculate the area

                #Sagar Jha center added to calculate centroid of polygon
                center = newPolygon.getCentroid()
                xCenter = int((center.x - minX) * ratio) + +margin_x / 0.5
                yCenter = int((maxY - center.y) * ratio) + +margin_y / 5

                if area > 0:
                    _polygon = self.mainCanvas.create_polygon(
                        tempXYlist,
                        activefill="blue",
                        fill=polygon.color,
                        outline="blue",
                        tags=self.datalist[tag_count]
                    )  #creating our polygon outline
                    #print k,_polygon
                    #Michigan Special Condition according to its 2 parts
                    if tag_count == 48:
                        if k == 4:
                            _oval = self.mainCanvas.create_oval(xCenter,
                                                                yCenter,
                                                                xCenter + 5,
                                                                yCenter + 5,
                                                                outline="red",
                                                                fill="green",
                                                                width=2,
                                                                tags=center)
                            dict1[_oval] = [center.x, center.y]
                    else:
                        if k == 0:
                            #print "Tag Count: ",tag_count," ",self.mainCanvas.gettags(_polygon)[0]
                            _oval = self.mainCanvas.create_oval(xCenter,
                                                                yCenter,
                                                                xCenter + 5,
                                                                yCenter + 5,
                                                                outline="red",
                                                                fill="green",
                                                                width=2,
                                                                tags=center)
                            dict1[_oval] = [center.x, center.y]
                            #_oval1   = self.mainCanvas.create_oval(xPoint, yPoint,xPoint +5,yPoint+ 5, outline="red",fill="green", width=2)
                else:
                    # If it is a hole, fill with the same color as the canvas background color
                    _polygon = self.mainCanvas.create_polygon(
                        tempXYlist,
                        fill="black",
                        outline="black",
                        tags=self.datalist[tag_count])
                #self.mainCanvas.tag_bind( _polygon, '<ButtonPress-1>', self.__showAttriInfo)
                #self.mainCanvas.tag_bind( _oval, '<ButtonPress-1>', self.__showAttriInfo)
            tag_count += 1

    def __showAttriInfo(self, event):
        """
        Show attribute information of clicked unit
        """
        widget_id = event.widget.find_closest(event.x, event.y)

        if widget_id[0] in dict1.keys():
            print widget_id[0], dict1[widget_id[0]][0], dict1[widget_id[0]][1]
        else:
            print "click!!!!", widget_id
            print self.attributeName + " is: " + self.mainCanvas.gettags(
                widget_id)[0]
Example #14
0
class UI():
    """The UI class manages the buttons, map and tiles.

    The latter two are visual depictions of the world and cells respectively
    """

    def __init__(self, master, app, frame):
        """Create the window."""
        self.master = master
        self.app = app
        self.frame = frame
        self.world = self.app.simulation.world

        self.add_buttons()
        self.add_other_widgets()
        self.add_map()

        log(">> Creating tiles")
        self.create_tiles()
        log(">> Painting tiles")
        self.paint_tiles()

    def add_buttons(self):
        """Add buttons to the frame."""
        ground_button = Button(self.frame, text="TERRA", fg="red",
                               command=self.draw_terrain)
        ground_button.grid(row=1, column=0, sticky=W+E)
        water_button = Button(self.frame, text="WATER", fg="red",
                              command=self.toggle_water)
        water_button.grid(row=1, column=1, sticky=W+E)
        heat_button = Button(self.frame, text="HEAT", fg="red",
                             command=self.draw_heat)
        heat_button.grid(row=1, column=2, sticky=W+E)

    def add_other_widgets(self):
        """Add other widgets to the frame."""
        self.time_rate = StringVar()
        self.rate_label = Label(self.frame, textvariable=self.time_rate)
        self.rate_label.grid(row=0, column=0, sticky=W, columnspan=2)
        self.time_stamp = StringVar()
        self.time_label = Label(self.frame, textvariable=self.time_stamp)
        self.time_label.grid(row=0, column=2, columnspan=8)
        self.update_time_label(0)

    def add_map(self):
        """Add a blank map."""
        self.map = Canvas(self.master,
                          width=settings.map_width + 2*settings.map_border,
                          height=settings.map_height + 2*settings.map_border,
                          bg="black", highlightthickness=0)
        self.map.grid(columnspan=12)

        for c in range(12):
            self.frame.grid_columnconfigure(c, minsize=settings.map_width/12)

    def create_tiles(self):
        """Create blank tiles."""
        self.map.delete("all")
        self.tiles = []
        for cell in self.world.cells:
            n_in_row = len([c for c in self.world.cells
                            if c.latitude == cell.latitude])
            x_start = ((settings.map_width/2.0) -
                       (n_in_row/2.0)*settings.tile_width)
            y_start = ((cell.latitude/settings.cell_degree_width) *
                       settings.tile_height)

            self.tiles.append(self.map.create_rectangle(
                (x_start +
                    (cell.longitude/360.0) * n_in_row * settings.tile_width),
                y_start,
                (x_start + settings.tile_width + 1 +
                    (cell.longitude/360.0) * n_in_row * settings.tile_width),
                y_start + settings.tile_height,
                fill="yellow",
                outline=""))
        for x in range(len(self.tiles)):
            self.map.tag_bind(self.tiles[x], "<ButtonPress-1>",
                              lambda event, arg=x: self.left_click_tile(arg))
            self.map.tag_bind(self.tiles[x], "<ButtonPress-2>",
                              lambda event, arg=x: self.right_click_tile(arg))

    def left_click_tile(self, x):
        """Tell world to raise terrain at cell x."""
        self.world.raise_cell(x, 1000)
        self.paint_tiles()

    def right_click_tile(self, x):
        """Tell world to raise terrain at cell x."""
        self.world.raise_cell(x, -1000)
        self.paint_tiles()

    def paint_tiles(self):
        """Color the tiles."""
        for x in range(len(self.tiles)):
            self.map.itemconfigure(self.tiles[x],
                                   fill=self.cell_color(self.world.cells[x]))

    def update_time_label(self, time):
        """Update the UI time label."""
        year = time / (60*60*24*365)
        time -= year*(60*60*24*365)
        day = time / (60*60*24)
        time -= day * (60*60*24)
        hour = time / (60*60)
        time -= hour*(60*60)
        minute = time / 60
        time -= minute*60
        second = time
        year = format(year, ",d")
        hour = "%02d" % (hour, )
        minute = "%02d" % (minute, )
        second = "%02d" % (second, )
        self.time_stamp.set("{}:{}:{}, day {}, year {}."
                            .format(hour, minute, second, day, year))
        self.time_rate.set("x{}".format(settings.time_step_description))

    def cell_color(self, cell):
        """Work out what color a tile should be.

        The color depends on the cell and the draw_mode parameter.
        """
        if settings.draw_mode == "terrain":
            if cell.water.depth == 0.0 or settings.draw_water is False:
                col_min = [50, 20, 4]
                col_max = [255, 255, 255]
                p = ((cell.land.height - settings.min_ground_height) /
                     (settings.max_ground_height - settings.min_ground_height))
            else:
                col_min = [153, 204, 255]
                col_max = [20, 20, 80]
                p = cell.water.depth/6000.0
                if p > 1:
                    p = 1
        elif settings.draw_mode == "heat":
            if settings.draw_water is True:
                temp = cell.surface_temperature
            else:
                temp = cell.land.temperature
            if temp < 223:
                col_min = [0, 0, 0]
                col_max = [82, 219, 255]
                p = max(min((temp)/223.0, 1), 0)
            elif temp < 273:
                col_min = [82, 219, 255]
                col_max = [255, 255, 255]
                p = max(min((temp-223.0)/50.0, 1), 0)
            elif temp < 313:
                col_min = [255, 255, 255]
                col_max = [255, 66, 0]
                p = max(min((temp-273.0)/40.0, 1), 0)
            else:
                col_min = [255, 66, 0]
                col_max = [0, 0, 0]
                p = max(min((temp-313.0)/100.0, 1), 0)

        elif settings.draw_mode == "wind":
            col_min = [0, 0, 0]
            col_max = [255, 255, 255]
            p = min(cell.wind_speed, 10)/10
        q = 1-p
        col = [int(q*col_min[0] + p*col_max[0]),
               int(q*col_min[1] + p*col_max[1]),
               int(q*col_min[2] + p*col_max[2])]
        return '#%02X%02X%02X' % (col[0], col[1], col[2])

    def draw_terrain(self):
        """Paint map by altitude."""
        settings.draw_mode = "terrain"
        self.paint_tiles()

    def draw_heat(self):
        """Paint map by land temperature."""
        settings.draw_mode = "heat"
        self.paint_tiles()

    def toggle_water(self):
        """Toggle whether water is shown in terrain mode."""
        settings.draw_water = not settings.draw_water
        self.paint_tiles()
Example #15
0
class MainCanvas(object):
    """
    The shapefile displaying device based on TKinter Canvas

    Attributes
    ----------

    shapes           : array
                      The spatial units
    bbox             : array
                      The bounding box: minX, minY, maxX, maxY
    shp_type         : integer
                      The shape types: SHP_TYPE_POINT,SHP_TYPE_LINE,SHP_TYPE_POLYGON
    root             : Tk
                      The Tk Object
    attributeName    : string
                      The attribute name
    datalist         : array
                      The attribute data
                      
    """
    def __init__(self,shapes,bbox,shp_type,root,attributeName,datalist):
        self.shapes = shapes
        self.bbox = bbox
        self.shp_type = shp_type
        self.root = root
        self.attributeName = attributeName
        self.datalist = datalist
        self.__createCanvas()
         
    def __createCanvas(self):
        """
        Create the canvas and draw all the spatial objects
        """ 
        self.canvasRoot = Toplevel()
        self.canvasRoot.title(self.attributeName)
        self.canvasRoot.lower(belowThis = self.root)
        self.mainCanvas = Canvas(self.canvasRoot, bg = 'black', width = canvasWidth+margin_x, height = canvasHeight+margin_y, scrollregion=('-50c','-50c',"50c","50c"))

        #Change by Sagar for Full Screen
        self.canvasRoot.state('zoomed')
        self.canvasRoot.geometry=("1000x900+0+0")
        #Change End
        
        self.__drawShape()
        self.mainCanvas.pack()
        
    def __drawShape(self):
        """
        Draw all the spatial objects on the canvas
        """
        minX, minY, maxX, maxY = self.bbox[0],self.bbox[1],self.bbox[2],self.bbox[3]
        # calculate ratios of visualization
        ratiox = canvasWidth/(maxX-minX)
        ratioy = canvasHeight/(maxY-minY)
        # take the smaller ratio of window size to geographic distance
        ratio = ratiox
        if ratio>ratioy:
            ratio = ratioy
        
        if self.shp_type == SHP_TYPE_POINT:
            self.__drawPoints(minX, minY, maxX, maxY, ratio)
        elif self.shp_type == SHP_TYPE_LINE:
            self.__drawPolylines(minX, minY, maxX, maxY, ratio)
        elif self.shp_type == SHP_TYPE_POLYGON:
            self.__drawPolygons(minX, minY, maxX, maxY, ratio)
      
    def __drawPoints(self,minX, minY, maxX, maxY,ratio):
        """
        Draw points on the canvas
        """  
        tag_count = 0
        # loop through each point
        for point in self.shapes:
            #define an empty xylist for holding converted coordinates
            x = int((point.x-minX)*ratio)+margin_x/2
            y = int((maxY-point.y)*ratio)+margin_y/2
            _point = self.mainCanvas.create_oval(x-2, y-2, x+2, y+2,outline=point.color,  
                               fill=point.color, width=2, tags = self.datalist[tag_count])
            self.mainCanvas.tag_bind( _point, '<ButtonPress-1>', self.__showAttriInfo)
            tag_count += 1
        
    def __drawPolylines(self,minX, minY, maxX, maxY,ratio):
        """
        Draw polylines on the canvas
        """     
        tag_count = 0
        # loop through each polyline
        for polyline in self.shapes:
            #define an empty xylist for holding converted coordinates
            xylist = []
            # loops through each point and calculate the window coordinates, put in xylist
            for j in range(len(polyline.x)):
                pointx = int((polyline.x[j]-minX)*ratio)+margin_x/2
                pointy = int((maxY-polyline.y[j])*ratio)+margin_y/2
                xylist.append(pointx)
                xylist.append(pointy)
            # loop through each part of the polyline
            for k in range(polyline.partsNum):
                #get the end sequence number of points in the part
                if (k==polyline.partsNum-1):
                    endPointIndex = len(polyline.x)
                else:
                    endPointIndex = polyline.partsIndex[k+1]
                # define a temporary list for holding the part coordinates
                tempXYlist = []
                #take out points' coordinates for the part and add to the temporary list
                for m in range(polyline.partsIndex[k], endPointIndex):
                    tempXYlist.append(xylist[m*2])
                    tempXYlist.append(xylist[m*2+1])
                # create the line
                _line = self.mainCanvas.create_line(tempXYlist,fill=polyline.color, tags = self.datalist[tag_count])
                self.mainCanvas.tag_bind( _line, '<ButtonPress-1>', self.__showAttriInfo)            
            tag_count += 1
  
    def __drawPolygons(self,minX, minY, maxX, maxY,ratio):
        """
        Draw polygons on the canvas
        """      
        tag_count = 0
        for polygon in self.shapes:
            #define an empty xylist for holding converted coordinates
            xylist = []
            # loops through each point and calculate the window coordinates, put in xylist
            for point in polygon.points:
                pointx = int((point.x -minX)*ratio) + +margin_x/0.5
                pointy = int((maxY- point.y)*ratio) + +margin_y/5
                xylist.append(pointx)
                xylist.append(pointy)
##            print xylist
            """
            polyline.partsIndex is a tuple data type holding the starting points for each
            part. For example, if the polyline.partsIndex of a polyline equals to (0, 4, 9),
            and the total points, which is calcuate by len(polyline.points) equals to 13.
            This means that the polyline has three parts, and the each part would have the points
            as follows.
            
            part 1: p0,p1,p2,p3
            part 2: p4,p5,p6,p7,p8
            part 3: p9,p10,p11,p12
            
            The xylist would be:
            xylist = [x0, y0, x1, y1, x2, y2, x3, y3, x4, y4....x12, y12]
            where 
            xylist[0] = x0
            xylist[1] = y0
            xylist[2] = x1
            xylist[3] = y1
            .....
            
            To draw the first part of polyline, we want to get tempXYlist as
        
            tempXYlist = [x0, y0, x1, y1, x2, y2, x3, y3]
            
            At this time, m is in range(0,4)
            
            xylist[m*2] would be is x0(when m=0), x1(when m=1), x2(when m=2), x3(when m=3)
        
            xylist[m*2+1] would be is y0(when m=0), y1(when m=1), y2(when m=2), y3(when m=3)
            """
            
            for k in range(polygon.partsNum):
                #get the end sequence number of points in the part
                if (k==polygon.partsNum-1):
                    endPointIndex = len(polygon.points)
                else:
                    endPointIndex = polygon.partsIndex[k+1]
         
                #Define a temporary list for holding the part coordinates
                tempXYlist = []
                tempXlist  = []
                tempYlist  = []
                #take out points' coordinates for the part and add to the temporary list
                for m in range(polygon.partsIndex[k], endPointIndex):            
                    tempXYlist.append(xylist[m*2])
                    tempXYlist.append(xylist[m*2+1])
                    tempXlist.append (xylist[m*2])
                    tempYlist.append (xylist[m*2+1])

                xMax = max(tempXlist)
                xMin = min(tempXlist)

                yMax = max(tempYlist)
                yMin = min(tempYlist)

                if xMax == xMin:
                    xMin = xMax - 1

                if yMax == yMin:
                    yMin = yMax - 1

                tempVar = False
                #while not tempVar:
                xPoint = rd.randrange(xMin,xMax)
                yPoint = rd.randrange(yMin,yMax)
                tempVar =  point_inside_polygon(xPoint,yPoint,tempXYlist)
                
                
                startIndex = polygon.partsIndex[k] #start index for our positive polygon.                
                tempPoints = polygon.points[startIndex: endPointIndex]#we get our temppoints to help use create our polygon using positive data
                newPolygon = Polygon(tempPoints) #here we create our polygons using positve data
                area = newPolygon.getArea() # Calculate the area
                
                #Sagar Jha center added to calculate centroid of polygon
                center = newPolygon.getCentroid()
                xCenter = int((center.x -minX)*ratio) + +margin_x/0.5
                yCenter = int((maxY- center.y)*ratio) + +margin_y/5
                
                if area > 0:
                    _polygon = self.mainCanvas.create_polygon(tempXYlist,activefill="blue",fill=polygon.color,outline="blue",tags = self.datalist[tag_count])#creating our polygon outline
                    #print k,_polygon
                    #Michigan Special Condition according to its 2 parts
                    if tag_count == 48:
                        if k==4:
                            _oval    = self.mainCanvas.create_oval(xCenter, yCenter,xCenter +5,yCenter+ 5, outline="red",fill="green", width=2,tags = center)
                            dict1[_oval]=[center.x,center.y]
                    else:
                        if k==0:
                            #print "Tag Count: ",tag_count," ",self.mainCanvas.gettags(_polygon)[0]
                            _oval    = self.mainCanvas.create_oval(xCenter, yCenter,xCenter +5,yCenter+ 5, outline="red",fill="green", width=2,tags = center)
                            dict1[_oval]=[center.x,center.y]
                            #_oval1   = self.mainCanvas.create_oval(xPoint, yPoint,xPoint +5,yPoint+ 5, outline="red",fill="green", width=2)
                else:
                    # If it is a hole, fill with the same color as the canvas background color 
                    _polygon = self.mainCanvas.create_polygon(tempXYlist,fill="black",outline="black", tags = self.datalist[tag_count])
                #self.mainCanvas.tag_bind( _polygon, '<ButtonPress-1>', self.__showAttriInfo)
                #self.mainCanvas.tag_bind( _oval, '<ButtonPress-1>', self.__showAttriInfo)
            tag_count += 1
            
    def __showAttriInfo(self,event):
        """
        Show attribute information of clicked unit
        """        
        widget_id=event.widget.find_closest(event.x, event.y)
        
        if widget_id[0] in dict1.keys():
            print widget_id[0], dict1[widget_id[0]][0],dict1[widget_id[0]][1]
        else:
            print "click!!!!", widget_id
            print self.attributeName+" is: "+self.mainCanvas.gettags(widget_id)[0]
class SimulationCanvas(object):
    """A canvas where blocks can be dragged around and connected up"""

    size = (width, height) = (550, 300)

    def __init__(self, frame):
        # Create the canvas
        self.canvas = Canvas(frame,
                             width=self.width,
                             height=self.height,
                             relief=RIDGE,
                             background=colours["background"],
                             borderwidth=1)
        # Add event handlers for dragable items
        self.canvas.tag_bind("DRAG", "<ButtonPress-1>", self.mouse_down)
        #self.canvas.tag_bind ("DRAG", "<ButtonRelease-1>", self.mouse_release)
        self.canvas.tag_bind("DRAG", "<Enter>", self.enter)
        self.canvas.tag_bind("DRAG", "<Leave>", self.leave)
        self.canvas.pack(side=TOP)

        # Some default locations
        self.PREVIEW_WIDTH = 80
        self.PREVIEW_LOCATION = (self.PREVIEW_X, self.PREVIEW_Y) = (15, 30)

        # Draw a "preview" area
        self.canvas.create_line(self.PREVIEW_WIDTH,
                                0,
                                self.PREVIEW_WIDTH,
                                self.height,
                                dash=True)

        # A dict indexed by unique ID of elements in the canvas.
        self.blocks = {}

    def preview_actor(self, codefile):
        """
        Display a preview of an actor or compisite actor in the canvas,
        it will be dragable into desired position
        """
        logging.debug("Creating a preview of %(name)s on simulation canvas." %
                      {'name': codefile.name})

        logging.debug("Deleting any existing items still tagged 'preview'")
        self.canvas.delete("preview")

        block = CanvasBlock(self.canvas, codefile, *self.PREVIEW_LOCATION)
        self.blocks[block.id] = block

    def mouse_down(self, event):
        logging.debug("The mouse was pressed at (%d, %d)" % (event.x, event.y))
        logging.debug(
            "The mouse went down on a block. Binding mouse release...")
        selected = self.canvas.gettags("current")
        logging.debug("Currently selected items tags are %s" %
                      selected.__repr__())
        self.selected_name = [
            tag for tag in selected if tag.startswith("name:")
        ][0][5:]
        self.selected_id = [tag for tag in selected
                            if tag.startswith("id:")][0][3:]
        self.selected_type = [
            tag for tag in selected if tag.startswith("type:")
        ][0][5:]
        logging.debug("Block selected was %s with id:%s" %
                      (self.selected_name, self.selected_id))

        #self.canvas.addtag( 'Selected', 'withtag', self.selected_id )
        logging.debug("Current blocks are: %s" % self.blocks)
        #self.blocks[block_id].set_colour( colours['selected'] )

        if self.selected_type == "block" or self.selected_type == "text":
            self.blocks[self.selected_id].select(event.x, event.y)
            self.canvas.bind("<ButtonRelease-1>",
                             self.block_move_mouse_release)
        elif self.selected_type.startswith(
                "input") or self.selected_type.startswith("output"):
            self.blocks[self.selected_id].select_port(self.selected_type)
            self.canvas.bind("<ButtonRelease-1>",
                             self.port_connect_mouse_release)
        else:
            logging.info("Tried to select %s" % self.selected_type)

    def block_move_mouse_release(self, event):
        logging.debug("The mouse was released at (%d, %d)" %
                      (event.x, event.y))
        self.canvas.bind("<ButtonRelease-1>", lambda e: None)
        if event.x >= 0 and event.x <= self.canvas.winfo_width() \
            and event.y >= 0 and event.y <= self.canvas.winfo_height():
            logging.debug("Valid move inside canvas. Relocating block.")
            self.blocks[self.selected_id].move_to(event.x, event.y)
            if event.x >= self.PREVIEW_WIDTH:
                if self.blocks[self.selected_id].is_preview():
                    logging.info(
                        "Moved out of preview zone, adding new component to model"
                    )
                    #TODO HERE - add to model compiler or what ever...
                self.blocks[self.selected_id].unselect()
            else:
                self.blocks[self.selected_id].preview()
        else:
            logging.info("Invalid move.")

    def port_connect_mouse_release(self, event):
        logging.debug("The mouse was released at (%d, %d)" %
                      (event.x, event.y))
        self.canvas.bind("<ButtonRelease-1>", lambda e: None)
        if event.x >= 0 and event.x <= self.canvas.winfo_width() \
            and event.y >= 0 and event.y <= self.canvas.winfo_height():
            logging.debug("Valid location inside canvas.")

            event.widget.itemconfigure("Selected", fill="#000000")
            event.widget.itemconfigure("type:text", fill="#000000")

            #block = self.canvas.gettags("Selected")
            #logging.debug("Block moved was made up of these components: %s" % block.__repr__())
            self.canvas.dtag("Selected", "Selected")

        else:
            logging.info("Invalid wiring.")

    def enter(self, event):
        logging.debug("Enter")

    def leave(self, event):
        logging.debug("Leaving")