From c77ed6a82fefbbf8879ba87d62bd895ebfd1e6fa Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sun, 1 Oct 2023 21:53:06 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B4=D0=B8=D1=81=D0=BF=D0=B5=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=B0=D0=BD=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #30 --- statapp/calculations.py | 7 +++ statapp/main_window.py | 21 ++++----- statapp/models/ro_table_model.py | 53 +++++++++++++++++++++++ statapp/models/variance_analysis_model.py | 14 ++++++ statapp/ui/main_window.ui | 9 +++- statapp/ui/ui_main_window.py | 7 ++- statapp/ui/ui_variance_analysis_window.py | 41 ++++++++++++++++++ statapp/ui/variance_analysis_window.ui | 28 ++++++++++++ statapp/utils.py | 21 +++++++++ statapp/variance_analysis.py | 18 ++++++++ 10 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 statapp/calculations.py create mode 100644 statapp/models/ro_table_model.py create mode 100644 statapp/models/variance_analysis_model.py create mode 100644 statapp/ui/ui_variance_analysis_window.py create mode 100644 statapp/ui/variance_analysis_window.ui create mode 100644 statapp/variance_analysis.py diff --git a/statapp/calculations.py b/statapp/calculations.py new file mode 100644 index 0000000..08ce6cd --- /dev/null +++ b/statapp/calculations.py @@ -0,0 +1,7 @@ +import numpy as np + + +def variance_analysis(data): + return np.array([ + [np.mean(col), np.std(col), np.min(col), np.max(col)] for col in data.T + ]) diff --git a/statapp/main_window.py b/statapp/main_window.py index c751541..3fadbdc 100644 --- a/statapp/main_window.py +++ b/statapp/main_window.py @@ -9,7 +9,8 @@ from statapp.generate_window import GenerateWindow from statapp.about_window import AboutWindow from statapp.models.fileslc_model import FileSLCModel from statapp.ui.ui_main_window import Ui_MainWindow -from statapp.utils import resource_path +from statapp.utils import resource_path, buildMessageBox +from statapp.variance_analysis import VarianceAnalysisWindow class MainWindow(QMainWindow): @@ -36,7 +37,7 @@ class MainWindow(QMainWindow): if self.fileModel.file_name: file = '\nФайл сохранения:' + self.fileModel.file_name - msgBox = self.createMessageBox \ + msgBox = buildMessageBox \ ('Сохранение данных', "Сохранить данные?" + file, QMessageBox.Question, @@ -119,16 +120,10 @@ class MainWindow(QMainWindow): about_window = AboutWindow() about_window.show() - def createMessageBox(self, title, text, icon, buttons, defaultButton): - msgBox = QMessageBox() - - msgBox.setIcon(icon) - msgBox.setWindowTitle(title) - msgBox.setText(text) - msgBox.setStandardButtons(buttons) - msgBox.setDefaultButton(defaultButton) - - return msgBox + @Slot() + def on_varianceAnalysisAction_triggered(self): + dw = VarianceAnalysisWindow(self.model.getData()) + dw.exec() def closeEvent(self, event): if self.isDataChanged: @@ -136,7 +131,7 @@ class MainWindow(QMainWindow): if self.fileModel.file_name: file = '\nФайл сохранения:' + self.fileModel.file_name - msgBox = self.createMessageBox \ + msgBox = buildMessageBox \ ('Завершение работы', "Сохранить данные?" + file, QMessageBox.Question, diff --git a/statapp/models/ro_table_model.py b/statapp/models/ro_table_model.py new file mode 100644 index 0000000..cb67ee9 --- /dev/null +++ b/statapp/models/ro_table_model.py @@ -0,0 +1,53 @@ +import PySide2 +import numpy as np +from PySide2 import QtCore +from PySide2.QtCore import Qt + +from statapp.utils import safe_list_get + + +class ROTableModel(QtCore.QAbstractTableModel): + def __init__(self, + data=np.array([[]], dtype=np.float32), + ): + super().__init__() + self._data = data + self._headers = { + Qt.Vertical: self.getVerticalHeader, + Qt.Horizontal: self.getHorizontalHeader, + } + + def updateAllData(self, data): + self.layoutAboutToBeChanged.emit() + self._data = data + self.layoutChanged.emit() + + def rowCount(self, index): + return self._data.shape[0] + + def columnCount(self, index): + return self._data.shape[1] + + def getVerticalHeader(self): + return [] + + def getHorizontalHeader(self): + return [] + + def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...): + if role == Qt.DisplayRole: + return safe_list_get(self._headers[orientation](), section, None) + + return None + + def getData(self): + return self._data + + def getY(self): + return self._data[:, 0] + + + def data(self, index, role): + if role == Qt.DisplayRole: + return float(self._data[index.row(), index.column()]) + return None diff --git a/statapp/models/variance_analysis_model.py b/statapp/models/variance_analysis_model.py new file mode 100644 index 0000000..8a57895 --- /dev/null +++ b/statapp/models/variance_analysis_model.py @@ -0,0 +1,14 @@ +from PySide2.QtCore import QModelIndex + +from statapp.models.ro_table_model import ROTableModel + + +class VarianceAnalysisModel(ROTableModel): + def __init__(self, data): + super().__init__(data) + + def getHorizontalHeader(self): + return ['Мат. ожидание', 'Среднекв. отклонение', 'Минимум', 'Максимум'] + + def getVerticalHeader(self): + return ['Y'] + [f'X{i}' for i in range(1, self.rowCount(QModelIndex()))] diff --git a/statapp/ui/main_window.ui b/statapp/ui/main_window.ui index 62e034e..850ad4c 100644 --- a/statapp/ui/main_window.ui +++ b/statapp/ui/main_window.ui @@ -40,7 +40,7 @@ 0 0 800 - 21 + 27 @@ -62,6 +62,7 @@ Анализ данных + @@ -111,6 +112,12 @@ Закрыть + + + Дисперсионный анализ + + + diff --git a/statapp/ui/ui_main_window.py b/statapp/ui/ui_main_window.py index b7b658d..bfd7bfd 100644 --- a/statapp/ui/ui_main_window.py +++ b/statapp/ui/ui_main_window.py @@ -12,6 +12,7 @@ from PySide2.QtCore import * from PySide2.QtGui import * from PySide2.QtWidgets import * + class Ui_MainWindow(object): def setupUi(self, MainWindow): if not MainWindow.objectName(): @@ -29,6 +30,8 @@ class Ui_MainWindow(object): self.savefileaction.setObjectName(u"savefileaction") self.closefileaction = QAction(MainWindow) self.closefileaction.setObjectName(u"closefileaction") + self.varianceAnalysisAction = QAction(MainWindow) + self.varianceAnalysisAction.setObjectName(u"varianceAnalysisAction") self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.gridLayout = QGridLayout(self.centralwidget) @@ -48,7 +51,7 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 800, 21)) + self.menubar.setGeometry(QRect(0, 0, 800, 27)) self.filemenu = QMenu(self.menubar) self.filemenu.setObjectName(u"filemenu") self.generatemenu = QMenu(self.menubar) @@ -74,6 +77,7 @@ class Ui_MainWindow(object): self.filemenu.addAction(self.closefileaction) self.generatemenu.addAction(self.generateYaction) self.generatemenu.addAction(self.generateXaction) + self.analyzemenu.addAction(self.varianceAnalysisAction) self.helpmenu.addAction(self.aboutmenuaction) self.retranslateUi(MainWindow) @@ -89,6 +93,7 @@ class Ui_MainWindow(object): self.openfileaction.setText(QCoreApplication.translate("MainWindow", u"\u041e\u0442\u043a\u0440\u044b\u0442\u044c", None)) self.savefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", None)) self.closefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0417\u0430\u043a\u0440\u044b\u0442\u044c", None)) + self.varianceAnalysisAction.setText(QCoreApplication.translate("MainWindow", u"\u0414\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437", None)) self.label.setText(QCoreApplication.translate("MainWindow", u"\u0421\u0422\u0410\u0422\u0418\u0421\u0422\u0418\u0427\u0415\u0421\u041a\u0418\u0415 \u0414\u0410\u041d\u041d\u042b\u0415", None)) self.filemenu.setTitle(QCoreApplication.translate("MainWindow", u"\u0424\u0430\u0439\u043b", None)) self.generatemenu.setTitle(QCoreApplication.translate("MainWindow", u"\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0435\u0439", None)) diff --git a/statapp/ui/ui_variance_analysis_window.py b/statapp/ui/ui_variance_analysis_window.py new file mode 100644 index 0000000..7cea5c2 --- /dev/null +++ b/statapp/ui/ui_variance_analysis_window.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'variance_analysis_window.ui' +## +## Created by: Qt User Interface Compiler version 5.15.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide2.QtCore import * +from PySide2.QtGui import * +from PySide2.QtWidgets import * + + +class Ui_VarianceAnalysisWindow(object): + def setupUi(self, VarianceAnalysisWindow): + if not VarianceAnalysisWindow.objectName(): + VarianceAnalysisWindow.setObjectName(u"VarianceAnalysisWindow") + VarianceAnalysisWindow.resize(942, 606) + self.gridLayout_2 = QGridLayout(VarianceAnalysisWindow) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.gridLayout = QGridLayout() + self.gridLayout.setObjectName(u"gridLayout") + self.tableView = QTableView(VarianceAnalysisWindow) + self.tableView.setObjectName(u"tableView") + + self.gridLayout.addWidget(self.tableView, 0, 0, 1, 1) + + + self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1) + + + self.retranslateUi(VarianceAnalysisWindow) + + QMetaObject.connectSlotsByName(VarianceAnalysisWindow) + # setupUi + + def retranslateUi(self, VarianceAnalysisWindow): + VarianceAnalysisWindow.setWindowTitle(QCoreApplication.translate("VarianceAnalysisWindow", u"\u0414\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437", None)) + # retranslateUi diff --git a/statapp/ui/variance_analysis_window.ui b/statapp/ui/variance_analysis_window.ui new file mode 100644 index 0000000..80de4a0 --- /dev/null +++ b/statapp/ui/variance_analysis_window.ui @@ -0,0 +1,28 @@ + + + VarianceAnalysisWindow + + + + 0 + 0 + 942 + 606 + + + + Дисперсионный анализ + + + + + + + + + + + + + + diff --git a/statapp/utils.py b/statapp/utils.py index e6e19cb..9dbc210 100644 --- a/statapp/utils.py +++ b/statapp/utils.py @@ -1,6 +1,8 @@ import os import sys +from PySide2.QtWidgets import QMessageBox + def resource_path(relative): if getattr(sys, 'frozen', False): @@ -9,3 +11,22 @@ def resource_path(relative): # we are running in a normal Python environment bundle_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(bundle_dir, relative) + + +def safe_list_get(l, idx, default): + try: + return l[idx] + except IndexError: + return default + + +def buildMessageBox(title, text, icon, buttons, defaultButton): + msgBox = QMessageBox() + + msgBox.setIcon(icon) + msgBox.setWindowTitle(title) + msgBox.setText(text) + msgBox.setStandardButtons(buttons) + msgBox.setDefaultButton(defaultButton) + + return msgBox diff --git a/statapp/variance_analysis.py b/statapp/variance_analysis.py new file mode 100644 index 0000000..df8476a --- /dev/null +++ b/statapp/variance_analysis.py @@ -0,0 +1,18 @@ +from PySide2.QtWidgets import QDialog, QHeaderView + +from statapp.calculations import variance_analysis +from statapp.models.variance_analysis_model import VarianceAnalysisModel +from statapp.ui.ui_variance_analysis_window import Ui_VarianceAnalysisWindow + + +class VarianceAnalysisWindow(QDialog): + def __init__(self, data): + super().__init__() + self.ui = Ui_VarianceAnalysisWindow() + self.ui.setupUi(self) + + res = variance_analysis(data) + self.model = VarianceAnalysisModel(res.round(2)) + self.ui.tableView.setModel(self.model) + header = self.ui.tableView.horizontalHeader() + header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)