diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2be345b45..125290754 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,492 +1,496 @@
find_package(SharedMimeInfo REQUIRED)
set(KDE_FRONTEND true)
set(KDEFRONTEND_DIR kdefrontend)
set(BACKEND_DIR backend)
set(COMMONFRONTEND_DIR commonfrontend)
set(CANTOR_DIR cantor)
set(TOOLS_DIR tools)
set(CMAKE_AUTOMOC ON)
set(SRC_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
# do not process this file
set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON)
set(GUI_SOURCES
${KDEFRONTEND_DIR}/GuiObserver.cpp
${KDEFRONTEND_DIR}/GuiTools.cpp
${KDEFRONTEND_DIR}/HistoryDialog.cpp
${KDEFRONTEND_DIR}/MainWin.cpp
${KDEFRONTEND_DIR}/SettingsDialog.cpp
${KDEFRONTEND_DIR}/SettingsGeneralPage.cpp
${KDEFRONTEND_DIR}/SettingsWorksheetPage.cpp
${KDEFRONTEND_DIR}/SettingsPage.h
${KDEFRONTEND_DIR}/TemplateHandler.cpp
${KDEFRONTEND_DIR}/ThemeHandler.cpp
${KDEFRONTEND_DIR}/datasources/AsciiOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/BinaryOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/DatabaseManagerDialog.cpp
${KDEFRONTEND_DIR}/datasources/DatabaseManagerWidget.cpp
${KDEFRONTEND_DIR}/datasources/HDF5OptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/FileInfoDialog.cpp
${KDEFRONTEND_DIR}/datasources/ImageOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/ImportDialog.cpp
${KDEFRONTEND_DIR}/datasources/ImportFileWidget.cpp
${KDEFRONTEND_DIR}/datasources/ImportFileDialog.cpp
${KDEFRONTEND_DIR}/datasources/ImportProjectDialog.cpp
${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseDialog.cpp
${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseWidget.cpp
${KDEFRONTEND_DIR}/datasources/NetCDFOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/ROOTOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/FITSOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/MQTTErrorWidget.cpp
${KDEFRONTEND_DIR}/datasources/MQTTSubscriptionWidget.cpp
${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerWidget.cpp
${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerDialog.cpp
${KDEFRONTEND_DIR}/dockwidgets/BaseDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CursorDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp
+ ${KDEFRONTEND_DIR}/dockwidgets/CorrelationCoefficientDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/HypothesisTestDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CustomPointDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/ColumnDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/LiveDataDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/MatrixDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/PivotTableDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/ProjectDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/SpreadsheetDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYEquationCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYDataReductionCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYDifferentiationCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYIntegrationCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYInterpolationCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYSmoothCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYFitCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYFourierFilterCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYFourierTransformCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYConvolutionCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/XYCorrelationCurveDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/WorksheetDock.cpp
+ ${KDEFRONTEND_DIR}/generalTest/CorrelationCoefficientView.cpp
+ ${KDEFRONTEND_DIR}/generalTest/GeneralTestView.cpp
${KDEFRONTEND_DIR}/generalTest/HypothesisTestView.cpp
- ${KDEFRONTEND_DIR}/generalTest/GeneralTestView.cpp
${KDEFRONTEND_DIR}/matrix/MatrixFunctionDialog.cpp
${KDEFRONTEND_DIR}/pivot/HierarchicalHeaderView.cpp
${KDEFRONTEND_DIR}/pivot/PivotTableView.cpp
${KDEFRONTEND_DIR}/spreadsheet/PlotDataDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/EquidistantValuesDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/ExportSpreadsheetDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/AddSubtractValueDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/DropValuesDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/FunctionValuesDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/RandomValuesDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/SortDialog.cpp
${KDEFRONTEND_DIR}/spreadsheet/StatisticsDialog.cpp
${KDEFRONTEND_DIR}/worksheet/ExportWorksheetDialog.cpp
${KDEFRONTEND_DIR}/worksheet/GridDialog.cpp
${KDEFRONTEND_DIR}/worksheet/DynamicPresenterWidget.cpp
${KDEFRONTEND_DIR}/worksheet/PresenterWidget.cpp
${KDEFRONTEND_DIR}/worksheet/SlidingPanel.cpp
${KDEFRONTEND_DIR}/widgets/ConstantsWidget.cpp
${KDEFRONTEND_DIR}/widgets/ThemesComboBox.cpp
${KDEFRONTEND_DIR}/widgets/ThemesWidget.cpp
${KDEFRONTEND_DIR}/widgets/ExpressionTextEdit.cpp
${KDEFRONTEND_DIR}/widgets/FitOptionsWidget.cpp
${KDEFRONTEND_DIR}/widgets/FitParametersWidget.cpp
${KDEFRONTEND_DIR}/widgets/FunctionsWidget.cpp
${KDEFRONTEND_DIR}/widgets/LabelWidget.cpp
${KDEFRONTEND_DIR}/widgets/DatapickerImageWidget.cpp
${KDEFRONTEND_DIR}/widgets/DatapickerCurveWidget.cpp
${KDEFRONTEND_DIR}/widgets/FITSHeaderEditWidget.cpp
${KDEFRONTEND_DIR}/widgets/FITSHeaderEditNewKeywordDialog.cpp
${KDEFRONTEND_DIR}/widgets/FITSHeaderEditAddUnitDialog.cpp
${KDEFRONTEND_DIR}/widgets/FITSHeaderEditDialog.cpp
${KDEFRONTEND_DIR}/widgets/ResizableTextEdit.cpp
${KDEFRONTEND_DIR}/widgets/MQTTWillSettingsWidget.cpp
)
set(UI_SOURCES
${KDEFRONTEND_DIR}/ui/constantswidget.ui
${KDEFRONTEND_DIR}/ui/functionswidget.ui
${KDEFRONTEND_DIR}/ui/fitoptionswidget.ui
${KDEFRONTEND_DIR}/ui/fitparameterswidget.ui
${KDEFRONTEND_DIR}/ui/labelwidget.ui
${KDEFRONTEND_DIR}/ui/settingsgeneralpage.ui
${KDEFRONTEND_DIR}/ui/settingsworksheetpage.ui
${KDEFRONTEND_DIR}/ui/settingsprintingpage.ui
${KDEFRONTEND_DIR}/ui/mqttwillsettingswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/asciioptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/binaryoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/databasemanagerwidget.ui
${KDEFRONTEND_DIR}/ui/datasources/hdf5optionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/imageoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/importfilewidget.ui
${KDEFRONTEND_DIR}/ui/datasources/importprojectwidget.ui
${KDEFRONTEND_DIR}/ui/datasources/importsqldatabasewidget.ui
${KDEFRONTEND_DIR}/ui/datasources/netcdfoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/rootoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/fitsoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/mqtterrorwidget.ui
${KDEFRONTEND_DIR}/ui/datasources/mqttsubscriptionwidget.ui
${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/mqttconnectionmanagerwidget.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/cursordock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotlegenddock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/columndock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/custompointdock.ui
+ ${KDEFRONTEND_DIR}/ui/dockwidgets/correlationcoefficientdock.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}/generalTest/HypothesisTest.cpp
+ ${BACKEND_DIR}/generalTest/CorrelationCoefficient.cpp
${BACKEND_DIR}/generalTest/GeneralTest.cpp
+ ${BACKEND_DIR}/generalTest/HypothesisTest.cpp
${BACKEND_DIR}/matrix/Matrix.cpp
${BACKEND_DIR}/matrix/matrixcommands.cpp
${BACKEND_DIR}/matrix/MatrixModel.cpp
${BACKEND_DIR}/pivot/PivotTable.cpp
${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp
${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp
${BACKEND_DIR}/lib/XmlStreamReader.cpp
${BACKEND_DIR}/note/Note.cpp
${BACKEND_DIR}/worksheet/WorksheetElement.cpp
${BACKEND_DIR}/worksheet/TextLabel.cpp
${BACKEND_DIR}/worksheet/Worksheet.cpp
${BACKEND_DIR}/worksheet/WorksheetElementContainer.cpp
${BACKEND_DIR}/worksheet/WorksheetElementGroup.cpp
${BACKEND_DIR}/worksheet/TreeModel.cpp
${BACKEND_DIR}/worksheet/plots/AbstractPlot.cpp
${BACKEND_DIR}/worksheet/plots/AbstractCoordinateSystem.cpp
${BACKEND_DIR}/worksheet/plots/PlotArea.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/Axis.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlot.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlotLegend.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/Histogram.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/CustomPoint.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/Symbol.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYAnalysisCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYEquationCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYDataReductionCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYDifferentiationCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYIntegrationCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYInterpolationCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYSmoothCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYFitCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierFilterCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierTransformCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYConvolutionCurve.cpp
${BACKEND_DIR}/worksheet/plots/cartesian/XYCorrelationCurve.cpp
${BACKEND_DIR}/lib/SignallingUndoCommand.cpp
${BACKEND_DIR}/datapicker/DatapickerPoint.cpp
${BACKEND_DIR}/datapicker/DatapickerImage.cpp
${BACKEND_DIR}/datapicker/Datapicker.cpp
${BACKEND_DIR}/datapicker/Transform.cpp
${BACKEND_DIR}/datapicker/ImageEditor.cpp
${BACKEND_DIR}/datapicker/Segment.cpp
${BACKEND_DIR}/datapicker/Segments.cpp
${BACKEND_DIR}/datapicker/DatapickerCurve.cpp
)
IF (ENABLE_LIBORIGIN)
list(APPEND BACKEND_SOURCES ${BACKEND_DIR}/datasources/projects/OriginProjectParser.cpp)
ENDIF ()
set(NSL_SOURCES
${BACKEND_DIR}/nsl/nsl_conv.c
${BACKEND_DIR}/nsl/nsl_corr.c
${BACKEND_DIR}/nsl/nsl_dft.c
${BACKEND_DIR}/nsl/nsl_diff.c
${BACKEND_DIR}/nsl/nsl_filter.c
${BACKEND_DIR}/nsl/nsl_fit.c
${BACKEND_DIR}/nsl/nsl_geom.c
${BACKEND_DIR}/nsl/nsl_geom_linesim.c
${BACKEND_DIR}/nsl/nsl_int.c
${BACKEND_DIR}/nsl/nsl_interp.c
${BACKEND_DIR}/nsl/nsl_math.c
${BACKEND_DIR}/nsl/nsl_sf_basic.c
${BACKEND_DIR}/nsl/nsl_sf_kernel.c
${BACKEND_DIR}/nsl/nsl_sf_poly.c
${BACKEND_DIR}/nsl/nsl_sf_stats.c
${BACKEND_DIR}/nsl/nsl_sf_window.c
${BACKEND_DIR}/nsl/nsl_smooth.c
${BACKEND_DIR}/nsl/nsl_sort.c
${BACKEND_DIR}/nsl/nsl_stats.c
)
IF (NOT MSVC_FOUND)
IF (NOT LIBCERF_FOUND)
list(APPEND NSL_SOURCES
${BACKEND_DIR}/nsl/Faddeeva.c
)
ENDIF ()
ENDIF ()
set(COMMONFRONTEND_SOURCES
${COMMONFRONTEND_DIR}/matrix/MatrixView.cpp
${COMMONFRONTEND_DIR}/note/NoteView.cpp
${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetCommentsHeaderModel.cpp
${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetHeaderView.cpp
${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetItemDelegate.cpp
${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetView.cpp
${COMMONFRONTEND_DIR}/workbook/WorkbookView.cpp
${COMMONFRONTEND_DIR}/worksheet/WorksheetView.cpp
${COMMONFRONTEND_DIR}/ProjectExplorer.cpp
${COMMONFRONTEND_DIR}/core/PartMdiView.cpp
${COMMONFRONTEND_DIR}/widgets/TreeViewComboBox.cpp
${COMMONFRONTEND_DIR}/widgets/qxtspanslider.cpp
${COMMONFRONTEND_DIR}/widgets/MemoryWidget.cpp
${COMMONFRONTEND_DIR}/widgets/DateTimeSpinBox.cpp
${COMMONFRONTEND_DIR}/datapicker/DatapickerView.cpp
${COMMONFRONTEND_DIR}/datapicker/DatapickerImageView.cpp
)
IF (${Cantor_FOUND})
set(CANTOR_SOURCES
${KDEFRONTEND_DIR}/dockwidgets/CantorWorksheetDock.cpp
${BACKEND_DIR}/cantorWorksheet/VariableParser.cpp
${BACKEND_DIR}/cantorWorksheet/CantorWorksheet.cpp
${COMMONFRONTEND_DIR}/cantorWorksheet/CantorWorksheetView.cpp
)
set(CANTOR_UI_SOURCES ${KDEFRONTEND_DIR}/ui/dockwidgets/cantorworksheetdock.ui)
set(UI_SOURCES ${UI_SOURCES} ${CANTOR_UI_SOURCES})
ELSE ()
set(CANTOR_SOURCES "")
ENDIF ()
set(TOOLS_SOURCES
${TOOLS_DIR}/EquationHighlighter.cpp
${TOOLS_DIR}/ImageTools.cpp
${TOOLS_DIR}/TeXRenderer.cpp
)
bison_target(GslParser
${BACKEND_DIR}/gsl/parser.y
${CMAKE_CURRENT_BINARY_DIR}/gsl_parser.c
)
set(GENERATED_SOURCES
${BISON_GslParser_OUTPUTS}
)
set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON)
##############################################################################
INCLUDE_DIRECTORIES( . ${BACKEND_DIR}/gsl ${GSL_INCLUDE_DIR} )
set( LABPLOT_SRCS ${GUI_SOURCES} )
ki18n_wrap_ui( LABPLOT_SRCS ${UI_SOURCES} )
# see also QT_MINIMUM_VERSION
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50500)
# static library
add_library( labplot2lib STATIC ${LABPLOT_SRCS} ${BACKEND_SOURCES} ${NSL_SOURCES} ${CANTOR_SOURCES} ${DATASOURCES_SOURCES} ${COMMONFRONTEND_SOURCES} ${TOOLS_SOURCES} ${GENERATED_SOURCES} ${QTMOC_HDRS} )
# set_property(TARGET ${objlib} PROPERTY POSITION_INDEPENDENT_CODE 1)
target_link_libraries( labplot2lib
KF5::Archive
KF5::Completion
KF5::ConfigCore
KF5::Crash
KF5::I18n
KF5::IconThemes
KF5::KIOCore
KF5::KIOFileWidgets
KF5::KIOWidgets
KF5::TextWidgets
KF5::XmlGui
Qt5::Svg
Qt5::Core
Qt5::Network
Qt5::PrintSupport
Qt5::Sql
${GSL_LIBRARIES}
${GSL_CBLAS_LIBRARIES} )
IF (Qt5SerialPort_FOUND)
target_link_libraries( labplot2lib Qt5::SerialPort )
ENDIF ()
IF (Qt5Mqtt_FOUND)
target_link_libraries( labplot2lib Qt5::Mqtt )
ENDIF ()
IF (KF5SyntaxHighlighting_FOUND)
target_link_libraries( labplot2lib KF5::SyntaxHighlighting )
ENDIF ()
#TODO: KF5::NewStuff
IF (Cantor_FOUND)
target_link_libraries( labplot2lib Cantor::cantorlibs KF5::Service KF5::Parts)
ENDIF ()
IF (HDF5_FOUND)
target_link_libraries( labplot2lib ${HDF5_LIBRARIES} )
ENDIF ()
IF (FFTW3_FOUND)
target_link_libraries( labplot2lib ${FFTW3_LIBRARIES} )
ENDIF ()
IF (netCDF_FOUND)
target_link_libraries( labplot2lib netcdf )
ENDIF ()
IF (CFITSIO_FOUND)
target_link_libraries( labplot2lib ${CFITSIO_LIBRARIES} )
ENDIF ()
IF (LIBCERF_FOUND)
target_link_libraries( labplot2lib ${LIBCERF_LIBRARY} )
ENDIF ()
IF (ZLIB_FOUND AND LZ4_FOUND)
target_link_libraries( labplot2lib ${ZLIB_LIBRARY} ${LZ4_LIBRARY} )
ENDIF ()
IF (ENABLE_LIBORIGIN)
target_link_libraries( labplot2lib liborigin-static )
ENDIF ()
IF (WIN32)
target_link_libraries( labplot2lib ${PSAPI} )
ENDIF ()
# icons for the executable and project files on Windows and Mac OS X
set(LABPLOT_ICONS
${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-apps-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-apps-labplot2.png
)
set(LML_ICONS
${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-application-x-labplot2.png
${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-application-x-labplot2.png
)
# main executable
set(LABPLOT_SOURCE ${KDEFRONTEND_DIR}/LabPlot.cpp)
# create icon files on WIN/MAC and add icons to the executable
IF (${ECM_VERSION} VERSION_GREATER "5.48.0")
# creates LABPLOT_ICONS.ico/LABPLOT_ICONS.icns
ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS} OUTFILE_BASENAME LABPLOT_ICONS)
ELSE ()
# creates LABPLOT_SOURCE.ico/LABPLOT_SOURCE.icns
ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS})
ENDIF ()
# create LML_ICONS.icns on MACOSX
IF (APPLE AND ${ECM_VERSION} VERSION_GREATER "5.48.0")
ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LML_ICONS} OUTFILE_BASENAME LML_ICONS)
ENDIF ()
add_executable(labplot2 ${LABPLOT_SOURCE})
target_link_libraries(labplot2 labplot2lib)
############## installation ################################
install( TARGETS labplot2 DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS} )
install( FILES ${KDEFRONTEND_DIR}/labplot2ui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/${PROJECT_NAME} )
install( FILES ${KDEFRONTEND_DIR}/splash.png ${KDEFRONTEND_DIR}/labplot2.ico ${CMAKE_CURRENT_SOURCE_DIR}/../icons/application-x-labplot2.ico DESTINATION ${DATA_INSTALL_DIR}/${PROJECT_NAME} )
install( PROGRAMS org.kde.labplot2.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
install( FILES labplot2.xml DESTINATION ${XDG_MIME_INSTALL_DIR} )
install( FILES labplot2_themes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} )
update_xdg_mimetypes( ${XDG_MIME_INSTALL_DIR} )
diff --git a/src/backend/core/AbstractAspect.h b/src/backend/core/AbstractAspect.h
index 7bdd24445..9fce4cfc6 100644
--- a/src/backend/core/AbstractAspect.h
+++ b/src/backend/core/AbstractAspect.h
@@ -1,300 +1,301 @@
/***************************************************************************
File : AbstractAspect.h
Project : LabPlot
--------------------------------------------------------------------
Copyright : (C) 2007-2009 by Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2007-2010 by Knut Franke (knut.franke@gmx.de)
Copyright : (C) 2011-2015 by Alexander Semke (alexander.semke@web.de)
Description : Base class for all objects in a Project.
***************************************************************************/
/***************************************************************************
* *
* 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 ABSTRACT_ASPECT_H
#define ABSTRACT_ASPECT_H
#include
#include
class AbstractAspectPrivate;
class Folder;
class Project;
class XmlStreamReader;
class QDateTime;
class QDropEvent;
class QIcon;
class QMenu;
class QUndoCommand;
class QUndoStack;
class QXmlStreamWriter;
/// Information about class inheritance
/// enum values are chosen such that @verbatim inherits(base)@endverbatim
/// returns true iff the class inherits from @verbatim base@endverbatim.
///
/// AspectType is used in GuiObserver to select the correct dock widget.
enum class AspectType : quint64 {
AbstractAspect = 0,
// classes without inheriters
AbstractFilter = 0x0100001,
DatapickerCurve = 0x0100002,
DatapickerPoint = 0x0100004,
WorksheetElement = 0x0200000,
Axis = 0x0210001,
CartesianPlotLegend = 0x0210002,
CustomPoint = 0x0210004,
Histogram = 0x0210008,
PlotArea = 0x0210010,
TextLabel = 0x0210020,
WorksheetElementContainer = 0x0220000,
AbstractPlot = 0x0221000,
CartesianPlot = 0x0221001,
WorksheetElementGroup = 0x0222000,
XYCurve = 0x0240000,
XYEquationCurve = 0x0240001,
XYAnalysisCurve = 0x0280000,
XYConvolution = 0x0280001,
XYCorrelationCurve = 0x0280002,
XYDataReductionCurve = 0x0280004,
XYDifferentiationCurve = 0x0280008,
XYFitCurve = 0x0280010,
XYFourierFilterCurve = 0x0280020,
XYFourierTransformCurve = 0x0280040,
XYInterpolationCurve = 0x0280080,
XYIntegrationCurve = 0x0280100,
XYSmoothCurve = 0x0280200,
AbstractPart = 0x0400000,
AbstractDataSource = 0x0410000,
Matrix = 0x0411000,
Spreadsheet = 0x0412000,
LiveDataSource = 0x0412001,
MQTTTopic = 0x0412002,
CantorWorksheet = 0x0420001,
Datapicker = 0x0420002,
DatapickerImage = 0x0420004,
Note = 0x0420008,
Workbook = 0x0420010,
Worksheet = 0x0420020,
PivotTable = 0x0420040,
HypothesisTest = 0x0420080,
+ CorrelationCoefficient = 0x0420100,
AbstractColumn = 0x1000000,
Column = 0x1000001,
SimpleFilterColumn = 0x1000002,
ColumnStringIO = 0x1000004,
Folder = 0x2000000,
Project = 0x2000001,
MQTTClient = 0x2000002,
MQTTSubscription = 0x2000004,
};
class AbstractAspect : public QObject {
Q_OBJECT
public:
enum ChildIndexFlag {
IncludeHidden = 0x01,
Recursive = 0x02,
Compress = 0x04
};
Q_DECLARE_FLAGS(ChildIndexFlags, ChildIndexFlag)
friend class AspectChildAddCmd;
friend class AspectChildRemoveCmd;
friend class AbstractAspectPrivate;
AbstractAspect(const QString& name, AspectType type);
~AbstractAspect() override;
QString name() const;
QString comment() const;
void setCreationTime(const QDateTime&);
QDateTime creationTime() const;
virtual Project* project();
virtual QString path() const;
void setHidden(bool);
bool hidden() const;
void setSelected(bool);
void setIsLoading(bool);
bool isLoading() const;
virtual QIcon icon() const;
virtual QMenu* createContextMenu();
AspectType type() const;
bool inherits(AspectType type) const;
//functions related to the handling of the tree-like project structure
AbstractAspect* parentAspect() const;
AbstractAspect* parent(AspectType type) const;
void setParentAspect(AbstractAspect*);
Folder* folder();
bool isDescendantOf(AbstractAspect* other);
void addChild(AbstractAspect*);
void addChildFast(AbstractAspect*);
virtual void finalizeAdd() {};
QVector children(AspectType type, ChildIndexFlags flags=nullptr);
void insertChildBefore(AbstractAspect* child, AbstractAspect* before);
void insertChildBeforeFast(AbstractAspect* child, AbstractAspect* before);
void reparent(AbstractAspect* newParent, int newIndex = -1);
void removeChild(AbstractAspect*);
void removeAllChildren();
virtual QVector dependsOn() const;
virtual bool isDraggable() const;
virtual QVector dropableOn() const;
virtual void processDropEvent(QDropEvent*) {};
template T* ancestor() const {
AbstractAspect* parent = parentAspect();
while (parent) {
T* ancestorAspect = dynamic_cast(parent);
if (ancestorAspect)
return ancestorAspect;
parent = parent->parentAspect();
}
return nullptr;
}
template QVector children(ChildIndexFlags flags = nullptr) const {
QVector result;
for (auto* child: children()) {
if (flags & IncludeHidden || !child->hidden()) {
T* i = dynamic_cast(child);
if (i)
result << i;
if (flags & Recursive)
result << child->template children(flags);
}
}
return result;
}
template T* child(int index, ChildIndexFlags flags=nullptr) const {
int i = 0;
for (auto* child: children()) {
T* c = dynamic_cast(child);
if (c && (flags & IncludeHidden || !child->hidden()) && index == i++)
return c;
}
return nullptr;
}
template T* child(const QString& name) const {
for (auto* child: children()) {
T* c = dynamic_cast(child);
if (c && child->name() == name)
return c;
}
return nullptr;
}
template int childCount(ChildIndexFlags flags = nullptr) const {
int result = 0;
for (auto* child: children()) {
T* i = dynamic_cast(child);
if (i && (flags & IncludeHidden || !child->hidden()))
result++;
}
return result;
}
template int indexOfChild(const AbstractAspect* child, ChildIndexFlags flags = nullptr) const {
int index = 0;
for (auto* c: children()) {
if (child == c) return index;
T* i = dynamic_cast(c);
if (i && (flags & IncludeHidden || !c->hidden()))
index++;
}
return -1;
}
//undo/redo related functions
void setUndoAware(bool);
virtual QUndoStack* undoStack() const;
void exec(QUndoCommand*);
void exec(QUndoCommand* command, const char* preChangeSignal, const char* postChangeSignal,
QGenericArgument val0 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument());
void beginMacro(const QString& text);
void endMacro();
//save/load
virtual void save(QXmlStreamWriter*) const = 0;
virtual bool load(XmlStreamReader*, bool preview) = 0;
protected:
void info(const QString& text) { emit statusInfo(text); }
//serialization/deserialization
bool readBasicAttributes(XmlStreamReader*);
void writeBasicAttributes(QXmlStreamWriter*) const;
void writeCommentElement(QXmlStreamWriter*) const;
bool readCommentElement(XmlStreamReader*);
const AspectType m_type;
private:
AbstractAspectPrivate* d;
QString uniqueNameFor(const QString&) const;
const QVector children() const;
void connectChild(AbstractAspect*);
public slots:
bool setName(const QString&, bool autoUnique = true);
void setComment(const QString&);
void remove();
protected slots:
virtual void childSelected(const AbstractAspect*);
virtual void childDeselected(const AbstractAspect*);
signals:
void aspectDescriptionAboutToChange(const AbstractAspect*);
void aspectDescriptionChanged(const AbstractAspect*);
void aspectAboutToBeAdded(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child);
void aspectAdded(const AbstractAspect*);
void aspectAboutToBeRemoved(const AbstractAspect*);
void aspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child);
void aspectHiddenAboutToChange(const AbstractAspect*);
void aspectHiddenChanged(const AbstractAspect*);
void statusInfo(const QString&);
void renameRequested();
//selection/deselection in model (project explorer)
void selected(const AbstractAspect*);
void deselected(const AbstractAspect*);
//selection/deselection in view
void childAspectSelectedInView(const AbstractAspect*);
void childAspectDeselectedInView(const AbstractAspect*);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractAspect::ChildIndexFlags)
#endif // ifndef ABSTRACT_ASPECT_H
diff --git a/src/backend/generalTest/CorrelationCoefficient.cpp b/src/backend/generalTest/CorrelationCoefficient.cpp
new file mode 100644
index 000000000..c10e5dff8
--- /dev/null
+++ b/src/backend/generalTest/CorrelationCoefficient.cpp
@@ -0,0 +1,119 @@
+/***************************************************************************
+ File : CorrelationCoefficient.cpp
+ Project : LabPlot
+ Description : Finding Correlation Coefficient on data provided
+ --------------------------------------------------------------------
+ Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com)
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
+ * Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#include "CorrelationCoefficient.h"
+#include "GeneralTest.h"
+#include "kdefrontend/generalTest/CorrelationCoefficientView.h"
+#include "backend/spreadsheet/Spreadsheet.h"
+#include "backend/core/column/Column.h"
+#include "backend/lib/macros.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+extern "C" {
+#include "backend/nsl/nsl_stats.h"
+}
+
+CorrelationCoefficient::CorrelationCoefficient(const QString &name) : GeneralTest (name, AspectType::CorrelationCoefficient) {
+}
+
+CorrelationCoefficient::~CorrelationCoefficient() {
+}
+
+void CorrelationCoefficient::performTest(Test test, bool categoricalVariable) {
+ m_statsTable = "";
+ m_tooltips.clear();
+ for (int i = 0; i < 10; i++)
+ m_resultLine[i]->clear();
+
+ switch (test) {
+ case CorrelationCoefficient::Test::Pearson: {
+ m_currTestName = "" + i18n("Pearson's r Correlation Test") + "
";
+ performPearson(categoricalVariable);
+ break;
+ }
+ case CorrelationCoefficient::Test::Kendall:
+ m_currTestName = "" + i18n("Kendall's Correlation Test") + "
";
+ performKendall();
+ break;
+ case CorrelationCoefficient::Test::Spearman: {
+ m_currTestName = "" + i18n("Spearman Correlation Test") + "
";
+ performSpearman();
+ break;
+ }
+ }
+
+ emit changed();
+}
+
+
+double CorrelationCoefficient::correlationValue() {
+ return m_correlationValue;
+}
+
+
+/***************************************************************************************************************************
+ * Private Implementations
+ * ************************************************************************************************************************/
+
+/*********************************************Pearson r ******************************************************************/
+void CorrelationCoefficient::performPearson(bool categoricalVariable) {
+ Q_UNUSED(categoricalVariable);
+
+}
+
+/***********************************************Kendall ******************************************************************/
+void CorrelationCoefficient::performKendall() {
+
+}
+
+/***********************************************Spearman ******************************************************************/
+void CorrelationCoefficient::performSpearman() {
+
+}
+
+// Virtual functions
+QWidget* CorrelationCoefficient::view() const {
+ if (!m_partView) {
+ m_view = new CorrelationCoefficientView(const_cast(this));
+ m_partView = m_view;
+ }
+ return m_partView;
+}
diff --git a/src/backend/generalTest/CorrelationCoefficient.h b/src/backend/generalTest/CorrelationCoefficient.h
new file mode 100644
index 000000000..1b75acaa7
--- /dev/null
+++ b/src/backend/generalTest/CorrelationCoefficient.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ File : CorrelationCoefficient.h
+ Project : LabPlot
+ Description : Finding Correlation Coefficient on data provided
+ --------------------------------------------------------------------
+ Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com)
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
+ * Boston, MA 02110-1301 USA *
+ * *
+ ***************************************************************************/
+
+#ifndef CORRELATIONCOEFFICIENT_H
+#define CORRELATIONCOEFFICIENT_H
+
+#include "backend/core/AbstractPart.h"
+#include "GeneralTest.h"
+#include "backend/lib/macros.h"
+
+class CorrelationCoefficientView;
+class Spreadsheet;
+class QString;
+class Column;
+class QVBoxLayout;
+class QLabel;
+
+class CorrelationCoefficient : public GeneralTest {
+ Q_OBJECT
+
+public:
+ explicit CorrelationCoefficient(const QString& name);
+ ~CorrelationCoefficient() override;
+
+ enum Test{
+ Pearson,
+ Kendall,
+ Spearman
+ };
+ double correlationValue();
+ QWidget* view() const override;
+
+ void performTest(Test m_test, bool categoricalVariable = true);
+private:
+ void performPearson(bool categoricalVariable);
+ void performKendall();
+ void performSpearman();
+
+ double m_correlationValue;
+};
+
+#endif // CORRELATIONCOEFFICIENT_H
diff --git a/src/backend/generalTest/GeneralTest.cpp b/src/backend/generalTest/GeneralTest.cpp
index 6b7e8e585..7ebcfc477 100644
--- a/src/backend/generalTest/GeneralTest.cpp
+++ b/src/backend/generalTest/GeneralTest.cpp
@@ -1,484 +1,484 @@
/***************************************************************************
File : GeneralTest.cpp
Project : LabPlot
Description : Doing Hypothesis-Test on data provided
--------------------------------------------------------------------
Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301 USA *
* *
***************************************************************************/
#include "GeneralTest.h"
#include "kdefrontend/generalTest/HypothesisTestView.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/core/column/Column.h"
#include "backend/lib/macros.h"
//#include
//#include
//#include
#include
#include
//#include
//#include
//#include
#include
#include
#include
extern "C" {
#include "backend/nsl/nsl_stats.h"
}
-GeneralTest::GeneralTest(const QString &name) : AbstractPart(name),
+GeneralTest::GeneralTest(const QString &name, const AspectType& type) : AbstractPart(name, type),
m_summaryLayout(new QVBoxLayout()) {
for (int i = 0; i < 10; i++) {
m_resultLine[i] = new QLabel();
m_summaryLayout->addWidget(m_resultLine[i]);
}
}
GeneralTest::~GeneralTest() {
}
void GeneralTest::setDataSourceType(DataSourceType type) {
if (type != m_dataSourceType)
m_dataSourceType = type;
}
GeneralTest::DataSourceType GeneralTest::dataSourceType() const {
return m_dataSourceType;
}
void GeneralTest::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) {
m_dataSourceSpreadsheet = spreadsheet;
for (auto* col : m_dataSourceSpreadsheet->children())
m_allColumns << col->name();
}
QString GeneralTest::testName() {
return m_currTestName;
}
QString GeneralTest::statsTable() {
return m_statsTable;
}
QMap GeneralTest::tooltips() {
return m_tooltips;
}
QVBoxLayout* GeneralTest::summaryLayout() {
return m_summaryLayout;
}
void GeneralTest::setColumns(QStringList cols) {
m_columns.clear();
Column* column = new Column("column");
for (QString col : cols) {
if (!cols.isEmpty()) {
column = m_dataSourceSpreadsheet->column(col);
m_columns.append(column);
}
}
delete[] column;
}
void GeneralTest::setColumns(const QVector &cols) {
m_columns = cols;
}
/********************************************************************************************************************
* Protected functions implementations [Helper Functions]
********************************************************************************************************************/
QString GeneralTest::round(QVariant number, int precision) {
if (number.userType() == QMetaType::Double || number.userType() == QMetaType::Float) {
double multiplierPrecision = gsl_pow_int(10, precision);
int tempNum = int(number.toDouble()*multiplierPrecision*10);
if (tempNum % 10 < 5)
return QString::number((tempNum/10) / multiplierPrecision);
else
return QString::number((tempNum/10 + 1) / multiplierPrecision);
}
return i18n("%1", number.toString());
}
bool GeneralTest::isNumericOrInteger(Column* column) {
return (column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer);
}
GeneralTest::ErrorType GeneralTest::findStats(const Column* column, int& count, double& sum, double& mean, double& std) {
sum = 0;
mean = 0;
std = 0;
count = column->rowCount();
for (int i = 0; i < count; i++) {
double row = column->valueAt(i);
if ( std::isnan(row)) {
count = i;
break;
}
sum += row;
}
if (count < 1)
return GeneralTest::ErrorEmptyColumn;
mean = sum / count;
for (int i = 0; i < count; i++) {
double row = column->valueAt(i);
std += gsl_pow_2( (row - mean));
}
if (count > 1)
std = std / (count-1);
std = sqrt(std);
return GeneralTest::NoError;
}
GeneralTest::ErrorType GeneralTest::findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std) {
sum = 0;
mean = 0;
std = 0;
int count1 = column1->rowCount();
int count2 = column2->rowCount();
count = qMin(count1, count2);
double cell1, cell2;
for (int i = 0; i < count; i++) {
cell1 = column1->valueAt(i);
cell2 = column2->valueAt(i);
if (std::isnan(cell1) || std::isnan(cell2)) {
if (std::isnan(cell1) && std::isnan(cell2))
count = i;
else
return GeneralTest::ErrorUnqualSize;
break;
}
sum += cell1 - cell2;
}
if (count < 1)
return GeneralTest::ErrorEmptyColumn;
mean = sum / count;
double row;
for (int i = 0; i < count; i++) {
cell1 = column1->valueAt(i);
cell2 = column2->valueAt(i);
row = cell1 - cell2;
std += gsl_pow_2( (row - mean));
}
if (count > 1)
std = std / (count-1);
std = sqrt(std);
return GeneralTest::NoError;
}
void GeneralTest::countPartitions(Column* column, int& np, int& totalRows) {
totalRows = column->rowCount();
np = 0;
QString cellValue;
QMap discoveredCategoricalVar;
AbstractColumn::ColumnMode originalColMode = column->columnMode();
column->setColumnMode(AbstractColumn::Text);
for (int i = 0; i < totalRows; i++) {
cellValue = column->textAt(i);
if (cellValue.isEmpty()) {
totalRows = i;
break;
}
if (discoveredCategoricalVar[cellValue])
continue;
discoveredCategoricalVar[cellValue] = true;
np++;
}
column->setColumnMode(originalColMode);
}
GeneralTest::ErrorType GeneralTest::findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows) {
Column* columns[] = {column1, column2};
for (int i = 0; i < np; i++) {
n[i] = 0;
sum[i] = 0;
mean[i] = 0;
std[i] = 0;
}
AbstractColumn::ColumnMode originalColMode = columns[0]->columnMode();
columns[0]->setColumnMode(AbstractColumn::Text);
int partitionNumber = 1;
for (int i = 0; i < totalRows; i++) {
QString name = columns[0]->textAt(i);
double value = columns[1]->valueAt(i);
if (std::isnan(value)) {
columns[0]->setColumnMode(originalColMode);
return GeneralTest::ErrorUnqualSize;
}
if (colName[name] == 0) {
colName[name] = partitionNumber;
partitionNumber++;
}
n[colName[name]-1]++;
sum[colName[name]-1] += value;
}
for (int i = 0; i < np; i++)
mean[i] = sum[i] / n[i];
for (int i = 0; i < totalRows; i++) {
QString name = columns[0]->textAt(i);
double value = columns[1]->valueAt(i);
std[colName[name]-1] += gsl_pow_2( (value - mean[colName[name]-1]));
}
for (int i = 0; i < np; i++) {
if (n[i] > 1)
std[i] = std[i] / (n[i] - 1);
std[i] = sqrt(std[i]);
}
columns[0]->setColumnMode(originalColMode);
if (isNumericOrInteger(columns[0])) {
}
return GeneralTest::NoError;
}
QString GeneralTest::getHtmlTable(int row, int column, QVariant* rowMajor) {
if (row < 1 || column < 1)
return QString();
QString table;
table = ""
""
" ";
QString bg = "tg-0pky";
bool pky = true;
QString element;
table += "
";
for (int j = 0; j < column; j++) {
element = rowMajor[j].toString();
table += " " + i18n("%1", element) + " | ";
}
table += "
";
if (pky)
bg = "tg-0pky";
else
bg = "tg-btxf";
pky = !pky;
for (int i = 1; i < row; i++) {
table += " ";
QString element = round(rowMajor[i*column]);
table += " " + i18n("%1", element) + " | ";
for (int j = 1; j < column; j++) {
element = round(rowMajor[i*column+j]);
table += " " + i18n("%1", element) + " | ";
}
table += "
";
if (pky)
bg = "tg-0pky";
else
bg = "tg-btxf";
pky = !pky;
}
table += "
";
return table;
}
QString GeneralTest::getHtmlTable3(const QList& rowMajor) {
m_tooltips.clear();
int rowMajorSize = rowMajor.size();
if (rowMajorSize == 0)
return QString();
QString table;
table = "";
table += "";
table += " ";
int prevLevel = 0;
for (int i = 0; i < rowMajorSize; i++) {
Cell* currCell = rowMajor[i];
if (currCell->level != prevLevel) {
table += "
";
table += " ";
prevLevel = currCell->level;
}
QString cellStartTag = "isHeader) {
cellStartTag = " | rowSpanCount) +
" " +
"colspan=" + QString::number(currCell->columnSpanCount) +
">" +
i18n("%1", currCell->data) +
cellEndTag;
if (!currCell->tooltip.isEmpty())
m_tooltips.insert(currCell->data, currCell->tooltip);
}
table += " |
";
table += "
";
return table;
}
QString GeneralTest::getLine(const QString& msg, const QString& color) {
return "" + i18n("%1", msg) + "
";
}
void GeneralTest::printLine(const int& index, const QString& msg, const QString& color) {
if (index < 0 || index >= 10)
return;
m_resultLine[index]->setText(getLine(msg, color));
return;
}
void GeneralTest::printTooltip(const int &index, const QString &msg) {
if (index < 0 || index >= 10)
return;
m_resultLine[index]->setToolTip(i18n("%1", msg));
}
void GeneralTest::printError(const QString& errorMsg) {
printLine(0, errorMsg, "red");
}
/********************************************************************************************************************
* virtual functions implementations
********************************************************************************************************************/
/*!
Saves as XML.
*/
void GeneralTest::save(QXmlStreamWriter* writer) const {
writer->writeStartElement("GeneralTest");
writeBasicAttributes(writer);
writeCommentElement(writer);
writer->writeEndElement();
}
/*!
Loads from XML.
*/
bool GeneralTest::load(XmlStreamReader* reader, bool preview) {
Q_UNUSED(preview);
if (!readBasicAttributes(reader))
return false;
return !reader->hasError();
}
Spreadsheet *GeneralTest::dataSourceSpreadsheet() const {
return m_dataSourceSpreadsheet;
}
bool GeneralTest::exportView() const {
return true;
}
bool GeneralTest::printView() {
return true;
}
bool GeneralTest::printPreview() const {
return true;
}
/*! Constructs a primary view on me.
This method may be called multiple times during the life time of an Aspect, or it might not get
called at all. Aspects must not depend on the existence of a view for their operation.
*/
//QWidget* GeneralTest::view() const {
// if (!m_partView) {
// m_view = new HypothesisTestView(const_cast(this));
// m_partView = m_view;
// }
// return m_partView;
//}
/*!
Returns a new context menu. The caller takes ownership of the menu.
*/
QMenu* GeneralTest::createContextMenu() {
QMenu* menu = AbstractPart::createContextMenu();
// Q_ASSERT(menu);
// emit requestProjectContextMenu(menu);
return menu;
}
diff --git a/src/backend/generalTest/GeneralTest.h b/src/backend/generalTest/GeneralTest.h
index 9f3134ae3..97085b470 100644
--- a/src/backend/generalTest/GeneralTest.h
+++ b/src/backend/generalTest/GeneralTest.h
@@ -1,135 +1,135 @@
/***************************************************************************
File : GeneralTest.h
Project : LabPlot
Description : Doing Hypothesis-Test on data provided
--------------------------------------------------------------------
Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301 USA *
* *
***************************************************************************/
#ifndef GENERALTEST_H
#define GENERALTEST_H
#include "backend/core/AbstractPart.h"
#include "backend/lib/macros.h"
-class HypothesisTestView;
+class GeneralTestView;
class Spreadsheet;
class QString;
class Column;
class QVBoxLayout;
class QLabel;
class GeneralTest : public AbstractPart {
Q_OBJECT
public:
- explicit GeneralTest(const QString& name);
+ explicit GeneralTest(const QString& name, const AspectType& type);
~GeneralTest() override;
enum DataSourceType {DataSourceSpreadsheet, DataSourceDatabase};
struct Cell {
QString data;
int level;
bool isHeader;
QString tooltip;
int rowSpanCount;
int columnSpanCount;
Cell(QVariant data = "", int level = 0, bool isHeader = false, QString tooltip = "", int rowSpanCount = 1, int columnSpanCount = 1) {
this->data = data.toString();
this->level = level;
this->isHeader = isHeader;
this->tooltip = tooltip;
this->rowSpanCount = rowSpanCount;
this->columnSpanCount = columnSpanCount;
}
};
enum ErrorType {ErrorUnqualSize, ErrorEmptyColumn, NoError};
void setDataSourceType(DataSourceType type);
DataSourceType dataSourceType() const;
void setDataSourceSpreadsheet(Spreadsheet* spreadsheet);
Spreadsheet* dataSourceSpreadsheet() const;
void setColumns(const QVector& cols);
void setColumns(QStringList cols);
QStringList allColumns();
QString testName();
QString statsTable();
QMap tooltips();
QVBoxLayout* summaryLayout();
//virtual methods
// QIcon icon() const override;
QMenu* createContextMenu() override;
// QWidget* view() const override;
bool exportView() const override;
bool printView() override;
bool printPreview() const override;
void save(QXmlStreamWriter*) const override;
bool load(XmlStreamReader*, bool preview) override;
signals:
void changed();
void requestProjectContextMenu(QMenu*);
void dataSourceTypeChanged(GeneralTest::DataSourceType);
void dataSourceSpreadsheetChanged(Spreadsheet*);
protected:
DataSourceType m_dataSourceType{GeneralTest::DataSourceSpreadsheet};
Spreadsheet* m_dataSourceSpreadsheet{nullptr};
QVector m_columns;
QStringList m_allColumns;
QString m_currTestName{"Result Table"};
QString m_statsTable;
QVBoxLayout* m_summaryLayout{nullptr};
QLabel* m_resultLine[10];
QMap m_tooltips;
bool isNumericOrInteger(Column* column);
QString round(QVariant number, int precision = 3);
void countPartitions(Column* column, int& np, int& totalRows);
ErrorType findStats(const Column* column,int& count, double& sum, double& mean, double& std);
ErrorType findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std);
ErrorType findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows);
QString getHtmlTable(int row, int column, QVariant* rowMajor);
QString getHtmlTable3(const QList& rowMajor);
QString getLine(const QString& msg, const QString& color = "black");
void printLine(const int& index, const QString& msg, const QString& color = "black");
void printTooltip(const int& index, const QString& msg);
void printError(const QString& errorMsg);
bool m_dbCreated{false};
- mutable HypothesisTestView* m_view{nullptr};
+ mutable GeneralTestView* m_view{nullptr};
};
#endif // GeneralTest_H
diff --git a/src/backend/generalTest/HypothesisTest.cpp b/src/backend/generalTest/HypothesisTest.cpp
index fd7218c4b..a375f88cd 100644
--- a/src/backend/generalTest/HypothesisTest.cpp
+++ b/src/backend/generalTest/HypothesisTest.cpp
@@ -1,1142 +1,1143 @@
/***************************************************************************
File : HypothesisTest.cpp
Project : LabPlot
Description : Doing Hypothesis-Test on data provided
--------------------------------------------------------------------
Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301 USA *
* *
***************************************************************************/
#include "HypothesisTest.h"
#include "kdefrontend/generalTest/HypothesisTestView.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/core/column/Column.h"
#include "backend/lib/macros.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern "C" {
#include "backend/nsl/nsl_stats.h"
}
-HypothesisTest::HypothesisTest(const QString &name) : GeneralTest (name) {
+HypothesisTest::HypothesisTest(const QString &name) : GeneralTest (name, AspectType::HypothesisTest) {
}
HypothesisTest::~HypothesisTest() {
}
void HypothesisTest::setPopulationMean(QVariant m_populationMean) {
m_populationMean = m_populationMean.toDouble();
}
void HypothesisTest::setSignificanceLevel(QVariant alpha) {
m_significanceLevel = alpha.toDouble();
}
void HypothesisTest::performTest(Test test, bool categoricalVariable, bool equalVariance) {
m_tailType = test.tail;
m_pValue.clear();
m_statisticValue.clear();
m_statsTable = "";
m_tooltips.clear();
for (int i = 0; i < 10; i++)
m_resultLine[i]->clear();
switch (test.subtype) {
case HypothesisTest::Test::SubType::TwoSampleIndependent: {
m_currTestName = "" + i18n("Two Sample Independent Test") + "";
performTwoSampleIndependentTest(test.type, categoricalVariable, equalVariance);
break;
}
case HypothesisTest::Test::SubType::TwoSamplePaired:
m_currTestName = "" + i18n("Two Sample Paired Test") + "";
performTwoSamplePairedTest(test.type);
break;
case HypothesisTest::Test::SubType::OneSample: {
m_currTestName = "" + i18n("One Sample Test") + "";
performOneSampleTest(test.type);
break;
}
case HypothesisTest::Test::SubType::OneWay: {
m_currTestName = "" + i18n("One Way Anova") + "";
performOneWayAnova();
break;
}
case HypothesisTest::Test::SubType::TwoWay: {
m_currTestName = "" + i18n("Two Way Anova") + "";
performTwoWayAnova();
break;
}
case HypothesisTest::Test::SubType::NoneSubType:
break;
}
emit changed();
}
void HypothesisTest::performLeveneTest(bool categoricalVariable) {
m_pValue.clear();
m_statisticValue.clear();
m_statsTable = "";
m_tooltips.clear();
for (int i = 0; i < 10; i++)
m_resultLine[i]->clear();
m_currTestName = "" + i18n("Levene Test for Equality of Variance") + "";
m_performLeveneTest(categoricalVariable);
emit changed();
}
QList HypothesisTest::statisticValue() {
return m_statisticValue;
}
QList HypothesisTest::pValue() {
return m_pValue;
}
/******************************************************************************
* Private Implementations
* ****************************************************************************/
//TODO: backend of z test;
//TODO: add tooltip to tables. (currently it is not possible to use with QTextDocument);
//TODO: use https://www.gnu.org/software/gsl/doc/html/statistics.html for basic statistic calculations
/**************************Two Sample Independent *************************************/
void HypothesisTest::performTwoSampleIndependentTest(HypothesisTest::Test::Type test, bool categoricalVariable, bool equalVariance) {
if (m_columns.size() != 2) {
printError("Inappropriate number of m_columns selected");
return;
}
int n[2];
double sum[2], mean[2], std[2];
QString col1Name = m_columns[0]->name();
QString col2Name = m_columns[1]->name();
if (!categoricalVariable && isNumericOrInteger(m_columns[0])) {
for (int i = 0; i < 2; i++) {
findStats(m_columns[i], n[i], sum[i], mean[i], std[i]);
if (n[i] == 0) {
printError("Atleast two values should be there in every column");
return;
}
- if (std[i] == 0) {
+ if (std[i] <= 0) {
printError(i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", m_columns[i]->name()));
return;
}
}
} else {
QMap colName;
QString baseColName;
int np;
int totalRows;
countPartitions(m_columns[0], np, totalRows);
if (np != 2) {
printError( i18n("Number of Categorical Variable in Column %1 is not equal to 2", m_columns[0]->name()));
return;
}
if (isNumericOrInteger(m_columns[0]))
baseColName = m_columns[0]->name();
ErrorType errorCode = findStatsCategorical(m_columns[0], m_columns[1], n, sum, mean, std, colName, np, totalRows);
switch (errorCode) {
case ErrorUnqualSize: {
printError( i18n("Unequal size between Column %1 and Column %2", m_columns[0]->name(), m_columns[1]->name()));
return;
}
case ErrorEmptyColumn: {
printError("At least one of selected column is empty");
return;
}
case NoError:
break;
}
QMapIterator i(colName);
while (i.hasNext()) {
i.next();
if (i.value() == 1)
col1Name = baseColName + " " + i.key();
else
col2Name = baseColName + " " + i.key();
}
}
QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std",
col1Name, n[0], sum[0], mean[0], std[0],
col2Name, n[1], sum[1], mean[1], std[1]
};
m_statsTable = getHtmlTable(3, 5, rowMajor);
for (int i = 0; i < 2; i++) {
if (n[i] == 0) {
printError("Atleast two values should be there in every column");
return;
}
- if (std[i] == 0) {
+ if (std[i] <= 0) {
printError( i18n("Standard Deviation of atleast one column is equal to 0: last column is: %1", m_columns[i]->name()));
return;
}
}
QString testName;
int df = 0;
double sp = 0;
switch (test) {
case HypothesisTest::Test::Type::TTest: {
testName = "T";
if (equalVariance) {
df = n[0] + n[1] - 2;
sp = qSqrt(((n[0]-1) * gsl_pow_2(std[0]) +
(n[1]-1) * gsl_pow_2(std[1]) ) / df );
m_statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt(1.0/n[0] + 1.0/n[1])));
printLine(9, "Assumption: Equal Variance b/w both population means");
} else {
double temp_val;
temp_val = gsl_pow_2( gsl_pow_2(std[0]) / n[0] + gsl_pow_2(std[1]) / n[1]);
temp_val = temp_val / ( (gsl_pow_2( (gsl_pow_2(std[0]) / n[0]) ) / (n[0]-1)) +
(gsl_pow_2( (gsl_pow_2(std[1]) / n[1]) ) / (n[1]-1)));
df = qRound(temp_val);
m_statisticValue.append((mean[0] - mean[1]) / (qSqrt( (gsl_pow_2(std[0])/n[0]) +
(gsl_pow_2(std[1])/n[1]))));
printLine(9, "Assumption: UnEqual Variance b/w both population means");
}
printLine(8, "Assumption: Both Populations approximately follow normal distribution");
break;
}
case HypothesisTest::Test::Type::ZTest: {
testName = "Z";
sp = qSqrt( ((n[0]-1) * gsl_pow_2(std[0]) + (n[1]-1) * gsl_pow_2(std[1])) / df);
m_statisticValue.append((mean[0] - mean[1]) / (sp * qSqrt( 1.0 / n[0] + 1.0 / n[1])));
// m_pValue.append(gsl_cdf_gaussian_P(m_statisticValue, sp));
break;
}
case HypothesisTest::Test::Type::Anova:
case HypothesisTest::Test::Type::NoneType:
break;
}
m_currTestName = "" + i18n("Two Sample Independent %1 Test for %2 vs %3", testName, col1Name, col2Name) + "";
m_pValue.append(getPValue(test, m_statisticValue[0], col1Name, col2Name, (mean[0] - mean[1]), sp, df));
printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue");
printLine(4, i18n("%1 Value is %2 ", testName, round(m_statisticValue[0])), "green");
printTooltip(4, i18n("More is the |%1-value|, more safely we can reject the null hypothesis", testName));
printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green");
printLine(6, i18n("Degree of Freedom is %1", df), "green");
printTooltip(6, i18n("Number of independent Pieces of information that went into calculating the estimate"));
if (m_pValue[0] <= m_significanceLevel)
printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", round(m_significanceLevel)));
else
printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true"));
return;
}
/********************************Two Sample Paired ***************************************/
void HypothesisTest::performTwoSamplePairedTest(HypothesisTest::Test::Type test) {
if (m_columns.size() != 2) {
printError("Inappropriate number of m_columns selected");
return;
}
for (int i = 0; i < 2; i++) {
if ( !isNumericOrInteger(m_columns[0])) {
printError("select only m_columns with numbers");
return;
}
}
int n;
double sum, mean, std;
ErrorType errorCode = findStatsPaired(m_columns[0], m_columns[1], n, sum, mean, std);
switch (errorCode) {
case ErrorUnqualSize: {
printError("both m_columns are having different sizes");
return;
}
case ErrorEmptyColumn: {
printError("m_columns are empty");
return;
}
case NoError:
break;
}
QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std",
"difference", n, sum, mean, std
};
m_statsTable = getHtmlTable(2, 5, rowMajor);
- if (std == 0) {
+ if (std <= 0) {
printError("Standard deviation of the difference is 0");
return;
}
QString testName;
int df = 0;
switch (test) {
case HypothesisTest::Test::Type::TTest: {
m_statisticValue[0] = mean / (std / qSqrt(n));
df = n - 1;
testName = "T";
printLine(6, i18n("Degree of Freedom is %1 |
name(), i18n("%1", m_populationMean), mean, std, df));
m_currTestName = "" + i18n("One Sample %1 Test for %2 vs %3", testName, m_columns[0]->name(), m_columns[1]->name()) + "
";
printLine(2, i18n("Significance level is %1 ", round(m_significanceLevel)), "blue");
printLine(4, i18n("%1 Value is %2 ", testName, round(m_statisticValue[0])), "green");
printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green");
if (m_pValue[0] <= m_significanceLevel)
printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel));
else
printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true"));
return;
}
/******************************** One Sample ***************************************/
void HypothesisTest::performOneSampleTest(HypothesisTest::Test::Type test) {
if (m_columns.size() != 1) {
printError("Inappropriate number of m_columns selected");
return;
}
if ( !isNumericOrInteger(m_columns[0])) {
printError("select only m_columns with numbers");
return;
}
int n;
double sum, mean, std;
ErrorType errorCode = findStats(m_columns[0], n, sum, mean, std);
switch (errorCode) {
case ErrorEmptyColumn: {
printError("column is empty");
return;
}
case NoError:
break;
case ErrorUnqualSize: {
return;
}
}
QVariant rowMajor[] = {"", "N", "Sum", "Mean", "Std",
m_columns[0]->name(), n, sum, mean, std
};
m_statsTable = getHtmlTable(2, 5, rowMajor);
- if (std == 0) {
+ if (std <= 0) {
printError("Standard deviation is 0");
return;
}
QString testName;
int df = 0;
switch (test) {
case HypothesisTest::Test::Type::TTest: {
testName = "T";
m_statisticValue.append((mean - m_populationMean) / (std / qSqrt(n)));
df = n - 1;
printLine(6, i18n("Degree of Freedom is %1", df), "blue");
break;
}
case HypothesisTest::Test::Type::ZTest: {
testName = "Z";
df = 0;
m_statisticValue.append((mean - m_populationMean) / (std / qSqrt(n)));
break;
}
case HypothesisTest::Test::Type::Anova:
case HypothesisTest::Test::Type::NoneType:
break;
}
m_pValue.append(getPValue(test, m_statisticValue[0], m_columns[0]->name(), i18n("%1",m_populationMean), mean - m_populationMean, std, df));
m_currTestName = "" + i18n("One Sample %1 Test for %2", testName, m_columns[0]->name()) + "
";
printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue");
printLine(4, i18n("%1 Value is %2", testName, round(m_statisticValue[0])), "green");
printLine(5, i18n("P Value is %1", m_pValue[0]), "green");
if (m_pValue[0] <= m_significanceLevel)
printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel));
else
printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true"));
return;
}
/*************************************One Way Anova***************************************/
// all standard variables and formulas are taken from this wikipedia page:
// https://en.wikipedia.org/wiki/One-way_analysis_of_variance
// b stands for b/w groups
// w stands for within groups
// np is number of partition i.e., number of classes
void HypothesisTest::performOneWayAnova() {
int np, totalRows;
countPartitions(m_columns[0], np, totalRows);
int* ni = new int[np];
double* sum = new double[np];
double* mean = new double[np];
double* std = new double[np];
QString* colNames = new QString[np];
QMap classnameToIndex;
QString baseColName;
if (isNumericOrInteger(m_columns[0]))
baseColName = m_columns[0]->name();
findStatsCategorical(m_columns[0], m_columns[1], ni, sum, mean, std, classnameToIndex, np, totalRows);
double yBar = 0; // overall mean
double sB = 0; // sum of squares of (mean - overall_mean) between the groups
int fB = 0; // degree of freedom between the groups
double msB = 0; // mean sum of squares between the groups
double sW = 0; // sum of squares of (value - mean of group) within the groups
int fW = 0; // degree of freedom within the group
double msW = 0; // mean sum of squares within the groups
// now finding mean of each group;
for (int i = 0; i < np; i++)
yBar += mean[i];
yBar = yBar / np;
for (int i = 0; i < np; i++) {
sB += ni[i] * gsl_pow_2( ( mean[i] - yBar));
if (ni[i] > 1)
sW += gsl_pow_2( std[i])*(ni[i] - 1);
else
sW += gsl_pow_2( std[i]);
fW += ni[i] - 1;
}
fB = np - 1;
msB = sB / fB;
msW = sW / fW;
m_statisticValue.append(msB / msW);
m_pValue.append(nsl_stats_fdist_p(m_statisticValue[0], static_cast(np-1), fW));
QMapIterator i(classnameToIndex);
while (i.hasNext()) {
i.next();
colNames[i.value()-1] = baseColName + " " + i.key();
}
// now printing the statistics and result;
int rowCount = np + 1, columnCount = 5;
QVariant* rowMajor = new QVariant[rowCount*columnCount];
// header data;
rowMajor[0] = "";
rowMajor[1] = "Ni";
rowMajor[2] = "Sum";
rowMajor[3] = "Mean";
rowMajor[4] = "Std";
// table data
for (int row_i = 1; row_i < rowCount ; row_i++) {
rowMajor[row_i*columnCount] = colNames[row_i - 1];
rowMajor[row_i*columnCount + 1] = ni[row_i - 1];
rowMajor[row_i*columnCount + 2] = sum[row_i - 1];
rowMajor[row_i*columnCount + 3] = mean[row_i - 1];
rowMajor[row_i*columnCount + 4] = std[row_i - 1];
}
m_statsTable = "" + i18n("Group Summary Statistics") + "
";
m_statsTable += getHtmlTable(rowCount, columnCount, rowMajor);
m_statsTable += getLine("");
m_statsTable += getLine("");
m_statsTable += "" + i18n("Grand Summary Statistics") + "
";
m_statsTable += getLine("");
m_statsTable += getLine(i18n("Overall Mean is %1", round(yBar)));
rowCount = 4;
columnCount = 3;
rowMajor->clear();
rowMajor[0] = "";
rowMajor[1] = "Between Groups";
rowMajor[2] = "Within Groups";
int baseIndex = 0;
baseIndex = 1 * columnCount;
rowMajor[baseIndex + 0] = "Sum of Squares";
rowMajor[baseIndex + 1] = sB;
rowMajor[baseIndex + 2] = sW;
baseIndex = 2 * columnCount;
rowMajor[baseIndex + 0] = "Degree of Freedom";
rowMajor[baseIndex + 1] = fB;
rowMajor[baseIndex + 2] = fW;
baseIndex = 3 * columnCount;
rowMajor[baseIndex + 0] = "Mean Square Value";
rowMajor[baseIndex + 1] = msB;
rowMajor[baseIndex + 2] = msW;
m_statsTable += getHtmlTable(rowCount, columnCount, rowMajor);
delete[] ni;
delete[] sum;
delete[] mean;
delete[] std;
delete[] colNames;
printLine(1, i18n("F Value is %1", round(m_statisticValue[0])), "green");
printLine(2, i18n("P Value is %1 ", m_pValue[0]), "green");
if (m_pValue[0] <= m_significanceLevel)
printTooltip(2, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel));
else
printTooltip(2, i18n("There is a plausibility for Null Hypothesis to be true"));
return;
}
/*************************************Two Way Anova***************************************/
// all formulas and symbols are taken from: http://statweb.stanford.edu/~susan/courses/s141/exanova.pdf
//TODO: suppress warning of variable length array are a C99 feature.
//TODO: add assumptions verification option
//TODO: add tail option (if needed)
void HypothesisTest::performTwoWayAnova() {
int np_a, totalRows_a;
int np_b, totalRows_b;
countPartitions(m_columns[0], np_a, totalRows_a);
countPartitions(m_columns[1], np_b, totalRows_b);
double groupMean[np_a][np_b];
int replicates[np_a][np_b];
for (int i = 0; i < np_a; i++)
for (int j = 0; j < np_b; j++) {
groupMean[i][j] = 0;
replicates[i][j] = 0;
}
if (totalRows_a != totalRows_b) {
printError("There is missing data in atleast one of the rows");
return;
}
QMap catToNumber_a;
QMap catToNumber_b;
int partitionNumber_a = 1;
int partitionNumber_b = 1;
for (int i = 0; i < totalRows_a; i++) {
QString name_a = m_columns[0]->textAt(i);
QString name_b = m_columns[1]->textAt(i);
double value = m_columns[2]->valueAt(i);
if (catToNumber_a[name_a] == 0) {
catToNumber_a[name_a] = partitionNumber_a;
partitionNumber_a++;
}
if (catToNumber_b[name_b] == 0) {
catToNumber_b[name_b] = partitionNumber_b;
partitionNumber_b++;
}
groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += value;
replicates[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1] += 1;
}
int replicate = replicates[0][0];
for (int i = 0; i < np_a; i++)
for (int j = 0; j < np_b; j++) {
if (replicates[i][j] == 0) {
printError("Dataset should have atleast one data value corresponding to each feature combination");
return;
}
if (replicates[i][j] != replicate) {
printError("Number of experiments perfomed for each combination of levels
"
"between Independet Var.1 and Independent Var.2 must be equal");
return;
}
groupMean[i][j] /= replicates[i][j];
}
- // for (int i = 0; i < np_a; i++)
- // for (int j = 0; j < np_b; j++)
- // groupMean[i][j] = int(groupMean[i][j]);
-
double ss_within = 0;
for (int i = 0; i < totalRows_a; i++) {
QString name_a = m_columns[0]->textAt(i);
QString name_b = m_columns[1]->textAt(i);
double value = m_columns[2]->valueAt(i);
ss_within += gsl_pow_2(value - groupMean[catToNumber_a[name_a] - 1][catToNumber_b[name_b] - 1]);
}
int df_within = (replicate - 1) * np_a * np_b;
double ms_within = ss_within / df_within;
- double mean_a[np_a];
- double mean_b[np_b];
+ double* mean_a = new double[np_a];
+ double* mean_b = new double[np_b];
for (int i = 0; i < np_a; i++) {
for (int j = 0; j < np_b; j++) {
mean_a[i] += groupMean[i][j] / np_b;
mean_b[j] += groupMean[i][j] / np_a;
}
}
double mean = 0;
for (int i = 0; i < np_a; i++)
mean += mean_a[i] / np_a;
double ss_a = 0;
for (int i = 0; i < np_a; i++)
ss_a += gsl_pow_2(mean_a[i] - mean);
ss_a *= replicate * np_b;
int df_a = np_a - 1;
double ms_a = ss_a / df_a;
double ss_b = 0;
for (int i = 0; i < np_b; i++)
ss_b += gsl_pow_2(mean_b[i] - mean);
ss_b *= replicate * np_a;
int df_b = np_b - 1;
double ms_b = ss_b / df_b;
double ss_interaction = 0;
for (int i = 0; i < np_a; i++)
for (int j = 0; j < np_b; j++)
ss_interaction += gsl_pow_2(groupMean[i][j] - mean_a[i] - mean_b[j] + mean);
ss_interaction *= replicate;
int df_interaction = (np_a - 1) * (np_b - 1);
double ms_interaction = ss_interaction / df_interaction;
- QString partitionNames_a[np_a];
- QString partitionNames_b[np_b];
+ QString* partitionNames_a = new QString[np_a];
+ QString* partitionNames_b = new QString[np_b];
QMapIterator itr_a(catToNumber_a);
while (itr_a.hasNext()) {
itr_a.next();
partitionNames_a[itr_a.value()-1] = itr_a.key();
}
QMapIterator itr_b(catToNumber_b);
while (itr_b.hasNext()) {
itr_b.next();
partitionNames_b[itr_b.value()-1] = itr_b.key();
}
// printing table;
// cell constructor structure; data, level, rowSpanCount, m_columnspanCount, isHeader;
QList rowMajor;
rowMajor.append(new Cell("", 0, true, "", 2, 1));
for (int i = 0; i < np_b; i++)
rowMajor.append(new Cell(partitionNames_b[i], 0, true, "", 1, 2));
rowMajor.append(new Cell("Mean", 0, true, "", 2));
for (int i = 0; i < np_b; i++) {
rowMajor.append(new Cell("Mean", 1, true));
rowMajor.append(new Cell("Replicate", 1, true));
}
int level = 2;
for (int i = 0; i < np_a; i++) {
rowMajor.append(new Cell(partitionNames_a[i], level, true));
for (int j = 0; j < np_b; j++) {
rowMajor.append(new Cell(round(groupMean[i][j]), level));
rowMajor.append(new Cell(replicates[i][j], level));
}
rowMajor.append(new Cell(round(mean_a[i]), level));
level++;
}
rowMajor.append(new Cell("Mean", level, true));
for (int i = 0; i < np_b; i++)
rowMajor.append(new Cell(round(mean_b[i]), level, false, "", 1, 2));
rowMajor.append(new Cell(round(mean), level));
m_statsTable = "" + i18n("Contingency Table") + "";
m_statsTable += getHtmlTable3(rowMajor);
m_statsTable += "";
m_statsTable += "" + i18n("results table") + "";
rowMajor.clear();
level = 0;
rowMajor.append(new Cell("", level, true));
rowMajor.append(new Cell("SS", level, true));
rowMajor.append(new Cell("DF", level, true, "degree of freedom"));
rowMajor.append(new Cell("MS", level, true));
level++;
rowMajor.append(new Cell(m_columns[0]->name(), level, true));
rowMajor.append(new Cell(round(ss_a), level));
rowMajor.append(new Cell(df_a, level));
rowMajor.append(new Cell(round(ms_a), level));
level++;
rowMajor.append(new Cell(m_columns[1]->name(), level, true));
rowMajor.append(new Cell(round(ss_b), level));
rowMajor.append(new Cell(df_b, level));
rowMajor.append(new Cell(round(ms_b), level));
level++;
rowMajor.append(new Cell("Interaction", level, true));
rowMajor.append(new Cell(round(ss_interaction), level));
rowMajor.append(new Cell(df_interaction, level));
rowMajor.append(new Cell(round(ms_interaction), level));
level++;
rowMajor.append(new Cell("Within", level, true));
rowMajor.append(new Cell(round(ss_within), level));
rowMajor.append(new Cell(df_within, level));
rowMajor.append(new Cell(round(ms_within), level));
m_statsTable += getHtmlTable3(rowMajor);
double fValue_a = ms_a / ms_within;
double fValue_b = ms_b / ms_within;
double fValue_interaction = ms_interaction / ms_within;
double m_pValue_a = nsl_stats_fdist_p(fValue_a, static_cast(np_a - 1), df_a);
double m_pValue_b = nsl_stats_fdist_p(fValue_b, static_cast(np_b - 1), df_b);
printLine(0, "F(df" + m_columns[0]->name() + ", dfwithin) is " + round(fValue_a), "blue");
printLine(1, "F(df" + m_columns[1]->name() + ", dfwithin) is " + round(fValue_b), "blue");
printLine(2, "F(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue");
printLine(4, "P(df" + m_columns[0]->name() + ", dfwithin) is " + round(m_pValue_a), "blue");
printLine(5, "P(df" + m_columns[1]->name() + ", dfwithin) is " + round(m_pValue_b), "blue");
// printLine(2, "P(dfinteraction, dfwithin) is " + round(fValue_interaction), "blue");
m_statisticValue.append(fValue_a);
m_statisticValue.append(fValue_b);
m_statisticValue.append(fValue_interaction);
m_pValue.append(m_pValue_a);
m_pValue.append(m_pValue_b);
+ delete[] mean_a;
+ delete[] mean_b;
+ delete[] partitionNames_a;
+ delete[] partitionNames_b;
+
return;
}
/**************************************Levene Test****************************************/
// Some reference to local variables.
// np = number of partitions
// df = degree of fredom
// totalRows = total number of rows in column
// these variables are taken from: https://en.wikipedia.org/wiki/Levene%27s_test
// yiBar = mean of ith group;
// Zij = |Yij - yiBar|
// ziBar = mean of Zij for group i
// ziBarBar = mean for all zij
// ni = number of elements in group i
void HypothesisTest::m_performLeveneTest(bool categoricalVariable) {
if (m_columns.size() != 2) {
printError("Inappropriate number of m_columns selected");
return;
}
int np = 0;
int n = 0;
if (!categoricalVariable && isNumericOrInteger(m_columns[0]))
np = m_columns.size();
else
countPartitions(m_columns[0], np, n);
if (np < 2) {
printError("Select atleast two m_columns / classes");
return;
}
double* yiBar = new double[np];
double* ziBar = new double[np];
double ziBarBar = 0;
double* ni = new double[np];
for (int i = 0; i < np; i++) {
yiBar[i] = 0;
ziBar[i] = 0;
ni[i] = 0;
}
double fValue;
int df = 0;
int totalRows = 0;
QString* colNames = new QString[np];
if (!categoricalVariable && isNumericOrInteger(m_columns[0])) {
totalRows = m_columns[0]->rowCount();
double value = 0;
for (int j = 0; j < totalRows; j++) {
int numberNaNCols = 0;
for (int i = 0; i < np; i++) {
value = m_columns[i]->valueAt(j);
if (std::isnan(value)) {
numberNaNCols++;
continue;
}
yiBar[i] += value;
ni[i]++;
n++;
}
if (numberNaNCols == np) {
totalRows = j;
break;
}
}
for (int i = 0; i < np; i++) {
if (ni[i] > 0)
yiBar[i] = yiBar[i] / ni[i];
else {
printError("One of the selected m_columns is empty "
"or have choosen Independent Var.1 wrongly");
return;
}
}
for (int j = 0; j < totalRows; j++) {
for (int i = 0; i < np; i++) {
value = m_columns[i]->valueAt(j);
if (!(std::isnan(value)))
ziBar[i] += fabs(value - yiBar[i]);
}
}
for (int i = 0; i < np; i++) {
ziBarBar += ziBar[i];
if (ni[i] > 0)
ziBar[i] = ziBar[i] / ni[i];
}
ziBarBar = ziBarBar / n;
double numberatorValue = 0;
double denominatorValue = 0;
for (int j = 0; j < totalRows; j++) {
for (int i = 0; i < np; i++) {
value = m_columns[i]->valueAt(j);
if (!(std::isnan(value))) {
double zij = fabs(value - yiBar[i]);
denominatorValue += gsl_pow_2( (zij - ziBar[i]));
}
}
}
if (denominatorValue <= 0) {
printError( i18n("Denominator value is %1", denominatorValue));
return;
}
for (int i = 0; i < np; i++) {
colNames[i] = m_columns[i]->name();
numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar));
}
fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue);
} else {
QMap classnameToIndex;
AbstractColumn::ColumnMode originalColMode = m_columns[0]->columnMode();
m_columns[0]->setColumnMode(AbstractColumn::Text);
int partitionNumber = 1;
QString name;
double value;
int classIndex;
for (int j = 0; j < n; j++) {
name = m_columns[0]->textAt(j);
value = m_columns[1]->valueAt(j);
if (std::isnan(value)) {
n = j;
break;
}
if (classnameToIndex[name] == 0) {
classnameToIndex[name] = partitionNumber;
partitionNumber++;
}
classIndex = classnameToIndex[name]-1;
ni[classIndex]++;
yiBar[classIndex] += value;
}
for (int i = 0; i < np; i++) {
if (ni[i] > 0)
yiBar[i] = yiBar[i] / ni[i];
else {
printError("One of the selected m_columns is empty "
"or have choosen Independent Var.1 wrongly");
m_columns[0]->setColumnMode(originalColMode);
return;
}
}
for (int j = 0; j < n; j++) {
name = m_columns[0]->textAt(j);
value = m_columns[1]->valueAt(j);
classIndex = classnameToIndex[name] - 1;
ziBar[classIndex] += fabs(value - yiBar[classIndex]);
}
for (int i = 0; i < np; i++) {
ziBarBar += ziBar[i];
ziBar[i] = ziBar[i] / ni[i];
}
ziBarBar = ziBarBar / n;
double numberatorValue = 0;
double denominatorValue = 0;
for (int j = 0; j < n; j++) {
name = m_columns[0]->textAt(j);
value = m_columns[1]->valueAt(j);
classIndex = classnameToIndex[name] - 1;
double zij = fabs(value - yiBar[classIndex]);
denominatorValue += gsl_pow_2( (zij - ziBar[classIndex]));
}
for (int i = 0; i < np; i++)
numberatorValue += ni[i]*gsl_pow_2( (ziBar[i]-ziBarBar));
if (denominatorValue <= 0) {
printError( "number of data points is less or than equal to number of categorical variables");
m_columns[0]->setColumnMode(originalColMode);
return;
}
fValue = ((n - np) / (np - 1)) * (numberatorValue / denominatorValue);
QMapIterator i(classnameToIndex);
while (i.hasNext()) {
i.next();
colNames[i.value()-1] = m_columns[0]->name() + " " + i.key();
}
m_columns[0]->setColumnMode(originalColMode);
}
df = n - np;
// now making the stats table.
int rowCount = np+1;
int columnCount = 4;
QVariant* rowMajor = new QVariant[rowCount*columnCount];
// header data;
rowMajor[0] = "";
rowMajor[1] = "Ni";
rowMajor[2] = "yiBar";
rowMajor[3] = "ziBar";
// table data
for (int row_i = 1; row_i < rowCount; row_i++) {
rowMajor[row_i*columnCount] = colNames[row_i-1];
rowMajor[row_i*columnCount + 1] = ni[row_i-1];
rowMajor[row_i*columnCount + 2] = yiBar[row_i-1];
rowMajor[row_i*columnCount + 3] = ziBar[row_i-1];
}
m_statsTable = getHtmlTable(rowCount, columnCount, rowMajor);
delete[] rowMajor;
delete[] yiBar;
delete[] ziBar;
delete[] ni;
m_pValue.append(nsl_stats_fdist_p(fValue, static_cast(np-1), df));
printLine(0, "Null Hypothesis: Variance is equal between all classes", "blue");
printLine(1, "Alternate Hypothesis: Variance is not equal in at-least one pair of classes", "blue");
printLine(2, i18n("Significance level is %1", round(m_significanceLevel)), "blue");
printLine(4, i18n("F Value is %1 ", round(fValue)), "green");
printLine(5, i18n("P Value is %1 ", m_pValue[0]), "green");
printLine(6, i18n("Degree of Freedom is %1", df), "green");
if (m_pValue[0] <= m_significanceLevel) {
printTooltip(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significanceLevel));
printLine(8, "Requirement for homogeneity is not met", "red");
} else {
printTooltip(5, i18n("There is a plausibility for Null Hypothesis to be true"));
printLine(8, "Requirement for homogeneity is met", "green");
}
m_statisticValue.append(fValue);
return;
}
//TODO change ("⋖") symbol to ("<"), currently macro UTF8_QSTRING is not working properly if used "<" symbol;
// TODO: check for correctness between: for TestZ with TailTwo
// m_pValue.append(2*gsl_cdf_tdist_P(value, df) v/s
// m_pValue.append(gsl_cdf_tdis_P(value, df) + gsl_cdf_tdis_P(-value, df);
double HypothesisTest::getPValue(const HypothesisTest::Test::Type& test, double& value, const QString& col1Name, const QString& col2Name, const double mean, const double sp, const int df) {
switch (test) {
case HypothesisTest::Test::Type::TTest: {
switch (m_tailType) {
case HypothesisTest::Test::Tail::Negative: {
m_pValue.append(gsl_cdf_tdist_P(value, df));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≥"), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue");
break;
}
case HypothesisTest::Test::Tail::Positive: {
value *= -1;
m_pValue.append(gsl_cdf_tdist_P(value, df));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≤"), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING(">"), col2Name), "blue");
break;
}
case HypothesisTest::Test::Tail::Two: {
m_pValue.append(2.*gsl_cdf_tdist_P(-fabs(value), df));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("="), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3", col1Name, UTF8_QSTRING("≠"), col2Name), "blue");
break;
}
}
break;
}
case HypothesisTest::Test::Type::ZTest: {
switch (m_tailType) {
case HypothesisTest::Test::Tail::Negative: {
m_pValue.append(gsl_cdf_gaussian_P(value - mean, sp));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≥"), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("⋖"), col2Name), "blue");
break;
}
case HypothesisTest::Test::Tail::Positive: {
value *= -1;
m_pValue.append(nsl_stats_tdist_p(value - mean, sp));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≤"), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING(">"), col2Name), "blue");
break;
}
case HypothesisTest::Test::Tail::Two: {
m_pValue.append(2.*gsl_cdf_gaussian_P(value - mean, sp));
printLine(0, i18n("Null Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("="), col2Name), "blue");
printLine(1, i18n("Alternate Hypothesis: Population mean of %1 %2 Population mean of %3 ", col1Name, UTF8_QSTRING("≠"), col2Name), "blue");
break;
}
}
break;
}
case HypothesisTest::Test::Type::Anova:
case HypothesisTest::Test::Type::NoneType:
break;
}
if (m_pValue[0] > 1)
return 1;
return m_pValue[0];
}
// Virtual functions
QWidget* HypothesisTest::view() const {
if (!m_partView) {
m_view = new HypothesisTestView(const_cast(this));
m_partView = m_view;
}
return m_partView;
}
diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp
index 9afa312db..afc914d0c 100644
--- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp
+++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp
@@ -1,2975 +1,2978 @@
/***************************************************************************
File : SpreadsheetView.cpp
Project : LabPlot
Description : View class for Spreadsheet
--------------------------------------------------------------------
Copyright : (C) 2011-2019 by Alexander Semke (alexander.semke@web.de)
Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301 USA *
* *
***************************************************************************/
#include "SpreadsheetView.h"
#include "backend/spreadsheet/SpreadsheetModel.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "commonfrontend/spreadsheet/SpreadsheetItemDelegate.h"
#include "commonfrontend/spreadsheet/SpreadsheetHeaderView.h"
#include "backend/datasources/filters/FITSFilter.h"
#include "backend/lib/macros.h"
#include "backend/lib/trace.h"
#include "backend/core/column/Column.h"
#include "backend/core/column/ColumnPrivate.h"
#include "backend/core/datatypes/SimpleCopyThroughFilter.h"
#include "backend/core/datatypes/Double2StringFilter.h"
#include "backend/core/datatypes/String2DoubleFilter.h"
#include "backend/core/datatypes/DateTime2StringFilter.h"
#include "backend/core/datatypes/String2DateTimeFilter.h"
#include "backend/pivot/PivotTable.h"
#include "backend/generalTest/HypothesisTest.h"
-
+#include "backend/generalTest/CorrelationCoefficient.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h"
#include "kdefrontend/spreadsheet/PlotDataDialog.h"
#include "kdefrontend/spreadsheet/AddSubtractValueDialog.h"
#include "kdefrontend/spreadsheet/DropValuesDialog.h"
#include "kdefrontend/spreadsheet/SortDialog.h"
#include "kdefrontend/spreadsheet/RandomValuesDialog.h"
#include "kdefrontend/spreadsheet/EquidistantValuesDialog.h"
#include "kdefrontend/spreadsheet/FunctionValuesDialog.h"
#include "kdefrontend/spreadsheet/StatisticsDialog.h"
#include //for std::reverse
/*!
\class SpreadsheetView
\brief View class for Spreadsheet
\ingroup commonfrontend
*/
SpreadsheetView::SpreadsheetView(Spreadsheet* spreadsheet, bool readOnly) : QWidget(),
m_tableView(new QTableView(this)),
m_spreadsheet(spreadsheet),
m_model(new SpreadsheetModel(spreadsheet)),
m_readOnly(readOnly) {
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(m_tableView);
if (m_readOnly)
m_tableView->setEditTriggers(QTableView::NoEditTriggers);
init();
//resize the view to show alls columns and the first 50 rows.
//no need to resize the view when the project is being opened,
//all views will be resized to the stored values at the end
if (!m_spreadsheet->isLoading()) {
int w = m_tableView->verticalHeader()->width();
int h = m_horizontalHeader->height();
for (int i = 0; i < m_horizontalHeader->count(); ++i)
w += m_horizontalHeader->sectionSize(i);
if (m_tableView->verticalHeader()->count() > 50 || m_tableView->verticalHeader()->count() < 10)
h += m_tableView->verticalHeader()->sectionSize(0)*50;
else
h += m_tableView->verticalHeader()->sectionSize(0)*m_tableView->verticalHeader()->count();
resize(w+50, h);
}
KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet"));
showComments(group.readEntry(QLatin1String("ShowComments"), false));
}
SpreadsheetView::~SpreadsheetView() {
delete m_model;
}
void SpreadsheetView::init() {
initActions();
initMenus();
m_tableView->setModel(m_model);
m_tableView->setItemDelegate(new SpreadsheetItemDelegate(this));
m_tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
//horizontal header
m_horizontalHeader = new SpreadsheetHeaderView(this);
m_horizontalHeader->setSectionsClickable(true);
m_horizontalHeader->setHighlightSections(true);
m_tableView->setHorizontalHeader(m_horizontalHeader);
m_horizontalHeader->setSectionsMovable(true);
m_horizontalHeader->installEventFilter(this);
resizeHeader();
connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionMoved, this, &SpreadsheetView::handleHorizontalSectionMoved);
connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionDoubleClicked, this, &SpreadsheetView::handleHorizontalHeaderDoubleClicked);
connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionResized, this, &SpreadsheetView::handleHorizontalSectionResized);
connect(m_horizontalHeader, &SpreadsheetHeaderView::sectionClicked, this, &SpreadsheetView::columnClicked);
// vertical header
QHeaderView* v_header = m_tableView->verticalHeader();
v_header->setSectionResizeMode(QHeaderView::Fixed);
v_header->setSectionsMovable(false);
v_header->installEventFilter(this);
setFocusPolicy(Qt::StrongFocus);
setFocus();
installEventFilter(this);
connectActions();
showComments(false);
connect(m_model, &SpreadsheetModel::headerDataChanged, this, &SpreadsheetView::updateHeaderGeometry);
connect(m_model, &SpreadsheetModel::headerDataChanged, this, &SpreadsheetView::handleHeaderDataChanged);
connect(m_spreadsheet, &Spreadsheet::aspectAdded, this, &SpreadsheetView::handleAspectAdded);
connect(m_spreadsheet, &Spreadsheet::aspectAboutToBeRemoved,this, &SpreadsheetView::handleAspectAboutToBeRemoved);
connect(m_spreadsheet, &Spreadsheet::requestProjectContextMenu, this, &SpreadsheetView::createContextMenu);
for (auto* column : m_spreadsheet->children())
connect(column, &Column::requestProjectContextMenu, this, &SpreadsheetView::createColumnContextMenu);
//selection relevant connections
QItemSelectionModel* sel_model = m_tableView->selectionModel();
connect(sel_model, &QItemSelectionModel::currentColumnChanged, this, &SpreadsheetView::currentColumnChanged);
connect(sel_model, &QItemSelectionModel::selectionChanged, this, &SpreadsheetView::selectionChanged);
connect(sel_model, &QItemSelectionModel::selectionChanged, this, &SpreadsheetView::selectionChanged);
connect(m_spreadsheet, &Spreadsheet::columnSelected, this, &SpreadsheetView::selectColumn);
connect(m_spreadsheet, &Spreadsheet::columnDeselected, this, &SpreadsheetView::deselectColumn);
}
/*!
set the column sizes to the saved values or resize to content if no size was saved yet
*/
void SpreadsheetView::resizeHeader() {
DEBUG("SpreadsheetView::resizeHeader()");
for (int i = 0; i < m_spreadsheet->children().size(); ++i) {
Column* col = m_spreadsheet->child(i);
if (col->width() == 0)
m_tableView->resizeColumnToContents(i);
else
m_tableView->setColumnWidth(i, col->width());
}
}
void SpreadsheetView::initActions() {
// selection related actions
action_cut_selection = new QAction(QIcon::fromTheme("edit-cut"), i18n("Cu&t"), this);
action_copy_selection = new QAction(QIcon::fromTheme("edit-copy"), i18n("&Copy"), this);
action_paste_into_selection = new QAction(QIcon::fromTheme("edit-paste"), i18n("Past&e"), this);
action_mask_selection = new QAction(QIcon::fromTheme("edit-node"), i18n("&Mask Selection"), this);
action_unmask_selection = new QAction(QIcon::fromTheme("format-remove-node"), i18n("&Unmask Selection"), this);
action_clear_selection = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selection"), this);
action_select_all = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this);
// action_set_formula = new QAction(QIcon::fromTheme(QString()), i18n("Assign &Formula"), this);
// action_recalculate = new QAction(QIcon::fromTheme(QString()), i18n("Recalculate"), this);
action_fill_sel_row_numbers = new QAction(QIcon::fromTheme(QString()), i18n("Row Numbers"), this);
action_fill_row_numbers = new QAction(QIcon::fromTheme(QString()), i18n("Row Numbers"), this);
action_fill_random = new QAction(QIcon::fromTheme(QString()), i18n("Uniform Random Values"), this);
action_fill_random_nonuniform = new QAction(QIcon::fromTheme(QString()), i18n("Random Values"), this);
action_fill_equidistant = new QAction(QIcon::fromTheme(QString()), i18n("Equidistant Values"), this);
action_fill_function = new QAction(QIcon::fromTheme(QString()), i18n("Function Values"), this);
action_fill_const = new QAction(QIcon::fromTheme(QString()), i18n("Const Values"), this);
//spreadsheet related actions
action_toggle_comments = new QAction(QIcon::fromTheme("document-properties"), i18n("Show Comments"), this);
action_clear_spreadsheet = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Spreadsheet"), this);
action_clear_masks = new QAction(QIcon::fromTheme("format-remove-node"), i18n("Clear Masks"), this);
action_sort_spreadsheet = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Sort Spreadsheet"), this);
action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this);
action_statistics_all_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this );
action_pivot_table = new QAction(QIcon::fromTheme("table"), i18n("Pivot Table"), this);
action_do_hypothesis_test = new QAction(i18n("Hypothesis Test"), this);
+ action_find_correlation_coefficient = new QAction(i18n("Correlation Coefficient"), this);
// column related actions
action_insert_column_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Column Left"), this);
action_insert_column_right = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("Insert Column Right"), this);
action_insert_columns_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Multiple Columns Left"), this);
action_insert_columns_right = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("Insert Multiple Columns Right"), this);
action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remove Selected Columns"), this);
action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Selected Columns"), this);
action_set_as_none = new QAction(i18n("None"), this);
action_set_as_none->setData(AbstractColumn::NoDesignation);
action_set_as_x = new QAction("X", this);
action_set_as_x->setData(AbstractColumn::X);
action_set_as_y = new QAction("Y", this);
action_set_as_y->setData(AbstractColumn::Y);
action_set_as_z = new QAction("Z", this);
action_set_as_z->setData(AbstractColumn::Z);
action_set_as_xerr = new QAction(i18n("X-error"), this);
action_set_as_xerr->setData(AbstractColumn::XError);
action_set_as_xerr_minus = new QAction(i18n("X-error minus"), this);
action_set_as_xerr_minus->setData(AbstractColumn::XErrorMinus);
action_set_as_xerr_plus = new QAction(i18n("X-error plus"), this);
action_set_as_xerr_plus->setData(AbstractColumn::XErrorPlus);
action_set_as_yerr = new QAction(i18n("Y-error"), this);
action_set_as_yerr->setData(AbstractColumn::YError);
action_set_as_yerr_minus = new QAction(i18n("Y-error minus"), this);
action_set_as_yerr_minus->setData(AbstractColumn::YErrorMinus);
action_set_as_yerr_plus = new QAction(i18n("Y-error plus"), this);
action_set_as_yerr_plus->setData(AbstractColumn::YErrorPlus);
//data manipulation
action_add_value = new QAction(i18n("Add Value"), this);
action_add_value->setData(AddSubtractValueDialog::Add);
action_subtract_value = new QAction(i18n("Subtract Value"), this);
action_subtract_value->setData(AddSubtractValueDialog::Subtract);
action_multiply_value = new QAction(i18n("Multiply by Value"), this);
action_multiply_value->setData(AddSubtractValueDialog::Multiply);
action_divide_value = new QAction(i18n("Divide by Value"), this);
action_divide_value->setData(AddSubtractValueDialog::Divide);
action_drop_values = new QAction(QIcon::fromTheme(QString()), i18n("Drop Values"), this);
action_mask_values = new QAction(QIcon::fromTheme(QString()), i18n("Mask Values"), this);
action_reverse_columns = new QAction(QIcon::fromTheme(QString()), i18n("Reverse"), this);
// action_join_columns = new QAction(QIcon::fromTheme(QString()), i18n("Join"), this);
action_normalize_columns = new QAction(QIcon::fromTheme(QString()), i18n("&Normalize"), this);
action_normalize_selection = new QAction(QIcon::fromTheme(QString()), i18n("&Normalize Selection"), this);
//sort and statistics
action_sort_columns = new QAction(QIcon::fromTheme(QString()), i18n("&Selected Columns"), this);
action_sort_asc_column = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Ascending"), this);
action_sort_desc_column = new QAction(QIcon::fromTheme("view-sort-descending"), i18n("&Descending"), this);
action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Column Statisti&cs"), this);
// row related actions
action_insert_row_above = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("Insert Row Above"), this);
action_insert_row_below = new QAction(QIcon::fromTheme("edit-table-insert-row-below"), i18n("Insert Row Below"), this);
action_insert_rows_above = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("Insert Multiple Rows Above"), this);
action_insert_rows_below = new QAction(QIcon::fromTheme("edit-table-insert-row-below"), i18n("Insert Multiple Rows Below"), this);
action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Selected Rows"), this);
action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selected Rows"), this);
action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Row Statisti&cs"), this);
//plot data action
action_plot_data_xycurve = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-Curve"), this);
action_plot_data_xycurve->setData(PlotDataDialog::PlotXYCurve);
action_plot_data_histogram = new QAction(QIcon::fromTheme("labplot-histogram"), i18n("Histogram"), this);
action_plot_data_histogram->setData(PlotDataDialog::PlotHistogram);
//Analyze and plot menu actions
addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Reduce Data"), this);
// addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Reduce Data"), this);
addDataReductionAction->setData(PlotDataDialog::DataReduction);
addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Differentiate"), this);
// addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("Differentiate"), this);
addDifferentiationAction->setData(PlotDataDialog::Differentiation);
addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Integrate"), this);
// addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("Integrate"), this);
addIntegrationAction->setData(PlotDataDialog::Integration);
addInterpolationAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("Interpolate"), this);
addInterpolationAction->setData(PlotDataDialog::Interpolation);
addSmoothAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("Smooth"), this);
addSmoothAction->setData(PlotDataDialog::Smoothing);
QAction* fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Linear"), this);
fitAction->setData(PlotDataDialog::FitLinear);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Power"), this);
fitAction->setData(PlotDataDialog::FitPower);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Exponential (degree 1)"), this);
fitAction->setData(PlotDataDialog::FitExp1);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Exponential (degree 2)"), this);
fitAction->setData(PlotDataDialog::FitExp2);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Inverse Exponential"), this);
fitAction->setData(PlotDataDialog::FitInvExp);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Gauss"), this);
fitAction->setData(PlotDataDialog::FitGauss);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Cauchy-Lorentz"), this);
fitAction->setData(PlotDataDialog::FitCauchyLorentz);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Arc Tangent"), this);
fitAction->setData(PlotDataDialog::FitTan);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Hyperbolic Tangent"), this);
fitAction->setData(PlotDataDialog::FitTanh);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Error Function"), this);
fitAction->setData(PlotDataDialog::FitErrFunc);
addFitAction.append(fitAction);
fitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Custom"), this);
fitAction->setData(PlotDataDialog::FitCustom);
addFitAction.append(fitAction);
addFourierFilterAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), this);
addFourierFilterAction->setData(PlotDataDialog::FourierFilter);
}
void SpreadsheetView::initMenus() {
//Selection menu
m_selectionMenu = new QMenu(i18n("Selection"), this);
m_selectionMenu->setIcon(QIcon::fromTheme("selection"));
QMenu* submenu = nullptr;
if (!m_readOnly) {
submenu = new QMenu(i18n("Fi&ll Selection With"), this);
submenu->setIcon(QIcon::fromTheme("select-rectangle"));
submenu->addAction(action_fill_sel_row_numbers);
submenu->addAction(action_fill_const);
m_selectionMenu->addMenu(submenu);
m_selectionMenu->addSeparator();
m_selectionMenu->addAction(action_cut_selection);
}
m_selectionMenu->addAction(action_copy_selection);
if (!m_readOnly) {
m_selectionMenu->addAction(action_paste_into_selection);
m_selectionMenu->addAction(action_clear_selection);
m_selectionMenu->addSeparator();
m_selectionMenu->addAction(action_mask_selection);
m_selectionMenu->addAction(action_unmask_selection);
m_selectionMenu->addSeparator();
m_selectionMenu->addAction(action_normalize_selection);
}
//plot data menu
m_plotDataMenu = new QMenu(i18n("Plot Data"), this);
m_plotDataMenu->addAction(action_plot_data_xycurve);
m_plotDataMenu->addAction(action_plot_data_histogram);
// Column menu
m_columnMenu = new QMenu(this);
m_columnMenu->addMenu(m_plotDataMenu);
// Data manipulation sub-menu
QMenu* dataManipulationMenu = new QMenu(i18n("Data Manipulation"), this);
dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw"));
dataManipulationMenu->addAction(addDataReductionAction);
// Data fit sub-menu
QMenu* dataFitMenu = new QMenu(i18n("Fit"), this);
dataFitMenu->setIcon(QIcon::fromTheme("labplot-xy-fit-curve"));
dataFitMenu->addAction(addFitAction.at(0));
dataFitMenu->addAction(addFitAction.at(1));
dataFitMenu->addAction(addFitAction.at(2));
dataFitMenu->addAction(addFitAction.at(3));
dataFitMenu->addAction(addFitAction.at(4));
dataFitMenu->addSeparator();
dataFitMenu->addAction(addFitAction.at(5));
dataFitMenu->addAction(addFitAction.at(6));
dataFitMenu->addSeparator();
dataFitMenu->addAction(addFitAction.at(7));
dataFitMenu->addAction(addFitAction.at(8));
dataFitMenu->addAction(addFitAction.at(9));
dataFitMenu->addSeparator();
dataFitMenu->addAction(addFitAction.at(10));
//analyze and plot data menu
m_analyzePlotMenu = new QMenu(i18n("Analyze and Plot Data"), this);
m_analyzePlotMenu->insertMenu(nullptr, dataManipulationMenu);
m_analyzePlotMenu->addSeparator();
m_analyzePlotMenu->addAction(addDifferentiationAction);
m_analyzePlotMenu->addAction(addIntegrationAction);
m_analyzePlotMenu->addSeparator();
m_analyzePlotMenu->addAction(addInterpolationAction);
m_analyzePlotMenu->addAction(addSmoothAction);
m_analyzePlotMenu->addAction(addFourierFilterAction);
m_analyzePlotMenu->addSeparator();
m_analyzePlotMenu->addMenu(dataFitMenu);
m_columnMenu->addMenu(m_analyzePlotMenu);
m_columnSetAsMenu = new QMenu(i18n("Set Column As"), this);
m_columnMenu->addSeparator();
m_columnSetAsMenu->addAction(action_set_as_x);
m_columnSetAsMenu->addAction(action_set_as_y);
m_columnSetAsMenu->addAction(action_set_as_z);
m_columnSetAsMenu->addSeparator();
m_columnSetAsMenu->addAction(action_set_as_xerr);
m_columnSetAsMenu->addAction(action_set_as_xerr_minus);
m_columnSetAsMenu->addAction(action_set_as_xerr_plus);
m_columnSetAsMenu->addSeparator();
m_columnSetAsMenu->addAction(action_set_as_yerr);
m_columnSetAsMenu->addAction(action_set_as_yerr_minus);
m_columnSetAsMenu->addAction(action_set_as_yerr_plus);
m_columnSetAsMenu->addSeparator();
m_columnSetAsMenu->addAction(action_set_as_none);
m_columnMenu->addMenu(m_columnSetAsMenu);
if (!m_readOnly) {
m_columnGenerateDataMenu = new QMenu(i18n("Generate Data"), this);
m_columnGenerateDataMenu->addAction(action_fill_row_numbers);
m_columnGenerateDataMenu->addAction(action_fill_const);
m_columnGenerateDataMenu->addAction(action_fill_equidistant);
m_columnGenerateDataMenu->addAction(action_fill_random_nonuniform);
m_columnGenerateDataMenu->addAction(action_fill_function);
m_columnMenu->addSeparator();
m_columnMenu->addMenu(m_columnGenerateDataMenu);
m_columnMenu->addSeparator();
m_columnManipulateDataMenu = new QMenu(i18n("Manipulate Data"), this);
m_columnManipulateDataMenu->addAction(action_add_value);
m_columnManipulateDataMenu->addAction(action_subtract_value);
m_columnManipulateDataMenu->addAction(action_multiply_value);
m_columnManipulateDataMenu->addAction(action_divide_value);
m_columnManipulateDataMenu->addSeparator();
m_columnManipulateDataMenu->addAction(action_reverse_columns);
m_columnManipulateDataMenu->addSeparator();
m_columnManipulateDataMenu->addAction(action_drop_values);
m_columnManipulateDataMenu->addAction(action_mask_values);
m_columnManipulateDataMenu->addSeparator();
// m_columnManipulateDataMenu->addAction(action_join_columns);
m_columnManipulateDataMenu->addAction(action_normalize_columns);
m_columnMenu->addMenu(m_columnManipulateDataMenu);
m_columnMenu->addSeparator();
m_columnSortMenu = new QMenu(i18n("Sort"), this);
m_columnSortMenu->setIcon(QIcon::fromTheme("view-sort-ascending"));
m_columnSortMenu->addAction(action_sort_asc_column);
m_columnSortMenu->addAction(action_sort_desc_column);
m_columnSortMenu->addAction(action_sort_columns);
m_columnMenu->addSeparator();
m_columnMenu->addMenu(m_columnSortMenu);
m_columnMenu->addSeparator();
m_columnMenu->addAction(action_insert_column_left);
m_columnMenu->addAction(action_insert_column_right);
m_columnMenu->addSeparator();
m_columnMenu->addAction(action_insert_columns_left);
m_columnMenu->addAction(action_insert_columns_right);
m_columnMenu->addSeparator();
m_columnMenu->addAction(action_remove_columns);
m_columnMenu->addAction(action_clear_columns);
}
m_columnMenu->addSeparator();
m_columnMenu->addAction(action_toggle_comments);
m_columnMenu->addSeparator();
m_columnMenu->addAction(action_statistics_columns);
//Spreadsheet menu
m_spreadsheetMenu = new QMenu(this);
m_spreadsheetMenu->addMenu(m_plotDataMenu);
m_spreadsheetMenu->addMenu(m_analyzePlotMenu);
m_spreadsheetMenu->addAction(action_do_hypothesis_test);
+ m_spreadsheetMenu->addAction(action_find_correlation_coefficient);
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addAction(action_pivot_table);
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addMenu(m_selectionMenu);
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addAction(action_select_all);
if (!m_readOnly) {
m_spreadsheetMenu->addAction(action_clear_spreadsheet);
m_spreadsheetMenu->addAction(action_clear_masks);
m_spreadsheetMenu->addAction(action_sort_spreadsheet);
}
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addAction(action_go_to_cell);
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addAction(action_toggle_comments);
m_spreadsheetMenu->addSeparator();
m_spreadsheetMenu->addAction(action_statistics_all_columns);
//Row menu
m_rowMenu = new QMenu(this);
if (!m_readOnly) {
submenu = new QMenu(i18n("Fi&ll Selection With"), this);
submenu->addAction(action_fill_sel_row_numbers);
submenu->addAction(action_fill_const);
m_rowMenu->addMenu(submenu);
m_rowMenu->addSeparator();
m_rowMenu->addAction(action_insert_row_above);
m_rowMenu->addAction(action_insert_row_below);
m_rowMenu->addSeparator();
m_rowMenu->addAction(action_insert_rows_above);
m_rowMenu->addAction(action_insert_rows_below);
m_rowMenu->addSeparator();
m_rowMenu->addAction(action_remove_rows);
m_rowMenu->addAction(action_clear_rows);
}
m_rowMenu->addSeparator();
m_rowMenu->addAction(action_statistics_rows);
action_statistics_rows->setVisible(false);
}
void SpreadsheetView::connectActions() {
connect(action_cut_selection, &QAction::triggered, this, &SpreadsheetView::cutSelection);
connect(action_copy_selection, &QAction::triggered, this, &SpreadsheetView::copySelection);
connect(action_paste_into_selection, &QAction::triggered, this, &SpreadsheetView::pasteIntoSelection);
connect(action_mask_selection, &QAction::triggered, this, &SpreadsheetView::maskSelection);
connect(action_unmask_selection, &QAction::triggered, this, &SpreadsheetView::unmaskSelection);
connect(action_clear_selection, &QAction::triggered, this, &SpreadsheetView::clearSelectedCells);
// connect(action_recalculate, &QAction::triggered, this, &SpreadsheetView::recalculateSelectedCells);
connect(action_fill_row_numbers, &QAction::triggered, this, &SpreadsheetView::fillWithRowNumbers);
connect(action_fill_sel_row_numbers, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithRowNumbers);
// connect(action_fill_random, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithRandomNumbers);
connect(action_fill_random_nonuniform, &QAction::triggered, this, &SpreadsheetView::fillWithRandomValues);
connect(action_fill_equidistant, &QAction::triggered, this, &SpreadsheetView::fillWithEquidistantValues);
connect(action_fill_function, &QAction::triggered, this, &SpreadsheetView::fillWithFunctionValues);
connect(action_fill_const, &QAction::triggered, this, &SpreadsheetView::fillSelectedCellsWithConstValues);
connect(action_select_all, &QAction::triggered, m_tableView, &QTableView::selectAll);
connect(action_clear_spreadsheet, &QAction::triggered, m_spreadsheet, &Spreadsheet::clear);
connect(action_clear_masks, &QAction::triggered, m_spreadsheet, &Spreadsheet::clearMasks);
connect(action_sort_spreadsheet, &QAction::triggered, this, &SpreadsheetView::sortSpreadsheet);
connect(action_go_to_cell, &QAction::triggered, this,
static_cast(&SpreadsheetView::goToCell));
connect(action_pivot_table, &QAction::triggered, this, &SpreadsheetView::createPivotTable);
connect(action_do_hypothesis_test, &QAction::triggered, this, &SpreadsheetView::doHypothesisTest);
+ connect(action_find_correlation_coefficient, &QAction::triggered, this, &SpreadsheetView::findCorrelationCoefficient);
connect(action_insert_column_left, &QAction::triggered, this, &SpreadsheetView::insertColumnLeft);
connect(action_insert_column_right, &QAction::triggered, this, &SpreadsheetView::insertColumnRight);
connect(action_insert_columns_left, &QAction::triggered, this, static_cast(&SpreadsheetView::insertColumnsLeft));
connect(action_insert_columns_right, &QAction::triggered, this, static_cast(&SpreadsheetView::insertColumnsRight));
connect(action_remove_columns, &QAction::triggered, this, &SpreadsheetView::removeSelectedColumns);
connect(action_clear_columns, &QAction::triggered, this, &SpreadsheetView::clearSelectedColumns);
connect(action_set_as_none, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_x, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_y, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_z, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_xerr, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_xerr_minus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_xerr_plus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_yerr, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_yerr_minus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
connect(action_set_as_yerr_plus, &QAction::triggered, this, &SpreadsheetView::setSelectionAs);
//data manipulation
connect(action_add_value, &QAction::triggered, this, &SpreadsheetView::modifyValues);
connect(action_subtract_value, &QAction::triggered, this, &SpreadsheetView::modifyValues);
connect(action_multiply_value, &QAction::triggered, this, &SpreadsheetView::modifyValues);
connect(action_divide_value, &QAction::triggered, this, &SpreadsheetView::modifyValues);
connect(action_reverse_columns, &QAction::triggered, this, &SpreadsheetView::reverseColumns);
connect(action_drop_values, &QAction::triggered, this, &SpreadsheetView::dropColumnValues);
connect(action_mask_values, &QAction::triggered, this, &SpreadsheetView::maskColumnValues);
// connect(action_join_columns, &QAction::triggered, this, &SpreadsheetView::joinColumns);
connect(action_normalize_columns, &QAction::triggered, this, &SpreadsheetView::normalizeSelectedColumns);
connect(action_normalize_selection, &QAction::triggered, this, &SpreadsheetView::normalizeSelection);
//sort
connect(action_sort_columns, &QAction::triggered, this, &SpreadsheetView::sortSelectedColumns);
connect(action_sort_asc_column, &QAction::triggered, this, &SpreadsheetView::sortColumnAscending);
connect(action_sort_desc_column, &QAction::triggered, this, &SpreadsheetView::sortColumnDescending);
//statistics
connect(action_statistics_columns, &QAction::triggered, this, &SpreadsheetView::showColumnStatistics);
connect(action_statistics_all_columns, &QAction::triggered, this, &SpreadsheetView::showAllColumnsStatistics);
connect(action_insert_row_above, &QAction::triggered, this, &SpreadsheetView::insertRowAbove);
connect(action_insert_row_below, &QAction::triggered, this, &SpreadsheetView::insertRowBelow);
connect(action_insert_rows_above, &QAction::triggered, this, static_cast(&SpreadsheetView::insertRowsAbove));
connect(action_insert_rows_below, &QAction::triggered, this, static_cast(&SpreadsheetView::insertRowsBelow));
connect(action_remove_rows, &QAction::triggered, this, &SpreadsheetView::removeSelectedRows);
connect(action_clear_rows, &QAction::triggered, this, &SpreadsheetView::clearSelectedRows);
connect(action_statistics_rows, &QAction::triggered, this, &SpreadsheetView::showRowStatistics);
connect(action_toggle_comments, &QAction::triggered, this, &SpreadsheetView::toggleComments);
connect(action_plot_data_xycurve, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(action_plot_data_histogram, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addDataReductionAction, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addDifferentiationAction, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addIntegrationAction, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addInterpolationAction, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addSmoothAction, &QAction::triggered, this, &SpreadsheetView::plotData);
for (const auto& action : addFitAction)
connect(action, &QAction::triggered, this, &SpreadsheetView::plotData);
connect(addFourierFilterAction, &QAction::triggered,this, &SpreadsheetView::plotData);
}
void SpreadsheetView::fillToolBar(QToolBar* toolBar) {
if (!m_readOnly) {
toolBar->addAction(action_insert_row_above);
toolBar->addAction(action_insert_row_below);
toolBar->addAction(action_remove_rows);
}
toolBar->addAction(action_statistics_rows);
toolBar->addSeparator();
if (!m_readOnly) {
toolBar->addAction(action_insert_column_left);
toolBar->addAction(action_insert_column_right);
toolBar->addAction(action_remove_columns);
}
toolBar->addAction(action_statistics_columns);
if (!m_readOnly) {
toolBar->addSeparator();
toolBar->addAction(action_sort_asc_column);
toolBar->addAction(action_sort_desc_column);
}
}
/*!
* Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions.
* The menu is used
* - as the context menu in SpreadsheetView
* - as the "spreadsheet menu" in the main menu-bar (called form MainWin)
* - as a part of the spreadsheet context menu in project explorer
*/
void SpreadsheetView::createContextMenu(QMenu* menu) {
Q_ASSERT(menu);
checkSpreadsheetMenu();
QAction* firstAction = nullptr;
// if we're populating the context menu for the project explorer, then
//there're already actions available there. Skip the first title-action
//and insert the action at the beginning of the menu.
if (menu->actions().size()>1)
firstAction = menu->actions().at(1);
if (m_spreadsheet->columnCount() > 0 && m_spreadsheet->rowCount() > 0) {
menu->insertMenu(firstAction, m_plotDataMenu);
menu->insertSeparator(firstAction);
}
menu->insertMenu(firstAction, m_selectionMenu);
menu->insertSeparator(firstAction);
menu->insertAction(firstAction, action_select_all);
if (!m_readOnly) {
menu->insertAction(firstAction, action_clear_spreadsheet);
menu->insertAction(firstAction, action_clear_masks);
menu->insertAction(firstAction, action_sort_spreadsheet);
menu->insertSeparator(firstAction);
}
menu->insertAction(firstAction, action_go_to_cell);
menu->insertSeparator(firstAction);
menu->insertAction(firstAction, action_toggle_comments);
menu->insertSeparator(firstAction);
menu->insertAction(firstAction, action_statistics_all_columns);
menu->insertSeparator(firstAction);
}
/*!
* adds column specific actions in SpreadsheetView to the context menu shown in the project explorer.
*/
void SpreadsheetView::createColumnContextMenu(QMenu* menu) {
const Column* column = dynamic_cast(QObject::sender());
if (!column)
return; //should never happen, since the sender is always a Column
if (column->isNumeric()) {
QAction* firstAction = menu->actions().at(1);
menu->insertMenu(firstAction, m_columnSetAsMenu);
const bool hasValues = column->hasValues();
if (!m_readOnly) {
menu->insertSeparator(firstAction);
menu->insertMenu(firstAction, m_columnGenerateDataMenu);
menu->insertSeparator(firstAction);
menu->insertMenu(firstAction, m_columnManipulateDataMenu);
menu->insertSeparator(firstAction);
menu->insertMenu(firstAction, m_columnSortMenu);
action_sort_asc_column->setVisible(true);
action_sort_desc_column->setVisible(true);
action_sort_columns->setVisible(false);
//in case no cells are available, deactivate the actions that only make sense in the presence of cells
const bool hasCells = m_spreadsheet->rowCount() > 0;
m_columnGenerateDataMenu->setEnabled(hasCells);
//in case no valid numerical values are available, deactivate the actions that only make sense in the presence of values
action_reverse_columns->setEnabled(hasValues);
action_drop_values->setEnabled(hasValues);
action_mask_values->setEnabled(hasValues);
action_normalize_columns->setEnabled(hasValues);
m_columnSortMenu->setEnabled(hasValues);
}
menu->insertSeparator(firstAction);
menu->insertAction(firstAction, action_statistics_columns);
action_statistics_columns->setEnabled(hasValues);
}
}
//SLOTS
void SpreadsheetView::handleAspectAdded(const AbstractAspect* aspect) {
const Column* col = dynamic_cast(aspect);
if (!col || col->parentAspect() != m_spreadsheet)
return;
int index = m_spreadsheet->indexOfChild(col);
if (col->width() == 0)
m_tableView->resizeColumnToContents(index);
else
m_tableView->setColumnWidth(index, col->width());
connect(col, &Column::requestProjectContextMenu, this, &SpreadsheetView::createColumnContextMenu);
}
void SpreadsheetView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) {
const Column* col = dynamic_cast(aspect);
if (!col || col->parentAspect() != m_spreadsheet)
return;
disconnect(col, nullptr, this, nullptr);
}
void SpreadsheetView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) {
Q_UNUSED(logicalIndex);
Q_UNUSED(oldSize);
//save the new size in the column
Column* col = m_spreadsheet->child(logicalIndex);
col->setWidth(newSize);
}
void SpreadsheetView::goToCell(int row, int col) {
QModelIndex index = m_model->index(row, col);
m_tableView->scrollTo(index);
m_tableView->setCurrentIndex(index);
}
void SpreadsheetView::createPivotTable() {
PivotTable* pivot = new PivotTable(i18n("Pivot Table for %1", m_spreadsheet->name()));
pivot->setDataSourceType(PivotTable::DataSourceSpreadsheet);
pivot->setDataSourceSpreadsheet(m_spreadsheet);
m_spreadsheet->parentAspect()->addChild(pivot);
}
-void SpreadsheetView::doHypothesisTest()
-{
- QDEBUG("in do hypothesis test");
+void SpreadsheetView::doHypothesisTest() {
HypothesisTest* hypothesis_test = new HypothesisTest(i18n("Hypothesis Test for %1", m_spreadsheet->name()));
- QDEBUG("created object");
hypothesis_test->setDataSourceType(HypothesisTest::DataSourceSpreadsheet);
- QDEBUG("data source type set");
hypothesis_test->setDataSourceSpreadsheet(m_spreadsheet);
- QDEBUG("added spreadsheet");
- QDEBUG(hypothesis_test->name());
m_spreadsheet->parentAspect()->addChild(hypothesis_test);
- QDEBUG("out of do hypothesis test");
}
+void SpreadsheetView::findCorrelationCoefficient() {
+ CorrelationCoefficient* correlation_coefficient = new CorrelationCoefficient(i18n("Correlation Coefficient for %1", m_spreadsheet->name()));
+ correlation_coefficient->setDataSourceType(CorrelationCoefficient::DataSourceSpreadsheet);
+ correlation_coefficient->setDataSourceSpreadsheet(m_spreadsheet);
+ m_spreadsheet->parentAspect()->addChild(correlation_coefficient);
+}
+
void SpreadsheetView::handleHorizontalSectionMoved(int index, int from, int to) {
Q_UNUSED(index);
static bool inside = false;
if (inside) return;
Q_ASSERT(index == from);
inside = true;
m_tableView->horizontalHeader()->moveSection(to, from);
inside = false;
m_spreadsheet->moveColumn(from, to);
}
//TODO Implement the "change of the column name"-mode upon a double click
void SpreadsheetView::handleHorizontalHeaderDoubleClicked(int index) {
Q_UNUSED(index);
}
/*!
Returns whether comments are shown currently or not
*/
bool SpreadsheetView::areCommentsShown() const {
return m_horizontalHeader->areCommentsShown();
}
/*!
toggles the column comment in the horizontal header
*/
void SpreadsheetView::toggleComments() {
showComments(!areCommentsShown());
//TODO
if (areCommentsShown())
action_toggle_comments->setText(i18n("Hide Comments"));
else
action_toggle_comments->setText(i18n("Show Comments"));
}
//! Shows (\c on=true) or hides (\c on=false) the column comments in the horizontal header
void SpreadsheetView::showComments(bool on) {
m_horizontalHeader->showComments(on);
}
void SpreadsheetView::currentColumnChanged(const QModelIndex & current, const QModelIndex & previous) {
Q_UNUSED(previous);
int col = current.column();
if (col < 0 || col >= m_spreadsheet->columnCount())
return;
}
//TODO
void SpreadsheetView::handleHeaderDataChanged(Qt::Orientation orientation, int first, int last) {
if (orientation != Qt::Horizontal) return;
QItemSelectionModel * sel_model = m_tableView->selectionModel();
int col = sel_model->currentIndex().column();
if (col < first || col > last) return;
}
/*!
Returns the number of selected columns.
If \c full is \c true, this function only returns the number of fully selected columns.
*/
int SpreadsheetView::selectedColumnCount(bool full) const {
int count = 0;
const int cols = m_spreadsheet->columnCount();
for (int i = 0; i < cols; i++)
if (isColumnSelected(i, full)) count++;
return count;
}
/*!
Returns the number of (at least partly) selected columns with the plot designation \param pd.
*/
int SpreadsheetView::selectedColumnCount(AbstractColumn::PlotDesignation pd) const{
int count = 0;
const int cols = m_spreadsheet->columnCount();
for (int i = 0; i < cols; i++)
if ( isColumnSelected(i, false) && (m_spreadsheet->column(i)->plotDesignation() == pd) ) count++;
return count;
}
/*!
Returns \c true if column \param col is selected, otherwise returns \c false.
If \param full is \c true, this function only returns true if the whole column is selected.
*/
bool SpreadsheetView::isColumnSelected(int col, bool full) const {
if (full)
return m_tableView->selectionModel()->isColumnSelected(col, QModelIndex());
else
return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex());
}
/*!
Returns all selected columns.
If \param full is true, this function only returns a column if the whole column is selected.
*/
QVector SpreadsheetView::selectedColumns(bool full) const {
QVector columns;
const int cols = m_spreadsheet->columnCount();
for (int i = 0; i < cols; i++)
if (isColumnSelected(i, full)) columns << m_spreadsheet->column(i);
return columns;
}
/*!
Returns \c true if row \param row is selected; otherwise returns \c false
If \param full is \c true, this function only returns \c true if the whole row is selected.
*/
bool SpreadsheetView::isRowSelected(int row, bool full) const {
if (full)
return m_tableView->selectionModel()->isRowSelected(row, QModelIndex());
else
return m_tableView->selectionModel()->rowIntersectsSelection(row, QModelIndex());
}
/*!
Return the index of the first selected column.
If \param full is \c true, this function only looks for fully selected columns.
*/
int SpreadsheetView::firstSelectedColumn(bool full) const {
const int cols = m_spreadsheet->columnCount();
for (int i = 0; i < cols; i++) {
if (isColumnSelected(i, full))
return i;
}
return -1;
}
/*!
Return the index of the last selected column.
If \param full is \c true, this function only looks for fully selected columns.
*/
int SpreadsheetView::lastSelectedColumn(bool full) const {
const int cols = m_spreadsheet->columnCount();
for (int i = cols - 1; i >= 0; i--)
if (isColumnSelected(i, full)) return i;
return -2;
}
/*!
Return the index of the first selected row.
If \param full is \c true, this function only looks for fully selected rows.
*/
int SpreadsheetView::firstSelectedRow(bool full) const{
QModelIndexList indexes;
if (!full)
indexes = m_tableView->selectionModel()->selectedIndexes();
else
indexes = m_tableView->selectionModel()->selectedRows();
if (!indexes.empty())
return indexes.first().row();
else
return -1;
}
/*!
Return the index of the last selected row.
If \param full is \c true, this function only looks for fully selected rows.
*/
int SpreadsheetView::lastSelectedRow(bool full) const {
QModelIndexList indexes;
if (!full)
indexes = m_tableView->selectionModel()->selectedIndexes();
else
indexes = m_tableView->selectionModel()->selectedRows();
if (!indexes.empty())
return indexes.last().row();
else
return -2;
}
/*!
Return whether a cell is selected
*/
bool SpreadsheetView::isCellSelected(int row, int col) const {
if (row < 0 || col < 0 || row >= m_spreadsheet->rowCount() || col >= m_spreadsheet->columnCount())
return false;
return m_tableView->selectionModel()->isSelected(m_model->index(row, col));
}
/*!
Get the complete set of selected rows.
*/
IntervalAttribute SpreadsheetView::selectedRows(bool full) const {
IntervalAttribute result;
const int rows = m_spreadsheet->rowCount();
for (int i = 0; i < rows; i++)
if (isRowSelected(i, full))
result.setValue(i, true);
return result;
}
/*!
Select/Deselect a cell.
*/
void SpreadsheetView::setCellSelected(int row, int col, bool select) {
m_tableView->selectionModel()->select(m_model->index(row, col),
select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
}
/*!
Select/Deselect a range of cells.
*/
void SpreadsheetView::setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select) {
QModelIndex top_left = m_model->index(first_row, first_col);
QModelIndex bottom_right = m_model->index(last_row, last_col);
m_tableView->selectionModel()->select(QItemSelection(top_left, bottom_right),
select ? QItemSelectionModel::SelectCurrent : QItemSelectionModel::Deselect);
}
/*!
Determine the current cell (-1 if no cell is designated as the current).
*/
void SpreadsheetView::getCurrentCell(int* row, int* col) const {
QModelIndex index = m_tableView->selectionModel()->currentIndex();
if (index.isValid()) {
*row = index.row();
*col = index.column();
} else {
*row = -1;
*col = -1;
}
}
bool SpreadsheetView::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::ContextMenu) {
auto* cm_event = static_cast(event);
const QPoint global_pos = cm_event->globalPos();
if (watched == m_tableView->verticalHeader()) {
bool onlyNumeric = true;
for (int i = 0; i < m_spreadsheet->columnCount(); ++i) {
if (m_spreadsheet->column(i)->columnMode() != AbstractColumn::Numeric) {
onlyNumeric = false;
break;
}
}
action_statistics_rows->setVisible(onlyNumeric);
m_rowMenu->exec(global_pos);
} else if (watched == m_horizontalHeader) {
const int col = m_horizontalHeader->logicalIndexAt(cm_event->pos());
if (!isColumnSelected(col, true)) {
QItemSelectionModel* sel_model = m_tableView->selectionModel();
sel_model->clearSelection();
sel_model->select(QItemSelection(m_model->index(0, col, QModelIndex()),
m_model->index(m_model->rowCount()-1, col, QModelIndex())),
QItemSelectionModel::Select);
}
if (selectedColumns().size() == 1) {
action_sort_columns->setVisible(false);
action_sort_asc_column->setVisible(true);
action_sort_desc_column->setVisible(true);
} else {
action_sort_columns->setVisible(true);
action_sort_asc_column->setVisible(false);
action_sort_desc_column->setVisible(false);
}
//check whether we have non-numeric columns selected and deactivate actions for numeric columns
bool numeric = true;
bool plottable = true;
bool datetime = false;
for (const Column* col : selectedColumns()) {
if ( !(col->columnMode() == AbstractColumn::Numeric || col->columnMode() == AbstractColumn::Integer) ) {
datetime = (col->columnMode() == AbstractColumn::DateTime);
if (!datetime)
plottable = false;
numeric = false;
break;
}
}
m_plotDataMenu->setEnabled(plottable);
m_analyzePlotMenu->setEnabled(numeric);
m_columnSetAsMenu->setEnabled(numeric);
if (!m_readOnly) {
m_columnGenerateDataMenu->setEnabled(numeric);
m_columnManipulateDataMenu->setEnabled(numeric || datetime);
m_columnSortMenu->setEnabled(numeric);
}
action_statistics_columns->setEnabled(numeric);
if (numeric) {
bool hasValues = false;
for (const Column* col : selectedColumns()) {
if (col->hasValues()) {
hasValues = true;
break;
}
}
if (!m_readOnly) {
//in case no cells are available, deactivate the actions that only make sense in the presence of cells
const bool hasCells = m_spreadsheet->rowCount() > 0;
m_columnGenerateDataMenu->setEnabled(hasCells);
//in case no valid numerical values are available, deactivate the actions that only make sense in the presence of values
m_columnManipulateDataMenu->setEnabled(hasValues);
m_columnSortMenu->setEnabled(hasValues);
}
action_statistics_columns->setEnabled(hasValues);
}
m_columnMenu->exec(global_pos);
} else if (watched == this) {
checkSpreadsheetMenu();
m_spreadsheetMenu->exec(global_pos);
}
return true;
} else if (event->type() == QEvent::KeyPress) {
auto* key_event = static_cast(event);
if (key_event->matches(QKeySequence::Copy))
copySelection();
else if (key_event->matches(QKeySequence::Paste))
pasteIntoSelection();
}
return QWidget::eventFilter(watched, event);
}
/*!
* disables cell data relevant actions in the spreadsheet menu if there're no cells available
*/
void SpreadsheetView::checkSpreadsheetMenu() {
const bool cellsAvail = m_spreadsheet->columnCount()>0 && m_spreadsheet->rowCount()>0;
m_plotDataMenu->setEnabled(cellsAvail);
m_selectionMenu->setEnabled(cellsAvail);
action_select_all->setEnabled(cellsAvail);
action_clear_spreadsheet->setEnabled(cellsAvail);
action_clear_masks->setEnabled(cellsAvail);
action_sort_spreadsheet->setEnabled(cellsAvail);
action_go_to_cell->setEnabled(cellsAvail);
action_statistics_all_columns->setEnabled(cellsAvail);
}
bool SpreadsheetView::formulaModeActive() const {
return m_model->formulaModeActive();
}
void SpreadsheetView::activateFormulaMode(bool on) {
m_model->activateFormulaMode(on);
}
void SpreadsheetView::goToNextColumn() {
if (m_spreadsheet->columnCount() == 0) return;
QModelIndex idx = m_tableView->currentIndex();
int col = idx.column()+1;
if (col >= m_spreadsheet->columnCount())
col = 0;
m_tableView->setCurrentIndex(idx.sibling(idx.row(), col));
}
void SpreadsheetView::goToPreviousColumn() {
if (m_spreadsheet->columnCount() == 0)
return;
QModelIndex idx = m_tableView->currentIndex();
int col = idx.column()-1;
if (col < 0)
col = m_spreadsheet->columnCount()-1;
m_tableView->setCurrentIndex(idx.sibling(idx.row(), col));
}
void SpreadsheetView::cutSelection() {
int first = firstSelectedRow();
if ( first < 0 )
return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: cut selected cells", m_spreadsheet->name()));
copySelection();
clearSelectedCells();
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::copySelection() {
PERFTRACE("copy selected cells");
const int first_col = firstSelectedColumn();
if (first_col == -1) return;
const int last_col = lastSelectedColumn();
if (last_col == -2) return;
const int first_row = firstSelectedRow();
if (first_row == -1) return;
const int last_row = lastSelectedRow();
if (last_row == -2) return;
const int cols = last_col - first_col + 1;
const int rows = last_row - first_row + 1;
WAIT_CURSOR;
QString output_str;
QVector columns;
QVector formats;
for (int c = 0; c < cols; c++) {
Column* col = m_spreadsheet->column(first_col + c);
columns << col;
const Double2StringFilter* out_fltr = static_cast(col->outputFilter());
formats << out_fltr->numericFormat();
}
QLocale locale;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
const Column* col_ptr = columns.at(c);
if (isCellSelected(first_row + r, first_col + c)) {
// if (formulaModeActive())
// output_str += col_ptr->formula(first_row + r);
// else
if (col_ptr->columnMode() == AbstractColumn::Numeric)
output_str += locale.toString(col_ptr->valueAt(first_row + r), formats.at(c), 16); // copy with max. precision
else
output_str += col_ptr->asStringColumn()->textAt(first_row + r);
}
if (c < cols-1)
output_str += '\t';
}
if (r < rows-1)
output_str += '\n';
}
QApplication::clipboard()->setText(output_str);
RESET_CURSOR;
}
void SpreadsheetView::pasteIntoSelection() {
if (m_spreadsheet->columnCount() < 1 || m_spreadsheet->rowCount() < 1)
return;
const QMimeData* mime_data = QApplication::clipboard()->mimeData();
if (!mime_data->hasFormat("text/plain"))
return;
PERFTRACE("paste selected cells");
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: paste from clipboard", m_spreadsheet->name()));
int first_col = firstSelectedColumn();
int last_col = lastSelectedColumn();
int first_row = firstSelectedRow();
int last_row = lastSelectedRow();
int input_row_count = 0;
int input_col_count = 0;
QString input_str = QString(mime_data->data("text/plain")).trimmed();
QVector cellTexts;
QStringList input_rows(input_str.split(QLatin1Char('\n')));
input_row_count = input_rows.count();
input_col_count = 0;
for (int i = 0; i < input_row_count; i++) {
cellTexts.append(input_rows.at(i).split(QRegExp(QLatin1String("\\s+"))));
if (cellTexts.at(i).count() > input_col_count)
input_col_count = cellTexts.at(i).count();
}
if ( (first_col == -1 || first_row == -1) || (last_row == first_row && last_col == first_col) ) {
// if there is no selection or only one cell selected, the
// selection will be expanded to the needed size from the current cell
int current_row, current_col;
getCurrentCell(¤t_row, ¤t_col);
if (current_row == -1) current_row = 0;
if (current_col == -1) current_col = 0;
setCellSelected(current_row, current_col);
first_col = current_col;
first_row = current_row;
last_row = first_row + input_row_count -1;
last_col = first_col + input_col_count -1;
//add columns if necessary
const int columnCount = m_spreadsheet->columnCount();
if (last_col >= columnCount) {
for (int c = 0; c < last_col - (columnCount - 1); ++c) {
const int curCol = columnCount - 1 + c;
//first non-empty value in the column to paste determines the column mode/type of the new column to be added
QString nonEmptyValue;
for (auto r : cellTexts) {
if (!r.at(curCol).isEmpty()) {
nonEmptyValue = r.at(curCol);
break;
}
}
const AbstractColumn::ColumnMode mode = AbstractFileFilter::columnMode(nonEmptyValue,
QLatin1String("yyyy-dd-MM hh:mm:ss:zzz"), QLocale::AnyLanguage);
Column* new_col = new Column(QString::number(curCol), mode);
new_col->setPlotDesignation(AbstractColumn::Y);
new_col->insertRows(0, m_spreadsheet->rowCount());
m_spreadsheet->addChild(new_col);
}
}
//add rows if necessary
if (last_row >= m_spreadsheet->rowCount())
m_spreadsheet->appendRows(last_row + 1 - m_spreadsheet->rowCount());
// select the rectangle to be pasted in
setCellsSelected(first_row, first_col, last_row, last_col);
}
const int rows = last_row - first_row + 1;
const int cols = last_col - first_col + 1;
QLocale locale;
for (int c = 0; c < cols && c < input_col_count; c++) {
Column* col = m_spreadsheet->column(first_col + c);
col->setSuppressDataChangedSignal(true);
if (col->columnMode() == AbstractColumn::Numeric) {
if (rows == m_spreadsheet->rowCount() && rows == cellTexts.size()) {
QVector new_data(rows);
for (int r = 0; r < rows; ++r)
new_data[r] = locale.toDouble(cellTexts.at(r).at(c));
col->replaceValues(0, new_data);
} else {
for (int r = 0; r < rows && r < input_row_count; r++) {
if ( isCellSelected(first_row + r, first_col + c) && (c < cellTexts.at(r).count()) ) {
if (!cellTexts.at(r).at(c).isEmpty())
col->setValueAt(first_row + r, locale.toDouble(cellTexts.at(r).at(c)));
else
col->setValueAt(first_row + r, NAN);
}
}
}
} else {
for (int r = 0; r < rows && r < input_row_count; r++) {
if (isCellSelected(first_row + r, first_col + c) && (c < cellTexts.at(r).count()) ) {
// if (formulaModeActive())
// col->setFormula(first_row + r, cellTexts.at(r).at(c));
// else
col->asStringColumn()->setTextAt(first_row + r, cellTexts.at(r).at(c));
}
}
}
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::maskSelection() {
int first = firstSelectedRow();
if (first < 0) return;
int last = lastSelectedRow();
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: mask selected cells", m_spreadsheet->name()));
for (auto* column : selectedColumns()) {
int col = m_spreadsheet->indexOfChild(column);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col)) column->setMasked(row);
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::unmaskSelection() {
int first = firstSelectedRow();
if (first < 0) return;
int last = lastSelectedRow();
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: unmask selected cells", m_spreadsheet->name()));
for (auto* column : selectedColumns()) {
int col = m_spreadsheet->indexOfChild(column);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col)) column->setMasked(row, false);
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::plotData() {
const QAction* action = dynamic_cast(QObject::sender());
PlotDataDialog::PlotType type = PlotDataDialog::PlotXYCurve;
if (action == action_plot_data_xycurve || action == action_plot_data_histogram)
type = (PlotDataDialog::PlotType)action->data().toInt();
auto* dlg = new PlotDataDialog(m_spreadsheet, type);
if (action != action_plot_data_xycurve && action != action_plot_data_histogram) {
PlotDataDialog::AnalysisAction type = (PlotDataDialog::AnalysisAction)action->data().toInt();
dlg->setAnalysisAction(type);
}
dlg->exec();
}
void SpreadsheetView::fillSelectedCellsWithRowNumbers() {
if (selectedColumnCount() < 1) return;
int first = firstSelectedRow();
if (first < 0) return;
int last = lastSelectedRow();
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: fill cells with row numbers", m_spreadsheet->name()));
for (auto* col_ptr : selectedColumns()) {
int col = m_spreadsheet->indexOfChild(col_ptr);
col_ptr->setSuppressDataChangedSignal(true);
switch (col_ptr->columnMode()) {
case AbstractColumn::Numeric: {
QVector results(last-first+1);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results[row-first] = row + 1;
else
results[row-first] = col_ptr->valueAt(row);
col_ptr->replaceValues(first, results);
break;
}
case AbstractColumn::Integer: {
QVector results(last-first+1);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results[row-first] = row + 1;
else
results[row-first] = col_ptr->integerAt(row);
col_ptr->replaceInteger(first, results);
break;
}
case AbstractColumn::Text: {
QVector results;
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results << QString::number(row+1);
else
results << col_ptr->textAt(row);
col_ptr->replaceTexts(first, results);
break;
}
//TODO: handle other modes
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
break;
}
col_ptr->setSuppressDataChangedSignal(false);
col_ptr->setChanged();
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::fillWithRowNumbers() {
if (selectedColumnCount() < 1) return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18np("%1: fill column with row numbers",
"%1: fill columns with row numbers",
m_spreadsheet->name(),
selectedColumnCount()));
const int rows = m_spreadsheet->rowCount();
QVector int_data(rows);
for (int i = 0; i < rows; ++i)
int_data[i] = i + 1;
for (auto* col : selectedColumns()) {
switch (col->columnMode()) {
case AbstractColumn::Numeric:
col->setColumnMode(AbstractColumn::Integer);
col->replaceInteger(0, int_data);
break;
case AbstractColumn::Integer:
col->replaceInteger(0, int_data);
break;
case AbstractColumn::Text:
case AbstractColumn::DateTime:
case AbstractColumn::Day:
case AbstractColumn::Month:
break;
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
//TODO: this function is not used currently.
void SpreadsheetView::fillSelectedCellsWithRandomNumbers() {
if (selectedColumnCount() < 1) return;
int first = firstSelectedRow();
int last = lastSelectedRow();
if (first < 0) return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: fill cells with random values", m_spreadsheet->name()));
qsrand(QTime::currentTime().msec());
for (auto* col_ptr : selectedColumns()) {
int col = m_spreadsheet->indexOfChild(col_ptr);
col_ptr->setSuppressDataChangedSignal(true);
switch (col_ptr->columnMode()) {
case AbstractColumn::Numeric: {
QVector results(last-first+1);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results[row-first] = double(qrand())/double(RAND_MAX);
else
results[row-first] = col_ptr->valueAt(row);
col_ptr->replaceValues(first, results);
break;
}
case AbstractColumn::Integer: {
QVector results(last-first+1);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results[row-first] = qrand();
else
results[row-first] = col_ptr->integerAt(row);
col_ptr->replaceInteger(first, results);
break;
}
case AbstractColumn::Text: {
QVector results;
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results << QString::number(double(qrand())/double(RAND_MAX));
else
results << col_ptr->textAt(row);
col_ptr->replaceTexts(first, results);
break;
}
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day: {
QVector results;
QDate earliestDate(1, 1, 1);
QDate latestDate(2999, 12, 31);
QTime midnight(0, 0, 0, 0);
for (int row = first; row <= last; row++)
if (isCellSelected(row, col))
results << QDateTime( earliestDate.addDays(((double)qrand())*((double)earliestDate.daysTo(latestDate))/((double)RAND_MAX)), midnight.addMSecs(((qint64)qrand())*1000*60*60*24/RAND_MAX));
else
results << col_ptr->dateTimeAt(row);
col_ptr->replaceDateTimes(first, results);
break;
}
}
col_ptr->setSuppressDataChangedSignal(false);
col_ptr->setChanged();
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::fillWithRandomValues() {
if (selectedColumnCount() < 1) return;
auto* dlg = new RandomValuesDialog(m_spreadsheet);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::fillWithEquidistantValues() {
if (selectedColumnCount() < 1) return;
auto* dlg = new EquidistantValuesDialog(m_spreadsheet);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::fillWithFunctionValues() {
if (selectedColumnCount() < 1) return;
auto* dlg = new FunctionValuesDialog(m_spreadsheet);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::fillSelectedCellsWithConstValues() {
if (selectedColumnCount() < 1) return;
int first = firstSelectedRow();
int last = lastSelectedRow();
if (first < 0)
return;
bool doubleOk = false;
bool intOk = false;
bool stringOk = false;
double doubleValue = 0;
int intValue = 0;
QString stringValue;
m_spreadsheet->beginMacro(i18n("%1: fill cells with const values", m_spreadsheet->name()));
for (auto* col_ptr : selectedColumns()) {
int col = m_spreadsheet->indexOfChild(col_ptr);
col_ptr->setSuppressDataChangedSignal(true);
switch (col_ptr->columnMode()) {
case AbstractColumn::Numeric:
if (!doubleOk)
doubleValue = QInputDialog::getDouble(this, i18n("Fill the selection with constant value"),
i18n("Value"), 0, -2147483647, 2147483647, 6, &doubleOk);
if (doubleOk) {
WAIT_CURSOR;
QVector results(last-first+1);
for (int row = first; row <= last; row++) {
if (isCellSelected(row, col))
results[row-first] = doubleValue;
else
results[row-first] = col_ptr->valueAt(row);
}
col_ptr->replaceValues(first, results);
RESET_CURSOR;
}
break;
case AbstractColumn::Integer:
if (!intOk)
intValue = QInputDialog::getInt(this, i18n("Fill the selection with constant value"),
i18n("Value"), 0, -2147483647, 2147483647, 1, &intOk);
if (intOk) {
WAIT_CURSOR;
QVector results(last-first+1);
for (int row = first; row <= last; row++) {
if (isCellSelected(row, col))
results[row-first] = intValue;
else
results[row-first] = col_ptr->integerAt(row);
}
col_ptr->replaceInteger(first, results);
RESET_CURSOR;
}
break;
case AbstractColumn::Text:
if (!stringOk)
stringValue = QInputDialog::getText(this, i18n("Fill the selection with constant value"),
i18n("Value"), QLineEdit::Normal, nullptr, &stringOk);
if (stringOk && !stringValue.isEmpty()) {
WAIT_CURSOR;
QVector results;
for (int row = first; row <= last; row++) {
if (isCellSelected(row, col))
results << stringValue;
else
results << col_ptr->textAt(row);
}
col_ptr->replaceTexts(first, results);
RESET_CURSOR;
}
break;
//TODO: handle other modes
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
break;
}
col_ptr->setSuppressDataChangedSignal(false);
col_ptr->setChanged();
}
m_spreadsheet->endMacro();
}
/*!
Open the sort dialog for all columns.
*/
void SpreadsheetView::sortSpreadsheet() {
sortDialog(m_spreadsheet->children());
}
/*!
Insert an empty column left to the firt selected column
*/
void SpreadsheetView::insertColumnLeft() {
insertColumnsLeft(1);
}
/*!
Insert multiple empty columns left to the firt selected column
*/
void SpreadsheetView::insertColumnsLeft() {
bool ok = false;
int count = QInputDialog::getInt(nullptr, i18n("Insert empty columns"), i18n("Enter the number of columns to insert"), 1/*value*/, 1/*min*/, 1000/*max*/, 1/*step*/, &ok);
if (!ok)
return;
insertColumnsLeft(count);
}
/*!
* private helper function doing the actual insertion of columns to the left
*/
void SpreadsheetView::insertColumnsLeft(int count) {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18np("%1: insert empty column",
"%1: insert empty columns",
m_spreadsheet->name(),
count
));
const int first = firstSelectedColumn();
if (first >= 0) {
//determine the first selected column
Column* firstCol = m_spreadsheet->child(first);
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric);
newCol->setPlotDesignation(AbstractColumn::Y);
//resize the new column and insert it before the first selected column
newCol->insertRows(0, m_spreadsheet->rowCount());
m_spreadsheet->insertChildBefore(newCol, firstCol);
}
} else {
if (m_spreadsheet->columnCount()>0) {
//columns available but no columns selected -> prepend the new column at the very beginning
Column* firstCol = m_spreadsheet->child(0);
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric);
newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, m_spreadsheet->rowCount());
m_spreadsheet->insertChildBefore(newCol, firstCol);
}
} else {
//no columns available anymore -> resize the spreadsheet and the new column to the default size
KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet"));
const int rows = group.readEntry(QLatin1String("RowCount"), 100);
m_spreadsheet->setRowCount(rows);
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i + 1), AbstractColumn::Numeric);
(i == 0) ? newCol->setPlotDesignation(AbstractColumn::X) : newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, rows);
//add/append a new column
m_spreadsheet->addChild(newCol);
}
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
/*!
Insert an empty column right to the last selected column
*/
void SpreadsheetView::insertColumnRight() {
insertColumnsRight(1);
}
/*!
Insert multiple empty columns right to the last selected column
*/
void SpreadsheetView::insertColumnsRight() {
bool ok = false;
int count = QInputDialog::getInt(nullptr, i18n("Insert empty columns"), i18n("Enter the number of columns to insert"), 1/*value*/, 1/*min*/, 1000/*max*/, 1/*step*/, &ok);
if (!ok)
return;
insertColumnsRight(count);
}
/*!
* private helper function doing the actual insertion of columns to the right
*/
void SpreadsheetView::insertColumnsRight(int count) {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18np("%1: insert empty column",
"%1: insert empty columns",
m_spreadsheet->name(),
count
));
const int last = lastSelectedColumn();
if (last >= 0) {
if (last < m_spreadsheet->columnCount() - 1) {
//determine the column next to the last selected column
Column* nextCol = m_spreadsheet->child(last + 1);
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric);
newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, m_spreadsheet->rowCount());
//insert the new column before the column next to the last selected column
m_spreadsheet->insertChildBefore(newCol, nextCol);
}
} else {
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric);
newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, m_spreadsheet->rowCount());
//last column selected, no next column available -> add/append a new column
m_spreadsheet->addChild(newCol);
}
}
} else {
if (m_spreadsheet->columnCount()>0) {
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric);
newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, m_spreadsheet->rowCount());
//columns available but no columns selected -> append the new column at the very end
m_spreadsheet->addChild(newCol);
}
} else {
//no columns available anymore -> resize the spreadsheet and the new column to the default size
KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet"));
const int rows = group.readEntry(QLatin1String("RowCount"), 100);
m_spreadsheet->setRowCount(rows);
for (int i = 0; i < count; ++i) {
Column* newCol = new Column(QString::number(i+1), AbstractColumn::Numeric);
(i == 0) ? newCol->setPlotDesignation(AbstractColumn::X) : newCol->setPlotDesignation(AbstractColumn::Y);
newCol->insertRows(0, rows);
//add/append a new column
m_spreadsheet->addChild(newCol);
}
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::removeSelectedColumns() {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: remove selected columns", m_spreadsheet->name()));
for (auto* column : selectedColumns())
m_spreadsheet->removeChild(column);
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::clearSelectedColumns() {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: clear selected columns", m_spreadsheet->name()));
if (formulaModeActive()) {
for (auto* col : selectedColumns()) {
col->setSuppressDataChangedSignal(true);
col->clearFormulas();
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
} else {
for (auto* col : selectedColumns()) {
col->setSuppressDataChangedSignal(true);
col->clear();
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::setSelectionAs() {
QVector columns = selectedColumns();
if (!columns.size())
return;
m_spreadsheet->beginMacro(i18n("%1: set plot designation", m_spreadsheet->name()));
QAction* action = dynamic_cast(QObject::sender());
if (!action)
return;
AbstractColumn::PlotDesignation pd = (AbstractColumn::PlotDesignation)action->data().toInt();
for (auto* col : columns)
col->setPlotDesignation(pd);
m_spreadsheet->endMacro();
}
/*!
* add, subtract, multiply, divide
*/
void SpreadsheetView::modifyValues() {
if (selectedColumnCount() < 1)
return;
const QAction* action = dynamic_cast(QObject::sender());
AddSubtractValueDialog::Operation op = (AddSubtractValueDialog::Operation)action->data().toInt();
auto* dlg = new AddSubtractValueDialog(m_spreadsheet, op);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::reverseColumns() {
WAIT_CURSOR;
QVector cols = selectedColumns();
m_spreadsheet->beginMacro(i18np("%1: reverse column", "%1: reverse columns",
m_spreadsheet->name(), cols.size()));
for (auto* col : cols) {
if (col->columnMode() != AbstractColumn::Numeric)
continue;
auto* data = static_cast* >(col->data());
QVector new_data(*data);
std::reverse(new_data.begin(), new_data.end());
col->replaceValues(0, new_data);
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::dropColumnValues() {
if (selectedColumnCount() < 1) return;
auto* dlg = new DropValuesDialog(m_spreadsheet);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::maskColumnValues() {
if (selectedColumnCount() < 1) return;
auto* dlg = new DropValuesDialog(m_spreadsheet, true);
dlg->setColumns(selectedColumns());
dlg->exec();
}
void SpreadsheetView::joinColumns() {
//TODO
}
void SpreadsheetView::normalizeSelectedColumns() {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: normalize columns", m_spreadsheet->name()));
for (auto* col : selectedColumns()) {
if (col->columnMode() == AbstractColumn::Numeric) {
col->setSuppressDataChangedSignal(true);
double max = col->maximum();
if (max != 0.0) {// avoid division by zero
for (int row = 0; row < col->rowCount(); row++)
col->setValueAt(row, col->valueAt(row) / max);
}
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::normalizeSelection() {
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: normalize selection", m_spreadsheet->name()));
double max = 0.0;
for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++)
if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric)
for (int row = 0; row < m_spreadsheet->rowCount(); row++) {
if (isCellSelected(row, col) && m_spreadsheet->column(col)->valueAt(row) > max)
max = m_spreadsheet->column(col)->valueAt(row);
}
if (max != 0.0) { // avoid division by zero
//TODO setSuppressDataChangedSignal
for (int col = firstSelectedColumn(); col <= lastSelectedColumn(); col++)
if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric)
for (int row = 0; row < m_spreadsheet->rowCount(); row++) {
if (isCellSelected(row, col))
m_spreadsheet->column(col)->setValueAt(row, m_spreadsheet->column(col)->valueAt(row) / max);
}
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::sortSelectedColumns() {
sortDialog(selectedColumns());
}
void SpreadsheetView::showAllColumnsStatistics() {
showColumnStatistics(true);
}
void SpreadsheetView::showColumnStatistics(bool forAll) {
QString dlgTitle(m_spreadsheet->name() + " column statistics");
auto* dlg = new StatisticsDialog(dlgTitle);
QVector columns;
if (!forAll)
dlg->setColumns(selectedColumns());
else if (forAll) {
for (int col = 0; col < m_spreadsheet->columnCount(); ++col) {
if (m_spreadsheet->column(col)->columnMode() == AbstractColumn::Numeric)
columns << m_spreadsheet->column(col);
}
dlg->setColumns(columns);
}
if (dlg->exec() == QDialog::Accepted) {
if (forAll)
columns.clear();
}
}
void SpreadsheetView::showRowStatistics() {
QString dlgTitle(m_spreadsheet->name() + " row statistics");
auto* dlg = new StatisticsDialog(dlgTitle);
QVector columns;
for (int i = 0; i < m_spreadsheet->rowCount(); ++i) {
if (isRowSelected(i)) {
QVector rowValues;
for (int j = 0; j < m_spreadsheet->columnCount(); ++j)
rowValues << m_spreadsheet->column(j)->valueAt(i);
columns << new Column(QString::number(i+1), rowValues);
}
}
dlg->setColumns(columns);
if (dlg->exec() == QDialog::Accepted) {
qDeleteAll(columns);
columns.clear();
}
}
/*!
Insert an empty row above(=before) the first selected row
*/
void SpreadsheetView::insertRowAbove() {
insertRowsAbove(1);
}
/*!
Insert multiple empty rows above(=before) the first selected row
*/
void SpreadsheetView::insertRowsAbove() {
bool ok = false;
int count = QInputDialog::getInt(nullptr, i18n("Insert multiple rows"), i18n("Enter the number of rows to insert"), 1/*value*/, 1/*min*/, 1000000/*max*/, 1/*step*/, &ok);
if (ok)
insertRowsAbove(count);
}
/*!
* private helper function doing the actual insertion of rows above
*/
void SpreadsheetView::insertRowsAbove(int count) {
int first = firstSelectedRow();
if (first < 0)
return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18np("%1: insert empty row",
"%1: insert empty rows",
m_spreadsheet->name(),
count
));
m_spreadsheet->insertRows(first, count);
m_spreadsheet->endMacro();
RESET_CURSOR;
}
/*!
Insert an empty row below the last selected row
*/
void SpreadsheetView::insertRowBelow() {
insertRowsBelow(1);
}
/*!
Insert an empty row below the last selected row
*/
void SpreadsheetView::insertRowsBelow() {
bool ok = false;
int count = QInputDialog::getInt(nullptr, i18n("Insert multiple rows"), i18n("Enter the number of rows to insert"), 1/*value*/, 1/*min*/, 1000000/*max*/, 1/*step*/, &ok);
if (ok)
insertRowsBelow(count);
}
/*!
* private helper function doing the actual insertion of rows below
*/
void SpreadsheetView::insertRowsBelow(int count) {
int last = lastSelectedRow();
if (last < 0)
return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18np("%1: insert empty row",
"%1: insert empty rows",
m_spreadsheet->name(),
count
));
if (last < m_spreadsheet->rowCount() - 1)
m_spreadsheet->insertRows(last + 1, count); //insert before the next to the last selected row
else
m_spreadsheet->appendRows(count); //append new rows at the end
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::removeSelectedRows() {
if (firstSelectedRow() < 0) return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: remove selected rows", m_spreadsheet->name()));
//TODO setSuppressDataChangedSignal
for (const auto& i : selectedRows().intervals())
m_spreadsheet->removeRows(i.start(), i.size());
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::clearSelectedRows() {
if (firstSelectedRow() < 0) return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: clear selected rows", m_spreadsheet->name()));
for (auto* col : selectedColumns()) {
col->setSuppressDataChangedSignal(true);
if (formulaModeActive()) {
for (const auto& i : selectedRows().intervals())
col->setFormula(i, QString());
} else {
for (const auto& i : selectedRows().intervals()) {
if (i.end() == col->rowCount()-1)
col->removeRows(i.start(), i.size());
else {
QVector empties;
for (int j = 0; j < i.size(); j++)
empties << QString();
col->asStringColumn()->replaceTexts(i.start(), empties);
}
}
}
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
m_spreadsheet->endMacro();
RESET_CURSOR;
//selected rows were deleted but the view selection is still in place -> reset the selection in the view
m_tableView->clearSelection();
}
void SpreadsheetView::clearSelectedCells() {
int first = firstSelectedRow();
int last = lastSelectedRow();
if (first < 0) return;
WAIT_CURSOR;
m_spreadsheet->beginMacro(i18n("%1: clear selected cells", m_spreadsheet->name()));
for (auto* column : selectedColumns()) {
column->setSuppressDataChangedSignal(true);
if (formulaModeActive()) {
int col = m_spreadsheet->indexOfChild(column);
for (int row = last; row >= first; row--)
if (isCellSelected(row, col))
column->setFormula(row, QString());
} else {
int col = m_spreadsheet->indexOfChild(column);
for (int row = last; row >= first; row--)
if (isCellSelected(row, col)) {
if (row < column->rowCount())
column->asStringColumn()->setTextAt(row, QString());
}
}
column->setSuppressDataChangedSignal(false);
column->setChanged();
}
m_spreadsheet->endMacro();
RESET_CURSOR;
}
void SpreadsheetView::goToCell() {
bool ok;
int col = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_spreadsheet->columnCount(), 1, &ok);
if (!ok) return;
int row = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_spreadsheet->rowCount(), 1, &ok);
if (!ok) return;
goToCell(row-1, col-1);
}
//! Open the sort dialog for the given columns
void SpreadsheetView::sortDialog(QVector cols) {
if (cols.isEmpty()) return;
for (auto* col : cols)
col->setSuppressDataChangedSignal(true);
auto* dlg = new SortDialog();
connect(dlg, SIGNAL(sort(Column*,QVector,bool)), m_spreadsheet, SLOT(sortColumns(Column*,QVector,bool)));
dlg->setColumns(cols);
int rc = dlg->exec();
for (auto* col : cols) {
col->setSuppressDataChangedSignal(false);
if (rc == QDialog::Accepted)
col->setChanged();
}
}
void SpreadsheetView::sortColumnAscending() {
QVector cols = selectedColumns();
for (auto* col : cols)
col->setSuppressDataChangedSignal(true);
m_spreadsheet->sortColumns(cols.first(), cols, true);
for (auto* col : cols) {
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
}
void SpreadsheetView::sortColumnDescending() {
QVector cols = selectedColumns();
for (auto* col : cols)
col->setSuppressDataChangedSignal(true);
m_spreadsheet->sortColumns(cols.first(), cols, false);
for (auto* col : cols) {
col->setSuppressDataChangedSignal(false);
col->setChanged();
}
}
/*!
Cause a repaint of the header.
*/
void SpreadsheetView::updateHeaderGeometry(Qt::Orientation o, int first, int last) {
Q_UNUSED(first)
Q_UNUSED(last)
//TODO
if (o != Qt::Horizontal) return;
m_tableView->horizontalHeader()->setStretchLastSection(true); // ugly hack (flaw in Qt? Does anyone know a better way?)
m_tableView->horizontalHeader()->updateGeometry();
m_tableView->horizontalHeader()->setStretchLastSection(false); // ugly hack part 2
}
/*!
selects the column \c column in the speadsheet view .
*/
void SpreadsheetView::selectColumn(int column) {
QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) );
m_suppressSelectionChangedEvent = true;
m_tableView->selectionModel()->select(selection, QItemSelectionModel::Select);
m_suppressSelectionChangedEvent = false;
}
/*!
deselects the column \c column in the speadsheet view .
*/
void SpreadsheetView::deselectColumn(int column) {
QItemSelection selection(m_model->index(0, column), m_model->index(m_spreadsheet->rowCount()-1, column) );
m_suppressSelectionChangedEvent = true;
m_tableView->selectionModel()->select(selection, QItemSelectionModel::Deselect);
m_suppressSelectionChangedEvent = false;
}
/*!
called when a column in the speadsheet view was clicked (click in the header).
Propagates the selection of the column to the \c Spreadsheet object
(a click in the header always selects the column).
*/
void SpreadsheetView::columnClicked(int column) {
m_spreadsheet->setColumnSelectedInView(column, true);
}
/*!
called on selections changes. Propagates the selection/deselection of columns to the \c Spreadsheet object.
*/
void SpreadsheetView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
Q_UNUSED(selected);
Q_UNUSED(deselected);
if (m_suppressSelectionChangedEvent)
return;
QItemSelectionModel* selModel = m_tableView->selectionModel();
for (int i = 0; i < m_spreadsheet->columnCount(); i++)
m_spreadsheet->setColumnSelectedInView(i, selModel->isColumnSelected(i, QModelIndex()));
}
bool SpreadsheetView::exportView() {
auto* dlg = new ExportSpreadsheetDialog(this);
dlg->setFileName(m_spreadsheet->name());
dlg->setExportTo(QStringList() << i18n("FITS image") << i18n("FITS table"));
for (int i = 0; i < m_spreadsheet->columnCount(); ++i) {
if (m_spreadsheet->column(i)->columnMode() != AbstractColumn::Numeric) {
dlg->setExportToImage(false);
break;
}
}
if (selectedColumnCount() == 0)
dlg->setExportSelection(false);
bool ret;
if ((ret = dlg->exec()) == QDialog::Accepted) {
const QString path = dlg->path();
const bool exportHeader = dlg->exportHeader();
WAIT_CURSOR;
switch (dlg->format()) {
case ExportSpreadsheetDialog::ASCII: {
const QString separator = dlg->separator();
const QLocale::Language format = dlg->numberFormat();
exportToFile(path, exportHeader, separator, format);
break;
}
case ExportSpreadsheetDialog::Binary:
break;
case ExportSpreadsheetDialog::LaTeX: {
const bool exportLatexHeader = dlg->exportLatexHeader();
const bool gridLines = dlg->gridLines();
const bool captions = dlg->captions();
const bool skipEmptyRows = dlg->skipEmptyRows();
const bool exportEntire = dlg->entireSpreadheet();
exportToLaTeX(path, exportHeader, gridLines, captions,
exportLatexHeader, skipEmptyRows, exportEntire);
break;
}
case ExportSpreadsheetDialog::FITS: {
const int exportTo = dlg->exportToFits();
const bool commentsAsUnits = dlg->commentsAsUnitsFits();
exportToFits(path, exportTo, commentsAsUnits);
break;
}
case ExportSpreadsheetDialog::SQLite:
exportToSQLite(path);
break;
}
RESET_CURSOR;
}
delete dlg;
return ret;
}
bool SpreadsheetView::printView() {
QPrinter printer;
auto* dlg = new QPrintDialog(&printer, this);
dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet"));
bool ret;
if ((ret = dlg->exec()) == QDialog::Accepted) {
print(&printer);
}
delete dlg;
return ret;
}
bool SpreadsheetView::printPreview() {
QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this);
connect(dlg, &QPrintPreviewDialog::paintRequested, this, &SpreadsheetView::print);
return dlg->exec();
}
/*!
prints the complete spreadsheet to \c printer.
*/
void SpreadsheetView::print(QPrinter* printer) const {
WAIT_CURSOR;
QPainter painter (printer);
const int dpiy = printer->logicalDpiY();
const int margin = (int) ( (1/2.54)*dpiy ); // 1 cm margins
QHeaderView *hHeader = m_tableView->horizontalHeader();
QHeaderView *vHeader = m_tableView->verticalHeader();
const int rows = m_spreadsheet->rowCount();
const int cols = m_spreadsheet->columnCount();
int height = margin;
const int vertHeaderWidth = vHeader->width();
int right = margin + vertHeaderWidth;
int columnsPerTable = 0;
int headerStringWidth = 0;
int firstRowStringWidth = 0;
bool tablesNeeded = false;
for (int col = 0; col < cols; ++col) {
headerStringWidth += m_tableView->columnWidth(col);
firstRowStringWidth += m_spreadsheet->column(col)->asStringColumn()->textAt(0).length();
if ((headerStringWidth >= printer->pageRect().width() -2*margin) ||
(firstRowStringWidth >= printer->pageRect().width() - 2*margin)) {
tablesNeeded = true;
break;
}
columnsPerTable++;
}
int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0;
const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols;
if (!tablesNeeded) {
tablesCount = 1;
columnsPerTable = cols;
}
if (remainingColumns > 0)
tablesCount++;
//Paint the horizontal header first
for (int table = 0; table < tablesCount; ++table) {
right = margin + vertHeaderWidth;
painter.setFont(hHeader->font());
QString headerString = m_tableView->model()->headerData(0, Qt::Horizontal).toString();
QRect br;
br = painter.boundingRect(br, Qt::AlignCenter, headerString);
QRect tr(br);
if (table != 0)
height += tr.height();
painter.drawLine(right, height, right, height+br.height());
int i = table * columnsPerTable;
int toI = table * columnsPerTable + columnsPerTable;
if ((remainingColumns > 0) && (table == tablesCount-1)) {
i = (tablesCount-1)*columnsPerTable;
toI = (tablesCount-1)* columnsPerTable + remainingColumns;
}
for (; imodel()->headerData(i, Qt::Horizontal).toString();
const int w = m_tableView->columnWidth(i);
tr.setTopLeft(QPoint(right,height));
tr.setWidth(w);
tr.setHeight(br.height());
painter.drawText(tr, Qt::AlignCenter, headerString);
right += w;
painter.drawLine(right, height, right, height+tr.height());
}
painter.drawLine(margin + vertHeaderWidth, height, right-1, height);//first horizontal line
height += tr.height();
painter.drawLine(margin, height, right-1, height);
// print table values
QString cellText;
for (i = 0; i < rows; ++i) {
right = margin;
cellText = m_tableView->model()->headerData(i, Qt::Vertical).toString()+'\t';
tr = painter.boundingRect(tr, Qt::AlignCenter, cellText);
painter.drawLine(right, height, right, height+tr.height());
br.setTopLeft(QPoint(right,height));
br.setWidth(vertHeaderWidth);
br.setHeight(tr.height());
painter.drawText(br, Qt::AlignCenter, cellText);
right += vertHeaderWidth;
painter.drawLine(right, height, right, height+tr.height());
int j = table * columnsPerTable;
int toJ = table * columnsPerTable + columnsPerTable;
if ((remainingColumns > 0) && (table == tablesCount-1)) {
j = (tablesCount-1)*columnsPerTable;
toJ = (tablesCount-1)* columnsPerTable + remainingColumns;
}
for (; j < toJ; j++) {
int w = m_tableView->columnWidth(j);
cellText = m_spreadsheet->column(j)->isValid(i) ? m_spreadsheet->text(i,j)+'\t':
QLatin1String("- \t");
tr = painter.boundingRect(tr,Qt::AlignCenter,cellText);
br.setTopLeft(QPoint(right,height));
br.setWidth(w);
br.setHeight(tr.height());
painter.drawText(br, Qt::AlignCenter, cellText);
right += w;
painter.drawLine(right, height, right, height+tr.height());
}
height += br.height();
painter.drawLine(margin, height, right-1, height);
if (height >= printer->height() - margin) {
printer->newPage();
height = margin;
painter.drawLine(margin, height, right, height);
}
}
}
RESET_CURSOR;
}
void SpreadsheetView::registerShortcuts() {
action_clear_selection->setShortcut(QKeySequence::Delete);
}
void SpreadsheetView::unregisterShortcuts() {
action_clear_selection->setShortcut(QKeySequence());
}
void SpreadsheetView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const {
QFile file(path);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return;
PERFTRACE("export spreadsheet to file");
QTextStream out(&file);
const int cols = m_spreadsheet->columnCount();
QString sep = separator;
sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive);
sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive);
//export header (column names)
if (exportHeader) {
for (int j = 0; j < cols; ++j) {
out << m_spreadsheet->column(j)->name();
if (j != cols - 1)
out << sep;
}
out << '\n';
}
//export values
QLocale locale(language);
for (int i = 0; i < m_spreadsheet->rowCount(); ++i) {
for (int j = 0; j < cols; ++j) {
Column* col = m_spreadsheet->column(j);
if (col->columnMode() == AbstractColumn::Numeric) {
const Double2StringFilter* out_fltr = static_cast(col->outputFilter());
out << locale.toString(col->valueAt(i), out_fltr->numericFormat(), 16); // export with max. precision
} else
out << col->asStringColumn()->textAt(i);
if (j != cols - 1)
out << sep;
}
out << '\n';
}
}
void SpreadsheetView::exportToLaTeX(const QString & path, const bool exportHeaders,
const bool gridLines, const bool captions, const bool latexHeaders,
const bool skipEmptyRows, const bool exportEntire) const {
QFile file(path);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return;
QList toExport;
int cols;
int totalRowCount = 0;
if (exportEntire) {
cols = const_cast(this)->m_spreadsheet->columnCount();
totalRowCount = m_spreadsheet->rowCount();
for (int col = 0; col < cols; ++col)
toExport << m_spreadsheet->column(col);
} else {
cols = const_cast(this)->selectedColumnCount();
const int firtsSelectedCol = const_cast(this)->firstSelectedColumn();
bool rowsCalculated = false;
for (int col = firtsSelectedCol; col < firtsSelectedCol + cols; ++col) {
QVector textData;
for (int row = 0; row < m_spreadsheet->rowCount(); ++row) {
if (const_cast(this)->isRowSelected(row)) {
textData << m_spreadsheet->column(col)->asStringColumn()->textAt(row);
if (!rowsCalculated)
totalRowCount++;
}
}
if (!rowsCalculated)
rowsCalculated = true;
Column* column = new Column(m_spreadsheet->column(col)->name(), textData);
toExport << column;
}
}
int columnsStringSize = 0;
int columnsPerTable = 0;
for (int i = 0; i < cols; ++i) {
int maxSize = -1;
for (int j = 0; j < toExport.at(i)->asStringColumn()->rowCount(); ++j) {
if (toExport.at(i)->asStringColumn()->textAt(j).size() > maxSize)
maxSize = toExport.at(i)->asStringColumn()->textAt(j).size();
}
columnsStringSize += maxSize;
if (!toExport.at(i)->isValid(0))
columnsStringSize += 3;
if (columnsStringSize > 65)
break;
++columnsPerTable;
}
const int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0;
const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols;
bool columnsSeparating = (cols > columnsPerTable);
QTextStream out(&file);
QProcess tex;
tex.start("latex", QStringList() << "--version", QProcess::ReadOnly);
tex.waitForFinished(500);
QString texVersionOutput = QString(tex.readAllStandardOutput());
texVersionOutput = texVersionOutput.split('\n')[0];
int yearidx = -1;
for (int i = texVersionOutput.size() - 1; i >= 0; --i) {
if (texVersionOutput.at(i) == QChar('2')) {
yearidx = i;
break;
}
}
if (texVersionOutput.at(yearidx+1) == QChar('/'))
yearidx -= 3;
bool ok;
texVersionOutput.midRef(yearidx, 4).toInt(&ok);
int version = -1;
if (ok)
version = texVersionOutput.midRef(yearidx, 4).toInt(&ok);
if (latexHeaders) {
out << QLatin1String("\\documentclass[11pt,a4paper]{article} \n");
out << QLatin1String("\\usepackage{geometry} \n");
out << QLatin1String("\\usepackage{xcolor,colortbl} \n");
if (version >= 2015)
out << QLatin1String("\\extrafloats{1280} \n");
out << QLatin1String("\\definecolor{HeaderBgColor}{rgb}{0.81,0.81,0.81} \n");
out << QLatin1String("\\geometry{ \n");
out << QLatin1String("a4paper, \n");
out << QLatin1String("total={170mm,257mm}, \n");
out << QLatin1String("left=10mm, \n");
out << QLatin1String("top=10mm } \n");
out << QLatin1String("\\begin{document} \n");
out << QLatin1String("\\title{LabPlot Spreadsheet Export to \\LaTeX{} } \n");
out << QLatin1String("\\author{LabPlot} \n");
out << QLatin1String("\\date{\\today} \n");
}
QString endTabularTable ("\\end{tabular} \n \\end{table} \n");
QString tableCaption ("\\caption{"+ m_spreadsheet->name()+ "} \n");
QString beginTable ("\\begin{table}[ht] \n");
int rowCount = 0;
const int maxRows = 45;
bool captionRemoved = false;
if (columnsSeparating) {
QVector emptyRowIndices;
for (int table = 0; table < tablesCount; ++table) {
QStringList textable;
captionRemoved = false;
textable << beginTable;
if (captions)
textable << tableCaption;
textable << QLatin1String("\\centering \n");
textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString());
for (int i = 0; i < columnsPerTable; ++i)
textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") );
textable << QLatin1String("} \n");
if (gridLines)
textable << QLatin1String("\\hline \n");
if (exportHeaders) {
if (latexHeaders)
textable << QLatin1String("\\rowcolor{HeaderBgColor} \n");
for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) {
textable << toExport.at(col)->name();
if (col != ((table * columnsPerTable)+ columnsPerTable)-1)
textable << QLatin1String(" & ");
}
textable << QLatin1String("\\\\ \n");
if (gridLines)
textable << QLatin1String("\\hline \n");
}
for (const auto& s : textable)
out << s;
QStringList values;
for (int row = 0; row < totalRowCount; ++row) {
values.clear();
bool notEmpty = false;
for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) {
if (toExport.at(col)->isValid(row)) {
notEmpty = true;
values << toExport.at(col)->asStringColumn()->textAt(row);
} else
values << QLatin1String("-");
if (col != ((table * columnsPerTable)+ columnsPerTable)-1)
values << QLatin1String(" & ");
}
if (!notEmpty && skipEmptyRows) {
if (!emptyRowIndices.contains(row))
emptyRowIndices << row;
}
if (emptyRowIndices.contains(row) && notEmpty)
emptyRowIndices.remove(emptyRowIndices.indexOf(row));
if (notEmpty || !skipEmptyRows) {
for (const auto& s : values)
out << s;
out << QLatin1String("\\\\ \n");
if (gridLines)
out << QLatin1String("\\hline \n");
rowCount++;
if (rowCount == maxRows) {
out << endTabularTable;
out << QLatin1String("\\newpage \n");
if (captions)
if (!captionRemoved)
textable.removeAt(1);
for (const auto& s : textable)
out << s;
rowCount = 0;
if (!captionRemoved)
captionRemoved = true;
}
}
}
out << endTabularTable;
}
//new table for the remaining columns
QStringList remainingTable;
remainingTable << beginTable;
if (captions)
remainingTable << tableCaption;
remainingTable << QLatin1String("\\centering \n");
remainingTable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString());
for (int c = 0; c < remainingColumns; ++c)
remainingTable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") );
remainingTable << QLatin1String("} \n");
if (gridLines)
remainingTable << QLatin1String("\\hline \n");
if (exportHeaders) {
if (latexHeaders)
remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n");
for (int col = 0; col < remainingColumns; ++col) {
remainingTable << toExport.at(col + (tablesCount * columnsPerTable))->name();
if (col != remainingColumns-1)
remainingTable << QLatin1String(" & ");
}
remainingTable << QLatin1String("\\\\ \n");
if (gridLines)
remainingTable << QLatin1String("\\hline \n");
}
for (const auto& s : remainingTable)
out << s;
QStringList values;
captionRemoved = false;
for (int row = 0; row < totalRowCount; ++row) {
values.clear();
bool notEmpty = false;
for (int col = 0; col < remainingColumns; ++col ) {
if (toExport.at(col + (tablesCount * columnsPerTable))->isValid(row)) {
notEmpty = true;
values << toExport.at(col + (tablesCount * columnsPerTable))->asStringColumn()->textAt(row);
} else
values << QLatin1String("-");
if (col != remainingColumns-1)
values << QLatin1String(" & ");
}
if (!emptyRowIndices.contains(row) && !notEmpty)
notEmpty = true;
if (notEmpty || !skipEmptyRows) {
for (const auto& s : values)
out << s;
out << QLatin1String("\\\\ \n");
if (gridLines)
out << QLatin1String("\\hline \n");
rowCount++;
if (rowCount == maxRows) {
out << endTabularTable;
out << QLatin1String("\\pagebreak[4] \n");
if (captions)
if (!captionRemoved)
remainingTable.removeAt(1);
for (const auto& s : remainingTable)
out << s;
rowCount = 0;
if (!captionRemoved)
captionRemoved = true;
}
}
}
out << endTabularTable;
} else {
QStringList textable;
textable << beginTable;
if (captions)
textable << tableCaption;
textable << QLatin1String("\\centering \n");
textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString());
for (int c = 0; c < cols; ++c)
textable << ( gridLines ? QLatin1String(" c |") : QLatin1String(" c ") );
textable << QLatin1String("} \n");
if (gridLines)
textable << QLatin1String("\\hline \n");
if (exportHeaders) {
if (latexHeaders)
textable << QLatin1String("\\rowcolor{HeaderBgColor} \n");
for (int col = 0; col < cols; ++col) {
textable << toExport.at(col)->name();
if (col != cols-1)
textable << QLatin1String(" & ");
}
textable << QLatin1String("\\\\ \n");
if (gridLines)
textable << QLatin1String("\\hline \n");
}
for (const auto& s : textable)
out << s;
QStringList values;
captionRemoved = false;
for (int row = 0; row < totalRowCount; ++row) {
values.clear();
bool notEmpty = false;
for (int col = 0; col < cols; ++col ) {
if (toExport.at(col)->isValid(row)) {
notEmpty = true;
values << toExport.at(col)->asStringColumn()->textAt(row);
} else
values << "-";
if (col != cols-1)
values << " & ";
}
if (notEmpty || !skipEmptyRows) {
for (const auto& s : values)
out << s;
out << QLatin1String("\\\\ \n");
if (gridLines)
out << QLatin1String("\\hline \n");
rowCount++;
if (rowCount == maxRows) {
out << endTabularTable;
out << QLatin1String("\\newpage \n");
if (captions)
if (!captionRemoved)
textable.removeAt(1);
for (const auto& s : textable)
out << s;
rowCount = 0;
if (!captionRemoved)
captionRemoved = true;
}
}
}
out << endTabularTable;
}
if (latexHeaders)
out << QLatin1String("\\end{document} \n");
if (!exportEntire) {
qDeleteAll(toExport);
toExport.clear();
} else
toExport.clear();
}
void SpreadsheetView::exportToFits(const QString &fileName, const int exportTo, const bool commentsAsUnits) const {
auto* filter = new FITSFilter;
filter->setExportTo(exportTo);
filter->setCommentsAsUnits(commentsAsUnits);
filter->write(fileName, m_spreadsheet);
delete filter;
}
void SpreadsheetView::exportToSQLite(const QString& path) const {
QFile file(path);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return;
PERFTRACE("export spreadsheet to SQLite database");
QApplication::processEvents(QEventLoop::AllEvents, 0);
//create database
const QStringList& drivers = QSqlDatabase::drivers();
QString driver;
if (drivers.contains(QLatin1String("QSQLITE3")))
driver = QLatin1String("QSQLITE3");
else
driver = QLatin1String("QSQLITE");
QSqlDatabase db = QSqlDatabase::addDatabase(driver);
db.setDatabaseName(path);
if (!db.open()) {
RESET_CURSOR;
KMessageBox::error(nullptr, i18n("Couldn't create the SQLite database %1.", path));
}
//create table
const int cols = m_spreadsheet->columnCount();
QString query = QLatin1String("create table ") + m_spreadsheet->name() + QLatin1String(" (");
for (int i = 0; i < cols; ++i) {
Column* col = m_spreadsheet->column(i);
if (i != 0)
query += QLatin1String(", ");
query += QLatin1String("\"") + col->name() + QLatin1String("\" ");
switch (col->columnMode()) {
case AbstractColumn::Numeric:
query += QLatin1String("REAL");
break;
case AbstractColumn::Integer:
query += QLatin1String("INTEGER");
break;
case AbstractColumn::Text:
case AbstractColumn::Month:
case AbstractColumn::Day:
case AbstractColumn::DateTime:
query += QLatin1String("TEXT");
break;
}
}
query += QLatin1Char(')');
QSqlQuery q;
if (!q.exec(query)) {
RESET_CURSOR;
KMessageBox::error(nullptr, i18n("Failed to create table in the SQLite database %1.", path) + "\n" + q.lastError().databaseText());
db.close();
return;
}
//create bulk insert statement
{
PERFTRACE("Create the bulk insert statement");
q.exec(QLatin1String("BEGIN TRANSACTION;"));
query = "INSERT INTO '" + m_spreadsheet->name() + "' (";
for (int i = 0; i < cols; ++i) {
if (i != 0)
query += QLatin1String(", ");
query += QLatin1Char('\'') + m_spreadsheet->column(i)->name() + QLatin1Char('\'');
}
query += QLatin1String(") VALUES ");
for (int i = 0; i < m_spreadsheet->rowCount(); ++i) {
if (i != 0)
query += QLatin1String(",");
query += QLatin1Char('(');
for (int j = 0; j < cols; ++j) {
Column* col = m_spreadsheet->column(j);
if (j != 0)
query += QLatin1String(", ");
query += QLatin1Char('\'') + col->asStringColumn()->textAt(i) + QLatin1Char('\'');
}
query += QLatin1String(")");
}
query += QLatin1Char(';');
}
//insert values
if (!q.exec(query)) {
RESET_CURSOR;
KMessageBox::error(nullptr, i18n("Failed to insert values into the table."));
QDEBUG("bulk insert error " << q.lastError().databaseText());
} else
q.exec(QLatin1String("COMMIT TRANSACTION;"));
//close the database
db.close();
}
diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.h b/src/commonfrontend/spreadsheet/SpreadsheetView.h
index 8ad68014f..c4291e1d1 100644
--- a/src/commonfrontend/spreadsheet/SpreadsheetView.h
+++ b/src/commonfrontend/spreadsheet/SpreadsheetView.h
@@ -1,294 +1,296 @@
/***************************************************************************
File : SpreadsheetView.h
Project : LabPlot
Description : View class for Spreadsheet
--------------------------------------------------------------------
Copyright : (C) 2010-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 SPREADSHEETVIEW_H
#define SPREADSHEETVIEW_H
#include
#include "backend/core/AbstractColumn.h"
#include "backend/lib/IntervalAttribute.h"
class Column;
class Spreadsheet;
class SpreadsheetModel;
class SpreadsheetItemDelegate;
class SpreadsheetHeaderView;
class AbstractAspect;
class QTableView;
class QPrinter;
class QMenu;
class QToolBar;
class QModelIndex;
class QItemSelection;
class SpreadsheetView : public QWidget {
Q_OBJECT
public:
explicit SpreadsheetView(Spreadsheet* spreadsheet, bool readOnly = false);
~SpreadsheetView() override;
void resizeHeader();
void showComments(bool on = true);
bool areCommentsShown() const;
int selectedColumnCount(bool full = false) const;
int selectedColumnCount(AbstractColumn::PlotDesignation) const;
bool isColumnSelected(int col, bool full = false) const;
QVector selectedColumns(bool full = false) const;
int firstSelectedColumn(bool full = false) const;
int lastSelectedColumn(bool full = false) const;
bool isRowSelected(int row, bool full = false) const;
int firstSelectedRow(bool full = false) const;
int lastSelectedRow(bool full = false) const;
IntervalAttribute selectedRows(bool full = false) const;
bool isCellSelected(int row, int col) const;
void setCellSelected(int row, int col, bool select = true);
void setCellsSelected(int first_row, int first_col, int last_row, int last_col, bool select = true);
void getCurrentCell(int* row, int* col) const;
bool exportView();
bool printView();
bool printPreview();
void registerShortcuts();
void unregisterShortcuts();
private:
void init();
void initActions();
void initMenus();
void connectActions();
bool formulaModeActive() const;
void exportToFile(const QString&, const bool, const QString&, QLocale::Language) const;
void exportToLaTeX(const QString&, const bool exportHeaders,
const bool gridLines, const bool captions, const bool latexHeaders,
const bool skipEmptyRows,const bool exportEntire) const;
void exportToFits(const QString& path, const int exportTo, const bool commentsAsUnits) const;
void exportToSQLite(const QString& path) const;
void insertColumnsLeft(int);
void insertColumnsRight(int);
void insertRowsAbove(int);
void insertRowsBelow(int);
QTableView* m_tableView;
Spreadsheet* m_spreadsheet;
SpreadsheetItemDelegate* m_delegate;
SpreadsheetModel* m_model;
SpreadsheetHeaderView* m_horizontalHeader;
bool m_suppressSelectionChangedEvent{false};
bool m_readOnly;
bool eventFilter(QObject*, QEvent*) override;
void checkSpreadsheetMenu();
//selection related actions
QAction* action_cut_selection;
QAction* action_copy_selection;
QAction* action_paste_into_selection;
QAction* action_mask_selection;
QAction* action_unmask_selection;
QAction* action_clear_selection;
// QAction* action_set_formula;
// QAction* action_recalculate;
QAction* action_fill_row_numbers;
QAction* action_fill_sel_row_numbers;
QAction* action_fill_random;
QAction* action_fill_equidistant;
QAction* action_fill_random_nonuniform;
QAction* action_fill_const;
QAction* action_fill_function;
//spreadsheet related actions
QAction* action_toggle_comments;
QAction* action_select_all;
QAction* action_clear_spreadsheet;
QAction* action_clear_masks;
QAction* action_sort_spreadsheet;
QAction* action_go_to_cell;
QAction* action_statistics_all_columns;
QAction* action_pivot_table;
QAction* action_do_hypothesis_test;
+ QAction* action_find_correlation_coefficient;
//column related actions
QAction* action_insert_column_left;
QAction* action_insert_column_right;
QAction* action_insert_columns_left;
QAction* action_insert_columns_right;
QAction* action_remove_columns;
QAction* action_clear_columns;
QAction* action_add_columns;
QAction* action_set_as_none;
QAction* action_set_as_x;
QAction* action_set_as_y;
QAction* action_set_as_z;
QAction* action_set_as_xerr;
QAction* action_set_as_xerr_plus;
QAction* action_set_as_xerr_minus;
QAction* action_set_as_yerr;
QAction* action_set_as_yerr_plus;
QAction* action_set_as_yerr_minus;
QAction* action_reverse_columns;
QAction* action_add_value;
QAction* action_subtract_value;
QAction* action_multiply_value;
QAction* action_divide_value;
QAction* action_drop_values;
QAction* action_mask_values;
QAction* action_join_columns;
QAction* action_normalize_columns;
QAction* action_normalize_selection;
QAction* action_sort_columns;
QAction* action_sort_asc_column;
QAction* action_sort_desc_column;
QAction* action_statistics_columns;
//row related actions
QAction* action_insert_row_above;
QAction* action_insert_row_below;
QAction* action_insert_rows_above;
QAction* action_insert_rows_below;
QAction* action_remove_rows;
QAction* action_clear_rows;
QAction* action_statistics_rows;
//analysis and plot data menu actions
QAction* action_plot_data_xycurve;
QAction* action_plot_data_histogram;
QAction* addDataOperationAction;
QAction* addDataReductionAction;
QAction* addDifferentiationAction;
QAction* addIntegrationAction;
QAction* addInterpolationAction;
QAction* addSmoothAction;
QVector addFitAction;
QAction* addFourierFilterAction;
//Menus
QMenu* m_selectionMenu;
QMenu* m_columnMenu;
QMenu* m_columnSetAsMenu{nullptr};
QMenu* m_columnGenerateDataMenu{nullptr};
QMenu* m_columnManipulateDataMenu;
QMenu* m_columnSortMenu{nullptr};
QMenu* m_rowMenu;
QMenu* m_spreadsheetMenu;
QMenu* m_plotDataMenu;
QMenu* m_analyzePlotMenu;
public slots:
void createContextMenu(QMenu*);
void fillToolBar(QToolBar*);
void print(QPrinter*) const;
private slots:
void createColumnContextMenu(QMenu*);
void goToCell(int row, int col);
void toggleComments();
void goToNextColumn();
void goToPreviousColumn();
void goToCell();
void createPivotTable();
void doHypothesisTest();
+ void findCorrelationCoefficient();
void sortSpreadsheet();
void sortDialog(QVector);
void cutSelection();
void copySelection();
void pasteIntoSelection();
void clearSelectedCells();
void maskSelection();
void unmaskSelection();
// void recalculateSelectedCells();
void plotData();
void fillSelectedCellsWithRowNumbers();
void fillWithRowNumbers();
void fillSelectedCellsWithRandomNumbers();
void fillWithRandomValues();
void fillWithEquidistantValues();
void fillWithFunctionValues();
void fillSelectedCellsWithConstValues();
void insertRowAbove();
void insertRowBelow();
void insertRowsAbove();
void insertRowsBelow();
void removeSelectedRows();
void clearSelectedRows();
void insertColumnLeft();
void insertColumnRight();
void insertColumnsLeft();
void insertColumnsRight();
void removeSelectedColumns();
void clearSelectedColumns();
void modifyValues();
void reverseColumns();
void dropColumnValues();
void maskColumnValues();
void joinColumns();
void normalizeSelectedColumns();
void normalizeSelection();
void sortSelectedColumns();
void sortColumnAscending();
void sortColumnDescending();
void setSelectionAs();
void activateFormulaMode(bool on);
void showColumnStatistics(bool forAll = false);
void showAllColumnsStatistics();
void showRowStatistics();
void handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize);
void handleHorizontalSectionMoved(int index, int from, int to);
void handleHorizontalHeaderDoubleClicked(int index);
void handleHeaderDataChanged(Qt::Orientation orientation, int first, int last);
void currentColumnChanged(const QModelIndex& current, const QModelIndex & previous);
void handleAspectAdded(const AbstractAspect*);
void handleAspectAboutToBeRemoved(const AbstractAspect*);
void updateHeaderGeometry(Qt::Orientation o, int first, int last);
void selectColumn(int);
void deselectColumn(int);
void columnClicked(int);
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
};
#endif
diff --git a/src/kdefrontend/GuiObserver.cpp b/src/kdefrontend/GuiObserver.cpp
index ca25276b0..f97efda4e 100644
--- a/src/kdefrontend/GuiObserver.cpp
+++ b/src/kdefrontend/GuiObserver.cpp
@@ -1,487 +1,502 @@
/***************************************************************************
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/generalTest/HypothesisTest.h"
+#include "backend/generalTest/CorrelationCoefficient.h"
#include "backend/matrix/Matrix.h"
#include "backend/pivot/PivotTable.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/worksheet/Worksheet.h"
#include "backend/worksheet/plots/cartesian/CartesianPlot.h"
#include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h"
#include "backend/worksheet/plots/cartesian/XYCurve.h"
#include "backend/worksheet/plots/cartesian/Axis.h"
#include "backend/worksheet/plots/cartesian/CustomPoint.h"
#include "backend/worksheet/plots/cartesian/Histogram.h"
#include "backend/worksheet/TextLabel.h"
#ifdef HAVE_CANTOR_LIBS
#include "backend/cantorWorksheet/CantorWorksheet.h"
#endif
#ifdef HAVE_MQTT
#include "backend/datasources/MQTTClient.h"
#include "backend/datasources/MQTTSubscription.h"
#include "backend/datasources/MQTTTopic.h"
#endif
#include "backend/core/Project.h"
#include "backend/datapicker/Datapicker.h"
#include "backend/datapicker/DatapickerImage.h"
#include "backend/datapicker/DatapickerCurve.h"
#include "commonfrontend/ProjectExplorer.h"
#include "kdefrontend/MainWin.h"
#include "kdefrontend/dockwidgets/AxisDock.h"
#include "kdefrontend/dockwidgets/NoteDock.h"
#include "kdefrontend/dockwidgets/CursorDock.h"
#include "kdefrontend/dockwidgets/CartesianPlotDock.h"
#include "kdefrontend/dockwidgets/CartesianPlotLegendDock.h"
#include "kdefrontend/dockwidgets/ColumnDock.h"
#include "kdefrontend/dockwidgets/HypothesisTestDock.h"
+#include "kdefrontend/dockwidgets/CorrelationCoefficientDock.h"
#include "kdefrontend/dockwidgets/LiveDataDock.h"
#include "kdefrontend/dockwidgets/MatrixDock.h"
#include "kdefrontend/dockwidgets/PivotTableDock.h"
#include "kdefrontend/dockwidgets/ProjectDock.h"
#include "kdefrontend/dockwidgets/SpreadsheetDock.h"
#include "kdefrontend/dockwidgets/XYCurveDock.h"
#include "kdefrontend/dockwidgets/HistogramDock.h"
#include "kdefrontend/dockwidgets/XYEquationCurveDock.h"
#include "kdefrontend/dockwidgets/XYDataReductionCurveDock.h"
#include "kdefrontend/dockwidgets/XYDifferentiationCurveDock.h"
#include "kdefrontend/dockwidgets/XYIntegrationCurveDock.h"
#include "kdefrontend/dockwidgets/XYInterpolationCurveDock.h"
#include "kdefrontend/dockwidgets/XYSmoothCurveDock.h"
#include "kdefrontend/dockwidgets/XYFitCurveDock.h"
#include "kdefrontend/dockwidgets/XYFourierFilterCurveDock.h"
#include "kdefrontend/dockwidgets/XYFourierTransformCurveDock.h"
#include "kdefrontend/dockwidgets/XYConvolutionCurveDock.h"
#include "kdefrontend/dockwidgets/XYCorrelationCurveDock.h"
#include "kdefrontend/dockwidgets/CustomPointDock.h"
#include "kdefrontend/dockwidgets/WorksheetDock.h"
#ifdef HAVE_CANTOR_LIBS
#include "kdefrontend/dockwidgets/CantorWorksheetDock.h"
#endif
#include "kdefrontend/widgets/LabelWidget.h"
#include "kdefrontend/widgets/DatapickerImageWidget.h"
#include "kdefrontend/widgets/DatapickerCurveWidget.h"
#include
#include
#include
#include
#include
/*!
\class GuiObserver
\brief The GUI observer looks for the selection changes in the main window
and shows/hides the correspondings dock widgets, toolbars etc.
This class is intended to simplify (or not to overload) the code in MainWin.
\ingroup kdefrontend
*/
namespace GuiObserverHelper
{
template
bool raiseDock(T*& dock, QStackedWidget* parent)
{
- const bool generated = !dock;
- if (generated) {
- dock = new T(parent);
- parent->addWidget(dock);
- }
- parent->setCurrentWidget(dock);
-
- return generated;
+ const bool generated = !dock;
+ if (generated) {
+ dock = new T(parent);
+ parent->addWidget(dock);
+ }
+ parent->setCurrentWidget(dock);
+
+ return generated;
}
template
void raiseDockConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent)
{
- if (raiseDock(dock, parent))
- QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); });
+ if (raiseDock(dock, parent))
+ QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); });
}
template
void raiseDockSetupConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent)
{
- if (raiseDock(dock, parent)) {
- dock->setupGeneral();
- QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); });
- }
+ if (raiseDock(dock, parent)) {
+ dock->setupGeneral();
+ QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); });
+ }
}
template
QList castList(QList& selectedAspects)
{
- QList list;
- for (auto* aspect : selectedAspects)
- list << static_cast(aspect);
- return list;
+ QList list;
+ for (auto* aspect : selectedAspects)
+ list << static_cast(aspect);
+ return list;
}
}
using namespace GuiObserverHelper;
GuiObserver::GuiObserver(MainWin* mainWin) {
- connect(mainWin->m_projectExplorer, &ProjectExplorer::selectedAspectsChanged, this, &GuiObserver::selectedAspectsChanged);
- connect(mainWin->m_projectExplorer, &ProjectExplorer::hiddenAspectSelected, this, &GuiObserver::hiddenAspectSelected);
- m_mainWindow = mainWin;
+ connect(mainWin->m_projectExplorer, &ProjectExplorer::selectedAspectsChanged, this, &GuiObserver::selectedAspectsChanged);
+ connect(mainWin->m_projectExplorer, &ProjectExplorer::hiddenAspectSelected, this, &GuiObserver::hiddenAspectSelected);
+ m_mainWindow = mainWin;
}
/*!
called on selection changes in the project explorer.
Determines the type of the currently selected objects (aspects)
and activates the corresponding dockwidgets, toolbars etc.
*/
void GuiObserver::selectedAspectsChanged(QList& selectedAspects) const {
- auto clearDock = [&](){
- if (m_mainWindow->stackedWidget->currentWidget())
- m_mainWindow->stackedWidget->currentWidget()->hide();
-
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties"));
- };
-
- if (selectedAspects.isEmpty()) {
- clearDock();
- return;
- }
-
- // update cursor dock
- AbstractAspect* parent = selectedAspects[0]->parent(AspectType::Worksheet);
- if (selectedAspects[0]->inherits(AspectType::Worksheet)) {
- Worksheet* worksheet = static_cast(selectedAspects[0]);
-
- if (m_mainWindow->cursorWidget) {
- m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel());
- QVector plots = worksheet->children();
- if (!plots.isEmpty())
- m_mainWindow->cursorWidget->setPlots(plots);
- }
- } else if (parent) {
- if (m_mainWindow->cursorWidget) {
- Worksheet* worksheet = static_cast(parent);
- QVector plots = worksheet->children();
- m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel());
- m_mainWindow->cursorWidget->setPlots(plots);
- }
-
- }
-
- const AspectType type = selectedAspects.front()->type();
-
- // Check, whether objects of different types were selected.
- // Don't show any dock widgets in this case.
- for (auto* aspect : selectedAspects) {
- if (aspect->type() != type) {
- clearDock();
- return;
- }
- }
-
- switch (type) {
- case AspectType::Spreadsheet:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet"));
- raiseDockConnect(m_mainWindow->spreadsheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->spreadsheetDock->setSpreadsheets(castList(selectedAspects));
- break;
- case AspectType::PivotTable:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Pivot Table"));
-
- if (!m_mainWindow->pivotTableDock) {
- m_mainWindow->pivotTableDock = new PivotTableDock(m_mainWindow->stackedWidget);
- connect(m_mainWindow->pivotTableDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->pivotTableDock);
- }
-
- m_mainWindow->pivotTableDock->setPivotTable(static_cast(selectedAspects.first()));
-
- m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->pivotTableDock);
- break;
- case AspectType::HypothesisTest:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Hypothesis Test"));
- if (!m_mainWindow->hypothesisTestDock) {
- m_mainWindow->hypothesisTestDock = new HypothesisTestDock(m_mainWindow->stackedWidget);
- connect(m_mainWindow->hypothesisTestDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->hypothesisTestDock);
- }
- m_mainWindow->hypothesisTestDock->setHypothesisTest(static_cast(selectedAspects.first()));
-
- m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->hypothesisTestDock);
- break;
- case AspectType::Column:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column"));
- raiseDockConnect(m_mainWindow->columnDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->columnDock->setColumns(castList(selectedAspects));
- break;
- case AspectType::Matrix:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix"));
- raiseDockConnect(m_mainWindow->matrixDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->matrixDock->setMatrices(castList(selectedAspects));
- break;
- case AspectType::Worksheet: {
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Worksheet"));
- if (!m_mainWindow->worksheetDock) {
- m_mainWindow->worksheetDock = new WorksheetDock(m_mainWindow->stackedWidget);
- connect(m_mainWindow->worksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->worksheetDock);
- }
- raiseDockConnect(m_mainWindow->worksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->worksheetDock->setWorksheets(castList(selectedAspects));
- break;
- } case AspectType::CartesianPlot: {
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot"));
- raiseDockConnect(m_mainWindow->cartesianPlotDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->cartesianPlotDock->setPlots(castList(selectedAspects));
- break;
- } case AspectType::CartesianPlotLegend:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend"));
- raiseDockConnect(m_mainWindow->cartesianPlotLegendDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->cartesianPlotLegendDock->setLegends(castList(selectedAspects));
- break;
- case AspectType::Axis:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis"));
- raiseDockConnect(m_mainWindow->axisDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->axisDock->setAxes(castList(selectedAspects));
- break;
- case AspectType::XYCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve"));
- raiseDockSetupConnect(m_mainWindow->xyCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYEquationCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation"));
- raiseDockSetupConnect(m_mainWindow->xyEquationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyEquationCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYDataReductionCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Data Reduction"));
-
- if (!m_mainWindow->xyDataReductionCurveDock) {
- m_mainWindow->xyDataReductionCurveDock = new XYDataReductionCurveDock(m_mainWindow->stackedWidget, m_mainWindow->statusBar());
- m_mainWindow->xyDataReductionCurveDock->setupGeneral();
- connect(m_mainWindow->xyDataReductionCurveDock, &XYDataReductionCurveDock::info,
- [&](const QString& text){ m_mainWindow->statusBar()->showMessage(text); });
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock);
- }
-
- m_mainWindow->xyDataReductionCurveDock->setCurves(castList(selectedAspects));
-
- m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock);
- break;
- case AspectType::XYDifferentiationCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation"));
- raiseDockSetupConnect(m_mainWindow->xyDifferentiationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyDifferentiationCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYIntegrationCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration"));
- raiseDockSetupConnect(m_mainWindow->xyIntegrationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyIntegrationCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYInterpolationCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation"));
- raiseDockSetupConnect(m_mainWindow->xyInterpolationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyInterpolationCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYSmoothCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing"));
- raiseDockSetupConnect(m_mainWindow->xySmoothCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xySmoothCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYFitCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit"));
- raiseDockSetupConnect(m_mainWindow->xyFitCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyFitCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYFourierTransformCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform"));
- raiseDockSetupConnect(m_mainWindow->xyFourierTransformCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyFourierTransformCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYFourierFilterCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter"));
- raiseDockSetupConnect(m_mainWindow->xyFourierFilterCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyFourierFilterCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYConvolution:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Convolution/Deconvolution"));
- raiseDockSetupConnect(m_mainWindow->xyConvolutionCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyConvolutionCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::XYCorrelationCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Auto-/Cross-Correlation"));
- raiseDockSetupConnect(m_mainWindow->xyCorrelationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->xyCorrelationCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::Histogram:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties"));
- raiseDockConnect(m_mainWindow->histogramDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
- m_mainWindow->histogramDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::TextLabel:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label"));
- raiseDock(m_mainWindow->textLabelDock, m_mainWindow->stackedWidget);
- m_mainWindow->textLabelDock->setLabels(castList(selectedAspects));
- break;
- case AspectType::CustomPoint:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point"));
- raiseDock(m_mainWindow->customPointDock, m_mainWindow->stackedWidget);
- m_mainWindow->customPointDock->setPoints(castList(selectedAspects));
- break;
- case AspectType::DatapickerCurve:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve"));
- raiseDock(m_mainWindow->datapickerCurveDock, m_mainWindow->stackedWidget);
- m_mainWindow->datapickerCurveDock->setCurves(castList(selectedAspects));
- break;
- case AspectType::Datapicker:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker"));
- raiseDock(m_mainWindow->datapickerImageDock, m_mainWindow->stackedWidget);
-
- {
- QList list;
- for (auto* aspect : selectedAspects)
- list << qobject_cast(aspect)->image();
- m_mainWindow->datapickerImageDock->setImages(list);
- }
- break;
- case AspectType::Project:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project"));
- raiseDock(m_mainWindow->projectDock, m_mainWindow->stackedWidget);
- m_mainWindow->projectDock->setProject(m_mainWindow->m_project);
- break;
- case AspectType::CantorWorksheet:
+ auto clearDock = [&](){
+ if (m_mainWindow->stackedWidget->currentWidget())
+ m_mainWindow->stackedWidget->currentWidget()->hide();
+
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties"));
+ };
+
+ if (selectedAspects.isEmpty()) {
+ clearDock();
+ return;
+ }
+
+ // update cursor dock
+ AbstractAspect* parent = selectedAspects[0]->parent(AspectType::Worksheet);
+ if (selectedAspects[0]->inherits(AspectType::Worksheet)) {
+ Worksheet* worksheet = static_cast(selectedAspects[0]);
+
+ if (m_mainWindow->cursorWidget) {
+ m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel());
+ QVector plots = worksheet->children();
+ if (!plots.isEmpty())
+ m_mainWindow->cursorWidget->setPlots(plots);
+ }
+ } else if (parent) {
+ if (m_mainWindow->cursorWidget) {
+ Worksheet* worksheet = static_cast(parent);
+ QVector plots = worksheet->children();
+ m_mainWindow->cursorWidget->setCursorTreeViewModel(worksheet->cursorModel());
+ m_mainWindow->cursorWidget->setPlots(plots);
+ }
+
+ }
+
+ const AspectType type = selectedAspects.front()->type();
+
+ // Check, whether objects of different types were selected.
+ // Don't show any dock widgets in this case.
+ for (auto* aspect : selectedAspects) {
+ if (aspect->type() != type) {
+ clearDock();
+ return;
+ }
+ }
+
+ switch (type) {
+ case AspectType::Spreadsheet:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet"));
+ raiseDockConnect(m_mainWindow->spreadsheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->spreadsheetDock->setSpreadsheets(castList(selectedAspects));
+ break;
+ case AspectType::PivotTable:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Pivot Table"));
+
+ if (!m_mainWindow->pivotTableDock) {
+ m_mainWindow->pivotTableDock = new PivotTableDock(m_mainWindow->stackedWidget);
+ connect(m_mainWindow->pivotTableDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->pivotTableDock);
+ }
+
+ m_mainWindow->pivotTableDock->setPivotTable(static_cast(selectedAspects.first()));
+
+ m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->pivotTableDock);
+ break;
+ case AspectType::HypothesisTest:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Hypothesis Test"));
+ if (!m_mainWindow->hypothesisTestDock) {
+ m_mainWindow->hypothesisTestDock = new HypothesisTestDock(m_mainWindow->stackedWidget);
+ connect(m_mainWindow->hypothesisTestDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->hypothesisTestDock);
+ }
+ m_mainWindow->hypothesisTestDock->setHypothesisTest(static_cast(selectedAspects.first()));
+
+ m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->hypothesisTestDock);
+ break;
+ case AspectType::CorrelationCoefficient:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Correlation Coefficient"));
+
+ if (!m_mainWindow->correlationCoefficientDock) {
+ m_mainWindow->correlationCoefficientDock = new CorrelationCoefficientDock(m_mainWindow->stackedWidget);
+ connect(m_mainWindow->correlationCoefficientDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->correlationCoefficientDock);
+ }
+
+ m_mainWindow->correlationCoefficientDock->setCorrelationCoefficient(static_cast(selectedAspects.first()));
+
+ m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->correlationCoefficientDock);
+ break;
+ case AspectType::Column:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column"));
+ raiseDockConnect(m_mainWindow->columnDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->columnDock->setColumns(castList(selectedAspects));
+ break;
+ case AspectType::Matrix:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix"));
+ raiseDockConnect(m_mainWindow->matrixDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->matrixDock->setMatrices(castList(selectedAspects));
+ break;
+ case AspectType::Worksheet: {
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Worksheet"));
+ if (!m_mainWindow->worksheetDock) {
+ m_mainWindow->worksheetDock = new WorksheetDock(m_mainWindow->stackedWidget);
+ connect(m_mainWindow->worksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString)));
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->worksheetDock);
+ }
+ raiseDockConnect(m_mainWindow->worksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->worksheetDock->setWorksheets(castList(selectedAspects));
+ break;
+ } case AspectType::CartesianPlot: {
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot"));
+ raiseDockConnect(m_mainWindow->cartesianPlotDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->cartesianPlotDock->setPlots(castList(selectedAspects));
+ break;
+ } case AspectType::CartesianPlotLegend:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend"));
+ raiseDockConnect(m_mainWindow->cartesianPlotLegendDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->cartesianPlotLegendDock->setLegends(castList(selectedAspects));
+ break;
+ case AspectType::Axis:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis"));
+ raiseDockConnect(m_mainWindow->axisDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->axisDock->setAxes(castList(selectedAspects));
+ break;
+ case AspectType::XYCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve"));
+ raiseDockSetupConnect(m_mainWindow->xyCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYEquationCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation"));
+ raiseDockSetupConnect(m_mainWindow->xyEquationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyEquationCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYDataReductionCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Data Reduction"));
+
+ if (!m_mainWindow->xyDataReductionCurveDock) {
+ m_mainWindow->xyDataReductionCurveDock = new XYDataReductionCurveDock(m_mainWindow->stackedWidget, m_mainWindow->statusBar());
+ m_mainWindow->xyDataReductionCurveDock->setupGeneral();
+ connect(m_mainWindow->xyDataReductionCurveDock, &XYDataReductionCurveDock::info,
+ [&](const QString& text){ m_mainWindow->statusBar()->showMessage(text); });
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock);
+ }
+
+ m_mainWindow->xyDataReductionCurveDock->setCurves(castList(selectedAspects));
+
+ m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock);
+ break;
+ case AspectType::XYDifferentiationCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation"));
+ raiseDockSetupConnect(m_mainWindow->xyDifferentiationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyDifferentiationCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYIntegrationCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration"));
+ raiseDockSetupConnect(m_mainWindow->xyIntegrationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyIntegrationCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYInterpolationCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation"));
+ raiseDockSetupConnect(m_mainWindow->xyInterpolationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyInterpolationCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYSmoothCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing"));
+ raiseDockSetupConnect(m_mainWindow->xySmoothCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xySmoothCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYFitCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit"));
+ raiseDockSetupConnect(m_mainWindow->xyFitCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyFitCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYFourierTransformCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform"));
+ raiseDockSetupConnect(m_mainWindow->xyFourierTransformCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyFourierTransformCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYFourierFilterCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter"));
+ raiseDockSetupConnect(m_mainWindow->xyFourierFilterCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyFourierFilterCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYConvolution:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Convolution/Deconvolution"));
+ raiseDockSetupConnect(m_mainWindow->xyConvolutionCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyConvolutionCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::XYCorrelationCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Auto-/Cross-Correlation"));
+ raiseDockSetupConnect(m_mainWindow->xyCorrelationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->xyCorrelationCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::Histogram:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties"));
+ raiseDockConnect(m_mainWindow->histogramDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+ m_mainWindow->histogramDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::TextLabel:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label"));
+ raiseDock(m_mainWindow->textLabelDock, m_mainWindow->stackedWidget);
+ m_mainWindow->textLabelDock->setLabels(castList(selectedAspects));
+ break;
+ case AspectType::CustomPoint:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point"));
+ raiseDock(m_mainWindow->customPointDock, m_mainWindow->stackedWidget);
+ m_mainWindow->customPointDock->setPoints(castList(selectedAspects));
+ break;
+ case AspectType::DatapickerCurve:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve"));
+ raiseDock(m_mainWindow->datapickerCurveDock, m_mainWindow->stackedWidget);
+ m_mainWindow->datapickerCurveDock->setCurves(castList(selectedAspects));
+ break;
+ case AspectType::Datapicker:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker"));
+ raiseDock(m_mainWindow->datapickerImageDock, m_mainWindow->stackedWidget);
+
+ {
+ QList list;
+ for (auto* aspect : selectedAspects)
+ list << qobject_cast(aspect)->image();
+ m_mainWindow->datapickerImageDock->setImages(list);
+ }
+ break;
+ case AspectType::Project:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project"));
+ raiseDock(m_mainWindow->projectDock, m_mainWindow->stackedWidget);
+ m_mainWindow->projectDock->setProject(m_mainWindow->m_project);
+ break;
+ case AspectType::CantorWorksheet:
#ifdef HAVE_CANTOR_LIBS
- raiseDockConnect(m_mainWindow->cantorWorksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
-
- {
- QList list = castList(selectedAspects);
- if (list.size() == 1)
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window %1 is a Cantor backend", "%1 Properties", list.first()->backendName()));
- else
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "CAS Properties"));
- m_mainWindow->cantorWorksheetDock->setCantorWorksheets(list);
- }
+ raiseDockConnect(m_mainWindow->cantorWorksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget);
+
+ {
+ QList list = castList(selectedAspects);
+ if (list.size() == 1)
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window %1 is a Cantor backend", "%1 Properties", list.first()->backendName()));
+ else
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "CAS Properties"));
+ m_mainWindow->cantorWorksheetDock->setCantorWorksheets(list);
+ }
#endif
- break;
- case AspectType::Note:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes"));
- raiseDock(m_mainWindow->notesDock, m_mainWindow->stackedWidget);
- m_mainWindow->notesDock->setNotesList(castList(selectedAspects));
- break;
- case AspectType::MQTTClient:
+ break;
+ case AspectType::Note:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes"));
+ raiseDock(m_mainWindow->notesDock, m_mainWindow->stackedWidget);
+ m_mainWindow->notesDock->setNotesList(castList(selectedAspects));
+ break;
+ case AspectType::MQTTClient:
#ifdef HAVE_MQTT
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
- raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
- m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first()));
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
+ raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
+ m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first()));
#endif
- break;
- case AspectType::MQTTSubscription:
+ break;
+ case AspectType::MQTTSubscription:
#ifdef HAVE_MQTT
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
- raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
- m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient());
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
+ raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
+ m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient());
#endif
- break;
- case AspectType::MQTTTopic:
+ break;
+ case AspectType::MQTTTopic:
#ifdef HAVE_MQTT
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
- raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
- m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient());
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source"));
+ raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
+ m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient());
#endif
- break;
- case AspectType::LiveDataSource:
- m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source"));
- raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
- m_mainWindow->m_liveDataDock->setLiveDataSource(static_cast(selectedAspects.first()));
- break;
- case AspectType::AbstractAspect:
- case AspectType::AbstractColumn:
- case AspectType::AbstractDataSource:
- case AspectType::AbstractFilter:
- case AspectType::AbstractPart:
- case AspectType::AbstractPlot:
- case AspectType::ColumnStringIO:
- case AspectType::DatapickerImage:
- case AspectType::DatapickerPoint:
- case AspectType::Folder:
- case AspectType::PlotArea:
- case AspectType::SimpleFilterColumn:
- case AspectType::Workbook:
- case AspectType::WorksheetElement:
- case AspectType::WorksheetElementContainer:
- case AspectType::WorksheetElementGroup:
- case AspectType::XYAnalysisCurve:
- clearDock();
- return;
- }
-
- m_mainWindow->stackedWidget->currentWidget()->show();
+ break;
+ case AspectType::LiveDataSource:
+ m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source"));
+ raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget);
+ m_mainWindow->m_liveDataDock->setLiveDataSource(static_cast(selectedAspects.first()));
+ break;
+ case AspectType::AbstractAspect:
+ case AspectType::AbstractColumn:
+ case AspectType::AbstractDataSource:
+ case AspectType::AbstractFilter:
+ case AspectType::AbstractPart:
+ case AspectType::AbstractPlot:
+ case AspectType::ColumnStringIO:
+ case AspectType::DatapickerImage:
+ case AspectType::DatapickerPoint:
+ case AspectType::Folder:
+ case AspectType::PlotArea:
+ case AspectType::SimpleFilterColumn:
+ case AspectType::Workbook:
+ case AspectType::WorksheetElement:
+ case AspectType::WorksheetElementContainer:
+ case AspectType::WorksheetElementGroup:
+ case AspectType::XYAnalysisCurve:
+ clearDock();
+ return;
+ }
+
+ m_mainWindow->stackedWidget->currentWidget()->show();
}
/*!
- handles the selection of a hidden aspect \c aspect in the view (relevant for WorksheetView only at the moment).
- Currently, a hidden aspect can only be a plot title lable or an axis label.
- -> Activate the corresponding DockWidget and make the title tab current.
+ handles the selection of a hidden aspect \c aspect in the view (relevant for WorksheetView only at the moment).
+ Currently, a hidden aspect can only be a plot title lable or an axis label.
+ -> Activate the corresponding DockWidget and make the title tab current.
*/
void GuiObserver::hiddenAspectSelected(const AbstractAspect* aspect) const {
- const AbstractAspect* parent = aspect->parentAspect();
- if (!parent)
- return;
-
- switch (static_cast(parent->type())) { // cast the enum to turn off warnings about unhandled cases
- case static_cast(AspectType::Axis):
- if (!m_mainWindow->axisDock) {
- m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget);
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock);
- }
- m_mainWindow->axisDock->activateTitleTab();
- break;
- case static_cast(AspectType::CartesianPlot):
- if (!m_mainWindow->cartesianPlotDock) {
- m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget);
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock);
- }
- m_mainWindow->cartesianPlotDock->activateTitleTab();
- break;
- case static_cast(AspectType::CartesianPlotLegend):
- if (!m_mainWindow->cartesianPlotLegendDock) {
- m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget);
- m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock);
- }
- m_mainWindow->cartesianPlotLegendDock->activateTitleTab();
- break;
- default:
- break;
- }
+ const AbstractAspect* parent = aspect->parentAspect();
+ if (!parent)
+ return;
+
+ switch (static_cast(parent->type())) { // cast the enum to turn off warnings about unhandled cases
+ case static_cast(AspectType::Axis):
+ if (!m_mainWindow->axisDock) {
+ m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget);
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock);
+ }
+ m_mainWindow->axisDock->activateTitleTab();
+ break;
+ case static_cast(AspectType::CartesianPlot):
+ if (!m_mainWindow->cartesianPlotDock) {
+ m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget);
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock);
+ }
+ m_mainWindow->cartesianPlotDock->activateTitleTab();
+ break;
+ case static_cast(AspectType::CartesianPlotLegend):
+ if (!m_mainWindow->cartesianPlotLegendDock) {
+ m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget);
+ m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock);
+ }
+ m_mainWindow->cartesianPlotLegendDock->activateTitleTab();
+ break;
+ default:
+ break;
+ }
}
diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp
index db3cd17ae..3ac5f39cc 100644
--- a/src/kdefrontend/MainWin.cpp
+++ b/src/kdefrontend/MainWin.cpp
@@ -1,2086 +1,2100 @@
/***************************************************************************
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/generalTest/HypothesisTest.h"
+#include "backend/generalTest/CorrelationCoefficient.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/matrix/Matrix.h"
#include "backend/pivot/PivotTable.h"
#include "backend/worksheet/Worksheet.h"
#include "backend/datasources/LiveDataSource.h"
#ifdef HAVE_LIBORIGIN
#include "backend/datasources/projects/OriginProjectParser.h"
#endif
#ifdef HAVE_CANTOR_LIBS
#include "backend/cantorWorksheet/CantorWorksheet.h"
#endif
#include "backend/datapicker/Datapicker.h"
#include "backend/note/Note.h"
#include "backend/lib/macros.h"
#include "backend/worksheet/TreeModel.h"
#include "backend/worksheet/plots/cartesian/CartesianPlot.h"
#ifdef HAVE_MQTT
#include "backend/datasources/MQTTClient.h"
#endif
#include "commonfrontend/core/PartMdiView.h"
#include "commonfrontend/ProjectExplorer.h"
#include "commonfrontend/matrix/MatrixView.h"
#include "commonfrontend/spreadsheet/SpreadsheetView.h"
#include "commonfrontend/worksheet/WorksheetView.h"
#ifdef HAVE_CANTOR_LIBS
#include "commonfrontend/cantorWorksheet/CantorWorksheetView.h"
#endif
#include "commonfrontend/datapicker/DatapickerView.h"
#include "commonfrontend/datapicker/DatapickerImageView.h"
#include "commonfrontend/note/NoteView.h"
#include "commonfrontend/widgets/MemoryWidget.h"
#include "kdefrontend/datasources/ImportFileDialog.h"
#include "kdefrontend/datasources/ImportProjectDialog.h"
#include "kdefrontend/datasources/ImportSQLDatabaseDialog.h"
#include
#include "kdefrontend/dockwidgets/ProjectDock.h"
#include "kdefrontend/HistoryDialog.h"
#include "kdefrontend/SettingsDialog.h"
#include "kdefrontend/GuiObserver.h"
#include "kdefrontend/widgets/FITSHeaderEditDialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_CANTOR_LIBS
#include
#include
#include
#include
#endif
/*!
\class MainWin
\brief Main application window.
\ingroup kdefrontend
*/
MainWin::MainWin(QWidget *parent, const QString& filename)
: KXmlGuiWindow(parent) {
initGUI(filename);
setAcceptDrops(true);
//restore the geometry
KConfigGroup group = KSharedConfig::openConfig()->group("MainWin");
restoreGeometry(group.readEntry("geometry", QByteArray()));
}
MainWin::~MainWin() {
//save the recent opened files
m_recentProjectsAction->saveEntries( KSharedConfig::openConfig()->group("Recent Files") );
KConfigGroup group = KSharedConfig::openConfig()->group("MainWin");
group.writeEntry("geometry", saveGeometry());
KSharedConfig::openConfig()->sync();
if (m_project != nullptr) {
m_mdiArea->closeAllSubWindows();
disconnect(m_project, nullptr, this, nullptr);
delete m_project;
}
if (m_aspectTreeModel)
delete m_aspectTreeModel;
if (m_guiObserver)
delete m_guiObserver;
}
void MainWin::showPresenter() {
Worksheet* w = activeWorksheet();
if (w) {
auto* view = dynamic_cast(w->view());
view->presenterMode();
} else {
//currently active object is not a worksheet but we're asked to start in the presenter mode
//determine the first available worksheet and show it in the presenter mode
QVector worksheets = m_project->children();
if (worksheets.size() > 0) {
auto* view = qobject_cast(worksheets.first()->view());
view->presenterMode();
} else {
QMessageBox::information(this, i18n("Presenter Mode"),
i18n("No worksheets are available in the project. The presenter mode will not be started."));
}
}
}
AspectTreeModel* MainWin::model() const {
return m_aspectTreeModel;
}
/*!
* Show cursor dock and set the treeview model
* @param model
*/
void MainWin::showCursorDock(TreeModel* model, QVector plots) {
if (!cursorDock) {
cursorDock = new QDockWidget(i18n("Cursor"), this);
cursorWidget = new CursorDock(cursorDock);
cursorDock->setWidget(cursorWidget);
cursorDock->setFloating(true);
// does not work. Don't understand why
// if (m_propertiesDock)
// tabifyDockWidget(cursorDock, m_propertiesDock);
// else
addDockWidget(Qt::DockWidgetArea::AllDockWidgetAreas, cursorDock);
}
cursorWidget->setCursorTreeViewModel(model);
cursorWidget->setPlots(plots);
cursorDock->show();
}
Project* MainWin::project() const {
return m_project;
}
void MainWin::initGUI(const QString& fileName) {
m_mdiArea = new QMdiArea;
setCentralWidget(m_mdiArea);
connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &MainWin::handleCurrentSubWindowChanged);
statusBar()->showMessage(i18nc("%1 is the LabPlot version", "Welcome to LabPlot %1", QLatin1String(LVERSION)));
initActions();
#ifdef Q_OS_DARWIN
setupGUI(Default, QLatin1String("/Applications/labplot2.app/Contents/Resources/labplot2ui.rc"));
#else
setupGUI(Default, KXMLGUIClient::xmlFile()); // should be "labplot2ui.rc"
#endif
DEBUG("component name: " << KXMLGUIClient::componentName().toStdString());
DEBUG("XML file: " << KXMLGUIClient::xmlFile().toStdString() << " (should be \"labplot2ui.rc\")");
//all toolbars created via the KXMLGUI framework are locked on default:
// * on the very first program start, unlock all toolbars
// * on later program starts, set stored lock status
//Furthermore, we want to show icons only after the first program start.
KConfigGroup groupMain = KSharedConfig::openConfig()->group("MainWindow");
if (groupMain.exists()) {
//KXMLGUI framework automatically stores "Disabled" for the key "ToolBarsMovable"
//in case the toolbars are locked -> load this value
const QString& str = groupMain.readEntry(QLatin1String("ToolBarsMovable"), "");
bool locked = (str == QLatin1String("Disabled"));
KToolBar::setToolBarsLocked(locked);
} else {
//first start
KToolBar::setToolBarsLocked(false);
//show icons only
for (auto* container : factory()->containers(QLatin1String("ToolBar"))) {
auto* toolbar = dynamic_cast(container);
if (toolbar)
toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
}
}
initMenus();
auto* mainToolBar = qobject_cast |