Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit 009bbc4

Browse files
committed
Release v0.5
2 parents b586768 + 51f0075 commit 009bbc4

File tree

6 files changed

+117
-15
lines changed

6 files changed

+117
-15
lines changed

.gitignore

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
# VS Code dir
2-
.vscode
2+
.vscode/
33

44
# Mac OS files
55
.DS_Store
66

77
# Python Cache
8-
__pycache__
8+
__pycache__/
99

1010
# Pycharm temp dir
11-
.idea
11+
.idea/
12+
13+
# Build Directory
14+
dist/
15+
build/
16+
*.egg-info/

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[![Python Version](https://img.shields.io/badge/Python-3.6-orange.svg)](https://www.python.org/downloads/release/python-360/) [![GitHub release](https://img.shields.io/github/release/sco1/dragpy.svg)](https://github.com/sco1/dragpy/releases)
2+
# dragpy

__init__.py

Whitespace-only changes.

dragpy/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Provides a set of draggable matplotlib graphics objects"""
2+
3+
__version__ = '0.4'
4+
from .dragpy import *

dragpy.py renamed to dragpy/dragpy.py

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
class _DragObj:
66
def __init__(self, ax):
7+
"""Generic draggable object initialization
8+
9+
Draggable object is tagged as 'dragobj' using matplotlib's graphics
10+
objects' url property
11+
"""
712
self.parentcanvas = ax.figure.canvas
813
self.parentax = ax
914

@@ -12,10 +17,11 @@ def __init__(self, ax):
1217
self.clicked = False
1318

1419
def on_click(self, event):
20+
"""Wrapper for on_click and motion callback methods"""
1521
# Executed on mouse click
1622
if event.inaxes != self.parentax: return # See if the mouse is over the parent axes object
1723

18-
# Check for overlaps, make sure we only fire for one object per click
24+
# Check for object overlap
1925
timetomove = self.shouldthismove(event)
2026
if not timetomove: return
2127

@@ -29,6 +35,11 @@ def on_click(self, event):
2935
self.clicked = True
3036

3137
def shouldthismove(self, event):
38+
"""Determine whether the event firing object is the topmost rendered
39+
40+
Mitigates issues when the on_click callback fires for multiple
41+
overlapping objects at the same time, causing both to move
42+
"""
3243
# Check to see if this object has been clicked on
3344
contains, attrs = self.myobj.contains(event)
3445
if not contains:
@@ -52,24 +63,36 @@ def shouldthismove(self, event):
5263
return timetomove
5364

5465
def on_release(self, event):
66+
"""Mouse button release callback"""
5567
self.clicked = False
5668
self.disconnect()
5769

5870
def disconnect(self):
71+
"""Disconnect mouse motion and click release callbacks from parent canvas"""
5972
self.parentcanvas.mpl_disconnect(self.mousemotion)
6073
self.parentcanvas.mpl_disconnect(self.clickrelease)
6174
self.parentcanvas.draw()
6275

6376
def stopdrag(self):
77+
"""Disconnect on_click callback and remove dragobj url property tag"""
6478
self.myobj.set_url('')
6579
self.parentcanvas.mpl_disconnect(self.clickpress)
6680

6781

6882
class _DragLine(_DragObj):
6983
def __init__(self, ax):
84+
"""Generic draggable line class
85+
86+
Provides line-specific on_motion callback
87+
"""
7088
super().__init__(ax)
7189

7290
def on_motion(self, event):
91+
"""Update position of draggable line on mouse motion
92+
93+
If self.snapto is set to a valid lineseries object, dragging will be
94+
limited to the extent of the lineseries
95+
"""
7396
# Executed on mouse motion
7497
if not self.clicked:
7598
# See if we've clicked yet
@@ -98,11 +121,26 @@ def on_motion(self, event):
98121

99122
class _DragPatch(_DragObj):
100123
def __init__(self, ax, xy):
124+
"""Generic draggable line class
125+
126+
Provides patch-specific on_motion callback and helpers
127+
128+
self.oldxy stores the previous location of the patch, or its initial
129+
location if the object has not been moved. This is used for the
130+
on_motion cllback
131+
"""
101132
super().__init__(ax)
102133

103134
self.oldxy = xy # Store for motion callback
104135

105136
def on_motion(self, event):
137+
"""Update position of draggable patch on mouse motion
138+
139+
self.oldxy is used to calculate the mouse motion delta in the xy
140+
directions. This prevents the patch from jumping to the mouse location
141+
due to (most) objects beind defined from either their lower left corner
142+
or their center.
143+
"""
106144
# Executed on mouse motion
107145
if not self.clicked:
108146
# See if we've clicked yet
@@ -129,6 +167,7 @@ def on_motion(self, event):
129167
self.parentcanvas.draw()
130168

131169
def on_release(self, event):
170+
"""Update helper xy property"""
132171
self.clicked = False
133172

134173
# LBYL for patches with centers (e.g. ellipse) vs. xy location (e.g. rectangle)
@@ -164,18 +203,26 @@ def __init__(self, ax, position, orientation='vertical', snapto=None, **kwargs):
164203

165204
self.snapto = snapto
166205

167-
def get_xydata(self):
206+
@staticmethod
207+
def get_validorientations():
208+
return ('vertical', 'horizontal')
209+
210+
# Expose matplotlib object's property getters
211+
@property
212+
def xydata(self):
168213
"""Return the xy data as a Nx2 numpy array."""
169214
return self.myobj.get_xydata()
170215

171-
def get_xdata(self, orig=True):
216+
@property
217+
def xdata(self, orig=True):
172218
"""Return the xdata.
173219
174220
If orig is True, return the original data, else the processed data.
175221
"""
176222
return self.myobj.get_xdata(orig)
177223

178-
def get_ydata(self, orig=True):
224+
@property
225+
def ydata(self, orig=True):
179226
"""Return the ydata.
180227
181228
If orig is True, return the original data, else the processed data.
@@ -212,10 +259,10 @@ def __init__(self, ax, primaryedge, windowsize, orientation='vertical', snapto=N
212259
alpha=0.25, facecolor='limegreen', edgecolor='green', **kwargs):
213260
self.orientation = orientation.lower()
214261
if self.orientation == 'vertical':
215-
axesdimension = get_axesextent(ax)[1] # Axes height
262+
axesdimension = axesextent(ax)[1] # Axes height
216263
xy = (primaryedge, ax.get_ylim()[0])
217264
elif self.orientation == 'horizontal':
218-
axesdimension = get_axesextent(ax)[0] # Axes width
265+
axesdimension = axesextent(ax)[0] # Axes width
219266
xy = (ax.get_xlim()[0], primaryedge)
220267
else:
221268
raise ValueError(f"Unsupported orientation string: '{orientation}'")
@@ -258,6 +305,18 @@ def on_motion(self, event):
258305

259306
self.parentcanvas.draw()
260307

308+
@property
309+
def bounds(self):
310+
xy = self.myobj.get_xy()
311+
if self.orientation == 'vertical':
312+
return (xy[0], xy[0] + self.myobj.get_width())
313+
elif self.orientation == 'horizontal':
314+
return (xy[1], xy[1] + self.myobj.get_height())
315+
316+
@staticmethod
317+
def validorientations():
318+
return ('vertical', 'horizontal')
319+
261320

262321
class Window:
263322
def __init__(self, ax, primaryedge, windowstartsize, orientation='vertical', snapto=None,
@@ -267,6 +326,7 @@ def __init__(self, ax, primaryedge, windowstartsize, orientation='vertical', sna
267326
self.edges = []
268327
self.edges.append(DragLine2D(ax, primaryedge, orientation, snapto, color=edgecolor))
269328
self.edges.append(DragLine2D(ax, (primaryedge+windowstartsize), orientation, snapto, color=edgecolor))
329+
self.orientation = orientation
270330

271331
# Add spanning rectangle
272332
xy, width, height = self.spanpatchdims(*self.edges)
@@ -275,6 +335,14 @@ def __init__(self, ax, primaryedge, windowstartsize, orientation='vertical', sna
275335

276336
# TODO: Refactor to monitor changes in edge locations rather than firing on all redraws
277337
ax.figure.canvas.mpl_connect('draw_event', self.resizespanpatch)
338+
339+
@property
340+
def bounds(self):
341+
xy = self.spanpatch.get_xy()
342+
if self.orientation == 'vertical':
343+
return (xy[0], xy[0] + self.spanpatch.get_width())
344+
elif self.orientation == 'horizontal':
345+
return (xy[1], xy[1] + self.spanpatch.get_height())
278346

279347
def resizespanpatch(self, event):
280348
if self.spanpatch:
@@ -286,18 +354,22 @@ def resizespanpatch(self, event):
286354
@staticmethod
287355
def spanpatchdims(edge1, edge2):
288356
# Find leftmost, rightmost points
289-
minx = min(edge1.get_xdata() + edge2.get_xdata()) # Joining the two lists, not adding them
290-
maxx = max(edge1.get_xdata() + edge2.get_xdata()) # Joining the two lists, not adding them
357+
minx = min(edge1.xdata + edge2.xdata) # Joining the two lists, not adding them
358+
maxx = max(edge1.xdata + edge2.xdata) # Joining the two lists, not adding them
291359

292-
# Find bottommostm, topmost points
293-
miny = min(edge1.get_ydata() + edge2.get_ydata()) # Joining the two lists, not adding them
294-
maxy = max(edge1.get_ydata() + edge2.get_ydata()) # Joining the two lists, not adding them
360+
# Find bottommost, topmost points
361+
miny = min(edge1.ydata + edge2.ydata) # Joining the two lists, not adding them
362+
maxy = max(edge1.ydata + edge2.ydata) # Joining the two lists, not adding them
295363

296364
xy = (minx, miny)
297365
width = abs(maxx - minx)
298366
height = abs(maxy - miny)
299367

300368
return xy, width, height
369+
370+
@staticmethod
371+
def validorientations():
372+
return ('vertical', 'horizontal')
301373

302374

303375
class DragArc(_DragPatch):
@@ -323,7 +395,7 @@ def __init__(self, ax, xy, numVertices, radius=5, orientation=0, **kwargs):
323395

324396
super().__init__(ax, xy)
325397

326-
def get_axesextent(ax):
398+
def axesextent(ax):
327399
xlim = ax.get_xlim()
328400
ylim = ax.get_ylim()
329401

setup.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from setuptools import setup, find_packages
2+
import sys
3+
4+
if sys.version_info[:2] < (3, 6):
5+
raise RuntimeError("Python Version >= 3.6 required.")
6+
7+
__version__ = '0.5'
8+
9+
setup(
10+
name='dragpy',
11+
version=f'{__version__}',
12+
13+
author='sco1',
14+
author_email='[email protected]',
15+
url='https://github.com/sco1/dragpy',
16+
17+
packages=find_packages(),
18+
install_requires=["matplotlib"]
19+
)

0 commit comments

Comments
 (0)