From f302692742d5a8d19f05f3d4479e6cacc56f5eed Mon Sep 17 00:00:00 2001
From: Grzegorz Bokota <bokota+github@gmail.com>
Date: Tue, 3 Mar 2020 17:22:32 +0100
Subject: [PATCH] fixes similar functions from lgtm (#28)

---
 .../PartSeg/common_backend/base_settings.py   |  22 ++-
 package/PartSeg/common_gui/main_window.py     |  38 +++-
 .../common_gui/multiple_file_widget.py        |  21 ++-
 .../segmentation_analysis/main_window.py      |  21 +--
 .../segmentation_analysis/partseg_settings.py |  29 ---
 .../segmentation_mask/stack_gui_main.py       |  15 +-
 .../segmentation_mask/stack_settings.py       |  28 ---
 .../PartSegCore/sphinx/reference_resolve.py   | 169 ------------------
 8 files changed, 84 insertions(+), 259 deletions(-)
 delete mode 100644 package/PartSegCore/sphinx/reference_resolve.py

diff --git a/package/PartSeg/common_backend/base_settings.py b/package/PartSeg/common_backend/base_settings.py
index d96dd9e9..51093523 100644
--- a/package/PartSeg/common_backend/base_settings.py
+++ b/package/PartSeg/common_backend/base_settings.py
@@ -521,5 +521,23 @@ class BaseSettings(ViewSettings):
 
     @staticmethod
     def verify_image(image: Image, silent=True) -> Union[Image, bool]:
-        """verify if image is correct (ex. program can not support time data)"""
-        raise NotImplementedError
+        if image.is_time:
+            if image.is_stack:
+                raise TimeAndStackException()
+            if silent:
+                return image.swap_time_and_stack()
+            else:
+                raise SwapTimeStackException()
+        return True
+
+class SwapTimeStackException(Exception):
+    """
+    Exception which inform that current image shape is not supported,
+    but can be if time and stack axes were swapped
+    """
+
+class TimeAndStackException(Exception):
+    """
+    Exception which inform that current image has both time
+    and stack dat which is not supported
+    """
\ No newline at end of file
diff --git a/package/PartSeg/common_gui/main_window.py b/package/PartSeg/common_gui/main_window.py
index f709a2f7..0555f2b7 100644
--- a/package/PartSeg/common_gui/main_window.py
+++ b/package/PartSeg/common_gui/main_window.py
@@ -6,12 +6,13 @@ from qtpy.QtCore import Signal
 import os
 
 from PartSeg.common_gui.about_dialog import AboutDialog
+from PartSeg.common_gui.image_adjustment import ImageAdjustmentDialog
 from PartSeg.common_gui.show_directory_dialog import DirectoryDialog
 from PartSeg.common_backend.load_backup import import_config
 from PartSeg.common_gui.waiting_dialog import ExecuteFunctionDialog
 from PartSegCore.io_utils import ProjectInfoBase
 from PartSegImage import Image
-from PartSeg.common_backend.base_settings import BaseSettings
+from PartSeg.common_backend.base_settings import BaseSettings, SwapTimeStackException, TimeAndStackException
 
 
 class BaseMainMenu(QWidget):
@@ -42,7 +43,24 @@ class BaseMainMenu(QWidget):
                 )
                 if resp == QMessageBox.No:
                     return
-            image = self._settings.verify_image(data.image, False)
+            try:
+                image = self._settings.verify_image(data.image, False)
+            except SwapTimeStackException:
+                res = QMessageBox.question(
+                    self,
+                    "Not supported",
+                    "Time data are currently not supported. Maybe You would like to treat time as z-stack",
+                    QMessageBox.Yes | QMessageBox.No,
+                    QMessageBox.No,
+                )
+
+                if res == QMessageBox.Yes:
+                    image = data.image.swap_time_and_stack()
+                else:
+                    return
+            except TimeAndStackException:
+                QMessageBox.warning(self, "image error", "Do not support time and stack image")
+                return
             if image:
                 if isinstance(image, Image):
                     # noinspection PyProtectedMember
