Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit daf6984

Browse filesBrowse files
authored
Merge pull request #30 from kushalkolar/histwidget
"DockedViewport" - Viewports adjacent to `Subplot` `Viewport`
2 parents 06386b4 + 6611a6c commit daf6984
Copy full SHA for daf6984

File tree

6 files changed

+409
-129
lines changed
Filter options

6 files changed

+409
-129
lines changed

‎examples/gridplot_simple.py

Copy file name to clipboard
+53Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import numpy as np
2+
from wgpu.gui.auto import WgpuCanvas
3+
import pygfx as gfx
4+
from fastplotlib.layouts import GridPlot
5+
from fastplotlib.graphics import ImageGraphic, LineGraphic, HistogramGraphic
6+
from fastplotlib import run
7+
from math import sin, cos, radians
8+
9+
# GridPlot of shape 2 x 3
10+
grid_plot = GridPlot(shape=(2, 3))
11+
12+
image_graphics = list()
13+
14+
hist_data1 = np.random.normal(0, 256, 2048)
15+
hist_data2 = np.random.poisson(0, 256)
16+
17+
# Make a random image graphic for each subplot
18+
for i, subplot in enumerate(grid_plot):
19+
img = np.random.rand(512, 512) * 255
20+
ig = ImageGraphic(data=img, vmin=0, vmax=255, cmap='gnuplot2')
21+
image_graphics.append(ig)
22+
23+
# add the graphic to the subplot
24+
subplot.add_graphic(ig)
25+
26+
histogram = HistogramGraphic(data=hist_data1, bins=100)
27+
histogram.world_object.rotation.w = cos(radians(45))
28+
histogram.world_object.rotation.z = sin(radians(45))
29+
30+
histogram.world_object.scale.y = 1
31+
histogram.world_object.scale.x = 8
32+
33+
for dv_position in ["right", "top", "bottom", "left"]:
34+
h2 = HistogramGraphic(data=hist_data1, bins=100)
35+
36+
subplot.docked_viewports[dv_position].size = 60
37+
subplot.docked_viewports[dv_position].add_graphic(h2)
38+
#
39+
40+
# Define a function to update the image graphics
41+
# with new randomly generated data
42+
def set_random_frame():
43+
for ig in image_graphics:
44+
new_data = np.random.rand(512, 512) * 255
45+
ig.update_data(data=new_data)
46+
47+
48+
# add the animation
49+
# grid_plot.add_animations(set_random_frame)
50+
51+
grid_plot.show()
52+
53+
run()

‎fastplotlib/__init__.py

Copy file name to clipboardExpand all lines: fastplotlib/__init__.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .plot import Plot
22
from pathlib import Path
3+
from wgpu.gui.auto import run
34

45

56
with open(Path(__file__).parent.joinpath("VERSION"), "r") as f:

‎fastplotlib/layouts/_base.py

