Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ docs/build/*
docs/source/_build/*
src/idvc/version.py
build/*
__pycache__/
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# ChangeLog

## v26.0.0

Enhancements:
* use QtPy abstraction layer
* enables use of Qt6
* removes legacy qdarkstyle
* cancels DVC run correctly (probably due to Qt6 changes)


## v25.0.0

Enhancements:
Expand Down
45 changes: 29 additions & 16 deletions recipe/dev_environment.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
name: idvc_dev
name: idvc_qt6
channels:
- conda-forge
- ccpi
dependencies:
- eqt >=1.0.1
- openpyxl
- python
- numpy
- pandas
- pyside2
- scipy
- ccpi::ccpi-viewer >=25.0.0
- ccpi::ccpi-dvc >=22.0.0
- natsort
- docopt
- matplotlib
- openmp # [osx]
- qdarkstyle
- vtk
- 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

6 changes: 3 additions & 3 deletions recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ 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

about:
home: http://www.ccpi.ac.uk
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def version2pep440(version):
"docopt",
"matplotlib",
# "openmp; platform_system=='Darwin'",
"qdarkstyle",
"vtk",
"openpyxl",
],
Expand Down
52 changes: 21 additions & 31 deletions src/idvc/dvc_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -104,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)

Expand Down Expand Up @@ -180,7 +178,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)
Expand Down Expand Up @@ -219,13 +217,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:
Expand Down Expand Up @@ -417,7 +411,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]
Expand Down Expand Up @@ -640,7 +633,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

Expand Down Expand Up @@ -792,7 +784,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
Expand Down Expand Up @@ -973,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)

Expand All @@ -981,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)

Expand Down Expand Up @@ -1313,7 +1308,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")
Expand Down Expand Up @@ -2355,7 +2349,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():
Expand Down Expand Up @@ -3109,7 +3102,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'
Expand Down Expand Up @@ -4267,8 +4259,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
Expand Down Expand Up @@ -4545,7 +4537,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'):
Expand Down Expand Up @@ -5005,8 +4996,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()
Expand Down Expand Up @@ -5120,8 +5110,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')
Expand Down Expand Up @@ -5530,7 +5520,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()
Expand Down Expand Up @@ -5896,7 +5886,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:
Expand Down
27 changes: 18 additions & 9 deletions src/idvc/dvc_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@

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
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):
Expand Down Expand Up @@ -155,7 +158,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):

Expand Down Expand Up @@ -442,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
Expand Down Expand Up @@ -474,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)
Expand All @@ -494,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
Expand All @@ -518,11 +524,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:
Expand Down
Loading
Loading