@@ -167,3 +185,19 @@ class BaseMainWindow(QMainWindow):
     def show_about_dialog():
         """Show about dialog."""
         AboutDialog().exec()
+
+    @staticmethod
+    def get_project_info(file_path, image):
+        raise NotADirectoryError()
+
+    def image_adjust_exec(self):
+        dial = ImageAdjustmentDialog(self.settings.image)
+        if dial.exec():
+            algorithm = dial.result_val.algorithm
+            dial2 = ExecuteFunctionDialog(
+                algorithm.transform, [], {"image": self.settings.image, "arguments": dial.result_val.values}
+            )
+            if dial2.exec():
+                result: Image = dial2.get_result()
+                self.settings.set_project_info(self.get_project_info(result.file_path, result))
+
diff --git a/package/PartSeg/common_gui/multiple_file_widget.py b/package/PartSeg/common_gui/multiple_file_widget.py
index 37a5dfdc..9d4c2f44 100644
--- a/package/PartSeg/common_gui/multiple_file_widget.py
+++ b/package/PartSeg/common_gui/multiple_file_widget.py
@@ -23,7 +23,7 @@ from qtpy.QtGui import QFontMetrics, QResizeEvent, QMouseEvent
 from qtpy.QtCore import Qt, QTimer, Slot, Signal
 from collections import defaultdict, Counter
 
-from PartSeg.common_backend.base_settings import BaseSettings
+from PartSeg.common_backend.base_settings import BaseSettings, SwapTimeStackException, TimeAndStackException
 from PartSegImage import Image
 from PartSegCore.io_utils import LoadBase, ProjectInfoBase
 from .custom_load_dialog import CustomLoadDialog, LoadProperty
@@ -165,7 +165,24 @@ class MultipleFileWidget(QWidget):
         file_name = self.file_list[self.file_view.indexOfTopLevelItem(item.parent())]
         state_name = item.text(0)
         project_info = self.state_dict[file_name][state_name]
-        image = self.settings.verify_image(project_info.image, False)
+        try:
+            image = self._settings.verify_image(project_info.image, False)
+        except SwapTimeStackException:
+            res = QMessageBox.question(
+                self,
+                "Not supported",
+                "Time data are currently not supported. Maybe You would like to treat time as z-stack",
+                QMessageBox.Yes | QMessageBox.No,
+                QMessageBox.No,
+            )
+
+            if res == QMessageBox.Yes:
+                image = project_info.image.swap_time_and_stack()
+            else:
+                return
+        except TimeAndStackException:
+            QMessageBox.warning(self, "image error", "Do not support time and stack image")
+            return
         if isinstance(image, Image):
             project_info = project_info._replace(image=image)
             self.state_dict[file_name][state_name] = project_info
diff --git a/package/PartSeg/segmentation_analysis/main_window.py b/package/PartSeg/segmentation_analysis/main_window.py
index fdecde60..203e2f35 100644
--- a/package/PartSeg/segmentation_analysis/main_window.py
+++ b/package/PartSeg/segmentation_analysis/main_window.py
@@ -21,7 +21,6 @@ from qtpy.QtWidgets import (
 )
 
 from PartSeg.common_gui.custom_load_dialog import CustomLoadDialog
-from PartSeg.common_gui.image_adjustment import ImageAdjustmentDialog
 from PartSeg.common_gui.stacked_widget_with_selector import StackedWidgetWithSelector
 from PartSeg.segmentation_analysis.measurement_widget import MeasurementWidget
 from PartSegCore.analysis import ProjectTuple
@@ -41,7 +40,7 @@ from PartSeg.common_gui.main_window import BaseMainWindow, BaseMainMenu
 from .advanced_window import SegAdvancedWindow
 from .batch_window import BatchWindow
 from .calculation_pipeline_thread import CalculatePipelineThread
