Skip to content

Commit

Permalink
Edges can be dragged from cards with basic persistence
Browse files Browse the repository at this point in the history
for some reason, they don't follow changing card ids.
  • Loading branch information
Andrew Fleenor committed Apr 17, 2012
1 parent 5b3bc1e commit 15252a9
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 25 deletions.
4 changes: 2 additions & 2 deletions gpfile.py
Expand Up @@ -52,8 +52,8 @@ def __init__(self, filename):
try:
self.graph = model.Graph(datastore, head_ptr)
# after this, should be all loaded
except model.Error:
print 'failed to open gp file'
except model.Error as e:
print 'failed to open gp file:', e
raise ValueError

def load_default_config(self):
Expand Down
22 changes: 19 additions & 3 deletions graphpaper.py
Expand Up @@ -46,11 +46,27 @@ def __init__(self, master, gpfile):
self.canvas.bind("<B1-Motion>", self.mousemove)
self.canvas.bind("<Configure>", self.resize)
# load cards
self.cards = [ViewportCard(self, self.gpfile, card) for card in self.data.get_cards()]
cards_by_id = {}
self.cards = []
for card in self.data.get_cards():
new = ViewportCard(self, self.gpfile, card)
self.cards.append(new)
cards_by_id[card.obj.oid] = new
self.reset_scroll_region()
# load edges
self.edges = []
for edge in self.data.get_edges():
new = ViewportEdge(
self,
self.gpfile,
edge,
cards_by_id[edge.orig.obj.oid],
cards_by_id[edge.dest.obj.oid]
)
self.edges.append(new)
# test edges
edge = model.Edge(self.data, orig=self.data.get_cards()[0], dest=self.data.get_cards()[1])
self.edge = ViewportEdge(self, self.gpfile, edge, self.cards[0], self.cards[1])
# edge = model.Edge(self.data, orig=self.data.get_cards()[0], dest=self.data.get_cards()[1])
# self.edge = ViewportEdge(self, self.gpfile, edge, self.cards[0], self.cards[1])
# set up scrolling
self.yscroll["command"] = self.canvas.yview
self.xscroll["command"] = self.canvas.xview
Expand Down
4 changes: 4 additions & 0 deletions model.py
Expand Up @@ -65,6 +65,10 @@ def get_cards(self):
'''
return self.cards

def get_edges(self):
"as get_cards()"
return self.edges

def new_card(self, x=0, y=0, w=MIN_CARD_SIZE, h=MIN_CARD_SIZE):
c = Card(self, None)
c.x = x
Expand Down
45 changes: 42 additions & 3 deletions viewportcard.py
Expand Up @@ -5,11 +5,27 @@
from tkex import ResizableCanvasFrame
from slot import Slot

from viewportedge import ViewportEdge

class ViewportCard(object):
'''
Manages the graphical representation of a card in a
Tkinter canvas. Creates and destroys items as necessary, facilitates
editing, and so on and so forth.
Members:
* card: model.Card
* viewport: GPViewport
* gpfile: gpfile.GraphPaperFile, contains model.graph()
* canvas: TKinter canvas we get drawn on
* editing: bool, text is being edited
* moving: bool, being dragged
* moving_edgescroll_id: callback id to scroll periodicall when hovering
near edge of screen
* resize_state: {}
* resize_edgescroll_id: as moving_edgescroll_id
* slot: calls callbacks whenever geometry changes
* new_edge: if an edge is being dragged out from a handle, this is it.
'''
def __init__(self, viewport, gpfile, card):
self.card = card
Expand All @@ -25,6 +41,7 @@ def __init__(self, viewport, gpfile, card):
# slot triggered when geometry (pos/size) changes
# fn args: (self, x, y, w, h)
self.slot = Slot()
self.new_edge = None

def draw(self):
self.frame_thickness = 5
Expand Down Expand Up @@ -78,9 +95,9 @@ def move(event):
def dblclick(event):
print 'item double-click'
return 'break'
self.canvas.tag_bind(new, '<Button-1>', foo)
self.canvas.tag_bind(new, '<B1-Motion>', move)
self.canvas.tag_bind(new, '<Double-Button-1>', dblclick)
self.canvas.tag_bind(new, '<Button-1>', self.handle_click)
self.canvas.tag_bind(new, '<B1-Motion>', self.handle_mousemove)
self.canvas.tag_bind(new, '<ButtonRelease-1>', self.handle_mouseup)
return new
x, y = self.window.canvas_coords()
w, h = self.window.winfo_width(), self.window.winfo_height()
Expand Down Expand Up @@ -182,6 +199,28 @@ def mouseup(self, event):
self.cancel_moving_edgescroll_callback()
self.geometry_callback()

# next several functions are bound to the circular edge handles
def handle_click(self, event):
# create new edge
self.new_edge = ViewportEdge(
self.viewport,
self.gpfile,
None,
self,
None
)
self.new_edge.mousemove(event) # give it a real start pos

def handle_mousemove(self, event):
if self.new_edge:
self.new_edge.mousemove(event)

def handle_mouseup(self, event):
if self.new_edge:
self.new_edge.mouseup(event)
self.new_edge = None


def configure(self, event):
print 'configure'
self.redraw_edge_handles()
Expand Down
69 changes: 52 additions & 17 deletions viewportedge.py
Expand Up @@ -3,6 +3,7 @@
'''

