plotpy.tools.misc — PlotPy 2.7 Manual (original) (raw)
-- coding: utf-8 --
import os.path as osp import sys
import numpy as np from guidata.configtools import get_icon from guidata.dataset import BeginGroup, BoolItem, ChoiceItem, DataSet, EndGroup from guidata.qthelpers import get_std_icon from qtpy import QtCore as QC from qtpy import QtWidgets as QW from qtpy.QtPrintSupport import QPrintDialog, QPrinter
from plotpy import io from plotpy.config import _ from plotpy.interfaces import IImageItemType from plotpy.items import ( compute_trimageitems_original_size, get_image_from_plot, get_items_in_rectangle, get_plot_qrect, ) from plotpy.tools.base import CommandTool, DefaultToolbarID, RectangularActionTool from plotpy.widgets import about from plotpy.widgets.resizedialog import ResizeDialog
def save_snapshot(plot, p0, p1, new_size=None):
"""
Save rectangular plot area
p0, p1: resp. top left and bottom right points (QPointF
objects)
new_size: destination image size (tuple: (width, height))
"""
items = get_items_in_rectangle(plot, p0, p1)
if not items:
QW.QMessageBox.critical(
plot,
_("Rectangle snapshot"),
_("There is no supported image item in current selection."),
)
return
src_x, src_y, src_w, src_h = get_plot_qrect(plot, p0, p1).getRect()
original_size = compute_trimageitems_original_size(items, src_w, src_h)
if new_size is None:
new_size = (int(p1.x() - p0.x() + 1), int(p1.y() - p0.y() + 1)) # Screen size
dlg = ResizeDialog(
plot, new_size=new_size, old_size=original_size, text=_("Destination size:")
)
if not dlg.exec():
return
class SnapshotParam(DataSet):
_levels = BeginGroup(_("Image levels adjustments"))
apply_contrast = BoolItem(_("Apply contrast settings"), default=False)
apply_interpolation = BoolItem(_("Apply interpolation algorithm"), default=True)
norm_range = BoolItem(_("Scale levels to maximum range"), default=False)
_end_levels = EndGroup(_("Image levels adjustments"))
_multiple = BeginGroup(_("Superimposed images"))
add_images = ChoiceItem(
_("If image B is behind image A, " "replace intersection by"),
[(False, "A"), (True, "A+B")],
default=None,
)
_end_multiple = EndGroup(_("Superimposed images"))
param = SnapshotParam(_("Rectangle snapshot"))
if not param.edit(parent=plot):
return
if dlg.keep_original_size:
destw, desth = original_size
else:
destw, desth = dlg.width, dlg.height
try:
data = get_image_from_plot(
plot,
p0,
p1,
destw=destw,
desth=desth,
add_images=param.add_images,
apply_lut=param.apply_contrast,
apply_interpolation=param.apply_interpolation,
original_resolution=dlg.keep_original_size,
)
dtype = None
for item in items:
if dtype is None or item.data.dtype.itemsize > dtype.itemsize:
dtype = item.data.dtype
if param.norm_range:
data = io.scale_data_to_dtype(data, dtype=dtype)
else:
data = np.array(data, dtype=dtype)
except MemoryError:
mbytes = int(destw * desth * 32.0 / (8 * 1024**2))
text = _(
"There is not enough memory left to process "
"this {destw:d} x {desth:d} image ({mbytes:d} "
"MB would be required)."
)
text = text.format(destw=destw, desth=desth, mbytes=mbytes)
QW.QMessageBox.critical(plot, _("Memory error"), text)
return
for model_item in items:
model_fname = model_item.get_filename()
if model_fname is not None and model_fname.lower().endswith(".dcm"):
break
else:
model_fname = None
fname, _f = QW.QFileDialog.getSaveFileName(
plot,
_("Save as"),
_("untitled"),
io.iohandler.get_filters("save", data.dtype, template=True),
)
_base, ext = osp.splitext(fname)
options = {}
if not fname:
return
elif ext.lower() == ".png":
options.update(dict(dtype=np.uint8, max_range=True))
elif ext.lower() == ".dcm":
# This import statement must stay here because if pydicom is not installed,
# the extension .dcm is not registered in the io module, so we will not
# get here.
try:
from pydicom import dcmread # pylint: disable=import-outside-toplevel
except ImportError:
raise ImportError("This should not happen (pydicom is not installed)")
model_dcm = dcmread(model_fname)
try:
ps_attr = "ImagerPixelSpacing"
ps_x, ps_y = getattr(model_dcm, ps_attr)
except AttributeError:
ps_attr = "PixelSpacing"
ps_x, ps_y = getattr(model_dcm, ps_attr)
model_dcm.Rows, model_dcm.Columns = data.shape
dest_height, dest_width = data.shape
(
_x,
_y,
_angle,
model_dx,
model_dy,
_hflip,
_vflip,
) = model_item.get_transform()
new_ps_x = ps_x * src_w / (model_dx * dest_width)
new_ps_y = ps_y * src_h / (model_dy * dest_height)
setattr(model_dcm, ps_attr, [new_ps_x, new_ps_y])
options.update(dict(template=model_dcm))
io.imwrite(fname, data, **options)
[docs] class SnapshotTool(RectangularActionTool): """ """
SWITCH_TO_DEFAULT_TOOL = True
TITLE = _("Rectangle snapshot")
ICON = "snapshot.png"
def __init__(self, manager, toolbar_id=DefaultToolbarID):
super().__init__(
manager, save_snapshot, toolbar_id=toolbar_id, fix_orientation=True
)
class AboutTool(CommandTool): """ """
def __init__(self, manager, toolbar_id=DefaultToolbarID):
super().__init__(
manager, _("About") + " plotpy", get_icon("plotpy.svg"), toolbar_id=None
)
def activate_command(self, plot, checked):
"""Activate tool"""
about.show_about_dialog()
class FilterTool(CommandTool): """ """
def __init__(self, manager, filter, toolbar_id=None):
super().__init__(manager, str(filter.name), toolbar_id=toolbar_id)
self.filter = filter
def update_status(self, plot):
"""
:param plot:
"""
self.set_status_active_item(plot)
def activate_command(self, plot, checked):
"""Activate tool"""
plot.apply_filter(self.filter)