commit 0cd8e3e85130b7bcc8856bbd3ede263908fc9bc6 Author: zynfly Date: Fri Jan 7 15:42:08 2022 +0800 Init Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3fcfb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmake-build-*/ +.idea/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..52ef028 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.21) +project(Pearson) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + + +find_package(Qt5 COMPONENTS + Core + Gui + Widgets + REQUIRED) + +if (WIN32) + add_executable(Pearson WIN32 main.cpp mainwindow.cpp mainwindow.h mainwindow.ui Pearson.cpp Pearson.h DataTableModel.cpp DataTableModel.h) + target_sources(Pearson PRIVATE resource.rc) +else () + add_executable(Pearson main.cpp mainwindow.cpp mainwindow.h mainwindow.ui Pearson.cpp Pearson.h DataTableModel.cpp DataTableModel.h) +endif () +target_link_libraries(Pearson + Qt5::Core + Qt5::Gui + Qt5::Widgets + ) + +if (MSVC) + set(DEBUG_SUFFIX) + if (CMAKE_BUILD_TYPE MATCHES "Debug") + set(DEBUG_SUFFIX "d") + endif () + set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + endif () + endif () + if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/plugins/platforms/") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" + "$/plugins/platforms/") + endif () + foreach (QT_LIB Core Gui Widgets) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll" + "$") + endforeach (QT_LIB) +endif () diff --git a/DataTableModel.cpp b/DataTableModel.cpp new file mode 100644 index 0000000..5e752dc --- /dev/null +++ b/DataTableModel.cpp @@ -0,0 +1,114 @@ +// +// Created by fly on 2021/11/25. +// + +#include "DataTableModel.h" +#include +#include + + +DataTableModel::DataTableModel(QObject *parent) : QAbstractTableModel(parent) { + m_headerData << "Name" << "Pearson" << "Pearson.Abs"; +} + +QVariant DataTableModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + return m_headerData.at(section); + } + return QAbstractItemModel::headerData(section, orientation, role); +} + +int DataTableModel::rowCount(const QModelIndex &parent) const { + return m_tableData.size(); +} + +int DataTableModel::columnCount(const QModelIndex &parent) const { + return m_headerData.size(); +} + +QVariant DataTableModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) { + auto const &row = m_tableData.at(index.row()); + switch (index.column()) { + case 0: + return std::get<0>(row); + case 1: + return std::get<1>(row); + case 2: + return std::get<2>(row); + default: + break; + } + } + + return QVariant(); +} + +void DataTableModel::clear() { + beginResetModel(); + m_tableData.clear(); + endResetModel(); +} + +void DataTableModel::InsertRow(QString name, double pearson) { + beginInsertRows(QModelIndex(), m_tableData.size(), m_tableData.size()); + m_tableData.push_back({name, pearson, std::abs(pearson)}); + endInsertRows(); +} + +void DataTableModel::sort(int column, Qt::SortOrder order) { + beginResetModel(); + std::sort(m_tableData.begin(), m_tableData.end(), [&](auto first, auto last) { + if (order == Qt::AscendingOrder) { + switch (column) { + case 0: + return std::get<0>(first) > std::get<0>(last); + break; + case 1: + return std::get<1>(first) > std::get<1>(last); + break; + case 2: + return std::get<2>(first) > std::get<2>(last); + break; + default: + break; + } + } else { + switch (column) { + case 0: + return std::get<0>(first) < std::get<0>(last); + break; + case 1: + return std::get<1>(first) < std::get<1>(last); + break; + case 2: + return std::get<2>(first) < std::get<2>(last); + break; + default: + break; + } + } + return false; + }); + endResetModel(); +// QAbstractItemModel::sort(column, order); +} + +bool DataTableModel::SaveFile(const QString &fileName) { + QFile saveFile(fileName); + if (saveFile.open(QFile::WriteOnly)) { + auto header = QString("%1,%2,%3\r\n").arg(m_headerData.at(0)).arg(m_headerData.at(1)).arg(m_headerData.at(2)); + saveFile.write(header.toLocal8Bit()); + for (auto const &data: m_tableData) { + auto strData = QString("%1,%2,%3\r\n").arg(std::get<0>(data)).arg(std::get<1>(data)).arg(std::get<2>(data)); + saveFile.write(strData.toLocal8Bit()); + } + saveFile.close(); + return true; + } + + return false; +} diff --git a/DataTableModel.h b/DataTableModel.h new file mode 100644 index 0000000..bafe28c --- /dev/null +++ b/DataTableModel.h @@ -0,0 +1,44 @@ +// +// Created by fly on 2021/11/25. +// + +#ifndef PEARSON_DATATABLEMODEL_H +#define PEARSON_DATATABLEMODEL_H + +#include +#include +#include +#include +#include +#include + +class DataTableModel : public QAbstractTableModel { +Q_OBJECT +public: + explicit DataTableModel(QObject *parent = nullptr); + + // Header: + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + void sort(int column, Qt::SortOrder order) override; + + void clear(); + + void InsertRow(QString name, double pearson); + + bool SaveFile(QString const &fileName); + +private: + QStringList m_headerData; + std::vector> m_tableData; +}; + + +#endif //PEARSON_DATATABLEMODEL_H diff --git a/Pearson.cpp b/Pearson.cpp new file mode 100644 index 0000000..aee0f98 --- /dev/null +++ b/Pearson.cpp @@ -0,0 +1,39 @@ +// +// Created by fly on 2021/11/25. +// + +#include "Pearson.h" +#include +#include + +Pearson::Pearson(uint64_t index, const std::vector &primary, const std::vector &other, QObject *parent) + : + m_index(index), + m_primary(primary), + m_other(other), + QObject(parent) { + +} + +void Pearson::run() { + auto n = m_primary.size() * 1.0; + double pearson = n * std::inner_product(m_primary.begin(), m_primary.end(), m_other.begin(), 0.0) - + std::accumulate(m_primary.begin(), m_primary.end(), 0.0) * + std::accumulate(m_other.begin(), m_other.end(), 0.0); + + double temp1 = n * std::inner_product(m_primary.begin(), m_primary.end(), m_primary.begin(), 0.0) - + std::pow(std::accumulate(m_primary.begin(), m_primary.end(), 0.0), 2.0); + double temp2 = n * std::inner_product(m_other.begin(), m_other.end(), m_other.begin(), 0.0) - + std::pow(std::accumulate(m_other.begin(), m_other.end(), 0.0), 2.0); + temp1 = std::sqrt(temp1); + temp2 = std::sqrt(temp2); + auto temp = (temp1 * temp2); + if (temp != 0) { + pearson = pearson / temp; + if (!std::isnan(pearson)) { + emit result(m_index, pearson); + } + } + + emit finished(); +} diff --git a/Pearson.h b/Pearson.h new file mode 100644 index 0000000..9ab2e4c --- /dev/null +++ b/Pearson.h @@ -0,0 +1,33 @@ +// +// Created by fly on 2021/11/25. +// + +#ifndef PEARSON_PEARSON_H +#define PEARSON_PEARSON_H + +#include +#include +#include + +class Pearson : public QObject, public QRunnable { +Q_OBJECT +public: + Pearson(uint64_t index, std::vector const &primary, std::vector const &other, + QObject *parent = nullptr); + + void run() override; + +signals: + + void result(uint64_t index, double pearson); + + void finished(); + +private: + std::vector const &m_primary; + std::vector const &m_other; + uint16_t m_index; +}; + + +#endif //PEARSON_PEARSON_H diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000..4b63f56 Binary files /dev/null and b/icon.ico differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..5834df1 --- /dev/null +++ b/main.cpp @@ -0,0 +1,12 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + + qRegisterMetaType("uint64_t"); + MainWindow w; + w.show(); + + return QApplication::exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..e8c00f6 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,175 @@ +// +// Created by fly on 2021/11/25. +// + +// You may need to build the project (run Qt uic code generator) to get "ui_MainWindow.h" resolved + +#include "mainwindow.h" +#include "ui_MainWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "Pearson.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); + + m_threadPool.setMaxThreadCount(1); + + InitWidgetEnableState(); + InitThreadCountComboBox(); + InitTableView(); +} + +void MainWindow::InitHeaderComboBox() { + ui->primaryColComboBox->setEnabled(true); + ui->dropColComboBox->setEnabled(true); + + ui->primaryColComboBox->clear(); + ui->dropColComboBox->clear(); + + for (uint64_t i = 0; i < m_dataHeader.size(); ++i) { + ui->primaryColComboBox->addItem(m_dataHeader.at(i), i); + ui->dropColComboBox->addItem(m_dataHeader.at(i)); + } +} + +void MainWindow::InitThreadCountComboBox() { + auto count = QThread::idealThreadCount(); + for (int i = 1; i <= count; ++i) { + ui->threadCountComboBox->addItem(QString("%1").arg(i), i); + } + ui->threadCountComboBox->setCurrentIndex(0); +} + +void MainWindow::InitTableView() { + ui->tableView->setModel(&m_dataTableModel); + ui->tableView->setSelectionMode(QTableView::SingleSelection); + ui->tableView->setSelectionBehavior(QTableView::SelectRows); + ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + ui->tableView->setSortingEnabled(true); +} + +MainWindow::~MainWindow() { + delete ui; +} + +void MainWindow::InitWidgetEnableState() { + ui->primaryColComboBox->setEnabled(false); + ui->dropColComboBox->setEnabled(false); + ui->progressBar->setValue(0); + ui->threadCountComboBox->setEnabled(false); + ui->exportDataButton->setEnabled(false); + ui->startAnalysisButton->setEnabled(false); +} + +void MainWindow::on_exploreButton_clicked() { + m_fileName = QFileDialog::getOpenFileName(this, "请选择要分析的csv文件", "", "CSV File (*.csv)"); + if (m_fileName.size()) { + ui->openFileButton->setEnabled(true); + ui->filePathEdit->setText(m_fileName); + InitWidgetEnableState(); + } +} + +void MainWindow::on_openFileButton_clicked() { + static QRegExp split("\\s?,\\s?"); + static QRegExp remove("[ \\r\\n]"); + QFile file(m_fileName); + if (file.open(QFile::ReadOnly)) { + m_dataHeader.clear(); + m_dataVector.clear(); + QString headerLine = file.readLine(); + headerLine.remove(remove); + auto headers = headerLine.split(split); + for (const auto &header: headers) { + m_dataHeader.emplace_back(header); + m_dataVector.emplace_back(); + } + + while (!file.atEnd()) { + QString dataLine = file.readLine(); + dataLine.remove(remove); + auto strData = dataLine.split(split); + for (int i = 0; i < strData.size(); ++i) { + m_dataVector[i].push_back(strData[i].toDouble()); + } + } + + InitHeaderComboBox(); + } +} + +void MainWindow::on_primaryColComboBox_currentIndexChanged(int index) { + if (0 <= index) { + ui->threadCountComboBox->setEnabled(true); + ui->exportDataButton->setEnabled(true); + ui->startAnalysisButton->setEnabled(true); + m_primaryIndex = ui->primaryColComboBox->currentData().toULongLong(); + } +} + +void MainWindow::on_dropColComboBox_currentIndexChanged(int index) { + if (index == -1) return; + auto colName = ui->dropColComboBox->currentText(); + auto items = ui->dropListWidget->findItems(colName, Qt::MatchFixedString); + if (!items.size()) { + ui->dropListWidget->addItem(colName); + } + ui->dropColComboBox->setCurrentIndex(-1); +} + +void MainWindow::on_dropListWidget_itemDoubleClicked(QListWidgetItem *item) { + ui->dropListWidget->removeItemWidget(item); + ui->dropListWidget->takeItem(ui->dropListWidget->currentRow()); +} + +void MainWindow::on_threadCountComboBox_currentIndexChanged(int index) { + if (0 <= index) { + m_threadPool.setMaxThreadCount(ui->threadCountComboBox->currentData().toInt()); + } +} + +void MainWindow::on_startAnalysisButton_clicked() { + + auto const &primaryData = m_dataVector.at(m_primaryIndex); + ui->progressBar->setMaximum(m_dataHeader.size() - 1 - ui->dropListWidget->count()); + ui->progressBar->setValue(0); + m_dataTableModel.clear(); + for (int i = 0; i < m_dataHeader.size(); ++i) { + if (i == m_primaryIndex || !ui->dropListWidget->findItems(m_dataHeader[i], Qt::MatchFixedString).empty()) { + continue; + } + auto *pearson = new Pearson(i, primaryData, m_dataVector.at(i), this); + connect(pearson, SIGNAL(result(uint64_t, double)), this, SLOT(PearsonResult(uint64_t, double))); + connect(pearson, SIGNAL(finished()), this, SLOT(PearsonFinished())); + //pearson->run(); + m_threadPool.start(pearson); + } +} + +void MainWindow::on_exportDataButton_clicked() { + auto saveFileName = QFileDialog::getSaveFileName(this, "文件保存", "", "CSV File (*.csv)"); + if (m_dataTableModel.SaveFile(saveFileName)) { + QMessageBox::information(this, "保存成功", "文件保存成功", "确定"); + } else { + QMessageBox::warning(this, "保存失败", "文件保存失败", "确定"); + + } +} + +void MainWindow::PearsonResult(uint64_t index, double pearson) { + qDebug() << m_dataHeader[index] << " " << pearson; + m_dataTableModel.InsertRow(m_dataHeader[index], pearson); +} + +void MainWindow::PearsonFinished() { + // delete sender(); + ui->progressBar->setValue(ui->progressBar->value() + 1); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..8cb6eb4 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,72 @@ +// +// Created by fly on 2021/11/25. +// + +#ifndef PEARSON_MAINWINDOW_H +#define PEARSON_MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include "DataTableModel.h" + + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow { +Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + + ~MainWindow() override; + + void InitWidgetEnableState(); + + void InitHeaderComboBox(); + + void InitThreadCountComboBox(); + + void InitTableView(); + + +protected slots: + + void on_exploreButton_clicked(); + + void on_openFileButton_clicked(); + + void on_primaryColComboBox_currentIndexChanged(int index); + + void on_dropColComboBox_currentIndexChanged(int index); + + void on_threadCountComboBox_currentIndexChanged(int index); + + void on_dropListWidget_itemDoubleClicked(QListWidgetItem *item); + + void on_startAnalysisButton_clicked(); + + void on_exportDataButton_clicked(); + + void PearsonResult(uint64_t index, double pearson); + + void PearsonFinished(); + +private: + Ui::MainWindow *ui; + QThreadPool m_threadPool; + QString m_fileName; + uint64_t m_primaryIndex; + std::vector m_dataHeader; + std::vector> m_dataVector; + DataTableModel m_dataTableModel; +}; + + +#endif //PEARSON_MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..6db0c61 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,149 @@ + + + MainWindow + + + + 0 + 0 + 642 + 600 + + + + Pearson分析工具 v0.1.0 + + + + + + + ③ 选择丢弃数据列(双击删除) + + + + + + 请选择无需分析列 + + + + + + + + + + + + + ① 选择文件并打开 + + + + QLayout::SetDefaultConstraint + + + + + true + + + 请选择文件... + + + + + + + + 0 + 0 + + + + + 24 + 16777215 + + + + ... + + + + + + + 打开文件 + + + + + + + + + + ② 选择主要数据列 + + + + + + 请选择主要数据列 + + + + + + + + + + ④ 分析操作(先选择工作线程数量再分析) + + + + + + 请选择分析线程数 + + + + + + + 开始分析 + + + + + + + 导出数据 + + + + + + + + + + + + + 0 + + + Qt::AlignCenter + + + + + + + + + diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..14d43bd --- /dev/null +++ b/resource.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "icon.ico" \ No newline at end of file