diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d00a555b..2a871b844 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,479 +1,481 @@ 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/MQTTSubscriptionWidget.cpp ${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerDialog.cpp ${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CustomPointDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ColumnDock.cpp + ${KDEFRONTEND_DIR}/dockwidgets/HypothesisTestDock.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}/dockwidgets/WorksheetDock.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/mqttsubscriptionwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqttconnectionmanagerwidget.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.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}/matrix/Matrix.cpp ${BACKEND_DIR}/matrix/matrixcommands.cpp ${BACKEND_DIR}/matrix/MatrixModel.cpp ${BACKEND_DIR}/pivot/PivotTable.cpp - ${BACKEND_DIR}/t_test/TTest.cpp + ${BACKEND_DIR}/hypothesis_test/HypothesisTest.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/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_LIBS_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::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_LIBS_FOUND) target_link_libraries( labplot2lib ${CANTOR_LIBS} KF5::Service KF5::Parts) ENDIF () IF (HDF5_FOUND) target_link_libraries( labplot2lib ${HDF5_LIBRARIES} ) ENDIF () IF (FFTW_FOUND) target_link_libraries( labplot2lib ${FFTW_LIBRARIES} ) ENDIF () IF (NETCDF_FOUND) target_link_libraries( labplot2lib ${NETCDF_LIBRARY} ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries( labplot2lib ${CFITSIO_LIBRARY} ) 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 () add_executable(labplot2 ${LABPLOT_SOURCE}) # 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 () 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/t_test/TTest.cpp b/src/backend/hypothesis_test/HypothesisTest.cpp similarity index 50% rename from src/backend/t_test/TTest.cpp rename to src/backend/hypothesis_test/HypothesisTest.cpp index 8cef506b5..6e6265c66 100644 --- a/src/backend/t_test/TTest.cpp +++ b/src/backend/hypothesis_test/HypothesisTest.cpp @@ -1,152 +1,285 @@ /*************************************************************************** - File : TTest.cpp + File : HypothesisTest.cpp Project : LabPlot Description : Doing T-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 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 "TTest.h" +#include "HypothesisTest.h" +#include "HypothesisTestPrivate.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/core/column/Column.h" +#include "backend/lib/macros.h" +#include "QDebug" extern "C" { #include "backend/nsl/nsl_stats.h" } #include #include #include #include -TTest::TTest(const QString &name) { - Q_UNUSED(name); +HypothesisTest::HypothesisTest(const QString &name) : AbstractPart(name), +d(new HypothesisTestPrivate(this)) { } -void TTest::setDataSourceSpreadsheet(Spreadsheet *spreadsheet) { +HypothesisTest::~HypothesisTest() { + delete d; +} + +void HypothesisTest::setDataSourceType(DataSourceType type) { + if (type != d->dataSourceType) { + d->dataSourceType = type; + } +} + +void HypothesisTest::setDataSourceSpreadsheet(Spreadsheet *spreadsheet) { + if (spreadsheet != d->dataSourceSpreadsheet) + d->setDataSourceSpreadsheet(spreadsheet); +} + +QStringList HypothesisTest::allColumns() +{ + return d->all_columns; +} + +void HypothesisTest::setColumns(QVector cols) { + d->m_columns = cols; +} + +void HypothesisTest::setColumns(QStringList cols) { + return d->setColumns(cols); +} + +HypothesisTest::DataSourceType HypothesisTest::dataSourceType() const { + return d->dataSourceType; +} + +void HypothesisTest::performTwoSampleTTest() { + d->performTwoSampleTTest(); +} + +/****************************************************************************** + * Private Implementations + * ****************************************************************************/ + +HypothesisTestPrivate::HypothesisTestPrivate(HypothesisTest* owner) : q(owner) { +} + +HypothesisTestPrivate::~HypothesisTestPrivate() { +} + +void HypothesisTestPrivate::setDataSourceSpreadsheet(Spreadsheet *spreadsheet) { dataSourceSpreadsheet = spreadsheet; + //setting rows and columns count; m_rowCount = dataSourceSpreadsheet->rowCount(); m_columnCount = dataSourceSpreadsheet->columnCount(); - DEBUG("in ttest::setDataSourceSpreadsheet") - // now finding the number of columns and rows; - DEBUG("row count is " << m_rowCount) - DEBUG("row count is " << m_columnCount) - DEBUG("exiting ttest::setDataSourceSpreadsheet") + for (auto* col : dataSourceSpreadsheet->children()) { + all_columns << col->name(); + } } -void TTest::setColumns(QVector cols) { - m_columns = cols; - return; + +void HypothesisTestPrivate::setColumns(QStringList cols) { + m_columns.clear(); + Column* column = new Column("column"); + for (QString col : cols) { + if (col != "") { + column = dataSourceSpreadsheet->column(col); +// qDebug() << "col is " << col; + m_columns.append(column); + } + } } -void TTest::performTwoSampleTest() { +void HypothesisTestPrivate::performTwoSampleTTest() { QMessageBox* msg_box = new QMessageBox(); // checking for cols; if (m_columns.size() != 2) { msg_box->setText(i18n("Inappropriate number of columns selected")); msg_box->exec(); return; } bool modeOk = true; + bool allColumnsValid = true; for (int i = 0; i < 2; i++) { - if(m_columns[0]->columnMode() == AbstractColumn::Numeric || m_columns[i]->columnMode() == AbstractColumn::Integer) + if(m_columns[i]->columnMode() == AbstractColumn::Numeric || m_columns[i]->columnMode() == AbstractColumn::Integer) continue; modeOk = false; } + if(!allColumnsValid) { + msg_box->setText(i18n("one of the selected columns is invalid")); + msg_box->exec(); + return; + } + if (!modeOk) { msg_box->setText(i18n("select only columns with numbers")); msg_box->exec(); return; } // use of three than two for human readiblity of code; int n[2]; double sum[2], mean[2], std[2]; for (int i = 0; i < 2; i++) { findStats(m_columns[i], n[i], sum[i], mean[i], std[i]); DEBUG("for " << i); DEBUG("n is "<setText(i18n("atleast one of selected column empty")); msg_box->exec(); return; } } int df = n[0] + n[1] - 2; //Assuming equal variance double sp = qSqrt( ((n[0]-1)*qPow(std[0],2) + (n[1]-1)*qPow(std[1],2))/df); QDEBUG("sp is " << sp); double t = (mean[0] - mean[1])/(sp*qSqrt(1.0/n[0] + 1.0/n[1])); // now finding p value from t value double p_value = nsl_stats_tdist_p(t, df); QString text = i18n("T value for test is %1 and\n p value is %2",t, p_value); msg_box->setText(text); msg_box->exec(); return; // double t_value = } -void TTest::findStats(Column* column, int &count, double &sum, double &mean, double &std) { +void HypothesisTestPrivate::findStats(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; mean = sum/count; for (int i = 0; i < count; i++) { double row = column->valueAt(i); std += qPow((row - mean),2); } if (count > 1) std = std / (count-1); std = qSqrt(std); return; } + + +/********************************************************************************** + * virtual functions implementations + * ********************************************************************************/ + +/*! + Saves as XML. + */ +void HypothesisTest::save(QXmlStreamWriter* writer) const { + writer->writeStartElement("hypothesisTest"); + writeBasicAttributes(writer); + writeCommentElement(writer); + //TODO: + + writer->writeEndElement(); +} + +/*! + Loads from XML. +*/ +bool HypothesisTest::load(XmlStreamReader* reader, bool preview) { + Q_UNUSED(preview); + if (!readBasicAttributes(reader)) + return false; + + //TODO: + + 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 PivotTableView(const_cast(this)); +// m_partView = m_view; +// } +// return m_partView; + return new QWidget(); +} + +/*! + 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/backend/t_test/TTest.h b/src/backend/hypothesis_test/HypothesisTest.h similarity index 60% copy from src/backend/t_test/TTest.h copy to src/backend/hypothesis_test/HypothesisTest.h index 30a18d281..ad3e0cd23 100644 --- a/src/backend/t_test/TTest.h +++ b/src/backend/hypothesis_test/HypothesisTest.h @@ -1,55 +1,85 @@ /*************************************************************************** - File : TTest.h + File : HypothesisTest.h Project : LabPlot Description : Doing T-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 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 * * * ***************************************************************************/ -#ifndef TTEST_H -#define TTEST_H +#ifndef HYPOTHESISTEST_H +#define HYPOTHESISTEST_H #include +#include "backend/core/AbstractPart.h" +#include "backend/lib/macros.h" +class HypothesisTestPrivate; class Spreadsheet; class QString; class Column; -class TTest { +class HypothesisTest : public AbstractPart { + Q_OBJECT + public: - explicit TTest(const QString& name); + explicit HypothesisTest(const QString& name); + ~HypothesisTest() override; + + enum DataSourceType {DataSourceSpreadsheet, DataSourceDatabase}; + void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); void setColumns(QVector cols); - void performTwoSampleTest(); + void setColumns(QStringList cols); + void performTwoSampleTTest(); + DataSourceType dataSourceType() const; + + + //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; + + Spreadsheet* dataSourceSpreadsheet() const; + + void setDataSourceType(DataSourceType type); + QStringList allColumns(); + private: -// double findMean(Column* col); -// double findStandardDeviation(Column* col, double mean); - void findStats(Column* column, int &count, double &sum, double &mean, double &std); - - Spreadsheet* dataSourceSpreadsheet{nullptr}; - int m_rowCount{0}; - int m_columnCount{0}; - QVector m_columns; + HypothesisTestPrivate* const d; + friend class HypothesisTestPrivate; + +signals: + void changed(); + void requestProjectContextMenu(QMenu*); + void dataSourceTypeChanged(HypothesisTest::DataSourceType); + void dataSourceSpreadsheetChanged(Spreadsheet*); }; -#endif // TTEST_H +#endif // HypothesisTest_H diff --git a/src/backend/t_test/TTest.h b/src/backend/hypothesis_test/HypothesisTestPrivate.h similarity index 59% rename from src/backend/t_test/TTest.h rename to src/backend/hypothesis_test/HypothesisTestPrivate.h index 30a18d281..5ade4b98d 100644 --- a/src/backend/t_test/TTest.h +++ b/src/backend/hypothesis_test/HypothesisTestPrivate.h @@ -1,55 +1,62 @@ /*************************************************************************** - File : TTest.h + File : HypothesisTestPrivate.h Project : LabPlot - Description : Doing T-Test on data provided + Description : Private members of Hypothesis Test -------------------------------------------------------------------- - Copyright : (C) 2019 Alexander Semke(alexander.semke@web.de) + Copyright : (C) 2019 by 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 * * * ***************************************************************************/ -#ifndef TTEST_H -#define TTEST_H -#include +#ifndef HYPOTHESISTESTPRIVATE_H +#define HYPOTHESISTESTPRIVATE_H +#include -class Spreadsheet; -class QString; -class Column; - -class TTest { +class HypothesisTestPrivate { public: - explicit TTest(const QString& name); - void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); - void setColumns(QVector cols); - void performTwoSampleTest(); + explicit HypothesisTestPrivate(HypothesisTest*); + virtual ~HypothesisTestPrivate(); + + QString name() const; + + HypothesisTest* const q; + + HypothesisTest::DataSourceType dataSourceType{HypothesisTest::DataSourceSpreadsheet}; + Spreadsheet* dataSourceSpreadsheet{nullptr}; + + void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); + void setColumns(QStringList cols); + QVector m_columns; + + QStringList all_columns; + + bool m_dbCreated{false}; + int m_rowCount{0}; + int m_columnCount{0}; + + void performTwoSampleTTest(); private: -// double findMean(Column* col); -// double findStandardDeviation(Column* col, double mean); - void findStats(Column* column, int &count, double &sum, double &mean, double &std); - - Spreadsheet* dataSourceSpreadsheet{nullptr}; - int m_rowCount{0}; - int m_columnCount{0}; - QVector m_columns; + void findStats(Column* column, int &count, double &sum, double &mean, double &std); +// QMap m_members; }; -#endif // TTEST_H +#endif diff --git a/src/backend/pivot/PivotTable.cpp b/src/backend/pivot/PivotTable.cpp index c166da97a..8d685c68b 100644 --- a/src/backend/pivot/PivotTable.cpp +++ b/src/backend/pivot/PivotTable.cpp @@ -1,614 +1,613 @@ /*************************************************************************** File : PivotTable.cpp Project : LabPlot Description : Aspect providing pivot table functionality -------------------------------------------------------------------- Copyright : (C) 2019 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 "PivotTable.h" #include "PivotTablePrivate.h" #include "kdefrontend/pivot/PivotTableView.h" #include "kdefrontend/pivot/HierarchicalHeaderView.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/trace.h" #include "backend/lib/XmlStreamReader.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class PivotTable \brief Aspect providing a pivot table. \ingroup backend */ PivotTable::PivotTable(const QString& name, bool loading) : AbstractPart(name), d(new PivotTablePrivate(this)) { Q_UNUSED(loading) } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_D_READER_IMPL(PivotTable, PivotTable::DataSourceType, dataSourceType, dataSourceType) BASIC_D_READER_IMPL(PivotTable, Spreadsheet*, dataSourceSpreadsheet, dataSourceSpreadsheet) CLASS_D_READER_IMPL(PivotTable, QString, dataSourceConnection, dataSourceConnection) CLASS_D_READER_IMPL(PivotTable, QString, dataSourceTable, dataSourceTable) QAbstractItemModel* PivotTable::dataModel() const { return d->dataModel; } void PivotTable::setHorizontalHeaderModel(QAbstractItemModel* model) const { d->horizontalHeaderModel = dynamic_cast(model); qDebug()<<"model in set " << d->horizontalHeaderModel << " " << model; } void PivotTable::setVerticalHeaderModel(QAbstractItemModel* model) const { d->verticalHeaderModel = dynamic_cast(model); } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(PivotTable, SetDataSourceType, PivotTable::DataSourceType, dataSourceType, recalculate) void PivotTable::setDataSourceType(DataSourceType type) { if (type != d->dataSourceType) exec(new PivotTableSetDataSourceTypeCmd(d, type, ki18n("%1: data source type changed"))); } STD_SETTER_CMD_IMPL_F_S(PivotTable, SetDataSourceSpreadsheet, Spreadsheet*, dataSourceSpreadsheet, recalculate) void PivotTable::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) { if (spreadsheet != d->dataSourceSpreadsheet) exec(new PivotTableSetDataSourceSpreadsheetCmd(d, spreadsheet, ki18n("%1: data source spreadsheet changed"))); } const QStringList& PivotTable::dimensions() const { return d->dimensions; } const QStringList& PivotTable::measures() const { return d->measures; } const QStringList& PivotTable::rows() const { return d->rows; } void PivotTable::addToRows(const QString& field) { d->addToRows(field); } void PivotTable::removeFromRows(const QString& field) { d->removeFromRows(field); } const QStringList& PivotTable::columns() const { return d->columns; } void PivotTable::addToColumns(const QString& dimension) { d->addToColumns(dimension); } void PivotTable::removeFromColumns(const QString& field) { d->removeFromColumns(field); } bool PivotTable::exportView() const { return true; } bool PivotTable::printView() { return true; } bool PivotTable::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* PivotTable::view() const { if (!m_partView) { m_view = new PivotTableView(const_cast(this)); m_partView = m_view; } return m_partView; } /*! Returns a new context menu. The caller takes ownership of the menu. */ QMenu* PivotTable::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } /*! Returns an icon to be used for decorating my views. */ QIcon PivotTable::icon() const { return QIcon::fromTheme("labplot-spreadsheet"); } //############################################################################## //###################### Private implementation ############################### //############################################################################## PivotTablePrivate::PivotTablePrivate(PivotTable* owner) : q(owner) , dataModel(new QStandardItemModel) // horizontalHeaderModel(new HierarchicalHeaderModel), // verticalHeaderModel(new HierarchicalHeaderModel) { - } PivotTablePrivate::~PivotTablePrivate() { } QString PivotTablePrivate::name() const { return q->name(); } void PivotTablePrivate::addToRows(const QString& field) { rows << field; recalculate(); } void PivotTablePrivate::removeFromRows(const QString& field) { rows.removeOne(field); recalculate(); } void PivotTablePrivate::addToColumns(const QString& dimension) { columns << dimension; recalculate(); } void PivotTablePrivate::removeFromColumns(const QString& field) { columns.removeOne(field); recalculate(); } /* QStringList PivotTablePrivate::members(const QString& dimension, PivotTable::SortType sort) { //check whether we have the members already if (m_members.contains(dimension)) return m_members.value(dimension); QStringList members; //construct the query QString query; swich case (sort) { case PivotTable::NoSort: query = "SELECT DISTINCT " + dimension " FROM pivot;"; break; case PivotTable::SortAscending: query = "SELECT DISTINCT " + dimension " FROM pivot ASC;"; break; case PivotTable::SortDescending: query = "SELECT DISTINCT " + dimension " FROM pivot DESC;"; break; } //execute the query QSqlQuery q; if (!q.exec(query)) { KMessageBox::error(nullptr, i18n("Failed to process the query.") + "\n" + q.lastError().databaseText()); return members; } //copy the results to the string list while (query.next()) members << query.value(0).toString(); //cache the members of the new dimension m_members[dimension] = members; return members; } */ void PivotTablePrivate::recalculate() { //clear the previos result dataModel->clear(); horizontalHeaderModel->clear(); verticalHeaderModel->clear(); //nothing to do if no spreadsheet is set yet if (dataSourceType == PivotTable::DataSourceSpreadsheet && !dataSourceSpreadsheet) return; if (rows.isEmpty() && columns.isEmpty() && !showTotals) { //notify about the new result emit q->changed(); return; } WAIT_CURSOR; if (dataSourceType == PivotTable::DataSourceSpreadsheet && !m_dbCreated) createDb(); //construct the SQL statement string QString query{"SELECT "}; QString groupByString; if (!showNulls) { //if we don't need to show combinations with empty intersections, put everything into GROUP BY if (!rows.isEmpty()) groupByString = rows.join(','); if (!columns.isEmpty()) { if (!groupByString.isEmpty()) groupByString += ','; groupByString += columns.join(','); } if (!groupByString.isEmpty()) { query += groupByString; // if (showTotals) query += ", COUNT(*) FROM pivot"; query += " GROUP BY " + groupByString; if (!sortDimension.isEmpty()) { switch (sortType) { case PivotTable::NoSort: query += " ORDER BY " + sortDimension; break; case PivotTable::SortAscending: query += " ORDER BY " + sortDimension + " ASC"; break; case PivotTable::SortDescending: query += " ORDER BY " + sortDimension + " DESC"; break; } } } else { //no dimensions selected, show totals only query += "COUNT(*) FROM pivot"; } } else { } //QDEBUG(query); qDebug()<<"query is " << query; //execute the query QSqlQuery sqlQuery; if (!sqlQuery.exec(query)) { RESET_CURSOR; KMessageBox::error(nullptr, i18n("Failed to process the query.") + "\n" + sqlQuery.lastError().databaseText()); emit q->changed(); return; } //copy the result into the models int rowsCount = sqlQuery.size(); int columnsCount = sqlQuery.record().count(); int firstValueIndex = rows.size() + columns.size(); int valuesCount = columnsCount - firstValueIndex; // DEBUG("nubmer of columns " << columnsCount); // DEBUG("number rows: " << rowsCount); // DEBUG("number values: " << valuesCount); // DEBUG("index of the first value column: " << firstValueIndex); qDebug() << "number of columns " << columnsCount; qDebug() << "number of rows" << rowsCount; qDebug() << "number of values" << valuesCount; qDebug() << "index of first value column " << firstValueIndex; qDebug()<<"model in recalculate " << horizontalHeaderModel; if (!horizontalHeaderModel) { RESET_CURSOR; return; } //resize the hierarhical header models if (columns.isEmpty() && rows.isEmpty()) { //no labels provided, show the total count only //vertical header verticalHeaderModel->setColumnCount(0); verticalHeaderModel->setRowCount(0); //horizontal header horizontalHeaderModel->setColumnCount(1); horizontalHeaderModel->setRowCount(1); horizontalHeaderModel->setData(horizontalHeaderModel->index(0, 0), "Totals", Qt::DisplayRole); } else if (columns.isEmpty()) { //no column labels provided, we have: //* all labels on rows // -> one column for the vertical header with the number of rows equal to the number of record sets in the result query //* only values on columns // -> one row for the horizontal header with the number of columns equal to the number of values //vertical header verticalHeaderModel->setColumnCount(rows.count()); //horizontal header horizontalHeaderModel->setColumnCount(valuesCount); horizontalHeaderModel->setRowCount(1); //TODO: only "Totals" value at the moment, needs to be extended later when we allow to add other values horizontalHeaderModel->setData(horizontalHeaderModel->index(0, 0), "Totals", Qt::DisplayRole); } else if (rows.isEmpty()) { //no row labels provided, we have: //* all labels on rows // -> one column for the vertical header with the number of rows equal to the number of record sets in the result query //* only values on columns // -> one row for the horizontal header with the number of columns equal to the number of values //vertical header //horizontal header // for (int i = 0; i < columns.size(); ++i) // horizontalHeaderModel->setData(horizontalHeaderModel->index(0, i), columns.at(i), Qt::DisplayRole); } else { //TODO: } //handle the data model dataModel->setColumnCount(valuesCount); if (rowsCount != -1) dataModel->setRowCount(rowsCount); int row = 0; //add values to the data models if (columns.isEmpty() && rows.isEmpty()) { } else if (columns.isEmpty()) { qDebug()<<"everything on rows"; int* start_span = new int[firstValueIndex]; int* end_span = new int[firstValueIndex]; QString* last_value= new QString[firstValueIndex]; verticalHeaderModel->setRowCount(row+1); for (int i = 0; i < firstValueIndex; ++i) { start_span[i] = 1; end_span[i] = 1; last_value[i] = ""; verticalHeaderModel->setData(verticalHeaderModel->index(row, i), rows.at(i), Qt::DisplayRole); } row++; while (sqlQuery.next()) { qDebug()<<"row: " << row; verticalHeaderModel->setRowCount(row+1); // if(sqlQuery.value(0).toString() != last_value) // { // if(end_span > start_span) // verticalHeaderModel->setSpan(start_span,0,end_span-start_span,0); // start_span = end_span; // last_value = sqlQuery.value(0).toString(); // } // end_span = end_span + 1; bool parent_header_changed = false; for (int i = 0; i < firstValueIndex; ++i) { QString queryVal = sqlQuery.value(i).toString(); qDebug()<<"adding to the horizontal header " << query; if(queryVal != last_value[i] || parent_header_changed) { verticalHeaderModel->setData(verticalHeaderModel->index(row, i), queryVal, Qt::DisplayRole); if(end_span[i] > start_span[i]+1) verticalHeaderModel->setSpan(start_span[i],i,end_span[i]-start_span[i],0); start_span[i] = end_span[i]; parent_header_changed = true; } last_value[i] = queryVal; end_span[i] = end_span[i] + 1; } //values for (int i = firstValueIndex; i < columnsCount; ++i) { QString value = sqlQuery.value(i).toString(); qDebug()<<"adding value " << value; if (rowsCount == -1) dataModel->setRowCount(row + 1); dataModel->setItem(row, i - firstValueIndex, new QStandardItem(value)); } ++row; } for(int i = 0; i < firstValueIndex; ++i){ if(end_span[i] > start_span[i]){ verticalHeaderModel->setSpan(start_span[i],i,end_span[i]-start_span[i],0); } } verticalHeaderModel->setSpan(1,0,0,rows.count()); } else if (rows.isEmpty()) { qDebug()<<"everything on columns"; // for (int i = firstValueIndex; i < columnsCount; ++i) { // QString value = sqlQuery.value(i).toString(); // if (rowsCount == -1) // dataModel->setRowCount(row + 1); // dataModel->setItem(0, i - firstValueIndex + row, new QStandardItem(value)); // } } else { //TODO } //notify about the new result emit q->changed(); RESET_CURSOR; } void PivotTablePrivate::createDb() { for (auto* col : dataSourceSpreadsheet->children()) { if (col->isNumeric()) measures << col->name(); else dimensions << col->name(); } 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.open(); //create table const int cols = dataSourceSpreadsheet->columnCount(); QString query = QLatin1String("create table ") + QLatin1String("pivot") + QLatin1String(" ("); for (int i = 0; i < cols; ++i) { Column* col = dataSourceSpreadsheet->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 the SQLite database.") + "\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 '" + QLatin1String("pivot") + "' ("; for (int i = 0; i < cols; ++i) { if (i != 0) query += QLatin1String(", "); query += QLatin1Char('\'') + dataSourceSpreadsheet->column(i)->name() + QLatin1Char('\''); } query += QLatin1String(") VALUES "); for (int i = 0; i < dataSourceSpreadsheet->rowCount(); ++i) { if (i != 0) query += QLatin1String(","); query += QLatin1Char('('); for (int j = 0; j < cols; ++j) { Column* col = dataSourceSpreadsheet->column(j); if (j != 0) query += QLatin1String(", "); QString text = col->asStringColumn()->textAt(i); text = text.replace("'", "''"); query += QLatin1Char('\'') + text + 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(query); QDEBUG("bulk insert error " << q.lastError().databaseText()); } else q.exec(QLatin1String("COMMIT TRANSACTION;")); m_dbCreated = true; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void PivotTable::save(QXmlStreamWriter* writer) const { writer->writeStartElement("pivotTable"); writeBasicAttributes(writer); writeCommentElement(writer); //TODO: writer->writeEndElement(); } /*! Loads from XML. */ bool PivotTable::load(XmlStreamReader* reader, bool preview) { Q_UNUSED(preview); if (!readBasicAttributes(reader)) return false; //TODO: return !reader->hasError(); } diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp index 7a658653b..c59da9610 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp @@ -1,2831 +1,2833 @@ /*************************************************************************** File : SpreadsheetView.cpp Project : LabPlot Description : View class for Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2011-2017 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/t_test/TTest.h" +#include "backend/hypothesis_test/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_ttest = new QAction(i18n("T 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_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_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); QMenu* submenu = nullptr; 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_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); } + //for ttest statistics; + m_columnHypothesisTestingMenu = new QMenu("Hypothesis Testing",this); + m_columnHypothesisTestingMenu->addAction(action_do_ttest); + //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")); 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")); 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(); - //for ttest statistics; - m_columnHypothesisTestingMenu = new QMenu("Hypothesis Testing",this); - m_columnHypothesisTestingMenu->addAction(action_do_ttest); - m_columnMenu->addMenu(m_columnHypothesisTestingMenu); - 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_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->addSeparator(); + m_spreadsheetMenu->addMenu(m_columnHypothesisTestingMenu); + 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_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_ttest, &QAction::triggered, this, &SpreadsheetView::doTTest); connect(action_insert_column_left, &QAction::triggered, this, &SpreadsheetView::insertColumnLeft); connect(action_insert_column_right, &QAction::triggered, this, &SpreadsheetView::insertColumnRight); 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_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 const bool numeric = (column->columnMode() == AbstractColumn::Numeric) || (column->columnMode() == AbstractColumn::Integer); if (numeric) { 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::doTTest() { - TTest* ttest = new TTest(i18n("doing T Test for %1", m_spreadsheet->name())); - ttest->setColumns(this->selectedColumns()); - ttest->performTwoSampleTest(); + HypothesisTest* htest = new HypothesisTest(i18n("T Test for %1", m_spreadsheet->name())); +// htest->setColumns(this->selectedColumns()); +// htest->performTwoSampleTTest(); + htest->setDataSourceType(HypothesisTest::DataSourceSpreadsheet); + htest->setDataSourceSpreadsheet(m_spreadsheet); + m_spreadsheet->parentAspect()->addChild(htest); } 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() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty column", m_spreadsheet->name())); Column* newCol = new Column("1", AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); const int first = firstSelectedColumn(); if (first >= 0) { //determine the first selected column Column* firstCol = m_spreadsheet->child(first); //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); 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); 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() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty column", m_spreadsheet->name())); Column* newCol = new Column("1", AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); const int last = lastSelectedColumn(); if (last >= 0) { newCol->insertRows(0, m_spreadsheet->rowCount()); if (last < m_spreadsheet->columnCount() - 1) { //determine the column next to the last selected column Column* nextCol = m_spreadsheet->child(last + 1); //insert the new column before the column next to the last selected column m_spreadsheet->insertChildBefore(newCol, nextCol); } else { //last column selected, no next column available -> add/append a new column m_spreadsheet->addChild(newCol); } } else { if (m_spreadsheet->columnCount()>0) { //columns available but no columns selected -> append the new column at the very end newCol->insertRows(0, m_spreadsheet->rowCount()); 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); newCol->insertRows(0, rows); m_spreadsheet->setRowCount(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() { int first = firstSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty rows", m_spreadsheet->name())); m_spreadsheet->insertRows(first, 1); m_spreadsheet->endMacro(); RESET_CURSOR; } /*! Insert an empty row below the last selected row */ void SpreadsheetView::insertRowBelow() { int last = lastSelectedRow(); if (last < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty rows", m_spreadsheet->name())); if (last < m_spreadsheet->rowCount() -1) m_spreadsheet->insertRows(last + 1, 1); //insert before the next to the last selected row else m_spreadsheet->appendRow(); //append one row 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::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 b574604f2..6f33ce575 100644 --- a/src/kdefrontend/GuiObserver.cpp +++ b/src/kdefrontend/GuiObserver.cpp @@ -1,660 +1,674 @@ /*************************************************************************** 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/hypothesis_test/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/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 */ GuiObserver::GuiObserver(MainWin* mainWin) { connect(mainWin->m_projectExplorer, SIGNAL(selectedAspectsChanged(QList&)), this, SLOT(selectedAspectsChanged(QList&)) ); connect(mainWin->m_projectExplorer, SIGNAL(hiddenAspectSelected(const AbstractAspect*)), this, SLOT(hiddenAspectSelected(const AbstractAspect*)) ); 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; } QString prevClassName, className; //check, whether objects of different types where selected //don't show any dock widgets in this case. for (auto* aspect : selectedAspects) { className = aspect->metaObject()->className(); if (className != prevClassName && !prevClassName.isEmpty()) { clearDock(); return; } prevClassName = className; } if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->show(); if (className == QStringLiteral("Spreadsheet")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet")); if (!m_mainWindow->spreadsheetDock) { m_mainWindow->spreadsheetDock = new SpreadsheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->spreadsheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->spreadsheetDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->spreadsheetDock->setSpreadsheets(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->spreadsheetDock); } else if (className == QStringLiteral("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); - } else if (className == QStringLiteral("Column")) { + } else if (className == QStringLiteral("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); + } else if (className == QStringLiteral("Column")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column")); if (!m_mainWindow->columnDock) { m_mainWindow->columnDock = new ColumnDock(m_mainWindow->stackedWidget); connect(m_mainWindow->columnDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->columnDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->columnDock->setColumns(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->columnDock); } else if (className == QStringLiteral("Matrix")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix")); if (!m_mainWindow->matrixDock) { m_mainWindow->matrixDock = new MatrixDock(m_mainWindow->stackedWidget); connect(m_mainWindow->matrixDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->matrixDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->matrixDock->setMatrices(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->matrixDock); } else if (className == QStringLiteral("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); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->worksheetDock->setWorksheets(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->worksheetDock); } else if (className == QStringLiteral("CartesianPlot")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot")); if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cartesianPlotDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->cartesianPlotDock->setPlots(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cartesianPlotDock); } else if (className == QStringLiteral("CartesianPlotLegend")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend")); if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cartesianPlotLegendDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->cartesianPlotLegendDock->setLegends(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cartesianPlotLegendDock); } else if (className == QStringLiteral("Axis")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis")); if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); connect(m_mainWindow->axisDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->axisDock->setAxes(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->axisDock); } else if (className == QStringLiteral("XYCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve")); if (!m_mainWindow->xyCurveDock) { m_mainWindow->xyCurveDock = new XYCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyCurveDock->setupGeneral(); connect(m_mainWindow->xyCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyCurveDock); } else if (className == QStringLiteral("XYEquationCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation")); if (!m_mainWindow->xyEquationCurveDock) { m_mainWindow->xyEquationCurveDock = new XYEquationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyEquationCurveDock->setupGeneral(); connect(m_mainWindow->xyEquationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyEquationCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyEquationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyEquationCurveDock); } else if (className == QStringLiteral("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, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyDataReductionCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock); } else if (className == QStringLiteral("XYDifferentiationCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation")); if (!m_mainWindow->xyDifferentiationCurveDock) { m_mainWindow->xyDifferentiationCurveDock = new XYDifferentiationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyDifferentiationCurveDock->setupGeneral(); connect(m_mainWindow->xyDifferentiationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDifferentiationCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyDifferentiationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDifferentiationCurveDock); } else if (className == QStringLiteral("XYIntegrationCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration")); if (!m_mainWindow->xyIntegrationCurveDock) { m_mainWindow->xyIntegrationCurveDock = new XYIntegrationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyIntegrationCurveDock->setupGeneral(); connect(m_mainWindow->xyIntegrationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyIntegrationCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyIntegrationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyIntegrationCurveDock); } else if (className == QStringLiteral("XYInterpolationCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation")); if (!m_mainWindow->xyInterpolationCurveDock) { m_mainWindow->xyInterpolationCurveDock = new XYInterpolationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyInterpolationCurveDock->setupGeneral(); connect(m_mainWindow->xyInterpolationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyInterpolationCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyInterpolationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyInterpolationCurveDock); } else if (className == QStringLiteral("XYSmoothCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing")); if (!m_mainWindow->xySmoothCurveDock) { m_mainWindow->xySmoothCurveDock = new XYSmoothCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xySmoothCurveDock->setupGeneral(); connect(m_mainWindow->xySmoothCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xySmoothCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xySmoothCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xySmoothCurveDock); } else if (className == QStringLiteral("XYFitCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit")); if (!m_mainWindow->xyFitCurveDock) { m_mainWindow->xyFitCurveDock = new XYFitCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFitCurveDock->setupGeneral(); connect(m_mainWindow->xyFitCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFitCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFitCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFitCurveDock); } else if (className == QStringLiteral("XYFourierTransformCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform")); if (!m_mainWindow->xyFourierTransformCurveDock) { m_mainWindow->xyFourierTransformCurveDock = new XYFourierTransformCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFourierTransformCurveDock->setupGeneral(); connect(m_mainWindow->xyFourierTransformCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFourierTransformCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFourierTransformCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFourierTransformCurveDock); } else if (className == QStringLiteral("XYFourierFilterCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter")); if (!m_mainWindow->xyFourierFilterCurveDock) { m_mainWindow->xyFourierFilterCurveDock = new XYFourierFilterCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFourierFilterCurveDock->setupGeneral(); connect(m_mainWindow->xyFourierFilterCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFourierFilterCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFourierFilterCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFourierFilterCurveDock); } else if (className == QStringLiteral("XYConvolutionCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Convolution/Deconvolution")); if (!m_mainWindow->xyConvolutionCurveDock) { m_mainWindow->xyConvolutionCurveDock = new XYConvolutionCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyConvolutionCurveDock->setupGeneral(); connect(m_mainWindow->xyConvolutionCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyConvolutionCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyConvolutionCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyConvolutionCurveDock); } else if (className == QStringLiteral("XYCorrelationCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Auto-/Cross-Correlation")); if (!m_mainWindow->xyCorrelationCurveDock) { m_mainWindow->xyCorrelationCurveDock = new XYCorrelationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyCorrelationCurveDock->setupGeneral(); connect(m_mainWindow->xyCorrelationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyCorrelationCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyCorrelationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyCorrelationCurveDock); } else if (className == QStringLiteral("Histogram")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties")); if (!m_mainWindow->histogramDock) { m_mainWindow->histogramDock = new HistogramDock(m_mainWindow->stackedWidget); connect(m_mainWindow->histogramDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->histogramDock); } QList list; for (auto* aspect : selectedAspects) list<(aspect); m_mainWindow->histogramDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->histogramDock); } else if (className == QStringLiteral("TextLabel")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label")); if (!m_mainWindow->textLabelDock) { m_mainWindow->textLabelDock = new LabelWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->textLabelDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->textLabelDock->setLabels(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->textLabelDock); } else if (className == QStringLiteral("CustomPoint")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point")); if (!m_mainWindow->customPointDock) { m_mainWindow->customPointDock = new CustomPointDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->customPointDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->customPointDock->setPoints(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->customPointDock); } else if (className == QStringLiteral("DatapickerCurve")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve")); if (!m_mainWindow->datapickerCurveDock) { m_mainWindow->datapickerCurveDock = new DatapickerCurveWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->datapickerCurveDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->datapickerCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->datapickerCurveDock); } else if (className == QStringLiteral("Datapicker")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker")); if (!m_mainWindow->datapickerImageDock) { m_mainWindow->datapickerImageDock = new DatapickerImageWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->datapickerImageDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect)->image(); m_mainWindow->datapickerImageDock->setImages(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->datapickerImageDock); } else if (className == QStringLiteral("Project")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project")); if (!m_mainWindow->projectDock) { m_mainWindow->projectDock = new ProjectDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->projectDock); } m_mainWindow->projectDock->setProject(m_mainWindow->m_project); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->projectDock); } else if (className == QStringLiteral("CantorWorksheet")) { #ifdef HAVE_CANTOR_LIBS if (!m_mainWindow->cantorWorksheetDock) { m_mainWindow->cantorWorksheetDock = new CantorWorksheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cantorWorksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cantorWorksheetDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); 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); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cantorWorksheetDock); #endif } else if (className == QStringLiteral("Notes")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes")); if (!m_mainWindow->notesDock) { m_mainWindow->notesDock = new NoteDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->notesDock); } QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect); m_mainWindow->notesDock->setNotesList(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->notesDock); } #ifdef HAVE_MQTT else if (className == QStringLiteral("MQTTClient")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18n("MQTT Data Source")); if (!m_mainWindow->m_liveDataDock) { qDebug()<<"new live data dock"; m_mainWindow->m_liveDataDock = new LiveDataDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->m_liveDataDock); } m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->m_liveDataDock); } else if (className == QStringLiteral("MQTTSubscription")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18n("MQTT Data Source")); if (!m_mainWindow->m_liveDataDock) { qDebug()<<"new live data dock"; m_mainWindow->m_liveDataDock = new LiveDataDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->m_liveDataDock); } m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->m_liveDataDock); } else if (className == QStringLiteral("MQTTTopic")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18n("MQTT Data Source")); if (!m_mainWindow->m_liveDataDock) { m_mainWindow->m_liveDataDock = new LiveDataDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->m_liveDataDock); } m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->m_liveDataDock); } #endif else if (className == QStringLiteral("LiveDataSource")) { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source")); if (!m_mainWindow->m_liveDataDock) { m_mainWindow->m_liveDataDock = new LiveDataDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->m_liveDataDock); } m_mainWindow->m_liveDataDock->setLiveDataSource(static_cast(selectedAspects.first())); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->m_liveDataDock); } else { clearDock(); } } /*! 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; QString className = parent->metaObject()->className(); if (className == QStringLiteral("Axis")) { if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } m_mainWindow->axisDock->activateTitleTab(); } else if (className == QStringLiteral("CartesianPlot")) { if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } m_mainWindow->cartesianPlotDock->activateTitleTab(); } else if (className == QStringLiteral("CartesianPlotLegend")) { if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } m_mainWindow->cartesianPlotLegendDock->activateTitleTab(); } } diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index 5d7976d54..42c5de31d 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,2016 +1,2031 @@ /*************************************************************************** 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/hypothesis_test/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" #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 "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; } 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().first()); 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(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 for adding new aspects m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("document-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")); 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) 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); //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); 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; delete m_aspectTreeModel; m_aspectTreeModel = nullptr; delete m_project; m_project = nullptr; m_currentFileName.clear(); //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(); } 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); QXmlStreamWriter writer(file); m_project->save(&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("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("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); } } 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 { menu->addMenu(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->addMenu(m_visibilityMenu); } /*! 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("Spreadsheet") || m_currentAspect->inherits("Matrix") || m_currentAspect->inherits("Workbook")) dlg->setCurrentIndex( m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits("Column")) { if (m_currentAspect->parentAspect()->inherits("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("Spreadsheet") || m_currentAspect->inherits("Matrix") || m_currentAspect->inherits("Workbook")) dlg->setCurrentIndex( m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits("Column")) { if (m_currentAspect->parentAspect()->inherits("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/MainWin.h b/src/kdefrontend/MainWin.h index 88264ea18..5a600f6a0 100644 --- a/src/kdefrontend/MainWin.h +++ b/src/kdefrontend/MainWin.h @@ -1,314 +1,319 @@ /*************************************************************************** File : MainWin.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) Description : Main window of the application ***************************************************************************/ /*************************************************************************** * * * 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 MAINWIN_H #define MAINWIN_H #include #include class AbstractAspect; class AspectTreeModel; class Folder; class ProjectExplorer; class Project; class Worksheet; class Note; class Workbook; class Datapicker; +class HypothesisTest; class Image; class Spreadsheet; class Matrix; class PivotTable; class GuiObserver; class AxisDock; class NoteDock; class CartesianPlotDock; class HistogramDock; class BarChartPlotDock; class CartesianPlotLegendDock; class CustomPointDock; class ColumnDock; +class HypothesisTestDock; class LiveDataDock; class MatrixDock; class PivotTableDock; class ProjectDock; class SpreadsheetDock; class XYCurveDock; class XYEquationCurveDock; class XYDataReductionCurveDock; class XYDifferentiationCurveDock; class XYIntegrationCurveDock; class XYInterpolationCurveDock; class XYSmoothCurveDock; class XYFitCurveDock; class XYFourierFilterCurveDock; class XYFourierTransformCurveDock; class XYConvolutionCurveDock; class XYCorrelationCurveDock; class WorksheetDock; class LabelWidget; class DatapickerImageWidget; class DatapickerCurveWidget; class MemoryWidget; #ifdef HAVE_CANTOR_LIBS class CantorWorksheet; class CantorWorksheetDock; #endif class QDockWidget; class QStackedWidget; class QDragEnterEvent; class QDropEvent; class QMdiArea; class QMdiSubWindow; class QToolButton; class KRecentFilesAction; class MainWin : public KXmlGuiWindow { Q_OBJECT public: explicit MainWin(QWidget* parent = nullptr, const QString& filename = nullptr); ~MainWin() override; void showPresenter(); AspectTreeModel* model() const; Project* project() const; void addAspectToProject(AbstractAspect*); private: QMdiArea* m_mdiArea; QMdiSubWindow* m_currentSubWindow{nullptr}; Project* m_project{nullptr}; AspectTreeModel* m_aspectTreeModel{nullptr}; ProjectExplorer* m_projectExplorer{nullptr}; QDockWidget* m_projectExplorerDock{nullptr}; QDockWidget* m_propertiesDock{nullptr}; AbstractAspect* m_currentAspect{nullptr}; Folder* m_currentFolder{nullptr}; QString m_currentFileName; QString m_undoViewEmptyLabel; bool m_suppressCurrentSubWindowChangedEvent{false}; bool m_closing{false}; bool m_autoSaveActive{false}; QTimer m_autoSaveTimer; bool m_showMemoryInfo{true}; MemoryWidget* m_memoryInfoWidget{nullptr}; Qt::WindowStates m_lastWindowState; //< last window state before switching to full screen mode KRecentFilesAction* m_recentProjectsAction; QAction* m_saveAction; QAction* m_saveAsAction; QAction* m_printAction; QAction* m_printPreviewAction; QAction* m_importFileAction; QAction* m_importSqlAction; QAction* m_importLabPlotAction; QAction* m_importOpjAction; QAction* m_exportAction; QAction* m_closeAction; QAction* m_newFolderAction; + QAction* m_newHypothesisTestAction; QAction* m_newWorkbookAction; QAction* m_newSpreadsheetAction; QAction* m_newMatrixAction; QAction* m_newPivotTableAction; QAction* m_newWorksheetAction; QAction* m_newNotesAction; QAction* m_newLiveDataSourceAction; QAction* m_newSqlDataSourceAction; QAction* m_newScriptAction; QAction* m_newProjectAction; QAction* m_historyAction; QAction* m_undoAction; QAction* m_redoAction; QAction* m_tileWindows; QAction* m_cascadeWindows; QAction* m_newDatapickerAction; QAction* m_editFitsFileAction; //toggling doch widgets QAction* m_toggleProjectExplorerDocQAction; QAction* m_togglePropertiesDocQAction; //worksheet actions QAction* worksheetZoomInAction; QAction* worksheetZoomOutAction; QAction* worksheetZoomOriginAction; QAction* worksheetZoomFitPageHeightAction; QAction* worksheetZoomFitPageWidthAction; QAction* worksheetZoomFitSelectionAction; QAction* worksheetNavigationModeAction; QAction* worksheetZoomModeAction; QAction* worksheetSelectionModeAction; QAction* worksheetVerticalLayoutAction; QAction* worksheetHorizontalLayoutAction; QAction* worksheetGridLayoutAction; QAction* worksheetBreakLayoutAction; QAction* m_visibilityFolderAction; QAction* m_visibilitySubfolderAction; QAction* m_visibilityAllAction; QAction* m_toggleProjectExplorerDockAction; QAction* m_togglePropertiesDockAction; //Menus QMenu* m_visibilityMenu{nullptr}; QMenu* m_newMenu{nullptr}; QMenu* m_importMenu; QMenu* m_editMenu{nullptr}; //Docks QStackedWidget* stackedWidget; AxisDock* axisDock{nullptr}; NoteDock* notesDock{nullptr}; CartesianPlotDock* cartesianPlotDock{nullptr}; CartesianPlotLegendDock* cartesianPlotLegendDock{nullptr}; ColumnDock* columnDock{nullptr}; + HypothesisTestDock* hypothesisTestDock{nullptr}; LiveDataDock* m_liveDataDock{nullptr}; MatrixDock* matrixDock{nullptr}; PivotTableDock* pivotTableDock{nullptr}; SpreadsheetDock* spreadsheetDock{nullptr}; ProjectDock* projectDock{nullptr}; XYCurveDock* xyCurveDock{nullptr}; XYEquationCurveDock* xyEquationCurveDock{nullptr}; XYDataReductionCurveDock* xyDataReductionCurveDock{nullptr}; XYDifferentiationCurveDock* xyDifferentiationCurveDock{nullptr}; XYIntegrationCurveDock* xyIntegrationCurveDock{nullptr}; XYInterpolationCurveDock* xyInterpolationCurveDock{nullptr}; XYSmoothCurveDock* xySmoothCurveDock{nullptr}; XYFitCurveDock* xyFitCurveDock{nullptr}; XYFourierFilterCurveDock* xyFourierFilterCurveDock{nullptr}; XYFourierTransformCurveDock* xyFourierTransformCurveDock{nullptr}; XYConvolutionCurveDock* xyConvolutionCurveDock{nullptr}; XYCorrelationCurveDock* xyCorrelationCurveDock{nullptr}; HistogramDock* histogramDock{nullptr}; WorksheetDock* worksheetDock{nullptr}; LabelWidget* textLabelDock{nullptr}; CustomPointDock* customPointDock{nullptr}; DatapickerImageWidget* datapickerImageDock{nullptr}; DatapickerCurveWidget* datapickerCurveDock{nullptr}; void initActions(); void initMenus(); bool warnModified(); void activateSubWindowForAspect(const AbstractAspect*) const; bool save(const QString&); // void toggleShowWidget(QWidget* widget, bool showToRight); // void toggleHideWidget(QWidget* widget, bool hideToLeft); Workbook* activeWorkbook() const; Spreadsheet* activeSpreadsheet() const; Matrix* activeMatrix() const; Worksheet* activeWorksheet() const; Datapicker* activeDatapicker() const; //Cantor #ifdef HAVE_CANTOR_LIBS QMenu* m_newCantorWorksheetMenu; CantorWorksheetDock* cantorWorksheetDock{nullptr}; CantorWorksheet* activeCantorWorksheet() const; #endif friend class GuiObserver; GuiObserver* m_guiObserver{nullptr}; protected: void closeEvent(QCloseEvent*) override; void dragEnterEvent(QDragEnterEvent*) override; void dropEvent(QDropEvent*) override; private slots: void initGUI(const QString&); void updateGUI(); void updateGUIOnProjectChanges(); void undo(); void redo(); bool newProject(); void openProject(); void openProject(const QString&); void openRecentProject(const QUrl&); bool closeProject(); bool saveProject(); bool saveProjectAs(); void autoSaveProject(); void print(); void printPreview(); void historyDialog(); void importFileDialog(const QString& fileName = QString()); void importSqlDialog(); void importProjectDialog(); void exportDialog(); void editFitsFileDialog(); void settingsDialog(); void projectChanged(); void colorSchemeChanged(QAction*); //Cantor #ifdef HAVE_CANTOR_LIBS void newCantorWorksheet(QAction* action); void cantorSettingsDialog(); #endif void newFolder(); + void newHypothesisTest(); void newWorkbook(); void newSpreadsheet(); void newMatrix(); void newPivotTable(); void newWorksheet(); void newNotes(); void newDatapicker(); //TODO: void newScript(); void newLiveDataSourceActionTriggered(); void createContextMenu(QMenu*) const; void createFolderContextMenu(const Folder*, QMenu*) const; void handleAspectAdded(const AbstractAspect*); void handleAspectAboutToBeRemoved(const AbstractAspect*); void handleAspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*); void handleCurrentAspectChanged(AbstractAspect* ); void handleCurrentSubWindowChanged(QMdiSubWindow*); void handleShowSubWindowRequested(); void handleSettingsChanges(); void setMdiWindowVisibility(QAction*); void updateMdiWindowVisibility() const; void toggleDockWidget(QAction*); void toggleFullScreen(); void projectExplorerDockVisibilityChanged(bool); void propertiesDockVisibilityChanged(bool); }; #endif diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp new file mode 100644 index 000000000..0e86cbc87 --- /dev/null +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp @@ -0,0 +1,480 @@ +/*************************************************************************** + File : HypothesisTestDock.cpp + Project : LabPlot + Description : widget for hypothesis test properties + -------------------------------------------------------------------- + Copyright : (C) 2019 by 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 "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/pivot/hypothesisTestView.h" +#include "kdefrontend/TemplateHandler.h" + +#include +#include + +#include +#include +#include +#include + /*! + \class HypothesisTestDock + \brief Provides a dock (widget) for hypothesis testing: + \ingroup kdefrontend +*/ + +HypothesisTestDock::HypothesisTestDock(QWidget* parent) : QWidget(parent) { + ui.setupUi(this); + + ui.cbDataSourceType->addItem(i18n("Spreadsheet")); + ui.cbDataSourceType->addItem(i18n("Database")); + + cbSpreadsheet = new TreeViewComboBox; + ui.gridLayout->addWidget(cbSpreadsheet, 5, 3, 1, 4); + + ui.bDatabaseManager->setIcon(QIcon::fromTheme("network-server-database")); + ui.bDatabaseManager->setToolTip(i18n("Manage connections")); + m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + "sql_connections"; +// 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.pbPerformTest, &QPushButton::clicked, this, &HypothesisTestDock::doTTest); +} + +void HypothesisTestDock::setHypothesisTest(HypothesisTest* HypothesisTest) { +// m_initializing = true; + m_hypothesisTest = HypothesisTest; + +//// m_aspectTreeModel = new AspectTreeModel(m_hypothesisTest->project()); + +// QList list; +// list << "Folder" << "Workbook" << "Spreadsheet" << "LiveDataSource"; +// cbSpreadsheet->setTopLevelClasses(list); + +// list.clear(); +// list << "Spreadsheet" << "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::DataSourceSpreadsheet) +// setModelIndexFromAspect(cbSpreadsheet, m_hypothesisTest->dataSourceSpreadsheet()); +// else +// ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(m_hypothesisTest->dataSourceConnection())); + + + //clearing all cbCol* + ui.cbCol1->clear(); + ui.cbCol2->clear(); + for (auto* col : m_hypothesisTest->dataSourceSpreadsheet()->children()) { + ui.cbCol1->addItem(col->name()); + ui.cbCol2->addItem(col->name()); + } + + this->dataSourceTypeChanged(ui.cbDataSourceType->currentIndex()); + + // setting rows and columns in combo box; + + + // //available dimensions and measures +// ui.lwFields->clear(); +// for (auto dimension : m_hypothesisTest->dimensions()) +// ui.lwFields->addItem(new QListWidgetItem(QIcon::fromTheme("draw-text"), dimension)); + +// for (auto measure : m_hypothesisTest->measures()) +// ui.lwFields->addItem(new QListWidgetItem(measure)); + +// //undo functions +// connect(m_hypothesisTest, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(hypothesisTestDescriptionChanged(const AbstractAspect*))); +// //TODO: + +// m_initializing = false; +} + +void HypothesisTestDock::doTTest() { + QStringList cols; + cols << ui.cbCol1->currentText(); + cols << ui.cbCol2->currentText(); + m_hypothesisTest->setColumns(cols); + m_hypothesisTest->performTwoSampleTTest(); +} + +//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) { + HypothesisTest::DataSourceType type = (HypothesisTest::DataSourceType)index; + bool showDatabase = (type == HypothesisTest::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) { + auto* aspect = static_cast(index.internalPointer()); + Spreadsheet* spreadsheet = dynamic_cast(aspect); + + //clear the previous definitions of combo-box columns + ui.cbCol1->clear(); + ui.cbCol2->clear(); + //TODO: + //rows, columns, values + + //show all spreadsheet columns as available dimensions + for (const auto* col : spreadsheet->children()) { + ui.cbCol1->addItem(col->name()); + ui.cbCol2->addItem(col->name()); + } + + m_hypothesisTest->setDataSourceSpreadsheet(spreadsheet); +} + + +//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), +// 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 Matrix ********* +////************************************************************* +//void HypothesisTestDock::hypothesisTestDescriptionChanged(const AbstractAspect* aspect) { +// 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); +//} + diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.h b/src/kdefrontend/dockwidgets/HypothesisTestDock.h new file mode 100644 index 000000000..6aa8e8cf1 --- /dev/null +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.h @@ -0,0 +1,94 @@ +/*************************************************************************** + File : HypothesisTestDock.h + Project : LabPlot + Description : widget for hypothesis testing properties + -------------------------------------------------------------------- + Copyright : (C) 2019 by 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 * + * * + ***************************************************************************/ + +#ifndef HYPOTHESISTESTDOCK_H +#define HYPOTHESISTESTDOCK_H + +#include "backend/hypothesis_test/HypothesisTest.h" +#include "ui_hypothesistestdock.h" +#include + +class AbstractAspect; +class AspectTreeModel; +class HypothesisTest; +class TreeViewComboBox; +class KConfig; + +class HypothesisTestDock : public QWidget { + Q_OBJECT + +public: + explicit HypothesisTestDock(QWidget*); + void setHypothesisTest(HypothesisTest*); + +private slots: + +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; + +// void load(); +// void loadConfig(KConfig&); +// void setModelIndexFromAspect(TreeViewComboBox*, const AbstractAspect*); +// void readConnections(); +// void updateFields(); +// bool fieldSelected(const QString&); + +private slots: + //SLOTs for changes triggered in PivotTableDock +// void nameChanged(); +// void commentChanged(); + void dataSourceTypeChanged(int); + void doTTest(); + void spreadsheetChanged(const QModelIndex&); +// void connectionChanged(); +// void tableChanged(); +// void showDatabaseManager(); + +// //SLOTs for changes triggered in PivotTable +// void pivotTableDescriptionChanged(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/dockwidgets/hypothesistestdock.ui b/src/kdefrontend/dockwidgets/hypothesistestdock.ui new file mode 100644 index 000000000..33066a4c3 --- /dev/null +++ b/src/kdefrontend/dockwidgets/hypothesistestdock.ui @@ -0,0 +1,71 @@ + + + + + HypothesisTestDock + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + HypothesisTestDock + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + HypothesisTestDock + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/kdefrontend/ui/dockwidgets/hypothesistestdock.cpp b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.cpp new file mode 100644 index 000000000..50c5f8d54 --- /dev/null +++ b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.cpp @@ -0,0 +1,14 @@ +#include "hypothesistestdock.h" +#include "ui_hypothesistestdock.h" + +HypothesisTestDock::HypothesisTestDock(QWidget *parent) : + QDialog(parent), + ui(new Ui::HypothesisTestDock) +{ + ui->setupUi(this); +} + +HypothesisTestDock::~HypothesisTestDock() +{ + delete ui; +} diff --git a/src/kdefrontend/ui/dockwidgets/hypothesistestdock.h b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.h new file mode 100644 index 000000000..c8c839fa9 --- /dev/null +++ b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.h @@ -0,0 +1,22 @@ +#ifndef HYPOTHESISTESTDOCK_H +#define HYPOTHESISTESTDOCK_H + +#include + +namespace Ui { +class HypothesisTestDock; +} + +class HypothesisTestDock : public QDialog +{ + Q_OBJECT + +public: + explicit HypothesisTestDock(QWidget *parent = nullptr); + ~HypothesisTestDock(); + +private: + Ui::HypothesisTestDock *ui; +}; + +#endif // HYPOTHESISTESTDOCK_H diff --git a/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui new file mode 100644 index 000000000..3f1f1b855 --- /dev/null +++ b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui @@ -0,0 +1,295 @@ + + + HypothesisTestDock + + + + 0 + 0 + 588 + 996 + + + + Form + + + + + + + Tests + + + + + T Test + + + + Two Sample Independent + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 13 + + + + + + + + Source: + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 23 + + + + + + + + Table: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Comment: + + + + + + + Connection: + + + + + + + Spreadsheet: + + + + + + + + 75 + true + + + + Data + + + + + + + + 75 + true + + + + Structure: + + + + + + + + + + + 0 + 0 + + + + Name: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Column 1 + + + Qt::AlignCenter + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + Column 2 + + + Qt::AlignCenter + + + + + + + OK + + + + + + + + diff --git a/ttest.diff b/ttest.diff new file mode 100644 index 000000000..c29b0bd33 --- /dev/null +++ b/ttest.diff @@ -0,0 +1,275 @@ +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 1c39d438f..9eea05497 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -236,6 +236,7 @@ set(BACKEND_SOURCES + ${BACKEND_DIR}/matrix/matrixcommands.cpp + ${BACKEND_DIR}/matrix/MatrixModel.cpp + ${BACKEND_DIR}/pivot/PivotTable.cpp ++ ${BACKEND_DIR}/t_test/ttest.cpp + ${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp + ${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp + ${BACKEND_DIR}/lib/XmlStreamReader.cpp +diff --git a/src/backend/t_test/ttest.cpp b/src/backend/t_test/ttest.cpp +new file mode 100644 +index 000000000..b0365b042 +--- /dev/null ++++ b/src/backend/t_test/ttest.cpp +@@ -0,0 +1,134 @@ ++#include "ttest.h" ++#include "backend/spreadsheet/Spreadsheet.h" ++#include "backend/core/column/Column.h" ++//#include "commonfrontend/spreadsheet/SpreadsheetView.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++TTest::TTest(const QString &name) ++{ ++ Q_UNUSED(name); ++} ++ ++void TTest::setDataSourceSpreadsheet(Spreadsheet *spreadsheet) ++{ ++ dataSourceSpreadsheet = spreadsheet; ++ ++ m_rows = dataSourceSpreadsheet->rowCount(); ++ m_columns = dataSourceSpreadsheet->columnCount(); ++ qDebug() << "in ttest::setDataSourceSpreadsheet"; ++ ++ // now finding the number of columns and rows; ++ qDebug() << "row count is " << m_rows; ++ qDebug() << "row count is " << m_columns; ++ qDebug() << "exiting ttest::setDataSourceSpreadsheet"; ++} ++ ++void TTest::setColumns(QVector cols) ++{ ++ columns = cols; ++ return; ++} ++ ++void TTest::performTwoSampleTest() ++{ ++ QMessageBox* msg_box = new QMessageBox(); ++ // checking for cols; ++ if(columns.size() != 2) ++ { ++ msg_box->setText(QLatin1String("Inappropriate number of columns selected")); ++ msg_box->exec(); ++ return; ++ } ++ ++ bool modeOk = true; ++ for(int i=0; i<2; i++) ++ { ++ if(columns[0]->columnMode() == AbstractColumn::Numeric || columns[i]->columnMode() == AbstractColumn::Integer) ++ continue; ++ modeOk = false; ++ } ++ ++ if(!modeOk) ++ { ++ msg_box->setText("select only columns with numbers"); ++ msg_box->exec(); ++ return; ++ } ++ ++ // use of three than two for human readiblity of code; ++ int n[3]; ++ double sum[3], mean[3], std[3]; ++ ++ for(int i=1; i<=2; i++) ++ { ++ findStats(columns[i-1], n[i], sum[i], mean[i], std[i]); ++ qDebug() << "for " << i; ++ qDebug() << "n is "<setText("atleast one of selected column empty"); ++ msg_box->exec(); ++ return; ++ } ++ } ++ int df = n[1] + n[2] - 2; ++ ++ //Assuming equal variance ++ double sp = qSqrt( ((n[1]-1)*qPow(std[1],2) + (n[2]-1)*qPow(std[2],2))/df); ++ ++ qDebug() << "sp is " << sp; ++ ++ double t = (mean[1] - mean[2])/(sp*qSqrt(1.0/n[1] + 1.0/n[2])); ++ QString text = QString("T value for test is %1").arg(t); ++ msg_box->setText(text); ++ msg_box->exec(); ++ return; ++ ++// double t_value = ++ ++} ++ ++void TTest::findStats(Column* column, int &count, double &sum, double &mean, double &std) ++{ ++ sum = 0; ++ mean = 0; ++ std = 0; ++ ++ count = column->rowCount(); ++ for(int i=0; ivalueAt(i); ++ if(std::isnan(row)) ++ { ++ count = i; ++ break; ++ } ++ sum += row; ++ } ++ ++ if(count < 1) return; ++ mean = sum/count; ++ ++ for(int i=0; ivalueAt(i); ++ std += qPow((row - mean),2); ++ } ++ ++ if(count > 1) ++ std = std/(count-1); ++ std = qSqrt(std); ++ return; ++} ++ ++ ++ +diff --git a/src/backend/t_test/ttest.h b/src/backend/t_test/ttest.h +new file mode 100644 +index 000000000..042332806 +--- /dev/null ++++ b/src/backend/t_test/ttest.h +@@ -0,0 +1,28 @@ ++#ifndef TTEST_H ++#define TTEST_H ++#include ++ ++ ++class Spreadsheet; ++class QString; ++class Column; ++ ++class TTest ++{ ++public: ++ explicit TTest(const QString& name); ++ void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); ++ void setColumns(QVector cols); ++ void performTwoSampleTest(); ++private: ++// double findMean(Column* col); ++// double findStandardDeviation(Column* col, double mean); ++ void findStats(Column* column, int &count, double &sum, double &mean, double &std); ++ ++ Spreadsheet* dataSourceSpreadsheet{nullptr}; ++ int m_rows{0}; ++ int m_columns{0}; ++ QVector columns; ++}; ++ ++#endif // TTEST_H +diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +index ef0717c15..bfb09046f 100644 +--- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp ++++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +@@ -43,6 +43,8 @@ + #include "backend/core/datatypes/DateTime2StringFilter.h" + #include "backend/core/datatypes/String2DateTimeFilter.h" + #include "backend/pivot/PivotTable.h" ++#include "backend/t_test/ttest.h" ++ + + #include + #include +@@ -220,6 +222,7 @@ void SpreadsheetView::initActions() { + 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_ttest = new QAction(i18n("T Test"),this); + + // column related actions + action_insert_column_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Column Left"), this); +@@ -471,6 +474,12 @@ void SpreadsheetView::initMenus() { + m_columnMenu->addMenu(m_columnManipulateDataMenu); + m_columnMenu->addSeparator(); + ++ //for ttest statistics; ++ m_columnHypotheisTestingMenu = new QMenu("Hypothesis Testing",this); ++ m_columnHypotheisTestingMenu->addAction(action_do_ttest); ++ m_columnMenu->addMenu(m_columnHypotheisTestingMenu); ++ 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); +@@ -558,6 +567,7 @@ void SpreadsheetView::connectActions() { + connect(action_go_to_cell, &QAction::triggered, this, + static_cast(&SpreadsheetView::goToCell)); + connect(action_pivot_table, &QAction::triggered, this, &SpreadsheetView::createPivotTable); ++ connect(action_do_ttest, &QAction::triggered, this, &SpreadsheetView::doTTest); + + connect(action_insert_column_left, &QAction::triggered, this, &SpreadsheetView::insertColumnLeft); + connect(action_insert_column_right, &QAction::triggered, this, &SpreadsheetView::insertColumnRight); +@@ -764,9 +774,17 @@ 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); ++ m_spreadsheet->parentAspect()->addChild(pivot); + } + ++void SpreadsheetView::doTTest() ++{ ++ TTest* ttest = new TTest(i18n("doing T Test for %1", m_spreadsheet->name())); ++ ttest->setColumns(this->selectedColumns()); ++ ttest->performTwoSampleTest(); ++} ++ ++ + void SpreadsheetView::handleHorizontalSectionMoved(int index, int from, int to) { + Q_UNUSED(index); + +diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.h b/src/commonfrontend/spreadsheet/SpreadsheetView.h +index b67df1252..4023a9bb6 100644 +--- a/src/commonfrontend/spreadsheet/SpreadsheetView.h ++++ b/src/commonfrontend/spreadsheet/SpreadsheetView.h +@@ -130,6 +130,7 @@ private: + QAction* action_go_to_cell; + QAction* action_statistics_all_columns; + QAction* action_pivot_table; ++ QAction* action_do_ttest; + + //column related actions + QAction* action_insert_column_left; +@@ -192,6 +193,7 @@ private: + QMenu* m_spreadsheetMenu; + QMenu* m_plotDataMenu; + QMenu* m_analyzePlotMenu; ++ QMenu* m_columnHypotheisTestingMenu{nullptr}; + + public slots: + void createContextMenu(QMenu*); +@@ -206,6 +208,7 @@ private slots: + void goToPreviousColumn(); + void goToCell(); + void createPivotTable(); ++ void doTTest(); + void sortSpreadsheet(); + void sortDialog(QVector); + diff --git a/ttest2.diff b/ttest2.diff new file mode 100644 index 000000000..952e6657c --- /dev/null +++ b/ttest2.diff @@ -0,0 +1,260 @@ +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 1c39d438f..0d00a555b 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -236,6 +236,7 @@ set(BACKEND_SOURCES + ${BACKEND_DIR}/matrix/matrixcommands.cpp + ${BACKEND_DIR}/matrix/MatrixModel.cpp + ${BACKEND_DIR}/pivot/PivotTable.cpp ++ ${BACKEND_DIR}/t_test/TTest.cpp + ${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp + ${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp + ${BACKEND_DIR}/lib/XmlStreamReader.cpp +diff --git a/src/backend/t_test/TTest.cpp b/src/backend/t_test/TTest.cpp +new file mode 100644 +index 000000000..96f53db98 +--- /dev/null ++++ b/src/backend/t_test/TTest.cpp +@@ -0,0 +1,120 @@ ++#include "TTest.h" ++#include "backend/spreadsheet/Spreadsheet.h" ++#include "backend/core/column/Column.h" ++//#include "commonfrontend/spreadsheet/SpreadsheetView.h" ++ ++#include ++#include ++#include ++#include ++ ++ ++TTest::TTest(const QString &name){ ++ Q_UNUSED(name); ++} ++ ++void TTest::setDataSourceSpreadsheet(Spreadsheet *spreadsheet){ ++ dataSourceSpreadsheet = spreadsheet; ++ ++ m_rows = dataSourceSpreadsheet->rowCount(); ++ m_columns = dataSourceSpreadsheet->columnCount(); ++ QDEBUG("in ttest::setDataSourceSpreadsheet"); ++ ++ // now finding the number of columns and rows; ++ QDEBUG("row count is " << m_rows); ++ QDEBUG("row count is " << m_columns); ++ QDEBUG("exiting ttest::setDataSourceSpreadsheet"); ++} ++ ++void TTest::setColumns(QVector cols){ ++ columns = cols; ++ return; ++} ++ ++void TTest::performTwoSampleTest(){ ++ QMessageBox* msg_box = new QMessageBox(); ++ // checking for cols; ++ if(columns.size() != 2){ ++ msg_box->setText(i18n("Inappropriate number of columns selected")); ++ msg_box->exec(); ++ return; ++ } ++ ++ bool modeOk = true; ++ for (int i = 0; i < 2; i++){ ++ if(columns[0]->columnMode() == AbstractColumn::Numeric || columns[i]->columnMode() == AbstractColumn::Integer) ++ continue; ++ modeOk = false; ++ } ++ ++ if(!modeOk){ ++ msg_box->setText(i18n("select only columns with numbers")); ++ msg_box->exec(); ++ return; ++ } ++ ++ // use of three than two for human readiblity of code; ++ int n[3]; ++ double sum[3], mean[3], std[3]; ++ ++ for (int i = 1; i <= 2; i++) { ++ findStats(columns[i-1], n[i], sum[i], mean[i], std[i]); ++ QDEBUG("for " << i); ++ QDEBUG("n is "<setText(i18n("atleast one of selected column empty")); ++ msg_box->exec(); ++ return; ++ } ++ } ++ int df = n[1] + n[2] - 2; ++ ++ //Assuming equal variance ++ double sp = qSqrt( ((n[1]-1)*qPow(std[1],2) + (n[2]-1)*qPow(std[2],2))/df); ++ ++ QDEBUG("sp is " << sp); ++ ++ double t = (mean[1] - mean[2])/(sp*qSqrt(1.0/n[1] + 1.0/n[2])); ++ QString text = i18n("T value for test is %0").arg(t); ++ msg_box->setText(text); ++ msg_box->exec(); ++ return; ++ ++// double t_value = ++ ++} ++ ++void TTest::findStats(Column* column, int &count, double &sum, double &mean, double &std) { ++ sum = 0; ++ mean = 0; ++ std = 0; ++ ++ count = column->rowCount(); ++ for(int i=0; ivalueAt(i); ++ if(std::isnan(row)) { ++ count = i; ++ break; ++ } ++ sum += row; ++ } ++ ++ if(count < 1) return; ++ mean = sum/count; ++ ++ for(int i=0; ivalueAt(i); ++ std += qPow((row - mean),2); ++ } ++ ++ if(count > 1) ++ std = std/(count-1); ++ std = qSqrt(std); ++ return; ++} ++ ++ ++ +diff --git a/src/backend/t_test/TTest.h b/src/backend/t_test/TTest.h +new file mode 100644 +index 000000000..fb6ed43ab +--- /dev/null ++++ b/src/backend/t_test/TTest.h +@@ -0,0 +1,27 @@ ++#ifndef TTEST_H ++#define TTEST_H ++#include ++ ++ ++class Spreadsheet; ++class QString; ++class Column; ++ ++class TTest{ ++public: ++ explicit TTest(const QString& name); ++ void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); ++ void setColumns(QVector cols); ++ void performTwoSampleTest(); ++private: ++// double findMean(Column* col); ++// double findStandardDeviation(Column* col, double mean); ++ void findStats(Column* column, int &count, double &sum, double &mean, double &std); ++ ++ Spreadsheet* dataSourceSpreadsheet{nullptr}; ++ int m_rows{0}; ++ int m_columns{0}; ++ QVector columns; ++}; ++ ++#endif // TTEST_H +diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +index ef0717c15..7a658653b 100644 +--- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp ++++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +@@ -43,6 +43,8 @@ + #include "backend/core/datatypes/DateTime2StringFilter.h" + #include "backend/core/datatypes/String2DateTimeFilter.h" + #include "backend/pivot/PivotTable.h" ++#include "backend/t_test/TTest.h" ++ + + #include + #include +@@ -220,6 +222,7 @@ void SpreadsheetView::initActions() { + 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_ttest = new QAction(i18n("T Test"), this); + + // column related actions + action_insert_column_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Column Left"), this); +@@ -471,6 +474,12 @@ void SpreadsheetView::initMenus() { + m_columnMenu->addMenu(m_columnManipulateDataMenu); + m_columnMenu->addSeparator(); + ++ //for ttest statistics; ++ m_columnHypothesisTestingMenu = new QMenu("Hypothesis Testing",this); ++ m_columnHypothesisTestingMenu->addAction(action_do_ttest); ++ m_columnMenu->addMenu(m_columnHypothesisTestingMenu); ++ 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); +@@ -558,6 +567,7 @@ void SpreadsheetView::connectActions() { + connect(action_go_to_cell, &QAction::triggered, this, + static_cast(&SpreadsheetView::goToCell)); + connect(action_pivot_table, &QAction::triggered, this, &SpreadsheetView::createPivotTable); ++ connect(action_do_ttest, &QAction::triggered, this, &SpreadsheetView::doTTest); + + connect(action_insert_column_left, &QAction::triggered, this, &SpreadsheetView::insertColumnLeft); + connect(action_insert_column_right, &QAction::triggered, this, &SpreadsheetView::insertColumnRight); +@@ -764,9 +774,17 @@ 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); ++ m_spreadsheet->parentAspect()->addChild(pivot); + } + ++void SpreadsheetView::doTTest() ++{ ++ TTest* ttest = new TTest(i18n("doing T Test for %1", m_spreadsheet->name())); ++ ttest->setColumns(this->selectedColumns()); ++ ttest->performTwoSampleTest(); ++} ++ ++ + void SpreadsheetView::handleHorizontalSectionMoved(int index, int from, int to) { + Q_UNUSED(index); + +diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.h b/src/commonfrontend/spreadsheet/SpreadsheetView.h +index b67df1252..eceb1c556 100644 +--- a/src/commonfrontend/spreadsheet/SpreadsheetView.h ++++ b/src/commonfrontend/spreadsheet/SpreadsheetView.h +@@ -130,6 +130,7 @@ private: + QAction* action_go_to_cell; + QAction* action_statistics_all_columns; + QAction* action_pivot_table; ++ QAction* action_do_ttest; + + //column related actions + QAction* action_insert_column_left; +@@ -192,6 +193,7 @@ private: + QMenu* m_spreadsheetMenu; + QMenu* m_plotDataMenu; + QMenu* m_analyzePlotMenu; ++ QMenu* m_columnHypothesisTestingMenu{nullptr}; + + public slots: + void createContextMenu(QMenu*); +@@ -206,6 +208,7 @@ private slots: + void goToPreviousColumn(); + void goToCell(); + void createPivotTable(); ++ void doTTest(); + void sortSpreadsheet(); + void sortDialog(QVector); +