# -*- coding: utf-8 -*-
#
# Licensed under the terms of the BSD 3-Clause
# (see plotpy/LICENSE for details)
"""
Image file dialog helpers
-------------------------
Overview
^^^^^^^^
The :py:mod:``.imagefile`` module provides helper functions for opening and
saving image files using Qt dialogs and plotpy's I/O features.
Ready-to-use open/save dialogs:
:py:func:`.imagefile.exec_image_save_dialog`
Executes an image save dialog box (QFileDialog.getSaveFileName)
:py:func:`.imagefile.exec_image_open_dialog`
Executes an image open dialog box (QFileDialog.getOpenFileName)
:py:func:`.imagefile.exec_images_open_dialog`
Executes an image*s* open dialog box (QFileDialog.getOpenFileNames)
Reference
^^^^^^^^^
.. autofunction:: exec_image_save_dialog
.. autofunction:: exec_image_open_dialog
.. autofunction:: exec_images_open_dialog
"""
from __future__ import annotations
import os.path as osp
import sys
import numpy as np
from qtpy import QtWidgets as QW
from qtpy.QtWidgets import QWidget # only to help intersphinx find QWidget
from plotpy import io
from plotpy.config import _
# ===============================================================================
# Ready-to-use open/save dialogs
# ===============================================================================
[docs]
def exec_image_save_dialog(
parent: QWidget,
data: np.ndarray,
template: dict | None = None,
basedir: str = "",
app_name: str | None = None,
) -> str | None:
"""Executes an image save dialog box (QFileDialog.getSaveFileName)
Args:
parent: parent widget (None means no parent)
data: image pixel array data
template: image template (pydicom dataset) for DICOM files
basedir: base directory ('' means current directory)
app_name: application name (used as a title for an eventual
error message box in case something goes wrong when saving image)
Returns:
Filename if dialog is accepted, None otherwise
"""
saved_in, saved_out, saved_err = sys.stdin, sys.stdout, sys.stderr
sys.stdout = None
filename, _filter = QW.QFileDialog.getSaveFileName(
parent,
_("Save as"),
basedir,
io.iohandler.get_filters("save", dtype=data.dtype, template=template),
)
sys.stdin, sys.stdout, sys.stderr = saved_in, saved_out, saved_err
if filename:
filename = str(filename)
kwargs = {}
if osp.splitext(filename)[1].lower() == ".dcm":
kwargs["template"] = template
try:
io.imwrite(filename, data, **kwargs)
return filename
except Exception as msg:
import traceback
traceback.print_exc()
message = _("{filename} could not be written:\n{msg}").format(
filename=osp.basename(filename), msg=msg
)
QW.QMessageBox.critical(
parent, _("Error") if app_name is None else app_name, message
)
return
[docs]
def exec_image_open_dialog(
parent: QWidget,
basedir: str = "",
app_name: str | None = None,
to_grayscale: bool = True,
dtype: np.dtype | None = None,
) -> tuple[str, np.ndarray] | None:
"""Executes an image open dialog box (QFileDialog.getOpenFileName)
Args:
parent: parent widget (None means no parent)
basedir: base directory ('' means current directory)
app_name: application name (used as a title for an eventual
error message box in case something goes wrong when saving image)
to_grayscale (bool | None): convert image to grayscale
dtype: data type of the image
Returns:
(filename, data) tuple if dialog is accepted, None otherwise
"""
saved_in, saved_out, saved_err = sys.stdin, sys.stdout, sys.stderr
sys.stdout = None
filename, _filter = QW.QFileDialog.getOpenFileName(
parent, _("Open"), basedir, io.iohandler.get_filters("load", dtype=dtype)
)
sys.stdin, sys.stdout, sys.stderr = saved_in, saved_out, saved_err
filename = str(filename)
try:
data = io.imread(filename, to_grayscale=to_grayscale)
except Exception as msg:
import traceback
traceback.print_exc()
message = _("{filename} could not be opened:\n{msg}").format(
filename=osp.basename(filename), msg=msg
)
QW.QMessageBox.critical(
parent, _("Error") if app_name is None else app_name, message
)
return
return filename, data
[docs]
def exec_images_open_dialog(
parent: QWidget,
basedir: str = "",
app_name: str | None = None,
to_grayscale: bool = True,
dtype: np.dtype | None = None,
) -> list[tuple[str, np.ndarray]] | None:
"""Executes an image*s* open dialog box (QFileDialog.getOpenFileNames)
Args:
parent: parent widget (None means no parent)
basedir: base directory ('' means current directory)
app_name: application name (used as a title for an eventual
error message box in case something goes wrong when saving image)
to_grayscale (bool | None): convert image to grayscale
dtype: data type
Returns:
(filename, data) tuples if dialog is accepted, None otherwise
"""
saved_in, saved_out, saved_err = sys.stdin, sys.stdout, sys.stderr
sys.stdout = None
filenames, _filter = QW.QFileDialog.getOpenFileNames(
parent, _("Open"), basedir, io.iohandler.get_filters("load", dtype=dtype)
)
sys.stdin, sys.stdout, sys.stderr = saved_in, saved_out, saved_err
filenames = [str(fname) for fname in list(filenames)]
for filename in filenames:
try:
data = io.imread(filename, to_grayscale=to_grayscale)
except Exception as msg:
import traceback
traceback.print_exc()
message = _("{filename} could not be opened:\n{msg}").format(
filename=osp.basename(filename), msg=msg
)
QW.QMessageBox.critical(
parent, _("Error") if app_name is None else app_name, message
)
return
yield filename, data