diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/Report.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/Report.qml deleted file mode 100644 index 1e3ce72..0000000 --- a/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/Report.qml +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2023 EasyDiffraction contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2023 Contributors to the EasyDiffraction project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Components as EaComponents - -import Gui.Globals as Globals - - -EaComponents.BasicReport { - - xAxisTitle: "x" - yAxisTitle: "y" - - measuredXYData: Globals.Proxies.main.summary.isCreated ? - {'x': Globals.Proxies.main.experiment.xData, 'y': Globals.Proxies.main.experiment.yData} : - {} - calculatedXYData: Globals.Proxies.main.summary.isCreated ? - {'x': Globals.Proxies.main.experiment.xData, 'y': Globals.Proxies.main.model.yData} : - {} - - Component.onCompleted: Globals.Refs.summaryReportWebEngine = this - -} - diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/TextView.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/TextView.qml index 05911a3..639ada9 100644 --- a/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/TextView.qml +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/MainContent/TextView.qml @@ -13,51 +13,44 @@ import Gui.Globals as Globals Rectangle { - id: container - - anchors.fill: parent color: EaStyle.Colors.textViewBackground Behavior on color { EaAnimations.ThemeChange {} } - // ListView - ListView { - id: listView + // Flickable + Flickable { + id: flick anchors.fill: parent - anchors.topMargin: EaStyle.Sizes.fontPixelSize - anchors.bottomMargin: EaStyle.Sizes.fontPixelSize - anchors.leftMargin: EaStyle.Sizes.fontPixelSize + + contentWidth: textArea.contentWidth + contentHeight: textArea.contentHeight clip: true + flickableDirection: Flickable.VerticalFlick ScrollBar.vertical: EaElements.ScrollBar { policy: ScrollBar.AsNeeded interactive: false } - model: [Globals.Proxies.main.summary.dataBlocksCif] + // Main text area + EaElements.TextArea { + id: textArea - // ListView Delegate - delegate: TextEdit { readOnly: true - font.family: EaStyle.Fonts.monoFontFamily - font.pixelSize: EaStyle.Sizes.fontPixelSize - - color: EaStyle.Colors.themeForeground - Behavior on color { EaAnimations.ThemeChange {} } - - selectionColor: EaStyle.Colors.themeAccent - Behavior on selectionColor { EaAnimations.ThemeChange {} } + width: flick.width + topPadding: 0 + bottomPadding: 0 + padding: 2.5 * EaStyle.Sizes.fontPixelSize - selectedTextColor: EaStyle.Colors.themeBackground - Behavior on selectedTextColor { EaAnimations.ThemeChange {} } - - text: listView.model[index] + '\n' - } - // ListView Delegate + textFormat: TextEdit.RichText + text: Globals.Proxies.main.summary.asHtml + } + // Main text area } - // ListView + // Flickable + } diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/PageStructure.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/PageStructure.qml index f88b65f..01a4114 100644 --- a/easyDiffractionApp/Gui/Components/Pages/Summary/PageStructure.qml +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/PageStructure.qml @@ -22,12 +22,12 @@ EaComponents.ContentPage { mainView: EaComponents.MainContent { tabs: [ - EaElements.TabButton { text: qsTr("Report") } + EaElements.TabButton { text: qsTr("Summary") } ] items: [ Loader { - source: 'MainContent/TextView.qml' // 'MainContent/Report.qml' + source: 'MainContent/TextView.qml' onStatusChanged: if (status === Loader.Ready) console.debug(`${source} loaded`) } ] @@ -36,12 +36,14 @@ EaComponents.ContentPage { sideBar: EaComponents.SideBar { tabs: [ EaElements.TabButton { text: qsTr("Basic controls") }, - EaElements.TabButton { text: qsTr("Advanced controls"); enabled: false } + EaElements.TabButton { text: qsTr("Advanced controls"); enabled: false }, + EaElements.TabButton { text: qsTr("Text mode") } ] items: [ Loader { source: 'SideBarBasic.qml' }, - Loader { source: 'SideBarAdvanced.qml' } + Loader { source: 'SideBarAdvanced.qml' }, + Loader { source: 'SideBarText.qml' } ] continueButton.visible: false diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic.qml index 4abc69c..16a0863 100644 --- a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic.qml +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic.qml @@ -13,8 +13,9 @@ import Gui.Globals as Globals EaComponents.SideBarColumn { EaElements.GroupBox { - enabled: false //Globals.Proxies.main.project.created - title: qsTr("Export report") + enabled: Globals.Proxies.main.project.created && + !Globals.Proxies.main.project.location.includes(":/Examples") + title: qsTr("Export summary") collapsible: false Loader { source: 'SideBarBasic/ExportReportGroup.qml' } diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic/ExportReportGroup.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic/ExportReportGroup.qml index 0a5448d..cd84a01 100644 --- a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic/ExportReportGroup.qml +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarBasic/ExportReportGroup.qml @@ -5,14 +5,21 @@ import QtQuick import QtQuick.Controls import QtQuick.Dialogs +import QtCore import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Globals as EaGlobals import EasyApp.Gui.Elements as EaElements +import EasyApp.Gui.Logic as EaLogic import Gui.Globals as Globals Column { + property string projectLocation: Globals.Proxies.main.project.location + + EaLogic.Utils.osPathSep() + + 'summary' + spacing: EaStyle.Sizes.fontPixelSize // Name field + format selector @@ -26,8 +33,8 @@ Column { topInset: nameLabel.height topPadding: topInset + padding horizontalAlignment: TextInput.AlignLeft - placeholderText: qsTr("Enter report file name here") - //Component.onCompleted: text = 'report' + placeholderText: qsTr("Enter summary file name here") + Component.onCompleted: text = 'summary' EaElements.Label { id: nameLabel text: qsTr("Name") @@ -44,8 +51,7 @@ Column { textRole: "text" valueRole: "value" model: [ - { value: 'html', text: qsTr("Interactive HTML") }, - { value: 'pdf', text: qsTr("Static PDF") } + { value: 'html', text: qsTr("HTML") } ] EaElements.Label { id: formatLabel @@ -63,6 +69,7 @@ Column { rightPadding: chooseButton.width horizontalAlignment: TextInput.AlignLeft placeholderText: qsTr("Enter report location here") + Component.onCompleted: text = projectLocation EaElements.Label { id: locationLabel text: qsTr("Location") @@ -86,10 +93,11 @@ Column { text: qsTr('Save') onClicked: { console.debug(`Clicking '${text}' button: ${this}`) - if (formatField.currentValue === 'html') { - Globals.Proxies.main.summary.saveHtmlReport(reportLocationField.text) - } else if (formatField.currentValue === 'pdf') { - //Globals.Vars.reportWebView.printToPdf(reportLocationField.text) + if (formatField.currentValue === 'html' || formatField.currentValue === 'pdf') { + Globals.Proxies.main.summary.saveAsHtml(reportLocationField.text, + nameField.text) + } else { + console.error(`Unsupported file format '${formatField.currentValue}'`) } } } @@ -98,8 +106,10 @@ Column { FolderDialog { id: reportParentDirDialog title: qsTr("Choose report parent directory") - //folder: Globals.Proxies.main.project.currentProjectPath - //Component.onCompleted: selectedFolder = projectPathDict().parent + } + + onProjectLocationChanged: { + reportParentDirDialog.currentFolder = Globals.Proxies.main.backendHelpers.localFileToUrl(projectLocation) } } diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText.qml new file mode 100644 index 0000000..2b757f6 --- /dev/null +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText.qml @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 EasyDiffraction contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2023 Contributors to the EasyDiffraction project + +import QtQuick +import QtQuick.Controls + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Elements as EaElements +import EasyApp.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaComponents.SideBarColumn { + + EaElements.GroupBox { + collapsible: false + + Loader { source: 'SideBarText/TextView.qml' } + } + +} diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/Experiments.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/Experiments.qml new file mode 100644 index 0000000..991a74e --- /dev/null +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/Experiments.qml @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2023 EasyDiffraction contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2023 Contributors to the EasyDiffraction project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApp.Gui.Globals as EaGlobals +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Elements as EaElements +import EasyApp.Gui.Components as EaComponents +import EasyApp.Gui.Logic as EaLogic + +import Gui.Globals as Globals + + +EaElements.ComboBox { + id: comboBox + + topInset: 0 + bottomInset: 0 + + width: EaStyle.Sizes.sideBarContentWidth + anchors.bottomMargin: EaStyle.Sizes.fontPixelSize + + textRole: "name" + + model: Globals.Proxies.main.experiment.dataBlocksNoMeas + currentIndex: Globals.Proxies.main.experiment.currentIndex + + onActivated: Globals.Proxies.main.experiment.currentIndex = currentIndex + + // ComboBox delegate (popup rows) + delegate: ItemDelegate { + id: itemDelegate + + width: parent.width + height: EaStyle.Sizes.tableRowHeight + + highlighted: comboBox.highlightedIndex === index + + // ComboBox delegate (popup rows) contentItem + contentItem: Item { + width: parent.width + height: parent.height + + Row { + height: parent.height + spacing: EaStyle.Sizes.tableColumnSpacing + + EaComponents.TableViewLabel { + text: index + 1 + color: EaStyle.Colors.themeForegroundMinor + } + + EaComponents.TableViewButton { + anchors.verticalCenter: parent.verticalCenter + fontIcon: "microscope" + ToolTip.text: qsTr("Measured pattern color") + backgroundColor: "transparent" + borderColor: "transparent" + iconColor: EaStyle.Colors.chartForegroundsExtra[2] + } + + EaComponents.TableViewParameter { + enabled: false + text: comboBox.model[index].name.value + } + } + } + // ComboBox delegate (popup rows) contentItem + + // ComboBox delegate (popup rows) background + background: Rectangle { + color: itemDelegate.highlighted ? + EaStyle.Colors.tableHighlight : + index % 2 ? + EaStyle.Colors.themeBackgroundHovered2 : + EaStyle.Colors.themeBackgroundHovered1 + } + // ComboBox delegate (popup rows) background + + } + // ComboBox delegate (popup rows) + + // ComboBox (selected item) contentItem + contentItem: Item { + width: parent.width + height: parent.height + + Row { + height: parent.height + spacing: EaStyle.Sizes.tableColumnSpacing + + EaComponents.TableViewLabel { + text: currentIndex + 1 + color: EaStyle.Colors.themeForegroundMinor + } + + EaComponents.TableViewButton { + anchors.verticalCenter: parent.verticalCenter + fontIcon: "microscope" + ToolTip.text: qsTr("Measured pattern color") + backgroundColor: "transparent" + borderColor: "transparent" + iconColor: EaStyle.Colors.chartForegroundsExtra[2] + } + + EaComponents.TableViewParameter { + enabled: false + text: typeof comboBox.model[currentIndex] !== 'undefined' ? + comboBox.model[currentIndex].name.value : + '' + } + } + } + // ComboBox (selected item) contentItem +} diff --git a/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/TextView.qml b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/TextView.qml new file mode 100644 index 0000000..5608cdf --- /dev/null +++ b/easyDiffractionApp/Gui/Components/Pages/Summary/SideBarText/TextView.qml @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2023 EasyDiffraction contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2023 Contributors to the EasyDiffraction project + +import QtQuick +import QtQuick.Controls + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Animations as EaAnimations +import EasyApp.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +Rectangle { + id: container + + width: EaStyle.Sizes.sideBarContentWidth + height: 36 * EaStyle.Sizes.fontPixelSize + + (applicationWindow.height - EaStyle.Sizes.appWindowMinimumHeight) + + color: EaStyle.Colors.textViewBackground + Behavior on color { EaAnimations.ThemeChange {} } + + border.color: EaStyle.Colors.appBarComboBoxBorder + + // ListView + ListView { + id: listView + + anchors.fill: parent + anchors.topMargin: EaStyle.Sizes.fontPixelSize + anchors.bottomMargin: EaStyle.Sizes.fontPixelSize + anchors.leftMargin: EaStyle.Sizes.fontPixelSize + + clip: true + + ScrollBar.vertical: EaElements.ScrollBar { + policy: ScrollBar.AsNeeded + interactive: true + } + + model: [Globals.Proxies.main.summary.dataBlocksCif] + + // ListView Delegate + delegate: TextEdit { + readOnly: true + + font.family: EaStyle.Fonts.monoFontFamily + font.pixelSize: EaStyle.Sizes.fontPixelSize + + color: EaStyle.Colors.themeForeground + Behavior on color { EaAnimations.ThemeChange {} } + + selectionColor: EaStyle.Colors.themeAccent + Behavior on selectionColor { EaAnimations.ThemeChange {} } + + selectedTextColor: EaStyle.Colors.themeBackground + Behavior on selectedTextColor { EaAnimations.ThemeChange {} } + + text: listView.model[index] + '\n' + } + // ListView Delegate + + } + // ListView +} diff --git a/easyDiffractionApp/Gui/Sandbox/Report.qml b/easyDiffractionApp/Gui/Sandbox/Report.qml new file mode 100644 index 0000000..b1b96e4 --- /dev/null +++ b/easyDiffractionApp/Gui/Sandbox/Report.qml @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2023 EasyDiffraction contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2023 Contributors to the EasyDiffraction project + +import QtQuick +import QtQuick.Controls + +import EasyApp.Gui.Style as EaStyle +import EasyApp.Gui.Elements as EaElements +import EasyApp.Gui.Components as EaComponents + +Rectangle { + + width: 500 + height: 300 + + color: 'transparent' + + // Flickable + Flickable { + id: flick + + anchors.fill: parent + + contentWidth: textArea.contentWidth + contentHeight: textArea.contentHeight + + clip: true + flickableDirection: Flickable.VerticalFlick + + ScrollBar.vertical: EaElements.ScrollBar { + policy: ScrollBar.AsNeeded + interactive: false + } + + // Main text area + EaElements.TextArea { + id: textArea + + width: flick.width + topPadding: 0 + bottomPadding: 0 + + //backgroundRect.border.color: EaStyle.Colors.appBarComboBoxBorder + textFormat: TextEdit.RichText + text: content + } + // Main text area + + } + // Flickable + + property string content: ` + + + + + + + + +

Summary

+ + +

Crystal data

+ + + + + + coo + + + Crystal system, space group + Orthorhombic, Pnma + + + a, b, c (Å) + 8.92(1), 8.92(1), 8.92(1) + + + α, β, γ (°) + 90(1), 90(1), 90(1) + + + + + +

Data collection

+ + + + + + hrpt + + + Radiation probe + Neutron + + + Radiation type + Constant wavelength + + + Radiation wavelength, λ (Å) + 0.793 + + + Measured range, θmin, θmax, θinc (°) + 10, 150, 0.05 + + + No. of data points + 347 + + + + + +

Refinement

+ + + + + + Goodness-of-fit (χ2) + 3.17 + + + No. of reflections + 347 + + + No. of total, free, fixed parameters + 50, 20, 10 + + + No. of constraints + 0 + + + + + +` + +} diff --git a/easyDiffractionApp/Gui/Sandbox/TextView.qml b/easyDiffractionApp/Gui/Sandbox/TextView.qml index a9bdad1..30dcc68 100644 --- a/easyDiffractionApp/Gui/Sandbox/TextView.qml +++ b/easyDiffractionApp/Gui/Sandbox/TextView.qml @@ -13,11 +13,6 @@ EaComponents.SideBarColumn { width: EaStyle.Sizes.sideBarContentWidth height: 2 * EaStyle.Sizes.sideBarContentWidth - - - - - EaElements.TextArea { id: textArea diff --git a/easyDiffractionApp/Logic/Connections.py b/easyDiffractionApp/Logic/Connections.py index c00fa10..4e7673d 100644 --- a/easyDiffractionApp/Logic/Connections.py +++ b/easyDiffractionApp/Logic/Connections.py @@ -29,6 +29,7 @@ def __init__(self, parent): self._proxy.experiment.currentIndexChanged.connect(self.onExperimentCurrentIndexChanged) self._proxy.experiment.dataBlocksNoMeasChanged.connect(self.onExperimentDataBlocksNoMeasChanged) self._proxy.experiment.dataBlocksChanged.connect(self.onExperimentDataBlocksChanged) + self._proxy.experiment.yMeasArraysChanged.connect(self.onSummaryDataBlocksCifChanged) # Analysis self._proxy.analysis.definedChanged.connect(self.onAnalysisDefined) @@ -45,6 +46,15 @@ def __init__(self, parent): self._proxy.fitting.chiSqSignificantlyChanged.connect(self.onFittingChiSqSignificantlyChanged) self._proxy.fitting.minimizerMethodChanged.connect(self.onFittingMinimizerMethodChanged) + # Summary + self._proxy.summary.dataBlocksCifChanged.connect(self.onSummaryDataBlocksCifChanged) + + # Status + self._proxy.status.calculatorChanged.connect(self.onSummaryDataBlocksCifChanged) + self._proxy.status.minimizerChanged.connect(self.onSummaryDataBlocksCifChanged) + self._proxy.status.variablesChanged.connect(self.onSummaryDataBlocksCifChanged) + self._proxy.status.goodnessOfFitChanged.connect(self.onSummaryDataBlocksCifChanged) + ######### # Project ######### @@ -344,3 +354,10 @@ def onFittingChiSqSignificantlyChanged(self): def onFittingMinimizerMethodChanged(self): self._proxy.status.minimizer = f'Lmfit ({self._proxy.fitting.minimizerMethod})' + + ######### + # Summary + ######### + + def onSummaryDataBlocksCifChanged(self): + self._proxy.summary.setAsHtml() diff --git a/easyDiffractionApp/Logic/Experiment.py b/easyDiffractionApp/Logic/Experiment.py index 0b85577..11f6f8d 100644 --- a/easyDiffractionApp/Logic/Experiment.py +++ b/easyDiffractionApp/Logic/Experiment.py @@ -3,6 +3,7 @@ # © 2023 Contributors to the EasyDiffraction project import os +import re import copy from io import StringIO import numpy as np @@ -233,6 +234,9 @@ def loadExperimentsFromFiles(self, fpaths): console.error(f"Unsupported file extension {fext} of {fpath}") return + # Lowercase all data block names + procData = re.sub(r'data_(.*)', lambda m: m.group(0).lower(), procData) + self.loadExperimentsFromEdCif(procData) @Slot(str) diff --git a/easyDiffractionApp/Logic/Helpers.py b/easyDiffractionApp/Logic/Helpers.py index 657ad18..de977b3 100644 --- a/easyDiffractionApp/Logic/Helpers.py +++ b/easyDiffractionApp/Logic/Helpers.py @@ -12,7 +12,7 @@ import numpy as np from uncertainties import ufloat -from PySide6.QtCore import Qt, QObject, QCoreApplication, Signal, Slot, Property +from PySide6.QtCore import Qt, QObject, QCoreApplication, QUrl, Signal, Slot, Property #from PySide6.QtGui import QStyleHints from PySide6.QtWidgets import QApplication @@ -260,6 +260,16 @@ class BackendHelpers(QObject): def __init__(self, parent=None): super().__init__(parent) + @Slot(str, result=str) + def urlToLocalFile(self, url: str): + fpath = QUrl(url).toLocalFile() + return fpath + + @Slot(str, result=str) + def localFileToUrl(self, fpath: str): + url = QUrl.fromLocalFile(fpath).toString() + return url + @Slot(int) def exitApp(self, exitCode): console.debug(f'Force exiting application with code {exitCode}') diff --git a/easyDiffractionApp/Logic/Model.py b/easyDiffractionApp/Logic/Model.py index 2d9702d..bdeb40a 100644 --- a/easyDiffractionApp/Logic/Model.py +++ b/easyDiffractionApp/Logic/Model.py @@ -175,6 +175,7 @@ def loadModelsFromFiles(self, fpaths): console.debug(f"Loading model(s) from: {fpath}") with open(fpath, 'r') as file: edCif = file.read() + edCif = re.sub(r'data_(.*)', lambda m: m.group(0).lower(), edCif) # Lowercase all data block names self.loadModelsFromEdCif(edCif) @Slot(str) diff --git a/easyDiffractionApp/Logic/Summary.py b/easyDiffractionApp/Logic/Summary.py index f36d2d5..15458c3 100644 --- a/easyDiffractionApp/Logic/Summary.py +++ b/easyDiffractionApp/Logic/Summary.py @@ -16,15 +16,180 @@ console.error('No CrysPy module found') +_HTML_TEMPLATE = """ + + + + + + + + + + + + + + + + + + + + + + project_information_section + + + + + + + + + + crystal_data_section + + + + + + + + + + data_collection_section + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Summary

Crystal data

Data collection

Refinement

Calculation enginecalculation_engine — https://www.cryspy.fr
Minimization engineminimization_engine — https://lmfit.github.io/lmfit-py
Goodness-of-fit: reduced χ2goodness_of_fit
No. of parameters: total, free, fixednum_total_params,  num_free_params,  num_fixed_params
No. of constraints0
+ + + +""" + + +_HTML_PROJECT_INFORMATION_TEMPLATE = """ + +

Project information

+ + + + + + Title + project_title + + + Description + project_description + + + No. of phases + num_phases + + + No. of experiments + num_experiments + + + +""" + +_HTML_CRYSTAL_DATA_TEMPLATE = """ + + Phase datablock + phase_name + + + Crystal system, space group + crystal_system,  name_H_M_alt + + + Cell lengths: a, b, c (Å) + length_a,  length_b,  length_c + + + Cell angles: ɑ, β, ɣ (°) + angle_alpha,  angle_beta,  angle_gamma + + + +""" + +_HTML_DATA_COLLECTION_TEMPLATE = """ + + Experiment datablock + experiment_name + + + Radiation probe + radiation_probe + + + Radiation type + radiation_type + + + Measured range: min, max, inc (range_units) + range_min,  range_max,  range_inc + + + No. of data points + num_data_points + + + +""" + + class Summary(QObject): isCreatedChanged = Signal() dataBlocksCifChanged = Signal() + asHtmlChanged = Signal() def __init__(self, parent): super().__init__(parent) self._proxy = parent self._isCreated = False self._dataBlocksCif = '' + self._asHtml = '' @Slot() def resetAll(self): @@ -47,6 +212,24 @@ def isCreated(self, newValue): def dataBlocksCif(self): return self._dataBlocksCif + @Property(str, notify=asHtmlChanged) + def asHtml(self): + return self._asHtml + + @asHtml.setter + def asHtml(self, newValue): + if self._asHtml == newValue: + return + self._asHtml = newValue + self.asHtmlChanged.emit() + + @Slot(str, str) + def saveAsHtml(self, location, name): + fpath = os.path.join(location, f'{name}.html') + with open(fpath, 'w') as file: + file.write(self.asHtml) + console.debug(IO.formatMsg('sub', f'saved to: {fpath}')) + def setDataBlocksCif(self): dataBlocksCifList = [] cryspyDict = self._proxy.data._cryspyDict @@ -64,19 +247,137 @@ def setDataBlocksCif(self): blockCifNoMeas = self._proxy.experiment._dataBlocksCifNoMeas[idx] dataBlocksCifList.append(blockCifNoMeas) for dataBlock in cryspyObj.items: - if type(dataBlock) == cryspy.E_data_classes.cl_2_pd.Pd and dataBlock.data_name == block['name']['value']: - for subBlock in dataBlock.items: - if type(subBlock) == cryspy.C_item_loop_classes.cl_1_pd_proc.PdProcL: - dataBlockProcCif = subBlock.to_cif() - dataBlocksCifList.append(dataBlockProcCif) - elif type(subBlock) == cryspy.C_item_loop_classes.cl_1_pd_peak.PdPeakL: - dataBlockPeakCif = subBlock.to_cif() - dataBlocksCifList.append(dataBlockPeakCif) + if dataBlock.data_name == block['name']['value']: + if type(dataBlock) == cryspy.E_data_classes.cl_2_pd.Pd: + for subBlock in dataBlock.items: + if type(subBlock) == cryspy.C_item_loop_classes.cl_1_pd_peak.PdPeakL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) + elif type(subBlock) == cryspy.C_item_loop_classes.cl_1_pd_proc.PdProcL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) + elif type(subBlock) == cryspy.C_item_loop_classes.cl_1_pd_meas.PdMeasL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) + elif type(dataBlock) == cryspy.E_data_classes.cl_2_tof.TOF: + for subBlock in dataBlock.items: + if type(subBlock) == cryspy.C_item_loop_classes.cl_1_tof_peak.TOFPeakL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) + elif type(subBlock) == cryspy.C_item_loop_classes.cl_1_tof_proc.TOFProcL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) + elif type(subBlock) == cryspy.C_item_loop_classes.cl_1_tof_meas.TOFMeasL: + cif = subBlock.to_cif() + dataBlocksCifList.append(cif) console.debug(IO.formatMsg('sub', f'{len(dataBlocksCifList)} item(s)', '', 'to CIF string', 'converted')) self._dataBlocksCif = '\n\n'.join(dataBlocksCifList) self.dataBlocksCifChanged.emit() + def setAsHtml(self): + proxy = self._proxy + if proxy.model.dataBlocks == []: + return + + html = _HTML_TEMPLATE + + # Project information section + + html_project = '' + if proxy.project.created: + project_title = proxy.project.dataBlock['name']['value'] + project_description = proxy.project.dataBlock['params']['_project']['description']['value'] + + html_project = _HTML_PROJECT_INFORMATION_TEMPLATE + html_project = html_project.replace('project_title', f'{project_title}') + html_project = html_project.replace('project_description', f'{project_description}') + html_project = html_project.replace('num_phases', f'{proxy.status.phaseCount}') + html_project = html_project.replace('num_experiments', f'{proxy.status.experimentsCount}') + + html = html.replace('project_information_section', html_project) + + # Crystal data section + + html_phases = [] + + for phase in proxy.model.dataBlocks: + phase_name = phase['name']['value'] + crystal_system = phase['params']['_space_group']['crystal_system']['value'] + name_H_M_alt = phase['params']['_space_group']['name_H-M_alt']['value'] + length_a = phase['params']['_cell']['length_a']['value'] + length_b = phase['params']['_cell']['length_b']['value'] + length_c = phase['params']['_cell']['length_c']['value'] + angle_alpha = phase['params']['_cell']['angle_alpha']['value'] + angle_beta = phase['params']['_cell']['angle_beta']['value'] + angle_gamma = phase['params']['_cell']['angle_gamma']['value'] + + html_phase = _HTML_CRYSTAL_DATA_TEMPLATE + html_phase = html_phase.replace('phase_name', f'{phase_name}') + html_phase = html_phase.replace('crystal_system', f'{crystal_system}') + html_phase = html_phase.replace('name_H_M_alt', f'{name_H_M_alt}') + html_phase = html_phase.replace('length_a', f'{length_a}') + html_phase = html_phase.replace('length_b', f'{length_b}') + html_phase = html_phase.replace('length_c', f'{length_c}') + html_phase = html_phase.replace('angle_alpha', f'{angle_alpha}') + html_phase = html_phase.replace('angle_beta', f'{angle_beta}') + html_phase = html_phase.replace('angle_gamma', f'{angle_gamma}') + html_phases.append(html_phase) + + html = html.replace('crystal_data_section', '\n'.join(html_phases)) + + # Data collection + + html_experiments = [] + + for idx, experiment in enumerate(proxy.experiment.dataBlocksNoMeas): + experiment_name = experiment['name']['value'] + radiation_probe = experiment['params']['_diffrn_radiation']['probe']['value'] + radiation_type = experiment['params']['_diffrn_radiation']['type']['value'] + radiation_type = radiation_type.replace('cwl', 'constant wavelength') + radiation_type = radiation_type.replace('tof', 'time-of-flight') + num_data_points = len(proxy.experiment._yMeasArrays[idx]) + if 'tof_range_min' in experiment['params']['_pd_meas']: + range_min = experiment['params']['_pd_meas']['tof_range_min']['value'] + range_max = experiment['params']['_pd_meas']['tof_range_max']['value'] + range_inc = experiment['params']['_pd_meas']['tof_range_inc']['value'] + range_units = 'µs' + else: + range_min = experiment['params']['_pd_meas']['2theta_range_min']['value'] + range_max = experiment['params']['_pd_meas']['2theta_range_max']['value'] + range_inc = experiment['params']['_pd_meas']['2theta_range_inc']['value'] + range_units = '°' + + html_experiment = _HTML_DATA_COLLECTION_TEMPLATE + html_experiment = html_experiment.replace('experiment_name', f'{experiment_name}') + html_experiment = html_experiment.replace('radiation_probe', f"{radiation_probe}") + html_experiment = html_experiment.replace('radiation_type', f"{radiation_type}") + html_experiment = html_experiment.replace('range_min', f"{range_min}") + html_experiment = html_experiment.replace('range_max', f"{range_max}") + html_experiment = html_experiment.replace('range_inc', f"{range_inc}") + html_experiment = html_experiment.replace('range_units', f"{range_units}") + html_experiment = html_experiment.replace('num_data_points', f'{num_data_points}') + html_experiments.append(html_experiment) + + html = html.replace('data_collection_section', '\n'.join(html_experiments)) + + # Refinement section + + num_free_params = proxy.fittables.freeParamsCount + num_fixed_params = proxy.fittables.fixedParamsCount + num_params = num_free_params + num_fixed_params + goodness_of_fit = proxy.status.goodnessOfFit + goodness_of_fit = goodness_of_fit.split(' → ')[-1] + + html = html.replace('calculation_engine', f'{proxy.status.calculator}') + html = html.replace('minimization_engine', f'{proxy.status.minimizer}') + html = html.replace('goodness_of_fit', f'{goodness_of_fit}') + html = html.replace('num_total_params', f'{num_free_params + num_fixed_params}') + html = html.replace('num_free_params', f'{num_free_params}') + html = html.replace('num_fixed_params', f'{num_fixed_params}') + + self.asHtml = html + def loadReportFromResources(self, fpath): console.debug(f"Loading model(s) from: {fpath}") file = QFile(fpath) diff --git a/easyDiffractionApp/resources.qrc b/easyDiffractionApp/resources.qrc index fec30d1..126a747 100644 --- a/easyDiffractionApp/resources.qrc +++ b/easyDiffractionApp/resources.qrc @@ -60,12 +60,13 @@ Gui/Components/Pages/Project/SideBarAdvanced.qml Gui/Components/Pages/Project/SideBarBasic.qml Gui/Components/Pages/Project/SideBarText.qml - Gui/Components/Pages/Summary/MainContent/Report.qml Gui/Components/Pages/Summary/MainContent/TextView.qml Gui/Components/Pages/Summary/SideBarBasic/ExportReportGroup.qml + Gui/Components/Pages/Summary/SideBarText/TextView.qml Gui/Components/Pages/Summary/PageStructure.qml Gui/Components/Pages/Summary/SideBarAdvanced.qml Gui/Components/Pages/Summary/SideBarBasic.qml + Gui/Components/Pages/Summary/SideBarText.qml Gui/Components/ApplicationWindow.qml Gui/Components/GuiTestsController.qml Gui/Components/qmldir @@ -98,6 +99,7 @@ Gui/Sandbox/TableView.qml Gui/Sandbox/TextView.qml Gui/Sandbox/VariableMenu.qml + Gui/Sandbox/Report.qml Gui/Tests/NoProjectCreated.qml Gui/Tests/qmldir Gui/main.qml diff --git a/tests/gui/screenshots/desired/macos/HomePage.png b/tests/gui/screenshots/desired/macos/HomePage.png index 24b188d..69c4ce9 100644 Binary files a/tests/gui/screenshots/desired/macos/HomePage.png and b/tests/gui/screenshots/desired/macos/HomePage.png differ diff --git a/tests/gui/screenshots/desired/macos/SummaryPage.png b/tests/gui/screenshots/desired/macos/SummaryPage.png index 395b2d4..077a137 100644 Binary files a/tests/gui/screenshots/desired/macos/SummaryPage.png and b/tests/gui/screenshots/desired/macos/SummaryPage.png differ diff --git a/tests/gui/screenshots/desired/ubuntu/HomePage.png b/tests/gui/screenshots/desired/ubuntu/HomePage.png index 2ce263c..7e7d98f 100644 Binary files a/tests/gui/screenshots/desired/ubuntu/HomePage.png and b/tests/gui/screenshots/desired/ubuntu/HomePage.png differ diff --git a/tests/gui/screenshots/desired/ubuntu/SummaryPage.png b/tests/gui/screenshots/desired/ubuntu/SummaryPage.png index 045fad8..f631e5c 100644 Binary files a/tests/gui/screenshots/desired/ubuntu/SummaryPage.png and b/tests/gui/screenshots/desired/ubuntu/SummaryPage.png differ diff --git a/tests/gui/screenshots/desired/windows/HomePage.png b/tests/gui/screenshots/desired/windows/HomePage.png index 39b2ac6..117ef9b 100644 Binary files a/tests/gui/screenshots/desired/windows/HomePage.png and b/tests/gui/screenshots/desired/windows/HomePage.png differ diff --git a/tests/gui/screenshots/desired/windows/SummaryPage.png b/tests/gui/screenshots/desired/windows/SummaryPage.png index ee87645..6490a5e 100644 Binary files a/tests/gui/screenshots/desired/windows/SummaryPage.png and b/tests/gui/screenshots/desired/windows/SummaryPage.png differ