from math import sqrt
import model

class ViewportEdge(object):
'''
Expand All @@ -26,20 +27,49 @@ class ViewportEdge(object):
'''
def __init__(self, viewport, gpfile, edge, orig, dest):
'''
Either load an edge from the datastore, or start creating
a new one. If edge is None, we're creating a new edge and
one of orig or dest should be None. Otherwise, edge should be a
model.Edge and orig and dest should both be ViewportCards
corresponding to the cards in edge.
Arguments:
* viewport: GPViewport this edge lives in
* gpfile: GPFile, needed for committing
* edge: model.Edge that we will be managing.
* edge: model.Edge that we will be managing, or None if creating a new edge.
* orig: ViewportCard or None, if dragging a new edge
* dest: as above, but more likely.
'''
# store all the arguments
self.edge = edge
self.viewport = viewport
self.canvas = viewport.canvas
self.gpfile = gpfile
# store nodes
self.orig_callback = self.dest_callback = None # callback attrs needed before setting nodes
self.orig = orig
self.dest = dest
# we basically need to decide whether to start off dragging
if edge:
# member vars are all good, theoretically.
# just need to self self.coords
self.reset_coords()
# not dragging.
self.dragging_end = None # or 0 or 1
else:
# we start off dragging
if self.orig:
self.dragging_end = 1
nondrag = self.orig
elif self.dest:
self.dragging_end = 0
nondrag = self.dest
# use fake initial pos
initpos = nondrag.canvas_coords()
self.coords = [initpos, (initpos[0] + 10, initpos[1] + 10)]
self._highlighted_card = None
# draw self
self.reset_coords()
self.itemid = self.canvas.create_line(
#0, 0, 100, 0, 100, 100, 200, 100,
# have to unpack self.get_coords as first args, not last
*(self.get_coords()),
arrow='last',
Expand All @@ -51,25 +81,14 @@ def __init__(self, viewport, gpfile, edge, orig, dest):
self.canvas.tag_bind(self.itemid, "<Button-1>", self.click)
self.canvas.tag_bind(self.itemid, "<B1-Motion>", self.mousemove)
self.canvas.tag_bind(self.itemid, "<ButtonRelease-1>", self.mouseup)
# set up state
self.orig_callback = self.dest_callback = None
self.orig = orig
self.dest = dest
# drag state
self.dragging_end = None # or 0 or 1
self._highlighted_card = None

def refresh(self):
self.canvas.coords(self.itemid, *self.get_coords())

def reset_coords(self):
'''
Set self.coords based on current cards. Only call when orig and
dest are valid.
Calculates from the position of self.edge.orig/dest,
and at some point from the mouse position I guess.
Straight line between the centers of orig and dest.
dest are valid. Straight line between the centers of orig and dest.
'''
# watch out for loss of sync between viewport cards and model card
# also, this will have to be rewritten at some point so any
Expand Down Expand Up @@ -163,11 +182,18 @@ def mouseup(self, event):
self.orig = card
else:
self.dest = card
# create edge if needed (if this is first time edge is finished)
if self.edge is None:
self.edge = self.gpfile.graph.new_edge(
orig = self.orig.card,
dest = self.dest.card
)
# update graphics
self.reset_coords()
self.refresh()
self.dragging_end = None
self.highlighted_card = None
self.gpfile.commit()

def get_highlighted_card(self):
return self._highlighted_card
Expand All @@ -189,7 +215,8 @@ def set_orig(self, orig):
self._orig = orig
if orig:
self.orig_callback = orig.add_signal(self.geometry_callback)
self.edge.orig = orig.card
if self.edge:
self.edge.orig = orig.card
orig = property(get_orig, set_orig)

def get_dest(self):
Expand All @@ -200,9 +227,17 @@ def set_dest(self, dest):
self._dest = dest
if dest:
self.dest_callback = dest.add_signal(self.geometry_callback)
self.edge.dest = dest.card
if self.edge:
self.edge.dest = dest.card
dest = property(get_dest, set_dest)

@property
def non_dragging_end(self):
if self.dragging_end is not None:
# assume correct value of 0 or 1
return int(not self.dragging_end)
return None

def adjust_point(p1, box, p2):
'''
Moves p1 along the line p1<->p2 to be on an edge of box
Expand Down

0 comments on commit 15252a9

Please sign in to comment.