Beispiel #1
0
    def __init__(self, parent, id=-1, size=wx.DefaultSize):
        wx.ScrolledWindow.__init__(self, parent, id, (0, 0), size=size, style=wx.SUNKEN_BORDER)

        self.selected_item = None
        self.clicked_item = None
        self.lines = []
        self.flow_items = []
        self.maxWidth = 800
        self.maxHeight = 600
        self.x = self.y = 0
        self.curLine = []
        self.click_offset = None
        self.select_mode = False  # True means we are selecting an item to link to

        self.SetVirtualSize((self.maxWidth, self.maxHeight))
        #self.SetScrollRate(20, 20)

        # Initialize the buffer bitmap.  No real DC is needed at this point.
        self.buffer = wx.EmptyBitmap(self.maxWidth, self.maxHeight)
        self.UpdateDrawing()

        self.capturing_mouse = False
        self.edit_frame = RichTextFrame(self)

        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_RIGHT_UP, self.OnRightButtonEvent)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_MOTION, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.Bind(EVT_UPD_FLOW_ITEM, self.OnUpdateFlowItem)
        self.Bind(EVT_ADD_FLOW_ITEM, self.OnAddFlowItem)
        self.Bind(EVT_SELECT_FLOW_ITEM, self.OnSelectFlowItem)
        self.Bind(EVT_SWITCH_FLOW_ITEM, self.OnSwitchFlowItem)
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        if hasattr(self, 'StopAutoScrolling'):
            print "stopping auto scroll"
            self.StopAutoScrolling()