-from PartSegImage import TiffImageReader, Image
+from PartSegImage import TiffImageReader
 from PartSegCore.algorithm_describe_base import SegmentationProfile
 from PartSegCore.analysis.analysis_utils import SegmentationPipelineElement, SegmentationPipeline
 from .image_view import SynchronizeView, ResultImageView, CompareImageView
@@ -428,18 +427,6 @@ class MainMenu(BaseMainMenu):
             )
             dial2.exec()
 
-    def image_adjust_exec(self):
-        dial = ImageAdjustmentDialog(self.settings.image)
-        if dial.exec():
-            algorithm = dial.result_val.algorithm
-            dial2 = ExecuteFunctionDialog(
-                algorithm.transform, [], {"image": self.settings.image, "arguments": dial.result_val.values}
-            )
-            if dial2.exec():
-                result: Image = dial2.get_result()
-                self.settings.set_project_info(ProjectTuple(result.file_path, result))
-        return
-
     def mask_manager(self):
         if self.settings.segmentation is None:
             QMessageBox.information(self, "No segmentation", "Cannot create mask without segmentation")
@@ -611,7 +598,7 @@ class MainWindow(BaseMainWindow):
         file_menu.addAction("&Save").triggered.connect(self.main_menu.save_file)
         file_menu.addAction("Batch processing").triggered.connect(self.main_menu.batch_window)
         image_menu = menu_bar.addMenu("Image operations")
-        image_menu.addAction("Image adjustment").triggered.connect(self.main_menu.image_adjust_exec)
+        image_menu.addAction("Image adjustment").triggered.connect(self.image_adjust_exec)
         image_menu.addAction("Mask manager").triggered.connect(self.main_menu.mask_manager)
         help_menu = menu_bar.addMenu("Help")
         help_menu.addAction("State directory").triggered.connect(self.show_settings_directory)
@@ -686,3 +673,7 @@ class MainWindow(BaseMainWindow):
         self.settings.dump()
         del self.batch_window
         del self.advanced_window
+
+    @staticmethod
+    def get_project_info(file_path, image):
+        return ProjectTuple(file_path=file_path, image=image)
diff --git a/package/PartSeg/segmentation_analysis/partseg_settings.py b/package/PartSeg/segmentation_analysis/partseg_settings.py
index 023743c2..bfaad9f0 100644
--- a/package/PartSeg/segmentation_analysis/partseg_settings.py
+++ b/package/PartSeg/segmentation_analysis/partseg_settings.py
@@ -1,9 +1,7 @@
 import typing
 from copy import deepcopy
-from qtpy.QtWidgets import QMessageBox, QWidget
 from qtpy.QtCore import Signal
 
-from PartSegImage import Image
 from PartSegCore.analysis.calculation_plan import CalculationPlan
 from PartSegCore.analysis.io_utils import ProjectTuple, MaskInfo
 from PartSegCore.io_utils import HistoryElement
@@ -137,33 +135,6 @@ class PartSettings(BaseSettings):
             SaveSettingsDescription("batch_plans_save.json", self.batch_plans_dict),
         ]
 
-    @staticmethod
-    def verify_image(image: Image, silent=True) -> typing.Union[Image, bool]:
-        if image.is_time:
-            if image.is_stack:
-                if silent:
-                    raise ValueError("Do not support time and stack image")
-                else:
-                    wid = QWidget()
-                    QMessageBox.warning(wid, "image error", "Do not support time and stack image")
-                    return False
-            if silent:
-                return image.swap_time_and_stack()
-            else:
-                wid = QWidget()
-                res = QMessageBox.question(
-                    wid,
-                    "Not supported",
-                    "Time data are currently not supported." " Maybe You would like to treat time as z-stack",
-                    QMessageBox.Yes | QMessageBox.No,
-                    QMessageBox.No,
-                )
-
-                if res == QMessageBox.Yes:
-                    return image.swap_time_and_stack()
-                return False
-        return True
-
     @property
     def segmentation_pipelines(self) -> typing.Dict[str, SegmentationPipeline]:
         return self.segmentation_pipelines_dict.get(self.current_segmentation_dict, dict())
