Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fastplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from .legends import *
from .widgets import ImageWidget
from .utils import _notebook_print_banner, config
from .utils.gui import run

from wgpu.gui.auto import run
from wgpu.backends.wgpu_native import enumerate_adapters


Expand Down
16 changes: 7 additions & 9 deletions fastplotlib/graphics/selectors/_linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
from numbers import Real

import numpy as np

import pygfx

try:
import ipywidgets

HAS_IPYWIDGETS = True
except (ImportError, ModuleNotFoundError):
HAS_IPYWIDGETS = False

from ...utils.gui import IS_JUPYTER
from .._base import Graphic, GraphicCollection
from .._features._selection_features import LinearSelectionFeature
from ._base_selector import BaseSelector


if IS_JUPYTER:
# If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets
import ipywidgets


class LinearSelector(BaseSelector):
@property
def limits(self) -> Tuple[float, float]:
Expand Down Expand Up @@ -240,7 +238,7 @@ def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs):

"""

if not HAS_IPYWIDGETS:
if not IS_JUPYTER:
raise ImportError(
"Must installed `ipywidgets` to use `make_ipywidget_slider()`"
)
Expand Down
18 changes: 8 additions & 10 deletions fastplotlib/graphics/selectors/_linear_region.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
from typing import *
from numbers import Real

try:
import ipywidgets

HAS_IPYWIDGETS = True
except (ImportError, ModuleNotFoundError):
HAS_IPYWIDGETS = False

import numpy as np

import pygfx

from ...utils.gui import IS_JUPYTER
from .._base import Graphic, GraphicCollection
from ._base_selector import BaseSelector
from .._features._selection_features import LinearRegionSelectionFeature
from ._base_selector import BaseSelector


if IS_JUPYTER:
# If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets
import ipywidgets


class LinearRegionSelector(BaseSelector):
Expand Down Expand Up @@ -390,7 +388,7 @@ def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs):

"""

if not HAS_IPYWIDGETS:
if not IS_JUPYTER:
raise ImportError(
"Must installed `ipywidgets` to use `make_ipywidget_slider()`"
)
Expand Down
40 changes: 5 additions & 35 deletions fastplotlib/layouts/_frame/_frame.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,7 @@
import os

from ._toolbar import ToolBar

from ...graphics import ImageGraphic

from .._utils import CANVAS_OPTIONS_AVAILABLE


class UnavailableOutputContext:
# called when a requested output context is not available
# ex: if trying to force jupyter_rfb canvas but jupyter_rfb is not installed
def __init__(self, context_name, msg):
self.context_name = context_name
self.msg = msg

def __call__(self, *args, **kwargs):
raise ModuleNotFoundError(
f"The following output context is not available: {self.context_name}\n{self.msg}"
)


# TODO: potentially put all output context and toolbars in their own module and have this determination done at import
if CANVAS_OPTIONS_AVAILABLE["jupyter"]:
from ._jupyter_output import JupyterOutputContext
else:
JupyterOutputContext = UnavailableOutputContext(
"Jupyter",
"You must install fastplotlib using the `'notebook'` option to use this context:\n"
'pip install "fastplotlib[notebook]"',
)

if CANVAS_OPTIONS_AVAILABLE["qt"]:
from ._qt_output import QOutputContext
else:
QtOutput = UnavailableOutputContext(
"Qt", "You must install `PyQt6` to use this output context"
)
from ._toolbar import ToolBar