Beispiel #2
0
class MyCanvas(wx.ScrolledWindow):
    def __init__(self, parent, id=-1, size=wx.DefaultSize):
        wx.ScrolledWindow.__init__(self, parent, id, (0, 0), size=size, style=wx.SUNKEN_BORDER)

        self.selected_item = None
        self.clicked_item = None
        self.lines = []
        self.flow_items = []
        self.maxWidth = 800
        self.maxHeight = 600
        self.x = self.y = 0
        self.curLine = []
        self.click_offset = None
        self.select_mode = False  # True means we are selecting an item to link to

        self.SetVirtualSize((self.maxWidth, self.maxHeight))
        #self.SetScrollRate(20, 20)

        # Initialize the buffer bitmap.  No real DC is needed at this point.
        self.buffer = wx.EmptyBitmap(self.maxWidth, self.maxHeight)
        self.UpdateDrawing()

        self.capturing_mouse = False
        self.edit_frame = RichTextFrame(self)

        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_RIGHT_UP, self.OnRightButtonEvent)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_MOTION, self.OnLeftButtonEvent)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.Bind(EVT_UPD_FLOW_ITEM, self.OnUpdateFlowItem)
        self.Bind(EVT_ADD_FLOW_ITEM, self.OnAddFlowItem)
        self.Bind(EVT_SELECT_FLOW_ITEM, self.OnSelectFlowItem)
        self.Bind(EVT_SWITCH_FLOW_ITEM, self.OnSwitchFlowItem)
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
        if hasattr(self, 'StopAutoScrolling'):
            print "stopping auto scroll"
            self.StopAutoScrolling()


    def onKeyDown(self, event):
        if event.GetKeyCode() == wx.WXK_DELETE and self.selected_item:
            self.flow_items.remove(self.selected_item)
            for item in self.flow_items:
                if self.selected_item in item.linked_items:
                    item.linked_items.remove(self.selected_item)
            self.selected_item = None

        if event.GetKeyCode() == wx.WXK_SPACE and self.selected_item:
            self.selected_item.linked_items = []
            for item in self.flow_items:
                if self.selected_item in item.linked_items:
                    item.linked_items.remove(self.selected_item)

        self.UpdateDrawing()
        event.Skip()

    def getWidth(self):
        return self.maxWidth

    def getHeight(self):
        return self.maxHeight

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
        # the bitmap is copied to the screen when the object goes out of scope
        del dc

    def Draw(self, dc):
        dc.SetBackground(wx.Brush("LIGHT GREY"))
        dc.Clear()
        dc.BeginDrawing()
        # Arrows over items, items over lines
        self.DrawFlowLinkLines(dc)
        self.DrawFlowItems(dc)
        self.DrawFlowLinkArrows(dc)
        dc.EndDrawing()

    def UpdateDrawing(self):
        dc = wx.BufferedDC(None, self.buffer)
        self.Draw(dc)
        self.Refresh(eraseBackground=False)
        self.Update()

    def determine_link_points(self, source_item, target_item):
        """
        :param source_item: Item linking from
        :param targe_item: Item linking from
        :return: (start pos, end Pos)
        """
        delta_x = target_item.center.x - source_item.center.x
        delta_y = target_item.center.y - source_item.center.y
        source_offset = wx.Point(0, 0)
        target_offset = wx.Point(0, 0)
        if abs(delta_x) >= abs(delta_y):
            source_offset.x = source_item.size.x if delta_x > 0 else 0
            source_offset.y = source_item.size.y / 2
            target_offset.x = 0 if delta_x > 0 else source_item.size.x
            target_offset.y = source_item.size.y / 2
        elif abs(delta_y) >= abs(delta_x):
            source_offset.x = source_item.size.x / 2
            source_offset.y = source_item.size.y if delta_y > 0 else 0
            target_offset.x = source_item.size.x / 2
            target_offset.y = 0 if delta_y > 0 else source_item.size.y

        return source_item.pos + source_offset, target_item.pos + target_offset

    def DrawItem(self, item, dc):
        #if item == self.selected_item:
        #    dc.SetPen(wx.Pen("BLACK", 2))
        dc.SetPen(wx.Pen("WHITE", 0))
        if item == self.selected_item:
            dc.SetBrush(wx.Brush(wx.Colour(204, 255, 229)))
        else:
            dc.SetBrush(wx.Brush("WHITE"))
        dc.DrawRectangle(item.pos.x, item.pos.y, item.size.x, item.size.y)
        dc.SetPen(wx.Pen("WHITE", 0))

        # Draw the label box
        dc.SetBrush(wx.Brush(wx.Colour(49, 58, 117)))
        dc.DrawRectangle(item.pos.x, item.pos.y, item.size.x, 20)

        # Print the title
        font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.BOLD)
        dc.SetFont(font)
        dc.SetTextForeground(wx.WHITE)
        dc.DrawText(item.title, item.pos.x + 4, item.pos.y + 2)

        # Draw the context text
        font = wx.Font(8, wx.MODERN, wx.NORMAL, wx.BOLD)
        dc.SetFont(font)
        dc.SetTextForeground(wx.BLUE)
        if item.text_content:
            y = 20
            for line in item.text_content.split('\n'):
                line = line.strip(' ')
                if not line:
                    continue
                text = line[:16]
                if len(line) > 18:
                    text = text[:13] + "..."
                dc.DrawText(text, item.pos.x + 4, item.pos.y + y)
                y += 10



    def DrawFlowItems(self, dc):
        for item in self.flow_items:
            self.DrawItem(item, dc)

    def DrawFlowLinkLines(self, dc):

        dc.SetPen(wx.Pen('MEDIUM FOREST GREEN', 2))
        dc.SetBrush(wx.Brush(wx.NamedColour('MEDIUM FOREST GREEN'), wx.SOLID))

        for item in self.flow_items:
            # Draw the link lines
            for linked_item in item.linked_items:
                source_pos, target_pos = self.determine_link_points(item, linked_item)
                dc.DrawLine(source_pos.x, source_pos.y, target_pos.x, target_pos.y)


    def DrawFlowLinkArrows(self, dc):
        dc.SetPen(wx.Pen('MEDIUM FOREST GREEN', 1))
        dc.SetBrush(wx.Brush(wx.NamedColour('GREEN'), wx.SOLID))

        for item in self.flow_items:
            for linked_item in item.linked_items:
                source_pos, target_pos = self.determine_link_points(item, linked_item)

                # Draw the arrow
                ARROW_SIZE = 12
                ARROW_IN_SIZE = 6
                angle = atan2(target_pos.y - source_pos.y, target_pos.x - source_pos.x)
                pol_points = [
                    (target_pos.x - ARROW_IN_SIZE * cos(angle), target_pos.y - ARROW_IN_SIZE * sin(angle)),
                    (target_pos.x - ARROW_SIZE * cos(angle - pi / 6), target_pos.y - ARROW_SIZE * sin(angle - pi / 4)),
                    (target_pos.x, target_pos.y),
                    (target_pos.x - ARROW_SIZE * cos(angle + pi / 6), target_pos.y - ARROW_SIZE * sin(angle + pi / 4)),
                    ]
                dc.DrawPolygon(pol_points)

    def SetXY(self, event):
        self.x, self.y = self.ConvertEventCoords(event)

    def ConvertEventCoords(self, event):
        x, y = self.CalcUnscrolledPosition(event.GetX(), event.GetY())
        return wx.Point(x, y)


    def OnLeftButtonEvent(self, event):

        event_pos = wx.Point(event.GetX(), event.GetY())

        # Not available on wxPython2.8
        # if self.IsAutoScrolling():
        #    self.StopAutoScrolling()

        if event.LeftDown():
            self.SetFocus()
            self.clicked_item = clicked_item = self.item_at_pos(self.ConvertEventCoords(event))
            if self.select_mode:
                self.select_mode = False
                if self.clicked_item:
                    self.edit_frame.create_link_to(self.clicked_item)
                    if self.clicked_item not in self.selected_item.linked_items:
                        self.selected_item.linked_items.append(self.clicked_item)
                self.edit_frame.Show()
                return event.Skip()

            if clicked_item:
                self.selected_item = clicked_item
                self.edit_frame.update(self.selected_item.title, self.selected_item.content)
                self.capturing_mouse = True
                #self.CaptureMouse()

                self.selected_item = clicked_item
                self.UpdateDrawing()  # Need to update the selected item color
                self.click_offset = wx.Point(event_pos.x - clicked_item.pos.x, event_pos.y - clicked_item.pos.y)

        elif event.Dragging() and self.clicked_item:

            new_pos = event_pos - self.click_offset

            # Limit movement to the canvas boundaries
            new_pos.x = max(new_pos.x, 0)
            new_pos.y = max(new_pos.y, 0)
            new_pos.x = min(new_pos.x, self.maxWidth - self.clicked_item.size.x - 1)
            new_pos.y = min(new_pos.y, self.maxHeight - self.clicked_item.size.y - 1)
            self.clicked_item.pos = new_pos

            self.UpdateDrawing()

        elif event.LeftUp():
            if self.capturing_mouse:
                #self.ReleaseMouse()
                self.capturing_mouse = False
            self.clicked_item = None

    def OnRightButtonEvent(self, event):
        clicked_item = self.item_at_pos(self.ConvertEventCoords(event))
        if self.selected_item and clicked_item and clicked_item != self.selected_item:
            if clicked_item not in self.selected_item.linked_items:
                self.selected_item.linked_items.append(clicked_item)
            self.UpdateDrawing()
        event.Skip()

    def get_next_item_position(self):
        """
        Free position to place the next item
        :return:
        """

        lookup_locations = [wx.Point(300, 150)]
        selected_item = self.selected_item
        if selected_item:
            radius = wx.Point(selected_item.size.x, selected_item.size.y) + wx.Point(30, 30)
            potential_pos = ((radius.x, 0), (0, radius.y), (-radius.x, 0), (0, -radius.y))
            for x, y in potential_pos:
                lookup_locations.append(wx.Point(selected_item.pos.x + x, selected_item.pos.y + y))

        for pos in lookup_locations:
            if self.item_at_pos(pos) is None:
                return pos

    def item_at_pos(self, check_pos):
        for item in self.flow_items:
            x_delta = check_pos.x - item.pos.x
            y_delta = check_pos.y - item.pos.y
            if 0 <= x_delta < item.size.x and 0 <= y_delta < item.size.y:
                return item

    def add_flow_item(self):
        #self.edit_frame.Show()
        pos = self.get_next_item_position()
        size = wx.Size(120, 110)
        item = FlowItem(pos, size)
        self.flow_items.append(item)
        #if self.selected_item:  # Link from selected item
        #    self.selected_item.linked_items.append(item)
        self.selected_item = item
        self.UpdateDrawing()
        self.x, self.y = pos
        return item

    def OnDoubleClick(self, event):
        if self.selected_item:
            self.edit_frame.update(self.selected_item.title, self.selected_item.content)
            self.edit_frame.Show()

    def OnAddFlowItem(self, event):
        self.add_flow_item(event.title, event.content)

    def OnUpdateFlowItem(self, event):
        if self.selected_item:
            self.selected_item.title = event.title
            self.selected_item.content = event.content
            self.selected_item.text_content = rtc2txt(event.content)
        self.UpdateDrawing()
        self.Refresh()

    def OnSelectFlowItem(self, event):
        self.select_mode = True

    def OnSwitchFlowItem(self, event):
        switch_to_item_id = event.item_id
        for item in self.flow_items:
            print switch_to_item_id, item.id
            if switch_to_item_id == item.id:
                print "switching to", self.selected_item
                self.selected_item = item
                self.UpdateDrawing()
                self.OnDoubleClick(event)