Source code for spinningdiskanalyzer.frontend.pages.fit

from .fit_ui import Ui_page
from .page   import Page
from .       import page
from ..      import tasks
from ...     import parameters

from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtPdf        import QPdfDocument
from PySide6.QtWidgets    import QWidget
from PySide6.QtCore       import QCoreApplication
from PySide6.QtCore       import Qt

from matplotlib.figure import Figure
from pathlib           import Path
from tempfile          import NamedTemporaryFile
from shutil            import copy2 as copy


[docs] class Fit(page.ConfirmClose, page.NavRedoExit, Page): """Performs the curve fitting.""" def __init__(self, parent: QWidget = None): # Load user interface design created in Qt Designer. super().__init__(parent) self.ui = Ui_page() self.ui.setupUi(self) # Make sure hiding buttons does not change layout. for button in (self.ui.redo, self.ui.exit): size_policy = button.sizePolicy() size_policy.setRetainSizeWhenHidden(True) button.setSizePolicy(size_policy) # Configure PDF viewer properties not accessible in Designer. self.ui.viewer.setPageMode(QPdfView.PageMode.SinglePage) self.ui.viewer.setZoomMode(QPdfView.ZoomMode.FitInView) self.ui.viewer.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.ui.viewer.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.document = QPdfDocument() self.ui.viewer.setDocument(self.document) # Connect signals to slots. self.ui.back.clicked.connect(self.go_back.emit) self.ui.redo.clicked.connect(self.on_redo) self.ui.exit.clicked.connect(QCoreApplication.quit) self.ui.bins.valueChanged.connect(self.navigation_update) # Initialize further instance attributes. self.task = None self.temp_file = None # Used in `invalidate()`.
[docs] def parameters_load(self): """Loads parameter values from disk.""" folder = Path(self.parent().select.ui.folder_display.text()) parameters.init(folder/'analysis') section = self.ui.title.text().lower().replace(' ', '_') self.ui.bins.setValue(parameters.get(section, 'bins'))
[docs] def parameters_save(self): """Saves parameter values to disk.""" if self.parameters_different(): section = self.ui.title.text().lower().replace(' ', '_') parameters.set(section, 'bins', self.ui.bins.value()) parameters.save()
[docs] def parameters_different(self): """Returns if parameter values are different in UI than on disk.""" section = self.ui.title.text().lower().replace(' ', '_') bins_now = self.ui.bins.value() bins_old = parameters.get(section, 'bins') return (bins_now != bins_old)
[docs] def on_enter(self): """Called when entering the page.""" self.mark_busy() self.parameters_load() self.fit_curve()
[docs] def on_redo(self): """Called when the user pressed the redo button.""" self.mark_busy() self.status('Invalidating previous results…') self.parent().invalidate(self) self.fit_curve()
[docs] def fit_curve(self): """Fits the cell density to a curve.""" self.task = tasks.FitCurve() self.task.finished.connect(self.tasks_done) self.task.status_update.connect(self.status_update) self.task.status_clear.connect(self.status_clear) folder = Path(self.parent().select.ui.folder_display.text()) figure = Figure() cells = folder/'analysis'/'cells.tsv' target = folder/'analysis'/'fit.pdf' n_bins = self.ui.bins.value() self.task.start(figure, cells, target, n_bins)
[docs] def tasks_done(self): """Finishes up when all background tasks are done.""" self.task = None folder = Path(self.parent().select.ui.folder_display.text()) file = folder/'analysis'/'fit.pdf' self.document.load(str(file)) self.status_clear.emit() self.parameters_save() # Possibly remove temporary file create in `invalidate()`. if self.temp_file and self.temp_file.exists(): self.temp_file.unlink() self.mark_ready()
[docs] def invalidate(self): """Invalidates results from this analysis step.""" folder = Path(self.parent().select.ui.folder_display.text()) file = folder/'analysis'/'fit.pdf' if file.exists(): self.document.close() # Even after closing the document, the file we loaded it from is # still locked, so we cannot delete it. This may be a bug in Qt. # As a work-around, we copy the currently displayed file to a # temporary file, display that instead, and remove the temporary # file in `ready()` after the new document has been displayed. temp_file = NamedTemporaryFile( prefix='fit_', suffix='.pdf', delete=False ) temp_file.close() self.temp_file = Path(temp_file.name) copy(file, self.temp_file) self.document.load(str(self.temp_file)) self.flush() tasks.delete( ( folder/'analysis'/'fit.pdf', folder/'analysis'/'fit.png', folder/'analysis'/'fit.svg', ) )