class Frame:
Expand Down Expand Up @@ -158,6 +124,8 @@ def show(

# return the appropriate OutputContext based on the current canvas
if self.canvas.__class__.__name__ == "JupyterWgpuCanvas":
from ._jupyter_output import JupyterOutputContext # noqa - inline import
Comment thread
kushalkolar marked this conversation as resolved.

self._output = JupyterOutputContext(
frame=self,
make_toolbar=toolbar,
Expand All @@ -167,6 +135,8 @@ def show(
)

elif self.canvas.__class__.__name__ == "QWgpuCanvas":
from ._qt_output import QOutputContext # noqa - inline import

self._output = QOutputContext(
frame=self, make_toolbar=toolbar, add_widgets=add_widgets
)
Expand Down
3 changes: 1 addition & 2 deletions fastplotlib/layouts/_frame/_qt_output.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from PyQt6 import QtWidgets

from ...utils.gui import QtWidgets
from ._qt_toolbar import QToolbar


Expand Down
3 changes: 1 addition & 2 deletions fastplotlib/layouts/_frame/_qt_toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import traceback
from typing import *

from PyQt6 import QtWidgets, QtCore

from ...utils.gui import QtCore, QtWidgets
from ...graphics.selectors import PolygonSelector
from ._toolbar import ToolBar
from ._qtoolbar_template import Ui_QToolbar
Expand Down
5 changes: 2 additions & 3 deletions fastplotlib/layouts/_frame/_qtoolbar_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets
from ...utils.gui import QtGui, QtCore, QtWidgets


class Ui_QToolbar(object):
Expand All @@ -30,7 +29,7 @@ def setupUi(self, QToolbar):
self.maintain_aspect_button = QtWidgets.QPushButton(parent=QToolbar)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
font.setWeight(QtGui.QFont.Weight.Bold)
self.maintain_aspect_button.setFont(font)
self.maintain_aspect_button.setCheckable(True)
self.maintain_aspect_button.setObjectName("maintain_aspect_button")
Expand Down
12 changes: 3 additions & 9 deletions fastplotlib/layouts/_gridplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import pygfx

from wgpu.gui.auto import WgpuCanvas
from wgpu.gui import WgpuCanvasBase

from ._frame import Frame
from ._utils import make_canvas_and_renderer, create_controller, create_camera
Expand All @@ -22,7 +22,7 @@ def __init__(
cameras: Union[str, list, np.ndarray] = "2d",
controller_types: Union[str, list, np.ndarray] = None,
controller_ids: Union[str, list, np.ndarray] = None,
canvas: Union[str, WgpuCanvas, pygfx.Texture] = None,
canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None,
renderer: pygfx.WgpuRenderer = None,
size: Tuple[int, int] = (500, 300),
names: Union[list, np.ndarray] = None,
Expand Down Expand Up @@ -219,12 +219,6 @@ def __init__(
for cam in cams[1:]:
_controller.add_camera(cam)

if canvas is None:
canvas = WgpuCanvas()

if renderer is None:
renderer = pygfx.renderers.WgpuRenderer(canvas)

self._canvas = canvas
self._renderer = renderer

Expand Down Expand Up @@ -266,7 +260,7 @@ def __init__(
Frame.__init__(self)

@property
def canvas(self) -> WgpuCanvas:
def canvas(self) -> WgpuCanvasBase:
"""The canvas associated to this GridPlot"""
return self._canvas

Expand Down
4 changes: 2 additions & 2 deletions fastplotlib/layouts/_plot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import *

import pygfx
from wgpu.gui.auto import WgpuCanvas
from wgpu.gui import WgpuCanvasBase

from ._subplot import Subplot
from ._frame import Frame
Expand All @@ -11,7 +11,7 @@
class Plot(Subplot, Frame, RecordMixin):
def __init__(
self,
canvas: Union[str, WgpuCanvas] = None,
canvas: Union[str, WgpuCanvasBase] = None,
renderer: pygfx.WgpuRenderer = None,
camera: Union[str, pygfx.PerspectiveCamera] = "2d",
controller: Union[str, pygfx.Controller] = None,
Expand Down
6 changes: 3 additions & 3 deletions fastplotlib/layouts/_plot_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import pygfx
from pylinalg import vec_transform, vec_unproject
from wgpu.gui.auto import WgpuCanvas
from wgpu.gui import WgpuCanvasBase

from ._utils import create_camera, create_controller
from ..graphics._base import Graphic
Expand All @@ -29,7 +29,7 @@ def __init__(
camera: Union[pygfx.PerspectiveCamera],
controller: Union[pygfx.Controller],
scene: pygfx.Scene,
canvas: WgpuCanvas,
canvas: WgpuCanvasBase,
renderer: pygfx.WgpuRenderer,
name: str = None,
):
Expand Down Expand Up @@ -122,7 +122,7 @@ def scene(self) -> pygfx.Scene:
return self._scene

@property
def canvas(self) -> WgpuCanvas:
def canvas(self) -> WgpuCanvasBase:
"""Canvas associated to the plot area"""
return self._canvas

Expand Down
4 changes: 2 additions & 2 deletions fastplotlib/layouts/_subplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pygfx

from wgpu.gui.auto import WgpuCanvas
from wgpu.gui import WgpuCanvasBase

from ..graphics import TextGraphic
from ._utils import make_canvas_and_renderer, create_camera, create_controller
Expand All @@ -20,7 +20,7 @@ def __init__(
parent_dims: Tuple[int, int] = None,
camera: Union[str, pygfx.PerspectiveCamera] = "2d",
controller: Union[str, pygfx.Controller] = None,
canvas: Union[str, WgpuCanvas, pygfx.Texture] = None,
canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None,
renderer: pygfx.WgpuRenderer = None,
name: str = None,
):
Expand Down
74 changes: 8 additions & 66 deletions fastplotlib/layouts/_utils.py
Comment thread
kushalkolar marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,88 +1,30 @@
from typing import *
import importlib

import pygfx
from pygfx import WgpuRenderer, Texture
from wgpu.gui import WgpuCanvasBase

# default auto-determined canvas
from wgpu.gui.auto import WgpuCanvas
from wgpu.gui.base import WgpuCanvasBase


# TODO: this determination can be better
try:
from wgpu.gui.jupyter import JupyterWgpuCanvas
except ImportError:
JupyterWgpuCanvas = False

try:
import PyQt6
from wgpu.gui.qt import QWgpuCanvas
except ImportError:
QWgpuCanvas = False

try:
from wgpu.gui.glfw import GlfwWgpuCanvas
except ImportError:
GlfwWgpuCanvas = False


CANVAS_OPTIONS = ["jupyter", "glfw", "qt"]
CANVAS_OPTIONS_AVAILABLE = {
"jupyter": JupyterWgpuCanvas,
"glfw": GlfwWgpuCanvas,
"qt": QWgpuCanvas,
}


def auto_determine_canvas():
try:
ip = get_ipython()
if ip.has_trait("kernel"):
if hasattr(ip.kernel, "app"):
if ip.kernel.app.__class__.__name__ == "QApplication":
return QWgpuCanvas
else:
return JupyterWgpuCanvas
except NameError:
pass

else:
if CANVAS_OPTIONS_AVAILABLE["qt"]:
return QWgpuCanvas
elif CANVAS_OPTIONS_AVAILABLE["glfw"]:
return GlfwWgpuCanvas

# We go with the wgpu auto guess
# for example, offscreen canvas etc.
return WgpuCanvas
from ..utils import gui


def make_canvas_and_renderer(
canvas: Union[str, WgpuCanvas, Texture, None], renderer: [WgpuRenderer, None]
canvas: Union[str, WgpuCanvasBase, Texture, None], renderer: [WgpuRenderer, None]
):
"""
Parses arguments and returns the appropriate canvas and renderer instances
as a tuple (canvas, renderer)
"""

if canvas is None:
Canvas = auto_determine_canvas()
canvas = Canvas(max_fps=60)

canvas = gui.WgpuCanvas(max_fps=60)
elif isinstance(canvas, str):
if canvas not in CANVAS_OPTIONS:
raise ValueError(f"str canvas argument must be one of: {CANVAS_OPTIONS}")
elif not CANVAS_OPTIONS_AVAILABLE[canvas]:
raise ImportError(
f"The {canvas} framework is not installed for using this canvas"
)
else:
canvas = CANVAS_OPTIONS_AVAILABLE[canvas](max_fps=60)

m = importlib.import_module("wgpu.gui." + canvas)
canvas = m.WgpuCanvas(max_fps=60)
elif not isinstance(canvas, (WgpuCanvasBase, Texture)):
raise ValueError(
f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture"
f" or a str from the following options: {CANVAS_OPTIONS}"
f" or a str with the wgpu gui backend name."
)

if renderer is None:
Expand Down
Loading