diff --git a/package/PartSeg/segmentation_mask/stack_gui_main.py b/package/PartSeg/segmentation_mask/stack_gui_main.py
index 308f3193..0d3970e1 100644
--- a/package/PartSeg/segmentation_mask/stack_gui_main.py
+++ b/package/PartSeg/segmentation_mask/stack_gui_main.py
@@ -29,7 +29,6 @@ from qtpy.QtWidgets import (
 )
 
 from PartSeg.common_gui.advanced_tabs import AdvancedWindow
-from PartSeg.common_gui.image_adjustment import ImageAdjustmentDialog
 from PartSeg.common_gui.multiple_file_widget import MultipleFileWidget
 from PartSeg.segmentation_mask.segmentation_info_dialog import SegmentationInfoDialog
 from PartSegCore.io_utils import WrongFileTypeException, HistoryElement, HistoryProblem
@@ -941,14 +940,6 @@ class MainWindow(BaseMainWindow):
     def read_drop(self, paths):
         self._read_drop(paths, io_functions)
 
-    def image_adjust_exec(self):
-        dial = ImageAdjustmentDialog(self.settings.image)
-        if dial.exec():
-            algorithm = dial.result_val.algorithm
-            dial2 = ExecuteFunctionDialog(
-                algorithm.transform, [], {"image": self.settings.image, "arguments": dial.result_val.values}
-            )
-            if dial2.exec():
-                result: Image = dial2.get_result()
-                self.settings.set_project_info(SegmentationTuple(result.file_path, result))
-        return
+    @staticmethod
+    def get_project_info(file_path, image):
+        return SegmentationTuple(file_path=file_path, image=image)
diff --git a/package/PartSeg/segmentation_mask/stack_settings.py b/package/PartSeg/segmentation_mask/stack_settings.py
index 493d526e..7d43eca4 100644
--- a/package/PartSeg/segmentation_mask/stack_settings.py
+++ b/package/PartSeg/segmentation_mask/stack_settings.py
@@ -5,7 +5,6 @@ from os import path
 
 import numpy as np
 from qtpy.QtCore import Signal, Slot
-from qtpy.QtWidgets import QMessageBox, QWidget
 
 from PartSegCore.algorithm_describe_base import SegmentationProfile
 from PartSegCore.io_utils import HistoryElement, HistoryProblem
@@ -264,33 +263,6 @@ class StackSettings(BaseSettings):
             self.segmentation = new_segmentation_data
             self.components_parameters_dict = segmentation_parameters
 
