From 669e667b22d2681a7c8611e76ec72fbdc13f6517 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 23 Oct 2026 09:14:12 +0100 Subject: [PATCH 01/12] use qtpy abstraction layer --- recipe/dev_environment_osx.yml | 29 +++++++++++++++++++++++++++++ recipe/meta.yaml | 2 ++ src/idvc/dvc_interface.py | 24 ++++++++++++------------ src/idvc/dvc_runner.py | 6 +++--- src/idvc/idvc.py | 4 ++-- src/idvc/io.py | 10 +++++----- src/idvc/ui/dialogs.py | 14 +++++++------- src/idvc/ui/graphs_widgets.py | 6 +++--- src/idvc/ui/save_widgets.py | 12 ++++++------ src/idvc/ui/windows.py | 4 ++-- src/idvc/utilities.py | 10 +++++----- 11 files changed, 76 insertions(+), 45 deletions(-) create mode 100644 recipe/dev_environment_osx.yml diff --git a/recipe/dev_environment_osx.yml b/recipe/dev_environment_osx.yml new file mode 100644 index 00000000..db015d09 --- /dev/null +++ b/recipe/dev_environment_osx.yml @@ -0,0 +1,29 @@ +name: idvc_dev +channels: + - conda-forge + - ccpi +dependencies: + # - eqt >=1.0.1 + - openpyxl + - python ==3.11 + - numpy + - pandas + - pyside2 + - scipy + # - ccpi::ccpi-viewer >=25.0.0 + - ccpi/label/dev::ccpi-dvc >=25.0.0 + - natsort + - docopt + - matplotlib-base + - llvm-openmp + - qdarkstyle + - h5py + - pyyaml + - schema + - qtpy + - filetype + - pip: + - --extra-index-url https://wheels.vtk.org + - vtk + - git+https://github.com/TomographicImaging/CILViewer.git#subdirectory=Wrappers/Python + - eqt >=1.0.1 diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 49a3b47a..46236231 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -48,6 +48,8 @@ requirements: - matplotlib - openmp # [osx] - qdarkstyle + - qtpy + - pyside2 about: home: http://www.ccpi.ac.uk diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index 0c5c9e8d..0183d65b 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -16,11 +16,11 @@ import os from openpyxl import load_workbook import sys -from PySide2 import QtCore, QtGui, QtWidgets -from PySide2.QtCore import (QByteArray, QRegExp, QSettings, QSize, Qt, +from qtpy import QtCore, QtGui, QtWidgets +from qtpy.QtCore import (QByteArray, QRegularExpression, QSettings, QSize, Qt, QThreadPool) -from PySide2.QtGui import QCloseEvent, QKeySequence, QRegExpValidator -from PySide2.QtWidgets import (QAction, QCheckBox, QComboBox, +from qtpy.QtGui import QCloseEvent, QKeySequence, QRegularExpressionValidator +from qtpy.QtWidgets import (QAction, QCheckBox, QComboBox, QDockWidget, QDoubleSpinBox, QFileDialog, QFormLayout, QFrame, QGroupBox, QLabel, QLineEdit, @@ -180,7 +180,7 @@ def __init__(self): self.file_menu.addAction(exit_action) # # Window dimensions - geometry = qApp.desktop().availableGeometry(self) + geometry = QtWidgets.QApplication.primaryScreen().availableGeometry() border = 50 self.setGeometry(border, border, geometry.width()-2*border, geometry.height()-2*border) @@ -792,7 +792,7 @@ def displayFileErrorDialog(self, message, title, action_button=None): pass if action_button is not None: msg.addButton(action_button, msg.ActionRole) - msg.exec_() + msg.exec() def view_image(self): """Called when the view image button is clicked in the first tab, or @@ -4267,8 +4267,8 @@ def CreateRunDVCPanel(self): formLayout.setWidget(widgetno, QFormLayout.LabelRole, rdvc_widgets['name_label']) rdvc_widgets['name_entry'] = QLineEdit(self) - rx = QRegExp("[A-Za-z0-9]+") - validator = QRegExpValidator(rx, rdvc_widgets['name_entry']) #need to check this + rx = QRegularExpression("[A-Za-z0-9]+") + validator = QRegularExpressionValidator(rx, rdvc_widgets['name_entry']) #need to check this rdvc_widgets['name_entry'].setValidator(validator) formLayout.setWidget(widgetno, QFormLayout.FieldRole, rdvc_widgets['name_entry']) widgetno += 1 @@ -5120,8 +5120,8 @@ def CreateSaveWindow(self, cancel_text, event): qlabel.setText("Save session as:") qwidget = QtWidgets.QLineEdit(dialog.groupBox) qwidget.setClearButtonEnabled(True) - rx = QRegExp("[A-Za-z0-9]+") - validator = QRegExpValidator(rx, dialog) #need to check this + rx = QRegularExpression("[A-Za-z0-9]+") + validator = QRegularExpressionValidator(rx, dialog) #need to check this qwidget.setValidator(validator) # finally add to the form widget dialog.addWidget(qwidget, qlabel, 'session_name') @@ -5530,7 +5530,7 @@ def CreateSessionSelector(self, stage): dialog.Cancel.clicked.connect(self.load_session_new) self.SessionSelectionWindow = dialog # Try to centre the load session window - # from PySide2.QtGui import QScreen + # from qtpy.QtGui import QScreen # geom = QScreen().availableGeometry() # print (geom) # centrex = geom.topRight().x() + geom.topLeft().x() @@ -5896,7 +5896,7 @@ def warningDialog(self, message='', window_title='', detailed_text=''): dialog.setWindowTitle(window_title) dialog.setDetailedText(detailed_text) dialog.setStandardButtons(QMessageBox.Ok) - retval = dialog.exec_() + retval = dialog.exec() return retval # Loading and Error windows: diff --git a/src/idvc/dvc_runner.py b/src/idvc/dvc_runner.py index 386c2a4f..3a4799f7 100644 --- a/src/idvc/dvc_runner.py +++ b/src/idvc/dvc_runner.py @@ -15,9 +15,9 @@ import os import numpy as np -from PySide2 import QtCore +from qtpy import QtCore from datetime import datetime -from PySide2.QtWidgets import QMessageBox +from qtpy.QtWidgets import QMessageBox import json import time import shutil @@ -155,7 +155,7 @@ def displayFileErrorDialog(main_window, message, title): msg.setWindowTitle(title) msg.setText(message) #msg.setDetailedText(main_window.e.ErrorMessage()) - msg.exec_() + msg.exec() def cancel_run(main_window, process, run_succeeded): diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index ee9f168c..d34651d0 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -1,4 +1,4 @@ -from PySide2 import QtWidgets, QtGui +from qtpy import QtWidgets, QtGui import os, sys import logging import argparse @@ -42,7 +42,7 @@ def main(): - sys.exit(app.exec_()) + sys.exit(app.exec()) if __name__ == "__main__": main() diff --git a/src/idvc/io.py b/src/idvc/io.py index 92aa4158..30e01cd5 100644 --- a/src/idvc/io.py +++ b/src/idvc/io.py @@ -36,9 +36,9 @@ Converter) from ccpi.viewer.ui.dialogs import RawInputDialog, HDF5InputDialog from eqt.threading import Worker -from PySide2 import QtCore, QtGui -from PySide2.QtCore import QThreadPool -from PySide2.QtWidgets import (QComboBox, QDialog, QDialogButtonBox, +from qtpy import QtCore, QtGui +from qtpy.QtCore import QThreadPool +from qtpy.QtWidgets import (QComboBox, QDialog, QDialogButtonBox, QFormLayout, QGroupBox, QLabel, QLineEdit, QMessageBox, QProgressDialog, QVBoxLayout, QWidget) @@ -397,7 +397,7 @@ def displayErrorDialogFromWorker(main_window, error): msg.setWindowTitle(title) msg.setText(message) msg.setDetailedText(detailed_message) - msg.exec_() + msg.exec() def warningDialog(main_window, message='', window_title='', detailed_text=''): dialog = QMessageBox(main_window) @@ -406,7 +406,7 @@ def warningDialog(main_window, message='', window_title='', detailed_text=''): dialog.setWindowTitle(window_title) dialog.setDetailedText(detailed_text) dialog.setStandardButtons(QMessageBox.Ok) - retval = dialog.exec_() + retval = dialog.exec() return retval # Load images: diff --git a/src/idvc/ui/dialogs.py b/src/idvc/ui/dialogs.py index a4271cd8..406cce83 100644 --- a/src/idvc/ui/dialogs.py +++ b/src/idvc/ui/dialogs.py @@ -1,7 +1,7 @@ -import PySide2 -from PySide2.QtWidgets import * -from PySide2.QtCore import * -from PySide2.QtGui import * +import qtpy +from qtpy.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * import multiprocessing import vtk @@ -89,9 +89,9 @@ def __init__(self, parent, title="Settings"): def onOk(self): - default_font_family = PySide2.QtWidgets.QApplication.font().family() - font = PySide2.QtGui.QFont(default_font_family, self.fontsize_widget.value()) - PySide2.QtWidgets.QApplication.setFont(font) + default_font_family = qtpy.QtWidgets.QApplication.font().family() + font = qtpy.QtGui.QFont(default_font_family, self.fontsize_widget.value()) + qtpy.QtWidgets.QApplication.setFont(font) #self.parent.settings.setValue("settings_chosen", 1) if self.dark_checkbox.isChecked(): self.parent.settings.setValue("dark_mode", True) diff --git a/src/idvc/ui/graphs_widgets.py b/src/idvc/ui/graphs_widgets.py index 1277e90e..0165dbaf 100644 --- a/src/idvc/ui/graphs_widgets.py +++ b/src/idvc/ui/graphs_widgets.py @@ -1,6 +1,6 @@ -from PySide2.QtWidgets import * -from PySide2.QtCore import * -from PySide2.QtGui import * +from qtpy.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * import numpy as np from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas diff --git a/src/idvc/ui/save_widgets.py b/src/idvc/ui/save_widgets.py index 5b94bffc..f5d63ddc 100644 --- a/src/idvc/ui/save_widgets.py +++ b/src/idvc/ui/save_widgets.py @@ -1,7 +1,7 @@ -from PySide2 import QtWidgets, QtCore -from PySide2.QtWidgets import * -from PySide2.QtCore import * -from PySide2.QtGui import * +from qtpy import QtWidgets, QtCore +from qtpy.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * import shutil import os import tempfile @@ -31,8 +31,8 @@ def __init__(self, parent, object_type, save_only): #self.setInputMode(QtWidgets.QInputDialog.TextInput) self.textbox = QLineEdit(self) - rx = QRegExp("[A-Za-z0-9]+") - validator = QRegExpValidator(rx, self.textbox) #need to check this + rx = QRegularExpression("[A-Za-z0-9]+") + validator = QRegularExpressionValidator(rx, self.textbox) #need to check this self.textbox.setValidator(validator) self.save_button = QPushButton("Save") diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 766f70ee..3aac9688 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -8,7 +8,7 @@ from ccpi.viewer.QCILViewerWidget import QCILViewerWidget from ccpi.viewer.utils import * from ccpi.viewer.utils.error_handling import ErrorObserver -from PySide2 import QtCore, QtGui, QtWidgets +from qtpy import QtCore, QtGui, QtWidgets from idvc.ui.graphs_widgets import * import logging @@ -216,7 +216,7 @@ def __init__(self, parent=None): self.setDockOptions(QMainWindow.ForceTabbedDocks) # Window dimensions - geometry = qApp.desktop().availableGeometry(self) + geometry = QtWidgets.QApplication.primaryScreen().availableGeometry() self.setGeometry(50,50, geometry.width()-100, geometry.height()-100) #self.setFixedSize(geometry.width() * 0.6, geometry.height() * 0.8) diff --git a/src/idvc/utilities.py b/src/idvc/utilities.py index a50916a4..c1b1a351 100644 --- a/src/idvc/utilities.py +++ b/src/idvc/utilities.py @@ -1,8 +1,8 @@ -import PySide2 -from PySide2 import QtWidgets, QtCore, QtGui -from PySide2.QtWidgets import * -from PySide2.QtCore import * -from PySide2.QtGui import * +import qtpy +from qtpy import QtWidgets, QtCore, QtGui +from qtpy.QtWidgets import * +from qtpy.QtCore import * +from qtpy.QtGui import * import numpy as np import os from eqt.ui.NoBorderScrollArea import NoBorderScrollArea From 4f161acc87f6b557d806914137181b65605fdbba Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:42:27 +0100 Subject: [PATCH 02/12] remove qdarkstyle as we can use system colorScheme in the future Currently working in macos, to be tested in Windows and Linux --- recipe/dev_environment_osx.yml | 2 +- src/idvc/dvc_interface.py | 22 ++++------------------ src/idvc/ui/dialogs.py | 16 ---------------- 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/recipe/dev_environment_osx.yml b/recipe/dev_environment_osx.yml index db015d09..e9e13e6e 100644 --- a/recipe/dev_environment_osx.yml +++ b/recipe/dev_environment_osx.yml @@ -8,7 +8,7 @@ dependencies: - python ==3.11 - numpy - pandas - - pyside2 + - pyside6 - scipy # - ccpi::ccpi-viewer >=25.0.0 - ccpi/label/dev::ccpi-dvc >=25.0.0 diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index 0183d65b..d371b4c2 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -82,9 +82,6 @@ from eqt.ui import FormDialog -import qdarkstyle -from qdarkstyle.dark.palette import DarkPalette -from qdarkstyle.light.palette import LightPalette from idvc import version as gui_version @@ -219,13 +216,9 @@ def createPopupMenu(self): return QtWidgets.QMenu(self) # Create a new menu def SetAppStyle(self): - if self.settings.value("dark_mode") is None: - self.settings.setValue("dark_mode", True) - if self.settings.value("dark_mode") == "true": - style = qdarkstyle.load_stylesheet(palette=DarkPalette) - else: - style = qdarkstyle.load_stylesheet(palette=LightPalette) - self.setStyleSheet(style) + # No stylesheet is applied: the application follows the system theme. + # The qdarkstyle stylesheet hid the VTK viewers with Qt6 on macOS. + pass #Setting up the session: @@ -417,7 +410,6 @@ def CreateViewerSettingsPanel(self): self.visualisation_setting_widgets = vs_widgets scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.viewer_settings_panel[1]) def updateCoordinates(self): viewers_2D = [self.vis_widget_2D.frame.viewer] @@ -640,7 +632,6 @@ def CreateSelectImagePanel(self): self.addDockWidget(QtCore.Qt.LeftDockWidgetArea,dockWidget) scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.select_image_panel[1]) self.si_widgets = si_widgets @@ -1313,7 +1304,6 @@ def CreateRegistrationPanel(self): self.registration_parameters = rp scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.registration_panel[1]) def createRegistrationViewer(self): # print("Create reg viewer") @@ -2355,7 +2345,6 @@ def CreateMaskPanel(self): self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dockWidget) scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.mask_panel[1]) def warnIfUnchecking(self): if not self.mask_parameters['extendMaskCheck'].isChecked() and self.mask_parameters['extendMaskCheck'].isEnabled(): @@ -3109,7 +3098,6 @@ def CreatePointCloudPanel(self): self.graphWidgetFL.setWidget(widgetno, QFormLayout.FieldRole, pc['pc_points_value']) - scroll_area_point_cloud.apply_qdarkstyle_to_buttons(self.dockWidget) def _generatePointCloudClicked(self): self.pointcloud_is = 'generated' @@ -4545,7 +4533,6 @@ def CreateRunDVCPanel(self): self.rdvc_widgets = rdvc_widgets scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.run_dvc_panel[1]) def _set_num_points_in_run_to_all(self): if hasattr(self, 'pc_no_points'): @@ -5005,8 +4992,7 @@ def CreateViewDVCResultsPanel(self): self.result_widgets = result_widgets scroll_area = dockWidget.widget() - scroll_area.apply_qdarkstyle_to_buttons(self.dvc_results_panel[1]) - + def show_run_pcs(self): #show pointcloud files in list self.result_widgets['pc_entry'].clear() diff --git a/src/idvc/ui/dialogs.py b/src/idvc/ui/dialogs.py index 406cce83..44ba9604 100644 --- a/src/idvc/ui/dialogs.py +++ b/src/idvc/ui/dialogs.py @@ -24,18 +24,6 @@ def __init__(self, parent, title="Settings"): self.fontsize_widget.setSingleStep(1) self.fontsize_widget.setValue(12) self.addWidget(self.fontsize_widget, self.fontsize_label, 'fontsize') - self.dark_checkbox = QCheckBox("Dark Mode") - # populate from settings - if self.parent.settings.value("dark_mode") is not None: - if self.parent.settings.value("dark_mode") == "true": - self.dark_checkbox.setChecked(True) - else: - self.dark_checkbox.setChecked(False) - else: - self.dark_checkbox.setChecked(True) - - self.addWidget(self.dark_checkbox, '', 'darkmode') - self.copy_files_checkbox = QCheckBox("Allow a copy of the image files to be stored. ") self.copy_files_checkbox.setChecked(False) self.addWidget(self.copy_files_checkbox, '', 'copy_file_checkbox') @@ -93,10 +81,6 @@ def onOk(self): font = qtpy.QtGui.QFont(default_font_family, self.fontsize_widget.value()) qtpy.QtWidgets.QApplication.setFont(font) #self.parent.settings.setValue("settings_chosen", 1) - if self.dark_checkbox.isChecked(): - self.parent.settings.setValue("dark_mode", True) - else: - self.parent.settings.setValue("dark_mode", False) self.parent.SetAppStyle() if self.copy_files_checkbox.isChecked(): From 9d53b6ac1c703318637388612f9ec742b158707f Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:36:38 +0100 Subject: [PATCH 03/12] added__pycache__ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0b9d4c1d..90558d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ docs/build/* docs/source/_build/* src/idvc/version.py build/* +__pycache__/ From 7059580e60aecb5bd5fe9884b774af6ad91047e6 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:36:58 +0100 Subject: [PATCH 04/12] updated environment file --- recipe/dev_environment.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/recipe/dev_environment.yml b/recipe/dev_environment.yml index 53242c25..ff864e9c 100644 --- a/recipe/dev_environment.yml +++ b/recipe/dev_environment.yml @@ -8,13 +8,17 @@ dependencies: - python - numpy - pandas - - pyside2 + - pyside6 - scipy - ccpi::ccpi-viewer >=25.0.0 - - ccpi::ccpi-dvc >=22.0.0 + - ccpi/label/dev::ccpi-dvc >=25.0.0 - natsort - docopt - - matplotlib - - openmp # [osx] - - qdarkstyle - vtk + - matplotlib-base + - llvm-openmp + - h5py + - pyyaml + - schema + - qtpy + - filetype From a5333d82873fe8b19289c96e4af1257d0e415653 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:24:38 +0100 Subject: [PATCH 05/12] modified changelog --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f3303dd9..3b7fb278 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # ChangeLog +## v26.0.0 + +Enhancements: + * use QtPy abstraction layer + * enables use of Qt6 + * removes legacy qdarkstyle + ## v25.0.0 Enhancements: From 9f4023539c1c9b535532884c47baffb035026d42 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:02:48 +0000 Subject: [PATCH 06/12] remove qdarkstyle and add logging --- setup.py | 1 - src/idvc/dvc_runner.py | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index b8c538bc..deb23970 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,6 @@ def version2pep440(version): "docopt", "matplotlib", # "openmp; platform_system=='Darwin'", - "qdarkstyle", "vtk", "openpyxl", ], diff --git a/src/idvc/dvc_runner.py b/src/idvc/dvc_runner.py index 3a4799f7..bb24142e 100644 --- a/src/idvc/dvc_runner.py +++ b/src/idvc/dvc_runner.py @@ -24,6 +24,9 @@ import platform from .io import save_tiff_stack_as_raw, save_nxs_as_raw +import logging +logger = logging.getLogger(__name__) + class PrintCallback(object): '''Class to handle the emit call when no callback is provided''' def emit(self, *args, **kwargs): @@ -518,11 +521,14 @@ def finished_run(self, exitCode, exitStatus): .format(self.process_num + 1, required_runs, len(self.processes), exitCode, exitStatus)) - if exitStatus == 0: + logger.debug("Finished process {} with exit code {} and status {}".format(self.process_num + + 1, exitCode, exitStatus)) + logger.debug("Pre self.run_succeeded: {}".format(self.run_succeeded)) + if exitCode == 0: self.run_succeeded = self.run_succeeded and True else: self.run_succeeded = self.run_succeeded and False - + logger.debug("Post self.run_succeeded: {}".format(self.run_succeeded)) if self.process_num == required_runs-1: main_window.progress_window.close() if self.run_succeeded: From ef061f39a185477aee2abd7bf08305bb70cdc58d Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:48:47 +0000 Subject: [PATCH 07/12] add environment file to test on other os --- recipe/environment_qt6.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 recipe/environment_qt6.yml diff --git a/recipe/environment_qt6.yml b/recipe/environment_qt6.yml new file mode 100644 index 00000000..8a3c480a --- /dev/null +++ b/recipe/environment_qt6.yml @@ -0,0 +1,33 @@ +name: idvc__qt6 +channels: + - conda-forge + - ccpi +dependencies: + - python =3.11 + - pip + - numpy #=2.4.6 + - scipy #=1.17.1 + - pandas #=3.1.0 + - matplotlib-base >3.8 #=3.11.2 + - h5py #=3.16.0 + - openpyxl + - pyyaml + - schema + - natsort + - docopt + - filetype + - qtpy #=2.4.3 + - pyside6 #=6.11.2 + - llvm-openmp + - ccpi::ccpi-dvc =25.0.0 + - pip: + # Kitware wheels (not conda-forge vtk) to avoid Qt5/Qt6 conflicts on macOS. + # 9.7.x crashes with this app; 9.4.2 works. + - --extra-index-url https://wheels.vtk.org + - vtk==9.4.2 + - git+https://github.com/TomographicImaging/eqt.git@remove-qdarkstyle + - git+https://github.com/TomographicImaging/CILViewer.git@macos-fix#subdirectory=Wrappers/Python + # dev tools + - pre-commit + - pysnooper + From 35d0a2ac34d900f8f2b18b91254048bd914835da Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:39:56 +0000 Subject: [PATCH 08/12] use log instead of debug as parameter --- src/idvc/idvc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index d34651d0..17b41961 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -8,13 +8,13 @@ def main(): parser = argparse.ArgumentParser(description='iDVC - Digital Volume Correlation Software') - parser.add_argument('--debug', type=str) + parser.add_argument('--log', type=str) args = parser.parse_args() - if args.debug in ['debug', 'info', 'warning', 'error', 'critical']: - level = eval(f'logging.{args.debug.upper()}') + if args.log in ['debug', 'info', 'warning', 'error', 'critical']: + level = eval(f'logging.{args.log.upper()}') logging.basicConfig(level=level) - logging.info(f"iDVC: Setting debugging level to {args.debug.upper()}") + logging.info(f"iDVC: Setting debugging level to {args.log.upper()}") app = QtWidgets.QApplication([]) # Set a global font for the application default_font_family = app.font().family() From 5928092144a1038acaf0ba7429c72ac5f3e01af1 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:43:21 +0000 Subject: [PATCH 09/12] updated env files --- recipe/dev_environment_osx.yml | 2 +- recipe/environment_qt6.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/recipe/dev_environment_osx.yml b/recipe/dev_environment_osx.yml index e9e13e6e..ebee768d 100644 --- a/recipe/dev_environment_osx.yml +++ b/recipe/dev_environment_osx.yml @@ -11,7 +11,7 @@ dependencies: - pyside6 - scipy # - ccpi::ccpi-viewer >=25.0.0 - - ccpi/label/dev::ccpi-dvc >=25.0.0 + - ccpi::ccpi-dvc >=25.0.0 - natsort - docopt - matplotlib-base diff --git a/recipe/environment_qt6.yml b/recipe/environment_qt6.yml index 8a3c480a..af1ce183 100644 --- a/recipe/environment_qt6.yml +++ b/recipe/environment_qt6.yml @@ -1,4 +1,4 @@ -name: idvc__qt6 +name: idvc_qt6 channels: - conda-forge - ccpi From b5c10befc9fcc7bc8152c1994b150f54df3d5ba3 Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:45:01 +0000 Subject: [PATCH 10/12] unified environment file for development --- recipe/dev_environment.yml | 49 ++++++++++++++++++++-------------- recipe/dev_environment_osx.yml | 29 -------------------- recipe/environment_qt6.yml | 33 ----------------------- 3 files changed, 29 insertions(+), 82 deletions(-) delete mode 100644 recipe/dev_environment_osx.yml delete mode 100644 recipe/environment_qt6.yml diff --git a/recipe/dev_environment.yml b/recipe/dev_environment.yml index ff864e9c..af1ce183 100644 --- a/recipe/dev_environment.yml +++ b/recipe/dev_environment.yml @@ -1,24 +1,33 @@ -name: idvc_dev +name: idvc_qt6 channels: - conda-forge - ccpi dependencies: - - eqt >=1.0.1 - - openpyxl - - python - - numpy - - pandas - - pyside6 - - scipy - - ccpi::ccpi-viewer >=25.0.0 - - ccpi/label/dev::ccpi-dvc >=25.0.0 - - natsort - - docopt - - vtk - - matplotlib-base - - llvm-openmp - - h5py - - pyyaml - - schema - - qtpy - - filetype + - python =3.11 + - pip + - numpy #=2.4.6 + - scipy #=1.17.1 + - pandas #=3.1.0 + - matplotlib-base >3.8 #=3.11.2 + - h5py #=3.16.0 + - openpyxl + - pyyaml + - schema + - natsort + - docopt + - filetype + - qtpy #=2.4.3 + - pyside6 #=6.11.2 + - llvm-openmp + - ccpi::ccpi-dvc =25.0.0 + - pip: + # Kitware wheels (not conda-forge vtk) to avoid Qt5/Qt6 conflicts on macOS. + # 9.7.x crashes with this app; 9.4.2 works. + - --extra-index-url https://wheels.vtk.org + - vtk==9.4.2 + - git+https://github.com/TomographicImaging/eqt.git@remove-qdarkstyle + - git+https://github.com/TomographicImaging/CILViewer.git@macos-fix#subdirectory=Wrappers/Python + # dev tools + - pre-commit + - pysnooper + diff --git a/recipe/dev_environment_osx.yml b/recipe/dev_environment_osx.yml deleted file mode 100644 index ebee768d..00000000 --- a/recipe/dev_environment_osx.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: idvc_dev -channels: - - conda-forge - - ccpi -dependencies: - # - eqt >=1.0.1 - - openpyxl - - python ==3.11 - - numpy - - pandas - - pyside6 - - scipy - # - ccpi::ccpi-viewer >=25.0.0 - - ccpi::ccpi-dvc >=25.0.0 - - natsort - - docopt - - matplotlib-base - - llvm-openmp - - qdarkstyle - - h5py - - pyyaml - - schema - - qtpy - - filetype - - pip: - - --extra-index-url https://wheels.vtk.org - - vtk - - git+https://github.com/TomographicImaging/CILViewer.git#subdirectory=Wrappers/Python - - eqt >=1.0.1 diff --git a/recipe/environment_qt6.yml b/recipe/environment_qt6.yml deleted file mode 100644 index af1ce183..00000000 --- a/recipe/environment_qt6.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: idvc_qt6 -channels: - - conda-forge - - ccpi -dependencies: - - python =3.11 - - pip - - numpy #=2.4.6 - - scipy #=1.17.1 - - pandas #=3.1.0 - - matplotlib-base >3.8 #=3.11.2 - - h5py #=3.16.0 - - openpyxl - - pyyaml - - schema - - natsort - - docopt - - filetype - - qtpy #=2.4.3 - - pyside6 #=6.11.2 - - llvm-openmp - - ccpi::ccpi-dvc =25.0.0 - - pip: - # Kitware wheels (not conda-forge vtk) to avoid Qt5/Qt6 conflicts on macOS. - # 9.7.x crashes with this app; 9.4.2 works. - - --extra-index-url https://wheels.vtk.org - - vtk==9.4.2 - - git+https://github.com/TomographicImaging/eqt.git@remove-qdarkstyle - - git+https://github.com/TomographicImaging/CILViewer.git@macos-fix#subdirectory=Wrappers/Python - # dev tools - - pre-commit - - pysnooper - From 4ff7b1cf631124cb8f3e65285bf1f6a15b4373cc Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:48:54 +0000 Subject: [PATCH 11/12] modified recipe. Still not properly working as CILviewer and eqt require unreleased version --- recipe/meta.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 46236231..2658173e 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -39,15 +39,13 @@ requirements: - python - numpy - pandas - - pyside2 - scipy - ccpi-viewer >=25.0.0 - ccpi-dvc >=22.0.0 - natsort - docopt - matplotlib - - openmp # [osx] - - qdarkstyle + - llvm-openmp - qtpy - pyside2 From 3af560bfcf65586e4217b9b9031ee6b34931e84e Mon Sep 17 00:00:00 2001 From: Edoardo Pasca <14138589+paskino@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:24:33 +0000 Subject: [PATCH 12/12] handles DVC run cancel and adds logs --- CHANGES.md | 2 ++ src/idvc/dvc_interface.py | 6 +++++- src/idvc/dvc_runner.py | 11 +++++++---- src/idvc/idvc.py | 6 +++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3b7fb278..795ab282 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ Enhancements: * use QtPy abstraction layer * enables use of Qt6 * removes legacy qdarkstyle + * cancels DVC run correctly (probably due to Qt6 changes) + ## v25.0.0 diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index d371b4c2..906c1e9e 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -101,7 +101,8 @@ allowed_point_cloud_file_formats = ('.roi', '.txt', '.csv', '.xlsx', '.inp') class MainWindow(QMainWindow): - def __init__(self): + def __init__(self, logger=None): + self.logger = logger if logger is not None else logging.getLogger(__name__) """Creates the menu bar: File, Settings, Help.""" QMainWindow.__init__(self) @@ -964,6 +965,7 @@ def visualise(self): self.select_image_dock.raise_() def create_progress_window(self, title, text, max = 100, cancel = None): + self.logger.debug("Creating progress window with title: '{}' and text: '{}'".format(title, text)) self.progress_window = QProgressDialog(text, "Cancel", 0,max, self, QtCore.Qt.Dialog) self.progress_window.setWindowTitle(title) @@ -972,8 +974,10 @@ def create_progress_window(self, title, text, max = 100, cancel = None): self.progress_window.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, True) self.progress_window.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) self.progress_window.setAutoClose(True) + self.logger.debug(f"Cancel callback is {'set' if cancel is not None else 'not set'}") if cancel is None: self.progress_window.setCancelButton(None) + else: self.progress_window.canceled.connect(cancel) diff --git a/src/idvc/dvc_runner.py b/src/idvc/dvc_runner.py index bb24142e..e7370b4a 100644 --- a/src/idvc/dvc_runner.py +++ b/src/idvc/dvc_runner.py @@ -445,6 +445,7 @@ def set_up(self, *args, **kwargs): progress_callback.emit(100) def run_dvc(self, **kwargs): + logger.debug("Starting DVC run") main_window = self.main_window input_file = self.input_file finish_fn = self.finish_fn @@ -477,6 +478,7 @@ def run_dvc(self, **kwargs): process.finished.connect(self.finished_run) process.started.connect(self.onStarted) if self.process_num == 0: + logger.debug("Creating progress window for the first process") main_window.create_progress_window("Running", "Running DVC code {}/{}".format(self.process_num +1, len(self.processes)), 100, #lambda: cancel_run(main_window, process, run_succeeded) @@ -497,18 +499,19 @@ def onStarted(self): pass def onCancel(self, process): + logger.debug("Cancelling run for process with state: {}".format(process.state())) main_window = self.main_window run_succeeded = self.run_succeeded state = process.state() # print ("Process state", state) - if state in [2,1]: - # print ("Cancelling run") + if state == QtCore.QProcess.ProcessState.Running: + logger.debug ("Cancelling run") process.kill() main_window.alert = QMessageBox(QMessageBox.NoIcon,"Cancelled","The run was cancelled.", QMessageBox.Ok) main_window.alert.show() self.run_succeeded = False - elif state == 0: - print ("all OK, all processes ended") + else: + logger.info (f"Processes state: {state}") def finished_run(self, exitCode, exitStatus): main_window = self.main_window diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index 17b41961..51d55522 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -3,7 +3,7 @@ import logging import argparse - +logger = logging.getLogger(__name__) def main(): parser = argparse.ArgumentParser(description='iDVC - Digital Volume Correlation Software') @@ -14,7 +14,7 @@ def main(): if args.log in ['debug', 'info', 'warning', 'error', 'critical']: level = eval(f'logging.{args.log.upper()}') logging.basicConfig(level=level) - logging.info(f"iDVC: Setting debugging level to {args.log.upper()}") + logger.info(f"iDVC: Setting debugging level to {args.log.upper()}") app = QtWidgets.QApplication([]) # Set a global font for the application default_font_family = app.font().family() @@ -35,7 +35,7 @@ def main(): vtk.vtkOutputWindow.SetInstance(err) from idvc.dvc_interface import MainWindow - window = MainWindow() + window = MainWindow(logger) window.show() splash.finish(window)