Copy file name to clipboard
+182Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
from pygfx import Scene, OrthographicCamera, PerspectiveCamera, PanZoomController, OrbitController, \
2+
Viewport, WgpuRenderer
3+
from wgpu.gui.auto import WgpuCanvas
4+
from warnings import warn
5+
from ..graphics._base import Graphic
6+
from typing import *
7+
8+
9+
class PlotArea:
10+
def __init__(
11+
self,
12+
parent,
13+
position: Any,
14+
camera: Union[OrthographicCamera, PerspectiveCamera],
15+
controller: Union[PanZoomController, OrbitController],
16+
scene: Scene,
17+
canvas: WgpuCanvas,
18+
renderer: WgpuRenderer,
19+
name: str = None,
20+
):
21+
self._parent: PlotArea = parent
22+
self._position = position
23+
24+
self._scene = scene
25+
self._canvas = canvas
26+
self._renderer = renderer
27+
if parent is None:
28+
self._viewport: Viewport = Viewport(renderer)
29+
else:
30+
self._viewport = Viewport(parent.renderer)
31+
32+
self._camera = camera
33+
self._controller = controller
34+
35+
self.controller.add_default_event_handlers(
36+
self.viewport,
37+
self.camera
38+
)
39+
40+
self.renderer.add_event_handler(self.set_viewport_rect, "resize")
41+
42+
self._graphics: List[Graphic] = list()
43+
44+
self.name = name
45+
46+
# need to think about how to deal with children better
47+
self.children = list()
48+
49+
self.set_viewport_rect()
50+
51+
# several read-only properties
52+
@property
53+
def parent(self):
54+
return self._parent
55+
56+
@property
57+
def position(self) -> Union[Tuple[int, int], Any]:
58+
"""Used by subclass based on its referencing system"""
59+
return self._position
60+
61+
@property
62+
def scene(self) -> Scene:
63+
return self._scene
64+
65+
@property
66+
def canvas(self) -> WgpuCanvas:
67+
return self._canvas
68+
69+
@property
70+
def renderer(self) -> WgpuRenderer:
71+
return self._renderer
72+
73+
@property
74+
def viewport(self) -> Viewport:
75+
return self._viewport
76+
77+
@property
78+
def camera(self) -> Union[OrthographicCamera, PerspectiveCamera]:
79+
return self._camera
80+
81+
# in the future we can think about how to allow changing the controller
82+
@property
83+
def controller(self) -> Union[PanZoomController, OrbitController]:
84+
return self._controller
85+
86+
def get_rect(self) -> Tuple[float, float, float, float]:
87+
"""allows setting the region occupied by the viewport w.r.t. the parent"""
88+
raise NotImplementedError("Must be implemented in subclass")
89+
90+
def set_viewport_rect(self, *args):
91+
self.viewport.rect = self.get_rect()
92+
93+
def render(self):
94+
self.controller.update_camera(self.camera)
95+
self.viewport.render(self.scene, self.camera)
96+
97+
for child in self.children:
98+
child.render()
99+
100+
def add_graphic(self, graphic, center: bool = True):
101+
if graphic.name is not None: # skip for those that have no name
102+
graphic_names = list()
103+
104+
for g in self._graphics:
105+
graphic_names.append(g.name)
106+
107+
if graphic.name in graphic_names:
108+
raise ValueError(f"graphics must have unique names, current graphic names are:\n {graphic_names}")
109+
110+
self._graphics.append(graphic)
111+
self.scene.add(graphic.world_object)
112+
113+
if center:
114+
self.center_graphic(graphic)
115+
116+
def _refresh_camera(self):
117+
self.controller.update_camera(self.camera)
118+
if sum(self.renderer.logical_size) > 0:
119+
scene_lsize = self.viewport.rect[2], self.viewport.rect[3]
120+
else:
121+
scene_lsize = (1, 1)
122+
123+
self.camera.set_view_size(*scene_lsize)
124+
self.camera.update_projection_matrix()
125+
126+
def center_graphic(self, graphic, zoom: float = 1.3):
127+
if not isinstance(self.camera, OrthographicCamera):
128+
warn("`center_graphic()` not yet implemented for `PerspectiveCamera`")
129+
return
130+
131+
self._refresh_camera()
132+
133+
self.controller.show_object(self.camera, graphic.world_object)
134+
135+
self.controller.zoom(zoom)
136+
137+
def center_scene(self, zoom: float = 1.3):
138+
if not len(self.scene.children) > 0:
139+
return
140+
141+
if not isinstance(self.camera, OrthographicCamera):
142+
warn("`center_scene()` not yet implemented for `PerspectiveCamera`")
143+
return
144+
145+
self._refresh_camera()
146+
147+
self.controller.show_object(self.camera, self.scene)
148+
149+
self.controller.zoom(zoom)
150+
151+
def get_graphics(self):
152+
return self._graphics
153+
154+
def remove_graphic(self, graphic):
155+
self.scene.remove(graphic.world_object)
156+
157+
def __getitem__(self, name: str):
158+
for graphic in self._graphics:
159+
if graphic.name == name:
160+
return graphic
161+
162+
graphic_names = list()
163+
for g in self._graphics:
164+
graphic_names.append(g.name)
165+
raise IndexError(f"no graphic of given name, the current graphics are:\n {graphic_names}")
166+
167+
def __str__(self):
168+
if self.name is None:
169+
name = "unnamed"
170+
else:
171+
name = self.name
172+
173+
return f"{name}: {self.__class__.__name__} @ {hex(id(self))}"
174+
175+
def __repr__(self):
176+
newline = "\n\t"
177+
178+
return f"{self}\n" \
179+
f" parent: {self.parent}\n" \
180+
f" Graphics:\n" \
181+
f"\t{newline.join(graphic.__repr__() for graphic in self.get_graphics())}" \
182+
f"\n"

‎fastplotlib/layouts/_gridplot.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_gridplot.py
+3-6Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ def __getitem__(self, index: Union[Tuple[int, int], str]):
154154
else:
155155
return self._subplots[index[0], index[1]]
156156

157-
def animate(self):
157+
def render(self):
158158
for subplot in self:
159-
subplot.animate(self.canvas.get_logical_size())
159+
subplot.render()
160160

161161
for f in self._animate_funcs:
162162
f()
@@ -173,7 +173,7 @@ def add_animations(self, *funcs: callable):
173173
self._animate_funcs += funcs
174174

175175
def show(self):
176-
self.canvas.request_draw(self.animate)
176+
self.canvas.request_draw(self.render)
177177

178178
for subplot in self:
179179
subplot.center_scene()
@@ -193,6 +193,3 @@ def __next__(self) -> Subplot:
193193

194194
def __repr__(self):
195195
return f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}\n"
196-
197-
198-

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.