diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f28847b2..2be345b45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,490 +1,492 @@ find_package(SharedMimeInfo REQUIRED) set(KDE_FRONTEND true) set(KDEFRONTEND_DIR kdefrontend) set(BACKEND_DIR backend) set(COMMONFRONTEND_DIR commonfrontend) set(CANTOR_DIR cantor) set(TOOLS_DIR tools) set(CMAKE_AUTOMOC ON) set(SRC_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) # do not process this file set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON) set(GUI_SOURCES ${KDEFRONTEND_DIR}/GuiObserver.cpp ${KDEFRONTEND_DIR}/GuiTools.cpp ${KDEFRONTEND_DIR}/HistoryDialog.cpp ${KDEFRONTEND_DIR}/MainWin.cpp ${KDEFRONTEND_DIR}/SettingsDialog.cpp ${KDEFRONTEND_DIR}/SettingsGeneralPage.cpp ${KDEFRONTEND_DIR}/SettingsWorksheetPage.cpp ${KDEFRONTEND_DIR}/SettingsPage.h ${KDEFRONTEND_DIR}/TemplateHandler.cpp ${KDEFRONTEND_DIR}/ThemeHandler.cpp ${KDEFRONTEND_DIR}/datasources/AsciiOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/BinaryOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/DatabaseManagerDialog.cpp ${KDEFRONTEND_DIR}/datasources/DatabaseManagerWidget.cpp ${KDEFRONTEND_DIR}/datasources/HDF5OptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/FileInfoDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImageOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/ImportDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportFileWidget.cpp ${KDEFRONTEND_DIR}/datasources/ImportFileDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportProjectDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseWidget.cpp ${KDEFRONTEND_DIR}/datasources/NetCDFOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/ROOTOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/FITSOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTErrorWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTSubscriptionWidget.cpp ${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerDialog.cpp ${KDEFRONTEND_DIR}/dockwidgets/BaseDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CursorDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/HypothesisTestDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CustomPointDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ColumnDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/LiveDataDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/MatrixDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/PivotTableDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ProjectDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/SpreadsheetDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYEquationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYDataReductionCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYDifferentiationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYIntegrationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYInterpolationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYSmoothCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFitCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFourierFilterCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFourierTransformCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYConvolutionCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYCorrelationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/WorksheetDock.cpp - ${KDEFRONTEND_DIR}/hypothesisTest/HypothesisTestView.cpp + ${KDEFRONTEND_DIR}/generalTest/HypothesisTestView.cpp + ${KDEFRONTEND_DIR}/generalTest/GeneralTestView.cpp ${KDEFRONTEND_DIR}/matrix/MatrixFunctionDialog.cpp ${KDEFRONTEND_DIR}/pivot/HierarchicalHeaderView.cpp ${KDEFRONTEND_DIR}/pivot/PivotTableView.cpp ${KDEFRONTEND_DIR}/spreadsheet/PlotDataDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/EquidistantValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/ExportSpreadsheetDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/AddSubtractValueDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/DropValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/FunctionValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/RandomValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/SortDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/StatisticsDialog.cpp ${KDEFRONTEND_DIR}/worksheet/ExportWorksheetDialog.cpp ${KDEFRONTEND_DIR}/worksheet/GridDialog.cpp ${KDEFRONTEND_DIR}/worksheet/DynamicPresenterWidget.cpp ${KDEFRONTEND_DIR}/worksheet/PresenterWidget.cpp ${KDEFRONTEND_DIR}/worksheet/SlidingPanel.cpp ${KDEFRONTEND_DIR}/widgets/ConstantsWidget.cpp ${KDEFRONTEND_DIR}/widgets/ThemesComboBox.cpp ${KDEFRONTEND_DIR}/widgets/ThemesWidget.cpp ${KDEFRONTEND_DIR}/widgets/ExpressionTextEdit.cpp ${KDEFRONTEND_DIR}/widgets/FitOptionsWidget.cpp ${KDEFRONTEND_DIR}/widgets/FitParametersWidget.cpp ${KDEFRONTEND_DIR}/widgets/FunctionsWidget.cpp ${KDEFRONTEND_DIR}/widgets/LabelWidget.cpp ${KDEFRONTEND_DIR}/widgets/DatapickerImageWidget.cpp ${KDEFRONTEND_DIR}/widgets/DatapickerCurveWidget.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditWidget.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditNewKeywordDialog.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditAddUnitDialog.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditDialog.cpp ${KDEFRONTEND_DIR}/widgets/ResizableTextEdit.cpp ${KDEFRONTEND_DIR}/widgets/MQTTWillSettingsWidget.cpp ) set(UI_SOURCES ${KDEFRONTEND_DIR}/ui/constantswidget.ui ${KDEFRONTEND_DIR}/ui/functionswidget.ui ${KDEFRONTEND_DIR}/ui/fitoptionswidget.ui ${KDEFRONTEND_DIR}/ui/fitparameterswidget.ui ${KDEFRONTEND_DIR}/ui/labelwidget.ui ${KDEFRONTEND_DIR}/ui/settingsgeneralpage.ui ${KDEFRONTEND_DIR}/ui/settingsworksheetpage.ui ${KDEFRONTEND_DIR}/ui/settingsprintingpage.ui ${KDEFRONTEND_DIR}/ui/mqttwillsettingswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/asciioptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/binaryoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/databasemanagerwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/hdf5optionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/imageoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importfilewidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importprojectwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importsqldatabasewidget.ui ${KDEFRONTEND_DIR}/ui/datasources/netcdfoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/rootoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/fitsoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqtterrorwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqttsubscriptionwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqttconnectionmanagerwidget.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cursordock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotlegenddock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/columndock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/custompointdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/hypothesistestdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/livedatadock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/notedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/matrixdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/pivottabledock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/projectdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/spreadsheetdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycurvedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xydatareductioncurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xydifferentiationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyintegrationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyinterpolationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xysmoothcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfitcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfourierfiltercurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfouriertransformcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyconvolutioncurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycorrelationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyequationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/worksheetdock.ui ${KDEFRONTEND_DIR}/ui/matrix/matrixfunctionwidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/plotdatawidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/equidistantvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/exportspreadsheetwidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/addsubtractvaluewidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/dropvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/functionvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/randomvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/sortdialogwidget.ui ${KDEFRONTEND_DIR}/ui/worksheet/exportworksheetwidget.ui ${KDEFRONTEND_DIR}/ui/datapickerimagewidget.ui ${KDEFRONTEND_DIR}/ui/datapickercurvewidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditwidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditnewkeywordwidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditaddunitwidget.ui ) set(BACKEND_SOURCES ${BACKEND_DIR}/core/Folder.cpp ${BACKEND_DIR}/core/AbstractAspect.cpp ${BACKEND_DIR}/core/AbstractColumn.cpp ${BACKEND_DIR}/core/AbstractColumnPrivate.cpp ${BACKEND_DIR}/core/abstractcolumncommands.cpp ${BACKEND_DIR}/core/AbstractFilter.cpp ${BACKEND_DIR}/core/AbstractSimpleFilter.cpp ${BACKEND_DIR}/core/column/Column.cpp ${BACKEND_DIR}/core/column/ColumnPrivate.cpp ${BACKEND_DIR}/core/column/ColumnStringIO.cpp ${BACKEND_DIR}/core/column/columncommands.cpp ${BACKEND_DIR}/core/Project.cpp ${BACKEND_DIR}/core/AbstractPart.cpp ${BACKEND_DIR}/core/Workbook.cpp ${BACKEND_DIR}/core/AspectTreeModel.cpp ${BACKEND_DIR}/core/datatypes/SimpleCopyThroughFilter.h ${BACKEND_DIR}/core/datatypes/Double2DateTimeFilter.h ${BACKEND_DIR}/core/datatypes/Double2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/Double2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/Double2MonthFilter.h ${BACKEND_DIR}/core/datatypes/Double2StringFilter.cpp ${BACKEND_DIR}/core/datatypes/Integer2DateTimeFilter.h ${BACKEND_DIR}/core/datatypes/Integer2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/Integer2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/Integer2MonthFilter.h ${BACKEND_DIR}/core/datatypes/Integer2StringFilter.h ${BACKEND_DIR}/core/datatypes/String2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/String2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/String2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/String2MonthFilter.h ${BACKEND_DIR}/core/datatypes/String2DateTimeFilter.cpp ${BACKEND_DIR}/core/datatypes/DateTime2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/DateTime2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/DateTime2StringFilter.cpp ${BACKEND_DIR}/core/datatypes/Month2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/Month2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/DayOfWeek2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/DayOfWeek2IntegerFilter.h ${BACKEND_DIR}/core/plugin/PluginLoader.cpp ${BACKEND_DIR}/core/plugin/PluginManager.cpp ${BACKEND_DIR}/datasources/AbstractDataSource.cpp ${BACKEND_DIR}/datasources/LiveDataSource.cpp ${BACKEND_DIR}/datasources/MQTTClient.cpp ${BACKEND_DIR}/datasources/MQTTSubscription.cpp ${BACKEND_DIR}/datasources/MQTTTopic.cpp ${BACKEND_DIR}/datasources/filters/AbstractFileFilter.cpp ${BACKEND_DIR}/datasources/filters/AsciiFilter.cpp ${BACKEND_DIR}/datasources/filters/BinaryFilter.cpp ${BACKEND_DIR}/datasources/filters/HDF5Filter.cpp ${BACKEND_DIR}/datasources/filters/ImageFilter.cpp ${BACKEND_DIR}/datasources/filters/JsonFilter.cpp ${BACKEND_DIR}/datasources/filters/NetCDFFilter.cpp ${BACKEND_DIR}/datasources/filters/NgspiceRawAsciiFilter.cpp ${BACKEND_DIR}/datasources/filters/NgspiceRawBinaryFilter.cpp ${BACKEND_DIR}/datasources/filters/FITSFilter.cpp ${BACKEND_DIR}/datasources/filters/QJsonModel.cpp ${BACKEND_DIR}/datasources/filters/ROOTFilter.cpp ${BACKEND_DIR}/datasources/projects/ProjectParser.cpp ${BACKEND_DIR}/datasources/projects/LabPlotProjectParser.cpp ${BACKEND_DIR}/gsl/ExpressionParser.cpp - ${BACKEND_DIR}/hypothesisTest/HypothesisTest.cpp + ${BACKEND_DIR}/generalTest/HypothesisTest.cpp + ${BACKEND_DIR}/generalTest/GeneralTest.cpp ${BACKEND_DIR}/matrix/Matrix.cpp ${BACKEND_DIR}/matrix/matrixcommands.cpp ${BACKEND_DIR}/matrix/MatrixModel.cpp ${BACKEND_DIR}/pivot/PivotTable.cpp ${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp ${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp ${BACKEND_DIR}/lib/XmlStreamReader.cpp ${BACKEND_DIR}/note/Note.cpp ${BACKEND_DIR}/worksheet/WorksheetElement.cpp ${BACKEND_DIR}/worksheet/TextLabel.cpp ${BACKEND_DIR}/worksheet/Worksheet.cpp ${BACKEND_DIR}/worksheet/WorksheetElementContainer.cpp ${BACKEND_DIR}/worksheet/WorksheetElementGroup.cpp ${BACKEND_DIR}/worksheet/TreeModel.cpp ${BACKEND_DIR}/worksheet/plots/AbstractPlot.cpp ${BACKEND_DIR}/worksheet/plots/AbstractCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/PlotArea.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Axis.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlot.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlotLegend.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Histogram.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CustomPoint.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Symbol.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYAnalysisCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYEquationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYDataReductionCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYDifferentiationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYIntegrationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYInterpolationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYSmoothCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFitCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierFilterCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierTransformCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYConvolutionCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYCorrelationCurve.cpp ${BACKEND_DIR}/lib/SignallingUndoCommand.cpp ${BACKEND_DIR}/datapicker/DatapickerPoint.cpp ${BACKEND_DIR}/datapicker/DatapickerImage.cpp ${BACKEND_DIR}/datapicker/Datapicker.cpp ${BACKEND_DIR}/datapicker/Transform.cpp ${BACKEND_DIR}/datapicker/ImageEditor.cpp ${BACKEND_DIR}/datapicker/Segment.cpp ${BACKEND_DIR}/datapicker/Segments.cpp ${BACKEND_DIR}/datapicker/DatapickerCurve.cpp ) IF (ENABLE_LIBORIGIN) list(APPEND BACKEND_SOURCES ${BACKEND_DIR}/datasources/projects/OriginProjectParser.cpp) ENDIF () set(NSL_SOURCES ${BACKEND_DIR}/nsl/nsl_conv.c ${BACKEND_DIR}/nsl/nsl_corr.c ${BACKEND_DIR}/nsl/nsl_dft.c ${BACKEND_DIR}/nsl/nsl_diff.c ${BACKEND_DIR}/nsl/nsl_filter.c ${BACKEND_DIR}/nsl/nsl_fit.c ${BACKEND_DIR}/nsl/nsl_geom.c ${BACKEND_DIR}/nsl/nsl_geom_linesim.c ${BACKEND_DIR}/nsl/nsl_int.c ${BACKEND_DIR}/nsl/nsl_interp.c ${BACKEND_DIR}/nsl/nsl_math.c ${BACKEND_DIR}/nsl/nsl_sf_basic.c ${BACKEND_DIR}/nsl/nsl_sf_kernel.c ${BACKEND_DIR}/nsl/nsl_sf_poly.c ${BACKEND_DIR}/nsl/nsl_sf_stats.c ${BACKEND_DIR}/nsl/nsl_sf_window.c ${BACKEND_DIR}/nsl/nsl_smooth.c ${BACKEND_DIR}/nsl/nsl_sort.c ${BACKEND_DIR}/nsl/nsl_stats.c ) IF (NOT MSVC_FOUND) IF (NOT LIBCERF_FOUND) list(APPEND NSL_SOURCES ${BACKEND_DIR}/nsl/Faddeeva.c ) ENDIF () ENDIF () set(COMMONFRONTEND_SOURCES ${COMMONFRONTEND_DIR}/matrix/MatrixView.cpp ${COMMONFRONTEND_DIR}/note/NoteView.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetCommentsHeaderModel.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetHeaderView.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetItemDelegate.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetView.cpp ${COMMONFRONTEND_DIR}/workbook/WorkbookView.cpp ${COMMONFRONTEND_DIR}/worksheet/WorksheetView.cpp ${COMMONFRONTEND_DIR}/ProjectExplorer.cpp ${COMMONFRONTEND_DIR}/core/PartMdiView.cpp ${COMMONFRONTEND_DIR}/widgets/TreeViewComboBox.cpp ${COMMONFRONTEND_DIR}/widgets/qxtspanslider.cpp ${COMMONFRONTEND_DIR}/widgets/MemoryWidget.cpp ${COMMONFRONTEND_DIR}/widgets/DateTimeSpinBox.cpp ${COMMONFRONTEND_DIR}/datapicker/DatapickerView.cpp ${COMMONFRONTEND_DIR}/datapicker/DatapickerImageView.cpp ) IF (${Cantor_FOUND}) set(CANTOR_SOURCES ${KDEFRONTEND_DIR}/dockwidgets/CantorWorksheetDock.cpp ${BACKEND_DIR}/cantorWorksheet/VariableParser.cpp ${BACKEND_DIR}/cantorWorksheet/CantorWorksheet.cpp ${COMMONFRONTEND_DIR}/cantorWorksheet/CantorWorksheetView.cpp ) set(CANTOR_UI_SOURCES ${KDEFRONTEND_DIR}/ui/dockwidgets/cantorworksheetdock.ui) set(UI_SOURCES ${UI_SOURCES} ${CANTOR_UI_SOURCES}) ELSE () set(CANTOR_SOURCES "") ENDIF () set(TOOLS_SOURCES ${TOOLS_DIR}/EquationHighlighter.cpp ${TOOLS_DIR}/ImageTools.cpp ${TOOLS_DIR}/TeXRenderer.cpp ) bison_target(GslParser ${BACKEND_DIR}/gsl/parser.y ${CMAKE_CURRENT_BINARY_DIR}/gsl_parser.c ) set(GENERATED_SOURCES ${BISON_GslParser_OUTPUTS} ) set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON) ############################################################################## INCLUDE_DIRECTORIES( . ${BACKEND_DIR}/gsl ${GSL_INCLUDE_DIR} ) set( LABPLOT_SRCS ${GUI_SOURCES} ) ki18n_wrap_ui( LABPLOT_SRCS ${UI_SOURCES} ) # see also QT_MINIMUM_VERSION add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50500) # static library add_library( labplot2lib STATIC ${LABPLOT_SRCS} ${BACKEND_SOURCES} ${NSL_SOURCES} ${CANTOR_SOURCES} ${DATASOURCES_SOURCES} ${COMMONFRONTEND_SOURCES} ${TOOLS_SOURCES} ${GENERATED_SOURCES} ${QTMOC_HDRS} ) # set_property(TARGET ${objlib} PROPERTY POSITION_INDEPENDENT_CODE 1) target_link_libraries( labplot2lib KF5::Archive KF5::Completion KF5::ConfigCore KF5::Crash KF5::I18n KF5::IconThemes KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::TextWidgets KF5::XmlGui Qt5::Svg Qt5::Core Qt5::Network Qt5::PrintSupport Qt5::Sql ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES} ) IF (Qt5SerialPort_FOUND) target_link_libraries( labplot2lib Qt5::SerialPort ) ENDIF () IF (Qt5Mqtt_FOUND) target_link_libraries( labplot2lib Qt5::Mqtt ) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries( labplot2lib KF5::SyntaxHighlighting ) ENDIF () #TODO: KF5::NewStuff IF (Cantor_FOUND) target_link_libraries( labplot2lib Cantor::cantorlibs KF5::Service KF5::Parts) ENDIF () IF (HDF5_FOUND) target_link_libraries( labplot2lib ${HDF5_LIBRARIES} ) ENDIF () IF (FFTW3_FOUND) target_link_libraries( labplot2lib ${FFTW3_LIBRARIES} ) ENDIF () IF (netCDF_FOUND) target_link_libraries( labplot2lib netcdf ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries( labplot2lib ${CFITSIO_LIBRARIES} ) ENDIF () IF (LIBCERF_FOUND) target_link_libraries( labplot2lib ${LIBCERF_LIBRARY} ) ENDIF () IF (ZLIB_FOUND AND LZ4_FOUND) target_link_libraries( labplot2lib ${ZLIB_LIBRARY} ${LZ4_LIBRARY} ) ENDIF () IF (ENABLE_LIBORIGIN) target_link_libraries( labplot2lib liborigin-static ) ENDIF () IF (WIN32) target_link_libraries( labplot2lib ${PSAPI} ) ENDIF () # icons for the executable and project files on Windows and Mac OS X set(LABPLOT_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-apps-labplot2.png ) set(LML_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-application-x-labplot2.png ) # main executable set(LABPLOT_SOURCE ${KDEFRONTEND_DIR}/LabPlot.cpp) # create icon files on WIN/MAC and add icons to the executable IF (${ECM_VERSION} VERSION_GREATER "5.48.0") # creates LABPLOT_ICONS.ico/LABPLOT_ICONS.icns ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS} OUTFILE_BASENAME LABPLOT_ICONS) ELSE () # creates LABPLOT_SOURCE.ico/LABPLOT_SOURCE.icns ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS}) ENDIF () # create LML_ICONS.icns on MACOSX IF (APPLE AND ${ECM_VERSION} VERSION_GREATER "5.48.0") ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LML_ICONS} OUTFILE_BASENAME LML_ICONS) ENDIF () add_executable(labplot2 ${LABPLOT_SOURCE}) target_link_libraries(labplot2 labplot2lib) ############## installation ################################ install( TARGETS labplot2 DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS} ) install( FILES ${KDEFRONTEND_DIR}/labplot2ui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/${PROJECT_NAME} ) install( FILES ${KDEFRONTEND_DIR}/splash.png ${KDEFRONTEND_DIR}/labplot2.ico ${CMAKE_CURRENT_SOURCE_DIR}/../icons/application-x-labplot2.ico DESTINATION ${DATA_INSTALL_DIR}/${PROJECT_NAME} ) install( PROGRAMS org.kde.labplot2.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES labplot2.xml DESTINATION ${XDG_MIME_INSTALL_DIR} ) install( FILES labplot2_themes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) update_xdg_mimetypes( ${XDG_MIME_INSTALL_DIR} ) diff --git a/src/backend/generalTest/GeneralTest.cpp b/src/backend/generalTest/GeneralTest.cpp new file mode 100644 index 000000000..6b7e8e585 --- /dev/null +++ b/src/backend/generalTest/GeneralTest.cpp @@ -0,0 +1,484 @@ +/*************************************************************************** + File : GeneralTest.cpp + Project : LabPlot + Description : Doing Hypothesis-Test on data provided + -------------------------------------------------------------------- + Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301 USA * + * * + ***************************************************************************/ + +#include "GeneralTest.h" +#include "kdefrontend/generalTest/HypothesisTestView.h" +#include "backend/spreadsheet/Spreadsheet.h" +#include "backend/core/column/Column.h" +#include "backend/lib/macros.h" + +//#include +//#include +//#include +#include +#include +//#include +//#include +//#include + +#include + +#include +#include +extern "C" { +#include "backend/nsl/nsl_stats.h" +} + +GeneralTest::GeneralTest(const QString &name) : AbstractPart(name), + m_summaryLayout(new QVBoxLayout()) { + + for (int i = 0; i < 10; i++) { + m_resultLine[i] = new QLabel(); + m_summaryLayout->addWidget(m_resultLine[i]); + } +} + +GeneralTest::~GeneralTest() { +} + +void GeneralTest::setDataSourceType(DataSourceType type) { + if (type != m_dataSourceType) + m_dataSourceType = type; +} + +GeneralTest::DataSourceType GeneralTest::dataSourceType() const { + return m_dataSourceType; +} + +void GeneralTest::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) { + m_dataSourceSpreadsheet = spreadsheet; + for (auto* col : m_dataSourceSpreadsheet->children()) + m_allColumns << col->name(); +} + +QString GeneralTest::testName() { + return m_currTestName; +} + +QString GeneralTest::statsTable() { + return m_statsTable; +} + +QMap GeneralTest::tooltips() { + return m_tooltips; +} + +QVBoxLayout* GeneralTest::summaryLayout() { + return m_summaryLayout; +} + + +void GeneralTest::setColumns(QStringList cols) { + m_columns.clear(); + Column* column = new Column("column"); + for (QString col : cols) { + if (!cols.isEmpty()) { + column = m_dataSourceSpreadsheet->column(col); + m_columns.append(column); + } + } + delete[] column; +} + +void GeneralTest::setColumns(const QVector &cols) { + m_columns = cols; +} + + +/******************************************************************************************************************** +* Protected functions implementations [Helper Functions] +********************************************************************************************************************/ + +QString GeneralTest::round(QVariant number, int precision) { + if (number.userType() == QMetaType::Double || number.userType() == QMetaType::Float) { + double multiplierPrecision = gsl_pow_int(10, precision); + int tempNum = int(number.toDouble()*multiplierPrecision*10); + + if (tempNum % 10 < 5) + return QString::number((tempNum/10) / multiplierPrecision); + else + return QString::number((tempNum/10 + 1) / multiplierPrecision); + } + return i18n("%1", number.toString()); +} + + +bool GeneralTest::isNumericOrInteger(Column* column) { + return (column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer); +} + +GeneralTest::ErrorType GeneralTest::findStats(const Column* column, int& count, double& sum, double& mean, double& std) { + sum = 0; + mean = 0; + std = 0; + + count = column->rowCount(); + for (int i = 0; i < count; i++) { + double row = column->valueAt(i); + if ( std::isnan(row)) { + count = i; + break; + } + sum += row; + } + + if (count < 1) + return GeneralTest::ErrorEmptyColumn; + + mean = sum / count; + + for (int i = 0; i < count; i++) { + double row = column->valueAt(i); + std += gsl_pow_2( (row - mean)); + } + + if (count > 1) + std = std / (count-1); + std = sqrt(std); + + return GeneralTest::NoError; +} + +GeneralTest::ErrorType GeneralTest::findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std) { + sum = 0; + mean = 0; + std = 0; + + int count1 = column1->rowCount(); + int count2 = column2->rowCount(); + + count = qMin(count1, count2); + double cell1, cell2; + for (int i = 0; i < count; i++) { + cell1 = column1->valueAt(i); + cell2 = column2->valueAt(i); + + if (std::isnan(cell1) || std::isnan(cell2)) { + if (std::isnan(cell1) && std::isnan(cell2)) + count = i; + else + return GeneralTest::ErrorUnqualSize; + break; + } + + sum += cell1 - cell2; + } + + if (count < 1) + return GeneralTest::ErrorEmptyColumn; + + mean = sum / count; + + double row; + for (int i = 0; i < count; i++) { + cell1 = column1->valueAt(i); + cell2 = column2->valueAt(i); + row = cell1 - cell2; + std += gsl_pow_2( (row - mean)); + } + + if (count > 1) + std = std / (count-1); + + std = sqrt(std); + return GeneralTest::NoError; +} + +void GeneralTest::countPartitions(Column* column, int& np, int& totalRows) { + totalRows = column->rowCount(); + np = 0; + QString cellValue; + QMap discoveredCategoricalVar; + + AbstractColumn::ColumnMode originalColMode = column->columnMode(); + column->setColumnMode(AbstractColumn::Text); + + for (int i = 0; i < totalRows; i++) { + cellValue = column->textAt(i); + + if (cellValue.isEmpty()) { + totalRows = i; + break; + } + + if (discoveredCategoricalVar[cellValue]) + continue; + + discoveredCategoricalVar[cellValue] = true; + np++; + } + column->setColumnMode(originalColMode); +} + +GeneralTest::ErrorType GeneralTest::findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows) { + Column* columns[] = {column1, column2}; + + for (int i = 0; i < np; i++) { + n[i] = 0; + sum[i] = 0; + mean[i] = 0; + std[i] = 0; + } + + AbstractColumn::ColumnMode originalColMode = columns[0]->columnMode(); + columns[0]->setColumnMode(AbstractColumn::Text); + + int partitionNumber = 1; + for (int i = 0; i < totalRows; i++) { + QString name = columns[0]->textAt(i); + double value = columns[1]->valueAt(i); + + if (std::isnan(value)) { + columns[0]->setColumnMode(originalColMode); + return GeneralTest::ErrorUnqualSize; + } + + if (colName[name] == 0) { + colName[name] = partitionNumber; + partitionNumber++; + } + + n[colName[name]-1]++; + sum[colName[name]-1] += value; + } + + for (int i = 0; i < np; i++) + mean[i] = sum[i] / n[i]; + + for (int i = 0; i < totalRows; i++) { + QString name = columns[0]->textAt(i); + double value = columns[1]->valueAt(i); + + std[colName[name]-1] += gsl_pow_2( (value - mean[colName[name]-1])); + } + + for (int i = 0; i < np; i++) { + if (n[i] > 1) + std[i] = std[i] / (n[i] - 1); + std[i] = sqrt(std[i]); + } + + columns[0]->setColumnMode(originalColMode); + if (isNumericOrInteger(columns[0])) { + + } + + return GeneralTest::NoError; +} + +QString GeneralTest::getHtmlTable(int row, int column, QVariant* rowMajor) { + if (row < 1 || column < 1) + return QString(); + + QString table; + table = "" + "" + " "; + + QString bg = "tg-0pky"; + bool pky = true; + + QString element; + table += " "; + for (int j = 0; j < column; j++) { + element = rowMajor[j].toString(); + table += " "; + } + table += " "; + + if (pky) + bg = "tg-0pky"; + else + bg = "tg-btxf"; + pky = !pky; + + for (int i = 1; i < row; i++) { + table += " "; + + QString element = round(rowMajor[i*column]); + table += " "; + for (int j = 1; j < column; j++) { + element = round(rowMajor[i*column+j]); + table += " "; + } + + table += " "; + if (pky) + bg = "tg-0pky"; + else + bg = "tg-btxf"; + pky = !pky; + } + table += "
" + i18n("%1", element) + "
" + i18n("%1", element) + "" + i18n("%1", element) + "
"; + + return table; +} + +QString GeneralTest::getHtmlTable3(const QList& rowMajor) { + m_tooltips.clear(); + int rowMajorSize = rowMajor.size(); + + if (rowMajorSize == 0) + return QString(); + + QString table; + table = ""; + + table += ""; + + table += " "; + int prevLevel = 0; + for (int i = 0; i < rowMajorSize; i++) { + Cell* currCell = rowMajor[i]; + if (currCell->level != prevLevel) { + table += " "; + table += " "; + prevLevel = currCell->level; + } + QString cellStartTag = ""; + table += "
isHeader) { + cellStartTag = "" + + i18n("%1", currCell->data) + + cellEndTag; + + if (!currCell->tooltip.isEmpty()) + m_tooltips.insert(currCell->data, currCell->tooltip); + } + table += "
"; + return table; +} + +QString GeneralTest::getLine(const QString& msg, const QString& color) { + return "

" + i18n("%1", msg) + "

"; +} + +void GeneralTest::printLine(const int& index, const QString& msg, const QString& color) { + if (index < 0 || index >= 10) + return; + + m_resultLine[index]->setText(getLine(msg, color)); + return; +} + +void GeneralTest::printTooltip(const int &index, const QString &msg) { + if (index < 0 || index >= 10) + return; + + m_resultLine[index]->setToolTip(i18n("%1", msg)); +} + +void GeneralTest::printError(const QString& errorMsg) { + printLine(0, errorMsg, "red"); +} + + +/******************************************************************************************************************** +* virtual functions implementations +********************************************************************************************************************/ + +/*! + Saves as XML. + */ +void GeneralTest::save(QXmlStreamWriter* writer) const { + writer->writeStartElement("GeneralTest"); + writeBasicAttributes(writer); + writeCommentElement(writer); + + writer->writeEndElement(); +} + +/*! + Loads from XML. +*/ +bool GeneralTest::load(XmlStreamReader* reader, bool preview) { + Q_UNUSED(preview); + if (!readBasicAttributes(reader)) + return false; + + return !reader->hasError(); +} + +Spreadsheet *GeneralTest::dataSourceSpreadsheet() const { + return m_dataSourceSpreadsheet; +} + + +bool GeneralTest::exportView() const { + return true; +} + +bool GeneralTest::printView() { + return true; +} + +bool GeneralTest::printPreview() const { + return true; +} + +/*! Constructs a primary view on me. + This method may be called multiple times during the life time of an Aspect, or it might not get + called at all. Aspects must not depend on the existence of a view for their operation. +*/ +//QWidget* GeneralTest::view() const { +// if (!m_partView) { +// m_view = new HypothesisTestView(const_cast(this)); +// m_partView = m_view; +// } +// return m_partView; +//} + +/*! + Returns a new context menu. The caller takes ownership of the menu. +*/ +QMenu* GeneralTest::createContextMenu() { + QMenu* menu = AbstractPart::createContextMenu(); + // Q_ASSERT(menu); + // emit requestProjectContextMenu(menu); + return menu; +} diff --git a/src/backend/hypothesisTest/HypothesisTestPrivate.h b/src/backend/generalTest/GeneralTest.h similarity index 63% rename from src/backend/hypothesisTest/HypothesisTestPrivate.h rename to src/backend/generalTest/GeneralTest.h index a4a558345..9f3134ae3 100644 --- a/src/backend/hypothesisTest/HypothesisTestPrivate.h +++ b/src/backend/generalTest/GeneralTest.h @@ -1,129 +1,135 @@ /*************************************************************************** - File : HypothesisTestPrivate.h + File : GeneralTest.h Project : LabPlot - Description : Private members of Hypothesis Test + Description : Doing Hypothesis-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ -#ifndef HYPOTHESISTESTPRIVATE_H -#define HYPOTHESISTESTPRIVATE_H +#ifndef GENERALTEST_H +#define GENERALTEST_H -#include +#include "backend/core/AbstractPart.h" +#include "backend/lib/macros.h" -class QStandardItemModel; +class HypothesisTestView; +class Spreadsheet; +class QString; +class Column; +class QVBoxLayout; +class QLabel; + +class GeneralTest : public AbstractPart { + Q_OBJECT -class HypothesisTestPrivate { public: - explicit HypothesisTestPrivate(HypothesisTest*); - virtual ~HypothesisTestPrivate(); + explicit GeneralTest(const QString& name); + ~GeneralTest() override; - struct Node { - QString data; - int spanCount; - int level; - - QVector children; - void addChild(Node* child) { - children.push_back(child); - } - }; + enum DataSourceType {DataSourceSpreadsheet, DataSourceDatabase}; struct Cell { QString data; int level; bool isHeader; QString tooltip; int rowSpanCount; int columnSpanCount; Cell(QVariant data = "", int level = 0, bool isHeader = false, QString tooltip = "", int rowSpanCount = 1, int columnSpanCount = 1) { this->data = data.toString(); this->level = level; this->isHeader = isHeader; this->tooltip = tooltip; this->rowSpanCount = rowSpanCount; this->columnSpanCount = columnSpanCount; } }; enum ErrorType {ErrorUnqualSize, ErrorEmptyColumn, NoError}; - QString name() const; + void setDataSourceType(DataSourceType type); + DataSourceType dataSourceType() const; void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); + Spreadsheet* dataSourceSpreadsheet() const; + + void setColumns(const QVector& cols); void setColumns(QStringList cols); - void performTwoSampleIndependentTest(HypothesisTest::Test::Type test, bool categoricalVariable = false, bool equalVariance = true); - void performTwoSamplePairedTest(HypothesisTest::Test::Type test); - void performOneSampleTest(HypothesisTest::Test::Type test); - void performOneWayAnova(); - void performTwoWayAnova(); - void performSpearmanCorrelation(); - - void performLeveneTest(bool categoricalVariable); - - HypothesisTest* const q; - HypothesisTest::DataSourceType dataSourceType{HypothesisTest::DataSourceSpreadsheet}; - Spreadsheet* dataSourceSpreadsheet{nullptr}; - QVector columns; - QStringList allColumns; - -// int rowCount{0}; -// int columnCount{0}; - QString currTestName{"Result Table"}; - double populationMean; - double significanceLevel; - QString statsTable; - HypothesisTest::Test::Tail tailType; - QList pValue; - QList statisticValue; - - QVBoxLayout* summaryLayout{nullptr}; - QLabel* resultLine[10]; - QMap tooltips; - -private: - bool isNumericOrInteger(Column* column); + QStringList allColumns(); + QString testName(); + QString statsTable(); + QMap tooltips(); + + QVBoxLayout* summaryLayout(); + //virtual methods + // QIcon icon() const override; + QMenu* createContextMenu() override; +// QWidget* view() const override; + + bool exportView() const override; + bool printView() override; + bool printPreview() const override; + + void save(QXmlStreamWriter*) const override; + bool load(XmlStreamReader*, bool preview) override; + +signals: + void changed(); + void requestProjectContextMenu(QMenu*); + void dataSourceTypeChanged(GeneralTest::DataSourceType); + void dataSourceSpreadsheetChanged(Spreadsheet*); + +protected: + DataSourceType m_dataSourceType{GeneralTest::DataSourceSpreadsheet}; + Spreadsheet* m_dataSourceSpreadsheet{nullptr}; + QVector m_columns; + QStringList m_allColumns; + + QString m_currTestName{"Result Table"}; + QString m_statsTable; + + QVBoxLayout* m_summaryLayout{nullptr}; + QLabel* m_resultLine[10]; + QMap m_tooltips; + + bool isNumericOrInteger(Column* column); QString round(QVariant number, int precision = 3); void countPartitions(Column* column, int& np, int& totalRows); ErrorType findStats(const Column* column,int& count, double& sum, double& mean, double& std); ErrorType findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std); ErrorType findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows); - double getPValue(const HypothesisTest::Test::Type& test, double& value, const QString& col1Name, const QString& col2name, const double mean, const double sp, const int df); - int setSpanValues(Node* root, int& totalLevels); - QString getHtmlHeader(Node* root); - QString getHtmlTable2(int rowCount, int columnCount, Node* columnHeaderRoot, QVariant* rowMajor); QString getHtmlTable(int row, int column, QVariant* rowMajor); QString getHtmlTable3(const QList& rowMajor); - QString getLine(const QString& msg, const QString& color = "black"); void printLine(const int& index, const QString& msg, const QString& color = "black"); void printTooltip(const int& index, const QString& msg); void printError(const QString& errorMsg); bool m_dbCreated{false}; + mutable HypothesisTestView* m_view{nullptr}; }; -#endif +#endif // GeneralTest_H diff --git a/src/backend/generalTest/HypothesisTest.cpp b/src/backend/generalTest/HypothesisTest.cpp new file mode 100644 index 000000000..1d3b81bfc --- /dev/null +++ b/src/backend/generalTest/HypothesisTest.cpp @@ -0,0 +1,1133 @@ +/*************************************************************************** + File : HypothesisTest.cpp + Project : LabPlot + Description : Doing Hypothesis-Test on data provided + -------------------------------------------------------------------- + Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301 USA * + * * + ***************************************************************************/ + +#include "HypothesisTest.h" +#include "kdefrontend/generalTest/HypothesisTestView.h" +#include "backend/spreadsheet/Spreadsheet.h" +#include "backend/core/column/Column.h" +#include "backend/lib/macros.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern "C" { +#include "backend/nsl/nsl_stats.h" +} + +HypothesisTest::HypothesisTest(const QString &name) : GeneralTest (name) { +} + +HypothesisTest::~HypothesisTest() { +} + +void HypothesisTest::setPopulationMean(QVariant m_populationMean) { + m_populationMean = m_populationMean.toDouble(); +} + +void HypothesisTest::setSignificanceLevel(QVariant alpha) { + m_significanceLevel = alpha.toDouble(); +} + +void HypothesisTest::performTest(Test test, bool categoricalVariable, bool equalVariance) { + m_tailType = test.tail; + m_pValue.clear(); + m_statisticValue.clear(); + m_statsTable = ""; + m_tooltips.clear(); + for (int i = 0; i < 10; i++) + m_resultLine[i]->clear(); + + switch (test.subtype) { + case HypothesisTest::Test::SubType::TwoSampleIndependent: { + m_currTestName = "

" + i18n("Two Sample Independent Test") + "

"; + performTwoSampleIndependentTest(test.type, categoricalVariable, equalVariance); + break; + } + case HypothesisTest::Test::SubType::TwoSamplePaired: + m_currTestName = "

" + i18n("Two Sample Paired Test") + "

"; + performTwoSamplePairedTest(test.type); + break; + case HypothesisTest::Test::SubType::OneSample: { + m_currTestName = "

" + i18n("One Sample Test") + "

"; + performOneSampleTest(test.type); + break; + } + case HypothesisTest::Test::SubType::OneWay: { + m_currTestName = "

" + i18n("One Way Anova") + "

"; + performOneWayAnova(); + break; + } + case HypothesisTest::Test::SubType::TwoWay: { + m_currTestName = "

" + i18n("Two Way Anova") + "

"; + performTwoWayAnova(); + break; + } + case HypothesisTest::Test::SubType::NoneSubType: + break; + } + + emit changed(); +} + +void HypothesisTest::performLeveneTest(bool categoricalVariable) { + m_currTestName = "

" + i18n("Levene Test for Equality of Variance") + "

"; + m_performLeveneTest(categoricalVariable); + emit changed(); +} + +QList HypothesisTest::statisticValue() { + return m_statisticValue; +} + +QList HypothesisTest::pValue() { + return m_pValue; +} + +/****************************************************************************** + * Private Implementations + * ****************************************************************************/ +//TODO: backend of z test; +//TODO: add tooltip to tables. (currently it is not possible to use with QTextDocument); +//TODO: use https://www.gnu.org/software/gsl/doc/html/statistics.html for basic statistic calculations + + +/**************************Two Sample Independent *************************************/ + +void HypothesisTest::performTwoSampleIndependentTest(HypothesisTest::Test::Type test, bool categoricalVariable, bool equalVariance) { + if (m_columns.size() != 2) { + printError("Inappropriate number of m_columns selected"); + return; + } + + int n[2]; + double sum[2], mean[2], std[2]; + + QString col1Name = m_columns[0]->name(); + QString col2Name = m_columns[1]->name(); + + if (!categoricalVariable && isNumericOrInteger(m_columns[0])) { + for (int i = 0; i < 2; i++) { + findStats(m_columns[i], n[i], sum[i], mean[i], std[i]); + if (n[i] == 0) { + printError("Atleast two values should be there in every column"); + return; + } + if (std[i] == 0) { + printError(i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", m_columns[i]->name())); + return; + } + } + } else { + QMap colName; + QString baseColName; + int np; + int totalRows; + + countPartitions(m_columns[0], np, totalRows); + if (np != 2) { + printError( i18n("Number of Categorical Variable in Column %1 is not equal to 2", m_columns[0]->name())); + return; + } + + if (isNumericOrInteger(m_columns[0])) + baseColName = m_columns[0]->name(); + + ErrorType errorCode = findStatsCategorical(m_columns[0], m_columns[1], n, sum, mean, std, colName, np, totalRows); + + switch (errorCode) { + case ErrorUnqualSize: { + printError( i18n("Unequal size between Column %1 and Column %2", m_columns[0]->name(), m_columns[1]->name())); + return; + } + case ErrorEmptyColumn: { + printError("At least one of selected column is empty"); + + return; + } + case NoError: + break; + } + + QMapIterator i(colName); + while (i.hasNext()) { + i.next(); + if (i.value() == 1) + col1Name = baseColName + " " + i.key(); + else + col2Name = baseColName + " " + i.key(); + } + } + + QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", + col1Name, n[0], sum[0], mean[0], std[0], + col2Name, n[1], sum[1], mean[1], std[1] + }; + + m_statsTable = getHtmlTable(3, 5, rowMajor); + + for (int i = 0; i < 2; i++) { + if (n[i] == 0) { + printError("Atleast two values should be there in every column"); + return; + } + if (std[i] == 0) { + printError( i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", m_columns[i]->name())); + return; + } + } + + QString testName; + int df = 0; + double sp = 0; + + switch (test) { + case HypothesisTest::Test::Type::TTest: { + testName = "T"; + + if (equalVariance) { + df = n[0] + n[1] - 2; + + sp = qSqrt(((n[0]-1) * gsl_pow_2(std[0]) + + (n[1]-1) * gsl_pow_2(std[1]) ) / df ); + m_statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt(1.0/n[0] + 1.0/n[1]))); + printLine(9, "Assumption: Equal Variance b/w both population means"); + } else { + double temp_val; + temp_val = gsl_pow_2( gsl_pow_2(std[0]) / n[0] + gsl_pow_2(std[1]) / n[1]); + temp_val = temp_val / ( (gsl_pow_2( (gsl_pow_2(std[0]) / n[0]) ) / (n[0]-1)) + + (gsl_pow_2( (gsl_pow_2(std[1]) / n[1]) ) / (n[1]-1))); + df = qRound(temp_val); + + m_statisticValue.append((mean[0] - mean[1]) / (qSqrt( (gsl_pow_2(std[0])/n[0]) + + (gsl_pow_2(std[1])/n[1])))); + printLine(9, "Assumption: UnEqual Variance b/w both population means"); + } + + printLine(8, "Assumption: Both Populations approximately follow normal distribution"); + break; + } + case HypothesisTest::Test::Type::ZTest: { + testName = "Z"; + sp = qSqrt( ((n[0]-1) * gsl_pow_2(std[0]) + (n[1]-1) * gsl_pow_2(std[1])) / df); + m_statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt( 1.0 / n[0] + 1.0 / n[1]))); + // m_pValue.append(gsl_cdf_gaussian_P(m_statisticValue, sp)); + break; + } + case HypothesisTest::Test::Type::Anova: + case HypothesisTest::Test::Type::NoneType: + break; + } + + m_currTestName = "

" + i18n("Two Sample Independent %1 Test for %2 vs %3", testName, col1Name, col2Name) + "

"; + m_pValue.append(getPValue(test, m_statisticValue[0], col1Name, col2Name, (mean[0] - mean[1]), sp, df)); + + printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue"); + + printLine(4, i18n("%1 Value is %2 ", testName, round(m_statisticValue[0])), "green"); + printTooltip(4, i18n("More is the |%1-value|, more safely we can reject the null hypothesis", testName)); + + printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green"); + + printLine(6, i18n("Degree of Freedom is %1", df), "green"); + printTooltip(6, i18n("Number of independent Pieces of information that went into calculating the estimate")); + + if (m_pValue[0] <= m_significanceLevel) + printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", round(m_significanceLevel))); + else + printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); + return; +} + +/********************************Two Sample Paired ***************************************/ + +void HypothesisTest::performTwoSamplePairedTest(HypothesisTest::Test::Type test) { + if (m_columns.size() != 2) { + printError("Inappropriate number of m_columns selected"); + + return; + } + + for (int i = 0; i < 2; i++) { + if ( !isNumericOrInteger(m_columns[0])) { + printError("select only m_columns with numbers"); + return; + } + } + + int n; + double sum, mean, std; + ErrorType errorCode = findStatsPaired(m_columns[0], m_columns[1], n, sum, mean, std); + + switch (errorCode) { + case ErrorUnqualSize: { + printError("both m_columns are having different sizes"); + + return; + } + case ErrorEmptyColumn: { + printError("m_columns are empty"); + return; + } + case NoError: + break; + } + + + QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", + "difference", n, sum, mean, std + }; + + m_statsTable = getHtmlTable(2, 5, rowMajor); + + if (std == 0) { + printError("Standard deviation of the difference is 0"); + return; + } + + + QString testName; + int df = 0; + + switch (test) { + case HypothesisTest::Test::Type::TTest: { + m_statisticValue[0] = mean / (std / qSqrt(n)); + df = n - 1; + testName = "T"; + printLine(6, i18n("Degree of Freedom is %1name(), i18n("%1", m_populationMean), mean, std, df)); + m_currTestName = "

" + i18n("One Sample %1 Test for %2 vs %3", testName, m_columns[0]->name(), m_columns[1]->name()) + "

"; + + printLine(2, i18n("Significance level is %1 ", round(m_significanceLevel)), "blue"); + printLine(4, i18n("%1 Value is %2 ", testName, round(m_statisticValue[0])), "green"); + printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green"); + + if (m_pValue[0] <= m_significanceLevel) + printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel)); + else + printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); + + return; +} + +/******************************** One Sample ***************************************/ + +void HypothesisTest::performOneSampleTest(HypothesisTest::Test::Type test) { + if (m_columns.size() != 1) { + printError("Inappropriate number of m_columns selected"); + + return; + } + + if ( !isNumericOrInteger(m_columns[0])) { + printError("select only m_columns with numbers"); + + return; + } + + int n; + double sum, mean, std; + ErrorType errorCode = findStats(m_columns[0], n, sum, mean, std); + + switch (errorCode) { + case ErrorEmptyColumn: { + printError("column is empty"); + return; + } + case NoError: + break; + case ErrorUnqualSize: { + return; + } + } + + QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", + m_columns[0]->name(), n, sum, mean, std + }; + + m_statsTable = getHtmlTable(2, 5, rowMajor); + + if (std == 0) { + printError("Standard deviation is 0"); + return; + } + + + QString testName; + int df = 0; + + switch (test) { + case HypothesisTest::Test::Type::TTest: { + testName = "T"; + m_statisticValue.append((mean - m_populationMean) / (std / qSqrt(n))); + df = n - 1; + printLine(6, i18n("Degree of Freedom is %1", df), "blue"); + break; + } + case HypothesisTest::Test::Type::ZTest: { + testName = "Z"; + df = 0; + m_statisticValue.append((mean - m_populationMean) / (std / qSqrt(n))); + break; + } + case HypothesisTest::Test::Type::Anova: + case HypothesisTest::Test::Type::NoneType: + break; + } + + m_pValue.append(getPValue(test, m_statisticValue[0], m_columns[0]->name(), i18n("%1",m_populationMean), mean - m_populationMean, std, df)); + m_currTestName = "

" + i18n("One Sample %1 Test for %2", testName, m_columns[0]->name()) + "

"; + + printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue"); + printLine(4, i18n("%1 Value is %2", testName, round(m_statisticValue[0])), "green"); + printLine(5, i18n("P Value is %1", m_pValue[0]), "green"); + + if (m_pValue[0] <= m_significanceLevel) + printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel)); + else + printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); + + return; +} + +/*************************************One Way Anova***************************************/ +// all standard variables and formulas are taken from this wikipedia page: +// https://en.wikipedia.org/wiki/One-way_analysis_of_variance +// b stands for b/w groups +// w stands for within groups +// np is number of partition i.e., number of classes +void HypothesisTest::performOneWayAnova() { + int np, totalRows; + countPartitions(m_columns[0], np, totalRows); + + int* ni = new int[np]; + double* sum = new double[np]; + double* mean = new double[np]; + double* std = new double[np]; + QString* colNames = new QString[np]; + + QMap classnameToIndex; + QString baseColName; + + if (isNumericOrInteger(m_columns[0])) + baseColName = m_columns[0]->name(); + + findStatsCategorical(m_columns[0], m_columns[1], ni, sum, mean, std, classnameToIndex, np, totalRows); + + double yBar = 0; // overall mean + double sB = 0; // sum of squares of (mean - overall_mean) between the groups + int fB = 0; // degree of freedom between the groups + double msB = 0; // mean sum of squares between the groups + double sW = 0; // sum of squares of (value - mean of group) within the groups + int fW = 0; // degree of freedom within the group + double msW = 0; // mean sum of squares within the groups + + // now finding mean of each group; + + for (int i = 0; i < np; i++) + yBar += mean[i]; + yBar = yBar / np; + + for (int i = 0; i < np; i++) { + sB += ni[i] * gsl_pow_2( ( mean[i] - yBar)); + if (ni[i] > 1) + sW += gsl_pow_2( std[i])*(ni[i] - 1); + else + sW += gsl_pow_2( std[i]); + fW += ni[i] - 1; + } + + fB = np - 1; + msB = sB / fB; + + msW = sW / fW; + m_statisticValue.append(msB / msW); + + + m_pValue.append(nsl_stats_fdist_p(m_statisticValue[0], static_cast(np-1), fW)); + + QMapIterator i(classnameToIndex); + while (i.hasNext()) { + i.next(); + colNames[i.value()-1] = baseColName + " " + i.key(); + } + + // now printing the statistics and result; + int rowCount = np + 1, columnCount = 5; + QVariant* rowMajor = new QVariant[rowCount*columnCount]; + // header data; + rowMajor[0] = ""; + rowMajor[1] = "Ni"; + rowMajor[2] = "Sum"; + rowMajor[3] = "Mean"; + rowMajor[4] = "Std"; + + // table data + for (int row_i = 1; row_i < rowCount ; row_i++) { + rowMajor[row_i*columnCount] = colNames[row_i - 1]; + rowMajor[row_i*columnCount + 1] = ni[row_i - 1]; + rowMajor[row_i*columnCount + 2] = sum[row_i - 1]; + rowMajor[row_i*columnCount + 3] = mean[row_i - 1]; + rowMajor[row_i*columnCount + 4] = std[row_i - 1]; + } + + m_statsTable = "

" + i18n("Group Summary Statistics") + "

"; + + m_statsTable += getHtmlTable(rowCount, columnCount, rowMajor); + + m_statsTable += getLine(""); + m_statsTable += getLine(""); + m_statsTable += "

" + i18n("Grand Summary Statistics") + "

"; + m_statsTable += getLine(""); + m_statsTable += getLine(i18n("Overall Mean is %1", round(yBar))); + + rowCount = 4; + columnCount = 3; + rowMajor->clear(); + + rowMajor[0] = ""; + rowMajor[1] = "Between Groups"; + rowMajor[2] = "Within Groups"; + + int baseIndex = 0; + baseIndex = 1 * columnCount; + rowMajor[baseIndex + 0] = "Sum of Squares"; + rowMajor[baseIndex + 1] = sB; + rowMajor[baseIndex + 2] = sW; + baseIndex = 2 * columnCount; + rowMajor[baseIndex + 0] = "Degree of Freedom"; + rowMajor[baseIndex + 1] = fB; + rowMajor[baseIndex + 2] = fW; + baseIndex = 3 * columnCount; + rowMajor[baseIndex + 0] = "Mean Square Value"; + rowMajor[baseIndex + 1] = msB; + rowMajor[baseIndex + 2] = msW; + + m_statsTable += getHtmlTable(rowCount, columnCount, rowMajor); + + delete[] ni; + delete[] sum; + delete[] mean; + delete[] std; + delete[] colNames; + + printLine(1, i18n("F Value is %1", round(m_statisticValue[0])), "green"); + printLine(2, i18n("P Value is %1 ", m_pValue[0]), "green"); + + if (m_pValue[0] <= m_significanceLevel) + printTooltip(2, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel)); + else + printTooltip(2, i18n("There is a plausibility for Null Hypothesis to be true")); + + return; +} + +/*************************************Two Way Anova***************************************/ + +// all formulas and symbols are taken from: http://statweb.stanford.edu/~susan/courses/s141/exanova.pdf + +//TODO: suppress warning of variable length array are a C99 feature. +//TODO: add assumptions verification option +//TODO: add tail option (if needed) +void HypothesisTest::performTwoWayAnova() { + int np_a, totalRows_a; + int np_b, totalRows_b; + countPartitions(m_columns[0], np_a, totalRows_a); + countPartitions(m_columns[1], np_b, totalRows_b); + + double groupMean[np_a][np_b]; + int replicates[np_a][np_b]; + + for (int i = 0; i < np_a; i++) + for (int j = 0; j < np_b; j++) { + groupMean[i][j] = 0; + replicates[i][j] = 0; + } + + if (totalRows_a != totalRows_b) { + printError("There is missing data in atleast one of the rows"); + return; + } + + QMap catToNumber_a; + QMap catToNumber_b; + + int partitionNumber_a = 1; + int partitionNumber_b = 1; + for (int i = 0; i < totalRows_a; i++) { + QString name_a = m_columns[0]->textAt(i); + QString name_b = m_columns[1]->textAt(i); + double value = m_columns[2]->valueAt(i); + + if (catToNumber_a[name_a] == 0) { + catToNumber_a[name_a] = partitionNumber_a; + partitionNumber_a++; + } + + if (catToNumber_b[name_b] == 0) { + catToNumber_b[name_b] = partitionNumber_b; + partitionNumber_b++; + } + + groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += value; + replicates[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += 1; + } + + int replicate = replicates[0][0]; + for (int i = 0; i < np_a; i++) + for (int j = 0; j < np_b; j++) { + if (replicates[i][j] == 0) { + printError("Dataset should have atleast one data value corresponding to each feature combination"); + return; + } + if (replicates[i][j] != replicate) { + printError("Number of experiments perfomed for each combination of levels
" + "between Independet Var.1 and Independent Var.2 must be equal"); + return; + } + groupMean[i][j] /= replicates[i][j]; + } + + // for (int i = 0; i < np_a; i++) + // for (int j = 0; j < np_b; j++) + // groupMean[i][j] = int(groupMean[i][j]); + + double ss_within = 0; + for (int i = 0; i < totalRows_a; i++) { + QString name_a = m_columns[0]->textAt(i); + QString name_b = m_columns[1]->textAt(i); + double value = m_columns[2]->valueAt(i); + + ss_within += gsl_pow_2(value - groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1]); + } + + int df_within = (replicate - 1) * np_a * np_b; + double ms_within = ss_within / df_within; + + double mean_a[np_a]; + double mean_b[np_b]; + for (int i = 0; i < np_a; i++) { + for (int j = 0; j < np_b; j++) { + mean_a[i] += groupMean[i][j] / np_b; + mean_b[j] += groupMean[i][j] / np_a; + } + } + + double mean = 0; + for (int i = 0; i < np_a; i++) + mean += mean_a[i] / np_a; + + + double ss_a = 0; + for (int i = 0; i < np_a; i++) + ss_a += gsl_pow_2(mean_a[i] - mean); + ss_a *= replicate * np_b; + + int df_a = np_a - 1; + double ms_a = ss_a / df_a; + + double ss_b = 0; + for (int i = 0; i < np_b; i++) + ss_b += gsl_pow_2(mean_b[i] - mean); + ss_b *= replicate * np_a; + + int df_b = np_b - 1; + double ms_b = ss_b / df_b; + + double ss_interaction = 0; + + for (int i = 0; i < np_a; i++) + for (int j = 0; j < np_b; j++) + ss_interaction += gsl_pow_2(groupMean[i][j] - mean_a[i] - mean_b[j] + mean); + + ss_interaction *= replicate; + int df_interaction = (np_a - 1) * (np_b - 1); + double ms_interaction = ss_interaction / df_interaction; + + QString partitionNames_a[np_a]; + QString partitionNames_b[np_b]; + + QMapIterator itr_a(catToNumber_a); + while (itr_a.hasNext()) { + itr_a.next(); + partitionNames_a[itr_a.value()-1] = itr_a.key(); + } + + QMapIterator itr_b(catToNumber_b); + while (itr_b.hasNext()) { + itr_b.next(); + partitionNames_b[itr_b.value()-1] = itr_b.key(); + } + + // printing table; + // cell constructor structure; data, level, rowSpanCount, m_columnspanCount, isHeader; + QList rowMajor; + rowMajor.append(new Cell("", 0, true, "", 2, 1)); + for (int i = 0; i < np_b; i++) + rowMajor.append(new Cell(partitionNames_b[i], 0, true, "", 1, 2)); + rowMajor.append(new Cell("Mean", 0, true, "", 2)); + + for (int i = 0; i < np_b; i++) { + rowMajor.append(new Cell("Mean", 1, true)); + rowMajor.append(new Cell("Replicate", 1, true)); + } + + int level = 2; + for (int i = 0; i < np_a; i++) { + rowMajor.append(new Cell(partitionNames_a[i], level, true)); + for (int j = 0; j < np_b; j++) { + rowMajor.append(new Cell(round(groupMean[i][j]), level)); + rowMajor.append(new Cell(replicates[i][j], level)); + } + rowMajor.append(new Cell(round(mean_a[i]), level)); + level++; + } + + rowMajor.append(new Cell("Mean", level, true)); + for (int i = 0; i < np_b; i++) + rowMajor.append(new Cell(round(mean_b[i]), level, false, "", 1, 2)); + rowMajor.append(new Cell(round(mean), level)); + + m_statsTable = "

" + i18n("Contingency Table") + "

"; + m_statsTable += getHtmlTable3(rowMajor); + + m_statsTable += "
"; + m_statsTable += "

" + i18n("results table") + "

"; + + rowMajor.clear(); + level = 0; + rowMajor.append(new Cell("", level, true)); + rowMajor.append(new Cell("SS", level, true)); + rowMajor.append(new Cell("DF", level, true, "degree of freedom")); + rowMajor.append(new Cell("MS", level, true)); + + level++; + rowMajor.append(new Cell(m_columns[0]->name(), level, true)); + rowMajor.append(new Cell(round(ss_a), level)); + rowMajor.append(new Cell(df_a, level)); + rowMajor.append(new Cell(round(ms_a), level)); + + level++; + rowMajor.append(new Cell(m_columns[1]->name(), level, true)); + rowMajor.append(new Cell(round(ss_b), level)); + rowMajor.append(new Cell(df_b, level)); + rowMajor.append(new Cell(round(ms_b), level)); + + level++; + rowMajor.append(new Cell("Interaction", level, true)); + rowMajor.append(new Cell(round(ss_interaction), level)); + rowMajor.append(new Cell(df_interaction, level)); + rowMajor.append(new Cell(round(ms_interaction), level)); + + level++; + rowMajor.append(new Cell("Within", level, true)); + rowMajor.append(new Cell(round(ss_within), level)); + rowMajor.append(new Cell(df_within, level)); + rowMajor.append(new Cell(round(ms_within), level)); + + m_statsTable += getHtmlTable3(rowMajor); + + double fValue_a = ms_a / ms_within; + double fValue_b = ms_b / ms_within; + double fValue_interaction = ms_interaction / ms_within; + + double m_pValue_a = nsl_stats_fdist_p(fValue_a, static_cast(np_a - 1), df_a); + double m_pValue_b = nsl_stats_fdist_p(fValue_b, static_cast(np_b - 1), df_b); + + printLine(0, "F(df" + m_columns[0]->name() + ", dfwithin) is " + round(fValue_a), "blue"); + printLine(1, "F(df" + m_columns[1]->name() + ", dfwithin) is " + round(fValue_b), "blue"); + printLine(2, "F(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue"); + + printLine(4, "P(df" + m_columns[0]->name() + ", dfwithin) is " + round(m_pValue_a), "blue"); + printLine(5, "P(df" + m_columns[1]->name() + ", dfwithin) is " + round(m_pValue_b), "blue"); + // printLine(2, "P(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue"); + + m_statisticValue.append(fValue_a); + m_statisticValue.append(fValue_b); + m_statisticValue.append(fValue_interaction); + + m_pValue.append(m_pValue_a); + m_pValue.append(m_pValue_b); + + return; +} + +/**************************************Levene Test****************************************/ +// Some reference to local variables. +// np = number of partitions +// df = degree of fredom +// totalRows = total number of rows in column + +// these variables are taken from: https://en.wikipedia.org/wiki/Levene%27s_test +// yiBar = mean of ith group; +// Zij = |Yij - yiBar| +// ziBar = mean of Zij for group i +// ziBarBar = mean for all zij +// ni = number of elements in group i +void HypothesisTest::m_performLeveneTest(bool categoricalVariable) { + if (m_columns.size() != 2) { + printError("Inappropriate number of m_columns selected"); + return; + } + + int np = 0; + int n = 0; + + if (!categoricalVariable && isNumericOrInteger(m_columns[0])) + np = m_columns.size(); + else + countPartitions(m_columns[0], np, n); + + if (np < 2) { + printError("Select atleast two m_columns / classes"); + return; + } + + double* yiBar = new double[np]; + double* ziBar = new double[np]; + double ziBarBar = 0; + double* ni = new double[np]; + + for (int i = 0; i < np; i++) { + yiBar[i] = 0; + ziBar[i] = 0; + ni[i] = 0; + } + + double fValue; + int df = 0; + + int totalRows = 0; + + QString* colNames = new QString[np]; + if (!categoricalVariable && isNumericOrInteger(m_columns[0])) { + totalRows = m_columns[0]->rowCount(); + + double value = 0; + for (int j = 0; j < totalRows; j++) { + int numberNaNCols = 0; + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (std::isnan(value)) { + numberNaNCols++; + continue; + } + yiBar[i] += value; + ni[i]++; + n++; + } + if (numberNaNCols == np) { + totalRows = j; + break; + } + } + + for (int i = 0; i < np; i++) { + if (ni[i] > 0) + yiBar[i] = yiBar[i] / ni[i]; + else { + printError("One of the selected m_columns is empty"); + return; + } + } + + for (int j = 0; j < totalRows; j++) { + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (!(std::isnan(value))) + ziBar[i] += fabs(value - yiBar[i]); + } + } + + for (int i = 0; i < np; i++) { + ziBarBar += ziBar[i]; + if (ni[i] > 0) + ziBar[i] = ziBar[i] / ni[i]; + } + + ziBarBar = ziBarBar / n; + + double numberatorValue = 0; + double denominatorValue = 0; + + for (int j = 0; j < totalRows; j++) { + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (!(std::isnan(value))) { + double zij = fabs(value - yiBar[i]); + denominatorValue += gsl_pow_2( (zij - ziBar[i])); + } + } + } + + + if (denominatorValue <= 0) { + printError( i18n("Denominator value is %1", denominatorValue)); + return; + } + + for (int i = 0; i < np; i++) { + colNames[i] = m_columns[i]->name(); + numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar)); + } + + fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue); + + } else { + QMap classnameToIndex; + + AbstractColumn::ColumnMode originalColMode = m_columns[0]->columnMode(); + m_columns[0]->setColumnMode(AbstractColumn::Text); + + int partitionNumber = 1; + QString name; + double value; + int classIndex; + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + if (std::isnan(value)) { + n = j; + break; + } + + if (classnameToIndex[name] == 0) { + classnameToIndex[name] = partitionNumber; + partitionNumber++; + } + + classIndex = classnameToIndex[name]-1; + ni[classIndex]++; + yiBar[classIndex] += value; + } + + for (int i = 0; i < np; i++) { + if (ni[i] > 0) + yiBar[i] = yiBar[i] / ni[i]; + else { + printError("One of the selected m_columns is empty"); + m_columns[0]->setColumnMode(originalColMode); + return; + } + } + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + classIndex = classnameToIndex[name] - 1; + ziBar[classIndex] += fabs(value - yiBar[classIndex]); + } + + for (int i = 0; i < np; i++) { + ziBarBar += ziBar[i]; + ziBar[i] = ziBar[i] / ni[i]; + } + + ziBarBar = ziBarBar / n; + + double numberatorValue = 0; + double denominatorValue = 0; + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + classIndex = classnameToIndex[name] - 1; + double zij = fabs(value - yiBar[classIndex]); + denominatorValue += gsl_pow_2( (zij - ziBar[classIndex])); + } + + for (int i = 0; i < np; i++) + numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar)); + + if (denominatorValue <= 0) { + printError( "number of data points is less or than equal to number of categorical variables"); + m_columns[0]->setColumnMode(originalColMode); + return; + } + + fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue); + + QMapIterator i(classnameToIndex); + while (i.hasNext()) { + i.next(); + colNames[i.value()-1] = m_columns[0]->name() + " " + i.key(); + } + + m_columns[0]->setColumnMode(originalColMode); + } + + df = n - np; + + // now making the stats table. + int rowCount = np+1; + int columnCount = 4; + + QVariant* rowMajor = new QVariant[rowCount*columnCount]; + // header data; + rowMajor[0] = ""; + rowMajor[1] = "Ni"; + rowMajor[2] = "yiBar"; + rowMajor[3] = "ziBar"; + + // table data + for (int row_i = 1; row_i < rowCount; row_i++) { + rowMajor[row_i*columnCount] = colNames[row_i-1]; + rowMajor[row_i*columnCount + 1] = ni[row_i-1]; + rowMajor[row_i*columnCount + 2] = yiBar[row_i-1]; + rowMajor[row_i*columnCount + 3] = ziBar[row_i-1]; + } + + m_statsTable = getHtmlTable(rowCount, columnCount, rowMajor); + + delete[] rowMajor; + delete[] yiBar; + delete[] ziBar; + delete[] ni; + + m_pValue.append(nsl_stats_fdist_p(fValue, static_cast(np-1), df)); + + printLine(0, "Null Hypothesis: Variance is equal between all classes", "blue"); + printLine(1, "Alternate Hypothesis: Variance is not equal in at-least one pair of classes", "blue"); + printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue"); + printLine(4, i18n("F Value is %1 ", round(fValue)), "green"); + printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green"); + printLine(6, i18n("Degree of Freedom is %1", df), "green"); + + if (m_pValue[0] <= m_significanceLevel) { + printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel)); + printLine(8, "Requirement for homogeneity is not met", "red"); + } else { + printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); + printLine(8, "Requirement for homogeneity is met", "green"); + } + + m_statisticValue.append(fValue); + return; +} + + +//TODO change ("⋖") symbol to ("<"), currently macro UTF8_QSTRING is not working properly if used "<" symbol; +// TODO: check for correctness between: for TestZ with TailTwo +// m_pValue.append(2*gsl_cdf_tdist_P(value, df) v/s +// m_pValue.append(gsl_cdf_tdis_P(value, df) + gsl_cdf_tdis_P(-value, df); +double HypothesisTest::getPValue(const HypothesisTest::Test::Type& test, double& value, const QString& col1Name, const QString& col2Name, const double mean, const double sp, const int df) { + + switch (test) { + case HypothesisTest::Test::Type::TTest: { + switch (m_tailType) { + case HypothesisTest::Test::Tail::Negative: { + m_pValue.append(gsl_cdf_tdist_P(value, df)); + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≥"), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue"); + break; + } + case HypothesisTest::Test::Tail::Positive: { + value *= -1; + m_pValue.append(gsl_cdf_tdist_P(value, df)); + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≤"), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING(">"), col2Name), "blue"); + break; + } + case HypothesisTest::Test::Tail::Two: { + m_pValue.append(2.*gsl_cdf_tdist_P(-fabs(value), df)); + + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("="), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≠"), col2Name), "blue"); + break; + } + } + break; + } + case HypothesisTest::Test::Type::ZTest: { + switch (m_tailType) { + case HypothesisTest::Test::Tail::Negative: { + m_pValue.append(gsl_cdf_gaussian_P(value - mean, sp)); + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≥"), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue"); + break; + } + case HypothesisTest::Test::Tail::Positive: { + value *= -1; + m_pValue.append(nsl_stats_tdist_p(value - mean, sp)); + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≤"), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING(">"), col2Name), "blue"); + break; + } + case HypothesisTest::Test::Tail::Two: { + m_pValue.append(2.*gsl_cdf_gaussian_P(value - mean, sp)); + printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("="), col2Name), "blue"); + printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≠"), col2Name), "blue"); + break; + } + } + break; + } + case HypothesisTest::Test::Type::Anova: + case HypothesisTest::Test::Type::NoneType: + break; + } + + if (m_pValue[0] > 1) + return 1; + return m_pValue[0]; +} + +// Virtual functions +QWidget* HypothesisTest::view() const { + if (!m_partView) { + m_view = new HypothesisTestView(const_cast(this)); + m_partView = m_view; + } + return m_partView; +} diff --git a/src/backend/hypothesisTest/HypothesisTest.h b/src/backend/generalTest/HypothesisTest.h similarity index 68% rename from src/backend/hypothesisTest/HypothesisTest.h rename to src/backend/generalTest/HypothesisTest.h index b83d5e01d..95ec19205 100644 --- a/src/backend/hypothesisTest/HypothesisTest.h +++ b/src/backend/generalTest/HypothesisTest.h @@ -1,121 +1,100 @@ /*************************************************************************** File : HypothesisTest.h Project : LabPlot Description : Doing Hypothesis-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef HYPOTHESISTEST_H #define HYPOTHESISTEST_H #include "backend/core/AbstractPart.h" +#include "GeneralTest.h" #include "backend/lib/macros.h" -class HypothesisTestPrivate; class HypothesisTestView; class Spreadsheet; class QString; class Column; class QVBoxLayout; class QLabel; -class HypothesisTest : public AbstractPart { +class HypothesisTest : public GeneralTest { Q_OBJECT public: explicit HypothesisTest(const QString& name); ~HypothesisTest() override; struct Test { enum Type { NoneType = 0, TTest = 1 << 0, ZTest = 1 << 1, Anova = 1 << 2 }; enum SubType { NoneSubType = 0, TwoSampleIndependent = 1 << 0, TwoSamplePaired = 1 << 1, OneSample = 1 << 2, OneWay = 1 << 3, TwoWay = 1 << 4 }; enum Tail {Positive, Negative, Two}; Type type = NoneType; SubType subtype = NoneSubType; - Correlation correlation = NoneCorrelation; Tail tail; }; - enum DataSourceType {DataSourceSpreadsheet, DataSourceDatabase}; - - void setDataSourceType(DataSourceType type); - DataSourceType dataSourceType() const; - void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); - Spreadsheet* dataSourceSpreadsheet() const; - - void setColumns(const QVector& cols); - void setColumns(QStringList cols); - QStringList allColumns(); void setPopulationMean(QVariant populationMean); void setSignificanceLevel(QVariant alpha); - QString testName(); - QString statsTable(); - QMap tooltips(); - void performTest(Test m_test, bool categoricalVariable = true, bool equalVariance = true); - - void performLeveneTest(bool categorical_variable); + void performTest(Test m_test, bool categoricalVariable = true, bool equalVariance = true); + void performLeveneTest(bool categoricalVariable); QList statisticValue(); QList pValue(); - - QVBoxLayout* summaryLayout(); - - //virtual methods - // QIcon icon() const override; - QMenu* createContextMenu() override; - QWidget* view() const override; - - bool exportView() const override; - bool printView() override; - bool printPreview() const override; - - void save(QXmlStreamWriter*) const override; - bool load(XmlStreamReader*, bool preview) override; + QWidget* view() const override; private: - HypothesisTestPrivate* const d; - mutable HypothesisTestView* m_view{nullptr}; - friend class HypothesisTestPrivate; - -signals: - void changed(); - void requestProjectContextMenu(QMenu*); - void dataSourceTypeChanged(HypothesisTest::DataSourceType); - void dataSourceSpreadsheetChanged(Spreadsheet*); + void performTwoSampleIndependentTest(HypothesisTest::Test::Type test, bool categoricalVariable = false, bool equalVariance = true); + void performTwoSamplePairedTest(HypothesisTest::Test::Type test); + void performOneSampleTest(HypothesisTest::Test::Type test); + void performOneWayAnova(); + void performTwoWayAnova(); + void m_performLeveneTest(bool categoricalVariable); + + double getPValue(const HypothesisTest::Test::Type& test, double& value, + const QString& col1Name, const QString& col2name, + const double mean, const double sp, const int df); + + double m_populationMean; + double m_significanceLevel; + HypothesisTest::Test::Tail m_tailType; + QList m_pValue; + QList m_statisticValue; }; #endif // HypothesisTest_H diff --git a/src/backend/hypothesisTest/HypothesisTest.cpp b/src/backend/hypothesisTest/HypothesisTest.cpp deleted file mode 100644 index 62f41db31..000000000 --- a/src/backend/hypothesisTest/HypothesisTest.cpp +++ /dev/null @@ -1,1737 +0,0 @@ -/*************************************************************************** - File : HypothesisTest.cpp - Project : LabPlot - Description : Doing Hypothesis-Test on data provided - -------------------------------------------------------------------- - Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) - - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 51 Franklin Street, Fifth Floor, * - * Boston, MA 02110-1301 USA * - * * - ***************************************************************************/ - -#include "HypothesisTest.h" -#include "HypothesisTestPrivate.h" -#include "kdefrontend/hypothesisTest/HypothesisTestView.h" -#include "backend/spreadsheet/Spreadsheet.h" -#include "backend/core/column/Column.h" -#include "backend/lib/macros.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -extern "C" { -#include "backend/nsl/nsl_stats.h" -} - -HypothesisTest::HypothesisTest(const QString &name) : AbstractPart(name), - d(new HypothesisTestPrivate(this)) { -} - -HypothesisTest::~HypothesisTest() { - delete d; -} - -void HypothesisTest::setDataSourceType(DataSourceType type) { - if (type != d->dataSourceType) - d->dataSourceType = type; -} - -HypothesisTest::DataSourceType HypothesisTest::dataSourceType() const { - return d->dataSourceType; -} - -void HypothesisTest::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) { - if (spreadsheet != d->dataSourceSpreadsheet) - d->setDataSourceSpreadsheet(spreadsheet); -} - -void HypothesisTest::setColumns(const QVector& cols) { - d->columns = cols; -} - -void HypothesisTest::setColumns(QStringList cols) { - return d->setColumns(cols); -} - -QStringList HypothesisTest::allColumns() { - return d->allColumns; -} - -void HypothesisTest::setPopulationMean(QVariant populationMean) { - d->populationMean = populationMean.toDouble(); -} - -void HypothesisTest::setSignificanceLevel(QVariant alpha) { - d->significanceLevel = alpha.toDouble(); -} - - -QString HypothesisTest::testName() { - return d->currTestName; -} - -QString HypothesisTest::statsTable() { - return d->statsTable; -} - -QMap HypothesisTest::tooltips() { - return d->tooltips; -} - -void HypothesisTest::performTest(Test test, bool categoricalVariable, bool equalVariance) { - d->tailType = test.tail; - d->pValue.clear(); - d->statisticValue.clear(); - d->statsTable = ""; - d->tooltips.clear(); - for (int i = 0; i < 10; i++) - d->resultLine[i]->clear(); - - switch (test.subtype) { - case HypothesisTest::Test::SubType::TwoSampleIndependent: { - d->currTestName = "

" + i18n("Two Sample Independent Test") + "

"; - d->performTwoSampleIndependentTest(test.type, categoricalVariable, equalVariance); - break; - } - case HypothesisTest::Test::SubType::TwoSamplePaired: - d->currTestName = "

" + i18n("Two Sample Paired Test") + "

"; - d->performTwoSamplePairedTest(test.type); - break; - case HypothesisTest::Test::SubType::OneSample: { - d->currTestName = "

" + i18n("One Sample Test") + "

"; - d->performOneSampleTest(test.type); - break; - } - case HypothesisTest::Test::SubType::OneWay: { - d->currTestName = "

" + i18n("One Way Anova") + "

"; - d->performOneWayAnova(); - break; - } - case HypothesisTest::Test::SubType::TwoWay: { - d->currTestName = "

" + i18n("Two Way Anova") + "

"; - d->performTwoWayAnova(); - break; - } - case HypothesisTest::Test::SubType::NoneSubType: - break; - } - - emit changed(); -} - -void HypothesisTest::performLeveneTest(bool categoricalVariable) { - d->currTestName = "

" + i18n("Levene Test for Equality of Variance") + "

"; - d->performLeveneTest(categoricalVariable); - emit changed(); -} - -QList HypothesisTest::statisticValue() { - return d->statisticValue; -} - -QList HypothesisTest::pValue() { - return d->pValue; -} - -QVBoxLayout* HypothesisTest::summaryLayout() { - return d->summaryLayout; -} - -/****************************************************************************** - * Private Implementations - * ****************************************************************************/ -//TODO: backend of z test; -//TODO: add tooltip to tables. (currently it is not possible to use with QTextDocument); - -HypothesisTestPrivate::HypothesisTestPrivate(HypothesisTest* owner) : q(owner), - summaryLayout(new QVBoxLayout()) { - - for (int i = 0; i < 10; i++) { - resultLine[i] = new QLabel(); - summaryLayout->addWidget(resultLine[i]); - } -} - -HypothesisTestPrivate::~HypothesisTestPrivate() { -} - -void HypothesisTestPrivate::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) { - dataSourceSpreadsheet = spreadsheet; - - //setting rows and columns count; -// rowCount = dataSourceSpreadsheet->rowCount(); -// columnCount = dataSourceSpreadsheet->columnCount(); - - for (auto* col : dataSourceSpreadsheet->children()) - allColumns << col->name(); -} - - -void HypothesisTestPrivate::setColumns(QStringList cols) { - columns.clear(); - Column* column = new Column("column"); - for (QString col : cols) { - if (!cols.isEmpty()) { - column = dataSourceSpreadsheet->column(col); - columns.append(column); - } - } - delete[] column; -} - - -/**************************Two Sample Independent *************************************/ - -void HypothesisTestPrivate::performTwoSampleIndependentTest(HypothesisTest::Test::Type test, bool categoricalVariable, bool equalVariance) { - if (columns.size() != 2) { - printError("Inappropriate number of columns selected"); - return; - } - - int n[2]; - double sum[2], mean[2], std[2]; - - QString col1Name = columns[0]->name(); - QString col2Name = columns[1]->name(); - - if (!categoricalVariable && isNumericOrInteger(columns[0])) { - for (int i = 0; i < 2; i++) { - findStats(columns[i], n[i], sum[i], mean[i], std[i]); - if (n[i] == 0) { - printError("Atleast two values should be there in every column"); - return; - } - if (std[i] == 0) { - printError(i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", columns[i]->name())); - return; - } - } - } else { - QMap colName; - QString baseColName; - int np; - int totalRows; - - countPartitions(columns[0], np, totalRows); - if (np != 2) { - printError( i18n("Number of Categorical Variable in Column %1 is not equal to 2", columns[0]->name())); - return; - } - - if (isNumericOrInteger(columns[0])) - baseColName = columns[0]->name(); - - ErrorType errorCode = findStatsCategorical(columns[0], columns[1], n, sum, mean, std, colName, np, totalRows); - - switch (errorCode) { - case ErrorUnqualSize: { - printError( i18n("Unequal size between Column %1 and Column %2", columns[0]->name(), columns[1]->name())); - return; - } - case ErrorEmptyColumn: { - printError("At least one of selected column is empty"); - - return; - } - case NoError: - break; - } - - QMapIterator i(colName); - while (i.hasNext()) { - i.next(); - if (i.value() == 1) - col1Name = baseColName + " " + i.key(); - else - col2Name = baseColName + " " + i.key(); - } - } - - QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", - col1Name, n[0], sum[0], mean[0], std[0], - col2Name, n[1], sum[1], mean[1], std[1] - }; - - statsTable = getHtmlTable(3, 5, rowMajor); - - for (int i = 0; i < 2; i++) { - if (n[i] == 0) { - printError("Atleast two values should be there in every column"); - return; - } - if (std[i] == 0) { - printError( i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", columns[i]->name())); - return; - } - } - - QString testName; - int df = 0; - double sp = 0; - - switch (test) { - case HypothesisTest::Test::Type::TTest: { - testName = "T"; - - if (equalVariance) { - df = n[0] + n[1] - 2; - - sp = qSqrt(((n[0]-1) * gsl_pow_2(std[0]) + - (n[1]-1) * gsl_pow_2(std[1]) ) / df ); - statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt(1.0/n[0] + 1.0/n[1]))); - printLine(9, "Assumption: Equal Variance b/w both population means"); - } else { - double temp_val; - temp_val = gsl_pow_2( gsl_pow_2(std[0]) / n[0] + gsl_pow_2(std[1]) / n[1]); - temp_val = temp_val / ( (gsl_pow_2( (gsl_pow_2(std[0]) / n[0]) ) / (n[0]-1)) + - (gsl_pow_2( (gsl_pow_2(std[1]) / n[1]) ) / (n[1]-1))); - df = qRound(temp_val); - - statisticValue.append((mean[0] - mean[1]) / (qSqrt( (gsl_pow_2(std[0])/n[0]) + - (gsl_pow_2(std[1])/n[1])))); - printLine(9, "Assumption: UnEqual Variance b/w both population means"); - } - - printLine(8, "Assumption: Both Populations approximately follow normal distribution"); - break; - } - case HypothesisTest::Test::Type::ZTest: { - testName = "Z"; - sp = qSqrt( ((n[0]-1) * gsl_pow_2(std[0]) + (n[1]-1) * gsl_pow_2(std[1])) / df); - statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt( 1.0 / n[0] + 1.0 / n[1]))); -// pValue.append(gsl_cdf_gaussian_P(statisticValue, sp)); - break; - } - case HypothesisTest::Test::Type::Anova: - case HypothesisTest::Test::Type::NoneType: - break; - } - - currTestName = "

" + i18n("Two Sample Independent %1 Test for %2 vs %3", testName, col1Name, col2Name) + "

"; - pValue.append(getPValue(test, statisticValue[0], col1Name, col2Name, (mean[0] - mean[1]), sp, df)); - - printLine(2, i18n("Significance level is %1", round(significanceLevel)), "blue"); - - printLine(4, i18n("%1 Value is %2 ", testName, round(statisticValue[0])), "green"); - printTooltip(4, i18n("More is the |%1-value|, more safely we can reject the null hypothesis", testName)); - - printLine(5, i18n("P Value is %1 ", pValue[0]), "green"); - - printLine(6, i18n("Degree of Freedom is %1", df), "green"); - printTooltip(6, i18n("Number of independent Pieces of information that went into calculating the estimate")); - - if (pValue[0] <= significanceLevel) - printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", round(significanceLevel))); - else - printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); - return; -} - -/********************************Two Sample Paired ***************************************/ - -void HypothesisTestPrivate::performTwoSamplePairedTest(HypothesisTest::Test::Type test) { - if (columns.size() != 2) { - printError("Inappropriate number of columns selected"); - - return; - } - - for (int i = 0; i < 2; i++) { - if ( !isNumericOrInteger(columns[0])) { - printError("select only columns with numbers"); - return; - } - } - - int n; - double sum, mean, std; - ErrorType errorCode = findStatsPaired(columns[0], columns[1], n, sum, mean, std); - - switch (errorCode) { - case ErrorUnqualSize: { - printError("both columns are having different sizes"); - - return; - } - case ErrorEmptyColumn: { - printError("columns are empty"); - return; - } - case NoError: - break; - } - - - QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", - "difference", n, sum, mean, std - }; - - statsTable = getHtmlTable(2, 5, rowMajor); - - if (std == 0) { - printError("Standard deviation of the difference is 0"); - return; - } - - - QString testName; - int df = 0; - - switch (test) { - case HypothesisTest::Test::Type::TTest: { - statisticValue[0] = mean / (std / qSqrt(n)); - df = n - 1; - testName = "T"; - printLine(6, i18n("Degree of Freedom is %1name(), i18n("%1", populationMean), mean, std, df)); - currTestName = "

" + i18n("One Sample %1 Test for %2 vs %3", testName, columns[0]->name(), columns[1]->name()) + "

"; - - printLine(2, i18n("Significance level is %1 ", round(significanceLevel)), "blue"); - printLine(4, i18n("%1 Value is %2 ", testName, round(statisticValue[0])), "green"); - printLine(5, i18n("P Value is %1 ", pValue[0]), "green"); - - if (pValue[0] <= significanceLevel) - printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", significanceLevel)); - else - printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); - - return; -} - -/******************************** One Sample ***************************************/ - -void HypothesisTestPrivate::performOneSampleTest(HypothesisTest::Test::Type test) { - if (columns.size() != 1) { - printError("Inappropriate number of columns selected"); - - return; - } - - if ( !isNumericOrInteger(columns[0])) { - printError("select only columns with numbers"); - - return; - } - - int n; - double sum, mean, std; - ErrorType errorCode = findStats(columns[0], n, sum, mean, std); - - switch (errorCode) { - case ErrorEmptyColumn: { - printError("column is empty"); - return; - } - case NoError: - break; - case ErrorUnqualSize: { - return; - } - } - - QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std", - columns[0]->name(), n, sum, mean, std - }; - - statsTable = getHtmlTable(2, 5, rowMajor); - - if (std == 0) { - printError("Standard deviation is 0"); - return; - } - - - QString testName; - int df = 0; - - switch (test) { - case HypothesisTest::Test::Type::TTest: { - testName = "T"; - statisticValue.append((mean - populationMean) / (std / qSqrt(n))); - df = n - 1; - printLine(6, i18n("Degree of Freedom is %1", df), "blue"); - break; - } - case HypothesisTest::Test::Type::ZTest: { - testName = "Z"; - df = 0; - statisticValue.append((mean - populationMean) / (std / qSqrt(n))); - break; - } - case HypothesisTest::Test::Type::Anova: - case HypothesisTest::Test::Type::NoneType: - break; - } - - pValue.append(getPValue(test, statisticValue[0], columns[0]->name(), i18n("%1",populationMean), mean - populationMean, std, df)); - currTestName = "

" + i18n("One Sample %1 Test for %2", testName, columns[0]->name()) + "

"; - - printLine(2, i18n("Significance level is %1", round(significanceLevel)), "blue"); - printLine(4, i18n("%1 Value is %2", testName, round(statisticValue[0])), "green"); - printLine(5, i18n("P Value is %1", pValue[0]), "green"); - - if (pValue[0] <= significanceLevel) - printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", significanceLevel)); - else - printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); - - return; -} - -/*************************************One Way Anova***************************************/ -// all standard variables and formulas are taken from this wikipedia page: -// https://en.wikipedia.org/wiki/One-way_analysis_of_variance -// b stands for b/w groups -// w stands for within groups -// np is number of partition i.e., number of classes -void HypothesisTestPrivate::performOneWayAnova() { - int np, totalRows; - countPartitions(columns[0], np, totalRows); - - int* ni = new int[np]; - double* sum = new double[np]; - double* mean = new double[np]; - double* std = new double[np]; - QString* colNames = new QString[np]; - - QMap classnameToIndex; - QString baseColName; - - if (isNumericOrInteger(columns[0])) - baseColName = columns[0]->name(); - - findStatsCategorical(columns[0], columns[1], ni, sum, mean, std, classnameToIndex, np, totalRows); - - double yBar = 0; // overall mean - double sB = 0; // sum of squares of (mean - overall_mean) between the groups - int fB = 0; // degree of freedom between the groups - double msB = 0; // mean sum of squares between the groups - double sW = 0; // sum of squares of (value - mean of group) within the groups - int fW = 0; // degree of freedom within the group - double msW = 0; // mean sum of squares within the groups - - // now finding mean of each group; - - for (int i = 0; i < np; i++) - yBar += mean[i]; - yBar = yBar / np; - - for (int i = 0; i < np; i++) { - sB += ni[i] * gsl_pow_2( ( mean[i] - yBar)); - if (ni[i] > 1) - sW += gsl_pow_2( std[i])*(ni[i] - 1); - else - sW += gsl_pow_2( std[i]); - fW += ni[i] - 1; - } - - fB = np - 1; - msB = sB / fB; - - msW = sW / fW; - statisticValue.append(msB / msW); - - - pValue.append(nsl_stats_fdist_p(statisticValue[0], static_cast(np-1), fW)); - - QMapIterator i(classnameToIndex); - while (i.hasNext()) { - i.next(); - colNames[i.value()-1] = baseColName + " " + i.key(); - } - - // now printing the statistics and result; - int rowCount = np + 1, columnCount = 5; - QVariant* rowMajor = new QVariant[rowCount*columnCount]; - // header data; - rowMajor[0] = ""; - rowMajor[1] = "Ni"; - rowMajor[2] = "Sum"; - rowMajor[3] = "Mean"; - rowMajor[4] = "Std"; - - // table data - for (int row_i = 1; row_i < rowCount ; row_i++) { - rowMajor[row_i*columnCount] = colNames[row_i - 1]; - rowMajor[row_i*columnCount + 1] = ni[row_i - 1]; - rowMajor[row_i*columnCount + 2] = sum[row_i - 1]; - rowMajor[row_i*columnCount + 3] = mean[row_i - 1]; - rowMajor[row_i*columnCount + 4] = std[row_i - 1]; - } - - statsTable = "

" + i18n("Group Summary Statistics") + "

"; - - statsTable += getHtmlTable(rowCount, columnCount, rowMajor); - - statsTable += getLine(""); - statsTable += getLine(""); - statsTable += "

" + i18n("Grand Summary Statistics") + "

"; - statsTable += getLine(""); - statsTable += getLine(i18n("Overall Mean is %1", round(yBar))); - - rowCount = 4; - columnCount = 3; - rowMajor->clear(); - - rowMajor[0] = ""; - rowMajor[1] = "Between Groups"; - rowMajor[2] = "Within Groups"; - - int baseIndex = 0; - baseIndex = 1 * columnCount; - rowMajor[baseIndex + 0] = "Sum of Squares"; - rowMajor[baseIndex + 1] = sB; - rowMajor[baseIndex + 2] = sW; - baseIndex = 2 * columnCount; - rowMajor[baseIndex + 0] = "Degree of Freedom"; - rowMajor[baseIndex + 1] = fB; - rowMajor[baseIndex + 2] = fW; - baseIndex = 3 * columnCount; - rowMajor[baseIndex + 0] = "Mean Square Value"; - rowMajor[baseIndex + 1] = msB; - rowMajor[baseIndex + 2] = msW; - - statsTable += getHtmlTable(rowCount, columnCount, rowMajor); - - delete[] ni; - delete[] sum; - delete[] mean; - delete[] std; - delete[] colNames; - - printLine(1, i18n("F Value is %1", round(statisticValue[0])), "green"); - printLine(2, i18n("P Value is %1 ", pValue[0]), "green"); - - if (pValue[0] <= significanceLevel) - printTooltip(2, i18n("We can safely reject Null Hypothesis for significance level %1", significanceLevel)); - else - printTooltip(2, i18n("There is a plausibility for Null Hypothesis to be true")); - - return; -} - -/*************************************Two Way Anova***************************************/ - -// all formulas and symbols are taken from: http://statweb.stanford.edu/~susan/courses/s141/exanova.pdf - -//TODO: suppress warning of variable length array are a C99 feature. -//TODO: add assumptions verification option -//TODO: add tail option (if needed) -void HypothesisTestPrivate::performTwoWayAnova() { - int np_a, totalRows_a; - int np_b, totalRows_b; - countPartitions(columns[0], np_a, totalRows_a); - countPartitions(columns[1], np_b, totalRows_b); - - double groupMean[np_a][np_b]; - int replicates[np_a][np_b]; - - for (int i = 0; i < np_a; i++) - for (int j = 0; j < np_b; j++) { - groupMean[i][j] = 0; - replicates[i][j] = 0; - } - - if (totalRows_a != totalRows_b) { - printError("There is missing data in atleast one of the rows"); - return; - } - - QMap catToNumber_a; - QMap catToNumber_b; - - int partitionNumber_a = 1; - int partitionNumber_b = 1; - for (int i = 0; i < totalRows_a; i++) { - QString name_a = columns[0]->textAt(i); - QString name_b = columns[1]->textAt(i); - double value = columns[2]->valueAt(i); - - if (catToNumber_a[name_a] == 0) { - catToNumber_a[name_a] = partitionNumber_a; - partitionNumber_a++; - } - - if (catToNumber_b[name_b] == 0) { - catToNumber_b[name_b] = partitionNumber_b; - partitionNumber_b++; - } - - groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += value; - replicates[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += 1; - } - - int replicate = replicates[0][0]; - for (int i = 0; i < np_a; i++) - for (int j = 0; j < np_b; j++) { - if (replicates[i][j] == 0) { - printError("Dataset should have atleast one data value corresponding to each feature combination"); - return; - } - if (replicates[i][j] != replicate) { - printError("Number of experiments perfomed for each combination of levels
" - "between Independet Var.1 and Independent Var.2 must be equal"); - return; - } - groupMean[i][j] /= replicates[i][j]; - } - -// for (int i = 0; i < np_a; i++) -// for (int j = 0; j < np_b; j++) -// groupMean[i][j] = int(groupMean[i][j]); - - double ss_within = 0; - for (int i = 0; i < totalRows_a; i++) { - QString name_a = columns[0]->textAt(i); - QString name_b = columns[1]->textAt(i); - double value = columns[2]->valueAt(i); - - ss_within += gsl_pow_2(value - groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1]); - } - - int df_within = (replicate - 1) * np_a * np_b; - double ms_within = ss_within / df_within; - - double mean_a[np_a]; - double mean_b[np_b]; - for (int i = 0; i < np_a; i++) { - for (int j = 0; j < np_b; j++) { - mean_a[i] += groupMean[i][j] / np_b; - mean_b[j] += groupMean[i][j] / np_a; - } - } - - double mean = 0; - for (int i = 0; i < np_a; i++) - mean += mean_a[i] / np_a; - - - double ss_a = 0; - for (int i = 0; i < np_a; i++) - ss_a += gsl_pow_2(mean_a[i] - mean); - ss_a *= replicate * np_b; - - int df_a = np_a - 1; - double ms_a = ss_a / df_a; - - double ss_b = 0; - for (int i = 0; i < np_b; i++) - ss_b += gsl_pow_2(mean_b[i] - mean); - ss_b *= replicate * np_a; - - int df_b = np_b - 1; - double ms_b = ss_b / df_b; - - double ss_interaction = 0; - - for (int i = 0; i < np_a; i++) - for (int j = 0; j < np_b; j++) - ss_interaction += gsl_pow_2(groupMean[i][j] - mean_a[i] - mean_b[j] + mean); - - ss_interaction *= replicate; - int df_interaction = (np_a - 1) * (np_b - 1); - double ms_interaction = ss_interaction / df_interaction; - - QString partitionNames_a[np_a]; - QString partitionNames_b[np_b]; - - QMapIterator itr_a(catToNumber_a); - while (itr_a.hasNext()) { - itr_a.next(); - partitionNames_a[itr_a.value()-1] = itr_a.key(); - } - - QMapIterator itr_b(catToNumber_b); - while (itr_b.hasNext()) { - itr_b.next(); - partitionNames_b[itr_b.value()-1] = itr_b.key(); - } - - // printing table; - // cell constructor structure; data, level, rowSpanCount, columnSpanCount, isHeader; - QList rowMajor; - rowMajor.append(new Cell("", 0, true, "", 2, 1)); - for (int i = 0; i < np_b; i++) - rowMajor.append(new Cell(partitionNames_b[i], 0, true, "", 1, 2)); - rowMajor.append(new Cell("Mean", 0, true, "", 2)); - - for (int i = 0; i < np_b; i++) { - rowMajor.append(new Cell("Mean", 1, true)); - rowMajor.append(new Cell("Replicate", 1, true)); - } - - int level = 2; - for (int i = 0; i < np_a; i++) { - rowMajor.append(new Cell(partitionNames_a[i], level, true)); - for (int j = 0; j < np_b; j++) { - rowMajor.append(new Cell(round(groupMean[i][j]), level)); - rowMajor.append(new Cell(replicates[i][j], level)); - } - rowMajor.append(new Cell(round(mean_a[i]), level)); - level++; - } - - rowMajor.append(new Cell("Mean", level, true)); - for (int i = 0; i < np_b; i++) - rowMajor.append(new Cell(round(mean_b[i]), level, false, "", 1, 2)); - rowMajor.append(new Cell(round(mean), level)); - - statsTable = "

" + i18n("Contingency Table") + "

"; - statsTable += getHtmlTable3(rowMajor); - - statsTable += "
"; - statsTable += "

" + i18n("results table") + "

"; - - rowMajor.clear(); - level = 0; - rowMajor.append(new Cell("", level, true)); - rowMajor.append(new Cell("SS", level, true)); - rowMajor.append(new Cell("DF", level, true, "degree of freedom")); - rowMajor.append(new Cell("MS", level, true)); - - level++; - rowMajor.append(new Cell(columns[0]->name(), level, true)); - rowMajor.append(new Cell(round(ss_a), level)); - rowMajor.append(new Cell(df_a, level)); - rowMajor.append(new Cell(round(ms_a), level)); - - level++; - rowMajor.append(new Cell(columns[1]->name(), level, true)); - rowMajor.append(new Cell(round(ss_b), level)); - rowMajor.append(new Cell(df_b, level)); - rowMajor.append(new Cell(round(ms_b), level)); - - level++; - rowMajor.append(new Cell("Interaction", level, true)); - rowMajor.append(new Cell(round(ss_interaction), level)); - rowMajor.append(new Cell(df_interaction, level)); - rowMajor.append(new Cell(round(ms_interaction), level)); - - level++; - rowMajor.append(new Cell("Within", level, true)); - rowMajor.append(new Cell(round(ss_within), level)); - rowMajor.append(new Cell(df_within, level)); - rowMajor.append(new Cell(round(ms_within), level)); - - statsTable += getHtmlTable3(rowMajor); - - double fValue_a = ms_a / ms_within; - double fValue_b = ms_b / ms_within; - double fValue_interaction = ms_interaction / ms_within; - - double pValue_a = nsl_stats_fdist_p(fValue_a, static_cast(np_a - 1), df_a); - double pValue_b = nsl_stats_fdist_p(fValue_b, static_cast(np_b - 1), df_b); - - printLine(0, "F(df" + columns[0]->name() + ", dfwithin) is " + round(fValue_a), "blue"); - printLine(1, "F(df" + columns[1]->name() + ", dfwithin) is " + round(fValue_b), "blue"); - printLine(2, "F(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue"); - - printLine(4, "P(df" + columns[0]->name() + ", dfwithin) is " + round(pValue_a), "blue"); - printLine(5, "P(df" + columns[1]->name() + ", dfwithin) is " + round(pValue_b), "blue"); -// printLine(2, "P(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue"); - - statisticValue.append(fValue_a); - statisticValue.append(fValue_b); - statisticValue.append(fValue_interaction); - - pValue.append(pValue_a); - pValue.append(pValue_b); - - return; -} - -/**************************************Levene Test****************************************/ -// Some reference to local variables. -// np = number of partitions -// df = degree of fredom -// totalRows = total number of rows in column - -// these variables are taken from: https://en.wikipedia.org/wiki/Levene%27s_test -// yiBar = mean of ith group; -// Zij = |Yij - yiBar| -// ziBar = mean of Zij for group i -// ziBarBar = mean for all zij -// ni = number of elements in group i -void HypothesisTestPrivate::performLeveneTest(bool categoricalVariable) { - if (columns.size() != 2) { - printError("Inappropriate number of columns selected"); - return; - } - - int np = 0; - int n = 0; - - if (!categoricalVariable && isNumericOrInteger(columns[0])) - np = columns.size(); - else - countPartitions(columns[0], np, n); - - if (np < 2) { - printError("Select atleast two columns / classes"); - return; - } - - double* yiBar = new double[np]; - double* ziBar = new double[np]; - double ziBarBar = 0; - double* ni = new double[np]; - - for (int i = 0; i < np; i++) { - yiBar[i] = 0; - ziBar[i] = 0; - ni[i] = 0; - } - - double fValue; - int df = 0; - - int totalRows = 0; - - QString* colNames = new QString[np]; - if (!categoricalVariable && isNumericOrInteger(columns[0])) { - totalRows = columns[0]->rowCount(); - - double value = 0; - for (int j = 0; j < totalRows; j++) { - int numberNaNCols = 0; - for (int i = 0; i < np; i++) { - value = columns[i]->valueAt(j); - if (std::isnan(value)) { - numberNaNCols++; - continue; - } - yiBar[i] += value; - ni[i]++; - n++; - } - if (numberNaNCols == np) { - totalRows = j; - break; - } - } - - for (int i = 0; i < np; i++) { - if (ni[i] > 0) - yiBar[i] = yiBar[i] / ni[i]; - else { - printError("One of the selected columns is empty"); - return; - } - } - - for (int j = 0; j < totalRows; j++) { - for (int i = 0; i < np; i++) { - value = columns[i]->valueAt(j); - if (!(std::isnan(value))) - ziBar[i] += fabs(value - yiBar[i]); - } - } - - for (int i = 0; i < np; i++) { - ziBarBar += ziBar[i]; - if (ni[i] > 0) - ziBar[i] = ziBar[i] / ni[i]; - } - - ziBarBar = ziBarBar / n; - - double numberatorValue = 0; - double denominatorValue = 0; - - for (int j = 0; j < totalRows; j++) { - for (int i = 0; i < np; i++) { - value = columns[i]->valueAt(j); - if (!(std::isnan(value))) { - double zij = fabs(value - yiBar[i]); - denominatorValue += gsl_pow_2( (zij - ziBar[i])); - } - } - } - - - if (denominatorValue <= 0) { - printError( i18n("Denominator value is %1", denominatorValue)); - return; - } - - for (int i = 0; i < np; i++) { - colNames[i] = columns[i]->name(); - numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar)); - } - - fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue); - - } else { - QMap classnameToIndex; - - AbstractColumn::ColumnMode originalColMode = columns[0]->columnMode(); - columns[0]->setColumnMode(AbstractColumn::Text); - - int partitionNumber = 1; - QString name; - double value; - int classIndex; - - for (int j = 0; j < n; j++) { - name = columns[0]->textAt(j); - value = columns[1]->valueAt(j); - if (std::isnan(value)) { - n = j; - break; - } - - if (classnameToIndex[name] == 0) { - classnameToIndex[name] = partitionNumber; - partitionNumber++; - } - - classIndex = classnameToIndex[name]-1; - ni[classIndex]++; - yiBar[classIndex] += value; - } - - for (int i = 0; i < np; i++) { - if (ni[i] > 0) - yiBar[i] = yiBar[i] / ni[i]; - else { - printError("One of the selected columns is empty"); - return; - } - } - - for (int j = 0; j < n; j++) { - name = columns[0]->textAt(j); - value = columns[1]->valueAt(j); - classIndex = classnameToIndex[name] - 1; - ziBar[classIndex] += fabs(value - yiBar[classIndex]); - } - - for (int i = 0; i < np; i++) { - ziBarBar += ziBar[i]; - ziBar[i] = ziBar[i] / ni[i]; - } - - ziBarBar = ziBarBar / n; - - double numberatorValue = 0; - double denominatorValue = 0; - - for (int j = 0; j < n; j++) { - name = columns[0]->textAt(j); - value = columns[1]->valueAt(j); - classIndex = classnameToIndex[name] - 1; - double zij = fabs(value - yiBar[classIndex]); - denominatorValue += gsl_pow_2( (zij - ziBar[classIndex])); - } - - for (int i = 0; i < np; i++) - numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar)); - - if (denominatorValue <= 0) { - printError( "number of data points is less or than equal to number of categorical variables"); - return; - } - - fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue); - - QMapIterator i(classnameToIndex); - while (i.hasNext()) { - i.next(); - colNames[i.value()-1] = columns[0]->name() + " " + i.key(); - } - - columns[0]->setColumnMode(originalColMode); - } - - df = n - np; - - // now making the stats table. - int rowCount = np+1; - int columnCount = 4; - - QVariant* rowMajor = new QVariant[rowCount*columnCount]; - // header data; - rowMajor[0] = ""; - rowMajor[1] = "Ni"; - rowMajor[2] = "yiBar"; - rowMajor[3] = "ziBar"; - - // table data - for (int row_i = 1; row_i < rowCount; row_i++) { - rowMajor[row_i*columnCount] = colNames[row_i-1]; - rowMajor[row_i*columnCount + 1] = ni[row_i-1]; - rowMajor[row_i*columnCount + 2] = yiBar[row_i-1]; - rowMajor[row_i*columnCount + 3] = ziBar[row_i-1]; - } - - statsTable = getHtmlTable(rowCount, columnCount, rowMajor); - - delete[] rowMajor; - delete[] yiBar; - delete[] ziBar; - delete[] ni; - - pValue.append(nsl_stats_fdist_p(fValue, static_cast(np-1), df)); - - printLine(0, "Null Hypothesis: Variance is equal between all classes", "blue"); - printLine(1, "Alternate Hypothesis: Variance is not equal in at-least one pair of classes", "blue"); - printLine(2, i18n("Significance level is %1", round(significanceLevel)), "blue"); - printLine(4, i18n("F Value is %1 ", round(fValue)), "green"); - printLine(5, i18n("P Value is %1 ", pValue[0]), "green"); - printLine(6, i18n("Degree of Freedom is %1", df), "green"); - - if (pValue[0] <= significanceLevel) { - printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", significanceLevel)); - printLine(8, "Requirement for homogeneity is not met", "red"); - } else { - printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true")); - printLine(8, "Requirement for homogeneity is met", "green"); - } - - statisticValue.append(fValue); - return; -} - -/**************************************Spearman Correlation Test*****************************/ - -// N: Number of observations - -//TODO: add functionality where col1 contains categorical variables. -void HypothesisTestPrivate::performSpearmanCorrelation() { - int N = columns[0]->rowCount(); - - QString col1Name = columns[0]->name(); - QString col2Name = columns[1]->name(); - - currTestName = i18n("Pearson r Correlation Test between %1 and %2", col1Name, col2Name); - double sumXY = 0; - double sumX = 0; - double sumY = 0; - double sumXSq = 0; - double sumYSq = 0; - for (int i = 0; i < N; i++) { - double valueCol1 = columns[0]->valueAt(i); - double valueCol2 = columns[1]->valueAt(i); - - if (std::isnan(valueCol1) || std::isnan(valueCol2)) { - if (std::isnan(valueCol1) && std::isnan(valueCol2)) { - N = i; - break; - } - printError(i18n("Number of values in %1 and %2 are not equal", col1Name, col2Name)); - return; - } - - sumXY += valueCol1 * valueCol2; - sumX += valueCol1; - sumY += valueCol2; - sumXSq += gsl_pow_2(valueCol1); - sumYSq += gsl_pow_2(valueCol2); - } - - QList rowMajor; - int level = 0; - rowMajor.append(new Cell("", level, true)); - rowMajor.append(new Cell("N", level, true, "Total Number of Observations")); - rowMajor.append(new Cell("Sum " + col1Name, level, true, "Sum of Values in " + col1Name)); - rowMajor.append(new Cell("Sum " + col1Name + "2", level, true, "Sum of Square of Values in " + col1Name)); - rowMajor.append(new Cell("Sum " + col2Name, level, true, "Sum of Values in " + col2Name)); - rowMajor.append(new Cell("Sum " + col2Name + "2", level, true, "Sum of Square of Values in " + col2Name)); - rowMajor.append(new Cell("Sum " + col1Name + "x" + col2Name, level, true, "Sum of product of paired scores")); - - level++; - rowMajor.append(new Cell("Results", level, true)); - rowMajor.append(new Cell(N, level)); - rowMajor.append(new Cell(sumX, level)); - rowMajor.append(new Cell(sumXSq, level)); - rowMajor.append(new Cell(sumY, level)); - rowMajor.append(new Cell(sumYSq, level)); - rowMajor.append(new Cell(sumXY, level)); - - statsTable = "

" + i18n("Statistic Table") + "

"; - statsTable += getHtmlTable3(rowMajor); - - statisticValue.append((N * sumXY - sumX * sumY) / - qSqrt((N * sumXSq - gsl_pow_2(sumX)) * - (N * sumYSq - gsl_pow_2(sumY)))); - - printLine(0, "Correlation Value is " + round(statisticValue[0]), "blue"); -} - -/***************************************Helper Functions*************************************/ - -QString HypothesisTestPrivate::round(QVariant number, int precision) { - if (number.userType() == QMetaType::Double || number.userType() == QMetaType::Float) { - double multiplierPrecision = qPow(10, precision); - int tempNum = int(number.toDouble()*multiplierPrecision*10); - - if (tempNum % 10 < 5) - return QString::number((tempNum/10) / multiplierPrecision); - else - return QString::number((tempNum/10 + 1) / multiplierPrecision); - } - return i18n("%1", number.toString()); -} - - -bool HypothesisTestPrivate::isNumericOrInteger(Column* column) { - return (column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer); -} - -HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStats(const Column* column, int& count, double& sum, double& mean, double& std) { - sum = 0; - mean = 0; - std = 0; - - count = column->rowCount(); - for (int i = 0; i < count; i++) { - double row = column->valueAt(i); - if ( std::isnan(row)) { - count = i; - break; - } - sum += row; - } - - if (count < 1) - return HypothesisTestPrivate::ErrorEmptyColumn; - - mean = sum / count; - - for (int i = 0; i < count; i++) { - double row = column->valueAt(i); - std += gsl_pow_2( (row - mean)); - } - - if (count > 1) - std = std / (count-1); - std = qSqrt(std); - - return HypothesisTestPrivate::NoError; -} - -HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std) { - sum = 0; - mean = 0; - std = 0; - - int count1 = column1->rowCount(); - int count2 = column2->rowCount(); - - count = qMin(count1, count2); - double cell1, cell2; - for (int i = 0; i < count; i++) { - cell1 = column1->valueAt(i); - cell2 = column2->valueAt(i); - - if (std::isnan(cell1) || std::isnan(cell2)) { - if (std::isnan(cell1) && std::isnan(cell2)) - count = i; - else - return HypothesisTestPrivate::ErrorUnqualSize; - break; - } - - sum += cell1 - cell2; - } - - if (count < 1) - return HypothesisTestPrivate::ErrorEmptyColumn; - - mean = sum / count; - - double row; - for (int i = 0; i < count; i++) { - cell1 = column1->valueAt(i); - cell2 = column2->valueAt(i); - row = cell1 - cell2; - std += gsl_pow_2( (row - mean)); - } - - if (count > 1) - std = std / (count-1); - - std = qSqrt(std); - return HypothesisTestPrivate::NoError; -} - -void HypothesisTestPrivate::countPartitions(Column* column, int& np, int& totalRows) { - totalRows = column->rowCount(); - np = 0; - QString cellValue; - QMap discoveredCategoricalVar; - - AbstractColumn::ColumnMode originalColMode = column->columnMode(); - column->setColumnMode(AbstractColumn::Text); - - for (int i = 0; i < totalRows; i++) { - cellValue = column->textAt(i); - - if (cellValue.isEmpty()) { - totalRows = i; - break; - } - - if (discoveredCategoricalVar[cellValue]) - continue; - - discoveredCategoricalVar[cellValue] = true; - np++; - } - column->setColumnMode(originalColMode); -} - -HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows) { - Column* columns[] = {column1, column2}; - - for (int i = 0; i < np; i++) { - n[i] = 0; - sum[i] = 0; - mean[i] = 0; - std[i] = 0; - } - - AbstractColumn::ColumnMode originalColMode = columns[0]->columnMode(); - columns[0]->setColumnMode(AbstractColumn::Text); - - int partitionNumber = 1; - for (int i = 0; i < totalRows; i++) { - QString name = columns[0]->textAt(i); - double value = columns[1]->valueAt(i); - - if (std::isnan(value)) { - columns[0]->setColumnMode(originalColMode); - return HypothesisTestPrivate::ErrorUnqualSize; - } - - if (colName[name] == 0) { - colName[name] = partitionNumber; - partitionNumber++; - } - - n[colName[name]-1]++; - sum[colName[name]-1] += value; - } - - for (int i = 0; i < np; i++) - mean[i] = sum[i] / n[i]; - - for (int i = 0; i < totalRows; i++) { - QString name = columns[0]->textAt(i); - double value = columns[1]->valueAt(i); - - std[colName[name]-1] += gsl_pow_2( (value - mean[colName[name]-1])); - } - - for (int i = 0; i < np; i++) { - if (n[i] > 1) - std[i] = std[i] / (n[i] - 1); - std[i] = qSqrt(std[i]); - } - - columns[0]->setColumnMode(originalColMode); - if (isNumericOrInteger(columns[0])) { - - } - - return HypothesisTestPrivate::NoError; -} - - -//TODO change ("⋖") symbol to ("<"), currently macro UTF8_QSTRING is not working properly if used "<" symbol; -// TODO: check for correctness between: for TestZ with TailTwo -// pValue.append(2*gsl_cdf_tdist_P(value, df) v/s -// pValue.append(gsl_cdf_tdis_P(value, df) + gsl_cdf_tdis_P(-value, df); -double HypothesisTestPrivate::getPValue(const HypothesisTest::Test::Type& test, double& value, const QString& col1Name, const QString& col2Name, const double mean, const double sp, const int df) { - - switch (test) { - case HypothesisTest::Test::Type::TTest: { - switch (tailType) { - case HypothesisTest::Test::Tail::Negative: { - pValue.append(gsl_cdf_tdist_P(value, df)); - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≥"), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue"); - break; - } - case HypothesisTest::Test::Tail::Positive: { - value *= -1; - pValue.append(gsl_cdf_tdist_P(value, df)); - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≤"), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING(">"), col2Name), "blue"); - break; - } - case HypothesisTest::Test::Tail::Two: { - pValue.append(2.*gsl_cdf_tdist_P(-fabs(value), df)); - - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("="), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≠"), col2Name), "blue"); - break; - } - } - break; - } - case HypothesisTest::Test::Type::ZTest: { - switch (tailType) { - case HypothesisTest::Test::Tail::Negative: { - pValue.append(gsl_cdf_gaussian_P(value - mean, sp)); - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≥"), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue"); - break; - } - case HypothesisTest::Test::Tail::Positive: { - value *= -1; - pValue.append(nsl_stats_tdist_p(value - mean, sp)); - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≤"), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING(">"), col2Name), "blue"); - break; - } - case HypothesisTest::Test::Tail::Two: { - pValue.append(2.*gsl_cdf_gaussian_P(value - mean, sp)); - printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("="), col2Name), "blue"); - printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≠"), col2Name), "blue"); - break; - } - } - break; - } - case HypothesisTest::Test::Type::Anova: - case HypothesisTest::Test::Type::NoneType: - break; - } - - if (pValue[0] > 1) - return 1; - return pValue[0]; -} - -int HypothesisTestPrivate::setSpanValues(HypothesisTestPrivate::Node* root, int& totalLevels) { - if (root == nullptr) { - totalLevels = 0; - return 0; - } - - int val = 0; - int level = 0; - int maxLevel = 0; - for (int i = 0; i < root->children.size(); i++) { - val += setSpanValues(root->children[i], level); - maxLevel = std::max(level, maxLevel); - } - - totalLevels = maxLevel + 1; - if (val == 0) - root->spanCount = 1; - else - root->spanCount = val; - - return root->spanCount; - -} - -QString HypothesisTestPrivate::getHtmlHeader(HypothesisTestPrivate::Node *root) { - if (root == nullptr) - return QString(); - - QString header; - - int totalLevels = 0; - - setSpanValues(root, totalLevels); - totalLevels -= 1; - - root->level = 0; - QQueue nodeQueue; - - for (int i = 0; i < root->children.size(); i++) { - Node* child = root->children[i]; - child->level = 1; - nodeQueue.enqueue(child); - } - - int prevLevel = 1; - header = " "; - header += " "; - - while(!nodeQueue.isEmpty()) { - Node* node = nodeQueue.dequeue(); - int nodeLevel = node->level; - - for (int i = 0; i < node->children.size(); i++) { - Node* child = node->children[i]; - child->level = nodeLevel + 1; - nodeQueue.enqueue(child); - } - - if (nodeLevel != prevLevel) { - prevLevel = nodeLevel; - header += " "; - header += " "; - } - - header += " " + node->data + ""; - } - header += " "; - return header; -} - -QString HypothesisTestPrivate::getHtmlTable2(int rowCount, int columnCount, Node* columnHeaderRoot, QVariant* rowMajor) { - if (rowCount < 1 || columnCount < 1) - return QString(); - - QString table; - - table = "" - ""; - table += getHtmlHeader(columnHeaderRoot); - - for (int i = 0; i < rowCount; i++) { - table += " "; - table += " "; - for (int j = 1; j < columnCount; j++) - table += " "; - table += " "; - } - - table += "
" + round(rowMajor[i*columnCount]) + "" + round(rowMajor[i*columnCount + j]) + "
"; - return table; -} - - -QString HypothesisTestPrivate::getHtmlTable(int row, int column, QVariant* rowMajor) { - if (row < 1 || column < 1) - return QString(); - - QString table; - table = "" - "" - " "; - - QString bg = "tg-0pky"; - bool pky = true; - - QString element; - table += " "; - for (int j = 0; j < column; j++) { - element = rowMajor[j].toString(); - table += " "; - } - table += " "; - - if (pky) - bg = "tg-0pky"; - else - bg = "tg-btxf"; - pky = !pky; - - for (int i = 1; i < row; i++) { - table += " "; - - QString element = round(rowMajor[i*column]); - table += " "; - for (int j = 1; j < column; j++) { - element = round(rowMajor[i*column+j]); - table += " "; - } - - table += " "; - if (pky) - bg = "tg-0pky"; - else - bg = "tg-btxf"; - pky = !pky; - } - table += "
" + i18n("%1", element) + "
" + i18n("%1", element) + "" + i18n("%1", element) + "
"; - - return table; -} - -QString HypothesisTestPrivate::getHtmlTable3(const QList& rowMajor) { - tooltips.clear(); - int rowMajorSize = rowMajor.size(); - - if (rowMajorSize == 0) - return QString(); - - QString table; - table = ""; - - table += ""; - - table += " "; - int prevLevel = 0; - for (int i = 0; i < rowMajorSize; i++) { - Cell* currCell = rowMajor[i]; - if (currCell->level != prevLevel) { - table += " "; - table += " "; - prevLevel = currCell->level; - } - QString cellStartTag = ""; - table += "
isHeader) { - cellStartTag = "" + - i18n("%1", currCell->data) + - cellEndTag; - - if (!currCell->tooltip.isEmpty()) - tooltips.insert(currCell->data, currCell->tooltip); - } - table += "
"; - return table; -} - -QString HypothesisTestPrivate::getLine(const QString& msg, const QString& color) { - return "

" + i18n("%1", msg) + "

"; -} - -void HypothesisTestPrivate::printLine(const int& index, const QString& msg, const QString& color) { - if (index < 0 || index >= 10) - return; - - resultLine[index]->setText(getLine(msg, color)); - return; -} - -void HypothesisTestPrivate::printTooltip(const int &index, const QString &msg) { - if (index < 0 || index >= 10) - return; - - resultLine[index]->setToolTip(i18n("%1", msg)); -} - -void HypothesisTestPrivate::printError(const QString& errorMsg) { - printLine(0, errorMsg, "red"); -} - - -/********************************************************************************** - * virtual functions implementations - * ********************************************************************************/ - -/*! - Saves as XML. - */ -void HypothesisTest::save(QXmlStreamWriter* writer) const { - writer->writeStartElement("hypothesisTest"); - writeBasicAttributes(writer); - writeCommentElement(writer); - - writer->writeEndElement(); -} - -/*! - Loads from XML. -*/ -bool HypothesisTest::load(XmlStreamReader* reader, bool preview) { - Q_UNUSED(preview); - if (!readBasicAttributes(reader)) - return false; - - return !reader->hasError(); -} - -Spreadsheet *HypothesisTest::dataSourceSpreadsheet() const { - return d->dataSourceSpreadsheet; -} - - -bool HypothesisTest::exportView() const { - return true; -} - -bool HypothesisTest::printView() { - return true; -} - -bool HypothesisTest::printPreview() const { - return true; -} - -/*! Constructs a primary view on me. - This method may be called multiple times during the life time of an Aspect, or it might not get - called at all. Aspects must not depend on the existence of a view for their operation. -*/ -QWidget* HypothesisTest::view() const { - if (!m_partView) { - m_view = new HypothesisTestView(const_cast(this)); - m_partView = m_view; - } - return m_partView; -} - -/*! - Returns a new context menu. The caller takes ownership of the menu. -*/ -QMenu* HypothesisTest::createContextMenu() { - QMenu* menu = AbstractPart::createContextMenu(); - // Q_ASSERT(menu); - // emit requestProjectContextMenu(menu); - return menu; -} diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp index 8520c947b..9afa312db 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp @@ -1,2969 +1,2975 @@ /*************************************************************************** File : SpreadsheetView.cpp Project : LabPlot Description : View class for Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2011-2019 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "SpreadsheetView.h" #include "backend/spreadsheet/SpreadsheetModel.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/spreadsheet/SpreadsheetItemDelegate.h" #include "commonfrontend/spreadsheet/SpreadsheetHeaderView.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "backend/core/datatypes/SimpleCopyThroughFilter.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/String2DoubleFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/core/datatypes/String2DateTimeFilter.h" #include "backend/pivot/PivotTable.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "kdefrontend/spreadsheet/AddSubtractValueDialog.h" #include "kdefrontend/spreadsheet/DropValuesDialog.h" #include "kdefrontend/spreadsheet/SortDialog.h" #include "kdefrontend/spreadsheet/RandomValuesDialog.h" #include "kdefrontend/spreadsheet/EquidistantValuesDialog.h" #include "kdefrontend/spreadsheet/FunctionValuesDialog.h" #include "kdefrontend/spreadsheet/StatisticsDialog.h" #include //for std::reverse /*! \class SpreadsheetView \brief View class for Spreadsheet \ingroup commonfrontend */ SpreadsheetView::SpreadsheetView(Spreadsheet* spreadsheet, bool readOnly) : QWidget(), m_tableView(new QTableView(this)), m_spreadsheet(spreadsheet), m_model(new SpreadsheetModel(spreadsheet)), m_readOnly(readOnly) { auto* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_tableView); if (m_readOnly) m_tableView->setEditTriggers(QTableView::NoEditTriggers); init(); //resize the view to show alls columns and the first 50 rows. //no need to resize the view when the project is being opened, //all views will be resized to the stored values at the end if (!m_spreadsheet->isLoading()) { int w = m_tableView->verticalHeader()->width(); int h = m_horizontalHeader->height(); for (int i = 0; i < m_horizontalHeader->count(); ++i) w += m_horizontalHeader->sectionSize(i); if (m_tableView->verticalHeader()->count() > 50 || m_tableView->verticalHeader()->count() < 10) h += m_tableView->verticalHeader()->sectionSize(0)*50; else h += m_tableView->verticalHeader()->sectionSize(0)*m_tableView->verticalHeader()->count(); resize(w+50, h); } KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); showComments(group.readEntry(QLatin1String("ShowComments"), false)); } SpreadsheetView::~SpreadsheetView() { delete m_model; } void SpreadsheetView::init() { initActions(); initMenus(); m_tableView->setModel(m_model); m_tableView->setItemDelegate(new SpreadsheetItemDelegate(this)); m_tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); //horizontal header m_horizontalHeader = new SpreadsheetHeaderView(this); m_horizontalHeader->setSectionsClickable(true); m_horizontalHeader->setHighlightSections(true); m_tableView->setHorizontalHeader(m_horizontalHeader); m_horizontalHeader->setSectionsMovable(true); m_horizontalHeader->installEventFilter(this); resizeHeader(); connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionMoved, this, &SpreadsheetView::handleHorizontalSectionMoved); connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionDoubleClicked, this, &SpreadsheetView::handleHorizontalHeaderDoubleClicked); connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionResized, this, &SpreadsheetView::handleHorizontalSectionResized); connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionClicked, this, &SpreadsheetView::columnClicked); // vertical header QHeaderView* v_header = m_tableView->verticalHeader(); v_header->setSectionResizeMode(QHeaderView::Fixed); v_header->setSectionsMovable(false); v_header->installEventFilter(this); setFocusPolicy(Qt::StrongFocus); setFocus(); installEventFilter(this); connectActions(); showComments(false); connect(m_model, &SpreadsheetModel::headerDataChanged, this, &SpreadsheetView::updateHeaderGeometry); connect(m_model, &SpreadsheetModel::headerDataChanged, this, &SpreadsheetView::handleHeaderDataChanged); connect(m_spreadsheet, &Spreadsheet::aspectAdded, this, &SpreadsheetView::handleAspectAdded); connect(m_spreadsheet, &Spreadsheet::aspectAboutToBeRemoved,this, &SpreadsheetView::handleAspectAboutToBeRemoved); connect(m_spreadsheet, &Spreadsheet::requestProjectContextMenu, this, &SpreadsheetView::createContextMenu); for (auto* column : m_spreadsheet->children()) connect(column, &Column::requestProjectContextMenu, this, &SpreadsheetView::createColumnContextMenu); //selection relevant connections QItemSelectionModel* sel_model = m_tableView->selectionModel(); connect(sel_model, &QItemSelectionModel::currentColumnChanged, this, &SpreadsheetView::currentColumnChanged); connect(sel_model, &QItemSelectionModel::selectionChanged, this, &SpreadsheetView::selectionChanged); connect(sel_model, &QItemSelectionModel::selectionChanged, this, &SpreadsheetView::selectionChanged); connect(m_spreadsheet, &Spreadsheet::columnSelected, this, &SpreadsheetView::selectColumn); connect(m_spreadsheet, &Spreadsheet::columnDeselected, this, &SpreadsheetView::deselectColumn); } /*! set the column sizes to the saved values or resize to content if no size was saved yet */ void SpreadsheetView::resizeHeader() { DEBUG("SpreadsheetView::resizeHeader()"); for (int i = 0; i < m_spreadsheet->children().size(); ++i) { Column* col = m_spreadsheet->child(i); if (col->width() == 0) m_tableView->resizeColumnToContents(i); else m_tableView->setColumnWidth(i, col->width()); } } void SpreadsheetView::initActions() { // selection related actions action_cut_selection = new QAction(QIcon::fromTheme("edit-cut"), i18n("Cu&t"), this); action_copy_selection = new QAction(QIcon::fromTheme("edit-copy"), i18n("&Copy"), this); action_paste_into_selection = new QAction(QIcon::fromTheme("edit-paste"), i18n("Past&e"), this); action_mask_selection = new QAction(QIcon::fromTheme("edit-node"), i18n("&Mask Selection"), this); action_unmask_selection = new QAction(QIcon::fromTheme("format-remove-node"), i18n("&Unmask Selection"), this); action_clear_selection = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selection"), this); action_select_all = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this); // action_set_formula = new QAction(QIcon::fromTheme(QString()), i18n("Assign &Formula"), this); // action_recalculate = new QAction(QIcon::fromTheme(QString()), i18n("Recalculate"), this); action_fill_sel_row_numbers = new QAction(QIcon::fromTheme(QString()), i18n("Row Numbers"), this); action_fill_row_numbers = new QAction(QIcon::fromTheme(QString()), i18n("Row Numbers"), this); action_fill_random = new QAction(QIcon::fromTheme(QString()), i18n("Uniform Random Values"), this); action_fill_random_nonuniform = new QAction(QIcon::fromTheme(QString()), i18n("Random Values"), this); action_fill_equidistant = new QAction(QIcon::fromTheme(QString()), i18n("Equidistant Values"), this); action_fill_function = new QAction(QIcon::fromTheme(QString()), i18n("Function Values"), this); action_fill_const = new QAction(QIcon::fromTheme(QString()), i18n("Const Values"), this); //spreadsheet related actions action_toggle_comments = new QAction(QIcon::fromTheme("document-properties"), i18n("Show Comments"), this); action_clear_spreadsheet = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Spreadsheet"), this); action_clear_masks = new QAction(QIcon::fromTheme("format-remove-node"), i18n("Clear Masks"), this); action_sort_spreadsheet = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Sort Spreadsheet"), this); action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this); action_statistics_all_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this ); action_pivot_table = new QAction(QIcon::fromTheme("table"), i18n("Pivot Table"), this); action_do_hypothesis_test = new QAction(i18n("Hypothesis Test"), this); // column related actions action_insert_column_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Column Left"), this); action_insert_column_right = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("Insert Column Right"), this); action_insert_columns_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Multiple Columns Left"), this); action_insert_columns_right = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("Insert Multiple Columns Right"), this); action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remove Selected Columns"), this); action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Selected Columns"), this); action_set_as_none = new QAction(i18n("None"), this); action_set_as_none->setData(AbstractColumn::NoDesignation); action_set_as_x = new QAction("X", this); action_set_as_x->setData(AbstractColumn::X); action_set_as_y = new QAction("Y", this); action_set_as_y->setData(AbstractColumn::Y); action_set_as_z = new QAction("Z", this); action_set_as_z->setData(AbstractColumn::Z); action_set_as_xerr = new QAction(i18n("X-error"), this); action_set_as_xerr->setData(AbstractColumn::XError); action_set_as_xerr_minus = new QAction(i18n("X-error minus"), this); action_set_as_xerr_minus->setData(AbstractColumn::XErrorMinus); action_set_as_xerr_plus = new QAction(i18n("X-error plus"), this); action_set_as_xerr_plus->setData(AbstractColumn::XErrorPlus); action_set_as_yerr = new QAction(i18n("Y-error"), this); action_set_as_yerr->setData(AbstractColumn::YError); action_set_as_yerr_minus = new QAction(i18n("Y-error minus"), this); action_set_as_yerr_minus->setData(AbstractColumn::YErrorMinus); action_set_as_yerr_plus = new QAction(i18n("Y-error plus"), this); action_set_as_yerr_plus->setData(AbstractColumn::YErrorPlus); //data manipulation action_add_value = new QAction(i18n("Add Value"), this); action_add_value->setData(AddSubtractValueDialog::Add); action_subtract_value = new QAction(i18n("Subtract Value"), this); action_subtract_value->setData(AddSubtractValueDialog::Subtract); action_multiply_value = new QAction(i18n("Multiply by Value"), this); action_multiply_value->setData(AddSubtractValueDialog::Multiply); action_divide_value = new QAction(i18n("Divide by Value"), this); action_divide_value->setData(AddSubtractValueDialog::Divide); action_drop_values = new QAction(QIcon::fromTheme(QString()), i18n("Drop Values"), this); action_mask_values = new QAction(QIcon::fromTheme(QString()), i18n("Mask Values"), this); action_reverse_columns = new QAction(QIcon::fromTheme(QString()), i18n("Reverse"), this); // action_join_columns = new QAction(QIcon::fromTheme(QString()), i18n("Join"), this); action_normalize_columns = new QAction(QIcon::fromTheme(QString()), i18n("&Normalize"), this); action_normalize_selection = new QAction(QIcon::fromTheme(QString()), i18n("&Normalize Selection"), this); //sort and statistics action_sort_columns = new QAction(QIcon::fromTheme(QString()), i18n("&Selected Columns"), this); action_sort_asc_column = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Ascending"), this); action_sort_desc_column = new QAction(QIcon::fromTheme("view-sort-descending"), i18n("&Descending"), this); action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Column Statisti&cs"), this); // row related actions action_insert_row_above = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("Insert Row Above"), this); action_insert_row_below = new QAction(QIcon::fromTheme("edit-table-insert-row-below"), i18n("Insert Row Below"), this); action_insert_rows_above = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("Insert Multiple Rows Above"), this); action_insert_rows_below = new QAction(QIcon::fromTheme("edit-table-insert-row-below"), i18n("Insert Multiple Rows Below"), this); action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Selected Rows"), this); action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selected Rows"), this); action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Row Statisti&cs"), this); //plot data action action_plot_data_xycurve = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-Curve"), this); action_plot_data_xycurve->setData(PlotDataDialog::PlotXYCurve); action_plot_data_histogram = new QAction(QIcon::fromTheme("labplot-histogram"), i18n("Histogram"), this); action_plot_data_histogram->setData(PlotDataDialog::PlotHistogram); //Analyze and plot menu actions addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Reduce Data"), this); // addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Reduce Data"), this); addDataReductionAction->setData(PlotDataDialog::DataReduction); addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Differentiate"), this); // addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("Differentiate"), this); addDifferentiationAction->setData(PlotDataDialog::Differentiation); addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Integrate"), this); // addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("Integrate"), this); addIntegrationAction->setData(PlotDataDialog::Integration); addInterpolationAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("Interpolate"), this); addInterpolationAction->setData(PlotDataDialog::Interpolation); addSmoothAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("Smooth"), this); addSmoothAction->setData(PlotDataDialog::Smoothing); QAction* fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Linear"), this); fitAction->setData(PlotDataDialog::FitLinear); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Power"), this); fitAction->setData(PlotDataDialog::FitPower); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Exponential (degree 1)"), this); fitAction->setData(PlotDataDialog::FitExp1); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Exponential (degree 2)"), this); fitAction->setData(PlotDataDialog::FitExp2); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Inverse Exponential"), this); fitAction->setData(PlotDataDialog::FitInvExp); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Gauss"), this); fitAction->setData(PlotDataDialog::FitGauss); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Cauchy-Lorentz"), this); fitAction->setData(PlotDataDialog::FitCauchyLorentz); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Arc Tangent"), this); fitAction->setData(PlotDataDialog::FitTan); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Hyperbolic Tangent"), this); fitAction->setData(PlotDataDialog::FitTanh); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Error Function"), this); fitAction->setData(PlotDataDialog::FitErrFunc); addFitAction.append(fitAction); fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Custom"), this); fitAction->setData(PlotDataDialog::FitCustom); addFitAction.append(fitAction); addFourierFilterAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), this); addFourierFilterAction->setData(PlotDataDialog::FourierFilter); } void SpreadsheetView::initMenus() { //Selection menu m_selectionMenu = new QMenu(i18n("Selection"), this); m_selectionMenu->setIcon(QIcon::fromTheme("selection")); QMenu* submenu = nullptr; if (!m_readOnly) { submenu = new QMenu(i18n("Fi&ll Selection With"), this); submenu->setIcon(QIcon::fromTheme("select-rectangle")); submenu->addAction(action_fill_sel_row_numbers); submenu->addAction(action_fill_const); m_selectionMenu->addMenu(submenu); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_cut_selection); } m_selectionMenu->addAction(action_copy_selection); if (!m_readOnly) { m_selectionMenu->addAction(action_paste_into_selection); m_selectionMenu->addAction(action_clear_selection); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_mask_selection); m_selectionMenu->addAction(action_unmask_selection); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_normalize_selection); } //plot data menu m_plotDataMenu = new QMenu(i18n("Plot Data"), this); m_plotDataMenu->addAction(action_plot_data_xycurve); m_plotDataMenu->addAction(action_plot_data_histogram); // Column menu m_columnMenu = new QMenu(this); m_columnMenu->addMenu(m_plotDataMenu); // Data manipulation sub-menu QMenu* dataManipulationMenu = new QMenu(i18n("Data Manipulation"), this); dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw")); dataManipulationMenu->addAction(addDataReductionAction); // Data fit sub-menu QMenu* dataFitMenu = new QMenu(i18n("Fit"), this); dataFitMenu->setIcon(QIcon::fromTheme("labplot-xy-fit-curve")); dataFitMenu->addAction(addFitAction.at(0)); dataFitMenu->addAction(addFitAction.at(1)); dataFitMenu->addAction(addFitAction.at(2)); dataFitMenu->addAction(addFitAction.at(3)); dataFitMenu->addAction(addFitAction.at(4)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(5)); dataFitMenu->addAction(addFitAction.at(6)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(7)); dataFitMenu->addAction(addFitAction.at(8)); dataFitMenu->addAction(addFitAction.at(9)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(10)); //analyze and plot data menu m_analyzePlotMenu = new QMenu(i18n("Analyze and Plot Data"), this); m_analyzePlotMenu->insertMenu(nullptr, dataManipulationMenu); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addAction(addDifferentiationAction); m_analyzePlotMenu->addAction(addIntegrationAction); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addAction(addInterpolationAction); m_analyzePlotMenu->addAction(addSmoothAction); m_analyzePlotMenu->addAction(addFourierFilterAction); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addMenu(dataFitMenu); m_columnMenu->addMenu(m_analyzePlotMenu); m_columnSetAsMenu = new QMenu(i18n("Set Column As"), this); m_columnMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_x); m_columnSetAsMenu->addAction(action_set_as_y); m_columnSetAsMenu->addAction(action_set_as_z); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_xerr); m_columnSetAsMenu->addAction(action_set_as_xerr_minus); m_columnSetAsMenu->addAction(action_set_as_xerr_plus); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_yerr); m_columnSetAsMenu->addAction(action_set_as_yerr_minus); m_columnSetAsMenu->addAction(action_set_as_yerr_plus); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_none); m_columnMenu->addMenu(m_columnSetAsMenu); if (!m_readOnly) { m_columnGenerateDataMenu = new QMenu(i18n("Generate Data"), this); m_columnGenerateDataMenu->addAction(action_fill_row_numbers); m_columnGenerateDataMenu->addAction(action_fill_const); m_columnGenerateDataMenu->addAction(action_fill_equidistant); m_columnGenerateDataMenu->addAction(action_fill_random_nonuniform); m_columnGenerateDataMenu->addAction(action_fill_function); m_columnMenu->addSeparator(); m_columnMenu->addMenu(m_columnGenerateDataMenu); m_columnMenu->addSeparator(); m_columnManipulateDataMenu = new QMenu(i18n("Manipulate Data"), this); m_columnManipulateDataMenu->addAction(action_add_value); m_columnManipulateDataMenu->addAction(action_subtract_value); m_columnManipulateDataMenu->addAction(action_multiply_value); m_columnManipulateDataMenu->addAction(action_divide_value); m_columnManipulateDataMenu->addSeparator(); m_columnManipulateDataMenu->addAction(action_reverse_columns); m_columnManipulateDataMenu->addSeparator(); m_columnManipulateDataMenu->addAction(action_drop_values); m_columnManipulateDataMenu->addAction(action_mask_values); m_columnManipulateDataMenu->addSeparator(); // m_columnManipulateDataMenu->addAction(action_join_columns); m_columnManipulateDataMenu->addAction(action_normalize_columns); m_columnMenu->addMenu(m_columnManipulateDataMenu); m_columnMenu->addSeparator(); m_columnSortMenu = new QMenu(i18n("Sort"), this); m_columnSortMenu->setIcon(QIcon::fromTheme("view-sort-ascending")); m_columnSortMenu->addAction(action_sort_asc_column); m_columnSortMenu->addAction(action_sort_desc_column); m_columnSortMenu->addAction(action_sort_columns); m_columnMenu->addSeparator(); m_columnMenu->addMenu(m_columnSortMenu); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_insert_column_left); m_columnMenu->addAction(action_insert_column_right); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_insert_columns_left); m_columnMenu->addAction(action_insert_columns_right); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_remove_columns); m_columnMenu->addAction(action_clear_columns); } m_columnMenu->addSeparator(); m_columnMenu->addAction(action_toggle_comments); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_statistics_columns); //Spreadsheet menu m_spreadsheetMenu = new QMenu(this); m_spreadsheetMenu->addMenu(m_plotDataMenu); m_spreadsheetMenu->addMenu(m_analyzePlotMenu); m_spreadsheetMenu->addAction(action_do_hypothesis_test); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_pivot_table); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addMenu(m_selectionMenu); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_select_all); if (!m_readOnly) { m_spreadsheetMenu->addAction(action_clear_spreadsheet); m_spreadsheetMenu->addAction(action_clear_masks); m_spreadsheetMenu->addAction(action_sort_spreadsheet); } m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_go_to_cell); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_toggle_comments); m_spreadsheetMenu->addSeparator(); m_spreadsheetMenu->addAction(action_statistics_all_columns); //Row menu m_rowMenu = new QMenu(this); if (!m_readOnly) { submenu = new QMenu(i18n("Fi&ll Selection With"), this); submenu->addAction(action_fill_sel_row_numbers); submenu->addAction(action_fill_const); m_rowMenu->addMenu(submenu); m_rowMenu->addSeparator(); m_rowMenu->addAction(action_insert_row_above); m_rowMenu->addAction(action_insert_row_below); m_rowMenu->addSeparator(); m_rowMenu->addAction(action_insert_rows_above); m_rowMenu->addAction(action_insert_rows_below); m_rowMenu->addSeparator(); m_rowMenu->addAction(action_remove_rows); m_rowMenu->addAction(action_clear_rows); } m_rowMenu->addSeparator(); m_rowMenu->addAction(action_statistics_rows); action_statistics_rows->setVisible(false); } void SpreadsheetView::connectActions() { connect(action_cut_selection, &QAction::triggered, this, &SpreadsheetView::cutSelection); connect(action_copy_selection, &QAction::triggered, this, &SpreadsheetView::copySelection); connect(action_paste_into_selection, &QAction::triggered, this, &SpreadsheetView::pasteIntoSelection); connect(action_mask_selection, &QAction::triggered, this, &SpreadsheetView::maskSelection); connect(action_unmask_selection, &QAction::triggered, this, &SpreadsheetView::unmaskSelection); connect(action_clear_selection, &QAction::triggered, this, &SpreadsheetView::clearSelectedCells); // connect(action_recalculate, &QAction::triggered, this, &SpreadsheetView::recalculateSelectedCells); connect(action_fill_row_numbers, &QAction::triggered, this, &SpreadsheetView::fillWithRowNumbers); connect(action_fill_sel_row_numbers, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithRowNumbers); // connect(action_fill_random, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithRandomNumbers); connect(action_fill_random_nonuniform, &QAction::triggered, this, &SpreadsheetView::fillWithRandomValues); connect(action_fill_equidistant, &QAction::triggered, this, &SpreadsheetView::fillWithEquidistantValues); connect(action_fill_function, &QAction::triggered, this, &SpreadsheetView::fillWithFunctionValues); connect(action_fill_const, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithConstValues); connect(action_select_all, &QAction::triggered, m_tableView, &QTableView::selectAll); connect(action_clear_spreadsheet, &QAction::triggered, m_spreadsheet, &Spreadsheet::clear); connect(action_clear_masks, &QAction::triggered, m_spreadsheet, &Spreadsheet::clearMasks); connect(action_sort_spreadsheet, &QAction::triggered, this, &SpreadsheetView::sortSpreadsheet); connect(action_go_to_cell, &QAction::triggered, this, static_cast(&SpreadsheetView::goToCell)); connect(action_pivot_table, &QAction::triggered, this, &SpreadsheetView::createPivotTable); connect(action_do_hypothesis_test, &QAction::triggered, this, &SpreadsheetView::doHypothesisTest); connect(action_insert_column_left, &QAction::triggered, this, &SpreadsheetView::insertColumnLeft); connect(action_insert_column_right, &QAction::triggered, this, &SpreadsheetView::insertColumnRight); connect(action_insert_columns_left, &QAction::triggered, this, static_cast(&SpreadsheetView::insertColumnsLeft)); connect(action_insert_columns_right, &QAction::triggered, this, static_cast(&SpreadsheetView::insertColumnsRight)); connect(action_remove_columns, &QAction::triggered, this, &SpreadsheetView::removeSelectedColumns); connect(action_clear_columns, &QAction::triggered, this, &SpreadsheetView::clearSelectedColumns); connect(action_set_as_none, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_x, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_y, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_z, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_xerr, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_xerr_minus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_xerr_plus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_yerr, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_yerr_minus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); connect(action_set_as_yerr_plus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs); //data manipulation connect(action_add_value, &QAction::triggered, this, &SpreadsheetView::modifyValues); connect(action_subtract_value, &QAction::triggered, this, &SpreadsheetView::modifyValues); connect(action_multiply_value, &QAction::triggered, this, &SpreadsheetView::modifyValues); connect(action_divide_value, &QAction::triggered, this, &SpreadsheetView::modifyValues); connect(action_reverse_columns, &QAction::triggered, this, &SpreadsheetView::reverseColumns); connect(action_drop_values, &QAction::triggered, this, &SpreadsheetView::dropColumnValues); connect(action_mask_values, &QAction::triggered, this, &SpreadsheetView::maskColumnValues); // connect(action_join_columns, &QAction::triggered, this, &SpreadsheetView::joinColumns); connect(action_normalize_columns, &QAction::triggered, this, &SpreadsheetView::normalizeSelectedColumns); connect(action_normalize_selection, &QAction::triggered, this, &SpreadsheetView::normalizeSelection); //sort connect(action_sort_columns, &QAction::triggered, this, &SpreadsheetView::sortSelectedColumns); connect(action_sort_asc_column, &QAction::triggered, this, &SpreadsheetView::sortColumnAscending); connect(action_sort_desc_column, &QAction::triggered, this, &SpreadsheetView::sortColumnDescending); //statistics connect(action_statistics_columns, &QAction::triggered, this, &SpreadsheetView::showColumnStatistics); connect(action_statistics_all_columns, &QAction::triggered, this, &SpreadsheetView::showAllColumnsStatistics); connect(action_insert_row_above, &QAction::triggered, this, &SpreadsheetView::insertRowAbove); connect(action_insert_row_below, &QAction::triggered, this, &SpreadsheetView::insertRowBelow); connect(action_insert_rows_above, &QAction::triggered, this, static_cast(&SpreadsheetView::insertRowsAbove)); connect(action_insert_rows_below, &QAction::triggered, this, static_cast(&SpreadsheetView::insertRowsBelow)); connect(action_remove_rows, &QAction::triggered, this, &SpreadsheetView::removeSelectedRows); connect(action_clear_rows, &QAction::triggered, this, &SpreadsheetView::clearSelectedRows); connect(action_statistics_rows, &QAction::triggered, this, &SpreadsheetView::showRowStatistics); connect(action_toggle_comments, &QAction::triggered, this, &SpreadsheetView::toggleComments); connect(action_plot_data_xycurve, &QAction::triggered, this, &SpreadsheetView::plotData); connect(action_plot_data_histogram, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addDataReductionAction, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addDifferentiationAction, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addIntegrationAction, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addInterpolationAction, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addSmoothAction, &QAction::triggered, this, &SpreadsheetView::plotData); for (const auto& action : addFitAction) connect(action, &QAction::triggered, this, &SpreadsheetView::plotData); connect(addFourierFilterAction, &QAction::triggered,this, &SpreadsheetView::plotData); } void SpreadsheetView::fillToolBar(QToolBar* toolBar) { if (!m_readOnly) { toolBar->addAction(action_insert_row_above); toolBar->addAction(action_insert_row_below); toolBar->addAction(action_remove_rows); } toolBar->addAction(action_statistics_rows); toolBar->addSeparator(); if (!m_readOnly) { toolBar->addAction(action_insert_column_left); toolBar->addAction(action_insert_column_right); toolBar->addAction(action_remove_columns); } toolBar->addAction(action_statistics_columns); if (!m_readOnly) { toolBar->addSeparator(); toolBar->addAction(action_sort_asc_column); toolBar->addAction(action_sort_desc_column); } } /*! * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. * The menu is used * - as the context menu in SpreadsheetView * - as the "spreadsheet menu" in the main menu-bar (called form MainWin) * - as a part of the spreadsheet context menu in project explorer */ void SpreadsheetView::createContextMenu(QMenu* menu) { Q_ASSERT(menu); checkSpreadsheetMenu(); QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); if (m_spreadsheet->columnCount() > 0 && m_spreadsheet->rowCount() > 0) { menu->insertMenu(firstAction, m_plotDataMenu); menu->insertSeparator(firstAction); } menu->insertMenu(firstAction, m_selectionMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_select_all); if (!m_readOnly) { menu->insertAction(firstAction, action_clear_spreadsheet); menu->insertAction(firstAction, action_clear_masks); menu->insertAction(firstAction, action_sort_spreadsheet); menu->insertSeparator(firstAction); } menu->insertAction(firstAction, action_go_to_cell); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_toggle_comments); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_statistics_all_columns); menu->insertSeparator(firstAction); } /*! * adds column specific actions in SpreadsheetView to the context menu shown in the project explorer. */ void SpreadsheetView::createColumnContextMenu(QMenu* menu) { const Column* column = dynamic_cast(QObject::sender()); if (!column) return; //should never happen, since the sender is always a Column if (column->isNumeric()) { QAction* firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_columnSetAsMenu); const bool hasValues = column->hasValues(); if (!m_readOnly) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_columnGenerateDataMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_columnManipulateDataMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_columnSortMenu); action_sort_asc_column->setVisible(true); action_sort_desc_column->setVisible(true); action_sort_columns->setVisible(false); //in case no cells are available, deactivate the actions that only make sense in the presence of cells const bool hasCells = m_spreadsheet->rowCount() > 0; m_columnGenerateDataMenu->setEnabled(hasCells); //in case no valid numerical values are available, deactivate the actions that only make sense in the presence of values action_reverse_columns->setEnabled(hasValues); action_drop_values->setEnabled(hasValues); action_mask_values->setEnabled(hasValues); action_normalize_columns->setEnabled(hasValues); m_columnSortMenu->setEnabled(hasValues); } menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_statistics_columns); action_statistics_columns->setEnabled(hasValues); } } //SLOTS void SpreadsheetView::handleAspectAdded(const AbstractAspect* aspect) { const Column* col = dynamic_cast(aspect); if (!col || col->parentAspect() != m_spreadsheet) return; int index = m_spreadsheet->indexOfChild(col); if (col->width() == 0) m_tableView->resizeColumnToContents(index); else m_tableView->setColumnWidth(index, col->width()); connect(col, &Column::requestProjectContextMenu, this, &SpreadsheetView::createColumnContextMenu); } void SpreadsheetView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const Column* col = dynamic_cast(aspect); if (!col || col->parentAspect() != m_spreadsheet) return; disconnect(col, nullptr, this, nullptr); } void SpreadsheetView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(logicalIndex); Q_UNUSED(oldSize); //save the new size in the column Column* col = m_spreadsheet->child(logicalIndex); col->setWidth(newSize); } void SpreadsheetView::goToCell(int row, int col) { QModelIndex index = m_model->index(row, col); m_tableView->scrollTo(index); m_tableView->setCurrentIndex(index); } void SpreadsheetView::createPivotTable() { PivotTable* pivot = new PivotTable(i18n("Pivot Table for %1", m_spreadsheet->name())); pivot->setDataSourceType(PivotTable::DataSourceSpreadsheet); pivot->setDataSourceSpreadsheet(m_spreadsheet); m_spreadsheet->parentAspect()->addChild(pivot); } void SpreadsheetView::doHypothesisTest() { + QDEBUG("in do hypothesis test"); HypothesisTest* hypothesis_test = new HypothesisTest(i18n("Hypothesis Test for %1", m_spreadsheet->name())); + QDEBUG("created object"); hypothesis_test->setDataSourceType(HypothesisTest::DataSourceSpreadsheet); + QDEBUG("data source type set"); hypothesis_test->setDataSourceSpreadsheet(m_spreadsheet); + QDEBUG("added spreadsheet"); + QDEBUG(hypothesis_test->name()); m_spreadsheet->parentAspect()->addChild(hypothesis_test); + QDEBUG("out of do hypothesis test"); } void SpreadsheetView::handleHorizontalSectionMoved(int index, int from, int to) { Q_UNUSED(index); static bool inside = false; if (inside) return; Q_ASSERT(index == from); inside = true; m_tableView->horizontalHeader()->moveSection(to, from); inside = false; m_spreadsheet->moveColumn(from, to); } //TODO Implement the "change of the column name"-mode upon a double click void SpreadsheetView::handleHorizontalHeaderDoubleClicked(int index) { Q_UNUSED(index); } /*! Returns whether comments are shown currently or not */ bool SpreadsheetView::areCommentsShown() const { return m_horizontalHeader->areCommentsShown(); } /*! toggles the column comment in the horizontal header */ void SpreadsheetView::toggleComments() { showComments(!areCommentsShown()); //TODO if (areCommentsShown()) action_toggle_comments->setText(i18n("Hide Comments")); else action_toggle_comments->setText(i18n("Show Comments")); } //! Shows (\c on=true) or hides (\c on=false) the column comments in the horizontal header void SpreadsheetView::showComments(bool on) { m_horizontalHeader->showComments(on); } void SpreadsheetView::currentColumnChanged(const QModelIndex & current, const QModelIndex & previous) { Q_UNUSED(previous); int col = current.column(); if (col < 0 || col >= m_spreadsheet->columnCount()) return; } //TODO void SpreadsheetView::handleHeaderDataChanged(Qt::Orientation orientation, int first, int last) { if (orientation != Qt::Horizontal) return; QItemSelectionModel * sel_model = m_tableView->selectionModel(); int col = sel_model->currentIndex().column(); if (col < first || col > last) return; } /*! Returns the number of selected columns. If \c full is \c true, this function only returns the number of fully selected columns. */ int SpreadsheetView::selectedColumnCount(bool full) const { int count = 0; const int cols = m_spreadsheet->columnCount(); for (int i = 0; i < cols; i++) if (isColumnSelected(i, full)) count++; return count; } /*! Returns the number of (at least partly) selected columns with the plot designation \param pd. */ int SpreadsheetView::selectedColumnCount(AbstractColumn::PlotDesignation pd) const{ int count = 0; const int cols = m_spreadsheet->columnCount(); for (int i = 0; i < cols; i++) if ( isColumnSelected(i, false) && (m_spreadsheet->column(i)->plotDesignation() == pd) ) count++; return count; } /*! Returns \c true if column \param col is selected, otherwise returns \c false. If \param full is \c true, this function only returns true if the whole column is selected. */ bool SpreadsheetView::isColumnSelected(int col, bool full) const { if (full) return m_tableView->selectionModel()->isColumnSelected(col, QModelIndex()); else return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex()); } /*! Returns all selected columns. If \param full is true, this function only returns a column if the whole column is selected. */ QVector SpreadsheetView::selectedColumns(bool full) const { QVector columns; const int cols = m_spreadsheet->columnCount(); for (int i = 0; i < cols; i++) if (isColumnSelected(i, full)) columns << m_spreadsheet->column(i); return columns; } /*! Returns \c true if row \param row is selected; otherwise returns \c false If \param full is \c true, this function only returns \c true if the whole row is selected. */ bool SpreadsheetView::isRowSelected(int row, bool full) const { if (full) return m_tableView->selectionModel()->isRowSelected(row, QModelIndex()); else return m_tableView->selectionModel()->rowIntersectsSelection(row, QModelIndex()); } /*! Return the index of the first selected column. If \param full is \c true, this function only looks for fully selected columns. */ int SpreadsheetView::firstSelectedColumn(bool full) const { const int cols = m_spreadsheet->columnCount(); for (int i = 0; i < cols; i++) { if (isColumnSelected(i, full)) return i; } return -1; } /*! Return the index of the last selected column. If \param full is \c true, this function only looks for fully selected columns. */ int SpreadsheetView::lastSelectedColumn(bool full) const { const int cols = m_spreadsheet->columnCount(); for (int i = cols - 1; i >= 0; i--) if (isColumnSelected(i, full)) return i; return -2; } /*! Return the index of the first selected row. If \param full is \c true, this function only looks for fully selected rows. */ int SpreadsheetView::firstSelectedRow(bool full) const{ QModelIndexList indexes; if (!full) indexes = m_tableView->selectionModel()->selectedIndexes(); else indexes = m_tableView->selectionModel()->selectedRows(); if (!indexes.empty()) return indexes.first().row(); else return -1; } /*! Return the index of the last selected row. If \param full is \c true, this function only looks for fully selected rows. */ int SpreadsheetView::lastSelectedRow(bool full) const { QModelIndexList indexes; if (!full) indexes = m_tableView->selectionModel()->selectedIndexes(); else indexes = m_tableView->selectionModel()->selectedRows(); if (!indexes.empty()) return indexes.last().row(); else return -2; } /*! Return whether a cell is selected */ bool SpreadsheetView::isCellSelected(int row, int col) const { if (row < 0 || col < 0 || row >= m_spreadsheet->rowCount() || col >= m_spreadsheet->columnCount()) return false; return m_tableView->selectionModel()->isSelected(m_model->index(row, col)); } /*! Get the complete set of selected rows. */ IntervalAttribute SpreadsheetView::selectedRows(bool full) const { IntervalAttribute result; const int rows = m_spreadsheet->rowCount(); for (int i = 0; i < rows; i++) if (isRowSelected(i, full)) result.setValue(i, true); return result; } /*! Select/Deselect a cell. */ void SpreadsheetView::setCellSelected(int row, int col, bool select) { m_tableView->selectionModel()->select(m_model->index(row, col), select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); } /*! Select/Deselect a range of cells. */ void SpreadsheetView::setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select) { QModelIndex top_left = m_model->index(first_row, first_col); QModelIndex bottom_right = m_model->index(last_row, last_col); m_tableView->selectionModel()->select(QItemSelection(top_left, bottom_right), select ? QItemSelectionModel::SelectCurrent : QItemSelectionModel::Deselect); } /*! Determine the current cell (-1 if no cell is designated as the current). */ void SpreadsheetView::getCurrentCell(int* row, int* col) const { QModelIndex index = m_tableView->selectionModel()->currentIndex(); if (index.isValid()) { *row = index.row(); *col = index.column(); } else { *row = -1; *col = -1; } } bool SpreadsheetView::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::ContextMenu) { auto* cm_event = static_cast(event); const QPoint global_pos = cm_event->globalPos(); if (watched == m_tableView->verticalHeader()) { bool onlyNumeric = true; for (int i = 0; i < m_spreadsheet->columnCount(); ++i) { if (m_spreadsheet->column(i)->columnMode() != AbstractColumn::Numeric) { onlyNumeric = false; break; } } action_statistics_rows->setVisible(onlyNumeric); m_rowMenu->exec(global_pos); } else if (watched == m_horizontalHeader) { const int col = m_horizontalHeader->logicalIndexAt(cm_event->pos()); if (!isColumnSelected(col, true)) { QItemSelectionModel* sel_model = m_tableView->selectionModel(); sel_model->clearSelection(); sel_model->select(QItemSelection(m_model->index(0, col, QModelIndex()), m_model->index(m_model->rowCount()-1, col, QModelIndex())), QItemSelectionModel::Select); } if (selectedColumns().size() == 1) { action_sort_columns->setVisible(false); action_sort_asc_column->setVisible(true); action_sort_desc_column->setVisible(true); } else { action_sort_columns->setVisible(true); action_sort_asc_column->setVisible(false); action_sort_desc_column->setVisible(false); } //check whether we have non-numeric columns selected and deactivate actions for numeric columns bool numeric = true; bool plottable = true; bool datetime = false; for (const Column* col : selectedColumns()) { if ( !(col->columnMode() == AbstractColumn::Numeric || col->columnMode() == AbstractColumn::Integer) ) { datetime = (col->columnMode() == AbstractColumn::DateTime); if (!datetime) plottable = false; numeric = false; break; } } m_plotDataMenu->setEnabled(plottable); m_analyzePlotMenu->setEnabled(numeric); m_columnSetAsMenu->setEnabled(numeric); if (!m_readOnly) { m_columnGenerateDataMenu->setEnabled(numeric); m_columnManipulateDataMenu->setEnabled(numeric || datetime); m_columnSortMenu->setEnabled(numeric); } action_statistics_columns->setEnabled(numeric); if (numeric) { bool hasValues = false; for (const Column* col : selectedColumns()) { if (col->hasValues()) { hasValues = true; break; } } if (!m_readOnly) { //in case no cells are available, deactivate the actions that only make sense in the presence of cells const bool hasCells = m_spreadsheet->rowCount() > 0; m_columnGenerateDataMenu->setEnabled(hasCells); //in case no valid numerical values are available, deactivate the actions that only make sense in the presence of values m_columnManipulateDataMenu->setEnabled(hasValues); m_columnSortMenu->setEnabled(hasValues); } action_statistics_columns->setEnabled(hasValues); } m_columnMenu->exec(global_pos); } else if (watched == this) { checkSpreadsheetMenu(); m_spreadsheetMenu->exec(global_pos); } return true; } else if (event->type() == QEvent::KeyPress) { auto* key_event = static_cast(event); if (key_event->matches(QKeySequence::Copy)) copySelection(); else if (key_event->matches(QKeySequence::Paste)) pasteIntoSelection(); } return QWidget::eventFilter(watched, event); } /*! * disables cell data relevant actions in the spreadsheet menu if there're no cells available */ void SpreadsheetView::checkSpreadsheetMenu() { const bool cellsAvail = m_spreadsheet->columnCount()>0 && m_spreadsheet->rowCount()>0; m_plotDataMenu->setEnabled(cellsAvail); m_selectionMenu->setEnabled(cellsAvail); action_select_all->setEnabled(cellsAvail); action_clear_spreadsheet->setEnabled(cellsAvail); action_clear_masks->setEnabled(cellsAvail); action_sort_spreadsheet->setEnabled(cellsAvail); action_go_to_cell->setEnabled(cellsAvail); action_statistics_all_columns->setEnabled(cellsAvail); } bool SpreadsheetView::formulaModeActive() const { return m_model->formulaModeActive(); } void SpreadsheetView::activateFormulaMode(bool on) { m_model->activateFormulaMode(on); } void SpreadsheetView::goToNextColumn() { if (m_spreadsheet->columnCount() == 0) return; QModelIndex idx = m_tableView->currentIndex(); int col = idx.column()+1; if (col >= m_spreadsheet->columnCount()) col = 0; m_tableView->setCurrentIndex(idx.sibling(idx.row(), col)); } void SpreadsheetView::goToPreviousColumn() { if (m_spreadsheet->columnCount() == 0) return; QModelIndex idx = m_tableView->currentIndex(); int col = idx.column()-1; if (col < 0) col = m_spreadsheet->columnCount()-1; m_tableView->setCurrentIndex(idx.sibling(idx.row(), col)); } void SpreadsheetView::cutSelection() { int first = firstSelectedRow(); if ( first < 0 ) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: cut selected cells", m_spreadsheet->name())); copySelection(); clearSelectedCells(); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::copySelection() { PERFTRACE("copy selected cells"); const int first_col = firstSelectedColumn(); if (first_col == -1) return; const int last_col = lastSelectedColumn(); if (last_col == -2) return; const int first_row = firstSelectedRow(); if (first_row == -1) return; const int last_row = lastSelectedRow(); if (last_row == -2) return; const int cols = last_col - first_col + 1; const int rows = last_row - first_row + 1; WAIT_CURSOR; QString output_str; QVector columns; QVector formats; for (int c = 0; c < cols; c++) { Column* col = m_spreadsheet->column(first_col + c); columns << col; const Double2StringFilter* out_fltr = static_cast(col->outputFilter()); formats << out_fltr->numericFormat(); } QLocale locale; for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { const Column* col_ptr = columns.at(c); if (isCellSelected(first_row + r, first_col + c)) { // if (formulaModeActive()) // output_str += col_ptr->formula(first_row + r); // else if (col_ptr->columnMode() == AbstractColumn::Numeric) output_str += locale.toString(col_ptr->valueAt(first_row + r), formats.at(c), 16); // copy with max. precision else output_str += col_ptr->asStringColumn()->textAt(first_row + r); } if (c < cols-1) output_str += '\t'; } if (r < rows-1) output_str += '\n'; } QApplication::clipboard()->setText(output_str); RESET_CURSOR; } void SpreadsheetView::pasteIntoSelection() { if (m_spreadsheet->columnCount() < 1 || m_spreadsheet->rowCount() < 1) return; const QMimeData* mime_data = QApplication::clipboard()->mimeData(); if (!mime_data->hasFormat("text/plain")) return; PERFTRACE("paste selected cells"); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: paste from clipboard", m_spreadsheet->name())); int first_col = firstSelectedColumn(); int last_col = lastSelectedColumn(); int first_row = firstSelectedRow(); int last_row = lastSelectedRow(); int input_row_count = 0; int input_col_count = 0; QString input_str = QString(mime_data->data("text/plain")).trimmed(); QVector cellTexts; QStringList input_rows(input_str.split(QLatin1Char('\n'))); input_row_count = input_rows.count(); input_col_count = 0; for (int i = 0; i < input_row_count; i++) { cellTexts.append(input_rows.at(i).split(QRegExp(QLatin1String("\\s+")))); if (cellTexts.at(i).count() > input_col_count) input_col_count = cellTexts.at(i).count(); } if ( (first_col == -1 || first_row == -1) || (last_row == first_row && last_col == first_col) ) { // if there is no selection or only one cell selected, the // selection will be expanded to the needed size from the current cell int current_row, current_col; getCurrentCell(¤t_row, ¤t_col); if (current_row == -1) current_row = 0; if (current_col == -1) current_col = 0; setCellSelected(current_row, current_col); first_col = current_col; first_row = current_row; last_row = first_row + input_row_count -1; last_col = first_col + input_col_count -1; //add columns if necessary const int columnCount = m_spreadsheet->columnCount(); if (last_col >= columnCount) { for (int c = 0; c < last_col - (columnCount - 1); ++c) { const int curCol = columnCount - 1 + c; //first non-empty value in the column to paste determines the column mode/type of the new column to be added QString nonEmptyValue; for (auto r : cellTexts) { if (!r.at(curCol).isEmpty()) { nonEmptyValue = r.at(curCol); break; } } const AbstractColumn::ColumnMode mode = AbstractFileFilter::columnMode(nonEmptyValue, QLatin1String("yyyy-dd-MM hh:mm:ss:zzz"), QLocale::AnyLanguage); Column* new_col = new Column(QString::number(curCol), mode); new_col->setPlotDesignation(AbstractColumn::Y); new_col->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->addChild(new_col); } } //add rows if necessary if (last_row >= m_spreadsheet->rowCount()) m_spreadsheet->appendRows(last_row + 1 - m_spreadsheet->rowCount()); // select the rectangle to be pasted in setCellsSelected(first_row, first_col, last_row, last_col); } const int rows = last_row - first_row + 1; const int cols = last_col - first_col + 1; QLocale locale; for (int c = 0; c < cols && c < input_col_count; c++) { Column* col = m_spreadsheet->column(first_col + c); col->setSuppressDataChangedSignal(true); if (col->columnMode() == AbstractColumn::Numeric) { if (rows == m_spreadsheet->rowCount() && rows == cellTexts.size()) { QVector new_data(rows); for (int r = 0; r < rows; ++r) new_data[r] = locale.toDouble(cellTexts.at(r).at(c)); col->replaceValues(0, new_data); } else { for (int r = 0; r < rows && r < input_row_count; r++) { if ( isCellSelected(first_row + r, first_col + c) && (c < cellTexts.at(r).count()) ) { if (!cellTexts.at(r).at(c).isEmpty()) col->setValueAt(first_row + r, locale.toDouble(cellTexts.at(r).at(c))); else col->setValueAt(first_row + r, NAN); } } } } else { for (int r = 0; r < rows && r < input_row_count; r++) { if (isCellSelected(first_row + r, first_col + c) && (c < cellTexts.at(r).count()) ) { // if (formulaModeActive()) // col->setFormula(first_row + r, cellTexts.at(r).at(c)); // else col->asStringColumn()->setTextAt(first_row + r, cellTexts.at(r).at(c)); } } } col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::maskSelection() { int first = firstSelectedRow(); if (first < 0) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: mask selected cells", m_spreadsheet->name())); for (auto* column : selectedColumns()) { int col = m_spreadsheet->indexOfChild(column); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) column->setMasked(row); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::unmaskSelection() { int first = firstSelectedRow(); if (first < 0) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: unmask selected cells", m_spreadsheet->name())); for (auto* column : selectedColumns()) { int col = m_spreadsheet->indexOfChild(column); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) column->setMasked(row, false); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::plotData() { const QAction* action = dynamic_cast(QObject::sender()); PlotDataDialog::PlotType type = PlotDataDialog::PlotXYCurve; if (action == action_plot_data_xycurve || action == action_plot_data_histogram) type = (PlotDataDialog::PlotType)action->data().toInt(); auto* dlg = new PlotDataDialog(m_spreadsheet, type); if (action != action_plot_data_xycurve && action != action_plot_data_histogram) { PlotDataDialog::AnalysisAction type = (PlotDataDialog::AnalysisAction)action->data().toInt(); dlg->setAnalysisAction(type); } dlg->exec(); } void SpreadsheetView::fillSelectedCellsWithRowNumbers() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); if (first < 0) return; int last = lastSelectedRow(); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: fill cells with row numbers", m_spreadsheet->name())); for (auto* col_ptr : selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: { QVector results(last-first+1); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results[row-first] = row + 1; else results[row-first] = col_ptr->valueAt(row); col_ptr->replaceValues(first, results); break; } case AbstractColumn::Integer: { QVector results(last-first+1); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results[row-first] = row + 1; else results[row-first] = col_ptr->integerAt(row); col_ptr->replaceInteger(first, results); break; } case AbstractColumn::Text: { QVector results; for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results << QString::number(row+1); else results << col_ptr->textAt(row); col_ptr->replaceTexts(first, results); break; } //TODO: handle other modes case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::fillWithRowNumbers() { if (selectedColumnCount() < 1) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: fill column with row numbers", "%1: fill columns with row numbers", m_spreadsheet->name(), selectedColumnCount())); const int rows = m_spreadsheet->rowCount(); QVector int_data(rows); for (int i = 0; i < rows; ++i) int_data[i] = i + 1; for (auto* col : selectedColumns()) { switch (col->columnMode()) { case AbstractColumn::Numeric: col->setColumnMode(AbstractColumn::Integer); col->replaceInteger(0, int_data); break; case AbstractColumn::Integer: col->replaceInteger(0, int_data); break; case AbstractColumn::Text: case AbstractColumn::DateTime: case AbstractColumn::Day: case AbstractColumn::Month: break; } } m_spreadsheet->endMacro(); RESET_CURSOR; } //TODO: this function is not used currently. void SpreadsheetView::fillSelectedCellsWithRandomNumbers() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: fill cells with random values", m_spreadsheet->name())); qsrand(QTime::currentTime().msec()); for (auto* col_ptr : selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: { QVector results(last-first+1); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results[row-first] = double(qrand())/double(RAND_MAX); else results[row-first] = col_ptr->valueAt(row); col_ptr->replaceValues(first, results); break; } case AbstractColumn::Integer: { QVector results(last-first+1); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results[row-first] = qrand(); else results[row-first] = col_ptr->integerAt(row); col_ptr->replaceInteger(first, results); break; } case AbstractColumn::Text: { QVector results; for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results << QString::number(double(qrand())/double(RAND_MAX)); else results << col_ptr->textAt(row); col_ptr->replaceTexts(first, results); break; } case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: { QVector results; QDate earliestDate(1, 1, 1); QDate latestDate(2999, 12, 31); QTime midnight(0, 0, 0, 0); for (int row = first; row <= last; row++) if (isCellSelected(row, col)) results << QDateTime( earliestDate.addDays(((double)qrand())*((double)earliestDate.daysTo(latestDate))/((double)RAND_MAX)), midnight.addMSecs(((qint64)qrand())*1000*60*60*24/RAND_MAX)); else results << col_ptr->dateTimeAt(row); col_ptr->replaceDateTimes(first, results); break; } } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::fillWithRandomValues() { if (selectedColumnCount() < 1) return; auto* dlg = new RandomValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithEquidistantValues() { if (selectedColumnCount() < 1) return; auto* dlg = new EquidistantValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithFunctionValues() { if (selectedColumnCount() < 1) return; auto* dlg = new FunctionValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillSelectedCellsWithConstValues() { if (selectedColumnCount() < 1) return; int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; bool doubleOk = false; bool intOk = false; bool stringOk = false; double doubleValue = 0; int intValue = 0; QString stringValue; m_spreadsheet->beginMacro(i18n("%1: fill cells with const values", m_spreadsheet->name())); for (auto* col_ptr : selectedColumns()) { int col = m_spreadsheet->indexOfChild(col_ptr); col_ptr->setSuppressDataChangedSignal(true); switch (col_ptr->columnMode()) { case AbstractColumn::Numeric: if (!doubleOk) doubleValue = QInputDialog::getDouble(this, i18n("Fill the selection with constant value"), i18n("Value"), 0, -2147483647, 2147483647, 6, &doubleOk); if (doubleOk) { WAIT_CURSOR; QVector results(last-first+1); for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results[row-first] = doubleValue; else results[row-first] = col_ptr->valueAt(row); } col_ptr->replaceValues(first, results); RESET_CURSOR; } break; case AbstractColumn::Integer: if (!intOk) intValue = QInputDialog::getInt(this, i18n("Fill the selection with constant value"), i18n("Value"), 0, -2147483647, 2147483647, 1, &intOk); if (intOk) { WAIT_CURSOR; QVector results(last-first+1); for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results[row-first] = intValue; else results[row-first] = col_ptr->integerAt(row); } col_ptr->replaceInteger(first, results); RESET_CURSOR; } break; case AbstractColumn::Text: if (!stringOk) stringValue = QInputDialog::getText(this, i18n("Fill the selection with constant value"), i18n("Value"), QLineEdit::Normal, nullptr, &stringOk); if (stringOk && !stringValue.isEmpty()) { WAIT_CURSOR; QVector results; for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results << stringValue; else results << col_ptr->textAt(row); } col_ptr->replaceTexts(first, results); RESET_CURSOR; } break; //TODO: handle other modes case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); } /*! Open the sort dialog for all columns. */ void SpreadsheetView::sortSpreadsheet() { sortDialog(m_spreadsheet->children()); } /*! Insert an empty column left to the firt selected column */ void SpreadsheetView::insertColumnLeft() { insertColumnsLeft(1); } /*! Insert multiple empty columns left to the firt selected column */ void SpreadsheetView::insertColumnsLeft() { bool ok = false; int count = QInputDialog::getInt(nullptr, i18n("Insert empty columns"), i18n("Enter the number of columns to insert"), 1/*value*/, 1/*min*/, 1000/*max*/, 1/*step*/, &ok); if (!ok) return; insertColumnsLeft(count); } /*! * private helper function doing the actual insertion of columns to the left */ void SpreadsheetView::insertColumnsLeft(int count) { WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: insert empty column", "%1: insert empty columns", m_spreadsheet->name(), count )); const int first = firstSelectedColumn(); if (first >= 0) { //determine the first selected column Column* firstCol = m_spreadsheet->child(first); for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); //resize the new column and insert it before the first selected column newCol->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->insertChildBefore(newCol, firstCol); } } else { if (m_spreadsheet->columnCount()>0) { //columns available but no columns selected -> prepend the new column at the very beginning Column* firstCol = m_spreadsheet->child(0); for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->insertChildBefore(newCol, firstCol); } } else { //no columns available anymore -> resize the spreadsheet and the new column to the default size KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); const int rows = group.readEntry(QLatin1String("RowCount"), 100); m_spreadsheet->setRowCount(rows); for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric); (i == 0) ? newCol->setPlotDesignation(AbstractColumn::X) : newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, rows); //add/append a new column m_spreadsheet->addChild(newCol); } } } m_spreadsheet->endMacro(); RESET_CURSOR; } /*! Insert an empty column right to the last selected column */ void SpreadsheetView::insertColumnRight() { insertColumnsRight(1); } /*! Insert multiple empty columns right to the last selected column */ void SpreadsheetView::insertColumnsRight() { bool ok = false; int count = QInputDialog::getInt(nullptr, i18n("Insert empty columns"), i18n("Enter the number of columns to insert"), 1/*value*/, 1/*min*/, 1000/*max*/, 1/*step*/, &ok); if (!ok) return; insertColumnsRight(count); } /*! * private helper function doing the actual insertion of columns to the right */ void SpreadsheetView::insertColumnsRight(int count) { WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: insert empty column", "%1: insert empty columns", m_spreadsheet->name(), count )); const int last = lastSelectedColumn(); if (last >= 0) { if (last < m_spreadsheet->columnCount() - 1) { //determine the column next to the last selected column Column* nextCol = m_spreadsheet->child(last + 1); for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, m_spreadsheet->rowCount()); //insert the new column before the column next to the last selected column m_spreadsheet->insertChildBefore(newCol, nextCol); } } else { for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, m_spreadsheet->rowCount()); //last column selected, no next column available -> add/append a new column m_spreadsheet->addChild(newCol); } } } else { if (m_spreadsheet->columnCount()>0) { for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, m_spreadsheet->rowCount()); //columns available but no columns selected -> append the new column at the very end m_spreadsheet->addChild(newCol); } } else { //no columns available anymore -> resize the spreadsheet and the new column to the default size KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); const int rows = group.readEntry(QLatin1String("RowCount"), 100); m_spreadsheet->setRowCount(rows); for (int i = 0; i < count; ++i) { Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric); (i == 0) ? newCol->setPlotDesignation(AbstractColumn::X) : newCol->setPlotDesignation(AbstractColumn::Y); newCol->insertRows(0, rows); //add/append a new column m_spreadsheet->addChild(newCol); } } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected columns", m_spreadsheet->name())); for (auto* column : selectedColumns()) m_spreadsheet->removeChild(column); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected columns", m_spreadsheet->name())); if (formulaModeActive()) { for (auto* col : selectedColumns()) { col->setSuppressDataChangedSignal(true); col->clearFormulas(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } else { for (auto* col : selectedColumns()) { col->setSuppressDataChangedSignal(true); col->clear(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::setSelectionAs() { QVector columns = selectedColumns(); if (!columns.size()) return; m_spreadsheet->beginMacro(i18n("%1: set plot designation", m_spreadsheet->name())); QAction* action = dynamic_cast(QObject::sender()); if (!action) return; AbstractColumn::PlotDesignation pd = (AbstractColumn::PlotDesignation)action->data().toInt(); for (auto* col : columns) col->setPlotDesignation(pd); m_spreadsheet->endMacro(); } /*! * add, subtract, multiply, divide */ void SpreadsheetView::modifyValues() { if (selectedColumnCount() < 1) return; const QAction* action = dynamic_cast(QObject::sender()); AddSubtractValueDialog::Operation op = (AddSubtractValueDialog::Operation)action->data().toInt(); auto* dlg = new AddSubtractValueDialog(m_spreadsheet, op); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::reverseColumns() { WAIT_CURSOR; QVector cols = selectedColumns(); m_spreadsheet->beginMacro(i18np("%1: reverse column", "%1: reverse columns", m_spreadsheet->name(), cols.size())); for (auto* col : cols) { if (col->columnMode() != AbstractColumn::Numeric) continue; auto* data = static_cast* >(col->data()); QVector new_data(*data); std::reverse(new_data.begin(), new_data.end()); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::dropColumnValues() { if (selectedColumnCount() < 1) return; auto* dlg = new DropValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::maskColumnValues() { if (selectedColumnCount() < 1) return; auto* dlg = new DropValuesDialog(m_spreadsheet, true); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::joinColumns() { //TODO } void SpreadsheetView::normalizeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: normalize columns", m_spreadsheet->name())); for (auto* col : selectedColumns()) { if (col->columnMode() == AbstractColumn::Numeric) { col->setSuppressDataChangedSignal(true); double max = col->maximum(); if (max != 0.0) {// avoid division by zero for (int row = 0; row < col->rowCount(); row++) col->setValueAt(row, col->valueAt(row) / max); } col->setSuppressDataChangedSignal(false); col->setChanged(); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::normalizeSelection() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: normalize selection", m_spreadsheet->name())); double max = 0.0; for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++) if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) for (int row = 0; row < m_spreadsheet->rowCount(); row++) { if (isCellSelected(row, col) && m_spreadsheet->column(col)->valueAt(row) > max) max = m_spreadsheet->column(col)->valueAt(row); } if (max != 0.0) { // avoid division by zero //TODO setSuppressDataChangedSignal for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++) if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) for (int row = 0; row < m_spreadsheet->rowCount(); row++) { if (isCellSelected(row, col)) m_spreadsheet->column(col)->setValueAt(row, m_spreadsheet->column(col)->valueAt(row) / max); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::sortSelectedColumns() { sortDialog(selectedColumns()); } void SpreadsheetView::showAllColumnsStatistics() { showColumnStatistics(true); } void SpreadsheetView::showColumnStatistics(bool forAll) { QString dlgTitle(m_spreadsheet->name() + " column statistics"); auto* dlg = new StatisticsDialog(dlgTitle); QVector columns; if (!forAll) dlg->setColumns(selectedColumns()); else if (forAll) { for (int col = 0; col < m_spreadsheet->columnCount(); ++col) { if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric) columns << m_spreadsheet->column(col); } dlg->setColumns(columns); } if (dlg->exec() == QDialog::Accepted) { if (forAll) columns.clear(); } } void SpreadsheetView::showRowStatistics() { QString dlgTitle(m_spreadsheet->name() + " row statistics"); auto* dlg = new StatisticsDialog(dlgTitle); QVector columns; for (int i = 0; i < m_spreadsheet->rowCount(); ++i) { if (isRowSelected(i)) { QVector rowValues; for (int j = 0; j < m_spreadsheet->columnCount(); ++j) rowValues << m_spreadsheet->column(j)->valueAt(i); columns << new Column(QString::number(i+1), rowValues); } } dlg->setColumns(columns); if (dlg->exec() == QDialog::Accepted) { qDeleteAll(columns); columns.clear(); } } /*! Insert an empty row above(=before) the first selected row */ void SpreadsheetView::insertRowAbove() { insertRowsAbove(1); } /*! Insert multiple empty rows above(=before) the first selected row */ void SpreadsheetView::insertRowsAbove() { bool ok = false; int count = QInputDialog::getInt(nullptr, i18n("Insert multiple rows"), i18n("Enter the number of rows to insert"), 1/*value*/, 1/*min*/, 1000000/*max*/, 1/*step*/, &ok); if (ok) insertRowsAbove(count); } /*! * private helper function doing the actual insertion of rows above */ void SpreadsheetView::insertRowsAbove(int count) { int first = firstSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: insert empty row", "%1: insert empty rows", m_spreadsheet->name(), count )); m_spreadsheet->insertRows(first, count); m_spreadsheet->endMacro(); RESET_CURSOR; } /*! Insert an empty row below the last selected row */ void SpreadsheetView::insertRowBelow() { insertRowsBelow(1); } /*! Insert an empty row below the last selected row */ void SpreadsheetView::insertRowsBelow() { bool ok = false; int count = QInputDialog::getInt(nullptr, i18n("Insert multiple rows"), i18n("Enter the number of rows to insert"), 1/*value*/, 1/*min*/, 1000000/*max*/, 1/*step*/, &ok); if (ok) insertRowsBelow(count); } /*! * private helper function doing the actual insertion of rows below */ void SpreadsheetView::insertRowsBelow(int count) { int last = lastSelectedRow(); if (last < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: insert empty row", "%1: insert empty rows", m_spreadsheet->name(), count )); if (last < m_spreadsheet->rowCount() - 1) m_spreadsheet->insertRows(last + 1, count); //insert before the next to the last selected row else m_spreadsheet->appendRows(count); //append new rows at the end m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected rows", m_spreadsheet->name())); //TODO setSuppressDataChangedSignal for (const auto& i : selectedRows().intervals()) m_spreadsheet->removeRows(i.start(), i.size()); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected rows", m_spreadsheet->name())); for (auto* col : selectedColumns()) { col->setSuppressDataChangedSignal(true); if (formulaModeActive()) { for (const auto& i : selectedRows().intervals()) col->setFormula(i, QString()); } else { for (const auto& i : selectedRows().intervals()) { if (i.end() == col->rowCount()-1) col->removeRows(i.start(), i.size()); else { QVector empties; for (int j = 0; j < i.size(); j++) empties << QString(); col->asStringColumn()->replaceTexts(i.start(), empties); } } } col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; //selected rows were deleted but the view selection is still in place -> reset the selection in the view m_tableView->clearSelection(); } void SpreadsheetView::clearSelectedCells() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected cells", m_spreadsheet->name())); for (auto* column : selectedColumns()) { column->setSuppressDataChangedSignal(true); if (formulaModeActive()) { int col = m_spreadsheet->indexOfChild(column); for (int row = last; row >= first; row--) if (isCellSelected(row, col)) column->setFormula(row, QString()); } else { int col = m_spreadsheet->indexOfChild(column); for (int row = last; row >= first; row--) if (isCellSelected(row, col)) { if (row < column->rowCount()) column->asStringColumn()->setTextAt(row, QString()); } } column->setSuppressDataChangedSignal(false); column->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::goToCell() { bool ok; int col = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_spreadsheet->columnCount(), 1, &ok); if (!ok) return; int row = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_spreadsheet->rowCount(), 1, &ok); if (!ok) return; goToCell(row-1, col-1); } //! Open the sort dialog for the given columns void SpreadsheetView::sortDialog(QVector cols) { if (cols.isEmpty()) return; for (auto* col : cols) col->setSuppressDataChangedSignal(true); auto* dlg = new SortDialog(); connect(dlg, SIGNAL(sort(Column*,QVector,bool)), m_spreadsheet, SLOT(sortColumns(Column*,QVector,bool))); dlg->setColumns(cols); int rc = dlg->exec(); for (auto* col : cols) { col->setSuppressDataChangedSignal(false); if (rc == QDialog::Accepted) col->setChanged(); } } void SpreadsheetView::sortColumnAscending() { QVector cols = selectedColumns(); for (auto* col : cols) col->setSuppressDataChangedSignal(true); m_spreadsheet->sortColumns(cols.first(), cols, true); for (auto* col : cols) { col->setSuppressDataChangedSignal(false); col->setChanged(); } } void SpreadsheetView::sortColumnDescending() { QVector cols = selectedColumns(); for (auto* col : cols) col->setSuppressDataChangedSignal(true); m_spreadsheet->sortColumns(cols.first(), cols, false); for (auto* col : cols) { col->setSuppressDataChangedSignal(false); col->setChanged(); } } /*! Cause a repaint of the header. */ void SpreadsheetView::updateHeaderGeometry(Qt::Orientation o, int first, int last) { Q_UNUSED(first) Q_UNUSED(last) //TODO if (o != Qt::Horizontal) return; m_tableView->horizontalHeader()->setStretchLastSection(true); // ugly hack (flaw in Qt? Does anyone know a better way?) m_tableView->horizontalHeader()->updateGeometry(); m_tableView->horizontalHeader()->setStretchLastSection(false); // ugly hack part 2 } /*! selects the column \c column in the speadsheet view . */ void SpreadsheetView::selectColumn(int column) { QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) ); m_suppressSelectionChangedEvent = true; m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select); m_suppressSelectionChangedEvent = false; } /*! deselects the column \c column in the speadsheet view . */ void SpreadsheetView::deselectColumn(int column) { QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) ); m_suppressSelectionChangedEvent = true; m_tableView->selectionModel()->select(selection, QItemSelectionModel::Deselect); m_suppressSelectionChangedEvent = false; } /*! called when a column in the speadsheet view was clicked (click in the header). Propagates the selection of the column to the \c Spreadsheet object (a click in the header always selects the column). */ void SpreadsheetView::columnClicked(int column) { m_spreadsheet->setColumnSelectedInView(column, true); } /*! called on selections changes. Propagates the selection/deselection of columns to the \c Spreadsheet object. */ void SpreadsheetView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { Q_UNUSED(selected); Q_UNUSED(deselected); if (m_suppressSelectionChangedEvent) return; QItemSelectionModel* selModel = m_tableView->selectionModel(); for (int i = 0; i < m_spreadsheet->columnCount(); i++) m_spreadsheet->setColumnSelectedInView(i, selModel->isColumnSelected(i, QModelIndex())); } bool SpreadsheetView::exportView() { auto* dlg = new ExportSpreadsheetDialog(this); dlg->setFileName(m_spreadsheet->name()); dlg->setExportTo(QStringList() << i18n("FITS image") << i18n("FITS table")); for (int i = 0; i < m_spreadsheet->columnCount(); ++i) { if (m_spreadsheet->column(i)->columnMode() != AbstractColumn::Numeric) { dlg->setExportToImage(false); break; } } if (selectedColumnCount() == 0) dlg->setExportSelection(false); bool ret; if ((ret = dlg->exec()) == QDialog::Accepted) { const QString path = dlg->path(); const bool exportHeader = dlg->exportHeader(); WAIT_CURSOR; switch (dlg->format()) { case ExportSpreadsheetDialog::ASCII: { const QString separator = dlg->separator(); const QLocale::Language format = dlg->numberFormat(); exportToFile(path, exportHeader, separator, format); break; } case ExportSpreadsheetDialog::Binary: break; case ExportSpreadsheetDialog::LaTeX: { const bool exportLatexHeader = dlg->exportLatexHeader(); const bool gridLines = dlg->gridLines(); const bool captions = dlg->captions(); const bool skipEmptyRows = dlg->skipEmptyRows(); const bool exportEntire = dlg->entireSpreadheet(); exportToLaTeX(path, exportHeader, gridLines, captions, exportLatexHeader, skipEmptyRows, exportEntire); break; } case ExportSpreadsheetDialog::FITS: { const int exportTo = dlg->exportToFits(); const bool commentsAsUnits = dlg->commentsAsUnitsFits(); exportToFits(path, exportTo, commentsAsUnits); break; } case ExportSpreadsheetDialog::SQLite: exportToSQLite(path); break; } RESET_CURSOR; } delete dlg; return ret; } bool SpreadsheetView::printView() { QPrinter printer; auto* dlg = new QPrintDialog(&printer, this); dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); bool ret; if ((ret = dlg->exec()) == QDialog::Accepted) { print(&printer); } delete dlg; return ret; } bool SpreadsheetView::printPreview() { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); connect(dlg, &QPrintPreviewDialog::paintRequested, this, &SpreadsheetView::print); return dlg->exec(); } /*! prints the complete spreadsheet to \c printer. */ void SpreadsheetView::print(QPrinter* printer) const { WAIT_CURSOR; QPainter painter (printer); const int dpiy = printer->logicalDpiY(); const int margin = (int) ( (1/2.54)*dpiy ); // 1 cm margins QHeaderView *hHeader = m_tableView->horizontalHeader(); QHeaderView *vHeader = m_tableView->verticalHeader(); const int rows = m_spreadsheet->rowCount(); const int cols = m_spreadsheet->columnCount(); int height = margin; const int vertHeaderWidth = vHeader->width(); int right = margin + vertHeaderWidth; int columnsPerTable = 0; int headerStringWidth = 0; int firstRowStringWidth = 0; bool tablesNeeded = false; for (int col = 0; col < cols; ++col) { headerStringWidth += m_tableView->columnWidth(col); firstRowStringWidth += m_spreadsheet->column(col)->asStringColumn()->textAt(0).length(); if ((headerStringWidth >= printer->pageRect().width() -2*margin) || (firstRowStringWidth >= printer->pageRect().width() - 2*margin)) { tablesNeeded = true; break; } columnsPerTable++; } int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; if (!tablesNeeded) { tablesCount = 1; columnsPerTable = cols; } if (remainingColumns > 0) tablesCount++; //Paint the horizontal header first for (int table = 0; table < tablesCount; ++table) { right = margin + vertHeaderWidth; painter.setFont(hHeader->font()); QString headerString = m_tableView->model()->headerData(0, Qt::Horizontal).toString(); QRect br; br = painter.boundingRect(br, Qt::AlignCenter, headerString); QRect tr(br); if (table != 0) height += tr.height(); painter.drawLine(right, height, right, height+br.height()); int i = table * columnsPerTable; int toI = table * columnsPerTable + columnsPerTable; if ((remainingColumns > 0) && (table == tablesCount-1)) { i = (tablesCount-1)*columnsPerTable; toI = (tablesCount-1)* columnsPerTable + remainingColumns; } for (; imodel()->headerData(i, Qt::Horizontal).toString(); const int w = m_tableView->columnWidth(i); tr.setTopLeft(QPoint(right,height)); tr.setWidth(w); tr.setHeight(br.height()); painter.drawText(tr, Qt::AlignCenter, headerString); right += w; painter.drawLine(right, height, right, height+tr.height()); } painter.drawLine(margin + vertHeaderWidth, height, right-1, height);//first horizontal line height += tr.height(); painter.drawLine(margin, height, right-1, height); // print table values QString cellText; for (i = 0; i < rows; ++i) { right = margin; cellText = m_tableView->model()->headerData(i, Qt::Vertical).toString()+'\t'; tr = painter.boundingRect(tr, Qt::AlignCenter, cellText); painter.drawLine(right, height, right, height+tr.height()); br.setTopLeft(QPoint(right,height)); br.setWidth(vertHeaderWidth); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += vertHeaderWidth; painter.drawLine(right, height, right, height+tr.height()); int j = table * columnsPerTable; int toJ = table * columnsPerTable + columnsPerTable; if ((remainingColumns > 0) && (table == tablesCount-1)) { j = (tablesCount-1)*columnsPerTable; toJ = (tablesCount-1)* columnsPerTable + remainingColumns; } for (; j < toJ; j++) { int w = m_tableView->columnWidth(j); cellText = m_spreadsheet->column(j)->isValid(i) ? m_spreadsheet->text(i,j)+'\t': QLatin1String("- \t"); tr = painter.boundingRect(tr,Qt::AlignCenter,cellText); br.setTopLeft(QPoint(right,height)); br.setWidth(w); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += w; painter.drawLine(right, height, right, height+tr.height()); } height += br.height(); painter.drawLine(margin, height, right-1, height); if (height >= printer->height() - margin) { printer->newPage(); height = margin; painter.drawLine(margin, height, right, height); } } } RESET_CURSOR; } void SpreadsheetView::registerShortcuts() { action_clear_selection->setShortcut(QKeySequence::Delete); } void SpreadsheetView::unregisterShortcuts() { action_clear_selection->setShortcut(QKeySequence()); } void SpreadsheetView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export spreadsheet to file"); QTextStream out(&file); const int cols = m_spreadsheet->columnCount(); QString sep = separator; sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive); sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); //export header (column names) if (exportHeader) { for (int j = 0; j < cols; ++j) { out << m_spreadsheet->column(j)->name(); if (j != cols - 1) out << sep; } out << '\n'; } //export values QLocale locale(language); for (int i = 0; i < m_spreadsheet->rowCount(); ++i) { for (int j = 0; j < cols; ++j) { Column* col = m_spreadsheet->column(j); if (col->columnMode() == AbstractColumn::Numeric) { const Double2StringFilter* out_fltr = static_cast(col->outputFilter()); out << locale.toString(col->valueAt(i), out_fltr->numericFormat(), 16); // export with max. precision } else out << col->asStringColumn()->textAt(i); if (j != cols - 1) out << sep; } out << '\n'; } } void SpreadsheetView::exportToLaTeX(const QString & path, const bool exportHeaders, const bool gridLines, const bool captions, const bool latexHeaders, const bool skipEmptyRows, const bool exportEntire) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; QList toExport; int cols; int totalRowCount = 0; if (exportEntire) { cols = const_cast(this)->m_spreadsheet->columnCount(); totalRowCount = m_spreadsheet->rowCount(); for (int col = 0; col < cols; ++col) toExport << m_spreadsheet->column(col); } else { cols = const_cast(this)->selectedColumnCount(); const int firtsSelectedCol = const_cast(this)->firstSelectedColumn(); bool rowsCalculated = false; for (int col = firtsSelectedCol; col < firtsSelectedCol + cols; ++col) { QVector textData; for (int row = 0; row < m_spreadsheet->rowCount(); ++row) { if (const_cast(this)->isRowSelected(row)) { textData << m_spreadsheet->column(col)->asStringColumn()->textAt(row); if (!rowsCalculated) totalRowCount++; } } if (!rowsCalculated) rowsCalculated = true; Column* column = new Column(m_spreadsheet->column(col)->name(), textData); toExport << column; } } int columnsStringSize = 0; int columnsPerTable = 0; for (int i = 0; i < cols; ++i) { int maxSize = -1; for (int j = 0; j < toExport.at(i)->asStringColumn()->rowCount(); ++j) { if (toExport.at(i)->asStringColumn()->textAt(j).size() > maxSize) maxSize = toExport.at(i)->asStringColumn()->textAt(j).size(); } columnsStringSize += maxSize; if (!toExport.at(i)->isValid(0)) columnsStringSize += 3; if (columnsStringSize > 65) break; ++columnsPerTable; } const int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; bool columnsSeparating = (cols > columnsPerTable); QTextStream out(&file); QProcess tex; tex.start("latex", QStringList() << "--version", QProcess::ReadOnly); tex.waitForFinished(500); QString texVersionOutput = QString(tex.readAllStandardOutput()); texVersionOutput = texVersionOutput.split('\n')[0]; int yearidx = -1; for (int i = texVersionOutput.size() - 1; i >= 0; --i) { if (texVersionOutput.at(i) == QChar('2')) { yearidx = i; break; } } if (texVersionOutput.at(yearidx+1) == QChar('/')) yearidx -= 3; bool ok; texVersionOutput.midRef(yearidx, 4).toInt(&ok); int version = -1; if (ok) version = texVersionOutput.midRef(yearidx, 4).toInt(&ok); if (latexHeaders) { out << QLatin1String("\\documentclass[11pt,a4paper]{article} \n"); out << QLatin1String("\\usepackage{geometry} \n"); out << QLatin1String("\\usepackage{xcolor,colortbl} \n"); if (version >= 2015) out << QLatin1String("\\extrafloats{1280} \n"); out << QLatin1String("\\definecolor{HeaderBgColor}{rgb}{0.81,0.81,0.81} \n"); out << QLatin1String("\\geometry{ \n"); out << QLatin1String("a4paper, \n"); out << QLatin1String("total={170mm,257mm}, \n"); out << QLatin1String("left=10mm, \n"); out << QLatin1String("top=10mm } \n"); out << QLatin1String("\\begin{document} \n"); out << QLatin1String("\\title{LabPlot Spreadsheet Export to \\LaTeX{} } \n"); out << QLatin1String("\\author{LabPlot} \n"); out << QLatin1String("\\date{\\today} \n"); } QString endTabularTable ("\\end{tabular} \n \\end{table} \n"); QString tableCaption ("\\caption{"+ m_spreadsheet->name()+ "} \n"); QString beginTable ("\\begin{table}[ht] \n"); int rowCount = 0; const int maxRows = 45; bool captionRemoved = false; if (columnsSeparating) { QVector emptyRowIndices; for (int table = 0; table < tablesCount; ++table) { QStringList textable; captionRemoved = false; textable << beginTable; if (captions) textable << tableCaption; textable << QLatin1String("\\centering \n"); textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString()); for (int i = 0; i < columnsPerTable; ++i) textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) { textable << toExport.at(col)->name(); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } for (const auto& s : textable) out << s; QStringList values; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) { if (toExport.at(col)->isValid(row)) { notEmpty = true; values << toExport.at(col)->asStringColumn()->textAt(row); } else values << QLatin1String("-"); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) values << QLatin1String(" & "); } if (!notEmpty && skipEmptyRows) { if (!emptyRowIndices.contains(row)) emptyRowIndices << row; } if (emptyRowIndices.contains(row) && notEmpty) emptyRowIndices.remove(emptyRowIndices.indexOf(row)); if (notEmpty || !skipEmptyRows) { for (const auto& s : values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\newpage \n"); if (captions) if (!captionRemoved) textable.removeAt(1); for (const auto& s : textable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } //new table for the remaining columns QStringList remainingTable; remainingTable << beginTable; if (captions) remainingTable << tableCaption; remainingTable << QLatin1String("\\centering \n"); remainingTable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString()); for (int c = 0; c < remainingColumns; ++c) remainingTable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); remainingTable << QLatin1String("} \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = 0; col < remainingColumns; ++col) { remainingTable << toExport.at(col + (tablesCount * columnsPerTable))->name(); if (col != remainingColumns-1) remainingTable << QLatin1String(" & "); } remainingTable << QLatin1String("\\\\ \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); } for (const auto& s : remainingTable) out << s; QStringList values; captionRemoved = false; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = 0; col < remainingColumns; ++col ) { if (toExport.at(col + (tablesCount * columnsPerTable))->isValid(row)) { notEmpty = true; values << toExport.at(col + (tablesCount * columnsPerTable))->asStringColumn()->textAt(row); } else values << QLatin1String("-"); if (col != remainingColumns-1) values << QLatin1String(" & "); } if (!emptyRowIndices.contains(row) && !notEmpty) notEmpty = true; if (notEmpty || !skipEmptyRows) { for (const auto& s : values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\pagebreak[4] \n"); if (captions) if (!captionRemoved) remainingTable.removeAt(1); for (const auto& s : remainingTable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } else { QStringList textable; textable << beginTable; if (captions) textable << tableCaption; textable << QLatin1String("\\centering \n"); textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString()); for (int c = 0; c < cols; ++c) textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") ); textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (exportHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); for (int col = 0; col < cols; ++col) { textable << toExport.at(col)->name(); if (col != cols-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } for (const auto& s : textable) out << s; QStringList values; captionRemoved = false; for (int row = 0; row < totalRowCount; ++row) { values.clear(); bool notEmpty = false; for (int col = 0; col < cols; ++col ) { if (toExport.at(col)->isValid(row)) { notEmpty = true; values << toExport.at(col)->asStringColumn()->textAt(row); } else values << "-"; if (col != cols-1) values << " & "; } if (notEmpty || !skipEmptyRows) { for (const auto& s : values) out << s; out << QLatin1String("\\\\ \n"); if (gridLines) out << QLatin1String("\\hline \n"); rowCount++; if (rowCount == maxRows) { out << endTabularTable; out << QLatin1String("\\newpage \n"); if (captions) if (!captionRemoved) textable.removeAt(1); for (const auto& s : textable) out << s; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } } out << endTabularTable; } if (latexHeaders) out << QLatin1String("\\end{document} \n"); if (!exportEntire) { qDeleteAll(toExport); toExport.clear(); } else toExport.clear(); } void SpreadsheetView::exportToFits(const QString &fileName, const int exportTo, const bool commentsAsUnits) const { auto* filter = new FITSFilter; filter->setExportTo(exportTo); filter->setCommentsAsUnits(commentsAsUnits); filter->write(fileName, m_spreadsheet); delete filter; } void SpreadsheetView::exportToSQLite(const QString& path) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export spreadsheet to SQLite database"); QApplication::processEvents(QEventLoop::AllEvents, 0); //create database const QStringList& drivers = QSqlDatabase::drivers(); QString driver; if (drivers.contains(QLatin1String("QSQLITE3"))) driver = QLatin1String("QSQLITE3"); else driver = QLatin1String("QSQLITE"); QSqlDatabase db = QSqlDatabase::addDatabase(driver); db.setDatabaseName(path); if (!db.open()) { RESET_CURSOR; KMessageBox::error(nullptr, i18n("Couldn't create the SQLite database %1.", path)); } //create table const int cols = m_spreadsheet->columnCount(); QString query = QLatin1String("create table ") + m_spreadsheet->name() + QLatin1String(" ("); for (int i = 0; i < cols; ++i) { Column* col = m_spreadsheet->column(i); if (i != 0) query += QLatin1String(", "); query += QLatin1String("\"") + col->name() + QLatin1String("\" "); switch (col->columnMode()) { case AbstractColumn::Numeric: query += QLatin1String("REAL"); break; case AbstractColumn::Integer: query += QLatin1String("INTEGER"); break; case AbstractColumn::Text: case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: query += QLatin1String("TEXT"); break; } } query += QLatin1Char(')'); QSqlQuery q; if (!q.exec(query)) { RESET_CURSOR; KMessageBox::error(nullptr, i18n("Failed to create table in the SQLite database %1.", path) + "\n" + q.lastError().databaseText()); db.close(); return; } //create bulk insert statement { PERFTRACE("Create the bulk insert statement"); q.exec(QLatin1String("BEGIN TRANSACTION;")); query = "INSERT INTO '" + m_spreadsheet->name() + "' ("; for (int i = 0; i < cols; ++i) { if (i != 0) query += QLatin1String(", "); query += QLatin1Char('\'') + m_spreadsheet->column(i)->name() + QLatin1Char('\''); } query += QLatin1String(") VALUES "); for (int i = 0; i < m_spreadsheet->rowCount(); ++i) { if (i != 0) query += QLatin1String(","); query += QLatin1Char('('); for (int j = 0; j < cols; ++j) { Column* col = m_spreadsheet->column(j); if (j != 0) query += QLatin1String(", "); query += QLatin1Char('\'') + col->asStringColumn()->textAt(i) + QLatin1Char('\''); } query += QLatin1String(")"); } query += QLatin1Char(';'); } //insert values if (!q.exec(query)) { RESET_CURSOR; KMessageBox::error(nullptr, i18n("Failed to insert values into the table.")); QDEBUG("bulk insert error " << q.lastError().databaseText()); } else q.exec(QLatin1String("COMMIT TRANSACTION;")); //close the database db.close(); } diff --git a/src/kdefrontend/GuiObserver.cpp b/src/kdefrontend/GuiObserver.cpp index 3c0213cc1..ca25276b0 100644 --- a/src/kdefrontend/GuiObserver.cpp +++ b/src/kdefrontend/GuiObserver.cpp @@ -1,487 +1,487 @@ /*************************************************************************** File : GuiObserver.cpp Project : LabPlot Description : GUI observer -------------------------------------------------------------------- Copyright : (C) 2010-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2016 Garvit Khatri (garvitdelhi@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "kdefrontend/GuiObserver.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/AbstractAspect.h" #include "backend/datasources/LiveDataSource.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "backend/matrix/Matrix.h" #include "backend/pivot/PivotTable.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/CustomPoint.h" #include "backend/worksheet/plots/cartesian/Histogram.h" #include "backend/worksheet/TextLabel.h" #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #include "backend/datasources/MQTTSubscription.h" #include "backend/datasources/MQTTTopic.h" #endif #include "backend/core/Project.h" #include "backend/datapicker/Datapicker.h" #include "backend/datapicker/DatapickerImage.h" #include "backend/datapicker/DatapickerCurve.h" #include "commonfrontend/ProjectExplorer.h" #include "kdefrontend/MainWin.h" #include "kdefrontend/dockwidgets/AxisDock.h" #include "kdefrontend/dockwidgets/NoteDock.h" #include "kdefrontend/dockwidgets/CursorDock.h" #include "kdefrontend/dockwidgets/CartesianPlotDock.h" #include "kdefrontend/dockwidgets/CartesianPlotLegendDock.h" #include "kdefrontend/dockwidgets/ColumnDock.h" #include "kdefrontend/dockwidgets/HypothesisTestDock.h" #include "kdefrontend/dockwidgets/LiveDataDock.h" #include "kdefrontend/dockwidgets/MatrixDock.h" #include "kdefrontend/dockwidgets/PivotTableDock.h" #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/dockwidgets/SpreadsheetDock.h" #include "kdefrontend/dockwidgets/XYCurveDock.h" #include "kdefrontend/dockwidgets/HistogramDock.h" #include "kdefrontend/dockwidgets/XYEquationCurveDock.h" #include "kdefrontend/dockwidgets/XYDataReductionCurveDock.h" #include "kdefrontend/dockwidgets/XYDifferentiationCurveDock.h" #include "kdefrontend/dockwidgets/XYIntegrationCurveDock.h" #include "kdefrontend/dockwidgets/XYInterpolationCurveDock.h" #include "kdefrontend/dockwidgets/XYSmoothCurveDock.h" #include "kdefrontend/dockwidgets/XYFitCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierFilterCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierTransformCurveDock.h" #include "kdefrontend/dockwidgets/XYConvolutionCurveDock.h" #include "kdefrontend/dockwidgets/XYCorrelationCurveDock.h" #include "kdefrontend/dockwidgets/CustomPointDock.h" #include "kdefrontend/dockwidgets/WorksheetDock.h" #ifdef HAVE_CANTOR_LIBS #include "kdefrontend/dockwidgets/CantorWorksheetDock.h" #endif #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/widgets/DatapickerImageWidget.h" #include "kdefrontend/widgets/DatapickerCurveWidget.h" #include #include #include #include #include /*! \class GuiObserver \brief The GUI observer looks for the selection changes in the main window and shows/hides the correspondings dock widgets, toolbars etc. This class is intended to simplify (or not to overload) the code in MainWin. \ingroup kdefrontend */ namespace GuiObserverHelper { template bool raiseDock(T*& dock, QStackedWidget* parent) { const bool generated = !dock; if (generated) { dock = new T(parent); parent->addWidget(dock); } parent->setCurrentWidget(dock); return generated; } template void raiseDockConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent) { if (raiseDock(dock, parent)) QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); }); } template void raiseDockSetupConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent) { if (raiseDock(dock, parent)) { dock->setupGeneral(); QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); }); } } template QList castList(QList& selectedAspects) { QList list; for (auto* aspect : selectedAspects) list << static_cast(aspect); return list; } } using namespace GuiObserverHelper; GuiObserver::GuiObserver(MainWin* mainWin) { connect(mainWin->m_projectExplorer, &ProjectExplorer::selectedAspectsChanged, this, &GuiObserver::selectedAspectsChanged); connect(mainWin->m_projectExplorer, &ProjectExplorer::hiddenAspectSelected, this, &GuiObserver::hiddenAspectSelected); m_mainWindow = mainWin; } /*! called on selection changes in the project explorer. Determines the type of the currently selected objects (aspects) and activates the corresponding dockwidgets, toolbars etc. */ void GuiObserver::selectedAspectsChanged(QList& selectedAspects) const { auto clearDock = [&](){ if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->hide(); m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); }; if (selectedAspects.isEmpty()) { clearDock(); return; } // update cursor dock AbstractAspect* parent = selectedAspects[0]->parent(AspectType::Worksheet); if (selectedAspects[0]->inherits(AspectType::Worksheet)) { Worksheet* worksheet = static_cast(selectedAspects[0]); if (m_mainWindow->cursorWidget) { m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel()); QVector plots = worksheet->children(); if (!plots.isEmpty()) m_mainWindow->cursorWidget->setPlots(plots); } } else if (parent) { if (m_mainWindow->cursorWidget) { Worksheet* worksheet = static_cast(parent); QVector plots = worksheet->children(); m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel()); m_mainWindow->cursorWidget->setPlots(plots); } } const AspectType type = selectedAspects.front()->type(); // Check, whether objects of different types were selected. // Don't show any dock widgets in this case. for (auto* aspect : selectedAspects) { if (aspect->type() != type) { clearDock(); return; } } switch (type) { case AspectType::Spreadsheet: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet")); raiseDockConnect(m_mainWindow->spreadsheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->spreadsheetDock->setSpreadsheets(castList(selectedAspects)); break; case AspectType::PivotTable: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Pivot Table")); if (!m_mainWindow->pivotTableDock) { m_mainWindow->pivotTableDock = new PivotTableDock(m_mainWindow->stackedWidget); connect(m_mainWindow->pivotTableDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->pivotTableDock); } m_mainWindow->pivotTableDock->setPivotTable(static_cast(selectedAspects.first())); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->pivotTableDock); break; case AspectType::HypothesisTest: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Hypothesis Test")); if (!m_mainWindow->hypothesisTestDock) { m_mainWindow->hypothesisTestDock = new HypothesisTestDock(m_mainWindow->stackedWidget); connect(m_mainWindow->hypothesisTestDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->hypothesisTestDock); } m_mainWindow->hypothesisTestDock->setHypothesisTest(static_cast(selectedAspects.first())); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->hypothesisTestDock); break; case AspectType::Column: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column")); raiseDockConnect(m_mainWindow->columnDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->columnDock->setColumns(castList(selectedAspects)); break; case AspectType::Matrix: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix")); raiseDockConnect(m_mainWindow->matrixDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->matrixDock->setMatrices(castList(selectedAspects)); break; case AspectType::Worksheet: { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Worksheet")); if (!m_mainWindow->worksheetDock) { m_mainWindow->worksheetDock = new WorksheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->worksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->worksheetDock); } raiseDockConnect(m_mainWindow->worksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->worksheetDock->setWorksheets(castList(selectedAspects)); break; } case AspectType::CartesianPlot: { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot")); raiseDockConnect(m_mainWindow->cartesianPlotDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->cartesianPlotDock->setPlots(castList(selectedAspects)); break; } case AspectType::CartesianPlotLegend: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend")); raiseDockConnect(m_mainWindow->cartesianPlotLegendDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->cartesianPlotLegendDock->setLegends(castList(selectedAspects)); break; case AspectType::Axis: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis")); raiseDockConnect(m_mainWindow->axisDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->axisDock->setAxes(castList(selectedAspects)); break; case AspectType::XYCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve")); raiseDockSetupConnect(m_mainWindow->xyCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYEquationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation")); raiseDockSetupConnect(m_mainWindow->xyEquationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyEquationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYDataReductionCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Data Reduction")); if (!m_mainWindow->xyDataReductionCurveDock) { m_mainWindow->xyDataReductionCurveDock = new XYDataReductionCurveDock(m_mainWindow->stackedWidget, m_mainWindow->statusBar()); m_mainWindow->xyDataReductionCurveDock->setupGeneral(); connect(m_mainWindow->xyDataReductionCurveDock, &XYDataReductionCurveDock::info, [&](const QString& text){ m_mainWindow->statusBar()->showMessage(text); }); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock); } m_mainWindow->xyDataReductionCurveDock->setCurves(castList(selectedAspects)); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock); break; case AspectType::XYDifferentiationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation")); raiseDockSetupConnect(m_mainWindow->xyDifferentiationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyDifferentiationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYIntegrationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration")); raiseDockSetupConnect(m_mainWindow->xyIntegrationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyIntegrationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYInterpolationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation")); raiseDockSetupConnect(m_mainWindow->xyInterpolationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyInterpolationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYSmoothCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing")); raiseDockSetupConnect(m_mainWindow->xySmoothCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xySmoothCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFitCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit")); raiseDockSetupConnect(m_mainWindow->xyFitCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFitCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFourierTransformCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform")); raiseDockSetupConnect(m_mainWindow->xyFourierTransformCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFourierTransformCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFourierFilterCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter")); raiseDockSetupConnect(m_mainWindow->xyFourierFilterCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFourierFilterCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYConvolution: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Convolution/Deconvolution")); raiseDockSetupConnect(m_mainWindow->xyConvolutionCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyConvolutionCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYCorrelationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Auto-/Cross-Correlation")); raiseDockSetupConnect(m_mainWindow->xyCorrelationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyCorrelationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::Histogram: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties")); raiseDockConnect(m_mainWindow->histogramDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->histogramDock->setCurves(castList(selectedAspects)); break; case AspectType::TextLabel: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label")); raiseDock(m_mainWindow->textLabelDock, m_mainWindow->stackedWidget); m_mainWindow->textLabelDock->setLabels(castList(selectedAspects)); break; case AspectType::CustomPoint: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point")); raiseDock(m_mainWindow->customPointDock, m_mainWindow->stackedWidget); m_mainWindow->customPointDock->setPoints(castList(selectedAspects)); break; case AspectType::DatapickerCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve")); raiseDock(m_mainWindow->datapickerCurveDock, m_mainWindow->stackedWidget); m_mainWindow->datapickerCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::Datapicker: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker")); raiseDock(m_mainWindow->datapickerImageDock, m_mainWindow->stackedWidget); { QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect)->image(); m_mainWindow->datapickerImageDock->setImages(list); } break; case AspectType::Project: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project")); raiseDock(m_mainWindow->projectDock, m_mainWindow->stackedWidget); m_mainWindow->projectDock->setProject(m_mainWindow->m_project); break; case AspectType::CantorWorksheet: #ifdef HAVE_CANTOR_LIBS raiseDockConnect(m_mainWindow->cantorWorksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); { QList list = castList(selectedAspects); if (list.size() == 1) m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window %1 is a Cantor backend", "%1 Properties", list.first()->backendName())); else m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "CAS Properties")); m_mainWindow->cantorWorksheetDock->setCantorWorksheets(list); } #endif break; case AspectType::Note: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes")); raiseDock(m_mainWindow->notesDock, m_mainWindow->stackedWidget); m_mainWindow->notesDock->setNotesList(castList(selectedAspects)); break; case AspectType::MQTTClient: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())); #endif break; case AspectType::MQTTSubscription: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); #endif break; case AspectType::MQTTTopic: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); #endif break; case AspectType::LiveDataSource: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setLiveDataSource(static_cast(selectedAspects.first())); break; case AspectType::AbstractAspect: case AspectType::AbstractColumn: case AspectType::AbstractDataSource: case AspectType::AbstractFilter: case AspectType::AbstractPart: case AspectType::AbstractPlot: case AspectType::ColumnStringIO: case AspectType::DatapickerImage: case AspectType::DatapickerPoint: case AspectType::Folder: case AspectType::PlotArea: case AspectType::SimpleFilterColumn: case AspectType::Workbook: case AspectType::WorksheetElement: case AspectType::WorksheetElementContainer: case AspectType::WorksheetElementGroup: case AspectType::XYAnalysisCurve: clearDock(); return; } m_mainWindow->stackedWidget->currentWidget()->show(); } /*! handles the selection of a hidden aspect \c aspect in the view (relevant for WorksheetView only at the moment). Currently, a hidden aspect can only be a plot title lable or an axis label. -> Activate the corresponding DockWidget and make the title tab current. */ void GuiObserver::hiddenAspectSelected(const AbstractAspect* aspect) const { const AbstractAspect* parent = aspect->parentAspect(); if (!parent) return; switch (static_cast(parent->type())) { // cast the enum to turn off warnings about unhandled cases case static_cast(AspectType::Axis): if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } m_mainWindow->axisDock->activateTitleTab(); break; case static_cast(AspectType::CartesianPlot): if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } m_mainWindow->cartesianPlotDock->activateTitleTab(); break; case static_cast(AspectType::CartesianPlotLegend): if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } m_mainWindow->cartesianPlotLegendDock->activateTitleTab(); break; default: break; } } diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index 8666f15e6..db3cd17ae 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2086 +1,2086 @@ /*************************************************************************** File : MainWin.cc Project : LabPlot Description : Main window of the application -------------------------------------------------------------------- Copyright : (C) 2008-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "MainWin.h" #include "backend/core/Project.h" #include "backend/core/Folder.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Workbook.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/pivot/PivotTable.h" #include "backend/worksheet/Worksheet.h" #include "backend/datasources/LiveDataSource.h" #ifdef HAVE_LIBORIGIN #include "backend/datasources/projects/OriginProjectParser.h" #endif #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #include "backend/datapicker/Datapicker.h" #include "backend/note/Note.h" #include "backend/lib/macros.h" #include "backend/worksheet/TreeModel.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #endif #include "commonfrontend/core/PartMdiView.h" #include "commonfrontend/ProjectExplorer.h" #include "commonfrontend/matrix/MatrixView.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/worksheet/WorksheetView.h" #ifdef HAVE_CANTOR_LIBS #include "commonfrontend/cantorWorksheet/CantorWorksheetView.h" #endif #include "commonfrontend/datapicker/DatapickerView.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include "commonfrontend/note/NoteView.h" #include "commonfrontend/widgets/MemoryWidget.h" #include "kdefrontend/datasources/ImportFileDialog.h" #include "kdefrontend/datasources/ImportProjectDialog.h" #include "kdefrontend/datasources/ImportSQLDatabaseDialog.h" #include #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/HistoryDialog.h" #include "kdefrontend/SettingsDialog.h" #include "kdefrontend/GuiObserver.h" #include "kdefrontend/widgets/FITSHeaderEditDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CANTOR_LIBS #include #include #include #include #endif /*! \class MainWin \brief Main application window. \ingroup kdefrontend */ MainWin::MainWin(QWidget *parent, const QString& filename) : KXmlGuiWindow(parent) { initGUI(filename); setAcceptDrops(true); //restore the geometry KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); restoreGeometry(group.readEntry("geometry", QByteArray())); } MainWin::~MainWin() { //save the recent opened files m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") ); KConfigGroup group = KSharedConfig::openConfig()->group("MainWin"); group.writeEntry("geometry", saveGeometry()); KSharedConfig::openConfig()->sync(); if (m_project != nullptr) { m_mdiArea->closeAllSubWindows(); disconnect(m_project, nullptr, this, nullptr); delete m_project; } if (m_aspectTreeModel) delete m_aspectTreeModel; if (m_guiObserver) delete m_guiObserver; } void MainWin::showPresenter() { Worksheet* w = activeWorksheet(); if (w) { auto* view = dynamic_cast(w->view()); view->presenterMode(); } else { //currently active object is not a worksheet but we're asked to start in the presenter mode //determine the first available worksheet and show it in the presenter mode QVector worksheets = m_project->children(); if (worksheets.size() > 0) { auto* view = qobject_cast(worksheets.first()->view()); view->presenterMode(); } else { QMessageBox::information(this, i18n("Presenter Mode"), i18n("No worksheets are available in the project. The presenter mode will not be started.")); } } } AspectTreeModel* MainWin::model() const { return m_aspectTreeModel; } /*! * Show cursor dock and set the treeview model * @param model */ void MainWin::showCursorDock(TreeModel* model, QVector plots) { if (!cursorDock) { cursorDock = new QDockWidget(i18n("Cursor"), this); cursorWidget = new CursorDock(cursorDock); cursorDock->setWidget(cursorWidget); cursorDock->setFloating(true); // does not work. Don't understand why // if (m_propertiesDock) // tabifyDockWidget(cursorDock, m_propertiesDock); // else addDockWidget(Qt::DockWidgetArea::AllDockWidgetAreas, cursorDock); } cursorWidget->setCursorTreeViewModel(model); cursorWidget->setPlots(plots); cursorDock->show(); } Project* MainWin::project() const { return m_project; } void MainWin::initGUI(const QString& fileName) { m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged); statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION))); initActions(); #ifdef Q_OS_DARWIN setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc")); #else setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc" #endif DEBUG("component name: " << KXMLGUIClient::componentName().toStdString()); DEBUG("XML file: " << KXMLGUIClient::xmlFile().toStdString() << " (should be \"labplot2ui.rc\")"); //all toolbars created via the KXMLGUI framework are locked on default: // * on the very first program start, unlock all toolbars // * on later program starts, set stored lock status //Furthermore, we want to show icons only after the first program start. KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow"); if (groupMain.exists()) { //KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable" //in case the toolbars are locked -> load this value const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), ""); bool locked = (str == QLatin1String("Disabled")); KToolBar::setToolBarsLocked(locked); } else { //first start KToolBar::setToolBarsLocked(false); //show icons only for (auto* container : factory()->containers(QLatin1String("ToolBar"))) { auto* toolbar = dynamic_cast(container); if (toolbar) toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } } initMenus(); auto* mainToolBar = qobject_cast(factory()->container("main_toolbar", this)); if (!mainToolBar) { QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("%1 file was not found. Please check your installation.", KXMLGUIClient::xmlFile())); //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes //the application because of the splash screen. We need to find another solution. // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop return; } auto* tbImport = new QToolButton(mainToolBar); tbImport->setPopupMode(QToolButton::MenuButtonPopup); tbImport->setMenu(m_importMenu); tbImport->setDefaultAction(m_importFileAction); mainToolBar->addWidget(tbImport); qobject_cast(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import")); setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon())); setAttribute( Qt::WA_DeleteOnClose ); //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there. QFont font; font.setFamily(font.defaultFamily()); QFontMetrics fm(font); statusBar()->setFixedHeight(fm.height() + 5); //load recently used projects m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") ); //set the view mode of the mdi area KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); } //auto-save m_autoSaveActive = group.readEntry("AutoSave", false); int interval = group.readEntry("AutoSaveInterval", 1); interval = interval*60*1000; m_autoSaveTimer.setInterval(interval); connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWin::autoSaveProject); if (!fileName.isEmpty()) { #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(fileName) || OriginProjectParser::isOriginProject(fileName)) { #else if (Project::isLabPlotProject(fileName)) { #endif QTimer::singleShot(0, this, [=] () { openProject(fileName); }); } else { newProject(); QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); }); } } else { //There is no file to open. Depending on the settings do nothing, //create a new project or open the last used project. int load = group.readEntry("LoadOnStart", 0); if (load == 1) //create new project newProject(); else if (load == 2) { //create new project with a worksheet newProject(); newWorksheet(); } else if (load == 3) { //open last used project if (!m_recentProjectsAction->urls().isEmpty()) { QDEBUG("TO OPEN m_recentProjectsAction->urls() =" << m_recentProjectsAction->urls().constFirst()); openRecentProject( m_recentProjectsAction->urls().constFirst() ); } } } //show memory info const bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } updateGUIOnProjectChanges(); } void MainWin::initActions() { // ******************** File-menu ******************************* //add some standard actions KStandardAction::openNew(this, SLOT(newProject()),actionCollection()); KStandardAction::open(this, SLOT(openProject()),actionCollection()); m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openRecentProject(QUrl)),actionCollection()); m_closeAction = KStandardAction::close(this, SLOT(closeProject()),actionCollection()); actionCollection()->setDefaultShortcut(m_closeAction, QKeySequence()); //remove the shortcut, QKeySequence::Close will be used for closing sub-windows m_saveAction = KStandardAction::save(this, SLOT(saveProject()),actionCollection()); m_saveAsAction = KStandardAction::saveAs(this, SLOT(saveProjectAs()),actionCollection()); m_printAction = KStandardAction::print(this, SLOT(print()),actionCollection()); m_printPreviewAction = KStandardAction::printPreview(this, SLOT(printPreview()),actionCollection()); KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this); actionCollection()->addAction("new_workbook", m_newWorkbookAction); m_newWorkbookAction->setWhatsThis(i18n("Creates a new workbook for collection spreadsheets, matrices and plots")); connect(m_newWorkbookAction, &QAction::triggered, this, &MainWin::newWorkbook); m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this); m_newDatapickerAction->setWhatsThis(i18n("Creates a data picker for getting data from a picture")); actionCollection()->addAction("new_datapicker", m_newDatapickerAction); connect(m_newDatapickerAction, &QAction::triggered, this, &MainWin::newDatapicker); m_newHypothesisTestAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"), i18n("Hypothesis Test"), this); m_newHypothesisTestAction->setWhatsThis(i18n("Creates windows for hypothesis testing")); actionCollection()->addAction("new_hypothesis_test", m_newHypothesisTestAction); connect(m_newHypothesisTestAction, &QAction::triggered, this, &MainWin::newHypothesisTest); m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this); // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newSpreadsheetAction->setWhatsThis(i18n("Creates a new spreadsheet for data editing")); actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction); connect(m_newSpreadsheetAction, &QAction::triggered, this, &MainWin::newSpreadsheet); m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this); // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal); m_newMatrixAction->setWhatsThis(i18n("Creates a new matrix for data editing")); actionCollection()->addAction("new_matrix", m_newMatrixAction); connect(m_newMatrixAction, &QAction::triggered, this, &MainWin::newMatrix); m_newPivotTableAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Pivot Table"),this); m_newPivotTableAction->setWhatsThis(i18n("Creates a new pivot table")); actionCollection()->addAction("new_pivot_table", m_newPivotTableAction); connect(m_newPivotTableAction, &QAction::triggered, this, &MainWin::newPivotTable); m_newWorksheetAction = new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this); // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X); m_newWorksheetAction->setWhatsThis(i18n("Creates a new worksheet for data plotting")); actionCollection()->addAction("new_worksheet", m_newWorksheetAction); connect(m_newWorksheetAction, &QAction::triggered, this, &MainWin::newWorksheet); m_newNotesAction = new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this); m_newNotesAction->setWhatsThis(i18n("Creates a new note for arbitrary text")); actionCollection()->addAction("new_notes", m_newNotesAction); connect(m_newNotesAction, &QAction::triggered, this, &MainWin::newNotes); // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this); // actionCollection()->addAction("new_script", m_newScriptAction); // connect(m_newScriptAction, &QAction::triggered,SLOT(newScript())); m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this); m_newFolderAction->setWhatsThis(i18n("Creates a new folder to collect sheets and other elements")); actionCollection()->addAction("new_folder", m_newFolderAction); connect(m_newFolderAction, &QAction::triggered, this, &MainWin::newFolder); //"New file datasources" m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this); m_newLiveDataSourceAction->setWhatsThis(i18n("Creates a live data source to read data from a real time device")); actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction); connect(m_newLiveDataSourceAction, &QAction::triggered, this, &MainWin::newLiveDataSourceActionTriggered); //Import/Export m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("From File"), this); actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); m_importFileAction->setWhatsThis(i18n("Import data from a regular file")); actionCollection()->addAction("import_file", m_importFileAction); connect(m_importFileAction, &QAction::triggered, this, [=]() {importFileDialog();}); m_importSqlAction = new QAction(QIcon::fromTheme("document-import-database"), i18n("From SQL Database"), this); m_importSqlAction->setWhatsThis(i18n("Import data from a SQL database")); actionCollection()->addAction("import_sql", m_importSqlAction); connect(m_importSqlAction, &QAction::triggered, this, &MainWin::importSqlDialog); m_importLabPlotAction = new QAction(QIcon::fromTheme("document-import"), i18n("LabPlot Project"), this); m_importLabPlotAction->setWhatsThis(i18n("Import a project from a LabPlot project file (.lml)")); actionCollection()->addAction("import_labplot", m_importLabPlotAction); connect(m_importLabPlotAction, &QAction::triggered, this, &MainWin::importProjectDialog); #ifdef HAVE_LIBORIGIN m_importOpjAction = new QAction(QIcon::fromTheme("document-import-database"), i18n("Origin Project (OPJ)"), this); m_importOpjAction->setWhatsThis(i18n("Import a project from an OriginLab Origin project file (.opj)")); actionCollection()->addAction("import_opj", m_importOpjAction); connect(m_importOpjAction, &QAction::triggered, this, &MainWin::importProjectDialog); #endif m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this); m_exportAction->setWhatsThis(i18n("Export selected element")); actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E); actionCollection()->addAction("export", m_exportAction); connect(m_exportAction, &QAction::triggered, this, &MainWin::exportDialog); m_editFitsFileAction = new QAction(QIcon::fromTheme("editor"), i18n("FITS Metadata Editor"), this); m_editFitsFileAction->setWhatsThis(i18n("Open editor to edit FITS meta data")); actionCollection()->addAction("edit_fits", m_editFitsFileAction); connect(m_editFitsFileAction, &QAction::triggered, this, &MainWin::editFitsFileDialog); // Edit //Undo/Redo-stuff m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this); actionCollection()->addAction("history", m_historyAction); connect(m_historyAction, &QAction::triggered, this, &MainWin::historyDialog); // TODO: more menus // Appearance // Analysis: see WorksheetView.cpp // Drawing // Script //Windows QAction* action = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::Close); action->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow); action = new QAction(i18n("Close &All"), this); action->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows); m_tileWindows = new QAction(i18n("&Tile"), this); m_tileWindows->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindows); connect(m_tileWindows, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows); m_cascadeWindows = new QAction(i18n("&Cascade"), this); m_cascadeWindows->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindows); connect(m_cascadeWindows, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows); action = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::NextChild); action->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow); action = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); actionCollection()->setDefaultShortcut(action, QKeySequence::PreviousChild); action->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", action); connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow); //"Standard actions" KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); //Actions for window visibility auto* windowVisibilityActions = new QActionGroup(this); windowVisibilityActions->setExclusive(true); m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions); m_visibilityFolderAction->setCheckable(true); m_visibilityFolderAction->setData(Project::folderOnly); m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions); m_visibilitySubfolderAction->setCheckable(true); m_visibilitySubfolderAction->setData(Project::folderAndSubfolders); m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions); m_visibilityAllAction->setCheckable(true); m_visibilityAllAction->setData(Project::allMdiWindows); connect(windowVisibilityActions, &QActionGroup::triggered, this, &MainWin::setMdiWindowVisibility); //Actions for hiding/showing the dock widgets auto* docksActions = new QActionGroup(this); docksActions->setExclusive(false); m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions); m_toggleProjectExplorerDockAction->setCheckable(true); m_toggleProjectExplorerDockAction->setChecked(true); actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction); m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions); m_togglePropertiesDockAction->setCheckable(true); m_togglePropertiesDockAction->setChecked(true); actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction); connect(docksActions, &QActionGroup::triggered, this, &MainWin::toggleDockWidget); } void MainWin::initMenus() { //menu in the main toolbar for adding new aspects auto* menu = dynamic_cast(factory()->container("new", this)); menu->setIcon(QIcon::fromTheme("window-new")); //menu in the project explorr and in the toolbar for adding new aspects m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("window-new")); m_newMenu->addAction(m_newFolderAction); m_newMenu->addAction(m_newWorkbookAction); m_newMenu->addAction(m_newSpreadsheetAction); m_newMenu->addAction(m_newMatrixAction); m_newMenu->addAction(m_newWorksheetAction); m_newMenu->addAction(m_newNotesAction); m_newMenu->addAction(m_newDatapickerAction); m_newMenu->addSeparator(); m_newMenu->addAction(m_newLiveDataSourceAction); //import menu m_importMenu = new QMenu(this); m_importMenu->setIcon(QIcon::fromTheme("document-import")); m_importMenu ->addAction(m_importFileAction); m_importMenu ->addAction(m_importSqlAction); m_importMenu->addSeparator(); m_importMenu->addAction(m_importLabPlotAction); #ifdef HAVE_LIBORIGIN m_importMenu ->addAction(m_importOpjAction); #endif #ifdef HAVE_CANTOR_LIBS m_newMenu->addSeparator(); m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet"), this); m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert")); //"Adding Cantor backends to menue and context menu" QStringList m_availableBackend = Cantor::Backend::listAvailableBackends(); if (m_availableBackend.count() > 0) { unplugActionList(QLatin1String("backends_list")); QList newBackendActions; for (Cantor::Backend* backend : Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(),this); action->setData(backend->name()); newBackendActions << action; m_newCantorWorksheetMenu->addAction(action); } connect(m_newCantorWorksheetMenu, &QMenu::triggered, this, &MainWin::newCantorWorksheet); plugActionList(QLatin1String("backends_list"), newBackendActions); } m_newMenu->addMenu(m_newCantorWorksheetMenu); #else delete this->guiFactory()->container("cas_worksheet", this); delete this->guiFactory()->container("new_cas_worksheet", this); delete this->guiFactory()->container("cas_worksheet_toolbar", this); #endif //menu subwindow visibility policy m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this); m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate")); m_visibilityMenu ->addAction(m_visibilityFolderAction); m_visibilityMenu ->addAction(m_visibilitySubfolderAction); m_visibilityMenu ->addAction(m_visibilityAllAction); //menu for editing files m_editMenu = new QMenu(i18n("Edit"), this); m_editMenu->addAction(m_editFitsFileAction); KColorSchemeManager schemeManager; KActionMenu* schemesMenu = schemeManager.createSchemeSelectionMenu(i18n("Color Theme"), this); schemesMenu->menu()->setTitle(i18n("Color Theme")); schemesMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color"))); QMenu* settingsMenu = dynamic_cast(factory()->container("settings", this)); if (settingsMenu) settingsMenu->insertMenu(settingsMenu->actions().constFirst(), schemesMenu->menu()); //set the action for the current color scheme checked KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General"); QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze")); KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); QString schemeName = group.readEntry("ColorScheme", defaultSchemeName); for (auto* action : schemesMenu->menu()->actions()) { if (action->text() == schemeName) { action->setChecked(true); break; } } connect(schemesMenu->menu(), &QMenu::triggered, this, &MainWin::colorSchemeChanged); #ifdef HAVE_CANTOR_LIBS QAction* action = new QAction(QIcon::fromTheme(QLatin1String("cantor")), i18n("Configure CAS"), this); connect(action, &QAction::triggered, this, &MainWin::cantorSettingsDialog); if (settingsMenu) settingsMenu->addAction(action); #endif } void MainWin::colorSchemeChanged(QAction* action) { QString schemeName = KLocalizedString::removeAcceleratorMarker(action->text()); //background of the mdi area is not updated on theme changes, do it here. KColorSchemeManager schemeManager; QModelIndex index = schemeManager.indexForScheme(schemeName); const QPalette& palette = KColorScheme::createApplicationPalette( KSharedConfig::openConfig(index.data(Qt::UserRole).toString()) ); const QBrush& brush = palette.brush(QPalette::Dark); m_mdiArea->setBackground(brush); //save the selected color scheme KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); group.writeEntry("ColorScheme", schemeName); group.sync(); } /*! Asks to save the project if it was modified. \return \c true if the project still needs to be saved ("cancel" clicked), \c false otherwise. */ bool MainWin::warnModified() { if (m_project->hasChanged()) { int want_save = KMessageBox::warningYesNoCancel( this, i18n("The current project %1 has been modified. Do you want to save it?", m_project->name()), i18n("Save Project")); switch (want_save) { case KMessageBox::Yes: return !saveProject(); case KMessageBox::No: break; case KMessageBox::Cancel: return true; } } return false; } /*! * updates the state of actions, menus and toolbars (enabled or disabled) * on project changes (project closes and opens) */ void MainWin::updateGUIOnProjectChanges() { if (m_closing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } //disable all menus if there is no project bool b = (m_project == nullptr); m_saveAction->setEnabled(!b); m_saveAsAction->setEnabled(!b); m_printAction->setEnabled(!b); m_printPreviewAction->setEnabled(!b); m_importFileAction->setEnabled(!b); m_importSqlAction->setEnabled(!b); #ifdef HAVE_LIBORIGIN m_importOpjAction->setEnabled(!b); #endif m_exportAction->setEnabled(!b); m_newWorkbookAction->setEnabled(!b); m_newSpreadsheetAction->setEnabled(!b); m_newMatrixAction->setEnabled(!b); m_newWorksheetAction->setEnabled(!b); m_newDatapickerAction->setEnabled(!b); m_closeAction->setEnabled(!b); m_toggleProjectExplorerDockAction->setEnabled(!b); m_togglePropertiesDockAction->setEnabled(!b); if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif } factory->container("new", this)->setEnabled(!b); factory->container("edit", this)->setEnabled(!b); factory->container("import", this)->setEnabled(!b); if (b) setCaption("LabPlot2"); else setCaption(m_project->name()); // undo/redo actions are disabled in both cases - when the project is closed or opened m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } /* * updates the state of actions, menus and toolbars (enabled or disabled) * depending on the currently active window (worksheet or spreadsheet). */ void MainWin::updateGUI() { if (m_project->isLoading()) return; if (m_closing || m_projectClosing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == nullptr) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif return; } //Handle the Worksheet-object Worksheet* w = this->activeWorksheet(); if (w != nullptr) { //enable worksheet related menus factory->container("worksheet", this)->setEnabled(true); factory->container("analysis", this)->setEnabled(true); //TODO factory->container("drawing", this)->setEnabled(true); //disable spreadsheet and matrix related menus factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); //populate worksheet menu auto* view = qobject_cast(w->view()); auto* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); //populate worksheet-toolbar auto* toolbar = qobject_cast(factory->container("worksheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the toolbar for cartesian plots toolbar = qobject_cast(factory->container("cartesian_plot_toolbar", this)); toolbar->clear(); view->fillCartesianPlotToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //hide the spreadsheet toolbar factory->container("spreadsheet_toolbar", this)->setVisible(false); } else { factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); // factory->container("drawing", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setEnabled(false); factory->container("cartesian_plot_toolbar", this)->setEnabled(false); } //Handle the Spreadsheet-object const auto* spreadsheet = this->activeSpreadsheet(); if (spreadsheet) { //enable spreadsheet related menus factory->container("spreadsheet", this)->setEnabled(true); //populate spreadsheet-menu auto* view = qobject_cast(spreadsheet->view()); auto* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("spreadsheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); } else { factory->container("spreadsheet", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->setEnabled(false); } //Handle the Matrix-object const Matrix* matrix = this->activeMatrix(); if (matrix) { factory->container("matrix", this)->setEnabled(true); //populate matrix-menu auto* view = qobject_cast(matrix->view()); auto* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS CantorWorksheet* cantorworksheet = this->activeCantorWorksheet(); if (cantorworksheet) { // enable Cantor Worksheet related menus factory->container("cas_worksheet", this)->setEnabled(true); auto* view = qobject_cast(cantorworksheet->view()); auto* menu = qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); auto* toolbar = qobject_cast(factory->container("cas_worksheet_toolbar", this)); toolbar->setVisible(true); toolbar->clear(); view->fillToolBar(toolbar); } else { //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->setVisible(false); } #endif const Datapicker* datapicker = this->activeDatapicker(); if (datapicker) { factory->container("datapicker", this)->setEnabled(true); //populate datapicker-menu auto* view = qobject_cast(datapicker->view()); auto* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); //populate spreadsheet-toolbar auto* toolbar = qobject_cast(factory->container("datapicker_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); } else { factory->container("datapicker", this)->setEnabled(false); factory->container("datapicker_toolbar", this)->setVisible(false); } } /*! creates a new empty project. Returns \c true, if a new project was created. */ bool MainWin::newProject() { //close the current project, if available if (!closeProject()) return false; QApplication::processEvents(QEventLoop::AllEvents, 100); if (m_project) delete m_project; if (m_aspectTreeModel) delete m_aspectTreeModel; m_project = new Project(); m_currentAspect = m_project; m_currentFolder = m_project; KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); Project::MdiWindowVisibility vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0)); m_project->setMdiWindowVisibility( vis ); if (vis == Project::folderOnly) m_visibilityFolderAction->setChecked(true); else if (vis == Project::folderAndSubfolders) m_visibilitySubfolderAction->setChecked(true); else m_visibilityAllAction->setChecked(true); m_aspectTreeModel = new AspectTreeModel(m_project, this); connect(m_aspectTreeModel, &AspectTreeModel::statusInfo, [=](const QString& text){ statusBar()->showMessage(text); }); //newProject is called for the first time, there is no project explorer yet //-> initialize the project explorer, the GUI-observer and the dock widgets. if (m_projectExplorer == nullptr) { m_projectExplorerDock = new QDockWidget(this); m_projectExplorerDock->setObjectName("projectexplorer"); m_projectExplorerDock->setWindowTitle(i18nc("@title:window", "Project Explorer")); addDockWidget(Qt::LeftDockWidgetArea, m_projectExplorerDock); m_projectExplorer = new ProjectExplorer(m_projectExplorerDock); m_projectExplorerDock->setWidget(m_projectExplorer); connect(m_projectExplorer, &ProjectExplorer::currentAspectChanged, this, &MainWin::handleCurrentAspectChanged); connect(m_projectExplorerDock, &QDockWidget::visibilityChanged, this, &MainWin::projectExplorerDockVisibilityChanged); //Properties dock m_propertiesDock = new QDockWidget(this); m_propertiesDock->setObjectName("aspect_properties_dock"); m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); addDockWidget(Qt::RightDockWidgetArea, m_propertiesDock); auto* sa = new QScrollArea(m_propertiesDock); stackedWidget = new QStackedWidget(sa); sa->setWidget(stackedWidget); sa->setWidgetResizable(true); m_propertiesDock->setWidget(sa); connect(m_propertiesDock, &QDockWidget::visibilityChanged, this, &MainWin::propertiesDockVisibilityChanged); //GUI-observer; m_guiObserver = new GuiObserver(this); } m_projectExplorer->setModel(m_aspectTreeModel); m_projectExplorer->setProject(m_project); m_projectExplorer->setCurrentAspect(m_project); m_projectExplorerDock->show(); m_propertiesDock->show(); updateGUIOnProjectChanges(); connect(m_project, &Project::aspectAdded, this, &MainWin::handleAspectAdded); connect(m_project, &Project::aspectRemoved, this, &MainWin::handleAspectRemoved); connect(m_project, &Project::aspectAboutToBeRemoved, this, &MainWin::handleAspectAboutToBeRemoved); connect(m_project, SIGNAL(statusInfo(QString)), statusBar(), SLOT(showMessage(QString))); connect(m_project, &Project::changed, this, &MainWin::projectChanged); connect(m_project, &Project::requestProjectContextMenu, this, &MainWin::createContextMenu); connect(m_project, &Project::requestFolderContextMenu, this, &MainWin::createFolderContextMenu); connect(m_project, &Project::mdiWindowVisibilityChanged, this, &MainWin::updateMdiWindowVisibility); connect(m_project, &Project::closeRequested, this, &MainWin::closeProject); m_undoViewEmptyLabel = i18n("%1: created", m_project->name()); setCaption(m_project->name()); return true; } void MainWin::openProject() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); const QString& path = QFileDialog::getOpenFileName(this,i18n("Open Project"), dir, #ifdef HAVE_LIBORIGIN i18n("LabPlot Projects (%1);;Origin Projects (%2)", Project::supportedExtensions(), OriginProjectParser::supportedExtensions()) ); #else i18n("LabPlot Projects (%1)", Project::supportedExtensions()) ); #endif if (path.isEmpty())// "Cancel" was clicked return; this->openProject(path); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } } void MainWin::openProject(const QString& filename) { if (filename == m_currentFileName) { KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open Project")); return; } if (!newProject()) return; WAIT_CURSOR; QElapsedTimer timer; timer.start(); bool rc = false; if (Project::isLabPlotProject(filename)) rc = m_project->load(filename); #ifdef HAVE_LIBORIGIN else if (OriginProjectParser::isOriginProject(filename)) { OriginProjectParser parser; parser.setProjectFileName(filename); parser.importTo(m_project, QStringList()); //TODO: add return code rc = true; } #endif if (!rc) { closeProject(); RESET_CURSOR; return; } m_currentFileName = filename; m_project->setFileName(filename); m_project->undoStack()->clear(); m_undoViewEmptyLabel = i18n("%1: opened", m_project->name()); m_recentProjectsAction->addUrl( QUrl(filename) ); setCaption(m_project->name()); updateGUIOnProjectChanges(); updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI m_saveAction->setEnabled(false); statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) ); if (m_autoSaveActive) m_autoSaveTimer.start(); RESET_CURSOR; } void MainWin::openRecentProject(const QUrl& url) { if (url.isLocalFile()) // fix for Windows this->openProject(url.toLocalFile()); else this->openProject(url.path()); } /*! Closes the current project, if available. Return \c true, if the project was closed. */ bool MainWin::closeProject() { if (m_project == nullptr) return true; //nothing to close if (warnModified()) return false; m_projectClosing = true; delete m_aspectTreeModel; m_aspectTreeModel = nullptr; delete m_project; m_project = nullptr; m_currentFileName.clear(); m_projectClosing = false; //update the UI if we're just closing a project //and not closing(quitting) the application if (!m_closing) { m_projectExplorerDock->hide(); m_propertiesDock->hide(); m_currentAspect = nullptr; m_currentFolder = nullptr; updateGUIOnProjectChanges(); if (m_autoSaveActive) m_autoSaveTimer.stop(); } removeDockWidget(cursorDock); delete cursorDock; cursorDock = nullptr; cursorWidget = nullptr; // is deleted, because it's the cild of cursorDock return true; } bool MainWin::saveProject() { const QString& fileName = m_project->fileName(); if (fileName.isEmpty()) return saveProjectAs(); else return save(fileName); } bool MainWin::saveProjectAs() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir, i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)")); if (path.isEmpty())// "Cancel" was clicked return false; if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false) path.append(QLatin1String(".lml")); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } return save(path); } /*! * auxiliary function that does the actual saving of the project */ bool MainWin::save(const QString& fileName) { WAIT_CURSOR; // use file ending to find out how to compress file QIODevice* file; // if ending is .lml, do gzip compression anyway if (fileName.endsWith(QLatin1String(".lml"))) file = new KCompressionDevice(fileName, KCompressionDevice::GZip); else file = new KFilterDev(fileName); if (file == nullptr) file = new QFile(fileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QPixmap thumbnail = centralWidget()->grab(); QXmlStreamWriter writer(file); m_project->save(thumbnail, &writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); setCaption(m_project->name()); statusBar()->showMessage(i18n("Project saved")); m_saveAction->setEnabled(false); m_recentProjectsAction->addUrl( QUrl(fileName) ); ok = true; //if the project dock is visible, refresh the shown content //(version and modification time might have been changed) if (stackedWidget->currentWidget() == projectDock) projectDock->setProject(m_project); //we have a file name now // -> auto save can be activated now if not happened yet if (m_autoSaveActive && !m_autoSaveTimer.isActive()) m_autoSaveTimer.start(); } else { KMessageBox::error(this, i18n("Sorry. Could not open file for writing.")); ok = false; } delete file; RESET_CURSOR; return ok; } /*! * automatically saves the project in the specified time interval. */ void MainWin::autoSaveProject() { //don't auto save when there are no changes or the file name //was not provided yet (the project was never explicitly saved yet). if ( !m_project->hasChanged() || m_project->fileName().isEmpty()) return; this->saveProject(); } /*! prints the current sheet (worksheet, spreadsheet or matrix) */ void MainWin::print() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printView()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->showMessage(QString()); } void MainWin::printPreview() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printPreview()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->showMessage(QString()); } /**************************************************************************************/ /*! adds a new Folder to the project. */ void MainWin::newFolder() { Folder* folder = new Folder(i18n("Folder")); this->addAspectToProject(folder); } /*! adds a new Workbook to the project. */ void MainWin::newWorkbook() { Workbook* workbook = new Workbook(i18n("Workbook")); this->addAspectToProject(workbook); } /*! adds a new Datapicker to the project. */ void MainWin::newDatapicker() { Datapicker* datapicker = new Datapicker(i18n("Datapicker")); this->addAspectToProject(datapicker); } /*! adds a new Hypothesis Test window to the project. */ void MainWin::newHypothesisTest() { HypothesisTest* hypothesisTest = new HypothesisTest(i18n("HypothesisTest")); this->addAspectToProject(hypothesisTest); } /*! adds a new Spreadsheet to the project. */ void MainWin::newSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(i18n("Spreadsheet")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new spreadsheet to the workbook Workbook* workbook = activeWorkbook(); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(spreadsheet); return; } } this->addAspectToProject(spreadsheet); } /*! adds a new Matrix to the project. */ void MainWin::newMatrix() { Matrix* matrix = new Matrix(i18n("Matrix")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new matrix to the workbook Workbook* workbook = activeWorkbook(); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); const auto* aspect = static_cast(index.internalPointer()); if (!aspect->inherits(AspectType::Folder)) { workbook->addChild(matrix); return; } } this->addAspectToProject(matrix); } /*! adds a new pivot table to the project. */ void MainWin::newPivotTable() { PivotTable* pivotTable = new PivotTable(i18n("PivotTable")); this->addAspectToProject(pivotTable); } /*! adds a new Worksheet to the project. */ void MainWin::newWorksheet() { Worksheet* worksheet = new Worksheet(i18n("Worksheet")); this->addAspectToProject(worksheet); } /*! adds a new Note to the project. */ void MainWin::newNotes() { Note* notes = new Note(i18n("Note")); this->addAspectToProject(notes); } /*! returns a pointer to a Workbook-object, if the currently active Mdi-Subwindow is \a WorkbookView. Otherwise returns \a 0. */ Workbook* MainWin::activeWorkbook() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! returns a pointer to a Datapicker-object, if the currently active Mdi-Subwindow is \a DatapickerView. Otherwise returns \a 0. */ Datapicker* MainWin::activeDatapicker() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView Otherwise returns \c 0. */ Spreadsheet* MainWin::activeSpreadsheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Spreadsheet* spreadsheet = nullptr; const auto* workbook = dynamic_cast(part); if (workbook) { spreadsheet = workbook->currentSpreadsheet(); if (!spreadsheet) { //potentially, the spreadsheet was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a spreadsheet or one of its columns currently selected in the project explorer spreadsheet = dynamic_cast(m_currentAspect); if (!spreadsheet) { if (m_currentAspect->parentAspect()) spreadsheet = dynamic_cast(m_currentAspect->parentAspect()); } } } else spreadsheet = dynamic_cast(part); return spreadsheet; } /*! returns a pointer to a \c Matrix object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c MatrixView Otherwise returns \c 0. */ Matrix* MainWin::activeMatrix() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Matrix* matrix = nullptr; const auto* workbook = dynamic_cast(part); if (workbook) { matrix = workbook->currentMatrix(); if (!matrix) { //potentially, the matrix was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a matrix currently selected in the project explorer matrix = dynamic_cast(m_currentAspect); } } else matrix = dynamic_cast(part); return matrix; } /*! returns a pointer to a Worksheet-object, if the currently active Mdi-Subwindow is \a WorksheetView Otherwise returns \a 0. */ Worksheet* MainWin::activeWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #ifdef HAVE_CANTOR_LIBS /* adds a new Cantor Spreadsheet to the project. */ void MainWin::newCantorWorksheet(QAction* action) { CantorWorksheet* cantorworksheet = new CantorWorksheet(action->data().toString()); this->addAspectToProject(cantorworksheet); } /********************************************************************************/ /*! returns a pointer to a CantorWorksheet-object, if the currently active Mdi-Subwindow is \a CantorWorksheetView Otherwise returns \a 0. */ CantorWorksheet* MainWin::activeCantorWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return nullptr; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #endif /*! called if there were changes in the project. Adds "changed" to the window caption and activates the save-Action. */ void MainWin::projectChanged() { setCaption(i18n("%1 [Changed]", m_project->name())); m_saveAction->setEnabled(true); m_undoAction->setEnabled(true); return; } void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) { if (!win) return; auto* view = qobject_cast(win); if (!view) { updateGUI(); return; } if (view == m_currentSubWindow) { //do nothing, if the current sub-window gets selected again. //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application) //and gets it back (modal window is closed or the user switches back to labplot). return; } else m_currentSubWindow = view; updateGUI(); if (!m_suppressCurrentSubWindowChangedEvent) m_projectExplorer->setCurrentAspect(view->part()); } void MainWin::handleAspectAdded(const AbstractAspect* aspect) { const auto* part = dynamic_cast(aspect); if (part) { // connect(part, &AbstractPart::importFromFileRequested, this, &MainWin::importFileDialog); connect(part, &AbstractPart::importFromFileRequested, this, [=]() {importFileDialog();}); connect(part, &AbstractPart::importFromSQLDatabaseRequested, this, &MainWin::importSqlDialog); //TODO: export, print and print preview should be handled in the views and not in MainWin. connect(part, &AbstractPart::exportRequested, this, &MainWin::exportDialog); connect(part, &AbstractPart::printRequested, this, &MainWin::print); connect(part, &AbstractPart::printPreviewRequested, this, &MainWin::printPreview); connect(part, &AbstractPart::showRequested, this, &MainWin::handleShowSubWindowRequested); } const auto* worksheet = dynamic_cast(aspect); if (worksheet) connect(worksheet, &Worksheet::showCursorDock, this, &MainWin::showCursorDock); } void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) { Q_UNUSED(before); Q_UNUSED(aspect); //no need to react on AbstractSimpleFilter if (!dynamic_cast(aspect)) m_projectExplorer->setCurrentAspect(parent); } void MainWin::handleAspectAboutToBeRemoved(const AbstractAspect *aspect) { const auto* part = qobject_cast(aspect); if (!part) return; const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (!workbook && !datapicker) { PartMdiView* win = part->mdiSubWindow(); if (win) m_mdiArea->removeSubWindow(win); } } /*! called when the current aspect in the tree of the project explorer was changed. Selects the new aspect. */ void MainWin::handleCurrentAspectChanged(AbstractAspect *aspect) { if (!aspect) aspect = m_project; // should never happen, just in case m_suppressCurrentSubWindowChangedEvent = true; if (aspect->folder() != m_currentFolder) { m_currentFolder = aspect->folder(); updateMdiWindowVisibility(); } m_currentAspect = aspect; //activate the corresponding MDI sub window for the current aspect activateSubWindowForAspect(aspect); m_suppressCurrentSubWindowChangedEvent = false; updateGUI(); } void MainWin::activateSubWindowForAspect(const AbstractAspect* aspect) const { const auto* part = dynamic_cast(aspect); if (part) { //for LiveDataSource we currently don't show any view /*if (dynamic_cast(part)) return;*/ PartMdiView* win; //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part const auto* workbook = dynamic_cast(aspect->parentAspect()); auto* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (workbook) win = workbook->mdiSubWindow(); else if (datapicker) win = datapicker->mdiSubWindow(); else win = part->mdiSubWindow(); if (m_mdiArea->subWindowList().indexOf(win) == -1) { if (dynamic_cast(part)) m_mdiArea->addSubWindow(win, Qt::Tool); else m_mdiArea->addSubWindow(win); win->show(); //Qt provides its own "system menu" for every sub-window. The shortcut for the close-action //in this menu collides with our global m_closeAction. //remove the shortcuts in the system menu to avoid this collision. QMenu* menu = win->systemMenu(); if (menu) { for (QAction* action : menu->actions()) action->setShortcut(QKeySequence()); } } m_mdiArea->setActiveSubWindow(win); } else { //activate the mdiView of the parent, if a child was selected const AbstractAspect* parent = aspect->parentAspect(); if (parent) { activateSubWindowForAspect(parent); //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected), //we need to select the corresponding tab in WorkbookView too if (parent->parentAspect()) { auto* workbook = dynamic_cast(parent->parentAspect()); auto* datapicker = dynamic_cast(parent->parentAspect()); if (!datapicker) datapicker = dynamic_cast(parent->parentAspect()->parentAspect()); if (workbook) workbook->childSelected(parent); else if (datapicker) datapicker->childSelected(parent); } } } return; } void MainWin::setMdiWindowVisibility(QAction* action) { m_project->setMdiWindowVisibility((Project::MdiWindowVisibility)(action->data().toInt())); } /*! shows the sub window of a worksheet, matrix or a spreadsheet. Used if the window was closed before and the user asks to show the window again via the context menu in the project explorer. */ void MainWin::handleShowSubWindowRequested() { activateSubWindowForAspect(m_currentAspect); } /*! this is called on a right click on the root folder in the project explorer */ void MainWin::createContextMenu(QMenu* menu) const { QAction* firstAction = nullptr; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_newMenu); //The tabbed view collides with the visibility policy for the subwindows. //Hide the menus for the visibility policy if the tabbed view is used. if (m_mdiArea->viewMode() != QMdiArea::TabbedView) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_visibilityMenu); menu->insertSeparator(firstAction); } } /*! this is called on a right click on a non-root folder in the project explorer */ void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const { Q_UNUSED(folder); //Folder provides it's own context menu. Add a separator before adding additional actions. menu->addSeparator(); this->createContextMenu(menu); } void MainWin::undo() { WAIT_CURSOR; m_project->undoStack()->undo(); if (m_project->undoStack()->index() == 0) { setCaption(m_project->name()); m_saveAction->setEnabled(false); m_undoAction->setEnabled(false); m_project->setChanged(false); } m_redoAction->setEnabled(true); RESET_CURSOR; } void MainWin::redo() { WAIT_CURSOR; m_project->undoStack()->redo(); projectChanged(); if (m_project->undoStack()->index() == m_project->undoStack()->count()) m_redoAction->setEnabled(false); RESET_CURSOR; } /*! Shows/hides mdi sub-windows depending on the current visibility policy. */ void MainWin::updateMdiWindowVisibility() const { QList windows = m_mdiArea->subWindowList(); PartMdiView* part_view; switch (m_project->mdiWindowVisibility()) { case Project::allMdiWindows: for (auto* window : windows) window->show(); break; case Project::folderOnly: for (auto* window : windows) { part_view = qobject_cast(window); Q_ASSERT(part_view); if (part_view->part()->folder() == m_currentFolder) part_view->show(); else part_view->hide(); } break; case Project::folderAndSubfolders: for (auto* window : windows) { part_view = qobject_cast(window); if (part_view->part()->isDescendantOf(m_currentFolder)) part_view->show(); else part_view->hide(); } break; } } void MainWin::toggleDockWidget(QAction* action) { if (action->objectName() == "toggle_project_explorer_dock") { if (m_projectExplorerDock->isVisible()) m_projectExplorerDock->hide(); // toggleHideWidget(m_projectExplorerDock, true); else m_projectExplorerDock->show(); // toggleShowWidget(m_projectExplorerDock, true); } else if (action->objectName() == "toggle_properties_explorer_dock") { if (m_propertiesDock->isVisible()) m_propertiesDock->hide(); // toggleHideWidget(m_propertiesDock, false); else m_propertiesDock->show(); // toggleShowWidget(m_propertiesDock, false); } } /* void MainWin::toggleHideWidget(QWidget* widget, bool hideToLeft) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=] { const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (hideToLeft) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Backward); timeline->start(); connect(timeline, &QTimeLine::finished, [widget] {widget->hide();}); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } void MainWin::toggleShowWidget(QWidget* widget, bool showToRight) { auto* timeline = new QTimeLine(800, this); timeline->setEasingCurve(QEasingCurve::InOutQuad); connect(timeline, &QTimeLine::valueChanged, [=]() { if (widget->isHidden()) { widget->show(); } const qreal value = timeline->currentValue(); const int widgetWidth = widget->width(); const int widgetPosY = widget->pos().y(); int moveX = 0; if (showToRight) { moveX = static_cast(value * widgetWidth) - widgetWidth; } else { const int frameRight = this->frameGeometry().right(); moveX = frameRight - static_cast(value * widgetWidth); } widget->move(moveX, widgetPosY); }); timeline->setDirection(QTimeLine::Forward); timeline->start(); connect(timeline, &QTimeLine::finished, timeline, &QTimeLine::deleteLater); } */ void MainWin::projectExplorerDockVisibilityChanged(bool visible) { m_toggleProjectExplorerDockAction->setChecked(visible); } void MainWin::propertiesDockVisibilityChanged(bool visible) { m_togglePropertiesDockAction->setChecked(visible); } void MainWin::toggleFullScreen() { if (this->windowState() == Qt::WindowFullScreen) this->setWindowState(m_lastWindowState); else { m_lastWindowState = this->windowState(); this->showFullScreen(); } } void MainWin::closeEvent(QCloseEvent* event) { m_closing = true; if (!this->closeProject()) { m_closing = false; event->ignore(); } } void MainWin::dragEnterEvent(QDragEnterEvent* event) { event->accept(); } void MainWin::dropEvent(QDropEvent* event) { if (event->mimeData() && !event->mimeData()->urls().isEmpty()) { QUrl url = event->mimeData()->urls().at(0); const QString& f = url.toLocalFile(); #ifdef HAVE_LIBORIGIN if (Project::isLabPlotProject(f) || OriginProjectParser::isOriginProject(f)) #else if (Project::isLabPlotProject(f)) #endif openProject(f); else { if (!m_project) newProject(); importFileDialog(f); } event->accept(); } else event->ignore(); } void MainWin::handleSettingsChanges() { const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0)); if (m_mdiArea->viewMode() != viewMode) { m_mdiArea->setViewMode(viewMode); if (viewMode == QMdiArea::SubWindowView) this->updateMdiWindowVisibility(); } if (m_mdiArea->viewMode() == QMdiArea::TabbedView) { m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0)); if (m_mdiArea->tabPosition() != tabPosition) m_mdiArea->setTabPosition(tabPosition); } else { m_tileWindows->setVisible(true); m_cascadeWindows->setVisible(true); } //autosave bool autoSave = group.readEntry("AutoSave", 0); if (m_autoSaveActive != autoSave) { m_autoSaveActive = autoSave; if (autoSave) m_autoSaveTimer.start(); else m_autoSaveTimer.stop(); } int interval = group.readEntry("AutoSaveInterval", 1); interval *= 60*1000; if (interval != m_autoSaveTimer.interval()) m_autoSaveTimer.setInterval(interval); //show memory info bool showMemoryInfo = group.readEntry(QLatin1String("ShowMemoryInfo"), true); if (m_showMemoryInfo != showMemoryInfo) { m_showMemoryInfo = showMemoryInfo; if (showMemoryInfo) { m_memoryInfoWidget = new MemoryWidget(statusBar()); statusBar()->addPermanentWidget(m_memoryInfoWidget); } else { if (m_memoryInfoWidget) { statusBar()->removeWidget(m_memoryInfoWidget); delete m_memoryInfoWidget; m_memoryInfoWidget = nullptr; } } } } /***************************************************************************************/ /************************************** dialogs ***************************************/ /***************************************************************************************/ /*! shows the dialog with the Undo-history. */ void MainWin::historyDialog() { if (!m_project->undoStack()) return; auto* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel); int index = m_project->undoStack()->index(); if (dialog->exec() != QDialog::Accepted) { if (m_project->undoStack()->count() != 0) m_project->undoStack()->setIndex(index); } //disable undo/redo-actions if the history was cleared //(in both cases, when accepted or rejected in the dialog) if (m_project->undoStack()->count() == 0) { m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } /*! Opens the dialog to import data to the selected workbook, spreadsheet or matrix */ void MainWin::importFileDialog(const QString& fileName) { DEBUG("MainWin::importFileDialog()"); auto* dlg = new ImportFileDialog(this, false, fileName); // select existing container if (m_currentAspect->inherits(AspectType::Spreadsheet) || m_currentAspect->inherits(AspectType::Matrix) || m_currentAspect->inherits(AspectType::Workbook)) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits(AspectType::Column) && m_currentAspect->parentAspect()->inherits(AspectType::Spreadsheet)) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importFileDialog() DONE"); } void MainWin::importSqlDialog() { DEBUG("MainWin::importSqlDialog()"); auto* dlg = new ImportSQLDatabaseDialog(this); // select existing container if (m_currentAspect->inherits(AspectType::Spreadsheet) || m_currentAspect->inherits(AspectType::Matrix) || m_currentAspect->inherits(AspectType::Workbook)) dlg->setCurrentIndex(m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits(AspectType::Column) && m_currentAspect->parentAspect()->inherits(AspectType::Spreadsheet)) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importSqlDialog() DONE"); } void MainWin::importProjectDialog() { DEBUG("MainWin::importProjectDialog()"); ImportProjectDialog::ProjectType type; if (QObject::sender() == m_importOpjAction) type = ImportProjectDialog::ProjectOrigin; else type = ImportProjectDialog::ProjectLabPlot; auto* dlg = new ImportProjectDialog(this, type); // set current folder dlg->setCurrentFolder(m_currentFolder); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importProjectDialog() DONE"); } /*! opens the dialog for the export of the currently active worksheet, spreadsheet or matrix. */ void MainWin::exportDialog() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); if (part->exportView()) statusBar()->showMessage(i18n("%1 exported", part->name())); } void MainWin::editFitsFileDialog() { auto* editDialog = new FITSHeaderEditDialog(this); if (editDialog->exec() == QDialog::Accepted) { if (editDialog->saved()) statusBar()->showMessage(i18n("FITS files saved")); } } /*! adds a new file data source to the current project. */ void MainWin::newLiveDataSourceActionTriggered() { ImportFileDialog* dlg = new ImportFileDialog(this, true); if (dlg->exec() == QDialog::Accepted) { if (static_cast(dlg->sourceType()) == LiveDataSource::MQTT) { #ifdef HAVE_MQTT MQTTClient* mqttClient = new MQTTClient(i18n("MQTT Client%1", 1)); dlg->importToMQTT(mqttClient); mqttClient->setName(mqttClient->clientHostName()); QVector existingClients = m_project->children(AbstractAspect::Recursive); //doesn't make sense to have more MQTTClients connected to the same broker bool found = false; for (const auto* client : existingClients) { if (client->clientHostName() == mqttClient->clientHostName() && client->clientPort() == mqttClient->clientPort()) { found = true; break; } } if (!found) addAspectToProject(mqttClient); else { delete mqttClient; QMessageBox::warning(this, "Warning", "There already is a MQTTClient with this host!"); } #endif } else { LiveDataSource* dataSource = new LiveDataSource(i18n("Live data source%1", 1), false); dlg->importToLiveDataSource(dataSource, statusBar()); addAspectToProject(dataSource); } } delete dlg; } void MainWin::addAspectToProject(AbstractAspect* aspect) { const QModelIndex& index = m_projectExplorer->currentIndex(); if (index.isValid()) { auto* parent = static_cast(index.internalPointer()); #ifdef HAVE_MQTT //doesn't make sense to add a new MQTTClient to an existing MQTTClient or to any of its successors QString className = parent->metaObject()->className(); MQTTClient* clientAncestor = parent->ancestor(); if (className == "MQTTClient") parent = parent->parentAspect(); else if (clientAncestor != nullptr) parent = clientAncestor->parentAspect(); #endif parent->folder()->addChild(aspect); } else m_project->addChild(aspect); } void MainWin::settingsDialog() { auto* dlg = new SettingsDialog(this); connect (dlg, &SettingsDialog::settingsChanged, this, &MainWin::handleSettingsChanges); dlg->exec(); } #ifdef HAVE_CANTOR_LIBS void MainWin::cantorSettingsDialog() { static KCoreConfigSkeleton* emptyConfig = new KCoreConfigSkeleton(); KConfigDialog *cantorDialog = new KConfigDialog(this, QLatin1String("Cantor Settings"), emptyConfig); for (auto* backend : Cantor::Backend::availableBackends()) if (backend->config()) //It has something to configure, so add it to the dialog cantorDialog->addPage(backend->settingsWidget(cantorDialog), backend->config(), backend->name(), backend->icon()); cantorDialog->show(); } #endif diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp index 380789cbc..62bcf63d1 100644 --- a/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp @@ -1,875 +1,875 @@ /*************************************************************************** File : HypothesisTestDock.cpp Project : LabPlot Description : widget for hypothesis test properties -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "HypothesisTestDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/AbstractAspect.h" #include "backend/core/Project.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/datasources/DatabaseManagerDialog.h" #include "kdefrontend/datasources/DatabaseManagerWidget.h" #include "kdefrontend/TemplateHandler.h" #include #include #include #include #include #include #include #include /*! \class HypothesisTestDock \brief Provides a dock (widget) for hypothesis testing: \ingroup kdefrontend */ //TODO: To add tooltips in docks for non obvious widgets. //TODO: Add functionality for database along with spreadsheet. HypothesisTestDock::HypothesisTestDock(QWidget* parent) : QWidget(parent) { //QDEBUG("in hypothesis test constructor "); ui.setupUi(this); ui.cbDataSourceType->addItem(i18n("Spreadsheet")); ui.cbDataSourceType->addItem(i18n("Database")); cbSpreadsheet = new TreeViewComboBox; ui.gridLayout->addWidget(cbSpreadsheet, 5, 4, 1, 3); ui.bDatabaseManager->setIcon(QIcon::fromTheme("network-server-database")); ui.bDatabaseManager->setToolTip(i18n("Manage connections")); m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + "sql_connections"; // adding item to tests and testtype combo box; ui.cbTest->addItem( i18n("T Test"), HypothesisTest::Test::Type::TTest); ui.cbTest->addItem( i18n("Z Test"), HypothesisTest::Test::Type::ZTest); ui.cbTest->addItem( i18n("ANOVA"), HypothesisTest::Test::Type::Anova); ui.lPopulationSigma->setText( UTF8_QSTRING("σ")); // making all test blocks invisible at starting. ui.pbLeveneTest->hide(); ui.lCategorical->hide(); ui.chbCategorical->hide(); ui.lCol1->hide(); ui.cbCol1->hide(); ui.lCol2->hide(); ui.cbCol2->hide(); ui.lCol3->hide(); ui.cbCol3->hide(); ui.lEqualVariance->hide(); ui.chbEqualVariance->hide(); ui.chbEqualVariance->setChecked(true); ui.lPopulationSigma->hide(); ui.lPopulationSigma->setToolTip( i18n("Sigma of Population

" "Hint: Z-Test if preffered over T-Test if this is known")); ui.chbPopulationSigma->hide(); ui.lePopulationSigma->hide(); ui.pbPerformTest->setEnabled(false); ui.rbH1OneTail2->hide(); ui.rbH1OneTail1->hide(); ui.rbH1TwoTail->hide(); ui.rbH0OneTail1->hide(); ui.rbH0OneTail2->hide(); ui.rbH0TwoTail->hide(); ui.lH0->hide(); ui.lH1->hide(); QString mu = UTF8_QSTRING("μ"); QString mu0 = UTF8_QSTRING("μₒ"); // radio button for null and alternate hypothesis // for alternative hypothesis (h1) // one_tail_1 is mu > mu0; one_tail_2 is mu < mu0; two_tail = mu != mu0; ui.rbH1OneTail1->setText( i18n("%1 %2 %3", mu, UTF8_QSTRING(">"), mu0)); ui.rbH1OneTail2->setText( i18n("%1 %2 %3", mu, UTF8_QSTRING("<"), mu0)); ui.rbH1TwoTail->setText( i18n("%1 %2 %3", mu, UTF8_QSTRING("≠"), mu0)); ui.rbH0OneTail1->setText( i18n("%1 %2 %3",mu, UTF8_QSTRING("≤"), mu0)); ui.rbH0OneTail2->setText( i18n("%1 %2 %3", mu, UTF8_QSTRING("≥"), mu0)); ui.rbH0TwoTail->setText( i18n("%1 %2 %3", mu, UTF8_QSTRING("="), mu0)); ui.rbH0TwoTail->setEnabled(false); ui.rbH0OneTail1->setEnabled(false); ui.rbH0OneTail2->setEnabled(false); // setting muo and alpha buttons ui.lMuo->setText( i18n("%1", mu0)); ui.lAlpha->setText( i18n("%1", UTF8_QSTRING("α"))); ui.leMuo->setText( i18n("%1", m_populationMean)); ui.leAlpha->setText( i18n("%1", m_significanceLevel)); ui.lMuo->hide(); ui.lMuo->setToolTip( i18n("Population Mean")); ui.lAlpha->hide(); ui.lAlpha->setToolTip( i18n("Significance Level")); ui.leMuo->hide(); ui.leAlpha->hide(); ui.pbPerformTest->setIcon(QIcon::fromTheme("run-build")); ui.leMuo->setText( i18n("%1", m_populationMean)); ui.leAlpha->setText( i18n("%1", m_significanceLevel)); // readConnections(); // auto* style = ui.bAddRow->style(); // ui.bAddRow->setIcon(style->standardIcon(QStyle::SP_ArrowRight)); // ui.bAddRow->setToolTip(i18n("Add the selected field to rows")); // ui.bRemoveRow->setIcon(style->standardIcon(QStyle::SP_ArrowLeft)); // ui.bRemoveRow->setToolTip(i18n("Remove the selected field from rows")); // ui.bAddColumn->setIcon(style->standardIcon(QStyle::SP_ArrowRight)); // ui.bAddColumn->setToolTip(i18n("Add the selected field to columns")); // ui.bRemoveColumn->setIcon(style->standardIcon(QStyle::SP_ArrowLeft)); // ui.bRemoveColumn->setToolTip(i18n("Remove the selected field from columns")); // //add/remove buttons only enabled if something was selected // ui.bAddRow->setEnabled(false); // ui.bRemoveRow->setEnabled(false); // ui.bAddColumn->setEnabled(false); // ui.bRemoveColumn->setEnabled(false); // connect(ui.leName, &QLineEdit::textChanged, this, &HypothesisTestDock::nameChanged); // connect(ui.leComment, &QLineEdit::textChanged, this, &HypothesisTestDock::commentChanged); connect(ui.cbDataSourceType, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::dataSourceTypeChanged); connect(cbSpreadsheet, &TreeViewComboBox::currentModelIndexChanged, this, &HypothesisTestDock::spreadsheetChanged); // connect(ui.cbConnection, static_cast(&QComboBox::currentIndexChanged), // this, &HypothesisTestDock::connectionChanged); // connect(ui.cbTable, static_cast(&QComboBox::currentIndexChanged), // this, &HypothesisTestDock::tableChanged); // connect(ui.bDatabaseManager, &QPushButton::clicked, this, &HypothesisTestDock::showDatabaseManager); // connect(ui.bAddRow, &QPushButton::clicked, this, &HypothesisTestDock::addRow); // connect(ui.bRemoveRow, &QPushButton::clicked, this,&HypothesisTestDock::removeRow); // connect(ui.bAddColumn, &QPushButton::clicked, this, &HypothesisTestDock::addColumn); // connect(ui.bRemoveColumn, &QPushButton::clicked, this,&HypothesisTestDock::removeColumn); // connect(ui.cbCol1, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::doTTest); // connect(ui.cbCol2, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::doTTest); // connect(ui.lwFields, &QListWidget::itemSelectionChanged, this, [=]() { // bool enabled = !ui.lwFields->selectedItems().isEmpty(); // ui.bAddRow->setEnabled(enabled); // ui.bAddColumn->setEnabled(enabled); // }); // connect(ui.lwRows, &QListWidget::doubleClicked, this,&HypothesisTestDock::removeRow); // connect(ui.lwRows, &QListWidget::itemSelectionChanged, this, [=]() { // ui.bRemoveRow->setEnabled(!ui.lwRows->selectedItems().isEmpty()); // }); // connect(ui.lwColumns, &QListWidget::doubleClicked, this,&HypothesisTestDock::removeColumn); // connect(ui.lwColumns, &QListWidget::itemSelectionChanged, this, [=]() { // ui.bRemoveColumn->setEnabled(!ui.lwColumns->selectedItems().isEmpty()); // }); connect(ui.cbTest, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showTestType); connect(ui.cbTestType, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showHypothesisTest); // connect(ui.cbTest, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showHypothesisTest); // connect(ui.cbTestType, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showHypothesisTest); connect(ui.pbPerformTest, &QPushButton::clicked, this, &HypothesisTestDock::doHypothesisTest); connect(ui.pbLeveneTest, &QPushButton::clicked, this, &HypothesisTestDock::performLeveneTest); //connecting null hypothesis and alternate hypothesis radio button connect(ui.rbH1OneTail1, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1OneTail1Toggled); connect(ui.rbH1OneTail2, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1OneTail2Toggled); connect(ui.rbH1TwoTail, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1TwoTailToggled); connect(ui.cbCol1, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::col1IndexChanged); connect(ui.chbCategorical, &QCheckBox::stateChanged, this, &HypothesisTestDock::changeCbCol2Label); connect(ui.chbPopulationSigma, &QCheckBox::stateChanged, this, &HypothesisTestDock::chbPopulationSigmaStateChanged); ui.cbTest->setCurrentIndex(0); emit ui.cbTest->currentIndexChanged(0); ui.cbTestType->setCurrentIndex(0); emit ui.cbTestType->currentIndexChanged(0); } void HypothesisTestDock::setHypothesisTest(HypothesisTest* HypothesisTest) { //QDEBUG("in set hypothesis test"); m_initializing = true; m_hypothesisTest = HypothesisTest; m_aspectTreeModel = new AspectTreeModel(m_hypothesisTest->project()); QList list{AspectType::Folder, AspectType::Workbook, AspectType::Spreadsheet, AspectType::LiveDataSource}; cbSpreadsheet->setTopLevelClasses(list); list = {AspectType::Spreadsheet, AspectType::LiveDataSource}; m_aspectTreeModel->setSelectableAspects(list); cbSpreadsheet->setModel(m_aspectTreeModel); //show the properties ui.leName->setText(m_hypothesisTest->name()); ui.leComment->setText(m_hypothesisTest->comment()); ui.cbDataSourceType->setCurrentIndex(m_hypothesisTest->dataSourceType()); if (m_hypothesisTest->dataSourceType() == HypothesisTest::DataSourceType::DataSourceSpreadsheet) setModelIndexFromAspect(cbSpreadsheet, m_hypothesisTest->dataSourceSpreadsheet()); // else // ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(m_hypothesisTest->dataSourceConnection())); setColumnsComboBoxModel(m_hypothesisTest->dataSourceSpreadsheet()); this->dataSourceTypeChanged(ui.cbDataSourceType->currentIndex()); //setting rows and columns in combo box; //undo functions // connect(m_hypothesisTest, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(hypothesisTestDescriptionChanged(const AbstractAspect*))); m_initializing = false; } void HypothesisTestDock::showTestType() { //QDEBUG("in show test type"); m_test.type = HypothesisTest::Test::Type(ui.cbTest->currentData().toInt()); ui.cbTestType->clear(); if (m_test.type & (HypothesisTest::Test::Type::TTest | HypothesisTest::Test::Type::ZTest)) { ui.cbTestType->addItem( i18n("Two Sample Independent"), HypothesisTest::Test::SubType::TwoSampleIndependent); ui.cbTestType->addItem( i18n("Two Sample Paired"), HypothesisTest::Test::SubType::TwoSamplePaired); ui.cbTestType->addItem( i18n("One Sample"), HypothesisTest::Test::SubType::OneSample); } else if (m_test.type & HypothesisTest::Test::Type::Anova) { ui.cbTestType->addItem( i18n("One Way"), HypothesisTest::Test::SubType::OneWay); ui.cbTestType->addItem( i18n("Two Way"), HypothesisTest::Test::SubType::TwoWay); } } void HypothesisTestDock::showHypothesisTest() { //QDEBUG("in showHypothesisTest"); if (ui.cbTestType->count() == 0) return; m_test.subtype = HypothesisTest::Test::SubType(ui.cbTestType->currentData().toInt()); ui.lCol1->show(); ui.cbCol1->show(); ui.lCol2->setVisible(bool(m_test.subtype & (~HypothesisTest::Test::SubType::OneSample))); ui.cbCol2->setVisible(bool(m_test.subtype & (~HypothesisTest::Test::SubType::OneSample))); ui.lCol3->setVisible(bool(m_test.type & (HypothesisTest::Test::Anova) & setAllBits(m_test.subtype & HypothesisTest::Test::SubType::TwoWay))); ui.cbCol3->setVisible(bool(m_test.type & (HypothesisTest::Test::Anova) & setAllBits(m_test.subtype & HypothesisTest::Test::SubType::TwoWay))); ui.lEqualVariance->setVisible(bool( (m_test.type & HypothesisTest::Test::Type::TTest) & (m_test.subtype & HypothesisTest::Test::SubType::TwoSampleIndependent))); ui.chbEqualVariance->setVisible(bool( (m_test.type & HypothesisTest::Test::Type::TTest) & (m_test.subtype & HypothesisTest::Test::SubType::TwoSampleIndependent))); ui.lCategorical->setVisible(bool((m_test.type & HypothesisTest::Test::Type::TTest) & (m_test.subtype & HypothesisTest::Test::SubType::TwoSampleIndependent))); ui.chbCategorical->setVisible(bool((m_test.type & HypothesisTest::Test::Type::TTest) & (m_test.subtype & HypothesisTest::Test::SubType::TwoSampleIndependent))); ui.chbEqualVariance->setChecked(true); ui.lPopulationSigma->setVisible(bool((m_test.type & (HypothesisTest::Test::Type::TTest | HypothesisTest::Test::Type::ZTest)) & ~(setAllBits(m_test.subtype & HypothesisTest::Test::SubType::OneSample)))); ui.chbPopulationSigma->setVisible(bool((m_test.type & (HypothesisTest::Test::Type::TTest | HypothesisTest::Test::Type::ZTest)) & ~(setAllBits(m_test.subtype & HypothesisTest::Test::SubType::OneSample)))); ui.chbPopulationSigma->setChecked(false); ui.pbLeveneTest->setVisible(bool((m_test.type & HypothesisTest::Test::Type::Anova & setAllBits(m_test.subtype & HypothesisTest::Test::SubType::OneWay)) | (HypothesisTest::Test::Type::TTest & setAllBits(m_test.subtype & HypothesisTest::Test::SubType::TwoSampleIndependent)))); ui.lH1->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH1OneTail1->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH1OneTail2->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH1TwoTail->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.lH0->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH0OneTail1->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH0OneTail2->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH0TwoTail->setVisible(bool(m_test.type & ~HypothesisTest::Test::Type::Anova)); ui.rbH1TwoTail->setChecked(true); ui.lMuo->setVisible(bool(m_test.subtype & HypothesisTest::Test::SubType::OneSample)); ui.leMuo->setVisible(bool(ui.lMuo->isVisible())); ui.lAlpha->show(); ui.leAlpha->show(); setColumnsComboBoxView(); ui.pbPerformTest->setEnabled(nonEmptySelectedColumns()); ui.pbLeveneTest->setEnabled(nonEmptySelectedColumns()); } void HypothesisTestDock::doHypothesisTest() { //QDEBUG("in doHypothesisTest"); m_hypothesisTest->setPopulationMean(ui.leMuo->text()); m_hypothesisTest->setSignificanceLevel(ui.leAlpha->text()); QVector cols; if (ui.cbCol1->count() == 0) return; cols << reinterpret_cast(ui.cbCol1->currentData().toLongLong()); if (m_test.subtype & HypothesisTest::Test::SubType::TwoWay) cols << reinterpret_cast(ui.cbCol3->currentData().toLongLong()); if (m_test.subtype & (~HypothesisTest::Test::SubType::OneSample)) if (ui.cbCol2->count() > 0) cols << reinterpret_cast(ui.cbCol2->currentData().toLongLong()); m_hypothesisTest->setColumns(cols); m_hypothesisTest->performTest(m_test, ui.chbCategorical->isChecked(), ui.chbEqualVariance->isChecked()); } void HypothesisTestDock::performLeveneTest() { QVector cols; if (ui.cbCol1->count() == 0 || ui.cbCol2->count() == 0) return; cols << reinterpret_cast(ui.cbCol1->currentData().toLongLong()); cols << reinterpret_cast(ui.cbCol2->currentData().toLongLong()); - m_hypothesisTest->setColumns(cols); + m_hypothesisTest->setColumns(cols); m_hypothesisTest->setSignificanceLevel(ui.leAlpha->text()); m_hypothesisTest->performLeveneTest(ui.chbCategorical->isChecked()); } void HypothesisTestDock::setModelIndexFromAspect(TreeViewComboBox* cb, const AbstractAspect* aspect) { if (aspect) cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(aspect)); else cb->setCurrentModelIndex(QModelIndex()); } ///*! // shows the database manager where the connections are created and edited. // The selected connection is selected in the connection combo box in this widget. //**/ //void HypothesisTestDock::showDatabaseManager() { // DatabaseManagerDialog* dlg = new DatabaseManagerDialog(this, ui.cbConnection->currentText()); // if (dlg->exec() == QDialog::Accepted) { // //re-read the available connections to be in sync with the changes in DatabaseManager // m_initializing = true; // ui.cbConnection->clear(); // readConnections(); // //select the connection the user has selected in DatabaseManager // const QString& conn = dlg->connection(); // ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(conn)); // m_initializing = false; // connectionChanged(); // } // delete dlg; //} ///*! // loads all available saved connections //*/ //void HypothesisTestDock::readConnections() { // DEBUG("ImportSQLDatabaseWidget: reading available connections"); // KConfig config(m_configPath, KConfig::SimpleConfig); // for (const auto& name : config.groupList()) // ui.cbConnection->addItem(name); //} ///*! // * adds the selected field to the rows // */ //void HypothesisTestDock::addRow() { // QString field = ui.lwFields->currentItem()->text(); // ui.lwRows->addItem(field); // ui.lwFields->takeItem(ui.lwFields->currentRow()); // m_hypothesisTest->addToRows(field); //} ///*! // * removes the selected field from the rows // */ //void HypothesisTestDock::removeRow() { // const QString& field = ui.lwRows->currentItem()->text(); // ui.lwRows->takeItem(ui.lwRows->currentRow()); // m_hypothesisTest->removeFromRows(field); // updateFields(); //} ///*! // * adds the selected field to the columns // */ //void HypothesisTestDock::addColumn() { // QString field = ui.lwFields->currentItem()->text(); // ui.lwColumns->addItem(field); // ui.lwFields->takeItem(ui.lwFields->currentRow()); // m_hypothesisTest->addToColumns(field); //} ///*! // * removes the selected field from the columns // */ //void HypothesisTestDock::removeColumn() { // const QString& field = ui.lwColumns->currentItem()->text(); // ui.lwColumns->takeItem(ui.lwColumns->currentRow()); // m_hypothesisTest->removeFromColumns(field); // updateFields(); //} ///*! // * re-populates the content of the "Fields" list widget by adding the non-selected fields only. // * called when a selected field is removed from rows or columns. // */ //void HypothesisTestDock::updateFields() { // ui.lwFields->clear(); // for (auto dimension : m_hypothesisTest->dimensions()) // if (!fieldSelected(dimension)) // ui.lwFields->addItem(new QListWidgetItem(QIcon::fromTheme("draw-text"), dimension)); // for (auto measure : m_hypothesisTest->measures()) // if (!fieldSelected(measure)) // ui.lwFields->addItem(new QListWidgetItem(measure)); //} ///*! // * return \c true if the field name \c field was selected among rows or columns, // * return \c false otherwise. // * */ //bool HypothesisTestDock::fieldSelected(const QString& field) { // for (int i = 0; icount(); ++i) // if (ui.lwRows->item(i)->text() == field) // return true; // for (int i = 0; icount(); ++i) // if (ui.lwColumns->item(i)->text() == field) // return true; // return false; //} ////************************************************************* ////****** SLOTs for changes triggered in HypothesisTestDock ******* ////************************************************************* //void HypothesisTestDock::nameChanged() { // if (m_initializing) // return; // m_hypothesisTest->setName(ui.leName->text()); //} //void HypothesisTestDock::commentChanged() { // if (m_initializing) // return; // m_hypothesisTest->setComment(ui.leComment->text()); //} void HypothesisTestDock::dataSourceTypeChanged(int index) { //QDEBUG("in dataSourceTypeChanged"); HypothesisTest::DataSourceType type = static_cast(index); bool showDatabase = (type == HypothesisTest::DataSourceType::DataSourceDatabase); ui.lSpreadsheet->setVisible(!showDatabase); cbSpreadsheet->setVisible(!showDatabase); ui.lConnection->setVisible(showDatabase); ui.cbConnection->setVisible(showDatabase); ui.bDatabaseManager->setVisible(showDatabase); ui.lTable->setVisible(showDatabase); ui.cbTable->setVisible(showDatabase); if (m_initializing) return; m_hypothesisTest->setComment(ui.leComment->text()); } void HypothesisTestDock::spreadsheetChanged(const QModelIndex& index) { //QDEBUG("in spreadsheetChanged"); auto* aspect = static_cast(index.internalPointer()); Spreadsheet* spreadsheet = dynamic_cast(aspect); setColumnsComboBoxModel(spreadsheet); m_hypothesisTest->setDataSourceSpreadsheet(spreadsheet); } void HypothesisTestDock::changeCbCol2Label() { //QDEBUG("in changeCbCol2Label"); if ( (m_test.type & ~HypothesisTest::Test::Type::Anova) & (m_test.subtype & ~HypothesisTest::Test::SubType::TwoSampleIndependent)) { ui.lCol2->setText( i18n("Independent Var. 2")); return; } if (ui.cbCol1->count() == 0) return; QString selected_text = ui.cbCol1->currentText(); Column* col1 = m_hypothesisTest->dataSourceSpreadsheet()->column(selected_text); if (!ui.chbCategorical->isChecked() && (col1->columnMode() == AbstractColumn::Integer || col1->columnMode() == AbstractColumn::Numeric)) { ui.lCol2->setText( i18n("Independent Var. 2")); ui.chbCategorical->setChecked(false); ui.chbCategorical->setEnabled(true); } else { ui.lCol2->setText( i18n("Dependent Var. 1")); if (!ui.chbCategorical->isChecked()) ui.chbCategorical->setEnabled(false); else ui.chbCategorical->setEnabled(true); ui.chbCategorical->setChecked(true); } } void HypothesisTestDock::chbPopulationSigmaStateChanged() { if (ui.chbPopulationSigma->isVisible() && ui.chbPopulationSigma->isChecked()) ui.lePopulationSigma->show(); else ui.lePopulationSigma->hide(); } void HypothesisTestDock::col1IndexChanged(int index) { if (index < 0) return; changeCbCol2Label(); } //void HypothesisTestDock::connectionChanged() { // if (ui.cbConnection->currentIndex() == -1) { // ui.lTable->hide(); // ui.cbTable->hide(); // return; // } // //clear the previously shown tables // ui.cbTable->clear(); // ui.lTable->show(); // ui.cbTable->show(); // const QString& connection = ui.cbConnection->currentText(); // //connection name was changed, determine the current connections settings // KConfig config(m_configPath, KConfig::SimpleConfig); // KConfigGroup group = config.group(connection); // //close and remove the previos connection, if available // if (m_db.isOpen()) { // m_db.close(); // QSqlDatabase::removeDatabase(m_db.driverName()); // } // //open the selected connection // //QDEBUG("HypothesisTestDock: connecting to " + connection); // const QString& driver = group.readEntry("Driver"); // m_db = QSqlDatabase::addDatabase(driver); // const QString& dbName = group.readEntry("DatabaseName"); // if (DatabaseManagerWidget::isFileDB(driver)) { // if (!QFile::exists(dbName)) { // KMessageBox::error(this, i18n("Couldn't find the database file '%1'. Please check the connection settings.", dbName), // appendRow i18n("Connection Failed")); // return; // } else // m_db.setDatabaseName(dbName); // } else if (DatabaseManagerWidget::isODBC(driver)) { // if (group.readEntry("CustomConnectionEnabled", false)) // m_db.setDatabaseName(group.readEntry("CustomConnectionString")); // else // m_db.setDatabaseName(dbName); // } else { // m_db.setDatabaseName(dbName); // m_db.setHostName( group.readEntry("HostName") ); // m_db.setPort( group.readEntry("Port", 0) ); // m_db.setUserName( group.readEntry("UserName") ); // m_db.setPassword( group.readEntry("Password") ); // } // WAIT_CURSOR; // if (!m_db.open()) { // RESET_CURSOR; // KMessageBox::error(this, i18n("Failed to connect to the database '%1'. Please check the connection settings.", ui.cbConnection->currentText()) + // QLatin1String("\n\n") + m_db.lastError().databaseText(), // i18n("Connection Failed")); // return; // } // //show all available database tables // if (m_db.tables().size()) { // for (auto table : m_db.tables()) // ui.cbTable->addItem(QIcon::fromTheme("view-form-table"), table); // ui.cbTable->setCurrentIndex(0); // } // RESET_CURSOR; // if (m_initializing) // return; //// m_hypothesisTest->setDataSourceConnection(connection); //} //void HypothesisTestDock::tableChanged() { // const QString& table = ui.cbTable->currentText(); // //show all attributes of the selected table //// for (const auto* col : spreadsheet->children()) { //// QListWidgetItem* item = new QListWidgetItem(col->icon(), col->name()); //// ui.lwFields->addItem(item); //// } // if (m_initializing) // return; //// m_hypothesisTest->setDataSourceTable(table); //} ////************************************************************* ////******** SLOTs for changes triggered in Spreadsheet ********* ////************************************************************* void HypothesisTestDock::hypothesisTestDescriptionChanged(const AbstractAspect* aspect) { //QDEBUG("in hypothesisTestDescriptionChanged"); if (m_hypothesisTest != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) ui.leName->setText(aspect->name()); else if (aspect->comment() != ui.leComment->text()) ui.leComment->setText(aspect->comment()); m_initializing = false; } ////************************************************************* ////******************** SETTINGS ******************************* ////************************************************************* //void HypothesisTestDock::load() { //} //void HypothesisTestDock::loadConfigFromTemplate(KConfig& config) { // Q_UNUSED(config); //} ///*! // loads saved matrix properties from \c config. // */ //void HypothesisTestDock::loadConfig(KConfig& config) { // Q_UNUSED(config); //} ///*! // saves matrix properties to \c config. // */ //void HypothesisTestDock::saveConfigAsTemplate(KConfig& config) { // Q_UNUSED(config); //} //TODO: Rather than inbuilt slots use own decided slots for checked rather than clicked // for alternate hypothesis // one_tail_1 is mu > mu0; one_tail_2 is mu < mu0; two_tail = mu != mu0; void HypothesisTestDock::onRbH1OneTail1Toggled(bool checked) { if (!checked) return; ui.rbH0OneTail1->setChecked(true); m_test.tail = HypothesisTest::Test::Tail::Positive; } void HypothesisTestDock::onRbH1OneTail2Toggled(bool checked) { if (!checked) return; ui.rbH0OneTail2->setChecked(true); m_test.tail = HypothesisTest::Test::Tail::Negative; } void HypothesisTestDock::onRbH1TwoTailToggled(bool checked) { if (!checked) return; ui.rbH0TwoTail->setChecked(true); m_test.tail = HypothesisTest::Test::Tail::Two; } /**************************************Helper Functions********************************************/ void HypothesisTestDock::countPartitions(Column *column, int &np, int &total_rows) { total_rows = column->rowCount(); np = 0; QString cell_value; QMap discovered_categorical_var; AbstractColumn::ColumnMode original_col_mode = column->columnMode(); column->setColumnMode(AbstractColumn::Text); for (int i = 0; i < total_rows; i++) { cell_value = column->textAt(i); if (cell_value.isEmpty()) { total_rows = i; break; } if (discovered_categorical_var[cell_value]) continue; discovered_categorical_var[cell_value] = true; np++; } column->setColumnMode(original_col_mode); } void HypothesisTestDock::setColumnsComboBoxModel(Spreadsheet* spreadsheet) { m_onlyValuesCols.clear(); m_twoCategoricalCols.clear(); m_multiCategoricalCols.clear(); for (auto* col : spreadsheet->children()) { if (col->columnMode() == AbstractColumn::Integer || col->columnMode() == AbstractColumn::Numeric) m_onlyValuesCols.append(col); else { int np = 0, n_rows = 0; countPartitions(col, np, n_rows); if (np <= 1) continue; else if (np == 2) m_twoCategoricalCols.append(col); else m_multiCategoricalCols.append(col); } } setColumnsComboBoxView(); showHypothesisTest(); } //TODO: change from if else to switch case: void HypothesisTestDock::setColumnsComboBoxView() { ui.cbCol1->clear(); ui.cbCol2->clear(); ui.cbCol3->clear(); QList::iterator i; switch (m_test.type) { case (HypothesisTest::Test::Type::ZTest): case (HypothesisTest::Test::Type::TTest): { switch (m_test.subtype) { case (HypothesisTest::Test::SubType::TwoSampleIndependent): { for (i = m_onlyValuesCols.begin(); i != m_onlyValuesCols.end(); i++) { ui.cbCol1->addItem( (*i)->name(), qint64(*i)); ui.cbCol2->addItem( (*i)->name(), qint64(*i)); } for (i = m_twoCategoricalCols.begin(); i != m_twoCategoricalCols.end(); i++) ui.cbCol1->addItem( (*i)->name(), qint64(*i)); break; } case (HypothesisTest::Test::SubType::TwoSamplePaired): { for (i = m_onlyValuesCols.begin(); i != m_onlyValuesCols.end(); i++) { ui.cbCol1->addItem( (*i)->name(), qint64(*i)); ui.cbCol2->addItem( (*i)->name(), qint64(*i)); } break; } case (HypothesisTest::Test::SubType::OneSample): { for (i = m_onlyValuesCols.begin(); i != m_onlyValuesCols.end(); i++) ui.cbCol1->addItem( (*i)->name(), qint64(*i)); break; } case HypothesisTest::Test::SubType::OneWay: case HypothesisTest::Test::SubType::TwoWay: case HypothesisTest::Test::SubType::NoneSubType: break; } break; } case HypothesisTest::Test::Type::Anova: { switch (m_test.subtype) { case HypothesisTest::Test::SubType::OneWay: { for (i = m_onlyValuesCols.begin(); i != m_onlyValuesCols.end(); i++) ui.cbCol2->addItem( (*i)->name(), qint64(*i)); for (i = m_twoCategoricalCols.begin(); i != m_twoCategoricalCols.end(); i++) ui.cbCol1->addItem( (*i)->name(), qint64(*i)); for (i = m_multiCategoricalCols.begin(); i != m_multiCategoricalCols.end(); i++) ui.cbCol1->addItem( (*i)->name(), qint64(*i)); break; } case HypothesisTest::Test::SubType::TwoWay: { for (i = m_onlyValuesCols.begin(); i != m_onlyValuesCols.end(); i++) ui.cbCol2->addItem( (*i)->name(), qint64(*i)); for (i = m_twoCategoricalCols.begin(); i != m_twoCategoricalCols.end(); i++) { ui.cbCol1->addItem( (*i)->name(), qint64(*i)); ui.cbCol3->addItem( (*i)->name(), qint64(*i)); } for (i = m_multiCategoricalCols.begin(); i != m_multiCategoricalCols.end(); i++) { ui.cbCol1->addItem( (*i)->name(), qint64(*i)); ui.cbCol3->addItem( (*i)->name(), qint64(*i)); } break; } case HypothesisTest::Test::SubType::TwoSampleIndependent: case HypothesisTest::Test::SubType::TwoSamplePaired: case HypothesisTest::Test::SubType::OneSample: case HypothesisTest::Test::SubType::NoneSubType: break; } break; } case HypothesisTest::Test::Type::NoneType: break; } } bool HypothesisTestDock::nonEmptySelectedColumns() { if (ui.cbCol1->isVisible() && ui.cbCol1->count() < 1) return false; if (ui.cbCol2->isVisible() && ui.cbCol2->count() < 1) return false; return true; } uint8_t HypothesisTestDock::setAllBits(const uint8_t& bits) { if (!bits) return 0; return ~(bits & (bits-1)); } diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.h b/src/kdefrontend/dockwidgets/HypothesisTestDock.h index 94c9b69d6..719535dfb 100644 --- a/src/kdefrontend/dockwidgets/HypothesisTestDock.h +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.h @@ -1,119 +1,119 @@ /*************************************************************************** File : HypothesisTestDock.h Project : LabPlot Description : widget for hypothesis testing properties -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef HYPOTHESISTESTDOCK_H #define HYPOTHESISTESTDOCK_H -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "ui_hypothesistestdock.h" #include class AbstractAspect; class AspectTreeModel; class HypothesisTest; class TreeViewComboBox; class KConfig; class QScrollArea; class QStandardItemModel; class QStandardItem; class QComboBox; class HypothesisTestDock : public QWidget { Q_OBJECT public: explicit HypothesisTestDock(QWidget*); void setHypothesisTest(HypothesisTest*); private: Ui::HypothesisTestDock ui; bool m_initializing{false}; TreeViewComboBox* cbSpreadsheet{nullptr}; HypothesisTest* m_hypothesisTest{nullptr}; AspectTreeModel* m_aspectTreeModel{nullptr}; QSqlDatabase m_db; QString m_configPath; double m_populationMean{0}; double m_significanceLevel{0.05}; // void load(); // void loadConfig(KConfig&); void setModelIndexFromAspect(TreeViewComboBox*, const AbstractAspect*); // void readConnections(); // void updateFields(); // bool fieldSelected(const QString&); HypothesisTest::Test m_test; QScrollArea* scrollDock; void countPartitions(Column *column, int &np, int &total_rows); void setColumnsComboBoxModel(Spreadsheet* spreadsheet); void setColumnsComboBoxView(); bool nonEmptySelectedColumns(); std::uint8_t setAllBits(const std::uint8_t& bits); QList m_onlyValuesCols; QList m_twoCategoricalCols; QList m_multiCategoricalCols; private slots: //SLOTs for changes triggered in PivotTableDock // void nameChanged(); // void commentChanged(); void dataSourceTypeChanged(int); void showTestType(); void showHypothesisTest(); void doHypothesisTest(); void performLeveneTest(); void spreadsheetChanged(const QModelIndex&); void changeCbCol2Label(); void chbPopulationSigmaStateChanged(); void col1IndexChanged(int index); void onRbH1OneTail1Toggled(bool checked); void onRbH1OneTail2Toggled(bool checked); void onRbH1TwoTailToggled(bool checked); // void connectionChanged(); // void tableChanged(); // void showDatabaseManager(); // //SLOTs for changes triggered in PivotTable void hypothesisTestDescriptionChanged(const AbstractAspect*); // void addRow(); // void removeRow(); // void addColumn(); // void removeColumn(); // //save/load template // void loadConfigFromTemplate(KConfig&); // void saveConfigAsTemplate(KConfig&); signals: // void info(const QString&); }; #endif // PIVOTTABLEDOCK_H diff --git a/src/kdefrontend/hypothesisTest/HypothesisTestView.cpp b/src/kdefrontend/generalTest/GeneralTestView.cpp similarity index 74% copy from src/kdefrontend/hypothesisTest/HypothesisTestView.cpp copy to src/kdefrontend/generalTest/GeneralTestView.cpp index c130dc34b..38c5d0016 100644 --- a/src/kdefrontend/hypothesisTest/HypothesisTestView.cpp +++ b/src/kdefrontend/generalTest/GeneralTestView.cpp @@ -1,196 +1,196 @@ /*************************************************************************** - File : HypothesisTestView.cpp + File : GeneralTestView.cpp Project : LabPlot Description : View class for Hypothesis Tests' Table -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ -#include "HypothesisTestView.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "GeneralTestView.h" +#include "backend/generalTest/GeneralTest.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! - \class HypothesisTestView + \class GeneralTestView \brief View class for Hypothesis Test \ingroup kdefrontend */ -HypothesisTestView::HypothesisTestView(HypothesisTest* hypothesisTest) : QWidget(), - m_hypothesisTest(hypothesisTest), +GeneralTestView::GeneralTestView(GeneralTest* GeneralTest) : QWidget(), + m_GeneralTest(GeneralTest), m_testName(new QLabel()), m_statsTable(new QTextEdit()), m_summaryResults(new QWidget()) { m_statsTable->setReadOnly(true); auto* layout = new QVBoxLayout(this); layout->addWidget(m_testName); layout->addWidget(m_statsTable); layout->addWidget(m_summaryResults); layout->addWidget(m_summaryResults); init(); } -HypothesisTestView::~HypothesisTestView() = default; +GeneralTestView::~GeneralTestView() = default; -void HypothesisTestView::init() { +void GeneralTestView::init() { initActions(); initMenus(); m_statsTable->setMouseTracking(true); // m_summaryResults->setStyleSheet("background-color:white; border: 0px; margin: 0px; padding 0px;qproperty-frame: false;"); - connect(m_hypothesisTest, &HypothesisTest::changed, this, &HypothesisTestView::changed); - connect(m_statsTable, &QTextEdit::cursorPositionChanged, this, &HypothesisTestView::cursorPositionChanged); + connect(m_GeneralTest, &GeneralTest::changed, this, &GeneralTestView::changed); + connect(m_statsTable, &QTextEdit::cursorPositionChanged, this, &GeneralTestView::cursorPositionChanged); } -void HypothesisTestView::initActions() { +void GeneralTestView::initActions() { } -void HypothesisTestView::initMenus() { +void GeneralTestView::initMenus() { } -void HypothesisTestView::clearResult() { +void GeneralTestView::clearResult() { for (int i = 0; i < 10; i++) m_resultLine[i]->clear(); } -void HypothesisTestView::connectActions() { +void GeneralTestView::connectActions() { } -void HypothesisTestView::fillToolBar(QToolBar* toolBar) { +void GeneralTestView::fillToolBar(QToolBar* toolBar) { Q_UNUSED(toolBar); } /*! * Populates the menu \c menu with the pivot table and pivot table view relevant actions. * The menu is used * - as the context menu in PivotTableView * - as the "pivot table menu" in the main menu-bar (called form MainWin) * - as a part of the pivot table context menu in project explorer */ -void HypothesisTestView::createContextMenu(QMenu* menu) { +void GeneralTestView::createContextMenu(QMenu* menu) { Q_ASSERT(menu); } -bool HypothesisTestView::exportView() { +bool GeneralTestView::exportView() { return true; } -bool HypothesisTestView::printView() { +bool GeneralTestView::printView() { QPrinter printer; auto* dlg = new QPrintDialog(&printer, this); dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); bool ret; if ((ret = dlg->exec()) == QDialog::Accepted) { print(&printer); } delete dlg; return ret; } -bool HypothesisTestView::printPreview() { +bool GeneralTestView::printPreview() { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); - connect(dlg, &QPrintPreviewDialog::paintRequested, this, &HypothesisTestView::print); + connect(dlg, &QPrintPreviewDialog::paintRequested, this, &GeneralTestView::print); return dlg->exec(); } /*! prints the complete spreadsheet to \c printer. */ -void HypothesisTestView::print(QPrinter* printer) const { +void GeneralTestView::print(QPrinter* printer) const { WAIT_CURSOR; QPainter painter (printer); RESET_CURSOR; } - void HypothesisTestView::changed() { - m_testName->setText(m_hypothesisTest->testName()); - m_statsTable->setHtml(m_hypothesisTest->statsTable()); - m_summaryResults->setLayout(m_hypothesisTest->summaryLayout()); + void GeneralTestView::changed() { + m_testName->setText(m_GeneralTest->testName()); + m_statsTable->setHtml(m_GeneralTest->statsTable()); + m_summaryResults->setLayout(m_GeneralTest->summaryLayout()); } - void HypothesisTestView::cursorPositionChanged() { + void GeneralTestView::cursorPositionChanged() { QTextCursor cursor = m_statsTable->textCursor(); cursor.select(QTextCursor::WordUnderCursor); - QMap tooltips = m_hypothesisTest->tooltips(); + QMap tooltips = m_GeneralTest->tooltips(); if (!cursor.selectedText().isEmpty()) QToolTip::showText(QCursor::pos(), QString("%1") .arg(tooltips.value(cursor.selectedText()))); else QToolTip::hideText(); } -void HypothesisTestView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { +void GeneralTestView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { Q_UNUSED(exportHeader); Q_UNUSED(separator); Q_UNUSED(language); QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export pivot table to file"); } -void HypothesisTestView::exportToLaTeX(const QString & path, const bool exportHeaders, +void GeneralTestView::exportToLaTeX(const QString & path, const bool exportHeaders, const bool gridLines, const bool captions, const bool latexHeaders, const bool skipEmptyRows, const bool exportEntire) const { Q_UNUSED(exportHeaders); Q_UNUSED(gridLines); Q_UNUSED(captions); Q_UNUSED(latexHeaders); Q_UNUSED(skipEmptyRows); Q_UNUSED(exportEntire); QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export pivot table to latex"); } diff --git a/src/kdefrontend/hypothesisTest/HypothesisTestView.h b/src/kdefrontend/generalTest/GeneralTestView.h similarity index 72% copy from src/kdefrontend/hypothesisTest/HypothesisTestView.h copy to src/kdefrontend/generalTest/GeneralTestView.h index abeac1959..d826bee1c 100644 --- a/src/kdefrontend/hypothesisTest/HypothesisTestView.h +++ b/src/kdefrontend/generalTest/GeneralTestView.h @@ -1,92 +1,91 @@ /*************************************************************************** File : PivotTableView.h Project : LabPlot Description : View class for Hypothesis Tests' -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ -#ifndef HYPOTHESISTESTVIEW_H -#define HYPOTHESISTESTVIEW_H +#ifndef GENERALTESTVIEW_H +#define GENERALTESTVIEW_H #include #include "backend/core/AbstractColumn.h" #include "backend/lib/IntervalAttribute.h" class Column; -class HypothesisTest; -class HypothesisTestModel; +class GeneralTest; class AbstractAspect; class QTableView; class QHeaderView; class QListView; class QPrinter; class QMenu; class QToolBar; class QModelIndex; class QItemSelection; class QLabel; class QTextEdit; class QTextCursor; -class HypothesisTestView : public QWidget { - Q_OBJECT +class GeneralTestView : public QWidget { + Q_OBJECT public: - explicit HypothesisTestView(HypothesisTest*); - ~HypothesisTestView() override; + explicit GeneralTestView(GeneralTest*); + ~GeneralTestView() override; - bool exportView(); - bool printView(); - bool printPreview(); + bool exportView(); + bool printView(); + bool printPreview(); private: - void init(); - void initActions(); - void initMenus(); - void connectActions(); + void init(); + void initActions(); + void initMenus(); + void connectActions(); - void exportToFile(const QString&, const bool, const QString&, QLocale::Language) const; - void exportToLaTeX(const QString&, const bool exportHeaders, - const bool gridLines, const bool captions, const bool latexHeaders, - const bool skipEmptyRows,const bool exportEntire) const; + void exportToFile(const QString&, const bool, const QString&, QLocale::Language) const; + void exportToLaTeX(const QString&, const bool exportHeaders, + const bool gridLines, const bool captions, const bool latexHeaders, + const bool skipEmptyRows,const bool exportEntire) const; - HypothesisTest* m_hypothesisTest; + GeneralTest* m_GeneralTest; QLabel* m_testName; QTextEdit* m_statsTable; - QWidget* m_summaryResults{nullptr}; + QWidget* m_summaryResults{nullptr}; QLabel* m_resultLine[10]; public slots: - void createContextMenu(QMenu*); - void fillToolBar(QToolBar*); - void print(QPrinter*) const; - void changed(); + void createContextMenu(QMenu*); + void fillToolBar(QToolBar*); + void print(QPrinter*) const; + void changed(); void cursorPositionChanged(); void clearResult(); private slots: }; #endif diff --git a/src/kdefrontend/hypothesisTest/HypothesisTestView.cpp b/src/kdefrontend/generalTest/HypothesisTestView.cpp similarity index 83% rename from src/kdefrontend/hypothesisTest/HypothesisTestView.cpp rename to src/kdefrontend/generalTest/HypothesisTestView.cpp index c130dc34b..736b25d60 100644 --- a/src/kdefrontend/hypothesisTest/HypothesisTestView.cpp +++ b/src/kdefrontend/generalTest/HypothesisTestView.cpp @@ -1,196 +1,196 @@ /*************************************************************************** File : HypothesisTestView.cpp Project : LabPlot Description : View class for Hypothesis Tests' Table -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "HypothesisTestView.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class HypothesisTestView \brief View class for Hypothesis Test \ingroup kdefrontend */ HypothesisTestView::HypothesisTestView(HypothesisTest* hypothesisTest) : QWidget(), m_hypothesisTest(hypothesisTest), m_testName(new QLabel()), m_statsTable(new QTextEdit()), - m_summaryResults(new QWidget()) { + m_summaryResults(new QWidget()) { m_statsTable->setReadOnly(true); auto* layout = new QVBoxLayout(this); layout->addWidget(m_testName); layout->addWidget(m_statsTable); layout->addWidget(m_summaryResults); layout->addWidget(m_summaryResults); init(); } HypothesisTestView::~HypothesisTestView() = default; void HypothesisTestView::init() { - initActions(); - initMenus(); + initActions(); + initMenus(); m_statsTable->setMouseTracking(true); // m_summaryResults->setStyleSheet("background-color:white; border: 0px; margin: 0px; padding 0px;qproperty-frame: false;"); connect(m_hypothesisTest, &HypothesisTest::changed, this, &HypothesisTestView::changed); connect(m_statsTable, &QTextEdit::cursorPositionChanged, this, &HypothesisTestView::cursorPositionChanged); } void HypothesisTestView::initActions() { } void HypothesisTestView::initMenus() { } void HypothesisTestView::clearResult() { for (int i = 0; i < 10; i++) m_resultLine[i]->clear(); } void HypothesisTestView::connectActions() { } void HypothesisTestView::fillToolBar(QToolBar* toolBar) { - Q_UNUSED(toolBar); + Q_UNUSED(toolBar); } /*! * Populates the menu \c menu with the pivot table and pivot table view relevant actions. * The menu is used * - as the context menu in PivotTableView * - as the "pivot table menu" in the main menu-bar (called form MainWin) * - as a part of the pivot table context menu in project explorer */ void HypothesisTestView::createContextMenu(QMenu* menu) { - Q_ASSERT(menu); + Q_ASSERT(menu); } bool HypothesisTestView::exportView() { - return true; + return true; } bool HypothesisTestView::printView() { - QPrinter printer; - auto* dlg = new QPrintDialog(&printer, this); - dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); - - bool ret; - if ((ret = dlg->exec()) == QDialog::Accepted) { - print(&printer); - } - delete dlg; - return ret; + QPrinter printer; + auto* dlg = new QPrintDialog(&printer, this); + dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); + + bool ret; + if ((ret = dlg->exec()) == QDialog::Accepted) { + print(&printer); + } + delete dlg; + return ret; } bool HypothesisTestView::printPreview() { - QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); + QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); connect(dlg, &QPrintPreviewDialog::paintRequested, this, &HypothesisTestView::print); - return dlg->exec(); + return dlg->exec(); } /*! prints the complete spreadsheet to \c printer. */ void HypothesisTestView::print(QPrinter* printer) const { - WAIT_CURSOR; - QPainter painter (printer); + WAIT_CURSOR; + QPainter painter (printer); - RESET_CURSOR; + RESET_CURSOR; } void HypothesisTestView::changed() { m_testName->setText(m_hypothesisTest->testName()); m_statsTable->setHtml(m_hypothesisTest->statsTable()); m_summaryResults->setLayout(m_hypothesisTest->summaryLayout()); } void HypothesisTestView::cursorPositionChanged() { QTextCursor cursor = m_statsTable->textCursor(); cursor.select(QTextCursor::WordUnderCursor); QMap tooltips = m_hypothesisTest->tooltips(); if (!cursor.selectedText().isEmpty()) QToolTip::showText(QCursor::pos(), QString("%1") .arg(tooltips.value(cursor.selectedText()))); else QToolTip::hideText(); } void HypothesisTestView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { - Q_UNUSED(exportHeader); - Q_UNUSED(separator); - Q_UNUSED(language); - QFile file(path); - if (!file.open(QFile::WriteOnly | QFile::Truncate)) - return; + Q_UNUSED(exportHeader); + Q_UNUSED(separator); + Q_UNUSED(language); + QFile file(path); + if (!file.open(QFile::WriteOnly | QFile::Truncate)) + return; - PERFTRACE("export pivot table to file"); + PERFTRACE("export pivot table to file"); } void HypothesisTestView::exportToLaTeX(const QString & path, const bool exportHeaders, const bool gridLines, const bool captions, const bool latexHeaders, const bool skipEmptyRows, const bool exportEntire) const { - Q_UNUSED(exportHeaders); - Q_UNUSED(gridLines); - Q_UNUSED(captions); - Q_UNUSED(latexHeaders); - Q_UNUSED(skipEmptyRows); - Q_UNUSED(exportEntire); - QFile file(path); - if (!file.open(QFile::WriteOnly | QFile::Truncate)) - return; + Q_UNUSED(exportHeaders); + Q_UNUSED(gridLines); + Q_UNUSED(captions); + Q_UNUSED(latexHeaders); + Q_UNUSED(skipEmptyRows); + Q_UNUSED(exportEntire); + QFile file(path); + if (!file.open(QFile::WriteOnly | QFile::Truncate)) + return; PERFTRACE("export pivot table to latex"); } diff --git a/src/kdefrontend/hypothesisTest/HypothesisTestView.h b/src/kdefrontend/generalTest/HypothesisTestView.h similarity index 80% rename from src/kdefrontend/hypothesisTest/HypothesisTestView.h rename to src/kdefrontend/generalTest/HypothesisTestView.h index abeac1959..1e9dd6d00 100644 --- a/src/kdefrontend/hypothesisTest/HypothesisTestView.h +++ b/src/kdefrontend/generalTest/HypothesisTestView.h @@ -1,92 +1,92 @@ /*************************************************************************** File : PivotTableView.h Project : LabPlot Description : View class for Hypothesis Tests' -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef HYPOTHESISTESTVIEW_H #define HYPOTHESISTESTVIEW_H #include #include "backend/core/AbstractColumn.h" #include "backend/lib/IntervalAttribute.h" class Column; class HypothesisTest; class HypothesisTestModel; class AbstractAspect; class QTableView; class QHeaderView; class QListView; class QPrinter; class QMenu; class QToolBar; class QModelIndex; class QItemSelection; class QLabel; class QTextEdit; class QTextCursor; class HypothesisTestView : public QWidget { - Q_OBJECT + Q_OBJECT public: explicit HypothesisTestView(HypothesisTest*); ~HypothesisTestView() override; - bool exportView(); - bool printView(); - bool printPreview(); + bool exportView(); + bool printView(); + bool printPreview(); private: - void init(); - void initActions(); - void initMenus(); - void connectActions(); + void init(); + void initActions(); + void initMenus(); + void connectActions(); - void exportToFile(const QString&, const bool, const QString&, QLocale::Language) const; - void exportToLaTeX(const QString&, const bool exportHeaders, - const bool gridLines, const bool captions, const bool latexHeaders, - const bool skipEmptyRows,const bool exportEntire) const; + void exportToFile(const QString&, const bool, const QString&, QLocale::Language) const; + void exportToLaTeX(const QString&, const bool exportHeaders, + const bool gridLines, const bool captions, const bool latexHeaders, + const bool skipEmptyRows,const bool exportEntire) const; HypothesisTest* m_hypothesisTest; QLabel* m_testName; QTextEdit* m_statsTable; - QWidget* m_summaryResults{nullptr}; + QWidget* m_summaryResults{nullptr}; QLabel* m_resultLine[10]; public slots: - void createContextMenu(QMenu*); - void fillToolBar(QToolBar*); - void print(QPrinter*) const; - void changed(); + void createContextMenu(QMenu*); + void fillToolBar(QToolBar*); + void print(QPrinter*) const; + void changed(); void cursorPositionChanged(); void clearResult(); private slots: }; #endif diff --git a/tests/stats/anova/AnovaTest.cpp b/tests/stats/anova/AnovaTest.cpp index c393caa1c..0b9fcaaa0 100644 --- a/tests/stats/anova/AnovaTest.cpp +++ b/tests/stats/anova/AnovaTest.cpp @@ -1,183 +1,183 @@ /*************************************************************************** File : AnovaTest.cpp Project : LabPlot Description : Tests for data correlation -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal (agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "AnovaTest.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" void AnovaTest::oneWayAnova_data() { QTest::addColumn>("col1Data"); QTest::addColumn>("col2Data"); QTest::addColumn("fValue_expected"); QTest::addColumn("pValue_expected"); // First Sample QVector col1Data = {"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "3", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", "5"}; QVector col2Data = {1, 43, 15, 40, 8, 17, 30, 34, 34, 26, 1, 7, 22, 30, 40, 15, 20, 9, 14, 15, 6, 30, 15, 30, 12, 17, 21, 23, 20, 27, -19, -18, -8, -6, -6, -9, -17, -12, -11, -6, 5, 8, 12, 19, 8, 15, 21, 28, 26, 27, -10, 6, 4, 3, 0, 4, 9, -5, 7, 13, 38, 20, 20, 28, 11, 17, 15, 27, 24, 23, 28, 26, 34, 32, 24, 29, 30, 24, 34, 23, -5, -12, -15, -4, -2, -6, -2, -7, -10, -15, -13, -16, -23, -22, -9, -18, -17, -15, -14, -15}; double fValue_expected = 33.1289; double pValue_expected = 0; QTest::newRow("socscistatistics") << col1Data << col2Data << fValue_expected << pValue_expected; } void AnovaTest::oneWayAnova() { QFETCH(QVector, col1Data); QFETCH(QVector, col2Data); QFETCH(double, fValue_expected); QFETCH(double, pValue_expected); Column* col1 = new Column("col1", AbstractColumn::Text); Column* col2 = new Column("col2", AbstractColumn::Numeric); col1->replaceTexts(0, col1Data); col2->replaceValues(0, col2Data); QVector cols; cols << col1 << col2; HypothesisTest anovaTest("One Way Anova"); anovaTest.setColumns(cols); HypothesisTest::Test test; test.type = HypothesisTest::Test::Type::Anova; test.subtype = HypothesisTest::Test::SubType::OneWay; test.tail = HypothesisTest::Test::Tail::Two; bool categoricalVariable = true; bool equalVariance = true; anovaTest.performTest(test, categoricalVariable, equalVariance); double fValue = anovaTest.statisticValue()[0]; double pValue = anovaTest.pValue()[0]; QDEBUG("fValue is " << fValue); QDEBUG("pValue is: " << pValue); QDEBUG("fValue_expected is " << fValue_expected); QDEBUG("pValue_expected is: " << pValue_expected); FuzzyCompare(fValue, fValue_expected, 0.1); FuzzyCompare(pValue, pValue_expected, 0.1); } void AnovaTest::twoWayAnova_data() { QTest::addColumn>("col1Data"); QTest::addColumn>("col2Data"); QTest::addColumn>("col3Data"); QTest::addColumn("fCol1Value_expected"); QTest::addColumn("fCol2Value_expected"); QTest::addColumn("fInteractionValue_expected"); QTest::addColumn("pCol1Value_expected"); QTest::addColumn("pCol2Value_expected"); // First Sample // This data set is taken from: http://statweb.stanford.edu/~susan/courses/s141/exanova.pdf QVector col1Data = {"Super", "Super", "Super", "Super", "Super", "Super", "Super", "Super", "Super", "Super", "Super", "Super", "Best", "Best", "Best", "Best", "Best", "Best", "Best", "Best", "Best", "Best", "Best", "Best"}; QVector col2Data = {"cold", "cold", "cold", "cold", "warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot", "cold", "cold", "cold", "cold", "warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot"}; QVector col3Data = {4, 5, 6, 5, 7, 9, 8, 12, 10, 12, 11, 9, 6, 6, 4, 4, 13, 15, 12, 12, 12, 13, 10, 13}; double fCol1Value_expected = 9.8108; double fCol2Value_expected = 48.7297; double fInteractionValue_expected = 3.9730; double pCol1Value_expected = 0.005758; double pCol2Value_expected = 5.44e-08; // double pInteractionValue_expected = 0.037224; QTest::newRow("detergent vs temperature") << col1Data << col2Data << col3Data << fCol1Value_expected << fCol2Value_expected << fInteractionValue_expected << pCol1Value_expected << pCol2Value_expected; } //TODO: check for pValue. In document probabilty is Pr(>F) void AnovaTest::twoWayAnova() { QFETCH(QVector, col1Data); QFETCH(QVector, col2Data); QFETCH(QVector, col3Data); QFETCH(double, fCol1Value_expected); QFETCH(double, fCol2Value_expected); QFETCH(double, fInteractionValue_expected); QFETCH(double, pCol1Value_expected); QFETCH(double, pCol2Value_expected); Column* col1 = new Column("col1", AbstractColumn::Text); Column* col2 = new Column("col2", AbstractColumn::Text); Column* col3 = new Column("col3", AbstractColumn::Numeric); col1->replaceTexts(0, col1Data); col2->replaceTexts(0, col2Data); col3->replaceValues(0, col3Data); QVector cols; cols << col1 << col2 << col3; HypothesisTest anovaTest("Two Way Anova"); anovaTest.setColumns(cols); HypothesisTest::Test test; test.type = HypothesisTest::Test::Type::Anova; test.subtype = HypothesisTest::Test::SubType::TwoWay; test.tail = HypothesisTest::Test::Tail::Two; anovaTest.performTest(test); double fCol1Value = anovaTest.statisticValue()[0]; double fCol2Value = anovaTest.statisticValue()[1]; double fInteractionValue = anovaTest.statisticValue()[2]; double pCol1Value = anovaTest.pValue()[0]; double pCol2Value = anovaTest.pValue()[1]; QDEBUG("size of statistic value is " << anovaTest.statisticValue().size()); QDEBUG("fCol1Value is " << fCol1Value); QDEBUG("fCol1Value_expected is " << fCol1Value_expected); QDEBUG("fCol2Value is " << fCol2Value); QDEBUG("fCol2Value_expected is " << fCol2Value_expected); QDEBUG("fInteractionValue is " << fInteractionValue); QDEBUG("fInteractionValue_expected is " << fInteractionValue_expected); QDEBUG("pCol1Value is " << pCol1Value); QDEBUG("pCol1Value_expected is " << pCol1Value_expected); QDEBUG("pCol2Value is " << pCol2Value); QDEBUG("pCol2Value_expected is " << pCol2Value_expected); FuzzyCompare(fCol1Value, fCol1Value_expected, 0.1); FuzzyCompare(fCol2Value, fCol2Value_expected, 0.1); FuzzyCompare(fInteractionValue, fInteractionValue_expected, 0.1); FuzzyCompare(pCol1Value, pCol1Value_expected, 0.1); FuzzyCompare(pCol2Value, pCol2Value_expected, 0.1); } QTEST_MAIN(AnovaTest) diff --git a/tests/stats/ttest/TTestTest.cpp b/tests/stats/ttest/TTestTest.cpp index ff6455e22..ae86edf1c 100644 --- a/tests/stats/ttest/TTestTest.cpp +++ b/tests/stats/ttest/TTestTest.cpp @@ -1,200 +1,200 @@ /*************************************************************************** File : CorrelationTest.cpp Project : LabPlot Description : Tests for data correlation -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal (agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "TTestTest.h" -#include "backend/hypothesisTest/HypothesisTest.h" +#include "backend/generalTest/HypothesisTest.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" void TTestTest::twoSampleIndependent_data() { QTest::addColumn>("col1Data"); QTest::addColumn>("col2Data"); QTest::addColumn("tValue_expected"); QTest::addColumn("pValue_expected"); // First Sample // This data set is taken from "JASP" QVector col1Data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; QVector col2Data = {3, 1, 5, 4, 6, 4, 6, 2, 0, 5, 4, 5, 4, 3, 6, 6, 8, 5, 5, 4, 2, 5, 7, 5}; double tValue_expected = -1.713; double pValue_expected = 0.101; QTest::newRow("invisible cloak") << col1Data << col2Data << tValue_expected << pValue_expected; // Second Sample // This data set is taken from "JASP" col1Data = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; col2Data = {42, 46, 43, 10, 55, 17, 26, 60, 62, 53, 37, 42, 33, 37, 41, 42, 19, 55, 54, 28, 20, 48, 85, 24, 56, 43, 59, 58, 52, 71, 62, 43, 54, 49, 57, 61, 33, 44, 46, 67, 43, 49, 57, 53}; tValue_expected = -2.26; pValue_expected = 0.028; QTest::newRow("directed control activities") << col1Data << col2Data << tValue_expected << pValue_expected; } void TTestTest::twoSampleIndependent() { QFETCH(QVector, col1Data); QFETCH(QVector, col2Data); QFETCH(double, tValue_expected); QFETCH(double, pValue_expected); Column* col1 = new Column("col1", AbstractColumn::Numeric); Column* col2 = new Column("col2", AbstractColumn::Numeric); col1->replaceValues(0, col1Data); col2->replaceValues(0, col2Data); QVector cols; cols << col1 << col2; HypothesisTest tTest("Two Sample Independent"); tTest.setColumns(cols); HypothesisTest::Test test; test.type = HypothesisTest::Test::Type::TTest; test.subtype = HypothesisTest::Test::SubType::TwoSampleIndependent; test.tail = HypothesisTest::Test::Tail::Two; bool categoricalVariable = true; bool equalVariance = true; tTest.performTest(test, categoricalVariable, equalVariance); double tValue = tTest.statisticValue()[0]; double pValue = tTest.pValue()[0]; qDebug() << "tValue is " << tValue; qDebug() << "pValue is: " << pValue; qDebug() << "tValue_expected is " << tValue_expected; qDebug() << "pValue_expected is: " << pValue_expected; FuzzyCompare(tValue, tValue_expected, (0.01) / abs(tValue)); FuzzyCompare(pValue, pValue_expected, (0.01) / abs(pValue)); } void TTestTest::twoSamplePaired_data() { QTest::addColumn>("col1Data"); QTest::addColumn>("col2Data"); QTest::addColumn("tValue_expected"); QTest::addColumn("pValue_expected"); // First Sample // This data set is taken from "JASP" // DATA SET:: Moon and Aggression QVector col1Data = {3.33, 3.67, 2.67, 3.33, 3.33, 3.67, 4.67, 2.67, 6, 4.33, 3.33, 0.67, 1.33, 0.33, 2}; QVector col2Data = {0.27, 0.59, 0.32, 0.19, 1.26, 0.11, 0.3, 0.4, 1.59, 0.6, 0.65, 0.69, 1.26, 0.23, 0.38}; double tValue_expected = 6.452; double pValue_expected = 0.001; QTest::newRow("Moon and Aggression") << col1Data << col2Data << tValue_expected << pValue_expected; } void TTestTest::twoSamplePaired() { QFETCH(QVector, col1Data); QFETCH(QVector, col2Data); QFETCH(double, tValue_expected); QFETCH(double, pValue_expected); Column* col1 = new Column("col1", AbstractColumn::Numeric); Column* col2 = new Column("col2", AbstractColumn::Numeric); col1->replaceValues(0, col1Data); col2->replaceValues(0, col2Data); QVector cols; cols << col1 << col2; HypothesisTest tTest("Two Sample Paried"); tTest.setColumns(cols); HypothesisTest::Test test; test.type = HypothesisTest::Test::Type::TTest; test.subtype = HypothesisTest::Test::SubType::TwoSamplePaired; test.tail = HypothesisTest::Test::Tail::Two; tTest.performTest(test); double tValue = tTest.statisticValue()[0]; double pValue = tTest.pValue()[0]; qDebug() << "tValue is " << tValue; qDebug() << "pValue is: " << pValue; qDebug() << "tValue_expected is " << tValue_expected; qDebug() << "pValue_expected is: " << pValue_expected; FuzzyCompare(tValue, tValue_expected, (0.01) / abs(tValue)); FuzzyCompare(pValue, pValue_expected, (0.01) / abs(pValue)); } void TTestTest::oneSample_data() { QTest::addColumn>("col1Data"); QTest::addColumn("populationMean"); QTest::addColumn("tValue_expected"); QTest::addColumn("pValue_expected"); // First Sample // This data set is taken from "JASP" // DATA SET:: Weight Gain; QVector col1Data = {13.2, 8.58, 14.08, 8.58, 10.56, 14.74, 7.92, 13.2, 12.76, 5.72, 11.66, 7.04, 3.08, 15.62, 14.3, 5.5}; double populationMean = 16; double tValue_expected = -5.823; double pValue_expected = 0.001; QTest::newRow("weight gain") << col1Data << populationMean << tValue_expected << pValue_expected; } void TTestTest::oneSample() { QFETCH(QVector, col1Data); QFETCH(double, populationMean); QFETCH(double, tValue_expected); QFETCH(double, pValue_expected); Column* col1 = new Column("col1", AbstractColumn::Numeric); col1->replaceValues(0, col1Data); QVector cols; cols << col1; HypothesisTest tTest("One Sample"); tTest.setColumns(cols); tTest.setPopulationMean(populationMean); HypothesisTest::Test test; test.type = HypothesisTest::Test::Type::TTest; test.subtype = HypothesisTest::Test::SubType::OneSample; test.tail = HypothesisTest::Test::Tail::Two; tTest.performTest(test); double tValue = tTest.statisticValue()[0]; double pValue = tTest.pValue()[0]; qDebug() << "tValue is " << tValue; qDebug() << "pValue is: " << pValue; qDebug() << "tValue_expected is " << tValue_expected; qDebug() << "pValue_expected is: " << pValue_expected; FuzzyCompare(tValue, tValue_expected, (0.01) / fabs(tValue)); FuzzyCompare(pValue, pValue_expected, (0.01) / fabs(pValue)); } QTEST_MAIN(TTestTest)