-    @staticmethod
-    def verify_image(image: Image, silent=True) -> typing.Union[Image, bool]:
-        if image.is_time:
-            if image.is_stack:
-                if silent:
-                    raise ValueError("Do not support time and stack image")
-                else:
-                    wid = QWidget()
-                    QMessageBox.warning(wid, "image error", "Do not support time and stack image")
-                    return False
-            if silent:
-                return image.swap_time_and_stack()
-            else:
-                wid = QWidget()
-                res = QMessageBox.question(
-                    wid,
-                    "Not supported",
-                    "Time data are currently not supported. " "Maybe You would like to treat time as z-stack",
-                    QMessageBox.Yes | QMessageBox.No,
-                    QMessageBox.No,
-                )
-
-                if res == QMessageBox.Yes:
-                    return image.swap_time_and_stack()
-                return False
-        return True
-
 
 def get_mask(segmentation: typing.Optional[np.ndarray], mask: typing.Optional[np.ndarray], selected: typing.List[int]):
     """
diff --git a/package/PartSegCore/sphinx/reference_resolve.py b/package/PartSegCore/sphinx/reference_resolve.py
deleted file mode 100644
index 7d55c0b3..00000000
--- a/package/PartSegCore/sphinx/reference_resolve.py
+++ /dev/null
@@ -1,169 +0,0 @@
-"""
-This module contains sphinx extension supporting for build PartSeg documentation.
-
-this extensio provides one configuration option:
-
-`qt_documentation` with possibe values:
-
- * PyQt - linking to PyQt documentation on https://www.riverbankcomputing.com/static/Docs/PyQt5/api/ (incomplete)
- * Qt - linking to Qt documentation on "https://doc.qt.io/qt-5/" (default)
- * PySide - linking to PySide documentation on  "https://doc.qt.io/qtforpython/PySide2/"
-"""
-import importlib
-import inspect
-import re
-
-from sphinx.application import Sphinx
-from sphinx.config import ENUM
-from sphinx.environment import BuildEnvironment
-from docutils.nodes import Element, TextElement
-from docutils import nodes
-from typing import List, Optional, Dict, Any
-from sphinx.locale import get_translation
-from sphinx.ext.intersphinx import InventoryAdapter
-from qtpy.QtCore import Signal
-
-_ = get_translation("sphinx")
-
-try:
-    from qtpy import QT_VERSION
-except ImportError:
-    QT_VERSION = None
-
-# TODO add response to
-#  https://stackoverflow.com/questions/47102004/how-to-properly-link-to-pyqt5-documentation-using-intersphinx
-
-signal_slot_uri = {
-    "Qt": "https://doc.qt.io/qt-5/signalsandslots.html",
-    "PySide": "https://doc.qt.io/qtforpython/overviews/signalsandslots.html",
-    "PyQt": "https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html",
-}
-
-signal_name = {"Qt": "Signal", "PySide": "Signal", "PyQt": "pyqtSignal"}
-
-slot_name = {"Qt": "Slot", "PySide": "Slot", "PyQt": "pyqtSlot"}
-
-type_translate_dict = {"class": ["class"], "meth": ["method", "signal"], "mod": ["module"]}
-
-signal_pattern = re.compile(r"((\w+\d?\.QtCore\.)|(QtCore\.)|(\.))?(pyqt)?Signal")
-slot_pattern = re.compile(r"((\w+\d?\.QtCore\.)|(QtCore\.)|(\.))?(pyqt)?Slot")
-
-
-# noinspection PyUnusedLocal
-def missing_reference(
-    app: Sphinx, env: BuildEnvironment, node: Element, contnode: TextElement
-) -> Optional[nodes.reference]:
-    """Linking to Qt documentation."""
-    target: str = node["reftarget"]
-    inventories = InventoryAdapter(env)
-    objtypes: Optional[List[str]] = None
-    if node["reftype"] == "any":
-        # we search anything!
-        objtypes = [
-            "%s:%s" % (domain.name, objtype) for domain in env.domains.values() for objtype in domain.object_types
-        ]
-        domain = None
-    else:
-        domain = node.get("refdomain")
-        if not domain:
-            # only objects in domains are in the inventory
-            return None
-        objtypes = env.get_domain(domain).objtypes_for_role(node["reftype"])
-        if not objtypes:
-            return None
-        objtypes = ["%s:%s" % (domain, objtype) for objtype in objtypes]
-    if target.startswith("PySide2"):
-        _head, tail = target.split(".", 1)
-        target = "PyQt5." + tail
-    if signal_pattern.match(target):
-        uri = signal_slot_uri[app.config.qt_documentation]
-        dispname = signal_name[app.config.qt_documentation]
-        version = QT_VERSION
-    elif slot_pattern.match(target):
-        uri = signal_slot_uri[app.config.qt_documentation]
-        dispname = slot_name[app.config.qt_documentation]
-        version = QT_VERSION
-    else:
-        target_list = [target, "PyQt5." + target]
-        target_list += [name + "." + target for name in inventories.named_inventory["PyQt"]["sip:module"].keys()]
-        if node.get("reftype") in type_translate_dict:
-            type_names = type_translate_dict[node.get("reftype")]
-        else:
-            type_names = [node.get("reftype")]
-        for name in type_names:
-            obj_type_name = "sip:{}".format(name)
-            if obj_type_name not in inventories.named_inventory["PyQt"]:
-                return None
-            for target_name in target_list:
-                if target_name in inventories.main_inventory[obj_type_name]:
-                    _proj, version, uri, dispname = inventories.named_inventory["PyQt"][obj_type_name][target_name]
-                    uri = uri.replace("##", "#")
-                    #  print(node)  # print nodes with unresolved references
-                    break
-            else:
-                continue
-            break
-        else:
-            return None
-        if app.config.qt_documentation == "Qt":
-            html_name = uri.split("/")[-1]
-            uri = "https://doc.qt.io/qt-5/" + html_name
-        elif app.config.qt_documentation == "PySide":
-            if node.get("reftype") == "meth":
-                split_tup = target_name.split(".")[1:]
-                ref_name = ".".join(["PySide2", split_tup[0], "PySide2"] + split_tup)
-                html_name = "/".join(split_tup[:-1]) + ".html#" + ref_name
-            else:
-                html_name = "/".join(target_name.split(".")[1:]) + ".html"
-            uri = "https://doc.qt.io/qtforpython/PySide2/" + html_name
-
-    # remove this line if you would like straight to pyqt documentation
-    if version:
-        reftitle = _("(in %s v%s)") % (app.config.qt_documentation, version)
-    else:
-        reftitle = _("(in %s)") % (app.config.qt_documentation,)
-    newnode = nodes.reference("", "", internal=False, refuri=uri, reftitle=reftitle)
-    if node.get("refexplicit"):
-        # use whatever title was given
-        newnode.append(contnode)
-    else:
-        # else use the given display name (used for :ref:)
-        newnode.append(contnode.__class__(dispname, dispname))
-    return newnode
-
-
-re.compile(r" +algorithm_changed *= *Signal(\([^)]*\))")
-
-
-# noinspection PyUnusedLocal
-def autodoc_process_signature(app: Sphinx, what, name: str, obj, options, signature, return_annotation):
-    if isinstance(obj, Signal):
-        module_name, class_name, signal_name_local = name.rsplit(".", 2)
-        module = importlib.import_module(module_name)
-        class_ob = getattr(module, class_name)
-        reg = re.compile(r" +" + signal_name_local + r" *= *Signal(\([^)]*\))")
-        match = reg.findall(inspect.getsource(class_ob))
-        if match:
-            return match[0], None
-
-        pos = len(name.rsplit(".", 1)[1])
-        return ", ".join([sig[pos:] for sig in obj.signatures]), None
-
-
-def setup(app: Sphinx) -> Dict[str, Any]:
-    app.setup_extension("sphinx.ext.intersphinx")
-    if hasattr(app.config, "intersphinx_mapping"):
-        if "PyQt" not in app.config.intersphinx_mapping:
-            app.config.intersphinx_mapping["PyQt"] = ("https://www.riverbankcomputing.com/static/Docs/PyQt5", None)
-    else:
-        app.config.intersphinx_mapping = {"PyQt": ("https://www.riverbankcomputing.com/static/Docs/PyQt5", None)}
-    app.connect("missing-reference", missing_reference)
-    app.connect("autodoc-process-signature", autodoc_process_signature)
-    # app.connect('doctree-read', doctree_read)
-    app.add_config_value("qt_documentation", "Qt", True, ENUM("Qt", "PySide", "PyQt"))
-    return {"version": "0.9", "env_version": 1, "parallel_read_safe": True}
-
-
-# https://doc.qt.io/qtforpython/PySide2/QtWidgets/QListWidget.html#PySide2.QtWidgets.QListWidget.itemDoubleClicked
-# https://doc.qt.io/qtforpython/PySide2/QtWidgets/QListWidget.html#
-# PySide2.QtWidgets.PySide2.QtWidgets.QListWidget.itemDoubleClicked
-- 
GitLab