diff --git a/CMakeLists.txt b/CMakeLists.txt index 30581d22f..e23fba315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,337 +1,334 @@ project(labplot2) # minimum 3.2.0 for FindGSL.cmake cmake_minimum_required(VERSION 3.2.0) set(KF5_MIN_VERSION "5.16.0") find_package(ECM 1.3.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) # build type: "release", "debug", "debugfull" string (TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE) find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE REQUIRED COMPONENTS Concurrent Gui PrintSupport Sql Svg Widgets Test SerialPort ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Archive Completion Config ConfigWidgets CoreAddons DocTools I18n IconThemes -# TODO: remove KDELibs4Support - KDELibs4Support - KIO TextWidgets WidgetsAddons XmlGui OPTIONAL_COMPONENTS NewStuff SyntaxHighlighting ) IF (Qt5SerialPort_FOUND) MESSAGE (STATUS "Found Qt5 SerialPort") ELSE () MESSAGE (STATUS "Qt5 SerialPort not found") ENDIF () IF (KF5NewStuff_FOUND) MESSAGE (STATUS "Found KF5 new stuff") add_definitions (-DHAVE_KF5_NEW_STUFF) ELSE () MESSAGE (STATUS "KF5 new stuff not found") ENDIF () IF (KF5SyntaxHighlighting_FOUND) MESSAGE (STATUS "Found KF5 syntax highlighting") add_definitions (-DHAVE_KF5_SYNTAX_HIGHLIGHTING) ELSE () MESSAGE (STATUS "KF5 syntax highlighting not found") ENDIF () find_package(BISON REQUIRED) include(FeatureSummary) include(ECMAddAppIcon) include(ECMInstallIcons) include(KDEInstallDirs) include(KDECompilerSettings) include(KDECMakeSettings) ### compiler flags ###################################### set (GENERIC_FLAGS "-Wall -Wextra -Wundef -Wpointer-arith -Wunreachable-code -Wunused -Wdeprecated-declarations -fno-omit-frame-pointer -fstack-protector") set (GENERIC_GNU_FLAGS "-O2 -Wcast-align -Wswitch-enum -fvisibility=default") set (GENERIC_C_FLAGS "-std=c99 ${GENERIC_FLAGS} -fno-exceptions") # liborigin needs exceptions set (GENERIC_CXX_FLAGS "-std=c++11 ${GENERIC_FLAGS} -fexceptions") if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") message(STATUS "GNU C compiler detected, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERIC_C_FLAGS} ${GENERIC_GNU_FLAGS}") elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") message(STATUS "Clang C compiler detected, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE ${GENERIC_C_FLAGS} ${GENERIC_GNU_FLAGS}") elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "Intel") message(STATUS "Intel C compiler detected, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -O3 ${GENERIC_C_FLAGS}") elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "PGI") message(STATUS "PGI C compiler detected, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -O3 -D__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1 -Minform=inform -Mbounds -Mchkstk") # " x" postfix to work around a bug in CMake that causes "MSVC" to translate to something completely different elseif (("${CMAKE_C_COMPILER_ID} x" MATCHES "MSVC") OR MSVC) message(STATUS "MSVC C compiler detected, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -W3") set(MSVC_FOUND TRUE) else () message(STATUS "UNKNOWN C compiler, adding compile flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERIC_C_FLAGS}") endif() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") message(STATUS "GNU C++ compiler detected, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERIC_CXX_FLAGS} ${GENERIC_GNU_FLAGS}") elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") message(STATUS "Clang C++ compiler detected, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE ${GENERIC_CXX_FLAGS} ${GENERIC_GNU_FLAGS}") elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") message(STATUS "Intel C++ compiler detected, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE ${GENERIC_CXX_FLAGS}") #-std=c++0x comes with cmake's general flags, depricated in icc, remove it string(REPLACE "-std=c++0x" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "PGI") message(STATUS "PGI C++ compiler detected, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -O3 -std=c++11 -D__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1 -Minform=inform -Mbounds -Mchkstk") # " x" postfix to work around a bug in CMake that causes "MSVC" to translate to something completely different elseif (("${CMAKE_CXX_COMPILER_ID} x" MATCHES "MSVC") OR MSVC) message(STATUS "MSVC C++ compiler detected, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -W3") set(MSVC_FOUND TRUE) else () message(STATUS "UNKNOWN C++ compiler, adding compile flags") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERIC_CXX_FLAGS}") endif () ##########################################################ESC[m add_definitions (${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) include_directories (${QDBUS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) add_definitions (-DLVERSION=\"2.5.0\") # add_definitions (-DLDEBUG='1') set(BUILD_SHARED_LIBS true) cmake_policy(SET CMP0002 OLD) IF (CMAKE_VERSION VERSION_EQUAL "3.3" OR CMAKE_VERSION VERSION_GREATER "3.3") cmake_policy(SET CMP0063 NEW) ENDIF() ### Options ###################################### option(ENABLE_CANTOR "Build with Cantor support" ON) option(ENABLE_FFTW "Build with FFTW support" ON) option(ENABLE_HDF5 "Build with HDF5 support" ON) option(ENABLE_NETCDF "Build with NetCDF support" ON) option(ENABLE_FITS "Build with FITS support" ON) option(ENABLE_LIBCERF "Build with libcerf support" ON) option(ENABLE_LIBORIGIN "Build with liborigin support" ON) option(ENABLE_TESTS "Build with tests" ON) ### OS macros #################################### IF (WIN32) add_definitions (-DHAVE_WINDOWS) ENDIF () ### GSL (required) ############################### FIND_PACKAGE(GSL REQUIRED) ### liborigin (included) ############################### IF (ENABLE_LIBORIGIN) add_definitions (-DHAVE_LIBORIGIN) IF (CMAKE_BUILD_TYPE STREQUAL "debug" OR CMAKE_BUILD_TYPE STREQUAL "debugfull") MESSAGE (STATUS "Origin project import (through internal liborigin) enabled (parser logging enabled)") SET (ENABLE_ORIGIN_PARSER_LOG TRUE) ELSE () MESSAGE (STATUS "Origin project import (through internal liborigin) enabled (parser logging disabled)") ENDIF () ELSE () MESSAGE (STATUS "Origin project import disabled") ENDIF () ### Cantorlibs (optional) ############################### IF (ENABLE_CANTOR) FIND_LIBRARY (CANTOR_LIBS cantorlibs) FIND_PATH (CANTOR_INCLUDE_DIR cantor/worksheetaccess.h /usr/include /usr/local/include ) IF (CANTOR_LIBS AND CANTOR_INCLUDE_DIR) SET (CANTOR_LIBS_FOUND TRUE) ELSE () SET (CANTOR_LIBS_FOUND FALSE) SET (CANTOR_LIBS "") ENDIF() IF (CANTOR_LIBS_FOUND) MESSAGE (STATUS "Found Cantor Library: ${CANTOR_INCLUDE_DIR} ${CANTOR_LIBS}") add_definitions (-DHAVE_CANTOR_LIBS) ELSE () MESSAGE (STATUS "Cantor Library not found.") ENDIF () ELSE () MESSAGE (STATUS "Cantor Library DISABLED.") ENDIF () ### FFTW (optional) ##################################### IF (ENABLE_FFTW) FIND_LIBRARY (FFTW_LIBRARIES fftw3 PATHS /usr/lib /usr/local/lib ) FIND_PATH (FFTW_INCLUDE_DIR fftw3.h /usr/include /usr/local/include ) IF (FFTW_LIBRARIES AND FFTW_INCLUDE_DIR) SET (FFTW_FOUND TRUE) ELSE () SET (FFTW_FOUND FALSE) ENDIF () IF (FFTW_FOUND) MESSAGE (STATUS "Found FFTW 3 Library: ${FFTW_INCLUDE_DIR} ${FFTW_LIBRARIES}") add_definitions (-DHAVE_FFTW3) ELSE () MESSAGE (STATUS "FFTW 3 Library not found.") ENDIF () ELSE () MESSAGE (STATUS "FFTW 3 Library DISABLED.") ENDIF () ### HDF5 (optional) ############################## IF (ENABLE_HDF5) FIND_PACKAGE(HDF5 COMPONENTS C) IF (HDF5_FOUND) add_definitions (-DHAVE_HDF5) include_directories (${HDF5_INCLUDE_DIRS}) ELSE () MESSAGE (STATUS "Hierarchical Data Format (HDF5) Library not found.") ENDIF () ELSE () MESSAGE (STATUS "Hierarchical Data Format (HDF5) Library DISABLED.") ENDIF () ### NETCDF (optional) ############################# IF (ENABLE_NETCDF) FIND_LIBRARY (NETCDF_LIBRARY netcdf PATHS /usr/lib /usr/local/lib ) FIND_PATH (NETCDF_INCLUDE_DIR netcdf.h /usr/include /usr/local/include ) IF (NETCDF_LIBRARY AND NETCDF_INCLUDE_DIR) SET (NETCDF_FOUND TRUE) ELSE () SET (NETCDF_FOUND FALSE) ENDIF () IF (NETCDF_FOUND) MESSAGE (STATUS "Found Network Common Data Format (NetCDF) Library: ${NETCDF_INCLUDE_DIR} ${NETCDF_LIBRARY}") add_definitions (-DHAVE_NETCDF) ELSE () MESSAGE (STATUS "Network Common Data Format (NetCDF) Library not found.") ENDIF () ELSE () MESSAGE (STATUS "Network Common Data Format (NetCDF) Library DISABLED.") ENDIF () ### FITS (optional) ############################### IF (ENABLE_FITS) FIND_LIBRARY (CFITSIO_LIBRARY cfitsio PATHS /usr/lib /usr/local/lib $ENV{CFITSIO} ) FIND_PATH (CFITSIO_INCLUDE_DIR fitsio.h /usr/include /usr/include/cfitsio /usr/local/include $ENV{CFITSIO} ) IF (CFITSIO_LIBRARY AND CFITSIO_INCLUDE_DIR) SET (CFITSIO_FOUND TRUE) ELSE () SET (CFITSIO_FOUND FALSE) ENDIF () IF (CFITSIO_FOUND) MESSAGE (STATUS "Found Flexible Image Transport System Data Format (FITS) Library: ${CFITSIO_INCLUDE_DIR} ${CFITSIO_LIBRARY}") add_definitions (-DHAVE_FITS) include_directories (${CFITSIO_INCLUDE_DIR}) ELSE () MESSAGE (STATUS "Flexible Image Transport System Data Format (FITS) Library not found.") ENDIF () ELSE () MESSAGE (STATUS "Flexible Image Transport System Data Format (FITS) Library DISABLED.") ENDIF () ### LIBCERF (optional) ############################# IF (ENABLE_LIBCERF) FIND_LIBRARY (LIBCERF_LIBRARY cerf PATHS /usr/lib /usr/local/lib ) FIND_PATH (LIBCERF_INCLUDE_DIR cerf.h /usr/include /usr/local/include ) IF (LIBCERF_LIBRARY AND LIBCERF_INCLUDE_DIR) SET (LIBCERF_FOUND TRUE) ELSE () SET (LIBCERF_FOUND FALSE) ENDIF () IF (LIBCERF_FOUND) MESSAGE (STATUS "Found libcerf Library: ${LIBCERF_INCLUDE_DIR} ${LIBCERF_LIBRARY}") add_definitions (-DHAVE_LIBCERF) ELSE () MESSAGE (STATUS "libcerf Library not found.") ENDIF () ELSE () MESSAGE (STATUS "libcerf Library DISABLED.") ENDIF () ################################################# FIND_PATH (XLOCALE_INCLUDE_DIR xlocale.h /usr/include /usr/local/include ) IF (XLOCALE_INCLUDE_DIR) add_definitions (-DHAVE_XLOCALE) include_directories (${XLOCALE_INCLUDE_DIR}) ENDIF() add_subdirectory(data) add_subdirectory(doc) add_subdirectory(icons) add_subdirectory(src) IF (ENABLE_LIBORIGIN) add_subdirectory(liborigin) ENDIF () if (ENABLE_TESTS) enable_testing(true) add_subdirectory(tests) endif() install(FILES org.kde.labplot2.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL) diff --git a/ChangeLog b/ChangeLog index b6af780c8..a1e1fd6f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,204 +1,203 @@ -----2.5----- New features: * Support for reading and plotting of live-data * Improved data fitting * Automatically guess parameter of custom models * Better result presentation * Support different weight types * Consider given x- and y-error when fitting (can be switched off) * Show t statistics, P > |t| and confidence interval * Calculate p-value for chi-square and F test in nonlinear fitting * added fit models for most statistical distributions * Improved theming * Apply themes to worksheet and to all its children * Respect theme settings also in plot legends and labels - * Application option to set the default theme for new worksheets * Allow to disable theming in worksheets and plots after a theme was selected * Show currently active theme in the "Apply theme" button * New application option in the settings for the default theme used for new worksheets. * Support different data types * auto detect integer and datetime data in import * support number locale and datetime formats * improved data type support in spreadsheets * Import from SQL databases (tables or custom queries) * Import Origin OPJ projects * Much better support for Windows and macOS * Syntax highlighting for LaTeX in the text label * Allow to set the background color for LaTeX labels * Support Hermite polynomials from GSL 2.4 * Support error functions and related functions from libcerf * "Used in" sub-menu in column contex menu for faster navigation to the curves consuming the column * Direct application of analysis functions (smoothing, interpolating, etc.) on the ploted data via curve's context menu * Direct application of analysis functions on the data in the spreadsheet and plotting of the results via spreadsheet's context menu * Drag columns in the project explorer and drop them on plots (either in a worksheet view or in the project explorer) to create curves * "Show last N points" and "Show first N points" data ranges in cartesian plot * Added CLI option --presenter to start LabPlot directly in the presenter mode * Added CLI parameter to directly open project files (LabPlot or Origin) * Allow drag&drop of projects files (LabPlot and Origin) on the main window to load the project * Allow drag&drop of data files on the main window to import the data * Show tooltips for the supported mathematical functions and constants in the expression text field - * Automaticaly switch to the scientific representation for numbers bigger than 10^4 on the axis tick labels + * Automatically switch to the scientific representation for numbers bigger than 10^4 on the axis tick labels * Automatically allow the latex typesetting in the application after the latex environment was installed later without making the user to go to the settings dialog - * Allow to change the theme from within the application + * Allow to change the color scheme for the application * Smooth and animated zooming in the worksheet view * Allow to add text labels to plots * Improved building with MSVC, Intel and PGI compiler Performance improvements: * Faster copy&paste in the spreadsheet Bug fixes: * Bug 379877 - masked rows in spreadsheet not restored in project * Calculation of fit results corrected * Axes now support values larger than FLT_MAX (~10^38) and smaller than FLT_MIN (~10^-38) * When a LabPlot project is being droped in the main window, load the project directly instead of showing the import file dialog * Correctly save and restore masked cells * Don't crash if the rc-file was not found during the startup. -----2.4----- New features: * Support themes for plots * Import and editing of FITS data files * Data reduction by removing data points using multiple algorithms * Numerical differentiation and integration with several options * Many new pre-defined fit models (Gompertz, Weibull, Log-Normal, Gumbel, etc.) sorted in categories * Fit parameter now support fixed values, lower/upper limits and use Unicode * Fit model and random number distribution formulas are now rendered with LaTeX * Support user specified x range in all analysis functions * Allow to enter complete LaTeX documents in text labels * Configuration parameter to use different LaTex engines (LuaLaTex, XeLatex, pdfLaTex, LaTex) * Disable LaTeX typesetting if no LaTex installation (and other required tools) were found at runtime * Presenter mode for worksheets * Support for Mac OS * Support for Julia's vectors and tuples in CAS worksheets (requires Cantor v. 16.12 or higher) * Allow to jump directly to the data source spreadsheet via XYCurve's context menu * Select and delete multiple objects in project explorer * Improved and extended internal parser for mathematical expressions * Copy of worksheet elements as image to the clipboard via CTRL+C Bug fixes: * BUG: 361326 - Allow to select curves with overlapping bounding boxes * Correctly load worksheet sizes from saved templates * Fixed crash when removing columns in spreadsheet * Fixed crash when fitting using GSL >= 2 * List of available functions corrected * Constants are now available with full accuracy * Windows: Import of files and open recent files fixed -----2.3----- New features: * Integration of Cantor - Support for different open-source computer algebra systems * Statistics on spreadsheets and matrices * Export of spreadsheets and matrices to LaTeX tables * Interpolation of data including different splines, cosine, exponential, cubic Hermite (Catmull-Rom, cardinal, Kochanek-Bartels) and rational functions * Data smoothing using moving average (centered or lagged), percentile filter or Savitzky-Golay algorithm * Fourier filter (low pass, high pass, band pass, band reject) with ideal, Butterworth, Chebychev I+II, Legendre or Bessel-Thomson filter * Fourier transform with many window functions (Welch, Hann, Hamming, etc.) calculating magnitude, amplitude, power, phase, dB, etc. and supporting one/two sided spectrum with or without shift and x scaling to frequency, index or period * Filter and search capabilities in the drop down box for the selection of data sources * Sigmoid function as a new pre-defined fit model * Support for compiling on Microsoft Windows Performance improvements: * Faster generation of random values * Global option to enable/disable the double-buffering for faster painting of curves (enabled on default) Bug fixes: * Save and restore last used setting in RandomValuesDialog * Update axis title shape on title rotations correctly * Save and restore custom column widths in the spreadsheet * Fixed sporadic crashes during project close -----2.2----- New features: * Datapicker - tool for extracting curves and data points from imported images * Custom point on the plot with user-defined position and appearance * Accept drag&drop events * Support GSL 2.x * Import and export dialogs save and restore their settings and sizes Performance improvements: * Faster rendering of the image view of the matrix Bug fixes: * BUG: 354744 - make setting of range auto scaling in CartesianPlot undo/redo-able * Removed couple of hard coded sizes given in pixels for better user-experience on hidpi-displays * Fixes the bug with disabled undo/redo-actions in after the undo-history was cleared * Keep the information about the columns to be shown in the project explorer after project close * Fixed some bugs in painting of the selection band on the worksheet * Allow to open gz- and bz2-compressed LabPlot project files on the command line interface -----2.1----- New features: * New Matrix view for handling matrix data. * Workbook - a new container for grouping several objects of type Spreadsheet and/or Matrix. * Import of binary, image, NetCDF and HDF data into spreadsheet or matrix. * Visual HDF and NetCDF parser to view content of files and import data sets. * Preview of all supported file types in import dialog. * Transparently import compressed data files. * In xy-curve the points may not be connected by the line if there are NANs in-between. This behaviour is controlled by the parameter "skip gaps". * Multiplier of Pi format of the plot axis for periodical functions. * New operations on columns in Spreadsheet - reverse, drop values and mask values. * Formula used to generate the values in a column is stored and can be changed/adjusted in the formula dialog afterwards. * Curve filling: the area below, under, left to or right to the curve can be filled. * Support for multiple variables in "Function Values"-dialog - new data in the spreadsheet can be calculated out of multiple columns. Performance improvements: * Speeded up the creation of new columns during the import Bug fixes: * Fixed wrong behaviour when doing "zoom&select" in a plot and then deselecting the plot - it was not possible anymore to select the plot again on the worksheet. -----2.0.2----- New features: * Plot 2D-curves defined by mathematical equations in cartesian and polar coordinates or via a parametric equation. * Linear and non-linear regression analysis. Several predefined fit-models are provided. User-defined models are also possible. * Besides fixed worksheet sizes (predefined sizes like A4 etc. or user-defined), complete view size can be used. All sizes are automatically adjusted on resize. * Different axis arrow types. * "select region and zoom in", "select x-region and zoom in", "select y-region and zoom in" functions for cartesian plot. * Rounded border for several worksheet objects (plot area, legend etc.) * Hover effect for axis, curve and text label. * Added a MessageBox - ask befor deleting worksheet objects. * Added three new types for drop lines - "zero baseline", "min baseline" and "max baseline" * Fill the selection in Spreadsheet with a constant value provided by the user * Fill columns with uniform and non-uniform random numbers, several distributions are available. * Fill columns with function values * Allow custom resolutions for PNG-export * Export of the spreadsheet to a text file. * Simultaneous zooming and navigation accross multiple plots. * Implemented "Powers of 10/2/e" for the axis tick labels Bug fixes: * Don't crash when trying to create a plot in case no rc-file was installed. * Don't allow to select unwanted objects in TreeViewComboBox in ImportDialog and XYCurveDock. * Corrected painting of background images in plot area and legend. * BUG: 330723 - fixed weird selection in spreadsheet. * BUG: 330774 - fixed wrong positioning of axis on orientation changes. * Update main window title when project name is changed -----2.0.1----- Bug fix release. Solved issues: * Fixed wrong scaling of legend's text labels in pdf-export * Fixed memory corruption in CartesianPlotDock that can lead to crashes -----2.0.0----- First stable release of LabPlot2. LabPlot2 is a complete rewrite of LabPlot1 and lacks in this release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior as compared to LabPlot1 and there are several new features that were not available in LabPlot1. Brief summary of features and capabilities of LabPlot2 implemented in the first release: * project-based management of data * created objects are organized in a tree and are visualized and accessible in the project explorer * for a better management of objects, additional folders and sub-folders can be created within a project * spreadsheet with very basic functionality is available for manual data entry * "file data source" can be used to import a file and to let LabPlot2 watch for changes in that file * external data from an ascii file can be also directly imported to a spreadsheet for further editing * worksheet is the main object where plots, labels etc. are placed on * several zooming functions for the worksheet * only cartesian plot is implemented in the first release * arbitrary number of freely positionable axes is possible * xy-curve is implemented. As the data source for the x- and y-values columns either from a spreadsheet or from a file data source can be used * several zooming and "movement" functions are available for the plots which help to navigate through data * legend for xy-plots * a lot of properties of the worksheet elements can be edited in a very easy way in the corresponding dock widgets * plots on the worksheet can be put into a horizontal, vertical or grid layouts * export of worksheet (entire worksheet or current seleciton) to pdf, eps, png and svg diff --git a/admin/SLOC.txt b/admin/SLOC.txt index f1cff9750..d2e7b3e7e 100644 --- a/admin/SLOC.txt +++ b/admin/SLOC.txt @@ -1,171 +1,200 @@ Statistics generated using David A. Wheeler's 'SLOCCount'." +-----2.5----- +SLOC Directory SLOC-by-Language (Sorted) +50012 backend cpp=45264,ansic=4251,yacc=480,python=17 +26974 kdefrontend cpp=26974 +8914 commonfrontend cpp=8914 +487 tools cpp=487 +152 cantor cpp=152 +0 doc (none) +0 top_dir (none) + + +Totals grouped by language (dominant language first): +cpp: 81791 (94.51%) +ansic: 4251 (4.91%) +yacc: 480 (0.55%) +python: 17 (0.02%) + + +* Total Physical Source Lines of Code (SLOC) = 86,539 +* Development Effort Estimate, Person-Years (Person-Months) = 21.63 (259.59) + (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) +* Schedule Estimate, Years (Months) = 1.72 (20.67) + (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) +* Estimated Average Number of Developers (Effort/Schedule) = 12.56 +* Total Estimated Cost to Develop = $ 2,922,228 + (average salary = $56,286/year, overhead = 2.40) + + + -----2.4----- SLOC Directory SLOC-by-Language (Sorted) 41803 backend cpp=36475,ansic=4844,yacc=467,python=17 21807 kdefrontend cpp=21807 7633 commonfrontend cpp=7633 388 tools cpp=388 Totals grouped by language (dominant language first): cpp: 66303 (92.56%) ansic: 4844 (6.76%) yacc: 467 (0.65%) python: 17 (0.02%) * Total Physical Source Lines of Code (SLOC) = 71,631 * Development Effort Estimate, Person-Years (Person-Months) = 17.74 (212.85) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.60 (19.17) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 11.10 * Total Estimated Cost to Develop = $ 2,396,060 (average salary = $56,286/year, overhead = 2.40). ### kf5-version ### SLOC Directory SLOC-by-Language (Sorted) 42159 backend cpp=36831,ansic=4844,yacc=467,python=17 22096 kdefrontend cpp=22096 7841 commonfrontend cpp=7841 390 tools cpp=390 152 cantor cpp=152 Totals grouped by language (dominant language first): cpp: 67310 (92.66%) ansic: 4844 (6.67%) yacc: 467 (0.64%) python: 17 (0.02%) * Total Physical Source Lines of Code (SLOC) = 72,638 * Development Effort Estimate, Person-Years (Person-Months) = 18.00 (215.99) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.61 (19.28) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 11.20 * Total Estimated Cost to Develop = $ 2,431,441 (average salary = $56,286/year, overhead = 2.40). -----2.3----- SLOC Directory SLOC-by-Language (Sorted) 36609 backend cpp=33601,ansic=2991,python=17 18102 kdefrontend cpp=18102 7092 commonfrontend cpp=7092 256 tools cpp=256 Totals grouped by language (dominant language first): cpp: 59051 (95.15%) ansic: 2991 (4.82%) python: 17 (0.03%) * Total Physical Source Lines of Code (SLOC) = 62,059 * Development Effort Estimate, Person-Years (Person-Months) = 15.26 (183.09) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.51 (18.10) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 10.11 * Total Estimated Cost to Develop = $ 2,061,041 (average salary = $56,286/year, overhead = 2.40). ### kf5-version ### SLOC Directory SLOC-by-Language (Sorted) 36952 backend cpp=33944,ansic=2991,python=17 18380 kdefrontend cpp=18380 7297 commonfrontend cpp=7297 256 tools cpp=256 152 cantor cpp=152 Totals grouped by language (dominant language first): cpp: 60029 (95.23%) ansic: 2991 (4.74%) python: 17 (0.03%) * Total Physical Source Lines of Code (SLOC) = 63,037 * Development Effort Estimate, Person-Years (Person-Months) = 15.51 (186.12) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.52 (18.22) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 10.22 * Total Estimated Cost to Develop = $ 2,095,159 (average salary = $56,286/year, overhead = 2.40). -----2.2----- SLOC Directory SLOC-by-Language (Sorted) 32480 backend cpp=32480 15680 kdefrontend cpp=15680 6131 commonfrontend cpp=6131 256 tools cpp=256 Totals grouped by language (dominant language first): cpp: 54547 (100.00%) * Total Physical Source Lines of Code (SLOC) = 54,547 * Development Effort Estimate, Person-Years (Person-Months) = 13.32 (159.89) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.43 (17.19) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 9.30 * Total Estimated Cost to Develop = $ 1,799,911 (average salary = $56,286/year, overhead = 2.40). -----2.1----- SLOC Directory SLOC-by-Language (Sorted) 28516 backend cpp=28516 13913 kdefrontend cpp=13913 4636 commonfrontend cpp=4636 256 tools cpp=256 Totals grouped by language (dominant language first): cpp: 47321 (100.00%) * Total Physical Source Lines of Code (SLOC) = 47,321 * Development Effort Estimate, Person-Years (Person-Months) = 11.48 (137.73) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.35 (16.25) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 8.48 * Total Estimated Cost to Develop = $ 1,550,416 (average salary = $56,286/year, overhead = 2.40). -----2.0----- SLOC Directory SLOC-by-Language (Sorted) 23460 backend cpp=23460 13766 kdefrontend cpp=13559,yacc=207 3064 commonfrontend cpp=3064 137 tools cpp=137 Totals grouped by language (dominant language first): cpp: 40220 (99.49%) yacc: 207 (0.51%) * Total Physical Source Lines of Code (SLOC) = 40,427 * Development Effort Estimate, Person-Years (Person-Months) = 9.73 (116.74) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) * Schedule Estimate, Years (Months) = 1.27 (15.26) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) * Estimated Average Number of Developers (Effort/Schedule) = 7.65 * Total Estimated Cost to Develop = $ 1,314,155 (average salary = $56,286/year, overhead = 2.40). diff --git a/compile-devel b/compile-devel new file mode 100755 index 000000000..e4587737d --- /dev/null +++ b/compile-devel @@ -0,0 +1,14 @@ +#!/bin/bash + +BUILDDIR=build_devel + +#rm -rf $BUILDDIR + +mkdir $BUILDDIR +cd $BUILDDIR + +cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4 +#cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_LIBCERF=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4 +#cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4 + +#make install diff --git a/create-dmg.sh b/create-dmg.sh index 6ad5e8ccf..e4fbd861e 100755 --- a/create-dmg.sh +++ b/create-dmg.sh @@ -1,93 +1,93 @@ # Build it on Mac howto in script form # but be aware, some frameworks need patching to have this working # reference: https://cgit.kde.org/kate.git/tree/mac/emerge-deploy.sh # errors fatal set -e NAME=labplot2 PNAME=LabPlot2 VERSION=2.5.0 PREFIX=kde/Applications INPREFIX=$PREFIX/$PNAME.app/Contents TMPDIR=LabPlot2 SIGNATURE="Stefan Gerlach" ######################################### echo "CLEAN UP" rm -rf $PREFIX/$PNAME.app mkdir -pv $INPREFIX/{Frameworks,Resources,MacOS,PlugIns/iconengines,share/appdata,share/applications} # application cp -v labplot/build/src/$NAME.app/Contents/MacOS/$NAME $INPREFIX/MacOS echo "Running macdeployqt ..." # -verbose=3 macdeployqt $PREFIX/$PNAME.app -verbose=2 ######################################### echo "install files" # splash cp -v kde/share/$NAME/splash.png $INPREFIX/Resources/ # rc-file # Standardlocation (QSP): ~/Library/Application\ Support/kxmlgui5/labplot2/labplot2ui.rc # using hardcoded path: cp -v kde/share/kxmlgui5/$NAME/${NAME}ui.rc $INPREFIX/Resources/ # themes cp -vr kde/share/$NAME/themes $INPREFIX/Resources/ # gsl_distros, fit_models, colorchooser cp -vr kde/share/$NAME/pics $INPREFIX/Resources/ # color schemes (needs patched kcolorschememanager.cpp) cp -vr kde/share/$NAME/color-schemes $INPREFIX/Resources/color-schemes # appdata cp -v kde/share/metainfo/org.kde.labplot2.appdata.xml $INPREFIX/share/appdata/ cp -v kde/share/applications/org.kde.$NAME.desktop $INPREFIX/share/applications/ # cantor -cp -v kde/Applications/cantor.app/Contents/MacOS/cantor $INPREFIX/MacOS -cp -v kde/Applications/cantor_scripteditor.app/Contents/MacOS/cantor_scripteditor $INPREFIX/MacOS +cp -v kde/Applications/KDE/cantor.app/Contents/MacOS/cantor $INPREFIX/MacOS +cp -v kde/Applications/KDE/cantor_scripteditor.app/Contents/MacOS/cantor_scripteditor $INPREFIX/MacOS cp -vr kde/plugins/cantor $INPREFIX/PlugIns cp -v kde/lib/libcantor_config.dylib $INPREFIX/Frameworks/ cp -v kde/lib/libcantor_pythonbackend.dylib $INPREFIX/Frameworks/ cp -v kde/lib/libcantorlibs.18.07.70.dylib $INPREFIX/Frameworks/libcantorlibs.18.dylib # icons -cp -vf kde/bin/data/icontheme.rcc $INPREFIX/Resources/icontheme.rcc +cp -vf kde/share/icontheme.rcc $INPREFIX/Resources/icontheme.rcc # misc cp /Applications/KDE/labplot2.app/Contents/Info.plist $INPREFIX cp /Applications/KDE/labplot2.app/Contents/Resources/LABPLOT_SOURCE.icns $INPREFIX/Resources ### TODO # package icon # share/doc ########################################## # fix for hdf5 lib # install_name_tool -change /usr/local/Cellar/hdf5/1.8.17/lib/libhdf5.10.dylib /usr/local/opt/hdf5/1.8.17/lib/libhdf5.10.dylib /usr/local/opt/hdf5/1.8.17/lib/libhdf5_hl.10.dylib ############################################### if [ -d ./$TMPDIR ]; then rm -rf ./$TMPDIR/* else mkdir ./$TMPDIR fi mv $PREFIX/$PNAME.app ./$TMPDIR ln -s /Applications ./$TMPDIR/Applications ## remove stuff we don't need or like rm -rf $TMPDIR/$PNAME.app/Contents/Frameworks/QtTextToSpeech.framework #rm -rf $TMPDIR/$PNAME.app/Contents/Plugins/bearer ############################################### # create the final disk image echo "BUILDING PACKAGE" rm -f ./labplot-$VERSION.dmg hdiutil create -srcfolder ./$TMPDIR -format UDBZ -fs HFS+ -imagekey zlib-level=9 ./labplot-$VERSION.dmg diff --git a/data/pics/fit_models/pseudovoigt11.png b/data/pics/fit_models/pseudovoigt11.png new file mode 100644 index 000000000..a32ffd2ea Binary files /dev/null and b/data/pics/fit_models/pseudovoigt11.png differ diff --git a/data/pics/fit_models/pseudovoigt12.png b/data/pics/fit_models/pseudovoigt12.png new file mode 100644 index 000000000..258a40316 Binary files /dev/null and b/data/pics/fit_models/pseudovoigt12.png differ diff --git a/data/pics/fit_models/pseudovoigt13.png b/data/pics/fit_models/pseudovoigt13.png new file mode 100644 index 000000000..5c4da859b Binary files /dev/null and b/data/pics/fit_models/pseudovoigt13.png differ diff --git a/data/pics/fit_models/pseudovoigt14.png b/data/pics/fit_models/pseudovoigt14.png new file mode 100644 index 000000000..4623d5df3 Binary files /dev/null and b/data/pics/fit_models/pseudovoigt14.png differ diff --git a/data/pics/fit_models/voigt1.png b/data/pics/fit_models/voigt1.png index f784ba41a..16a76e9e1 100644 Binary files a/data/pics/fit_models/voigt1.png and b/data/pics/fit_models/voigt1.png differ diff --git a/data/pics/fit_models/voigt2.png b/data/pics/fit_models/voigt2.png new file mode 100644 index 000000000..08029a39c Binary files /dev/null and b/data/pics/fit_models/voigt2.png differ diff --git a/data/pics/fit_models/voigt3.png b/data/pics/fit_models/voigt3.png new file mode 100644 index 000000000..c998bf4b3 Binary files /dev/null and b/data/pics/fit_models/voigt3.png differ diff --git a/data/pics/fit_models/voigt4.png b/data/pics/fit_models/voigt4.png new file mode 100644 index 000000000..49981963b Binary files /dev/null and b/data/pics/fit_models/voigt4.png differ diff --git a/data/pics/sc-apps-labplot-matrix-new.svg b/data/pics/sc-apps-labplot-matrix-new.svg index ddec4c161..fc4b35a99 100644 --- a/data/pics/sc-apps-labplot-matrix-new.svg +++ b/data/pics/sc-apps-labplot-matrix-new.svg @@ -1,3906 +1,3906 @@ + style="fill-opacity:1" /> image/svg+xml Oxygen team diff --git a/data/pics/sc-apps-labplot-spreadsheet-new.svg b/data/pics/sc-apps-labplot-spreadsheet-new.svg index ff1614de2..ead44bb80 100644 --- a/data/pics/sc-apps-labplot-spreadsheet-new.svg +++ b/data/pics/sc-apps-labplot-spreadsheet-new.svg @@ -1,3889 +1,3889 @@ + style="fill-opacity:1" /> image/svg+xml Oxygen team diff --git a/data/pics/sc-apps-labplot-workbook-new.svg b/data/pics/sc-apps-labplot-workbook-new.svg index cd401b555..b28734dd9 100644 --- a/data/pics/sc-apps-labplot-workbook-new.svg +++ b/data/pics/sc-apps-labplot-workbook-new.svg @@ -1,3884 +1,3884 @@ + style="fill-opacity:1" /> image/svg+xml Oxygen team diff --git a/data/pics/sc-apps-labplot-worksheet-new.svg b/data/pics/sc-apps-labplot-worksheet-new.svg index 95d0acfe3..651c32a85 100644 --- a/data/pics/sc-apps-labplot-worksheet-new.svg +++ b/data/pics/sc-apps-labplot-worksheet-new.svg @@ -1,3797 +1,3797 @@ + style="fill-opacity:1" /> image/svg+xml Oxygen team diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 161339abf..554f5ed39 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,395 +1,396 @@ 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) 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/FITSOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp ${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/BarChartPlotDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CustomPointDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ColumnDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/LiveDataDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/MatrixDock.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/WorksheetDock.cpp ${KDEFRONTEND_DIR}/matrix/MatrixFunctionDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/PlotDataDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/EquidistantValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/ExportSpreadsheetDialog.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 ) 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/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/fitsoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotlegenddock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/barchartplotdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/columndock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/custompointdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/livedatadock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/notedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/matrixdock.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/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/dropvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/functionvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/randomvalueswidget.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/AbstractScriptingEngine.cpp ${BACKEND_DIR}/core/AbstractScript.cpp ${BACKEND_DIR}/core/ScriptingEngineManager.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/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/FITSFilter.cpp ${BACKEND_DIR}/datasources/filters/QJsonModel.cpp ${BACKEND_DIR}/datasources/projects/ProjectParser.cpp ${BACKEND_DIR}/datasources/projects/LabPlotProjectParser.cpp ${BACKEND_DIR}/datasources/projects/OriginProjectParser.cpp ${BACKEND_DIR}/gsl/ExpressionParser.cpp ${BACKEND_DIR}/matrix/Matrix.cpp ${BACKEND_DIR}/matrix/matrixcommands.cpp ${BACKEND_DIR}/matrix/MatrixModel.cpp ${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp ${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp ${BACKEND_DIR}/lib/XmlStreamReader.cpp ${BACKEND_DIR}/note/Note.cpp ${BACKEND_DIR}/worksheet/WorksheetElement.cpp ${BACKEND_DIR}/worksheet/TextLabel.cpp ${BACKEND_DIR}/worksheet/Worksheet.cpp ${BACKEND_DIR}/worksheet/WorksheetElementContainer.cpp ${BACKEND_DIR}/worksheet/WorksheetElementGroup.cpp ${BACKEND_DIR}/worksheet/plots/AbstractPlot.cpp ${BACKEND_DIR}/worksheet/plots/AbstractCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/PlotArea.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Axis.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlot.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlotLegend.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Histogram.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/BarChartPlot.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}/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 ) set(NSL_SOURCES ${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}/datapicker/DatapickerView.cpp ${COMMONFRONTEND_DIR}/datapicker/DatapickerImageView.cpp ) IF (${CANTOR_LIBS_FOUND}) set(CANTOR_SOURCES ${KDEFRONTEND_DIR}/dockwidgets/CantorWorksheetDock.cpp ${BACKEND_DIR}/cantorWorksheet/VariableParser.cpp ${BACKEND_DIR}/cantorWorksheet/CantorWorksheet.cpp ${COMMONFRONTEND_DIR}/cantorWorksheet/CantorWorksheetView.cpp ) set(CANTOR_UI_SOURCES ${KDEFRONTEND_DIR}/ui/dockwidgets/cantorworksheetdock.ui) set(UI_SOURCES ${UI_SOURCES} ${CANTOR_UI_SOURCES}) ELSE () set(CANTOR_SOURCES "") ENDIF () set(TOOLS_SOURCES ${TOOLS_DIR}/EquationHighlighter.cpp ${TOOLS_DIR}/ImageTools.cpp ${TOOLS_DIR}/TeXRenderer.cpp ) bison_target(GslParser ${BACKEND_DIR}/gsl/parser.y ${CMAKE_CURRENT_BINARY_DIR}/gsl_parser.c ) set(GENERATED_SOURCES ${BISON_GslParser_OUTPUTS} ) ############################################################################## INCLUDE_DIRECTORIES( . ${BACKEND_DIR}/gsl ${GSL_INCLUDE_DIR} ) set( LABPLOT_SRCS ${GUI_SOURCES} ) ki18n_wrap_ui( LABPLOT_SRCS ${UI_SOURCES} ) +# TODO: build without: +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) # 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) -# TODO: remove KF5::KDELibs4Support -target_link_libraries( labplot2lib KF5::KDELibs4Support KF5::Archive KF5::XmlGui Qt5::Svg Qt5::Core ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES} ${QT_QTSQL_LIBRARIES} ) +target_link_libraries( labplot2lib KF5::Archive KF5::Completion KF5::ConfigCore KF5::I18n KF5::IconThemes KF5::TextWidgets KF5::XmlGui Qt5::Svg Qt5::Core Qt5::Network Qt5::PrintSupport Qt5::Sql ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES} ) IF (Qt5SerialPort_FOUND) target_link_libraries( labplot2lib Qt5::SerialPort ) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries( labplot2lib KF5::SyntaxHighlighting ) ENDIF () #TODO: KF5::NewStuff IF (CANTOR_LIBS_FOUND) target_link_libraries( labplot2lib ${CANTOR_LIBS} ) ENDIF () IF (HDF5_FOUND) target_link_libraries( labplot2lib ${HDF5_LIBRARIES} ) ENDIF () IF (FFTW_FOUND) target_link_libraries( labplot2lib ${FFTW_LIBRARIES} ) ENDIF () IF (NETCDF_FOUND) target_link_libraries( labplot2lib ${NETCDF_LIBRARY} ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries( labplot2lib ${CFITSIO_LIBRARY} ) ENDIF () IF (LIBCERF_FOUND) target_link_libraries( labplot2lib ${LIBCERF_LIBRARY} ) ENDIF () IF (ENABLE_LIBORIGIN) target_link_libraries( labplot2lib liborigin-static ) ENDIF () # icons for the executable 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 ) # main executable set(LABPLOT_SOURCE ${KDEFRONTEND_DIR}/LabPlot.cpp) ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS}) 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 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/cantorWorksheet/CantorWorksheet.cpp b/src/backend/cantorWorksheet/CantorWorksheet.cpp index 2b2a80de1..79f57cad0 100644 --- a/src/backend/cantorWorksheet/CantorWorksheet.cpp +++ b/src/backend/cantorWorksheet/CantorWorksheet.cpp @@ -1,315 +1,315 @@ /*************************************************************************** File : CantorWorksheet.cpp Project : LabPlot Description : Aspect providing a Cantor Worksheets for Multiple backends -------------------------------------------------------------------- Copyright : (C) 2015 Garvit Khatri (garvitdelhi@gmail.com) Copyright : (C) 2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "CantorWorksheet.h" #include "VariableParser.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "backend/core/Project.h" #include "commonfrontend/cantorWorksheet/CantorWorksheetView.h" #include #include #include #include #include #include #include "cantor/cantor_part.h" #include #include #include CantorWorksheet::CantorWorksheet(AbstractScriptingEngine* engine, const QString &name, bool loading) : AbstractPart(name), scripted(engine), m_view(nullptr), m_backendName(name), m_session(nullptr), m_part(nullptr), m_variableModel(nullptr), m_worksheetAccess(nullptr) { if(!loading) init(); } /*! initializes Cantor's part and plugins */ bool CantorWorksheet::init(QByteArray* content) { KPluginFactory* factory = KPluginLoader(QLatin1String("libcantorpart")).factory(); if (factory) { m_part = factory->create(this, QVariantList() << m_backendName << QLatin1String("--noprogress")); if (!m_part) { qDebug() << "Could not create the Cantor Part."; return false; } m_worksheetAccess = m_part->findChild(Cantor::WorksheetAccessInterface::Name); //load worksheet content if available if(content) m_worksheetAccess->loadWorksheetFromByteArray(content); connect(m_worksheetAccess, SIGNAL(sessionChanged()), this, SLOT(sessionChanged())); //Cantor's session m_session = m_worksheetAccess->session(); connect(m_session, SIGNAL(statusChanged(Cantor::Session::Status)), this, SIGNAL(statusChanged(Cantor::Session::Status))); //variable model m_variableModel = m_session->variableModel(); connect(m_variableModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); connect(m_variableModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(m_variableModel, SIGNAL(modelReset()), this, SLOT(modelReset())); //available plugins Cantor::PanelPluginHandler* handler = m_part->findChild(QLatin1String("PanelPluginHandler")); if(!handler) { KMessageBox::error(view(), i18n("no PanelPluginHandle found for the Cantor Part.")); return false; } m_plugins = handler->plugins(); } else { //we can only get to this here if we open a project having Cantor content and Cantor plugins were not found. //return false here, a proper error message will be created in load() and propagated further. DEBUG("Failed to load cantor plugin"); return false; } return true; } //SLots void CantorWorksheet::rowsInserted(const QModelIndex& parent, int first, int last) { Q_UNUSED(parent) for(int i = first; i <= last; ++i) { const QString name = m_variableModel->data(m_variableModel->index(first, 0)).toString(); const QString value = m_variableModel->data(m_variableModel->index(first, 1)).toString(); VariableParser* parser = new VariableParser(m_backendName, value); if(parser->isParsed()) { Column* col = child(name); if (col) { col->replaceValues(0, parser->values()); } else { col = new Column(name, parser->values()); col->setUndoAware(false); addChild(col); //TODO: Cantor currently ignores the order of variables in the worksheets //and adds new variables at the last position in the model. //Fix this in Cantor and switch to insertChildBefore here later. //insertChildBefore(col, child(i)); } } else { //the already existing variable doesn't contain any numerical values -> remove it Column* col = child(name); if (col) removeChild(col); } delete(parser); } project()->setChanged(true); } void CantorWorksheet::sessionChanged() { //TODO: signal is never emitted in Cantor project()->setChanged(true); } void CantorWorksheet::modelReset() { for(int i = 0; i < childCount(); ++i) child(i)->remove(); } void CantorWorksheet::rowsAboutToBeRemoved(const QModelIndex & parent, int first, int last) { Q_UNUSED(parent); Q_UNUSED(first); Q_UNUSED(last); //TODO: Cantor removes rows from the model even when the variable was changed only. //We don't want this behaviour since this removes the columns from the datasource in the curve. //We need to fix/change this in Cantor. return; // Q_UNUSED(parent) // for(int i = first; i <= last; ++i) { // const QString name = m_variableModel->data(m_variableModel->index(first, 0)).toString(); // Column* column = child(name); // if(column) // column->remove(); // } } QList CantorWorksheet::getPlugins(){ return m_plugins; } KParts::ReadWritePart* CantorWorksheet::part() { return m_part; } QIcon CantorWorksheet::icon() const { if(m_session) return QIcon::fromTheme(m_session->backend()->icon()); return QIcon(); } QWidget* CantorWorksheet::view() const { if (!m_partView) { m_view = new CantorWorksheetView(const_cast(this)); m_view->setBaseSize(1500, 1500); m_partView = m_view; // connect(m_view, SIGNAL(statusInfo(QString)), this, SIGNAL(statusInfo(QString))); } return m_partView; } //! Return a new context menu. /** * The caller takes ownership of the menu. */ QMenu* CantorWorksheet::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } QString CantorWorksheet::backendName() { return this->m_backendName; } //TODO bool CantorWorksheet::exportView() const { return false; } bool CantorWorksheet::printView() { m_part->action("file_print")->trigger(); return true; } bool CantorWorksheet::printPreview() const { m_part->action("file_print_preview")->trigger(); return true; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void CantorWorksheet::save(QXmlStreamWriter* writer) const{ writer->writeStartElement("cantorWorksheet"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement( "general" ); writer->writeAttribute( "backend_name", m_backendName); //TODO: save worksheet settings writer->writeEndElement(); //save the content of Cantor's worksheet QByteArray content = m_worksheetAccess->saveWorksheetToByteArray(); writer->writeStartElement("worksheet"); writer->writeAttribute("content", content.toBase64()); writer->writeEndElement(); //save columns(variables) for (auto* col : children(IncludeHidden)) col->save(writer); writer->writeEndElement(); // close "cantorWorksheet" section } //! Load from XML bool CantorWorksheet::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "cantorWorksheet"){ reader->raiseError(i18n("no Cantor worksheet element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; bool rc = false; while (!reader->atEnd()){ reader->readNext(); if (reader->isEndElement() && reader->name() == "cantorWorksheet") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment"){ if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general"){ attribs = reader->attributes(); m_backendName = attribs.value("backend_name").toString().trimmed(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'backend_name'")); + reader->raiseWarning(attributeWarning.subs("backend_name").toString()); } else if (!preview && reader->name() == "worksheet"){ attribs = reader->attributes(); str = attribs.value("content").toString().trimmed(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'content'")); + reader->raiseWarning(attributeWarning.subs("content").toString()); QByteArray content = QByteArray::fromBase64(str.toAscii()); rc = init(&content); if (!rc) { QString msg = i18n("This project has Cantor content but no Cantor plugins were found. Please check your installation. The project will be closed."); reader->raiseError(msg); return false; } } else if(!preview && reader->name() == "column") { Column* column = new Column(""); column->setUndoAware(false); if (!column->load(reader, preview)) { delete column; return false; } addChild(column); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } diff --git a/src/backend/cantorWorksheet/VariableParser.cpp b/src/backend/cantorWorksheet/VariableParser.cpp index e4700312b..28fb393be 100644 --- a/src/backend/cantorWorksheet/VariableParser.cpp +++ b/src/backend/cantorWorksheet/VariableParser.cpp @@ -1,124 +1,123 @@ /*************************************************************************** File : VariableParser.h Project : LabPlot Description : Variable parser for different CAS backends -------------------------------------------------------------------- Copyright : (C) 2015 Garvit Khatri (garvitdelhi@gmail.com) Copyright : (C) 2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "VariableParser.h" #include #include "backend/lib/trace.h" #include VariableParser::VariableParser(const QString& name, const QString& value) : m_backendName(name), m_string(value), m_parsed(false) { PERFTRACE("parsing variable"); if(m_backendName.compare(QStringLiteral("Maxima"), Qt::CaseInsensitive) == 0) parseMaximaValues(); else if(m_backendName.compare(QStringLiteral("Python 3"), Qt::CaseInsensitive) == 0) parsePythonValues(); else if(m_backendName.compare(QStringLiteral("Python 2"), Qt::CaseInsensitive) == 0) parsePythonValues(); else if(m_backendName.compare(QStringLiteral("Sage"), Qt::CaseInsensitive) == 0) parsePythonValues(); else if(m_backendName.compare(QStringLiteral("R"), Qt::CaseInsensitive) == 0) parseRValues(); else if(m_backendName.compare(QStringLiteral("Julia"), Qt::CaseInsensitive) == 0) parsePythonValues(); } void VariableParser::parseMaximaValues() { if(m_string.count(QStringLiteral("[")) < 2) { m_string = m_string.replace(QStringLiteral("["), QStringLiteral("")); m_string = m_string.replace(QStringLiteral("]"), QStringLiteral("")); m_string = m_string.trimmed(); const QStringList valueStringList = m_string.split(QStringLiteral(",")); parseValues(valueStringList); } } void VariableParser::parsePythonValues() { QStringList valueStringList; m_string = m_string.trimmed(); if (m_string.startsWith(QStringLiteral("array"))) { //parse numpy arrays, string representation like array([1,2,3,4,5]) m_string = m_string.replace(QStringLiteral("array(["), QStringLiteral("")); m_string = m_string.replace(QStringLiteral("])"), QStringLiteral("")); } else if (m_string.startsWith(QStringLiteral("["))) { //parse python's lists m_string = m_string.replace(QStringLiteral("["), QStringLiteral("")); m_string = m_string.replace(QStringLiteral("]"), QStringLiteral("")); } else if(m_string.startsWith(QStringLiteral("("))) { //parse python's tuples m_string = m_string.replace(QStringLiteral("("), QStringLiteral("")); m_string = m_string.replace(QStringLiteral(")"), QStringLiteral("")); } else { return; } if(m_string.count(QStringLiteral(","))>1) valueStringList = m_string.split(QStringLiteral(",")); else valueStringList = m_string.split(QStringLiteral(" ")); parseValues(valueStringList); } void VariableParser::parseRValues() { - m_string = "[1] 1 2 3 4 5 6"; m_string = m_string.remove( QRegExp("\\[.*\\]")); m_string = m_string.trimmed(); const QStringList valueStringList = m_string.split(QStringLiteral(" ")); parseValues(valueStringList); } bool VariableParser::isParsed() { return m_parsed; } QVector< double > VariableParser::values() { return m_values; } void VariableParser::parseValues(const QStringList& values) { PERFTRACE("parsing variable values string list"); for(const QString& v : values) { bool isNumber = false; double value = v.trimmed().toDouble(&isNumber); //accept the variable only if there is at least one numerical value in the array. if(isNumber) { if (!m_parsed) m_parsed = true; } else { value = NAN; } m_values << value; } } diff --git a/src/backend/core/AbstractAspect.cpp b/src/backend/core/AbstractAspect.cpp index 34704525f..db37ba3d7 100644 --- a/src/backend/core/AbstractAspect.cpp +++ b/src/backend/core/AbstractAspect.cpp @@ -1,837 +1,837 @@ /*************************************************************************** File : AbstractAspect.cpp 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-2016 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 * * * ***************************************************************************/ #include "backend/core/AbstractAspect.h" #include "backend/core/AspectPrivate.h" #include "backend/core/aspectcommands.h" #include "backend/core/Project.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/datapicker/DatapickerCurve.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/SignallingUndoCommand.h" #include "backend/lib/PropertyChangeCommand.h" #include /** * \class AbstractAspect * \brief Base class of all persistent objects in a Project. * * Before going into the details, it's useful to understand the ideas behind the * \ref aspect "Aspect Framework". * * Aspects organize themselves into trees, where a parent takes ownership of its children. Usually, * though not necessarily, a Project instance will sit at the root of the tree (without a Project * ancestor, project() will return 0 and undo does not work). Children are organized using * addChild(), removeChild(), child(), indexOfChild() and childCount() on the parent's side as well * as the equivalent convenience methods index() and remove() on the child's side. * In contrast to the similar feature of QObject, Aspect trees are fully undo/redo aware and provide * signals around object adding/removal. * * AbstractAspect manages for every Aspect the properties #name, #comment, #captionSpec and * #creationTime. All of these translate into the caption() as described in the documentation * of setCaptionSpec(). * * If an undoStack() can be found (usually it is managed by Project), changes to the properties * as well as adding/removing children support multi-level undo/redo. In order to support undo/redo * for problem-specific data in derived classes, make sure that all changes to your data are done * by handing appropriate commands to exec(). */ /** * \enum AbstractAspect::ChildIndexFlag * \brief Flags which control numbering scheme of children. */ /** * \var AbstractAspect::IncludeHidden * \brief Include aspects marked as "hidden" in numbering or listing children. */ /** * \var AbstractAspect::Recursive * \brief Recursively handle all descendents, not just immediate children. */ /** * \var AbstractAspect::Compress * \brief Remove all null pointers from the result list. */ //////////////////////////////////////////////////////////////////////////////////////////////////// // documentation of template and inline methods //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \fn template < class T > T *AbstractAspect::ancestor() const * \brief Return the closest ancestor of class T (or NULL if none found). */ /** * \fn template < class T > QVector AbstractAspect::children(const ChildIndexFlags &flags=0) const * \brief Return list of children inheriting from class T. * * Use AbstractAspect for T in order to get all children. */ /** * \fn template < class T > T *AbstractAspect::child(int index, const ChildIndexFlags &flags=0) const * \brief Return child identified by (0 based) index and class. * * Identifying objects by an index is inherently error-prone and confusing, * given that the index can be based on different criteria (viz, counting * only instances of specific classes and including/excluding hidden * aspects). Therefore, it is recommended to avoid indices wherever possible * and instead refer to aspects using AbstractAspect pointers. */ /** * \fn template < class T > T *AbstractAspect::child(const QString &name) const * \brief Get child by name and class. */ /** * \fn template < class T > int AbstractAspect::childCount(const ChildIndexFlags &flags=0) const * \brief Return the number of child Aspects inheriting from given class. */ /** * \fn template < class T > int AbstractAspect::indexOfChild(const AbstractAspect * child, const ChildIndexFlags &flags=0) const * \brief Return (0 based) index of child in the list of children inheriting from class T. */ /** * \fn void AbstractAspect::aspectDescriptionAboutToChange(const AbstractAspect *aspect) * \brief Emitted before the name, comment or caption spec is changed */ /** * \fn void AbstractAspect::aspectDescriptionChanged(const AbstractAspect *aspect) * \brief Emitted after the name, comment or caption spec have changed */ /** * \fn void AbstractAspect::aspectAboutToBeAdded(const AbstractAspect *parent, const AbstractAspect *before, const AbstractAspect * child) * \brief Emitted before a new child is inserted */ /** * \fn void AbstractAspect::aspectAdded(const AbstractAspect *aspect) * \brief Emitted after a new Aspect has been added to the tree */ /** * \fn void AbstractAspect::aspectAboutToBeRemoved(const AbstractAspect *aspect) * \brief Emitted before an aspect is removed from its parent */ /** * \fn void AbstractAspect::aspectRemoved(const AbstractAspect *parent, const AbstractAspect * before, const AbstractAspect * child) * \brief Emitted from the parent after removing a child */ /** * \fn void AbstractAspect::aspectHiddenAboutToChange(const AbstractAspect *aspect) * \brief Emitted before the hidden attribute is changed */ /** * \fn void AbstractAspect::aspectHiddenChanged(const AbstractAspect *aspect) * \brief Emitted after the hidden attribute has changed */ /** * \fn void AbstractAspect::statusInfo(const QString &text) * \brief Emitted whenever some aspect in the tree wants to give status information to the user * \sa info(const QString&) */ /** * \fn protected void AbstractAspect::info(const QString &text) * \brief Implementations should call this whenever status information should be given to the user. * * This will cause statusInfo() to be emitted. Typically, this will cause the specified string * to be displayed in a status bar, a log window or some similar non-blocking way so as not to * disturb the workflow. */ /** * \fn protected virtual void childSelected(const AbstractAspect*){} * \brief called when a child's child aspect was selected in the model */ /** * \fn protected virtual void childDeselected() * \brief called when a child aspect was deselected in the model */ /** * \fn protected virtual void childDeselected(const AbstractAspect*) * \brief called when a child's child aspect was deselected in the model */ //////////////////////////////////////////////////////////////////////////////////////////////////// // start of AbstractAspect implementation //////////////////////////////////////////////////////////////////////////////////////////////////// AbstractAspect::AbstractAspect(const QString &name) : d(new AbstractAspectPrivate(this, name)) { } AbstractAspect::~AbstractAspect() { delete d; } QString AbstractAspect::name() const { return d->m_name; } void AbstractAspect::setName(const QString &value) { if (value.isEmpty()) { setName(QLatin1String("1")); return; } if (value == d->m_name) return; QString new_name; if (d->m_parent) { new_name = d->m_parent->uniqueNameFor(value); if (new_name != value) info(i18n("Intended name \"%1\" was changed to \"%2\" in order to avoid name collision.", value, new_name)); } else { new_name = value; } exec(new PropertyChangeCommand(i18n("%1: rename to %2", d->m_name, new_name), &d->m_name, new_name), "aspectDescriptionAboutToChange", "aspectDescriptionChanged", Q_ARG(const AbstractAspect*,this)); } QString AbstractAspect::comment() const { return d->m_comment; } void AbstractAspect::setComment(const QString& value) { if (value == d->m_comment) return; exec(new PropertyChangeCommand(i18n("%1: change comment", d->m_name), &d->m_comment, value), "aspectDescriptionAboutToChange", "aspectDescriptionChanged", Q_ARG(const AbstractAspect*,this)); } void AbstractAspect::setCreationTime(const QDateTime& time) { d->m_creation_time = time; } QDateTime AbstractAspect::creationTime() const { return d->m_creation_time; } bool AbstractAspect::hidden() const { return d->m_hidden; } /** * \brief Set "hidden" property, i.e. whether to exclude this aspect from being shown in the explorer. */ void AbstractAspect::setHidden(bool value) { if (value == d->m_hidden) return; exec(new PropertyChangeCommand(i18n("%1: change hidden status", d->m_name), &d->m_hidden, value), "aspectHiddenAboutToChange", "aspectHiddenChanged", Q_ARG(const AbstractAspect*,this)); } void AbstractAspect::setIsLoading(bool load) { d->m_isLoading = load; } bool AbstractAspect::isLoading() const { return d->m_isLoading; } /** * \brief Return an icon to be used for decorating my views. */ QIcon AbstractAspect::icon() const { return QIcon(); } /** * \brief Return a new context menu. * * The caller takes ownership of the menu. */ QMenu* AbstractAspect::createContextMenu() { QMenu* menu = new QMenu(); menu->addSection(this->name()); //TODO: activate this again when the functionality is implemented // menu->addAction( KStandardAction::cut(this) ); // menu->addAction(KStandardAction::copy(this)); // menu->addAction(KStandardAction::paste(this)); // menu->addSeparator(); menu->addAction(QIcon::fromTheme(QLatin1String("edit-rename")), i18n("Rename"), this, SIGNAL(renameRequested())); //don't allow to delete data spreadsheets in the datapicker curves if ( !(dynamic_cast(this) && dynamic_cast(this->parentAspect())) ) menu->addAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete"), this, SLOT(remove())); return menu; } /** * \brief Return my parent Aspect or 0 if I currently don't have one. */ AbstractAspect* AbstractAspect::parentAspect() const { return d->m_parent; } void AbstractAspect::setParentAspect(AbstractAspect* parent) { d->m_parent = parent; } /** * \brief Return the folder the Aspect is contained in or 0 if there is none. * * The returned folder may be the aspect itself if it inherits Folder. */ Folder* AbstractAspect::folder() { if(inherits("Folder")) return static_cast(this); AbstractAspect* parent_aspect = parentAspect(); while(parent_aspect && !parent_aspect->inherits("Folder")) parent_aspect = parent_aspect->parentAspect(); return static_cast(parent_aspect); } /** * \brief Return whether the there is a path upwards to the given aspect * * This also returns true if other==this. */ bool AbstractAspect::isDescendantOf(AbstractAspect* other) { if(other == this) return true; AbstractAspect* parent_aspect = parentAspect(); while(parent_aspect) { if(parent_aspect == other) return true; parent_aspect = parent_aspect->parentAspect(); } return false; } /** * \brief Return the Project this Aspect belongs to, or 0 if it is currently not part of one. */ Project* AbstractAspect::project() { return parentAspect() ? parentAspect()->project() : 0; } /** * \brief Return the path that leads from the top-most Aspect (usually a Project) to me. */ QString AbstractAspect::path() const { return parentAspect() ? parentAspect()->path() + QLatin1Char('/') + name() : QLatin1String(""); } /** * \brief Add the given Aspect to my list of children. */ void AbstractAspect::addChild(AbstractAspect* child) { Q_CHECK_PTR(child); QString new_name = uniqueNameFor(child->name()); beginMacro(i18n("%1: add %2", name(), new_name)); if (new_name != child->name()) { info(i18n("Renaming \"%1\" to \"%2\" in order to avoid name collision.", child->name(), new_name)); child->setName(new_name); } exec(new AspectChildAddCmd(d, child, d->m_children.count())); child->finalizeAdd(); endMacro(); } /** * \brief Add the given Aspect to my list of children without any checks and without putting this step onto the undo-stack */ void AbstractAspect::addChildFast(AbstractAspect* child) { emit aspectAboutToBeAdded(this, 0, child); //TODO: before-pointer is 0 here, also in the commands classes. why? d->insertChild(d->m_children.count(), child); child->finalizeAdd(); emit aspectAdded(child); } /** * \brief Insert the given Aspect at a specific position in my list of children. */ void AbstractAspect::insertChildBefore(AbstractAspect* child, AbstractAspect* before) { Q_CHECK_PTR(child); QString new_name = uniqueNameFor(child->name()); - beginMacro(i18n("%1: insert %2 before %3", name(), new_name, before ? before->name() : i18n("end"))); + beginMacro(before ? i18n("%1: insert %2 before %3", name(), new_name, before->name()) : i18n("%1: insert %2 before end", name(), new_name)); if (new_name != child->name()) { info(i18n("Renaming \"%1\" to \"%2\" in order to avoid name collision.", child->name(), new_name)); child->setName(new_name); } int index = d->indexOfChild(before); if (index == -1) index = d->m_children.count(); exec(new AspectChildAddCmd(d, child, index)); endMacro(); } /** * \brief Insert the given Aspect at a specific position in my list of children.without any checks and without putting this step onto the undo-stack */ void AbstractAspect::insertChildBeforeFast(AbstractAspect* child, AbstractAspect* before) { connect(child, &AbstractAspect::selected, this, &AbstractAspect::childSelected); connect(child, &AbstractAspect::deselected, this, &AbstractAspect::childDeselected); int index = d->indexOfChild(before); if (index == -1) index = d->m_children.count(); emit aspectAboutToBeAdded(this, 0, child); d->insertChild(index, child); emit aspectAdded(child); } /** * \brief Remove the given Aspect from my list of children. * * The ownership of the child is transferred to the undo command, * i.e., the aspect is deleted by the undo command. * \sa reparent() */ void AbstractAspect::removeChild(AbstractAspect* child) { Q_ASSERT(child->parentAspect() == this); beginMacro(i18n("%1: remove %2", name(), child->name())); exec(new AspectChildRemoveCmd(d, child)); endMacro(); } /** * \brief Remove all child Aspects. */ void AbstractAspect::removeAllChildren() { beginMacro(i18n("%1: remove all children", name())); QVector children_list = children(); QVector::const_iterator i = children_list.constBegin(); AbstractAspect *current = 0, *nextSibling = 0; if (i != children_list.constEnd()) { current = *i; if (++i != children_list.constEnd()) nextSibling = *i; } while (current) { emit aspectAboutToBeRemoved(current); exec(new AspectChildRemoveCmd(d, current)); emit aspectRemoved(this, nextSibling, current); current = nextSibling; if (i != children_list.constEnd() && ++i != children_list.constEnd()) nextSibling = *i; else nextSibling = 0; } endMacro(); } /** * \brief Move a child to another parent aspect and transfer ownership. */ void AbstractAspect::reparent(AbstractAspect* newParent, int newIndex) { Q_ASSERT(parentAspect() != NULL); Q_ASSERT(newParent != NULL); int max_index = newParent->childCount(IncludeHidden); if (newIndex == -1) newIndex = max_index; Q_ASSERT(newIndex >= 0 && newIndex <= max_index); AbstractAspect* old_parent = parentAspect(); int old_index = old_parent->indexOfChild(this, IncludeHidden); AbstractAspect* old_sibling = old_parent->child(old_index+1, IncludeHidden); AbstractAspect* new_sibling = newParent->child(newIndex, IncludeHidden); //TODO check/test this! emit aspectAboutToBeRemoved(this); emit newParent->aspectAboutToBeAdded(newParent, new_sibling, this); exec(new AspectChildReparentCmd(parentAspect()->d, newParent->d, this, newIndex)); emit old_parent->aspectRemoved(old_parent, old_sibling, this); emit aspectAdded(this); endMacro(); } QVector AbstractAspect::children(const char* className, ChildIndexFlags flags) { QVector result; for (auto* child : children()) { if (flags & IncludeHidden || !child->hidden()) { if ( child->inherits(className) || !(flags & Compress)) { result << child; if (flags & Recursive){ result << child->children(className, flags); } } } } return result; } const QVector AbstractAspect::children() const { return d->m_children; } /** * \brief Remove me from my parent's list of children. */ void AbstractAspect::remove() { if(parentAspect()) parentAspect()->removeChild(this); } /*! * returns the list of all parent aspects (folders and sub-folders) */ QVector AbstractAspect::dependsOn() const { QVector aspects; if (parentAspect()) aspects << parentAspect() << parentAspect()->dependsOn(); return aspects; } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name serialize/deserialize //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \fn virtual void AbstractAspect::save(QXmlStreamWriter *) const * \brief Save as XML */ /** * \fn virtual bool AbstractAspect::load(XmlStreamReader *) * \brief Load from XML * * XmlStreamReader supports errors as well as warnings. If only * warnings (non-critial errors) occur, this function must return * the reader at the end element corresponding to the current * element at the time the function was called. * * This function is normally intended to be called directly * after the ctor. If you want to call load on an aspect that * has been altered, you must make sure beforehand that * it is in the same state as after creation, e.g., remove * all its child aspects. * * \return false on error */ /** * \brief Save the comment to XML */ void AbstractAspect::writeCommentElement(QXmlStreamWriter * writer) const{ writer->writeStartElement(QLatin1String("comment")); writer->writeCharacters(comment()); writer->writeEndElement(); } /** * \brief Load comment from an XML element */ bool AbstractAspect::readCommentElement(XmlStreamReader * reader){ setComment(reader->readElementText()); return true; } /** * \brief Save name and creation time to XML */ void AbstractAspect::writeBasicAttributes(QXmlStreamWriter* writer) const { writer->writeAttribute(QLatin1String("creation_time") , creationTime().toString(QLatin1String("yyyy-dd-MM hh:mm:ss:zzz"))); writer->writeAttribute(QLatin1String("name"), name()); } /** * \brief Load name and creation time from XML * * \return false on error */ bool AbstractAspect::readBasicAttributes(XmlStreamReader* reader){ const QXmlStreamAttributes& attribs = reader->attributes(); // name QString str = attribs.value(QLatin1String("name")).toString(); if(str.isEmpty()) reader->raiseWarning(i18n("Attribute 'name' is missing or empty.")); d->m_name = str; // creation time str = attribs.value(QLatin1String("creation_time")).toString(); if(str.isEmpty()) { reader->raiseWarning(i18n("Invalid creation time for '%1'. Using current time.", name())); d->m_creation_time = QDateTime::currentDateTime(); } else { QDateTime creation_time = QDateTime::fromString(str, QLatin1String("yyyy-dd-MM hh:mm:ss:zzz")); if (creation_time.isValid()) d->m_creation_time = creation_time; else d->m_creation_time = QDateTime::currentDateTime(); } return true; } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name undo related //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// void AbstractAspect::setUndoAware(bool b) { d->m_undoAware = b; } /** * \brief Return the undo stack of the Project, or 0 if this Aspect is not part of a Project. * * It's also possible to construct undo-enabled Aspect trees without Project. * The only requirement is that the root Aspect reimplements undoStack() to get the * undo stack from somewhere (the default implementation just delegates to parentAspect()). */ QUndoStack* AbstractAspect::undoStack() const { return parentAspect() ? parentAspect()->undoStack() : 0; } /** * \brief Execute the given command, pushing it on the undoStack() if available. */ void AbstractAspect::exec(QUndoCommand* cmd) { Q_CHECK_PTR(cmd); if (d->m_undoAware) { QUndoStack *stack = undoStack(); if (stack) stack->push(cmd); else { cmd->redo(); delete cmd; } if (project()) project()->setChanged(true); } else { cmd->redo(); delete cmd; } } /** * \brief Execute command and arrange for signals to be sent before/after it is redone or undone. * * \arg \c command The command to be executed. * \arg \c preChangeSignal The name of the signal to be triggered before re-/undoing the command. * \arg \c postChangeSignal The name of the signal to be triggered after re-/undoing the command. * \arg val0,val1,val2,val3 Arguments to the signals; to be given using Q_ARG(). * * Signal arguments are given using the macro Q_ARG(typename, const value&). Since * the variable given as "value" will likely be out of scope when the signals are emitted, a copy * needs to be created. This uses QMetaType, which means that (non-trivial) argument types need to * be registered using qRegisterMetaType() before giving them to exec() (in particular, this also * goes for pointers to custom data types). * * \sa SignallingUndoCommand */ void AbstractAspect::exec(QUndoCommand* command, const char* preChangeSignal, const char* postChangeSignal, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3) { beginMacro(command->text()); exec(new SignallingUndoCommand(QLatin1String("change signal"), this, preChangeSignal, postChangeSignal, val0, val1, val2, val3)); exec(command); exec(new SignallingUndoCommand(QLatin1String("change signal"), this, postChangeSignal, preChangeSignal, val0, val1, val2, val3)); endMacro(); } /** * \brief Begin an undo stack macro (series of commands) */ void AbstractAspect::beginMacro(const QString& text) { if (!d->m_undoAware) return; QUndoStack* stack = undoStack(); if (stack) stack->beginMacro(text); } /** * \brief End the current undo stack macro */ void AbstractAspect::endMacro() { if (!d->m_undoAware) return; QUndoStack* stack = undoStack(); if (stack) stack->endMacro(); } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /*! * this function is called when the selection in ProjectExplorer was changed. * forwards the selection/deselection to the parent aspect via emitting a signal. */ void AbstractAspect::setSelected(bool s){ if (s) emit selected(this); else emit deselected(this); } void AbstractAspect::childSelected(const AbstractAspect* aspect) { //forward the signal to the highest possible level in the parent-child hierarchy //e.g. axis of a plot was selected. Don't include parent aspects here that do not //need to react on the selection of children: e.g. Folder or XYFitCurve with //the child column for calculated residuals if (aspect->parentAspect() != 0 && !aspect->parentAspect()->inherits("Folder") && !aspect->parentAspect()->inherits("XYFitCurve") && !aspect->parentAspect()->inherits("CantorWorksheet")) emit aspect->parentAspect()->selected(aspect); } void AbstractAspect::childDeselected(const AbstractAspect* aspect) { //forward the signal to the highest possible level in the parent-child hierarchy //e.g. axis of a plot was selected. Don't include parent aspects here that do not //need to react on the deselection of children: e.g. Folder or XYFitCurve with //the child column for calculated residuals if (aspect->parentAspect() != 0 && !aspect->parentAspect()->inherits("Folder") && !aspect->parentAspect()->inherits("XYFitCurve") && !aspect->parentAspect()->inherits("CantorWorksheet")) emit aspect->parentAspect()->deselected(aspect); } /** * \brief Make the specified name unique among my children by incrementing a trailing number. */ QString AbstractAspect::uniqueNameFor(const QString& current_name) const { QStringList child_names; for (auto* child : children()) child_names << child->name(); if (!child_names.contains(current_name)) return current_name; QString base = current_name; int last_non_digit; for (last_non_digit = base.size()-1; last_non_digit>=0 && base[last_non_digit].category() == QChar::Number_DecimalDigit; --last_non_digit) base.chop(1); if (last_non_digit >=0 && base[last_non_digit].category() != QChar::Separator_Space) base.append(" "); int new_nr = current_name.rightRef(current_name.size() - base.size()).toInt(); QString new_name; do new_name = base + QString::number(++new_nr); while (child_names.contains(new_name)); return new_name; } void AbstractAspect::connectChild(AbstractAspect* child) { connect(child, &AbstractAspect::aspectDescriptionAboutToChange, this, &AbstractAspect::aspectDescriptionAboutToChange); connect(child, &AbstractAspect::aspectDescriptionChanged, this, &AbstractAspect::aspectDescriptionChanged); connect(child, &AbstractAspect::aspectAboutToBeAdded, this, &AbstractAspect::aspectAboutToBeAdded); connect(child, &AbstractAspect::aspectAdded, this, &AbstractAspect::aspectAdded); connect(child, &AbstractAspect::aspectAboutToBeRemoved, this, &AbstractAspect::aspectAboutToBeRemoved); connect(child, &AbstractAspect::aspectRemoved, this, &AbstractAspect::aspectRemoved); connect(child, &AbstractAspect::aspectHiddenAboutToChange, this, &AbstractAspect::aspectHiddenAboutToChange); connect(child, &AbstractAspect::aspectHiddenChanged, this, &AbstractAspect::aspectHiddenChanged); connect(child, &AbstractAspect::statusInfo, this, &AbstractAspect::statusInfo); connect(child, &AbstractAspect::selected, this, &AbstractAspect::childSelected); connect(child, &AbstractAspect::deselected, this, &AbstractAspect::childDeselected); } //############################################################################## //###################### Private implementation ############################### //############################################################################## AbstractAspectPrivate::AbstractAspectPrivate(AbstractAspect* owner, const QString& name) : m_name(name.isEmpty() ? QLatin1String("1") : name), m_hidden(false), q(owner), m_parent(0), m_undoAware(true), m_isLoading(false) { m_creation_time = QDateTime::currentDateTime(); } AbstractAspectPrivate::~AbstractAspectPrivate() { for(auto* child : m_children) delete child; } void AbstractAspectPrivate::insertChild(int index, AbstractAspect* child) { m_children.insert(index, child); // Always remove from any previous parent before adding to a new one! // Can't handle this case here since two undo commands have to be created. Q_ASSERT(child->parentAspect() == 0); child->setParentAspect(q); q->connectChild(child); } int AbstractAspectPrivate::indexOfChild(const AbstractAspect* child) const { for(int i=0; isetParentAspect(0); return index; } diff --git a/src/backend/core/AbstractColumn.cpp b/src/backend/core/AbstractColumn.cpp index 25fa9ba00..c328889bd 100644 --- a/src/backend/core/AbstractColumn.cpp +++ b/src/backend/core/AbstractColumn.cpp @@ -1,710 +1,710 @@ /*************************************************************************** File : AbstractColumn.cpp Project : LabPlot Description : Interface definition for data with column logic -------------------------------------------------------------------- Copyright : (C) 2007,2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/core/AbstractColumn.h" #include "backend/core/AbstractColumnPrivate.h" #include "backend/core/abstractcolumncommands.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/SignallingUndoCommand.h" #include #include -#include +#include #include /** * \class AbstractColumn * \brief Interface definition for data with column logic * * This is an abstract base class for column-based data, * i.e. mathematically a vector or technically a 1D array or list. * It only defines the interface but has no data members itself. * * Classes derived from this are typically table columns or outputs * of filters which can be chained between table columns and plots. * From the point of view of the plot functions there will be no difference * between a table column and a filter output since both use this interface. * * Classes derived from this will either store a * vector with entries of one certain data type, e.g. double, QString, * QDateTime, or generate such values on demand. To determine the data * type of a class derived from this, use the columnMode() function. * AbstractColumn defines all access functions for all supported data * types but only those corresponding to the return value of columnMode() * will return a meaningful value. Calling functions not belonging to * the data type of the column is safe, but will do nothing (writing * function) or return some default value (reading functions). * * This class also defines all signals which indicate a data change. * Any class whose output values are subject to change over time must emit * the according signals. These signals notify any object working with the * column before and after a change of the column. * In some cases it will be necessary for a class using * the column to connect aboutToBeDestroyed(), to react * to a column's deletion, e.g. a filter's reaction to a * table deletion. * * All writing functions have a "do nothing" standard implementation to * make deriving a read-only class very easy without bothering about the * writing interface. */ /** * \brief Ctor * * \param name the column name (= aspect name) */ AbstractColumn::AbstractColumn(const QString &name) : AbstractAspect(name), d( new AbstractColumnPrivate(this) ) { } AbstractColumn::~AbstractColumn() { emit aboutToBeDestroyed(this); delete d; } QStringList AbstractColumn::dateFormats() { QStringList dates; dates << "yyyy-MM-dd"; dates << "yyyy/MM/dd"; dates << "dd/MM/yyyy"; dates << "dd/MM/yy"; dates << "dd.MM.yyyy"; dates << "dd.MM.yy"; dates << "MM/yyyy"; dates << "dd.MM."; dates << "yyyyMMdd"; return dates; } QStringList AbstractColumn::timeFormats() { QStringList times; times << "hh"; times << "hh ap"; times << "hh:mm"; times << "hh:mm ap"; times << "hh:mm:ss"; times << "hh:mm:ss.zzz"; times << "hh:mm:ss:zzz"; times << "mm:ss.zzz"; times << "hhmmss"; return times; } QStringList AbstractColumn::dateTimeFormats() { QStringList dateTimes; // any combination of date and times for (const auto& d : dateFormats()) dateTimes << d; for (const auto& t : timeFormats()) dateTimes << t; for (const auto& d : dateFormats()) for (const auto& t : timeFormats()) dateTimes << d + ' ' + t; return dateTimes; } /** * \brief Convenience method for mode-dependent icon */ QIcon AbstractColumn::iconForMode(ColumnMode mode) { switch (mode) { case AbstractColumn::Numeric: case AbstractColumn::Integer: break; case AbstractColumn::Text: return QIcon::fromTheme("draw-text"); case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: return QIcon::fromTheme("chronometer"); } return QIcon::fromTheme("x-shape-text"); } /** * \fn bool AbstractColumn::isReadOnly() const * \brief Return whether the object is read-only */ /** * \fn AbstractColumn::ColumnMode AbstractColumn::columnMode() const * \brief Return the column mode * * This function is most used by tables but can also be used * by plots. The column mode specifies how to interpret * the values in the column additional to the data type. */ /** * \brief Set the column mode * * This sets the column mode and, if * necessary, converts it to another datatype. */ void AbstractColumn::setColumnMode(AbstractColumn::ColumnMode) {} /** * \brief Copy another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * Use a filter to convert a column to another type. */ bool AbstractColumn::copy(const AbstractColumn *other) { Q_UNUSED(other) return false; } /** * \brief Copies part of another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * \param source pointer to the column to copy * \param source_start first row to copy in the column to copy * \param destination_start first row to copy in * \param num_rows the number of rows to copy */ bool AbstractColumn::copy(const AbstractColumn *source, int source_start, int destination_start, int num_rows) { Q_UNUSED(source) Q_UNUSED(source_start) Q_UNUSED(destination_start) Q_UNUSED(num_rows) return false; } /** * \fn int AbstractColumn::rowCount() const * \brief Return the data vector size */ /** * \brief Insert some empty (or initialized with invalid values) rows */ void AbstractColumn::insertRows(int before, int count) { beginMacro( i18np("%1: insert 1 row", "%1: insert %2 rows", name(), count) ); exec(new SignallingUndoCommand("pre-signal", this, "rowsAboutToBeInserted", "rowsRemoved", Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count))); handleRowInsertion(before, count); exec(new SignallingUndoCommand("post-signal", this, "rowsInserted", "rowsAboutToBeRemoved", Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count))); endMacro(); } void AbstractColumn::handleRowInsertion(int before, int count) { exec(new AbstractColumnInsertRowsCmd(this, before, count)); } /** * \brief Remove 'count' rows starting from row 'first' */ void AbstractColumn::removeRows(int first, int count) { beginMacro( i18np("%1: remove 1 row", "%1: remove %2 rows", name(), count) ); exec(new SignallingUndoCommand("change signal", this, "rowsAboutToBeRemoved", "rowsInserted", Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count))); handleRowRemoval(first, count); exec(new SignallingUndoCommand("change signal", this, "rowsRemoved", "rowsAboutToBeInserted", Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count))); endMacro(); } void AbstractColumn::handleRowRemoval(int first, int count) { exec(new AbstractColumnRemoveRowsCmd(this, first, count)); } /** * \fn AbstractColumn::PlotDesignation AbstractColumn::plotDesignation() const * \brief Return the column plot designation */ /** * \brief Set the column plot designation */ void AbstractColumn::setPlotDesignation(AbstractColumn::PlotDesignation pd) { Q_UNUSED(pd) } /** * \brief Clear the whole column */ void AbstractColumn::clear() {} /** * \brief Convenience method for mode-independent testing of validity */ bool AbstractColumn::isValid(int row) const { switch (columnMode()) { case AbstractColumn::Numeric: return !std::isnan(valueAt(row)); case AbstractColumn::Integer: // there is no invalid integer return true; case AbstractColumn::Text: return !textAt(row).isNull(); case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: return dateTimeAt(row).isValid(); } return false; } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name IntervalAttribute related functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return whether a certain row is masked */ bool AbstractColumn::isMasked(int row) const { return d->m_masking.isSet(row); } /** * \brief Return whether a certain interval of rows is fully masked */ bool AbstractColumn::isMasked(const Interval& i) const { return d->m_masking.isSet(i); } /** * \brief Return all intervals of masked rows */ QVector< Interval > AbstractColumn::maskedIntervals() const { return d->m_masking.intervals(); } /** * \brief Clear all masking information */ void AbstractColumn::clearMasks() { exec(new AbstractColumnClearMasksCmd(d), "maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this)); } /** * \brief Set an interval masked * * \param i the interval * \param mask true: mask, false: unmask */ void AbstractColumn::setMasked(const Interval& i, bool mask) { exec(new AbstractColumnSetMaskedCmd(d, i, mask), "maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this)); } /** * \brief Overloaded function for convenience */ void AbstractColumn::setMasked(int row, bool mask) { setMasked(Interval(row,row), mask); } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name Formula related functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return the formula associated with row 'row' */ QString AbstractColumn::formula(int row) const { Q_UNUSED(row); return QString(); } /** * \brief Return the intervals that have associated formulas * * This can be used to make a list of formulas with their intervals. * Here is some example code: * * \code * QStringList list; * QVector< Interval > intervals = my_column.formulaIntervals(); * foreach(Interval interval, intervals) * list << QString(interval.toString() + ": " + my_column.formula(interval.start())); * \endcode */ QVector< Interval > AbstractColumn::formulaIntervals() const { return QVector< Interval >(); } /** * \brief Set a formula string for an interval of rows */ void AbstractColumn::setFormula(const Interval& i, const QString& formula) { Q_UNUSED(i) Q_UNUSED(formula) } /** * \brief Overloaded function for convenience */ void AbstractColumn::setFormula(int row, const QString& formula) { Q_UNUSED(row) Q_UNUSED(formula) } /** * \brief Clear all formulas */ void AbstractColumn::clearFormulas() {}; //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name type specific functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return the content of row 'row'. * * Use this only when columnMode() is Text */ QString AbstractColumn::textAt(int row) const { Q_UNUSED(row); return ""; } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Text */ void AbstractColumn::setTextAt(int row, const QString& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) } /** * \brief Replace a range of values * * Use this only when columnMode() is Text */ void AbstractColumn::replaceTexts(int first, const QVector& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) }; /** * \brief Return the date part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDate AbstractColumn::dateAt(int row) const { Q_UNUSED(row); return QDate(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setDateAt(int row, QDate new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Return the time part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QTime AbstractColumn::timeAt(int row) const { Q_UNUSED(row); return QTime(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setTimeAt(int row, QTime new_value) { Q_UNUSED(row) Q_UNUSED(new_value) } /** * \brief Return the QDateTime in row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDateTime AbstractColumn::dateTimeAt(int row) const { Q_UNUSED(row); return QDateTime(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setDateTimeAt(int row, const QDateTime& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Replace a range of values * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::replaceDateTimes(int first, const QVector& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) }; /** * \brief Return the double value in row 'row' * * Use this only when columnMode() is Numeric */ double AbstractColumn::valueAt(int row) const { Q_UNUSED(row); return NAN; } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Numeric */ void AbstractColumn::setValueAt(int row, const double new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Replace a range of values * * Use this only when columnMode() is Numeric */ void AbstractColumn::replaceValues(int first, const QVector& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) } /** * \brief Return the integer value in row 'row' * * Use this only when columnMode() is Integer */ int AbstractColumn::integerAt(int row) const { Q_UNUSED(row); return 42; } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Integer */ void AbstractColumn::setIntegerAt(int row, const int new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Replace a range of values * * Use this only when columnMode() is Integer */ void AbstractColumn::replaceInteger(int first, const QVector& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) } /**********************************************************************/ double AbstractColumn::minimum(int count) const { Q_UNUSED(count); return -INFINITY; } double AbstractColumn::maximum(int count) const { Q_UNUSED(count); return INFINITY; } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \fn void AbstractColumn::plotDesignationAboutToChange(const AbstractColumn *source) * \brief Column plot designation will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::plotDesignationChanged(const AbstractColumn *source) * \brief Column plot designation changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::modeAboutToChange(const AbstractColumn *source) * \brief Column mode (possibly also the data type) will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::modeChanged(const AbstractColumn *source) * \brief Column mode (possibly also the data type) changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::dataAboutToChange(const AbstractColumn *source) * \brief Data of the column will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::dataChanged(const AbstractColumn *source) * \brief Data of the column has changed * * Important: When data has changed also the number * of rows in the column may have changed without * any other signal emission. * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::rowsAboutToBeInserted(const AbstractColumn *source, int before, int count) * \brief Rows will be inserted * * \param source the column that emitted the signal * \param before the row to insert before * \param count the number of rows to be inserted */ /** * \fn void AbstractColumn::rowsInserted(const AbstractColumn *source, int before, int count) * \brief Rows have been inserted * * \param source the column that emitted the signal * \param before the row to insert before * \param count the number of rows to be inserted */ /** * \fn void AbstractColumn::rowsAboutToBeRemoved(const AbstractColumn *source, int first, int count) * \brief Rows will be deleted * * \param source the column that emitted the signal * \param first the first row to be deleted * \param count the number of rows to be deleted */ /** * \fn void AbstractColumn::rowsRemoved(const AbstractColumn *source, int first, int count) * \brief Rows have been deleted * * \param source the column that emitted the signal * \param first the first row that was deleted * \param count the number of deleted rows */ /** * \fn void AbstractColumn::maskingAboutToChange(const AbstractColumn *source) * \brief Rows are about to be masked or unmasked */ /** * \fn void AbstractColumn::maskingChanged(const AbstractColumn *source) * \brief Rows have been masked or unmasked */ /** * \fn void AbstractColumn::aboutToBeDestroyed(const AbstractColumn *source) * \brief Emitted shortl before this column is deleted * * \param source the object emitting this signal * * This is needed by AbstractFilter. */ /** * \brief Read XML mask element */ bool AbstractColumn::XmlReadMask(XmlStreamReader *reader) { Q_ASSERT(reader->isStartElement() && reader->name() == "mask"); bool ok1, ok2; int start, end; start = reader->readAttributeInt("start_row", &ok1); end = reader->readAttributeInt("end_row", &ok2); if(!ok1 || !ok2) { reader->raiseError(i18n("invalid or missing start or end row")); return false; } setMasked(Interval(start,end)); if (!reader->skipToEndElement()) return false; return true; } /** * \brief Write XML mask element */ void AbstractColumn::XmlWriteMask(QXmlStreamWriter *writer) const { for (const auto& interval: maskedIntervals()) { writer->writeStartElement("mask"); writer->writeAttribute("start_row", QString::number(interval.start())); writer->writeAttribute("end_row", QString::number(interval.end())); writer->writeEndElement(); } } diff --git a/src/backend/core/AbstractFilter.cpp b/src/backend/core/AbstractFilter.cpp index ff9da2d55..f201e4891 100644 --- a/src/backend/core/AbstractFilter.cpp +++ b/src/backend/core/AbstractFilter.cpp @@ -1,351 +1,351 @@ /*************************************************************************** File : AbstractFilter.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2007 by Knut Franke, Tilman Benkert Email (use @ for *) : knut.franke*gmx.de, thzs*gmx.net Description : Base class for all analysis operations. ***************************************************************************/ /*************************************************************************** * * * 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 "AbstractFilter.h" -#include +#include #include "backend/core/AbstractColumn.h" #include "backend/lib/macros.h" /** * \class AbstractFilter * \brief Base class for all analysis operations. * * AbstractFilter provides an abstraction for analysis operations. It is modelled on an * electronic filtering circuit: From the outside, a filter appears as a black box with * a number of input and output ports (obviously, those numbers do not necessarily agree). * * \section using Using AbstractFilter * You can connect one AbstractColumn to each input port using * input(int port, AbstractColumn* source). Every output(int port) is realized * again by an AbstractColumn, which you can connect to as many other filters, tables * or plots as you like. * Ownership of the data sources always stays with the class which is providing the data, * that is, neither input() nor output() transfer ownership. * * Furthermore, you can use inputCount() and outputCount() to query the number of * input and output ports, respectively and you can obtain label strings for inputs (via * inputLabel()) and outputs (via AbstractColumn::label()). This allows generic filter * handling routines to be written, which is important for using filters provided by plugins. * * Its simplicity of use notwithstanding, AbstractFilter provides a powerful and versatile * basis also for analysis operations that would not commonly be referred to as "filter". * An example of such a more advanced filter implementation is StatisticsFilter. * * \section subclassing Subclassing AbstractFilter * The main design goal was to make implementing new filters as easy as possible. * Filters with only one output port can subclass AbstractSimpleFilter, which is even easier * to use. Filters with more than one output port have to subclass * AbstractFilter directly, which is slightly more involved, because in * addition to data transfer between these classes the signals defined by AbstractColumn * have to be handled on both inputs and outputs. Signals from data sources connected to the input * ports are automatically connected to a matching set of virtual methods, which can be * reimplemented by subclasses to handle these events. * * While AbstractFilter handles the tedious part of connecting a data source to an input port, * its subclasses are given a chance to reject such connections (e.g., based on the data type * of the source) by reimplementing inputAcceptable(). * * \sa AbstractSimpleFilter */ /** * \fn AbstractFilter::inputCount() const * \brief Return the number of input ports supported by the filter or -1 if any number of inputs is acceptable. */ /** * \fn AbstractFilter::outputCount() const * \brief Return the number of output ports provided by the filter. * * %Note that this number need not be static, but can be dynamically determined, for example * based on the inputs provided to the filter. */ /** * \brief Return the index of the highest input port that is connected. * * Note that this is different from both the number of ports that could be connected, * inputCount(), and the number of ports that actually have been connected, which are * not necessarily sequential. In conjunction with input(int), this method can be used to * traverse the connected inputs. */ int AbstractFilter::highestConnectedInput() const { return m_inputs.count() - 1; } /** * \brief Connect the provided data source to the specified input port. * \param port the port number to which to connect * \param source the data source to connect to the input port * \returns true if the connection was accepted, false otherwise. * * The port number is checked for validity against inputCount() and both port number and data * source are passed to inputAcceptable() for review. If both checks succeed, the * source is recorded in #m_inputs. * If applicable, the previously connected data source is disconnected before replacing it. * * You can also use this method to disconnect an input without replacing it with a new one by * calling it with source=0. * * \sa inputAcceptable(), #m_inputs */ bool AbstractFilter::input(int port, const AbstractColumn* source) { // DEBUG("AbstractFilter::input()"); if (port < 0 || (inputCount() >= 0 && port >= inputCount())) return false; if (source && !inputAcceptable(port, source)) return false; if (port >= m_inputs.size()) m_inputs.resize(port+1); const AbstractColumn* old_input = m_inputs.value(port); if (source == old_input) return true; if (old_input) { disconnect(old_input, 0, this, 0); // replace input, notifying the filter implementation of the changes inputDescriptionAboutToChange(old_input); inputPlotDesignationAboutToChange(old_input); inputMaskingAboutToChange(old_input); inputDataAboutToChange(old_input); if(source && source->columnMode() != old_input->columnMode()) inputModeAboutToChange(old_input); } if (!source) inputAboutToBeDisconnected(old_input); m_inputs[port] = source; if (source) { // we have a new source // DEBUG(" new source"); if(old_input && source->columnMode() != old_input->columnMode()) inputModeAboutToChange(source); inputDataChanged(source); inputMaskingChanged(source); inputPlotDesignationChanged(source); inputDescriptionChanged(source); // connect the source's signals connect(source, &AbstractColumn::aspectDescriptionAboutToChange, this, static_cast(&AbstractFilter::inputDescriptionAboutToChange)); connect(source, &AbstractColumn::aspectDescriptionChanged, this, static_cast(&AbstractFilter::inputDescriptionChanged)); connect(source, &AbstractColumn::plotDesignationAboutToChange, this, &AbstractFilter::inputPlotDesignationAboutToChange); connect(source, &AbstractColumn::plotDesignationChanged, this, &AbstractFilter::inputPlotDesignationChanged); connect(source, &AbstractColumn::modeAboutToChange, this, &AbstractFilter::inputModeAboutToChange); connect(source, &AbstractColumn::modeChanged, this, &AbstractFilter::inputModeChanged); connect(source, &AbstractColumn::dataAboutToChange, this, &AbstractFilter::inputDataAboutToChange); connect(source, &AbstractColumn::dataChanged, this, &AbstractFilter::inputDataChanged); connect(source, &AbstractColumn::rowsAboutToBeInserted, this, &AbstractFilter::inputRowsAboutToBeInserted); connect(source, &AbstractColumn::rowsInserted, this, &AbstractFilter::inputRowsInserted); connect(source, &AbstractColumn::rowsAboutToBeRemoved, this, &AbstractFilter::inputRowsAboutToBeRemoved); connect(source, &AbstractColumn::rowsRemoved, this, &AbstractFilter::inputRowsRemoved); connect(source, &AbstractColumn::maskingAboutToChange, this, &AbstractFilter::inputMaskingAboutToChange); connect(source, &AbstractColumn::maskingChanged, this, &AbstractFilter::inputMaskingChanged); connect(source, &AbstractColumn::aboutToBeDestroyed, this, &AbstractFilter::inputAboutToBeDestroyed); } else { // source==0, that is, the input port has been disconnected // DEBUG(" no source"); // try to shrink m_inputs int num_connected_inputs = m_inputs.size(); while (m_inputs.at(num_connected_inputs-1) == 0) { num_connected_inputs--; if(!num_connected_inputs) break; } m_inputs.resize(num_connected_inputs); } return true; } /** * \brief Connect all outputs of the provided filter to the corresponding inputs of this filter. * \returns true if all connections were accepted, false otherwise * * Overloaded method provided for convenience. */ bool AbstractFilter::input(const AbstractFilter* sources) { if (!sources) return false; bool result = true; for (int i = 0; i < sources->outputCount(); ++i) if (!input(i, sources->output(i))) result = false; return result; } /** * \brief Return the input currently connected to the specified port, or 0. */ const AbstractColumn* AbstractFilter::input(int port) const { return m_inputs.value(port); } /** * \brief Return the label associated to the given input port. * * Default labels are In1, In2, ... (or translated equivalents), but implementations can * reimplement this method to produce more meaningful labels. * * Output ports are implicitly labeled through AbstractAspect::name(). */ QString AbstractFilter::inputLabel(int port) const { return i18nc("default labels of filter input ports", "In%1", port + 1); } /** * \fn AbstractColumn *AbstractFilter::output(int port=0) * \brief Get the data source associated with the specified output port. * * The returned pointer may be 0 even for valid port numbers, for example if not all required * input ports have been connected. */ /** * \fn const AbstractColumn *AbstractFilter::output(int port=0) const * \brief Overloaded method for const access. */ /** * \brief Return the input port to which the column is connected or -1 if it's not connected */ int AbstractFilter::portIndexOf(const AbstractColumn* column) { for (int i = 0; i < m_inputs.size(); ++i) if(m_inputs.at(i) == column) return i; return -1; } /** * \brief Give implementations a chance to reject connections to their input ports. * * If not reimplemented, all connections to ports within [0, inputCount()-1] will be accepted. */ bool AbstractFilter::inputAcceptable(int port, const AbstractColumn* source) { Q_UNUSED(port); Q_UNUSED(source); return true; } /** * \brief Called whenever an input is disconnected or deleted. * * This is only to notify implementations of the event, the default implementation is a * no-op. */ void AbstractFilter::inputAboutToBeDisconnected(const AbstractColumn* source) { Q_UNUSED(source); } //////////////////////////////////////////////////////////////////////////////////////////////////// //!\name signal handlers //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Name and/or comment of an input will be changed. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputDescriptionAboutToChange(const AbstractColumn* source) { Q_UNUSED(source); } void AbstractFilter::inputDescriptionAboutToChange(const AbstractAspect* aspect) { const AbstractColumn* col = qobject_cast(aspect); if (col) inputDescriptionAboutToChange(col); } /** * \brief Name and/or comment of an input changed. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputDescriptionChanged(const AbstractColumn* source) { Q_UNUSED(source); } void AbstractFilter::inputDescriptionChanged(const AbstractAspect* aspect) { const AbstractColumn* col = qobject_cast(aspect); if (col && m_inputs.contains(col)) inputDescriptionChanged(col); } /** * \brief The plot designation of an input is about to change. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputPlotDesignationAboutToChange(const AbstractColumn* source) { Q_UNUSED(source); } /** * \brief The plot designation of an input changed. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputPlotDesignationChanged(const AbstractColumn* source) { Q_UNUSED(source); } /** * \brief The display mode and possibly the data type of an input is about to change. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputModeAboutToChange(const AbstractColumn* source) { Q_UNUSED(source); } /** * \brief The display mode and possibly the data type has changed. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputModeChanged(const AbstractColumn* source) { Q_UNUSED(source); } /** * \brief The data of an input is about to change. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputDataAboutToChange(const AbstractColumn* source) { Q_UNUSED(source); } /** * \brief The data of an input has changed. * * \param source is always the this pointer of the column that emitted the signal. */ void AbstractFilter::inputDataChanged(const AbstractColumn* source) { Q_UNUSED(source); } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \var AbstractFilter::m_inputs * \brief The data sources connected to my input ports. */ diff --git a/src/backend/core/AbstractPart.cpp b/src/backend/core/AbstractPart.cpp index e03001715..a4f1b7842 100644 --- a/src/backend/core/AbstractPart.cpp +++ b/src/backend/core/AbstractPart.cpp @@ -1,138 +1,138 @@ /*************************************************************************** File : AbstractPart.cpp Project : LabPlot Description : Base class of Aspects with MDI windows as views. -------------------------------------------------------------------- Copyright : (C) 2008 Knut Franke (knut.franke@gmx.de) Copyright : (C) 2012 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 "backend/core/AbstractPart.h" #include "commonfrontend/core/PartMdiView.h" #include "backend/core/Workbook.h" #include "backend/datapicker/Datapicker.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/datapicker/DatapickerCurve.h" #include #include -#include +#include /** * \class AbstractPart * \brief Base class of Aspects with MDI windows as views (AspectParts). */ AbstractPart::AbstractPart(const QString& name) : AbstractAspect(name), m_mdiWindow(nullptr), m_partView(nullptr) { } AbstractPart::~AbstractPart() { if (m_mdiWindow) delete m_mdiWindow; } /** * \fn QWidget *AbstractPart::view() const * \brief Construct a primary view on me. * * The caller receives ownership of the view. * * This method may be called multiple times during the life time of a Part, or it might not get * called at all. Parts must not depend on the existence of a view for their operation. */ /** * \brief Wrap the view() into a PartMdiView. * * A new view is only created the first time this method is called; * after that, a pointer to the pre-existing view is returned. */ PartMdiView* AbstractPart::mdiSubWindow() const { if (!m_mdiWindow) m_mdiWindow = new PartMdiView(const_cast(this)); return m_mdiWindow; } bool AbstractPart::hasMdiSubWindow() const { return m_mdiWindow; } /*! * this function is called when PartMdiView, the mdi-subwindow-wrapper of the actual view, * is closed (=deleted) in MainWindow. Makes sure that the view also gets deleted. */ void AbstractPart::deleteView() const { //if the parent is a Workbook or Datapicker, the actual view was already deleted when QTabWidget was deleted. //here just set the pointer to 0. if (dynamic_cast(parentAspect()) || dynamic_cast(parentAspect()) || dynamic_cast(parentAspect()->parentAspect())) { m_partView = nullptr; return; } if (m_partView) { delete m_partView; m_partView = nullptr; m_mdiWindow = nullptr; } } /** * \brief Return AbstractAspect::createContextMenu() plus operations on the primary view. */ QMenu* AbstractPart::createContextMenu() { QMenu* menu = AbstractAspect::createContextMenu(); Q_ASSERT(menu); menu->addSeparator(); if (m_mdiWindow) { menu->addAction(QIcon::fromTheme("document-export-database"), i18n("Export"), this, SIGNAL(exportRequested())); menu->addAction(QIcon::fromTheme("document-print"), i18n("Print"), this, SIGNAL(printRequested())); menu->addAction(QIcon::fromTheme("document-print-preview"), i18n("Print Preview"), this, SIGNAL(printPreviewRequested())); menu->addSeparator(); const QStyle *widget_style = m_mdiWindow->style(); if(m_mdiWindow->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized)) { QAction* action = menu->addAction(i18n("&Restore"), m_mdiWindow, SLOT(showNormal())); action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarNormalButton)); } if(!(m_mdiWindow->windowState() & Qt::WindowMinimized)) { QAction* action = menu->addAction(i18n("Mi&nimize"), m_mdiWindow, SLOT(showMinimized())); action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarMinButton)); } if(!(m_mdiWindow->windowState() & Qt::WindowMaximized)) { QAction* action = menu->addAction(i18n("Ma&ximize"), m_mdiWindow, SLOT(showMaximized())); action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarMaxButton)); } } else { //data spreadsheets in the datapicker curves cannot be hidden/minimized, don't show this menu entry if ( !(dynamic_cast(this) && dynamic_cast(this->parentAspect())) ) menu->addAction(i18n("Show"), this, SIGNAL(showRequested())); } return menu; } diff --git a/src/backend/core/AbstractScriptingEngine.cpp b/src/backend/core/AbstractScriptingEngine.cpp index 49a06b4c5..77ede193e 100644 --- a/src/backend/core/AbstractScriptingEngine.cpp +++ b/src/backend/core/AbstractScriptingEngine.cpp @@ -1,225 +1,225 @@ /*************************************************************************** File : AbstractScriptingEngine.cpp Project : SciDAVis -------------------------------------------------------------------- Copyright : (C) 2006,2008 by Knut Franke Email (use @ for *) : knut.franke*gmx.de Description : Implementations of generic scripting classes ***************************************************************************/ /*************************************************************************** * * * 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 "AbstractScriptingEngine.h" #include -#include +#include /** * \class AbstractScriptingEngine * \brief An interpreter for evaluating scripting code. * * AbstractScriptingEngine objects represent a running interpreter, possibly with global * variables, and are responsible for generating AbstractScript objects (which do * the actual evaluation of code). * * The class also keeps a static list of available interpreters and instantiates * them on demand. */ /** * \brief Only initialize general information here. * * All AbstractScriptingEngine subclasses are instantiated at startup (more precisely: when * instantiating ScriptingEngineManager). Therefor, loading the actual interpreter is done * in initialize(). */ AbstractScriptingEngine::AbstractScriptingEngine(const char *lang_name) { setObjectName(lang_name); m_initialized=false; m_refcount=0; } /** * \fn void AbstractScriptingEngine::initialize() * \brief Initialize the scripting environment. * * Don't forget to set m_initialized to true in implementations after a successful * initialization. */ /** * \brief initialization of the interpreter may fail; or there could be other errors setting up the environment */ bool AbstractScriptingEngine::initialized() const { return m_initialized; } /** * \brief whether asynchronuous execution is enabled (if supported by the implementation) */ bool AbstractScriptingEngine::isRunning() const { return false; } /** * \fn AbstractScript *AbstractScriptingEngine::makeScript(const QString &code, QObject *context, const QString &name) * \brief Instantiate the AbstractScript subclass matching the AbstractScriptingEngine subclass. */ /** * \brief If an exception / error occurred, return a nicely formated stack backtrace. */ QString AbstractScriptingEngine::stackTraceString() { return QString(); } /** * \brief Clear the global environment. * * What exactly happens depends on the implementation. */ void AbstractScriptingEngine::clear() {} /** * \brief If the implementation supports asynchronuos execution, deactivate it. */ void AbstractScriptingEngine::stopExecution() {} /** * \brief If the implementation supports asynchronuos execution, activate it. */ void AbstractScriptingEngine::startExecution() {} /** * \brief Return a list of supported mathematical functions. * * These should be imported into the global namespace. */ const QStringList AbstractScriptingEngine::mathFunctions() const { return QStringList(); } /** * \brief Return a documentation string for the given mathematical function. */ const QString AbstractScriptingEngine::mathFunctionDoc(const QString&) const { return QString(); } /** * \brief Return a list of file extensions commonly used for this language. */ const QStringList AbstractScriptingEngine::fileExtensions() const { return QStringList(); } /** * \brief Construct a filter expression from fileExtensions(), suitable for QFileDialog. */ const QString AbstractScriptingEngine::nameAndPatterns() const { QStringList extensions = fileExtensions(); if (extensions.isEmpty()) return ""; else return i18n("%1 Source (*.%2)", objectName(), extensions.join(" *.")); } /** * \brief Increase the reference count. * * This should only be called by scripted and Script to avoid memory leaks. */ void AbstractScriptingEngine::incref() { m_refcount++; } /** * \brief Decrease the reference count. * * This should only be called by scripted and Script to avoid segfaults. */ void AbstractScriptingEngine::decref() { m_refcount--; if (m_refcount==0) delete this; } /** * \fn void AbstractScriptingEngine::error(const QString &message, const QString &scriptName, int lineNumber) * \brief signal an error condition / exception */ /** * \fn void AbstractScriptingEngine::print(const QString &output) * \brief output that is not handled by a Script */ /** * \var AbstractScriptingEngine::m_initialized * \brief whether the interpreter has been successfully initialized */ /** * \var AbstractScriptingEngine::m_refcount * \brief the reference counter */ /******************************************************************************\ *Helper classes for managing instances of AbstractScriptingEngine subclasses.* \******************************************************************************/ /** * \class ScriptingChangeEvent * \brief notify an object that it should update its scripting environment (see class scripted) */ /** * \class scripted * \brief Interface for maintaining a reference to the current AbstractScriptingEngine * * Every class that wants to use a AbstractScriptingEngine should subclass this one and * implement slot customEvent(QEvent*) such that it forwards any * AbstractScriptingChangeEvents to scripted::scriptingChangeEvent. */ scripted::scripted(AbstractScriptingEngine *engine) { if (engine) engine->incref(); m_scripting_engine = engine; } scripted::~scripted() { if (m_scripting_engine) m_scripting_engine->decref(); } void scripted::scriptingChangeEvent(ScriptingChangeEvent *sce) { m_scripting_engine->decref(); sce->scriptingEngine()->incref(); m_scripting_engine = sce->scriptingEngine(); } diff --git a/src/backend/core/AbstractSimpleFilter.cpp b/src/backend/core/AbstractSimpleFilter.cpp index 7ccd515c5..f89222c81 100644 --- a/src/backend/core/AbstractSimpleFilter.cpp +++ b/src/backend/core/AbstractSimpleFilter.cpp @@ -1,409 +1,409 @@ /*************************************************************************** File : AbstractSimpleFilter.cpp Project : AbstractColumn -------------------------------------------------------------------- Copyright : (C) 2007,2008 by Knut Franke, Tilman Benkert Email (use @ for *) : knut.franke*gmx.de, thzs*gmx.net Description : Simplified filter interface for filters with only one output port. ***************************************************************************/ /*************************************************************************** * * * 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 "AbstractSimpleFilter.h" #include "backend/lib/XmlStreamReader.h" #include #include #include -#include +#include /** * \class AbstractSimpleFilter * \brief Simplified filter interface for filters with only one output port. * * This class is only meant to simplify implementation of a restricted subtype of filter. * It should not be instantiated directly. You should always either derive from * AbstractFilter or (if necessary) provide an actual (non-abstract) implementation. * * The trick here is that, in a sense, the filter is its own output port. This means you * can implement a complete filter in only one class and don't have to coordinate data * transfer between a filter class and a data source class. * Additionally, AbstractSimpleFilter offers some useful convenience methods which make writing * filters as painless as possible. * * For the data type of the output, all types supported by AbstractColumn (currently double, QString and * QDateTime) are supported. * * \section tutorial1 Tutorial, Step 1 * The simplest filter you can write assumes there's also only one input port and rows on the * input correspond 1:1 to rows in the output. All you need to specify is what data type you * want to have (in this example double) on the input port and how to compute the output values: * * \code * 01 #include "AbstractSimpleFilter.h" * 02 class TutorialFilter1 : public AbstractSimpleFilter * 03 { * 04 protected: * 05 virtual bool inputAcceptable(int, AbstractColumn *source) { * 06 return (source->columnMode() == AbstractColumn::Numeric); * 07 } * 08 public: * 09 virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; } * 10 * 11 virtual double valueAt(int row) const { * 12 if (!m_inputs.value(0)) return 0.0; * 13 double input_value = m_inputs.value(0)->valueAt(row); * 14 return input_value * input_value; * 15 } * 16 }; * \endcode * * This filter reads an input value (line 13) and returns its square (line 14). * Reimplementing inputAcceptable() makes sure that the data source really is of type * double (lines 5 to 7). Otherwise, the source will be rejected by AbstractFilter::input(). * The output type is reported by reimplementing columnMode() (line 09). * Before you actually use m_inputs.value(0), make sure that the input port has * been connected to a data source (line 12). * Otherwise line 13 would result in a crash. That's it, we've already written a * fully-functional filter! * * Equivalently, you can write 1:1-filters for QString or QDateTime inputs by checking for * AbstractColumn::TypeQString or AbstractColumn::TypeQDateTime in line 6. You would then use * AbstractColumn::textAt(row) or AbstractColumn::dateTimeAt(row) in line 13 to access the input data. * For QString output, you need to implement AbstractColumn::textAt(row). * For QDateTime output, you have to implement three methods: * \code * virtual QDateTime dateTimeAt(int row) const; * virtual QDate dateAt(int row) const; * virtual QTime timeAt(int row) const; * \endcode * * \section tutorial2 Tutorial, Step 2 * Now for something slightly more interesting: a filter that uses only every second row of its * input. We no longer have a 1:1 correspondence between input and output rows, so we'll have * to do a bit more work in order to have everything come out as expected. * We'll use double-typed input and output again: * \code * 01 #include "AbstractSimpleFilter.h" * 02 class TutorialFilter2 : public AbstractSimpleFilter * 03 { * 04 protected: * 05 virtual bool inputAcceptable(int, AbstractColumn *source) { * 06 return (source->columnMode() == AbstractColumn::Numeric); * 07 } * 08 public: * 09 virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; } * \endcode * Even rows (including row 0) get dropped, odd rows are renumbered: * \code * 10 public: * 11 virtual double valueAt(int row) const { * 12 if (!m_inputs.value(0)) return 0.0; * 13 return m_inputs.value(0)->valueAt(2*row + 1); * 14 } * \endcode */ // TODO: should simple filters have a name argument? /** * \brief Ctor */ AbstractSimpleFilter::AbstractSimpleFilter() : AbstractFilter("SimpleFilter"), m_output_column(new SimpleFilterColumn(this)) { addChild(m_output_column); } /** * \brief Default to one input port. */ int AbstractSimpleFilter::inputCount() const { return 1; } /** * \brief We manage only one output port (don't override unless you really know what you are doing). */ int AbstractSimpleFilter::outputCount() const { return 1; } /** * \brief Copy plot designation of input port 0. */ AbstractColumn::PlotDesignation AbstractSimpleFilter::plotDesignation() const { return m_inputs.value(0) ? m_inputs.at(0)->plotDesignation() : AbstractColumn::NoDesignation; } /** * \brief Return the column mode * * This function is most used by tables but can also be used * by plots. The column mode specifies how to interpret * the values in the column additional to the data type. */ AbstractColumn::ColumnMode AbstractSimpleFilter::columnMode() const { // calling this function while m_input is empty is a sign of very bad code // nevertheless it will return some rather meaningless value to // avoid crashes return m_inputs.value(0) ? m_inputs.at(0)->columnMode() : AbstractColumn::Text; } /** * \brief Return the content of row 'row'. * * Use this only when columnMode() is Text */ QString AbstractSimpleFilter::textAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->textAt(row) : QString(); } /** * \brief Return the date part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDate AbstractSimpleFilter::dateAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->dateAt(row) : QDate(); } /** * \brief Return the time part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QTime AbstractSimpleFilter::timeAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->timeAt(row) : QTime(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDateTime AbstractSimpleFilter::dateTimeAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->dateTimeAt(row) : QDateTime(); } /** * \brief Return the double value in row 'row' * * Use this only when columnMode() is Numeric */ double AbstractSimpleFilter::valueAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->valueAt(row) : 0.0; } /** * \brief Return the integer value in row 'row' * * Use this only when columnMode() is Integer */ int AbstractSimpleFilter::integerAt(int row) const { return m_inputs.value(0) ? m_inputs.at(0)->integerAt(row) : 0; } /** * \brief Number of output rows == number of input rows * * ... unless overridden in a subclass. */ int AbstractSimpleFilter::rowCount() const { return m_inputs.value(0) ? m_inputs.at(0)->rowCount() : 0; } /** * \brief Rows that will change when the given input interval changes. * * This implementation assumes a 1:1 correspondence between input and output rows, but can be * overridden in subclasses. */ QList< Interval > AbstractSimpleFilter::dependentRows(const Interval& inputRange) const { return QList< Interval >() << inputRange; } //////////////////////////////////////////////////////////////////////////////////////////////////// //!\name signal handlers //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// void AbstractSimpleFilter::inputPlotDesignationAboutToChange(const AbstractColumn*) { emit m_output_column->plotDesignationAboutToChange(m_output_column); } void AbstractSimpleFilter::inputPlotDesignationChanged(const AbstractColumn*) { emit m_output_column->plotDesignationChanged(m_output_column); } void AbstractSimpleFilter::inputModeAboutToChange(const AbstractColumn*) { emit m_output_column->dataAboutToChange(m_output_column); } void AbstractSimpleFilter::inputModeChanged(const AbstractColumn*) { emit m_output_column->dataChanged(m_output_column); } void AbstractSimpleFilter::inputDataAboutToChange(const AbstractColumn*) { emit m_output_column->dataAboutToChange(m_output_column); } void AbstractSimpleFilter::inputDataChanged(const AbstractColumn*) { emit m_output_column->dataChanged(m_output_column); } void AbstractSimpleFilter::inputRowsAboutToBeInserted(const AbstractColumn * source, int before, int count) { Q_UNUSED(source); Q_UNUSED(count); foreach (const Interval& output_range, dependentRows(Interval(before, before))) emit m_output_column->rowsAboutToBeInserted(m_output_column, output_range.start(), output_range.size()); } void AbstractSimpleFilter::inputRowsInserted(const AbstractColumn * source, int before, int count) { Q_UNUSED(source); Q_UNUSED(count); foreach (const Interval& output_range, dependentRows(Interval(before, before))) emit m_output_column->rowsInserted(m_output_column, output_range.start(), output_range.size()); } void AbstractSimpleFilter::inputRowsAboutToBeRemoved(const AbstractColumn * source, int first, int count) { Q_UNUSED(source); foreach (const Interval& output_range, dependentRows(Interval(first, first+count-1))) emit m_output_column->rowsAboutToBeRemoved(m_output_column, output_range.start(), output_range.size()); } void AbstractSimpleFilter::inputRowsRemoved(const AbstractColumn * source, int first, int count) { Q_UNUSED(source); foreach (const Interval& output_range, dependentRows(Interval(first, first+count-1))) emit m_output_column->rowsRemoved(m_output_column, output_range.start(), output_range.size()); } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return a pointer to #m_output_column on port 0 (don't override unless you really know what you are doing). */ AbstractColumn *AbstractSimpleFilter::output(int port) { return port == 0 ? static_cast(m_output_column) : 0; } const AbstractColumn *AbstractSimpleFilter::output(int port) const { return port == 0 ? static_cast(m_output_column) : 0; } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name serialize/deserialize //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Save to XML */ void AbstractSimpleFilter::save(QXmlStreamWriter * writer) const { writer->writeStartElement("simple_filter"); writeBasicAttributes(writer); writeExtraAttributes(writer); writer->writeAttribute("filter_name", metaObject()->className()); writeCommentElement(writer); writer->writeEndElement(); } /** * \brief Override this in derived classes if they have other attributes than filter_name */ void AbstractSimpleFilter::writeExtraAttributes(QXmlStreamWriter * writer) const { Q_UNUSED(writer) } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Load from XML */ bool AbstractSimpleFilter::load(XmlStreamReader* reader, bool preview) { Q_UNUSED(preview); //TODO if(reader->isStartElement() && reader->name() == "simple_filter") { if (!readBasicAttributes(reader)) return false; QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value(reader->namespaceUri().toString(), "filter_name").toString(); if(str != metaObject()->className()) { reader->raiseError(i18n("incompatible filter type")); return false; } // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } } } else reader->raiseError(i18n("no simple filter element found")); return !reader->hasError(); } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \class SimpleFilterColumn //////////////////////////////////////////////////////////////////////////////////////////////////// AbstractColumn::ColumnMode SimpleFilterColumn::columnMode() const { return m_owner->columnMode(); } QString SimpleFilterColumn::textAt(int row) const { return m_owner->textAt(row); } QDate SimpleFilterColumn::dateAt(int row) const { return m_owner->dateAt(row); } QTime SimpleFilterColumn::timeAt(int row) const { return m_owner->timeAt(row); } QDateTime SimpleFilterColumn::dateTimeAt(int row) const { return m_owner->dateTimeAt(row); } double SimpleFilterColumn::valueAt(int row) const { return m_owner->valueAt(row); } int SimpleFilterColumn::integerAt(int row) const { return m_owner->integerAt(row); } diff --git a/src/backend/core/AspectTreeModel.cpp b/src/backend/core/AspectTreeModel.cpp index b816d7dd8..e727ccefa 100644 --- a/src/backend/core/AspectTreeModel.cpp +++ b/src/backend/core/AspectTreeModel.cpp @@ -1,499 +1,499 @@ /*************************************************************************** File : AspectTreeModel.h Project : LabPlot Description : Represents a tree of AbstractAspect objects as a Qt item model. -------------------------------------------------------------------- Copyright : (C) 2007-2009 by Knut Franke (knut.franke@gmx.de) Copyright : (C) 2007-2009 by Tilman Benkert (thzs@gmx.net) Copyright : (C) 2011-2016 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 "backend/core/AbstractAspect.h" #include "backend/core/column/Column.h" #include "backend/worksheet/WorksheetElement.h" #include "backend/core/AspectTreeModel.h" #include #include #include #include #include -#include +#include /** * \class AspectTreeModel * \brief Represents a tree of AbstractAspect objects as a Qt item model. * * This class is an adapter between an AbstractAspect hierarchy and Qt's view classes. * * It represents children of an Aspect as rows in the model, with the fixed columns * Name (AbstractAspect::name()), Type (the class name), Created (AbstractAspect::creationTime()) * and Comment (AbstractAspect::comment()). Name is decorated using AbstractAspect::icon(). * The tooltip for all columns is generated from AbstractAspect::caption(). * * Name and Comment are editable. * * For views which support this (currently ProjectExplorer), the menu created by * AbstractAspect::createContextMenu() is made available via the custom role ContextMenuRole. */ /** * \enum AspectTreeModel::CustomDataRole * \brief Custom data roles used in addition to Qt::ItemDataRole */ /** * \var AspectTreeModel::ContextMenuRole * \brief pointer to a new context menu for an Aspect */ /** * \fn QModelIndex AspectTreeModel::modelIndexOfAspect(const AbstractAspect *aspect, int column=0) const * \brief Convenience wrapper around QAbstractItemModel::createIndex(). */ AspectTreeModel::AspectTreeModel(AbstractAspect* root, QObject* parent) : QAbstractItemModel(parent), m_root(root), m_readOnly(false), m_folderSelectable(true), m_numericColumnsOnly(false), m_nonEmptyNumericColumnsOnly(false), m_showPlotDesignation(false), m_filterCaseSensitivity(Qt::CaseInsensitive), m_matchCompleteWord(false) { connect(m_root, &AbstractAspect::aspectDescriptionChanged, this, &AspectTreeModel::aspectDescriptionChanged); connect(m_root, &AbstractAspect::aspectAboutToBeAdded, this, &AspectTreeModel::aspectAboutToBeAdded); connect(m_root, &AbstractAspect::aspectAboutToBeRemoved, this, &AspectTreeModel::aspectAboutToBeRemoved); connect(m_root, &AbstractAspect::aspectAdded, this, &AspectTreeModel::aspectAdded); connect(m_root, &AbstractAspect::aspectRemoved, this, &AspectTreeModel::aspectRemoved); connect(m_root, &AbstractAspect::aspectHiddenAboutToChange, this, &AspectTreeModel::aspectHiddenAboutToChange); connect(m_root, &AbstractAspect::aspectHiddenChanged, this, &AspectTreeModel::aspectHiddenChanged); } /*! \c list contains the class names of the aspects, that can be selected in the corresponding model view. */ void AspectTreeModel::setSelectableAspects(QList list) { m_selectableAspects=list; } void AspectTreeModel::setReadOnly(bool readOnly) { m_readOnly = readOnly; } void AspectTreeModel::enableNumericColumnsOnly(bool value) { m_numericColumnsOnly = value; } void AspectTreeModel::enableNonEmptyNumericColumnsOnly(bool value) { m_nonEmptyNumericColumnsOnly = value; } void AspectTreeModel::enableShowPlotDesignation(bool value) { m_showPlotDesignation = value; } QModelIndex AspectTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); if(!parent.isValid()) { if(row != 0) return QModelIndex(); return createIndex(row, column, m_root); } AbstractAspect *parent_aspect = static_cast(parent.internalPointer()); AbstractAspect *child_aspect = parent_aspect->child(row); if (!child_aspect) return QModelIndex(); return createIndex(row, column, child_aspect); } QModelIndex AspectTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); AbstractAspect *parent_aspect = static_cast(index.internalPointer())->parentAspect(); if (!parent_aspect) return QModelIndex(); return modelIndexOfAspect(parent_aspect); } int AspectTreeModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return 1; AbstractAspect *parent_aspect = static_cast(parent.internalPointer()); return parent_aspect->childCount(); } int AspectTreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4; } QVariant AspectTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation != Qt::Horizontal) return QVariant(); switch(role) { case Qt::DisplayRole: switch(section) { case 0: return i18n("Name"); case 1: return i18n("Type"); case 2: return i18n("Created"); case 3: return i18n("Comment"); default: return QVariant(); } default: return QVariant(); } } QVariant AspectTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); AbstractAspect* aspect = static_cast(index.internalPointer()); switch(role) { case Qt::DisplayRole: case Qt::EditRole: switch(index.column()) { case 0: { const Column* column = dynamic_cast(aspect); if (column) { QString name = aspect->name(); if (m_numericColumnsOnly && !(column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer)) - name += QLatin1String(" (") + i18n("non-numeric data") + QLatin1Char(')'); + name = i18n("%1 (non-numeric data)", name); else if (m_nonEmptyNumericColumnsOnly && !column->hasValues()) - name += QLatin1String(" (") + i18n("no values") + QLatin1Char(')'); + name = i18n("%1 (no values)", name); if (m_showPlotDesignation) { QString designation; switch(column->plotDesignation()) { case AbstractColumn::NoDesignation: break; case AbstractColumn::X: designation = QLatin1String(" [X]"); break; case AbstractColumn::Y: designation = QLatin1String(" [Y]"); break; case AbstractColumn::Z: designation = QLatin1String(" [Z]"); break; case AbstractColumn::XError: designation = QLatin1String(" [") + i18n("X-error") + QLatin1Char(']'); break; case AbstractColumn::XErrorPlus: designation = QLatin1String(" [") + i18n("X-error +") + QLatin1Char(']'); break; case AbstractColumn::XErrorMinus: designation = QLatin1String(" [") + i18n("X-error -") + QLatin1Char(']'); break; case AbstractColumn::YError: designation = QLatin1String(" [") + i18n("Y-error") + QLatin1Char(']'); break; case AbstractColumn::YErrorPlus: designation = QLatin1String(" [") + i18n("Y-error +") + QLatin1Char(']'); break; case AbstractColumn::YErrorMinus: designation = QLatin1String(" [") + i18n("Y-error -") + QLatin1Char(']'); break; } name += QLatin1Char('\t') + designation; } return name; } else return aspect->name(); } case 1: if (aspect->metaObject()->className() != QLatin1String("CantorWorksheet")) return aspect->metaObject()->className(); else return QLatin1String("CAS Worksheet"); case 2: return aspect->creationTime().toString(); case 3: return aspect->comment().replace('\n', ' ').simplified(); default: return QVariant(); } case Qt::ToolTipRole: if (aspect->comment().isEmpty()) return QLatin1String("") + aspect->name() + QLatin1String(""); else return QLatin1String("") + aspect->name() + QLatin1String("

") + aspect->comment().replace(QLatin1Char('\n'), QLatin1String("
")); case Qt::DecorationRole: return index.column() == 0 ? aspect->icon() : QIcon(); case Qt::ForegroundRole: { const WorksheetElement* we = qobject_cast(aspect); if (we) { if (!we->isVisible()) return QVariant( QApplication::palette().color(QPalette::Disabled,QPalette::Text ) ); } return QVariant( QApplication::palette().color(QPalette::Active,QPalette::Text ) ); } default: return QVariant(); } } Qt::ItemFlags AspectTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; Qt::ItemFlags result; AbstractAspect *aspect = static_cast(index.internalPointer()); if (!m_selectableAspects.isEmpty()) { foreach(const char * classString, m_selectableAspects) { if (aspect->inherits(classString)) { result = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if( index!=this->index(0,0,QModelIndex()) && !m_filterString.isEmpty() ) { if (this->containsFilterString(aspect)) result = Qt::ItemIsEnabled | Qt::ItemIsSelectable; else result &= ~Qt::ItemIsEnabled; } break; } else result &= ~Qt::ItemIsEnabled; } } else { //default case: the list for the selectable aspects is empty and all aspects are selectable. // Apply filter, if available. Indices, that don't match the filter are not selectable. //Don't apply any filter to the very first index in the model - this top index corresponds to the project item. if ( index!=this->index(0,0,QModelIndex()) && !m_filterString.isEmpty() ) { if (this->containsFilterString(aspect)) result = Qt::ItemIsEnabled | Qt::ItemIsSelectable; else result = Qt::ItemIsSelectable; } else result = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } //the columns "name" and "description" are editable if (!m_readOnly) { if (index.column() == 0 || index.column() == 3) result |= Qt::ItemIsEditable; } const Column* column = dynamic_cast(aspect); if (column) { //allow to drag and drop columns for the faster creation of curves in the plots. //TODO: allow drag&drop later for other objects too, once we implement copy and paste in the project explorer result = result |Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; if (m_numericColumnsOnly && !(column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer)) result &= ~Qt::ItemIsEnabled; if (m_nonEmptyNumericColumnsOnly && !column->hasValues()) result &= ~Qt::ItemIsEnabled; } return result; } void AspectTreeModel::aspectDescriptionChanged(const AbstractAspect *aspect) { emit dataChanged(modelIndexOfAspect(aspect), modelIndexOfAspect(aspect, 3)); } void AspectTreeModel::aspectAboutToBeAdded(const AbstractAspect *parent, const AbstractAspect *before, const AbstractAspect *child) { Q_UNUSED(child); int index = parent->indexOfChild(before); if (index == -1) index = parent->childCount(); beginInsertRows(modelIndexOfAspect(parent), index, index); } void AspectTreeModel::aspectAdded(const AbstractAspect *aspect) { endInsertRows(); AbstractAspect * parent = aspect->parentAspect(); emit dataChanged(modelIndexOfAspect(parent), modelIndexOfAspect(parent, 3)); connect(aspect, &AbstractAspect::renameRequested, this, &AspectTreeModel::renameRequestedSlot); connect(aspect, &AbstractAspect::childAspectSelectedInView, this, &AspectTreeModel::aspectSelectedInView); connect(aspect, &AbstractAspect::childAspectDeselectedInView, this, &AspectTreeModel::aspectDeselectedInView); //add signal-slot connects for all children, too for (const auto* child : aspect->children(AbstractAspect::Recursive)) { connect(child, &AbstractAspect::renameRequested, this, &AspectTreeModel::renameRequestedSlot); connect(child, &AbstractAspect::childAspectSelectedInView, this, &AspectTreeModel::aspectSelectedInView); connect(child, &AbstractAspect::childAspectDeselectedInView, this, &AspectTreeModel::aspectDeselectedInView); } } void AspectTreeModel::aspectAboutToBeRemoved(const AbstractAspect *aspect) { AbstractAspect * parent = aspect->parentAspect(); int index = parent->indexOfChild(aspect); beginRemoveRows(modelIndexOfAspect(parent), index, index); } void AspectTreeModel::aspectRemoved() { endRemoveRows(); } void AspectTreeModel::aspectHiddenAboutToChange(const AbstractAspect * aspect) { for (AbstractAspect * i = aspect->parentAspect(); i; i = i->parentAspect()) if (i->hidden()) return; if (aspect->hidden()) aspectAboutToBeAdded(aspect->parentAspect(), aspect, aspect); else aspectAboutToBeRemoved(aspect); } void AspectTreeModel::aspectHiddenChanged(const AbstractAspect *aspect) { for (AbstractAspect * i = aspect->parentAspect(); i; i = i->parentAspect()) if (i->hidden()) return; if (aspect->hidden()) aspectRemoved(); else aspectAdded(aspect); } bool AspectTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || role != Qt::EditRole) return false; AbstractAspect *aspect = static_cast(index.internalPointer()); switch (index.column()) { case 0: aspect->setName(value.toString()); break; case 3: aspect->setComment(value.toString()); break; default: return false; } emit dataChanged(index, index); return true; } QModelIndex AspectTreeModel::modelIndexOfAspect(const AbstractAspect* aspect, int column) const { AbstractAspect* parent = aspect->parentAspect(); return createIndex(parent ? parent->indexOfChild(aspect) : 0, column, const_cast(aspect)); } /*! returns the model index of an aspect defined via its path. */ QModelIndex AspectTreeModel::modelIndexOfAspect(const QString& path, int column) const { //determine the aspect out of aspect path AbstractAspect* aspect = nullptr; auto children = m_root->children("AbstractAspect", AbstractAspect::Recursive); for (auto* child: children) { if (child->path() == path) { aspect = child; break; } } //return the model index of the aspect if (aspect) return modelIndexOfAspect(aspect, column); else return QModelIndex(); } void AspectTreeModel::setFilterString(const QString & s) { m_filterString=s; QModelIndex topLeft = this->index(0,0, QModelIndex()); QModelIndex bottomRight = this->index(this->rowCount()-1,3, QModelIndex()); emit dataChanged(topLeft, bottomRight); } void AspectTreeModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) { m_filterCaseSensitivity = cs; } void AspectTreeModel::setFilterMatchCompleteWord(bool b) { m_matchCompleteWord = b; } bool AspectTreeModel::containsFilterString(const AbstractAspect* aspect) const { if (m_matchCompleteWord) { if (aspect->name().compare(m_filterString, m_filterCaseSensitivity) == 0) return true; } else { if (aspect->name().contains(m_filterString, m_filterCaseSensitivity)) return true; } //check for the occurrence of the filter string in the names of the parents if ( aspect->parentAspect() ) return this->containsFilterString(aspect->parentAspect()); else return false; //TODO make this optional // //check for the occurrence of the filter string in the names of the children // foreach(const AbstractAspect * child, aspect->children()){ // if ( this->containsFilterString(child) ) // return true; // } } //############################################################################## //################################# SLOTS #################################### //############################################################################## void AspectTreeModel::renameRequestedSlot() { AbstractAspect* aspect = qobject_cast(QObject::sender()); if (aspect) emit renameRequested(modelIndexOfAspect(aspect)); } void AspectTreeModel::aspectSelectedInView(const AbstractAspect* aspect) { if (aspect->hidden()) { //a hidden aspect was selected in the view (e.g. plot title in WorksheetView) //select the parent aspect first, if available AbstractAspect* parent = aspect->parentAspect(); if (parent) emit indexSelected(modelIndexOfAspect(parent)); //emit also this signal, so the GUI can handle this selection. emit hiddenAspectSelected(aspect); } else emit indexSelected(modelIndexOfAspect(aspect)); //deselect the root item when one of the children was selected in the view //in order to avoid multiple selection with the project item (if selected) in the project explorer emit indexDeselected(modelIndexOfAspect(m_root)); } void AspectTreeModel::aspectDeselectedInView(const AbstractAspect* aspect) { if (aspect->hidden()) { AbstractAspect* parent = aspect->parentAspect(); if (parent) emit indexDeselected(modelIndexOfAspect(parent)); } else emit indexDeselected(modelIndexOfAspect(aspect)); } diff --git a/src/backend/core/Project.cpp b/src/backend/core/Project.cpp index f3d51c9b6..9078a91de 100644 --- a/src/backend/core/Project.cpp +++ b/src/backend/core/Project.cpp @@ -1,457 +1,457 @@ /*************************************************************************** File : Project.cpp Project : LabPlot Description : Represents a LabPlot project. -------------------------------------------------------------------- Copyright : (C) 2011-2014 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2007-2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2007 Knut Franke (knut.franke@gmx.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 "backend/core/Project.h" #include "backend/lib/XmlStreamReader.h" #include "backend/datasources/LiveDataSource.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/XYEquationCurve.h" #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h" #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h" #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h" #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h" #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" #include "backend/worksheet/plots/cartesian/XYFitCurve.h" #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" #include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/datapicker/DatapickerCurve.h" #include #include #include #include #include #include #include #include -#include +#include #include /** * \class Project * \brief Represents a project. * \ingroup core * Project represents the root node of all objects created during the runtime of the program. * Manages also the undo stack. */ /** * \enum Project::MdiWindowVisibility * \brief MDI subwindow visibility setting */ /** * \var Project::folderOnly * \brief only show MDI windows corresponding to Parts in the current folder */ /** * \var Project::foldAndSubfolders * \brief show MDI windows corresponding to Parts in the current folder and its subfolders */ /** * \var Project::allMdiWindows * \brief show MDI windows for all Parts in the project simultaneously */ class Project::Private { public: Private() : mdiWindowVisibility(Project::folderOnly), scriptingEngine(0), version(LVERSION), author(QString(qgetenv("USER"))), modificationTime(QDateTime::currentDateTime()), changed(false) { } QUndoStack undo_stack; MdiWindowVisibility mdiWindowVisibility; AbstractScriptingEngine* scriptingEngine; QString fileName; QString version; QString author; QDateTime modificationTime; bool changed; }; Project::Project() : Folder(i18n("Project")), d(new Private()) { //load default values for name, comment and author from config KConfig config; KConfigGroup group = config.group("Project"); d->author = group.readEntry("Author", QString()); //we don't have direct access to the members name and comment //->temporaly disable the undo stack and call the setters setUndoAware(false); setIsLoading(true); setName(group.readEntry("Name", i18n("Project"))); setComment(group.readEntry("Comment", QString())); setUndoAware(true); setIsLoading(false); d->changed = false; // TODO: intelligent engine choosing // Q_ASSERT(ScriptingEngineManager::instance()->engineNames().size() > 0); // QString engine_name = ScriptingEngineManager::instance()->engineNames()[0]; // d->scriptingEngine = ScriptingEngineManager::instance()->engine(engine_name); connect(this, &Project::aspectDescriptionChanged,this, &Project::descriptionChanged); } Project::~Project() { //if the project is being closed and the live data sources still continue reading the data, //the dependend objects (columns, etc.), which are already deleted maybe here, are still being notified about the changes. //->stop reading the live data sources prior to deleting all objects. for (auto* lds : children()) lds->pauseReading(); //if the project is being closed, in Worksheet the scene items are being removed and the selection in the view can change. //don't react on these changes since this can lead crashes (worksheet object is already in the destructor). //->notify all worksheets about the project being closed. for (auto* w : children(AbstractAspect::Recursive)) w->setIsClosing(); d->undo_stack.clear(); delete d; } QUndoStack* Project::undoStack() const { return &d->undo_stack; } QMenu* Project::createContextMenu() { QMenu* menu = new QMenu(); // no remove action from AbstractAspect in the project context menu emit requestProjectContextMenu(menu); return menu; } QMenu* Project::createFolderContextMenu(const Folder* folder) { QMenu* menu = const_cast(folder)->AbstractAspect::createContextMenu(); emit requestFolderContextMenu(folder, menu); return menu; } void Project::setMdiWindowVisibility(MdiWindowVisibility visibility) { d->mdiWindowVisibility = visibility; emit mdiWindowVisibilityChanged(); } Project::MdiWindowVisibility Project::mdiWindowVisibility() const { return d->mdiWindowVisibility; } AbstractScriptingEngine* Project::scriptingEngine() const { return d->scriptingEngine; } CLASS_D_ACCESSOR_IMPL(Project, QString, fileName, FileName, fileName) BASIC_D_ACCESSOR_IMPL(Project, QString, version, Version, version) CLASS_D_ACCESSOR_IMPL(Project, QString, author, Author, author) CLASS_D_ACCESSOR_IMPL(Project, QDateTime, modificationTime, ModificationTime, modificationTime) void Project::setChanged(const bool value) { if (isLoading()) return; if (value) emit changed(); d->changed = value; } bool Project ::hasChanged() const { return d->changed ; } void Project::descriptionChanged(const AbstractAspect* aspect) { if (isLoading()) return; if (this != aspect) return; d->changed = true; emit changed(); } void Project::navigateTo(const QString& path) { emit requestNavigateTo(path); } bool Project::isLabPlotProject(const QString& fileName) { return fileName.endsWith(QStringLiteral(".lml"), Qt::CaseInsensitive) || fileName.endsWith(QStringLiteral(".lml.gz"), Qt::CaseInsensitive) || fileName.endsWith(QStringLiteral(".lml.bz2"), Qt::CaseInsensitive) || fileName.endsWith(QStringLiteral(".lml.xz"), Qt::CaseInsensitive); } QString Project::supportedExtensions() { static const QString extensions = "*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ"; return extensions; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /** * \brief Save as XML */ void Project::save(QXmlStreamWriter* writer) const { //set the version and the modification time to the current values d->version = LVERSION; d->modificationTime = QDateTime::currentDateTime(); writer->setAutoFormatting(true); writer->writeStartDocument(); writer->writeDTD(""); writer->writeStartElement("project"); writer->writeAttribute("version", version()); writer->writeAttribute("fileName", fileName()); writer->writeAttribute("modificationTime", modificationTime().toString("yyyy-dd-MM hh:mm:ss:zzz")); writer->writeAttribute("author", author()); writeBasicAttributes(writer); writeCommentElement(writer); //save all children for (auto* child : children(IncludeHidden)) { writer->writeStartElement("child_aspect"); child->save(writer); writer->writeEndElement(); } //save the state of the views (visible, maximized/minimized/geometry) //and the state of the project explorer (expanded items, currently selected item) emit requestSaveState(writer); writer->writeEndElement(); writer->writeEndDocument(); } bool Project::load(const QString& filename, bool preview) { QIODevice *file; // first try gzip compression, because projects can be gzipped and end with .lml if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive)) file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip")); else // opens filename using file ending file = new KFilterDev(filename); if (file == 0) file = new QFile(filename); if (!file->open(QIODevice::ReadOnly)) { KMessageBox::error(0, i18n("Sorry. Could not open file for reading.")); return false; } char c; bool rc = file->getChar(&c); if (!rc) { KMessageBox::error(0, i18n("The project file is empty."), i18n("Error opening project")); file->close(); delete file; return false; } file->seek(0); //parse XML XmlStreamReader reader(file); setIsLoading(true); rc = this->load(&reader, preview); setIsLoading(false); if (rc == false) { RESET_CURSOR; QString msg_text = reader.errorString(); KMessageBox::error(0, msg_text, i18n("Error when opening the project")); return false; } if (reader.hasWarnings()) { QString msg = i18n("The following problems occurred when loading the project file:\n"); const QStringList& warnings = reader.warningStrings(); for (const auto& str : warnings) msg += str + '\n'; qWarning() << msg; //TODO: show warnings in a kind of "log window" but not in message box // KMessageBox::error(this, msg, i18n("Project loading partly failed")); } file->close(); delete file; return true; } /** * \brief Load from XML */ bool Project::load(XmlStreamReader* reader, bool preview) { while (!(reader->isStartDocument() || reader->atEnd())) reader->readNext(); if(!(reader->atEnd())) { if (!reader->skipToNextTag()) return false; if (reader->name() == "project") { QString version = reader->attributes().value("version").toString(); if(version.isEmpty()) reader->raiseWarning(i18n("Attribute 'version' is missing.")); else d->version = version; if (!readBasicAttributes(reader)) return false; if (!readProjectAttributes(reader)) return false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if(reader->name() == "child_aspect") { if (!readChildAspectElement(reader, preview)) return false; } else if(reader->name() == "state") { //load the state of the views (visible, maximized/minimized/geometry) //and the state of the project explorer (expanded items, currently selected item) emit requestLoadState(reader); } else { reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } } //wait until all columns are decoded from base64-encoded data QThreadPool::globalInstance()->waitForDone(); //everything is read now. //restore the pointer to the data sets (columns) in xy-curves etc. QVector curves = children(AbstractAspect::Recursive); QVector axes = children(AbstractAspect::Recursive); QVector dataPickerCurves = children(AbstractAspect::Recursive); if (!curves.isEmpty() || !axes.isEmpty()) { QVector columns = children(AbstractAspect::Recursive); //XY-curves for (auto* curve : curves) { if (!curve) continue; curve->suppressRetransform(true); XYEquationCurve* equationCurve = dynamic_cast(curve); XYAnalysisCurve* analysisCurve = dynamic_cast(curve); if (equationCurve) { //curves defined by a mathematical equations recalculate their own columns on load again. if (!preview) equationCurve->recalculate(); } else if (analysisCurve) { RESTORE_COLUMN_POINTER(analysisCurve, xDataColumn, XDataColumn); RESTORE_COLUMN_POINTER(analysisCurve, yDataColumn, YDataColumn); XYFitCurve* fitCurve = dynamic_cast(curve); if (fitCurve) { RESTORE_COLUMN_POINTER(fitCurve, xErrorColumn, XErrorColumn); RESTORE_COLUMN_POINTER(fitCurve, yErrorColumn, YErrorColumn); } } else { RESTORE_COLUMN_POINTER(curve, xColumn, XColumn); RESTORE_COLUMN_POINTER(curve, yColumn, YColumn); RESTORE_COLUMN_POINTER(curve, valuesColumn, ValuesColumn); RESTORE_COLUMN_POINTER(curve, xErrorPlusColumn, XErrorPlusColumn); RESTORE_COLUMN_POINTER(curve, xErrorMinusColumn, XErrorMinusColumn); RESTORE_COLUMN_POINTER(curve, yErrorPlusColumn, YErrorPlusColumn); RESTORE_COLUMN_POINTER(curve, yErrorMinusColumn, YErrorMinusColumn); } if (dynamic_cast(curve)) RESTORE_POINTER(dynamic_cast(curve), dataSourceCurve, DataSourceCurve, XYCurve, curves); curve->suppressRetransform(false); } //Axes for (auto* axis : axes) { if (!axis) continue; RESTORE_COLUMN_POINTER(axis, majorTicksColumn, MajorTicksColumn); RESTORE_COLUMN_POINTER(axis, minorTicksColumn, MinorTicksColumn); } for (auto* dataPickerCurve : dataPickerCurves) { if (!dataPickerCurve) continue; RESTORE_COLUMN_POINTER(dataPickerCurve, posXColumn, PosXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, posYColumn, PosYColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaXColumn, PlusDeltaXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaXColumn, MinusDeltaXColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaYColumn, PlusDeltaYColumn); RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaYColumn, MinusDeltaYColumn); } } } else // no project element reader->raiseError(i18n("no project element found")); } else // no start document reader->raiseError(i18n("no valid XML document found")); if (!preview) { for (auto* plot : children(AbstractAspect::Recursive)) { plot->setIsLoading(false); plot->retransform(); } } emit loaded(); return !reader->hasError(); } bool Project::readProjectAttributes(XmlStreamReader* reader) { QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value(reader->namespaceUri().toString(), "fileName").toString(); if(str.isEmpty()) { reader->raiseError(i18n("Project file name missing.")); return false; } d->fileName = str; str = attribs.value(reader->namespaceUri().toString(), "modificationTime").toString(); QDateTime modificationTime = QDateTime::fromString(str, "yyyy-dd-MM hh:mm:ss:zzz"); if(str.isEmpty() || !modificationTime.isValid()) { reader->raiseWarning(i18n("Invalid project modification time. Using current time.")); d->modificationTime = QDateTime::currentDateTime(); } else d->modificationTime = modificationTime; str = attribs.value(reader->namespaceUri().toString(), "author").toString(); d->author = str; return true; } diff --git a/src/backend/core/Workbook.cpp b/src/backend/core/Workbook.cpp index 764af7c92..d612e51ea 100644 --- a/src/backend/core/Workbook.cpp +++ b/src/backend/core/Workbook.cpp @@ -1,229 +1,229 @@ /*************************************************************************** File : Workbook.h Project : LabPlot Description : Aspect providing a container for storing data in form of spreadsheets and matrices -------------------------------------------------------------------- Copyright : (C) 2015 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 "Workbook.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/lib/XmlStreamReader.h" #include "commonfrontend/workbook/WorkbookView.h" #include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h" -#include +#include /** * \class Workbook * \brief Top-level container for Spreadsheet and Matrix. * \ingroup backend */ Workbook::Workbook(AbstractScriptingEngine* engine, const QString& name) : AbstractPart(name), scripted(engine), m_view(nullptr) { } QIcon Workbook::icon() const { return QIcon::fromTheme("labplot-workbook"); } /*! * Returns a new context menu. The caller takes ownership of the menu. */ QMenu* Workbook::createContextMenu() { QMenu *menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } QWidget* Workbook::view() const { if (!m_partView) { m_view = new WorkbookView(const_cast(this)); m_partView = m_view; } return m_partView; } bool Workbook::exportView() const { Spreadsheet* s = currentSpreadsheet(); bool ret = false; if (s) ret = s->exportView(); else { Matrix* m = currentMatrix(); if (m) ret = m->exportView(); } return ret; } bool Workbook::printView() { Spreadsheet* s = currentSpreadsheet(); bool ret = false; if (s) ret = s->printView(); else { Matrix* m = currentMatrix(); if (m) ret = m->printView(); } return ret; } bool Workbook::printPreview() const { Spreadsheet* s = currentSpreadsheet(); bool ret = false; if (s) ret = s->printPreview(); else { Matrix* m = currentMatrix(); if (m) ret = m->printPreview(); } return ret; } Spreadsheet* Workbook::currentSpreadsheet() const { if (!m_view) return nullptr; int index = m_view->currentIndex(); if(index != -1) { AbstractAspect* aspect = child(index); return dynamic_cast(aspect); } return nullptr; } Matrix* Workbook::currentMatrix() const { if (!m_view) return nullptr; int index = reinterpret_cast(m_view)->currentIndex(); if(index != -1) { AbstractAspect* aspect = child(index); return dynamic_cast(aspect); } return nullptr; } /*! this slot is called when a workbook child is selected in the project explorer. emits \c workbookItemSelected() to forward this event to the \c WorkbookView in order to select the corresponding tab. */ void Workbook::childSelected(const AbstractAspect* aspect) { int index = indexOfChild(aspect); emit workbookItemSelected(index); } /*! this slot is called when a worksheet element is deselected in the project explorer. */ void Workbook::childDeselected(const AbstractAspect* aspect) { Q_UNUSED(aspect); } /*! * Emits the signal to select or to deselect the workbook item (spreadsheet or matrix) with the index \c index * in the project explorer, if \c selected=true or \c selected=false, respectively. * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. * This function is called in \c WorkbookView when the current tab was changed */ void Workbook::setChildSelectedInView(int index, bool selected) { AbstractAspect* aspect = child(index); if (selected) { emit childAspectSelectedInView(aspect); //deselect the workbook in the project explorer, if a child (spreadsheet or matrix) was selected. //prevents unwanted multiple selection with workbook if it was selected before. emit childAspectDeselectedInView(this); } else { emit childAspectDeselectedInView(aspect); //deselect also all children that were potentially selected before (columns of a spreadsheet) for (auto* child : aspect->children()) emit childAspectDeselectedInView(child); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Workbook::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "workbook" ); writeBasicAttributes(writer); writeCommentElement(writer); //serialize all children for (auto* aspect : children()) aspect->save(writer); writer->writeEndElement(); // close "workbook" section } //! Load from XML bool Workbook::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "workbook") { reader->raiseError(i18n("no workbook element found")); return false; } if (!readBasicAttributes(reader)) return false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "workbook") break; if (!reader->isStartElement()) continue; if(reader->name() == "spreadsheet") { Spreadsheet* spreadsheet = new Spreadsheet(0, "spreadsheet", true); if (!spreadsheet->load(reader, preview)) { delete spreadsheet; return false; } else addChild(spreadsheet); } else if (reader->name() == "matrix") { Matrix* matrix = new Matrix(0, i18n("matrix"), true); if (!matrix->load(reader, preview)) { delete matrix; return false; } else addChild(matrix); } else { // unknown element reader->raiseWarning(i18n("unknown workbook element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } diff --git a/src/backend/core/abstractcolumncommands.cpp b/src/backend/core/abstractcolumncommands.cpp index 5ccaf2f70..cac63ab58 100644 --- a/src/backend/core/abstractcolumncommands.cpp +++ b/src/backend/core/abstractcolumncommands.cpp @@ -1,258 +1,258 @@ /*************************************************************************** File : abstractcolumncommands.cpp Project : LabPlot Description : Commands to be called by AbstractColumn to modify AbstractColumnPrivate -------------------------------------------------------------------- Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2010 Knut Franke (knut.franke@gmx.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 "abstractcolumncommands.h" -#include +#include /** *************************************************************************** * \class AbstractColumnClearMasksCmd * \brief Clear masking information ** ***************************************************************************/ /** * \var AbstractColumnClearMasksCmd::m_col * \brief The private column data to modify */ /** * \var AbstractColumnClearMasksCmd::m_masking * \brief The old masks */ /** * \var AbstractColumnClearMasksCmd::m_copied * \brief A status flag */ /** * \brief Ctor */ AbstractColumnClearMasksCmd::AbstractColumnClearMasksCmd(AbstractColumnPrivate* col, QUndoCommand* parent) : QUndoCommand( parent ), m_col(col) { setText(i18n("%1: clear masks", col->name())); m_copied = false; } /** * \brief Dtor */ AbstractColumnClearMasksCmd::~AbstractColumnClearMasksCmd() { } /** * \brief Execute the command */ void AbstractColumnClearMasksCmd::redo() { if(!m_copied) { m_masking = m_col->m_masking; m_copied = true; } m_col->m_masking.clear(); emit m_col->owner()->dataChanged(m_col->owner()); } /** * \brief Undo the command */ void AbstractColumnClearMasksCmd::undo() { m_col->m_masking = m_masking; emit m_col->owner()->dataChanged(m_col->owner()); } /** *************************************************************************** * \class AbstractColumnSetMaskedCmd * \brief Mark an interval of rows as masked ** ***************************************************************************/ /** * \var AbstractColumnSetMaskedCmd::m_col * \brief The private AbstractColumn data to modify */ /** * \var AbstractColumnSetMaskedCmd::m_interval * \brief The interval */ /** * \var AbstractColumnSetMaskedCmd::m_masked * \brief Mask/unmask flag */ /** * \var AbstractColumnSetMaskedCmd::m_masking * \brief Interval attribute backup */ /** * \var AbstractColumnSetMaskedCmd::m_copied * \brief A status flag */ /** * \brief Ctor */ AbstractColumnSetMaskedCmd::AbstractColumnSetMaskedCmd(AbstractColumnPrivate * col, const Interval& interval, bool masked, QUndoCommand * parent ) : QUndoCommand( parent ), m_col(col), m_interval(interval), m_masked(masked) { if(masked) setText(i18n("%1: mask cells", col->name())); else setText(i18n("%1: unmask cells", col->name())); m_copied = false; } /** * \brief Dtor */ AbstractColumnSetMaskedCmd::~AbstractColumnSetMaskedCmd() { } /** * \brief Execute the command */ void AbstractColumnSetMaskedCmd::redo() { if(!m_copied) { m_masking = m_col->m_masking; m_copied = true; } m_col->m_masking.setValue(m_interval, m_masked); emit m_col->owner()->dataChanged(m_col->owner()); } /** * \brief Undo the command */ void AbstractColumnSetMaskedCmd::undo() { m_col->m_masking = m_masking; emit m_col->owner()->dataChanged(m_col->owner()); } /** *************************************************************************** * \class AbstractColumnInsertRowsCmd * \brief Insert empty rows into a column ** ***************************************************************************/ /** * \var AbstractColumnInsertRowsCmd::m_col * \brief Private object of AbstractColumn to be modified. */ /** * \var AbstractColumnInsertRowsCmd::m_before * \brief Row number before which to insert the new rows. */ /** * \var AbstractColumnInsertRowsCmd::m_count * \brief Number of rows to be inserted. */ /** * \brief Ctor */ AbstractColumnInsertRowsCmd::AbstractColumnInsertRowsCmd(AbstractColumn *col, int before, int count, QUndoCommand *parent) : QUndoCommand(parent), m_col(col->d), m_before(before), m_count(count) { } /** * \brief Dtor */ AbstractColumnInsertRowsCmd::~AbstractColumnInsertRowsCmd() { } void AbstractColumnInsertRowsCmd::redo() { m_col->m_masking.insertRows(m_before, m_count); } void AbstractColumnInsertRowsCmd::undo() { m_col->m_masking.removeRows(m_before, m_count); } /** *************************************************************************** * \class AbstractColumnRemoveRowsCmd * \brief Remove rows from a column * * See AbstractColumnInsertRowsCmd for a discussion of the design. ** ***************************************************************************/ /** * \var AbstractColumnRemoveRowsCmd::m_col * \brief Private object of AbstractColumn to be modified. */ /** * \var AbstractColumnRemoveRowsCmd::m_first * \brief First row number to be removed. */ /** * \var AbstractColumnRemoveRowsCmd::m_count * \brief Number of rows to be removed. */ /** * \brief Ctor */ AbstractColumnRemoveRowsCmd::AbstractColumnRemoveRowsCmd(AbstractColumn *col, int first, int count, QUndoCommand *parent) : QUndoCommand(parent), m_col(col->d), m_first(first), m_count(count) { } /** * \brief Dtor */ AbstractColumnRemoveRowsCmd::~AbstractColumnRemoveRowsCmd() { } void AbstractColumnRemoveRowsCmd::redo() { m_masking = m_col->m_masking; m_col->m_masking.removeRows(m_first, m_count); } void AbstractColumnRemoveRowsCmd::undo() { m_col->m_masking = m_masking; } diff --git a/src/backend/core/aspectcommands.h b/src/backend/core/aspectcommands.h index 300f4e853..9ff39bb8a 100644 --- a/src/backend/core/aspectcommands.h +++ b/src/backend/core/aspectcommands.h @@ -1,130 +1,130 @@ /*************************************************************************** File : aspectcommands.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2007-2010 by Knut Franke (knut.franke@gmx.de) Copyright : (C) 2007-2009 Tilman Benkert(thzs@gmx.net) Copyright : (C) 2013-2017 by Alexander Semke (alexander.semke@web.de) Description : Undo commands used by AbstractAspect. Only meant to be used within AbstractAspect.cpp ***************************************************************************/ /*************************************************************************** * * * 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 ASPECTCOMMANDS_H #define ASPECTCOMMANDS_H #include "AspectPrivate.h" #include -#include +#include class AspectChildRemoveCmd : public QUndoCommand { public: AspectChildRemoveCmd(AbstractAspectPrivate* target, AbstractAspect* child) : m_target(target), m_child(child), m_index(-1) { // , m_removed(false) { setText(i18n("%1: remove %2", m_target->m_name, m_child->name())); } ~AspectChildRemoveCmd() override { //TODO: this makes labplot crashing on project close/save. // if (m_removed) // delete m_child; } // calling redo transfers ownership of m_child to the undo command void redo() override { AbstractAspect* nextSibling; if (m_child == m_target->m_children.last()) nextSibling = 0; else nextSibling = m_target->m_children.at(m_target->indexOfChild(m_child) + 1); emit m_target->q->aspectAboutToBeRemoved(m_child); m_index = m_target->removeChild(m_child); emit m_target->q->aspectRemoved(m_target->q, nextSibling, m_child); // m_removed = true; } // calling undo transfers ownership of m_child back to its parent aspect void undo() override { Q_ASSERT(m_index != -1); // m_child must be a child of m_target->q emit m_target->q->aspectAboutToBeAdded(m_target->q, 0, m_child); m_target->insertChild(m_index, m_child); emit m_target->q->aspectAdded(m_child); // m_removed = false; } protected: AbstractAspectPrivate* m_target; AbstractAspect* m_child; int m_index; // bool m_removed; }; class AspectChildAddCmd : public AspectChildRemoveCmd { public: AspectChildAddCmd(AbstractAspectPrivate* target, AbstractAspect* child, int index) : AspectChildRemoveCmd(target, child) { setText(i18n("%1: add %2", m_target->m_name, m_child->name())); m_index = index; // m_removed = true; } void redo() override { AspectChildRemoveCmd::undo(); } void undo() override { AspectChildRemoveCmd::redo(); } }; class AspectChildReparentCmd : public QUndoCommand { public: AspectChildReparentCmd(AbstractAspectPrivate* target, AbstractAspectPrivate* new_parent, AbstractAspect* child, int new_index) : m_target(target), m_new_parent(new_parent), m_child(child), m_index(-1), m_new_index(new_index) { setText(i18n("%1: move %2 to %3.", m_target->m_name, m_child->name(), m_new_parent->m_name)); } // calling redo transfers ownership of m_child to the new parent aspect void redo() override { m_index = m_target->removeChild(m_child); m_new_parent->insertChild(m_new_index, m_child); } // calling undo transfers ownership of m_child back to its previous parent aspect void undo() override { Q_ASSERT(m_index != -1); m_new_parent->removeChild(m_child); m_target->insertChild(m_index, m_child); } protected: AbstractAspectPrivate * m_target; AbstractAspectPrivate * m_new_parent; AbstractAspect* m_child; int m_index; int m_new_index; }; #endif diff --git a/src/backend/core/column/Column.cpp b/src/backend/core/column/Column.cpp index 16c0d5280..903460002 100644 --- a/src/backend/core/column/Column.cpp +++ b/src/backend/core/column/Column.cpp @@ -1,1219 +1,1219 @@ /*************************************************************************** File : Column.cpp Project : LabPlot Description : Aspect that manages a column -------------------------------------------------------------------- Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2013-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "backend/core/column/ColumnStringIO.h" #include "backend/core/column/columncommands.h" #include "backend/core/Project.h" #include "backend/lib/XmlStreamReader.h" #include "backend/core/datatypes/String2DateTimeFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h" extern "C" { #include } #include #include #include #include #include -#include +#include /** * \class Column * \brief Aspect that manages a column * * This class represents a column, i.e., (mathematically) a 1D vector of * values with a header. It provides a public reading and (undo aware) writing * interface as defined in AbstractColumn. A column * can have one of currently three data types: double, QString, or * QDateTime. The string representation of the values can differ depending * on the mode of the column. * * Column inherits from AbstractAspect and is intended to be a child * of the corresponding Spreadsheet in the aspect hierarchy. Columns don't * have a view as they are intended to be displayed inside a spreadsheet. */ Column::Column(const QString& name, AbstractColumn::ColumnMode mode) : AbstractColumn(name), d(new ColumnPrivate(this, mode)) { init(); } /** * \brief Common part of ctors */ void Column::init() { m_string_io = new ColumnStringIO(this); d->inputFilter()->input(0, m_string_io); d->outputFilter()->input(0, this); d->inputFilter()->setHidden(true); d->outputFilter()->setHidden(true); addChild(d->inputFilter()); addChild(d->outputFilter()); m_suppressDataChangedSignal = false; m_usedInActionGroup = new QActionGroup(this); connect(m_usedInActionGroup, &QActionGroup::triggered, this, &Column::navigateTo); } Column::~Column() { delete m_string_io; delete d; } QMenu* Column::createContextMenu() { QMenu* menu = AbstractAspect::createContextMenu(); QAction* firstAction = menu->actions().at(1); //add actions available in SpreadsheetView emit requestProjectContextMenu(menu); //"Used in" menu containing all curves where the column is used QMenu* usedInMenu = new QMenu(i18n("Used in")); usedInMenu->setIcon(QIcon::fromTheme("go-next-view")); //remove previously added actions for (auto* action: m_usedInActionGroup->actions()) m_usedInActionGroup->removeAction(action); //add curves where the column is currently in use QVector curves = project()->children(AbstractAspect::Recursive); for (const auto* curve: curves) { bool used = false; const XYAnalysisCurve* analysisCurve = dynamic_cast(curve); if (analysisCurve) { if (analysisCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet && (analysisCurve->xDataColumn() == this || analysisCurve->yDataColumn() == this) ) used = true; } else { if (curve->xColumn() == this || curve->yColumn() == this) used = true; } if (used) { QAction* action = new QAction(curve->icon(), curve->name(), m_usedInActionGroup); action->setData(curve->path()); usedInMenu->addAction(action); } } menu->insertSeparator(firstAction); menu->insertMenu(firstAction, usedInMenu); menu->insertSeparator(firstAction); return menu; } void Column::navigateTo(QAction* action) { project()->navigateTo(action->data().toString()); } /*! * */ void Column::setSuppressDataChangedSignal(bool b) { m_suppressDataChangedSignal = b; } /** * \brief Set the column mode * * This sets the column mode and, if * necessary, converts it to another datatype. */ void Column::setColumnMode(AbstractColumn::ColumnMode mode) { if (mode == columnMode()) return; DEBUG("Column::setColumnMode()"); beginMacro(i18n("%1: change column type", name())); auto* old_input_filter = d->inputFilter(); auto* old_output_filter = d->outputFilter(); exec(new ColumnSetModeCmd(d, mode)); if (d->inputFilter() != old_input_filter) { removeChild(old_input_filter); addChild(d->inputFilter()); d->inputFilter()->input(0, m_string_io); } if (d->outputFilter() != old_output_filter) { removeChild(old_output_filter); addChild(d->outputFilter()); d->outputFilter()->input(0, this); } endMacro(); DEBUG("Column::setColumnMode() DONE"); } void Column::setColumnModeFast(AbstractColumn::ColumnMode mode) { if (mode == columnMode()) return; auto* old_input_filter = d->inputFilter(); auto* old_output_filter = d->outputFilter(); exec(new ColumnSetModeCmd(d, mode)); if (d->inputFilter() != old_input_filter) { removeChild(old_input_filter); addChildFast(d->inputFilter()); d->inputFilter()->input(0, m_string_io); } if (d->outputFilter() != old_output_filter) { removeChild(old_output_filter); addChildFast(d->outputFilter()); d->outputFilter()->input(0, this); } } /** * \brief Copy another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * Use a filter to convert a column to another type. */ bool Column::copy(const AbstractColumn* other) { Q_CHECK_PTR(other); if (other->columnMode() != columnMode()) return false; exec(new ColumnFullCopyCmd(d, other)); return true; } /** * \brief Copies a part of another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * \param other pointer to the column to copy * \param src_start first row to copy in the column to copy * \param dest_start first row to copy in * \param num_rows the number of rows to copy */ bool Column::copy(const AbstractColumn* source, int source_start, int dest_start, int num_rows) { Q_CHECK_PTR(source); if (source->columnMode() != columnMode()) return false; exec(new ColumnPartialCopyCmd(d, source, source_start, dest_start, num_rows)); return true; } /** * \brief Insert some empty (or initialized with zero) rows */ void Column::handleRowInsertion(int before, int count) { AbstractColumn::handleRowInsertion(before, count); exec(new ColumnInsertRowsCmd(d, before, count)); if (!m_suppressDataChangedSignal) emit dataChanged(this); d->statisticsAvailable = false;; d->hasValuesAvailable = false; } /** * \brief Remove 'count' rows starting from row 'first' */ void Column::handleRowRemoval(int first, int count) { AbstractColumn::handleRowRemoval(first, count); exec(new ColumnRemoveRowsCmd(d, first, count)); if (!m_suppressDataChangedSignal) emit dataChanged(this); d->statisticsAvailable = false;; d->hasValuesAvailable = false; } /** * \brief Set the column plot designation */ void Column::setPlotDesignation(AbstractColumn::PlotDesignation pd) { if (pd != plotDesignation()) exec(new ColumnSetPlotDesignationCmd(d, pd)); } /** * \brief Get width */ int Column::width() const { return d->width(); } /** * \brief Set width */ void Column::setWidth(int value) { d->setWidth(value); } /** * \brief Clear the whole column */ void Column::clear() { exec(new ColumnClearCmd(d)); } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //! \name Formula related functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Returns the formula used to generate column values */ QString Column:: formula() const { return d->formula(); } const QStringList& Column::formulaVariableNames() const { return d->formulaVariableNames(); } const QStringList& Column::formulaVariableColumnPathes() const { return d->formulaVariableColumnPathes(); } /** * \brief Sets the formula used to generate column values */ void Column::setFormula(const QString& formula, const QStringList& variableNames, const QStringList& columnPathes) { exec(new ColumnSetGlobalFormulaCmd(d, formula, variableNames, columnPathes)); } /** * \brief Set a formula string for an interval of rows */ void Column::setFormula(const Interval& i, const QString& formula) { exec(new ColumnSetFormulaCmd(d, i, formula)); } /** * \brief Overloaded function for convenience */ void Column::setFormula(int row, const QString& formula) { setFormula(Interval(row, row), formula); } /** * \brief Clear all formulas */ void Column::clearFormulas() { exec(new ColumnClearFormulasCmd(d)); } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //! \name type specific functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Text */ void Column::setTextAt(int row, const QString& new_value) { DEBUG("Column::setTextAt()"); d->statisticsAvailable = false;; exec(new ColumnSetTextCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Text */ void Column::replaceTexts(int first, const QVector& new_values) { DEBUG("Column::replaceTexts()"); if (!new_values.isEmpty()) { //TODO: do we really need this check? d->statisticsAvailable = false;; exec(new ColumnReplaceTextsCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setDateAt(int row, QDate new_value) { d->statisticsAvailable = false;; setDateTimeAt(row, QDateTime(new_value, timeAt(row))); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setTimeAt(int row, QTime new_value) { d->statisticsAvailable = false;; setDateTimeAt(row, QDateTime(dateAt(row), new_value)); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void Column::setDateTimeAt(int row, const QDateTime& new_value) { d->statisticsAvailable = false;; exec(new ColumnSetDateTimeCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is DateTime, Month or Day */ void Column::replaceDateTimes(int first, const QVector& new_values) { if (!new_values.isEmpty()) { d->statisticsAvailable = false;; exec(new ColumnReplaceDateTimesCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Numeric */ void Column::setValueAt(int row, const double new_value) { // DEBUG("Column::setValueAt()"); d->statisticsAvailable = false;; d->hasValuesAvailable = false; exec(new ColumnSetValueCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Numeric */ void Column::replaceValues(int first, const QVector& new_values) { DEBUG("Column::replaceValues()"); if (!new_values.isEmpty()) { d->statisticsAvailable = false;; d->hasValuesAvailable = false; exec(new ColumnReplaceValuesCmd(d, first, new_values)); } } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Integer */ void Column::setIntegerAt(int row, const int new_value) { DEBUG("Column::setIntegerAt()"); d->statisticsAvailable = false;; d->hasValuesAvailable = false; exec(new ColumnSetIntegerCmd(d, row, new_value)); } /** * \brief Replace a range of values * * Use this only when columnMode() is Integer */ void Column::replaceInteger(int first, const QVector& new_values) { DEBUG("Column::replaceInteger()"); if (!new_values.isEmpty()) { d->statisticsAvailable = false;; d->hasValuesAvailable = false; exec(new ColumnReplaceIntegersCmd(d, first, new_values)); } } const Column::ColumnStatistics& Column::statistics() { if (!d->statisticsAvailable) calculateStatistics(); return d->statistics; } void Column::calculateStatistics() { d->statistics = ColumnStatistics(); ColumnStatistics& statistics = d->statistics; // TODO: support other data types? QVector* rowValues = reinterpret_cast*>(data()); size_t notNanCount = 0; double val; double columnSum = 0.0; double columnProduct = 1.0; double columnSumNeg = 0.0; double columnSumSquare = 0.0; statistics.minimum = INFINITY; statistics.maximum = -INFINITY; QMap frequencyOfValues; QVector rowData; rowData.reserve(rowValues->size()); for (int row = 0; row < rowValues->size(); ++row) { val = rowValues->value(row); if (std::isnan(val) || isMasked(row)) continue; if (val < statistics.minimum) statistics.minimum = val; if (val > statistics.maximum) statistics.maximum = val; columnSum+= val; columnSumNeg += (1.0 / val); columnSumSquare += pow(val, 2.0); columnProduct *= val; if (frequencyOfValues.contains(val)) frequencyOfValues.operator [](val)++; else frequencyOfValues.insert(val, 1); ++notNanCount; rowData.push_back(val); } if (notNanCount == 0) { d->statisticsAvailable = true; return; } if (rowData.size() < rowValues->size()) rowData.squeeze(); statistics.arithmeticMean = columnSum / notNanCount; statistics.geometricMean = pow(columnProduct, 1.0 / notNanCount); statistics.harmonicMean = notNanCount / columnSumNeg; statistics.contraharmonicMean = columnSumSquare / columnSum; double columnSumVariance = 0; double columnSumMeanDeviation = 0.0; double columnSumMedianDeviation = 0.0; double sumForCentralMoment_r3 = 0.0; double sumForCentralMoment_r4 = 0.0; gsl_sort(rowData.data(), 1, notNanCount); statistics.median = (notNanCount%2) ? rowData.at((int)((notNanCount-1)/2)) : (rowData.at((int)((notNanCount-1)/2)) + rowData.at((int)(notNanCount/2)))/2.0; QVector absoluteMedianList; absoluteMedianList.reserve((int)notNanCount); absoluteMedianList.resize((int)notNanCount); int idx = 0; for(int row = 0; row < rowValues->size(); ++row) { val = rowValues->value(row); if (std::isnan(val) || isMasked(row) ) continue; columnSumVariance += pow(val - statistics.arithmeticMean, 2.0); sumForCentralMoment_r3 += pow(val - statistics.arithmeticMean, 3.0); sumForCentralMoment_r4 += pow(val - statistics.arithmeticMean, 4.0); columnSumMeanDeviation += fabs( val - statistics.arithmeticMean ); absoluteMedianList[idx] = fabs(val - statistics.median); columnSumMedianDeviation += absoluteMedianList[idx]; idx++; } statistics.meanDeviationAroundMedian = columnSumMedianDeviation / notNanCount; statistics.medianDeviation = (notNanCount%2) ? absoluteMedianList.at((int)((notNanCount-1)/2)) : (absoluteMedianList.at((int)((notNanCount-1)/2)) + absoluteMedianList.at((int)(notNanCount/2)))/2.0; const double centralMoment_r3 = sumForCentralMoment_r3 / notNanCount; const double centralMoment_r4 = sumForCentralMoment_r4 / notNanCount; statistics.variance = columnSumVariance / notNanCount; statistics.standardDeviation = sqrt(statistics.variance); statistics.skewness = centralMoment_r3 / pow(statistics.standardDeviation, 3.0); statistics.kurtosis = (centralMoment_r4 / pow(statistics.standardDeviation, 4.0)) - 3.0; statistics.meanDeviation = columnSumMeanDeviation / notNanCount; double entropy = 0.0; for (const auto& v: frequencyOfValues) { const double frequencyNorm = static_cast(v) / notNanCount; entropy += (frequencyNorm * log2(frequencyNorm)); } statistics.entropy = -entropy; d->statisticsAvailable = true; } ////////////////////////////////////////////////////////////////////////////////////////////// void* Column::data() const { return d->data(); } /*! * return \c true if the column has numeric values, \false otherwise. */ bool Column::hasValues() const { if (columnMode() == AbstractColumn::Numeric) { if (d->hasValuesAvailable) { return d->hasValues; } else { bool foundValues = false; for (int row = 0; row < rowCount(); ++row) { if (!std::isnan(valueAt(row))) { foundValues = true; break; } } d->hasValues = foundValues; d->hasValuesAvailable = true; return d->hasValues; } } else if (columnMode() == AbstractColumn::Integer) return true; //integer column has always valid values else return false; //non-numerical column } //TODO: support all data types /** * \brief Return the content of row 'row'. * * Use this only when columnMode() is Text */ QString Column::textAt(int row) const { return d->textAt(row); } /** * \brief Return the date part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDate Column::dateAt(int row) const { return d->dateAt(row); } /** * \brief Return the time part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QTime Column::timeAt(int row) const { return d->timeAt(row); } /** * \brief Return the QDateTime in row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDateTime Column::dateTimeAt(int row) const { return d->dateTimeAt(row); } /** * \brief Return the double value in row 'row' */ double Column::valueAt(int row) const { return d->valueAt(row); } /** * \brief Return the int value in row 'row' */ int Column::integerAt(int row) const { return d->integerAt(row); } /* * call this function if the data of the column was changed directly via the data()-pointer * and not via the setValueAt() in order to emit the dataChanged-signal. * This is used e.g. in \c XYFitCurvePrivate::recalculate() */ void Column::setChanged() { if (!m_suppressDataChangedSignal) emit dataChanged(this); d->statisticsAvailable = false;; d->hasValuesAvailable = false; } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// /** * \brief Return an icon to be used for decorating the views and spreadsheet column headers */ QIcon Column::icon() const { return iconForMode(columnMode()); } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name serialize/deserialize //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Save the column as XML */ void Column::save(QXmlStreamWriter* writer) const { writer->writeStartElement("column"); writeBasicAttributes(writer); writer->writeAttribute("designation", QString::number(plotDesignation())); writer->writeAttribute("mode", QString::number(columnMode())); writer->writeAttribute("width", QString::number(width())); //save the formula used to generate column values, if available if (!formula().isEmpty() ) { writer->writeStartElement("formula"); writer->writeTextElement("text", formula()); writer->writeStartElement("variableNames"); for (const auto& name: formulaVariableNames()) writer->writeTextElement("name", name); writer->writeEndElement(); writer->writeStartElement("columnPathes"); for (const auto& path: formulaVariableColumnPathes()) writer->writeTextElement("path", path); writer->writeEndElement(); writer->writeEndElement(); } writeCommentElement(writer); writer->writeStartElement("input_filter"); d->inputFilter()->save(writer); writer->writeEndElement(); writer->writeStartElement("output_filter"); d->outputFilter()->save(writer); writer->writeEndElement(); XmlWriteMask(writer); //TODO: formula in cells is not implemented yet // QVector< Interval > formulas = formulaIntervals(); // foreach(const Interval& interval, formulas) { // writer->writeStartElement("formula"); // writer->writeAttribute("start_row", QString::number(interval.start())); // writer->writeAttribute("end_row", QString::number(interval.end())); // writer->writeCharacters(formula(interval.start())); // writer->writeEndElement(); // } int i; switch(columnMode()) { case AbstractColumn::Numeric: { const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData()); size_t size = d->rowCount() * sizeof(double); writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64()); break; } case AbstractColumn::Integer: { const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData()); size_t size = d->rowCount() * sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64()); break; } case AbstractColumn::Text: for (i = 0; i < rowCount(); ++i) { writer->writeStartElement("row"); writer->writeAttribute("index", QString::number(i)); writer->writeCharacters(textAt(i)); writer->writeEndElement(); } break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: for (i = 0; i < rowCount(); ++i) { writer->writeStartElement("row"); writer->writeAttribute("index", QString::number(i)); writer->writeCharacters(dateTimeAt(i).toString("yyyy-dd-MM hh:mm:ss:zzz")); writer->writeEndElement(); } break; } writer->writeEndElement(); // "column" } //TODO: extra header class DecodeColumnTask : public QRunnable { public: DecodeColumnTask(ColumnPrivate* priv, const QString& content) { m_private = priv; m_content = content; }; void run() override { QByteArray bytes = QByteArray::fromBase64(m_content.toAscii()); if (m_private->columnMode() == AbstractColumn::Numeric) { QVector* data = new QVector(bytes.size()/(int)sizeof(double)); memcpy(data->data(), bytes.data(), bytes.size()); m_private->replaceData(data); } else { QVector* data = new QVector(bytes.size()/(int)sizeof(int)); memcpy(data->data(), bytes.data(), bytes.size()); m_private->replaceData(data); } } private: ColumnPrivate* m_private; QString m_content; }; /** * \brief Load the column from XML */ bool Column::load(XmlStreamReader* reader, bool preview) { if (reader->isStartElement() && reader->name() != "column") { reader->raiseError(i18n("no column element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value("designation").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'designation'")); + reader->raiseWarning(attributeWarning.subs("designation").toString()); else d->setPlotDesignation( AbstractColumn::PlotDesignation(str.toInt()) ); str = attribs.value("mode").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'mode'")); + reader->raiseWarning(attributeWarning.subs("mode").toString()); else setColumnModeFast( AbstractColumn::ColumnMode(str.toInt()) ); str = attribs.value("width").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'width'")); + reader->raiseWarning(attributeWarning.subs("width").toString()); else d->setWidth(str.toInt()); // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { bool ret_val = true; if (reader->name() == "comment") ret_val = readCommentElement(reader); else if (reader->name() == "input_filter") ret_val = XmlReadInputFilter(reader); else if (reader->name() == "output_filter") ret_val = XmlReadOutputFilter(reader); else if (reader->name() == "mask") ret_val = XmlReadMask(reader); else if (reader->name() == "formula") ret_val = XmlReadFormula(reader); else if (reader->name() == "row") ret_val = XmlReadRow(reader); else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } if (!ret_val) return false; } if (!preview) { QString content = reader->text().toString().trimmed(); if (!content.isEmpty() && ( columnMode() == AbstractColumn::Numeric || columnMode() == AbstractColumn::Integer)) { DecodeColumnTask* task = new DecodeColumnTask(d, content); QThreadPool::globalInstance()->start(task); } } } return !reader->error(); } /** * \brief Read XML input filter element */ bool Column::XmlReadInputFilter(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() == true && reader->name() == "input_filter"); if (!reader->skipToNextTag()) return false; if (!d->inputFilter()->load(reader, false)) return false; if (!reader->skipToNextTag()) return false; Q_ASSERT(reader->isEndElement() == true && reader->name() == "input_filter"); return true; } /** * \brief Read XML output filter element */ bool Column::XmlReadOutputFilter(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() == true && reader->name() == "output_filter"); if (!reader->skipToNextTag()) return false; if (!d->outputFilter()->load(reader, false)) return false; if (!reader->skipToNextTag()) return false; Q_ASSERT(reader->isEndElement() == true && reader->name() == "output_filter"); return true; } /** * \brief Read XML formula element */ bool Column::XmlReadFormula(XmlStreamReader* reader) { QString formula; QStringList variableNames; QStringList columnPathes; while (reader->readNext()) { if (reader->isEndElement()) break; if (reader->name() == "text") formula = reader->readElementText(); else if (reader->name() == "variableNames") { while (reader->readNext()) { if (reader->name() == "variableNames" && reader->isEndElement()) break; if (reader->isStartElement()) variableNames << reader->readElementText(); } } else if (reader->name() == "columnPathes") { while (reader->readNext()) { if (reader->name() == "columnPathes" && reader->isEndElement()) break; if (reader->isStartElement()) columnPathes << reader->readElementText(); } } } setFormula(formula, variableNames, columnPathes); return true; } //TODO: read cell formula, not implemented yet // bool Column::XmlReadFormula(XmlStreamReader* reader) // { // Q_ASSERT(reader->isStartElement() && reader->name() == "formula"); // // bool ok1, ok2; // int start, end; // start = reader->readAttributeInt("start_row", &ok1); // end = reader->readAttributeInt("end_row", &ok2); // if(!ok1 || !ok2) // { // reader->raiseError(i18n("invalid or missing start or end row")); // return false; // } // setFormula(Interval(start,end), reader->readElementText()); // // return true; // } /** * \brief Read XML row element */ bool Column::XmlReadRow(XmlStreamReader* reader) { Q_ASSERT(reader->isStartElement() == true && reader->name() == "row"); // QXmlStreamAttributes attribs = reader->attributes(); bool ok; int index = reader->readAttributeInt("index", &ok); if (!ok) { reader->raiseError(i18n("invalid or missing row index")); return false; } QString str = reader->readElementText(); switch (columnMode()) { case AbstractColumn::Numeric: { double value = str.toDouble(&ok); if(!ok) { reader->raiseError(i18n("invalid row value")); return false; } setValueAt(index, value); break; } case AbstractColumn::Integer: { int value = str.toInt(&ok); if(!ok) { reader->raiseError(i18n("invalid row value")); return false; } setIntegerAt(index, value); break; } case AbstractColumn::Text: setTextAt(index, str); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: QDateTime date_time = QDateTime::fromString(str,"yyyy-dd-MM hh:mm:ss:zzz"); setDateTimeAt(index, date_time); break; } return true; } //////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////// /** * \brief Return whether the object is read-only */ bool Column::isReadOnly() const { return false; } /** * \brief Return the column mode * * This function is mostly used by spreadsheets but can also be used * by plots. The column mode specifies how to interpret * the values in the column additional to the data type. */ AbstractColumn::ColumnMode Column::columnMode() const { return d->columnMode(); } /** * \brief Return the data vector size * * This returns the number of rows that actually contain data. * Rows beyond this can be masked etc. but should be ignored by filters, * plots etc. */ int Column::rowCount() const { return d->rowCount(); } /** * \brief Return the column plot designation */ AbstractColumn::PlotDesignation Column::plotDesignation() const { return d->plotDesignation(); } AbstractSimpleFilter* Column::outputFilter() const { return d->outputFilter(); } /** * \brief Return a wrapper column object used for String I/O. */ ColumnStringIO* Column::asStringColumn() const { return m_string_io; } //////////////////////////////////////////////////////////////////////////////// //! \name IntervalAttribute related functions //@{ //////////////////////////////////////////////////////////////////////////////// /** * \brief Return the formula associated with row 'row' */ QString Column::formula(int row) const { return d->formula(row); } /** * \brief Return the intervals that have associated formulas * * This can be used to make a list of formulas with their intervals. * Here is some example code: * * \code * QStringList list; * QVector< Interval > intervals = my_column.formulaIntervals(); * foreach(Interval interval, intervals) * list << QString(interval.toString() + ": " + my_column.formula(interval.start())); * \endcode */ QVector< Interval > Column::formulaIntervals() const { return d->formulaIntervals(); } void Column::handleFormatChange() { DEBUG("Column::handleFormatChange() mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnMode())); if (columnMode() == AbstractColumn::DateTime) { auto* input_filter = static_cast(d->inputFilter()); auto* output_filter = static_cast(d->outputFilter()); DEBUG("change format " << input_filter->format().toStdString() << " to " << output_filter->format().toStdString()); input_filter->setFormat(output_filter->format()); } emit aspectDescriptionChanged(this); // the icon for the type changed if (!m_suppressDataChangedSignal) emit dataChanged(this); // all cells must be repainted d->statisticsAvailable = false;; d->hasValuesAvailable = false; DEBUG("Column::handleFormatChange() DONE"); } /*! * calculates the minimal value in the column. * for \c count = 0, the minimum of all elements is returned. * for \c count > 0, the minimum of the first \count elements is returned. * for \c count < 0, the minimum of the last \count elements is returned. */ double Column::minimum(int count) const { double min = INFINITY; if (count == 0 && d->statisticsAvailable) min = const_cast(this)->statistics().minimum; else { ColumnMode mode = columnMode(); int start, end; if (count == 0) { start = 0; end = rowCount(); } else if (count > 0) { start = 0; end = qMin(rowCount(), count); } else { start = qMax(rowCount() + count, 0); end = rowCount(); } switch (mode) { case Numeric: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const double val = vec->at(row); if (std::isnan(val)) continue; if (val < min) min = val; } break; } case Integer: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const int val = vec->at(row); if (val < min) min = val; } break; } case Text: case DateTime: case Day: case Month: default: break; } } return min; } /*! * calculates the maximal value in the column. * for \c count = 0, the maximum of all elements is returned. * for \c count > 0, the maximum of the first \count elements is returned. * for \c count < 0, the maximum of the last \count elements is returned. */ double Column::maximum(int count) const { double max = -INFINITY; if (count == 0 && d->statisticsAvailable) max = const_cast(this)->statistics().maximum; else { ColumnMode mode = columnMode(); int start, end; if (count == 0) { start = 0; end = rowCount(); } else if (count > 0) { start = 0; end = qMin(rowCount(), count); } else { start = qMax(rowCount() + count, 0); end = rowCount(); } switch (mode) { case Numeric: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const double val = vec->at(row); if (std::isnan(val)) continue; if (val > max) max = val; } break; } case Integer: { QVector* vec = static_cast*>(data()); for (int row = start; row < end; ++row) { const int val = vec->at(row); if (val > max) max = val; } break; } case Text: case DateTime: case Day: case Month: default: break; } } return max; } diff --git a/src/backend/core/column/columncommands.cpp b/src/backend/core/column/columncommands.cpp index 3a626d0eb..cd1b4af59 100644 --- a/src/backend/core/column/columncommands.cpp +++ b/src/backend/core/column/columncommands.cpp @@ -1,1200 +1,1200 @@ /*************************************************************************** File : columncommands.cpp Project : AbstractColumn Description : Commands to be called by Column to modify ColumnPrivate -------------------------------------------------------------------- Copyright : (C) 2007,2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2010 by Knut Franke (knut.franke@gmx.de) Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "columncommands.h" #include "ColumnPrivate.h" -#include +#include #include /** *************************************************************************** * \class ColumnSetModeCmd * \brief Set the column mode ** ***************************************************************************/ /** * \var ColumnSetModeCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetModeCmd::m_old_mode * \brief The previous mode */ /** * \var ColumnSetModeCmd::m_mode * \brief The new mode */ /** * \var ColumnSetModeCmd::m_old_data * \brief Pointer to old data */ /** * \var ColumnSetModeCmd::m_new_data * \brief Pointer to new data */ /** * \var ColumnSetModeCmd::m_new_in_filter * \brief The new input filter */ /** * \var ColumnSetModeCmd::m_new_out_filter * \brief The new output filter */ /** * \var ColumnSetModeCmd::m_old_in_filter * \brief The old input filter */ /** * \var ColumnSetModeCmd::m_old_out_filter * \brief The old output filter */ /** * \var ColumnSetModeCmd::m_undone * \brief Flag indicating whether this command has been undone (and not redone). */ /** * \var ColumnSetModeCmd::m_excecuted * \brief Flag indicating whether the command has been executed at least once. */ /** * \brief Ctor */ ColumnSetModeCmd::ColumnSetModeCmd(ColumnPrivate* col, AbstractColumn::ColumnMode mode, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_mode(mode) { setText(i18n("%1: change column type", col->name())); m_undone = false; m_executed = false; } /** * \brief Dtor */ ColumnSetModeCmd::~ColumnSetModeCmd() { if(m_undone) { if(m_new_data != m_old_data) switch (m_mode) { case AbstractColumn::Numeric: delete static_cast*>(m_new_data); break; case AbstractColumn::Integer: delete static_cast*>(m_new_data); break; case AbstractColumn::Text: delete static_cast*>(m_new_data); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: delete static_cast*>(m_new_data); break; } } else { if(m_new_data != m_old_data) switch (m_old_mode) { case AbstractColumn::Numeric: delete static_cast*>(m_old_data); break; case AbstractColumn::Integer: delete static_cast*>(m_old_data); break; case AbstractColumn::Text: delete static_cast*>(m_old_data); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: delete static_cast*>(m_old_data); break; } } } /** * \brief Execute the command */ void ColumnSetModeCmd::redo() { if(!m_executed) { // save old values m_old_mode = m_col->columnMode(); m_old_data = m_col->data(); m_old_in_filter = m_col->inputFilter(); m_old_out_filter = m_col->outputFilter(); // do the conversion m_col->setColumnMode(m_mode); // save new values m_new_data = m_col->data(); m_new_in_filter = m_col->inputFilter(); m_new_out_filter = m_col->outputFilter(); m_executed = true; } else { // set to saved new values m_col->replaceModeData(m_mode, m_new_data, m_new_in_filter, m_new_out_filter); } m_undone = false; } /** * \brief Undo the command */ void ColumnSetModeCmd::undo() { // reset to old values m_col->replaceModeData(m_old_mode, m_old_data, m_old_in_filter, m_old_out_filter); m_undone = true; } /** *************************************************************************** * \class ColumnFullCopyCmd * \brief Copy a complete column ** ***************************************************************************/ /** * \var ColumnFullCopyCmd::m_col * \brief The private column data to modify */ /** * \var ColumnFullCopyCmd::m_src * \brief The column to copy */ /** * \var ColumnFullCopyCmd::m_backup * \brief A backup column */ /** * \var ColumnFullCopyCmd::m_backup_owner * \brief A dummy owner for the backup column * * This is needed because a ColumnPrivate must have an owner. We want access * to the ColumnPrivate object to access its data pointer for fast data * replacement without too much copying. */ /** * \brief Ctor */ ColumnFullCopyCmd::ColumnFullCopyCmd(ColumnPrivate* col, const AbstractColumn* src, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_src(src), m_backup(0), m_backup_owner(0) { setText(i18n("%1: change cell values", col->name())); } /** * \brief Dtor */ ColumnFullCopyCmd::~ColumnFullCopyCmd() { delete m_backup; delete m_backup_owner; } /** * \brief Execute the command */ void ColumnFullCopyCmd::redo() { if(m_backup == 0) { m_backup_owner = new Column("temp", m_src->columnMode()); m_backup = new ColumnPrivate(m_backup_owner, m_src->columnMode()); m_backup->copy(m_col); m_col->copy(m_src); } else { // swap data of orig. column and backup void * data_temp = m_col->data(); m_col->replaceData(m_backup->data()); m_backup->replaceData(data_temp); } } /** * \brief Undo the command */ void ColumnFullCopyCmd::undo() { // swap data of orig. column and backup void * data_temp = m_col->data(); m_col->replaceData(m_backup->data()); m_backup->replaceData(data_temp); } /** *************************************************************************** * \class ColumnPartialCopyCmd * \brief Copy parts of a column ** ***************************************************************************/ /** * \var ColumnPartialCopyCmd::m_col * \brief The private column data to modify */ /** * \var ColumnPartialCopyCmd::m_src * \brief The column to copy */ /** * \var ColumnPartialCopyCmd::m_col_backup * \brief A backup of the original column */ /** * \var ColumnPartialCopyCmd::m_src_backup * \brief A backup of the source column */ /** * \var ColumnPartialCopyCmd::m_col_backup_owner * \brief A dummy owner for the backup column * * This is needed because a ColumnPrivate must have an owner and * we must have a ColumnPrivate object as backup. * Using a Column object as backup would lead to an inifinite loop. */ /** * \var ColumnPartialCopyCmd::m_src_backup_owner * \brief A dummy owner for the source backup column * * This is needed because a ColumnPrivate must have an owner and * we must have a ColumnPrivate object as backup. * Using a Column object as backup would lead to an inifinite loop. */ /** * \var ColumnPartialCopyCmd::m_src_start * \brief Start index in source column */ /** * \var ColumnPartialCopyCmd::m_dest_start * \brief Start index in destination column */ /** * \var ColumnPartialCopyCmd::m_num_rows * \brief Number of rows to copy */ /** * \var ColumnPartialCopyCmd::m_old_row_count * \brief Previous number of rows in the destination column */ /** * \brief Ctor */ ColumnPartialCopyCmd::ColumnPartialCopyCmd(ColumnPrivate* col, const AbstractColumn* src, int src_start, int dest_start, int num_rows, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_src(src), m_col_backup(nullptr), m_src_backup(nullptr), m_col_backup_owner(nullptr), m_src_backup_owner(nullptr), m_src_start(src_start), m_dest_start(dest_start), m_num_rows(num_rows), m_old_row_count(0) { setText(i18n("%1: change cell values", col->name())); } /** * \brief Dtor */ ColumnPartialCopyCmd::~ColumnPartialCopyCmd() { delete m_src_backup; delete m_col_backup; delete m_src_backup_owner; delete m_col_backup_owner; } /** * \brief Execute the command */ void ColumnPartialCopyCmd::redo() { if(m_src_backup == 0) { // copy the relevant rows of source and destination column into backup columns m_src_backup_owner = new Column("temp", m_col->columnMode()); m_src_backup = new ColumnPrivate(m_src_backup_owner, m_col->columnMode()); m_src_backup->copy(m_src, m_src_start, 0, m_num_rows); m_col_backup_owner = new Column("temp", m_col->columnMode()); m_col_backup = new ColumnPrivate(m_col_backup_owner, m_col->columnMode()); m_col_backup->copy(m_col, m_dest_start, 0, m_num_rows); m_old_row_count = m_col->rowCount(); } m_col->copy(m_src_backup, 0, m_dest_start, m_num_rows); } /** * \brief Undo the command */ void ColumnPartialCopyCmd::undo() { m_col->copy(m_col_backup, 0, m_dest_start, m_num_rows); m_col->resizeTo(m_old_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnInsertRowsCmd * \brief Insert empty rows ** ***************************************************************************/ /** * \var ColumnInsertRowsCmd::m_col * \brief The private column data to modify */ /** * \brief Ctor */ ColumnInsertRowsCmd::ColumnInsertRowsCmd(ColumnPrivate* col, int before, int count, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_before(before), m_count(count) { } /** * \brief Execute the command */ void ColumnInsertRowsCmd::redo() { m_col->insertRows(m_before, m_count); } /** * \brief Undo the command */ void ColumnInsertRowsCmd::undo() { m_col->removeRows(m_before, m_count); } /** *************************************************************************** * \class ColumnRemoveRowsCmd * \brief Remove consecutive rows from a column ** ***************************************************************************/ /** * \var ColumnRemoveRowsCmd::m_col * \brief The private column data to modify */ /** * \var ColumnRemoveRowsCmd::m_data_row_count * \brief Number of removed rows actually containing data */ /** * \var ColumnRemoveRowsCmd::m_old_size * \brief The number of rows before the removal */ /** * \var ColumnRemoveRowsCmd::m_backup * \brief Column saving the removed rows */ /** * \var ColumnRemoveRowsCmd::m_backup_owner * \brief A dummy owner for the backup column * * This is needed because a ColumnPrivate must have an owner. We want access * to the ColumnPrivate object to access its data pointer for fast data * replacement without too much copying. */ /** * \var ColumnRemoveRowsCmd::m_formulas * \brief Backup of the formula attribute */ /** * \brief Ctor */ ColumnRemoveRowsCmd::ColumnRemoveRowsCmd(ColumnPrivate* col, int first, int count, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_first(first), m_count(count), m_data_row_count(0), m_old_size(0), m_backup(0), m_backup_owner(nullptr) { } /** * \brief Dtor */ ColumnRemoveRowsCmd::~ColumnRemoveRowsCmd() { delete m_backup; delete m_backup_owner; } /** * \brief Execute the command */ void ColumnRemoveRowsCmd::redo() { if(m_backup == 0) { if(m_first >= m_col->rowCount()) m_data_row_count = 0; else if(m_first + m_count > m_col->rowCount()) m_data_row_count = m_col->rowCount() - m_first; else m_data_row_count = m_count; m_old_size = m_col->rowCount(); m_backup_owner = new Column("temp", m_col->columnMode()); m_backup = new ColumnPrivate(m_backup_owner, m_col->columnMode()); m_backup->copy(m_col, m_first, 0, m_data_row_count); m_formulas = m_col->formulaAttribute(); } m_col->removeRows(m_first, m_count); } /** * \brief Undo the command */ void ColumnRemoveRowsCmd::undo() { m_col->insertRows(m_first, m_count); m_col->copy(m_backup, 0, m_first, m_data_row_count); m_col->resizeTo(m_old_size); m_col->replaceFormulas(m_formulas); } /** *************************************************************************** * \class ColumnSetPlotDesignationCmd * \brief Sets a column's plot designation ** ***************************************************************************/ /** * \var ColumnSetPlotDesignationCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetPlotDesignation::m_new_pd * \brief New plot designation */ /** * \var ColumnSetPlotDesignation::m_old_pd * \brief Old plot designation */ /** * \brief Ctor */ ColumnSetPlotDesignationCmd::ColumnSetPlotDesignationCmd(ColumnPrivate* col, AbstractColumn::PlotDesignation pd, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_new_pd(pd) { setText(i18n("%1: set plot designation", col->name())); } /** * \brief Execute the command */ void ColumnSetPlotDesignationCmd::redo() { m_old_pd = m_col->plotDesignation(); m_col->setPlotDesignation(m_new_pd); } /** * \brief Undo the command */ void ColumnSetPlotDesignationCmd::undo() { m_col->setPlotDesignation(m_old_pd); } /** *************************************************************************** * \class ColumnClearCmd * \brief Clear the column ** ***************************************************************************/ /** * \var ColumnClearCmd::m_col * \brief The private column data to modify */ /** * \var ColumnClearCmd::m_data * \brief Pointer to the old data pointer */ /** * \var ColumnClearCmd::m_empty_data * \brief Pointer to an empty data vector */ /** * \var ColumnClearCmd::m_undone * \brief Status flag */ /** * \brief Ctor */ ColumnClearCmd::ColumnClearCmd(ColumnPrivate* col, QUndoCommand* parent) : QUndoCommand(parent), m_col(col) { setText(i18n("%1: clear column", col->name())); m_empty_data = 0; m_data = 0; m_undone = false; } /** * \brief Dtor */ ColumnClearCmd::~ColumnClearCmd() { if(m_undone) { if (!m_empty_data) return; switch(m_col->columnMode()) { case AbstractColumn::Numeric: delete static_cast*>(m_empty_data); break; case AbstractColumn::Integer: delete static_cast*>(m_empty_data); break; case AbstractColumn::Text: delete static_cast*>(m_empty_data); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: delete static_cast*>(m_empty_data); break; } } else { if (!m_data) return; switch(m_col->columnMode()) { case AbstractColumn::Numeric: delete static_cast*>(m_data); break; case AbstractColumn::Integer: delete static_cast*>(m_data); break; case AbstractColumn::Text: delete static_cast*>(m_data); break; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: delete static_cast*>(m_data); break; } } } /** * \brief Execute the command */ void ColumnClearCmd::redo() { if(!m_empty_data) { const int rowCount = m_col->rowCount(); switch(m_col->columnMode()) { case AbstractColumn::Numeric: { QVector* vec = new QVector(rowCount); m_empty_data = vec; for (int i = 0; i < rowCount; ++i) vec->operator[](i) = NAN; break; } case AbstractColumn::Integer: { QVector* vec = new QVector(rowCount); m_empty_data = vec; for (int i = 0; i < rowCount; ++i) vec->operator[](i) = 0; break; } case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: m_empty_data = new QVector(); for (int i = 0; i < rowCount; ++i) static_cast< QVector*>(m_empty_data)->append(QDateTime()); break; case AbstractColumn::Text: m_empty_data = new QVector(); for (int i = 0; i < rowCount; ++i) static_cast*>(m_empty_data)->append(QString()); break; } m_data = m_col->data(); } m_col->replaceData(m_empty_data); m_undone = false; } /** * \brief Undo the command */ void ColumnClearCmd::undo() { m_col->replaceData(m_data); m_undone = true; } /** *************************************************************************** * \class ColumSetGlobalFormulaCmd * \brief Set the formula for the entire column (global formula) ** ***************************************************************************/ ColumnSetGlobalFormulaCmd::ColumnSetGlobalFormulaCmd(ColumnPrivate* col, const QString& formula, const QStringList& variableNames, const QStringList& variableColumns) : QUndoCommand(), m_col(col), m_newFormula(formula), m_newVariableNames(variableNames), m_newVariableColumnPathes(variableColumns), m_copied(false) { setText(i18n("%1: set formula", col->name())); } void ColumnSetGlobalFormulaCmd::redo() { if(!m_copied) { m_formula = m_col->formula(); m_variableNames = m_col->formulaVariableNames(); m_variableColumnPathes = m_col->formulaVariableColumnPathes(); m_copied = true; } m_col->setFormula(m_newFormula, m_newVariableNames, m_newVariableColumnPathes); } void ColumnSetGlobalFormulaCmd::undo() { m_col->setFormula(m_formula, m_variableNames, m_variableColumnPathes); } /** *************************************************************************** * \class ColumSetFormulaCmd * \brief Set the formula for a given interval ** ***************************************************************************/ /** * \var ColumnSetFormulaCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetFormulaCmd::m_interval * \brief The interval */ /** * \var ColumnSetFormulaCmd::m_formula * \brief The new formula */ /** * \var ColumnSetFormulaCmd::m_formulas * \brief Interval attribute backup */ /** * \var ColumnSetFormulaCmd::m_copied * \brief A status flag */ /** * \brief Ctor */ ColumnSetFormulaCmd::ColumnSetFormulaCmd(ColumnPrivate* col, const Interval& interval, const QString& formula, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_interval(interval), m_newFormula(formula), m_copied(false) { setText(i18n("%1: set cell formula", col->name())); } void ColumnSetFormulaCmd::redo() { if(!m_copied) { m_formulas = m_col->formulaAttribute(); m_copied = true; } m_col->setFormula(m_interval, m_newFormula); } void ColumnSetFormulaCmd::undo() { m_col->replaceFormulas(m_formulas); } /** *************************************************************************** * \class ColumnClearFormulasCmd * \brief Clear all associated formulas ** ***************************************************************************/ /** * \var ColumClearFormulasCmd::m_col * \brief The private column data to modify */ /** * \var ColumnClearFormulasCmd::m_formulas * \brief The old formulas */ /** * \var ColumnClearFormulasCmd::m_copied * \brief A status flag */ /** * \brief Ctor */ ColumnClearFormulasCmd::ColumnClearFormulasCmd(ColumnPrivate* col, QUndoCommand* parent) : QUndoCommand(parent), m_col(col) { setText(i18n("%1: clear all formulas", col->name())); m_copied = false; } /** * \brief Execute the command */ void ColumnClearFormulasCmd::redo() { if(!m_copied) { m_formulas = m_col->formulaAttribute(); m_copied = true; } m_col->clearFormulas(); } /** * \brief Undo the command */ void ColumnClearFormulasCmd::undo() { m_col->replaceFormulas(m_formulas); } /** *************************************************************************** * \class ColumnSetTextCmd * \brief Set the text for a string cell ** ***************************************************************************/ /** * \var ColumnSetTextCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetTextCmd::m_row * \brief The row to modify */ /** * \var ColumnSetTextCmd::m_new_value * \brief The new value */ /** * \var ColumnSetTextCmd::m_old_value * \brief The old value */ /** * \var ColumnSetTextCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnSetTextCmd::ColumnSetTextCmd(ColumnPrivate* col, int row, const QString& new_value, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_row_count(0) { setText(i18n("%1: set text for row %2", col->name(), row)); } /** * \brief Execute the command */ void ColumnSetTextCmd::redo() { m_old_value = m_col->textAt(m_row); m_row_count = m_col->rowCount(); m_col->setTextAt(m_row, m_new_value); } /** * \brief Undo the command */ void ColumnSetTextCmd::undo() { m_col->setTextAt(m_row, m_old_value); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnSetValueCmd * \brief Set the value for a double cell ** ***************************************************************************/ /** * \var ColumnSetValueCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetValueCmd::m_row * \brief The row to modify */ /** * \var ColumnSetValueCmd::m_new_value * \brief The new value */ /** * \var ColumnSetValueCmd::m_old_value * \brief The old value */ /** * \var ColumnSetValueCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnSetValueCmd::ColumnSetValueCmd(ColumnPrivate* col, int row, double new_value, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_old_value(0.0) { setText(i18n("%1: set value for row %2", col->name(), row)); } /** * \brief Execute the command */ void ColumnSetValueCmd::redo() { m_old_value = m_col->valueAt(m_row); m_row_count = m_col->rowCount(); m_col->setValueAt(m_row, m_new_value); } /** * \brief Undo the command */ void ColumnSetValueCmd::undo() { m_col->setValueAt(m_row, m_old_value); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnSetIntegerCmd * \brief Set the value for a int cell ** ***************************************************************************/ ColumnSetIntegerCmd::ColumnSetIntegerCmd(ColumnPrivate* col, int row, int new_value, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_old_value(0), m_row_count(0) { DEBUG("ColumnSetIntegerCmd::ColumnSetIntegerCmd()"); setText(i18n("%1: set value for row %2", col->name(), row)); } /** * \brief Execute the command */ void ColumnSetIntegerCmd::redo() { m_old_value = m_col->integerAt(m_row); m_row_count = m_col->rowCount(); m_col->setIntegerAt(m_row, m_new_value); } /** * \brief Undo the command */ void ColumnSetIntegerCmd::undo() { m_col->setIntegerAt(m_row, m_old_value); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnSetDataTimeCmd * \brief Set the value of a date-time cell ** ***************************************************************************/ /** * \var ColumnSetDateTimeCmd::m_col * \brief The private column data to modify */ /** * \var ColumnSetDateTimeCmd::m_row * \brief The row to modify */ /** * \var ColumnSetDateTimeCmd::m_new_value * \brief The new value */ /** * \var ColumnSetDateTimeCmd::m_old_value * \brief The old value */ /** * \var ColumnSetDateTimeCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnSetDateTimeCmd::ColumnSetDateTimeCmd(ColumnPrivate* col, int row, const QDateTime& new_value, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_row_count(0) { setText(i18n("%1: set value for row %2", col->name(), row)); } /** * \brief Execute the command */ void ColumnSetDateTimeCmd::redo() { m_old_value = m_col->dateTimeAt(m_row); m_row_count = m_col->rowCount(); m_col->setDateTimeAt(m_row, m_new_value); } /** * \brief Undo the command */ void ColumnSetDateTimeCmd::undo() { m_col->setDateTimeAt(m_row, m_old_value); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnReplaceTextsCmd * \brief Replace a range of strings in a string column ** ***************************************************************************/ /** * \var ColumnReplaceTextsCmd::m_col * \brief The private column data to modify */ /** * \var ColumnReplaceTextsCmd::m_first * \brief The first row to replace */ /** * \var ColumnReplaceTextsCmd::m_new_values * \brief The new values */ /** * \var ColumnReplaceTextsCmd::m_old_values * \brief The old values */ /** * \var ColumnReplaceTextsCmd::m_copied * \brief Status flag */ /** * \var ColumnReplaceTextsCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnReplaceTextsCmd::ColumnReplaceTextsCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_copied(false), m_row_count(0) { setText(i18n("%1: replace the texts for rows %2 to %3", col->name(), first, first + new_values.count() - 1)); } /** * \brief Execute the command */ void ColumnReplaceTextsCmd::redo() { if(!m_copied) { m_old_values = static_cast*>(m_col->data())->mid(m_first, m_new_values.count()); m_row_count = m_col->rowCount(); m_copied = true; } m_col->replaceTexts(m_first, m_new_values); } /** * \brief Undo the command */ void ColumnReplaceTextsCmd::undo() { m_col->replaceTexts(m_first, m_old_values); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnReplaceValuesCmd * \brief Replace a range of doubles in a double column ** ***************************************************************************/ /** * \var ColumnReplaceValuesCmd::m_col * \brief The private column data to modify */ /** * \var ColumnReplaceValuesCmd::m_first * \brief The first row to replace */ /** * \var ColumnReplaceValuesCmd::m_new_values * \brief The new values */ /** * \var ColumnReplaceValuesCmd::m_old_values * \brief The old values */ /** * \var ColumnReplaceValuesCmd::m_copied * \brief Status flag */ /** * \var ColumnReplaceValuesCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnReplaceValuesCmd::ColumnReplaceValuesCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_row_count(0) { setText(i18n("%1: replace the values for rows %2 to %3", col->name(), first, first + new_values.count() -1)); m_copied = false; } /** * \brief Execute the command */ void ColumnReplaceValuesCmd::redo() { if(!m_copied) { m_old_values = static_cast*>(m_col->data())->mid(m_first, m_new_values.count()); m_row_count = m_col->rowCount(); m_copied = true; } m_col->replaceValues(m_first, m_new_values); } /** * \brief Undo the command */ void ColumnReplaceValuesCmd::undo() { m_col->replaceValues(m_first, m_old_values); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnReplaceIntegersCmd * \brief Replace a range of integers in a int column ** ***************************************************************************/ ColumnReplaceIntegersCmd::ColumnReplaceIntegersCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_copied(false), m_row_count(0) { setText(i18n("%1: replace the values for rows %2 to %3", col->name(), first, first + new_values.count() -1)); } /** * \brief Execute the command */ void ColumnReplaceIntegersCmd::redo() { if(!m_copied) { m_old_values = static_cast*>(m_col->data())->mid(m_first, m_new_values.count()); m_row_count = m_col->rowCount(); m_copied = true; } m_col->replaceInteger(m_first, m_new_values); } /** * \brief Undo the command */ void ColumnReplaceIntegersCmd::undo() { m_col->replaceInteger(m_first, m_old_values); m_col->resizeTo(m_row_count); m_col->replaceData(m_col->data()); } /** *************************************************************************** * \class ColumnReplaceDateTimesCmd * \brief Replace a range of date-times in a date-time column ** ***************************************************************************/ /** * \var ColumnReplaceDateTimesCmd::m_col * \brief The private column data to modify */ /** * \var ColumnReplaceDateTimesCmd::m_first * \brief The first row to replace */ /** * \var ColumnReplaceDateTimesCmd::m_new_values * \brief The new values */ /** * \var ColumnReplaceDateTimesCmd::m_old_values * \brief The old values */ /** * \var ColumnReplaceDateTimesCmd::m_copied * \brief Status flag */ /** * \var ColumnReplaceDateTimesCmd::m_row_count * \brief The old number of rows */ /** * \brief Ctor */ ColumnReplaceDateTimesCmd::ColumnReplaceDateTimesCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent) : QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_row_count(0) { setText(i18n("%1: replace the values for rows %2 to %3", col->name(), first, first + new_values.count() -1)); m_copied = false; } /** * \brief Execute the command */ void ColumnReplaceDateTimesCmd::redo() { if(!m_copied) { m_old_values = static_cast*>(m_col->data())->mid(m_first, m_new_values.count()); m_row_count = m_col->rowCount(); m_copied = true; } m_col->replaceDateTimes(m_first, m_new_values); } /** * \brief Undo the command */ void ColumnReplaceDateTimesCmd::undo() { m_col->replaceDateTimes(m_first, m_old_values); m_col->replaceData(m_col->data()); m_col->resizeTo(m_row_count); } diff --git a/src/backend/core/datatypes/DateTime2StringFilter.cpp b/src/backend/core/datatypes/DateTime2StringFilter.cpp index add6f5e83..8f58e8015 100644 --- a/src/backend/core/datatypes/DateTime2StringFilter.cpp +++ b/src/backend/core/datatypes/DateTime2StringFilter.cpp @@ -1,108 +1,108 @@ /*************************************************************************** File : DateTime2StringFilter.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2007 by Tilman Benkert (thzs@gmx.net) Copyright : (C) 2007 by Knut Franke (knut.franke@gmx.de) Description : Conversion filter QDateTime -> QString. ***************************************************************************/ /*************************************************************************** * * * 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 "DateTime2StringFilter.h" #include "backend/lib/XmlStreamReader.h" #include #include #include -#include +#include class DateTime2StringFilterSetFormatCmd : public QUndoCommand { public: DateTime2StringFilterSetFormatCmd(DateTime2StringFilter* target, const QString &new_format); void redo() override; void undo() override; private: DateTime2StringFilter* m_target; QString m_other_format; }; void DateTime2StringFilter::setFormat(const QString& format) { exec(new DateTime2StringFilterSetFormatCmd(static_cast(this), format)); } QString DateTime2StringFilter::textAt(int row) const { //DEBUG("DateTime2StringFilter::textAt()"); if (!m_inputs.value(0)) return QString(); QDateTime inputValue = m_inputs.value(0)->dateTimeAt(row); if (!inputValue.isValid()) return QString(); //QDEBUG(" : " << inputValue << " -> " << m_format << " -> " << inputValue.toString(m_format)); return inputValue.toString(m_format); } bool DateTime2StringFilter::inputAcceptable(int, const AbstractColumn *source) { return (source->columnMode() == AbstractColumn::DateTime) || (source->columnMode() == AbstractColumn::Day) || (source->columnMode() == AbstractColumn::Month); } DateTime2StringFilterSetFormatCmd::DateTime2StringFilterSetFormatCmd(DateTime2StringFilter* target, const QString &new_format) : m_target(target), m_other_format(new_format) { if(m_target->parentAspect()) setText(i18n("%1: set date-time format to %2", m_target->parentAspect()->name(), new_format)); else setText(i18n("set date-time format to %1", new_format)); } void DateTime2StringFilterSetFormatCmd::redo() { QString tmp = m_target->m_format; m_target->m_format = m_other_format; m_other_format = tmp; emit m_target->formatChanged(); } void DateTime2StringFilterSetFormatCmd::undo() { redo(); } void DateTime2StringFilter::writeExtraAttributes(QXmlStreamWriter * writer) const { writer->writeAttribute("format", format()); } bool DateTime2StringFilter::load(XmlStreamReader* reader, bool preview) { if (preview) return true; QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value(reader->namespaceUri().toString(), "format").toString(); if (AbstractSimpleFilter::load(reader, preview)) setFormat(str); else return false; return !reader->hasError(); } diff --git a/src/backend/core/datatypes/Double2StringFilter.cpp b/src/backend/core/datatypes/Double2StringFilter.cpp index 76d9ad2ae..c3a464fe6 100644 --- a/src/backend/core/datatypes/Double2StringFilter.cpp +++ b/src/backend/core/datatypes/Double2StringFilter.cpp @@ -1,134 +1,134 @@ /*************************************************************************** File : Double2StringFilter.cpp Project : AbstractColumn -------------------------------------------------------------------- Copyright : (C) 2007 by Knut Franke, Tilman Benkert Email (use @ for *) : knut.franke*gmx.de, thzs@gmx.net Description : Locale-aware conversion filter double -> QString. ***************************************************************************/ /*************************************************************************** * * * 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 "Double2StringFilter.h" #include "backend/lib/XmlStreamReader.h" #include #include -#include +#include class Double2StringFilterSetFormatCmd : public QUndoCommand { public: Double2StringFilterSetFormatCmd(Double2StringFilter* target, char new_format); void redo() override; void undo() override; private: Double2StringFilter* m_target; char m_other_format; }; class Double2StringFilterSetDigitsCmd : public QUndoCommand { public: Double2StringFilterSetDigitsCmd(Double2StringFilter* target, int new_digits); void redo() override; void undo() override; private: Double2StringFilter* m_target; int m_other_digits; }; void Double2StringFilter::writeExtraAttributes(QXmlStreamWriter * writer) const { writer->writeAttribute("format", QString(QChar(numericFormat()))); writer->writeAttribute("digits", QString::number(numDigits())); } bool Double2StringFilter::load(XmlStreamReader* reader, bool preview) { if (preview) return true; QXmlStreamAttributes attribs = reader->attributes(); QString format_str = attribs.value(reader->namespaceUri().toString(), "format").toString(); QString digits_str = attribs.value(reader->namespaceUri().toString(), "digits").toString(); if (AbstractSimpleFilter::load(reader, preview)) { bool ok; int digits = digits_str.toInt(&ok); if ( (format_str.size() != 1) || !ok ) { reader->raiseError(i18n("missing or invalid format attribute")); } else { setNumericFormat( format_str.at(0).toAscii() ); setNumDigits( digits ); } } else return false; return !reader->hasError(); } void Double2StringFilter::setNumericFormat(char format) { exec(new Double2StringFilterSetFormatCmd(this, format)); } void Double2StringFilter::setNumDigits(int digits) { exec(new Double2StringFilterSetDigitsCmd(this, digits)); } Double2StringFilterSetFormatCmd::Double2StringFilterSetFormatCmd(Double2StringFilter* target, char new_format) : m_target(target), m_other_format(new_format) { if(m_target->parentAspect()) setText(i18n("%1: set numeric format to '%2'", m_target->parentAspect()->name(), new_format)); else setText(i18n("set numeric format to '%1'", new_format)); } void Double2StringFilterSetFormatCmd::redo() { char tmp = m_target->m_format; m_target->m_format = m_other_format; m_other_format = tmp; emit m_target->formatChanged(); } void Double2StringFilterSetFormatCmd::undo() { redo(); } Double2StringFilterSetDigitsCmd::Double2StringFilterSetDigitsCmd(Double2StringFilter* target, int new_digits) : m_target(target), m_other_digits(new_digits) { if(m_target->parentAspect()) setText(i18n("%1: set decimal digits to %2", m_target->parentAspect()->name(), new_digits)); else setText(i18n("set decimal digits to %1", new_digits)); } void Double2StringFilterSetDigitsCmd::redo() { int tmp = m_target->m_digits; m_target->m_digits = m_other_digits; m_other_digits = tmp; emit m_target->digitsChanged(); } void Double2StringFilterSetDigitsCmd::undo() { redo(); } diff --git a/src/backend/core/datatypes/String2DateTimeFilter.cpp b/src/backend/core/datatypes/String2DateTimeFilter.cpp index 9ba605d00..7eed6d125 100644 --- a/src/backend/core/datatypes/String2DateTimeFilter.cpp +++ b/src/backend/core/datatypes/String2DateTimeFilter.cpp @@ -1,161 +1,161 @@ /*************************************************************************** File : String2DateTimeFilter.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2007 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2007 Knut Franke (knut.franke@gmx.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) Description : Conversion filter QString -> QDateTime. ***************************************************************************/ /*************************************************************************** * * * 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 "String2DateTimeFilter.h" #include #include "backend/lib/XmlStreamReader.h" #include #include #include #include -#include +#include class String2DateTimeFilterSetFormatCmd : public QUndoCommand { public: String2DateTimeFilterSetFormatCmd(String2DateTimeFilter* target, const QString &new_format); void redo() override; void undo() override; private: String2DateTimeFilter* m_target; QString m_other_format; }; AbstractColumn::ColumnMode String2DateTimeFilter::columnMode() const { return AbstractColumn::DateTime; } QDateTime String2DateTimeFilter::dateTimeAt(int row) const { if (!m_inputs.value(0)) return QDateTime(); QString input_value = m_inputs.value(0)->textAt(row); if (input_value.isEmpty()) return QDateTime(); // first try the selected format string m_format QDateTime result = QDateTime::fromString(input_value, m_format); if(result.isValid()) return result; // fallback: // try other format strings built from date_formats and time_formats // comma and space are valid separators between date and time QStringList strings = input_value.simplified().split(',', QString::SkipEmptyParts); if(strings.size() == 1) strings = strings.at(0).split(' ', QString::SkipEmptyParts); if(strings.size() < 1) return result; // invalid date/time from first attempt QDate date_result; QTime time_result; QString date_string = strings.at(0).trimmed(); QString time_string; if(strings.size() > 1) time_string = strings.at(1).trimmed(); else time_string = date_string; // try to find a valid date for (const auto& format: AbstractColumn::dateFormats()) { date_result = QDate::fromString(date_string, format); if (date_result.isValid()) break; } // try to find a valid time for (const auto& format: AbstractColumn::timeFormats()) { time_result = QTime::fromString(time_string, format); if (time_result.isValid()) break; } if (!date_result.isValid() && time_result.isValid()) date_result.setDate(1900,1,1); // this is what QDateTime does e.g. for QDateTime::fromString("00:00","hh:mm"); else if (date_result.isValid() && !time_result.isValid()) time_result = QTime(0, 0, 0, 0); return QDateTime(date_result, time_result); } QDate String2DateTimeFilter::dateAt(int row) const { return dateTimeAt(row).date(); } QTime String2DateTimeFilter::timeAt(int row) const { return dateTimeAt(row).time(); } bool String2DateTimeFilter::inputAcceptable(int, const AbstractColumn* source) { return source->columnMode() == AbstractColumn::Text; } void String2DateTimeFilter::writeExtraAttributes(QXmlStreamWriter* writer) const { writer->writeAttribute("format", format()); } bool String2DateTimeFilter::load(XmlStreamReader* reader, bool preview) { if (preview) return true; QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value(reader->namespaceUri().toString(), "format").toString(); if (AbstractSimpleFilter::load(reader, preview)) setFormat(str); else return false; return !reader->hasError(); } void String2DateTimeFilter::setFormat(const QString& format) { exec(new String2DateTimeFilterSetFormatCmd(this, format)); } String2DateTimeFilterSetFormatCmd::String2DateTimeFilterSetFormatCmd(String2DateTimeFilter* target, const QString &new_format) : m_target(target), m_other_format(new_format) { if(m_target->parentAspect()) setText(i18n("%1: set date-time format to %2", m_target->parentAspect()->name(), new_format)); else setText(i18n("set date-time format to %1", new_format)); } void String2DateTimeFilterSetFormatCmd::redo() { QString tmp = m_target->m_format; m_target->m_format = m_other_format; m_other_format = tmp; emit m_target->formatChanged(); } void String2DateTimeFilterSetFormatCmd::undo() { redo(); } diff --git a/src/backend/core/plugin/PluginLoader.cpp b/src/backend/core/plugin/PluginLoader.cpp index 3465e32e0..96903e496 100644 --- a/src/backend/core/plugin/PluginLoader.cpp +++ b/src/backend/core/plugin/PluginLoader.cpp @@ -1,114 +1,114 @@ /*************************************************************************** File : PluginLoader.cpp Project : LabPlot/SciDAVis Description : Loader for VersionedPlugins. -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs*gmx.net) (replace * with @ in the email addresses) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/core/plugin/PluginLoader.h" #include "backend/core/interfaces.h" -#include +#include /** * \class PluginLoader * \brief Loader for VersionedPlugins. * * This class wraps a QPluginLoader object to support * custom error/status strings. */ PluginLoader::PluginLoader(const QString &fileName) : m_loader(NULL), m_fileName(fileName) { m_status = NotYetLoaded; m_statusString = i18n("Not yet loaded."); } PluginLoader::~PluginLoader() { unload(); } QString PluginLoader::statusString () const { return m_statusString; } PluginLoader::PluginStatus PluginLoader::status () const { return m_status; } QString PluginLoader::fileName() const { return m_fileName; } QObject *PluginLoader::instance() { return isActive() ? m_loader->instance() : NULL; } bool PluginLoader::isActive() const { return (Active == m_status); } bool PluginLoader::load() { if (!m_loader) m_loader = new QPluginLoader(m_fileName); if (!m_loader->isLoaded()) { if (m_loader->load()) { //TODO // VersionedPlugin *plugin = qobject_cast(m_loader->instance()); // if (plugin) { // int version = plugin->pluginTargetAppVersion(); // QString appName = plugin->pluginTargetAppName(); // if (SciDAVis::appName == appName && // (SciDAVis::version() & 0xFFFF00) == (version & 0xFFFF00)) { // m_statusString = i18n("Plugin '%1' successfully loaded.", m_fileName); // m_status = Active; // } else { // m_statusString = i18n("Plugin '%1' was created for incompatible version: %2 %3.%4.x", // m_fileName, appName, (version & 0xFF0000) >> 16, (version & 0x00FF00) >> 8); // m_status = IncompatibleApp; // } // } else { // m_statusString = i18n("Plugin '%1' is not a %2 plugin.", m_fileName, SciDAVis::appName); // m_status = NoVersionedPlugin; // } } else { m_statusString = m_loader->errorString(); m_status = ErrorFromQt; } } return (Active == m_status); } bool PluginLoader::unload() { if (m_loader && m_loader->isLoaded()) m_loader->unload(); delete m_loader; m_loader = NULL; m_status = NotYetLoaded; m_statusString = i18n("Not yet loaded."); return true; } diff --git a/src/backend/datapicker/Datapicker.cpp b/src/backend/datapicker/Datapicker.cpp index d0f2c6358..225921865 100644 --- a/src/backend/datapicker/Datapicker.cpp +++ b/src/backend/datapicker/Datapicker.cpp @@ -1,398 +1,398 @@ /*************************************************************************** File : Datapicker.cpp Project : LabPlot Description : Datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015-2016 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 "Datapicker.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/datapicker/DatapickerImage.h" #include "backend/lib/XmlStreamReader.h" #include "commonfrontend/datapicker/DatapickerView.h" #include "backend/datapicker/DatapickerCurve.h" #include "backend/datapicker/Transform.h" #include "backend/datapicker/DatapickerPoint.h" #include #include "QIcon" -#include +#include /** * \class Datapicker * \brief Top-level container for DatapickerCurve and DatapickerImage. * \ingroup backend */ Datapicker::Datapicker(AbstractScriptingEngine* engine, const QString& name, const bool loading) : AbstractPart(name), scripted(engine), m_view(nullptr), m_activeCurve(nullptr), m_transform(new Transform()), m_image(nullptr) { connect(this, &Datapicker::aspectAdded, this, &Datapicker::handleAspectAdded); connect(this, &Datapicker::aspectAboutToBeRemoved, this, &Datapicker::handleAspectAboutToBeRemoved); if (!loading) init(); } Datapicker::~Datapicker() { delete m_transform; } void Datapicker::init() { m_image = new DatapickerImage(0, i18n("Plot")); m_image->setHidden(true); setUndoAware(false); addChild(m_image); setUndoAware(true); connect(m_image, &DatapickerImage::statusInfo, this, &Datapicker::statusInfo); } /*! Returns an icon to be used in the project explorer. */ QIcon Datapicker::icon() const { return QIcon::fromTheme("color-picker-black"); } /*! * Returns a new context menu. The caller takes ownership of the menu. */ QMenu* Datapicker::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); m_image->createContextMenu(menu); return menu; } QWidget* Datapicker::view() const { if (!m_partView) { m_view = new DatapickerView(const_cast(this)); m_partView = m_view; } return m_partView; } bool Datapicker::exportView() const { Spreadsheet* s = currentSpreadsheet(); bool ret; if (s) ret = s->exportView(); else ret = m_image->exportView(); return ret; } bool Datapicker::printView() { Spreadsheet* s = currentSpreadsheet(); bool ret; if (s) ret = s->printView(); else ret = m_image->printView(); return ret; } bool Datapicker::printPreview() const { Spreadsheet* s = currentSpreadsheet(); bool ret; if (s) ret = s->printPreview(); else ret = m_image->printPreview(); return ret; } DatapickerCurve* Datapicker::activeCurve() { return m_activeCurve; } Spreadsheet* Datapicker::currentSpreadsheet() const { if (!m_view) return 0; const int index = m_view->currentIndex(); if(index>0) { DatapickerCurve* curve = child(index-1); return curve->child(0); } return 0; } DatapickerImage* Datapicker::image() const { return m_image; } /*! this slot is called when a datapicker child is selected in the project explorer. emits \c datapickerItemSelected() to forward this event to the \c DatapickerView in order to select the corresponding tab. */ void Datapicker::childSelected(const AbstractAspect* aspect) { m_activeCurve = dynamic_cast(const_cast(aspect)); int index = -1; if (m_activeCurve) { //if one of the curves is currently selected, select the image with the plot (the very first child) index = 0; - emit statusInfo(this->name() + ", " + i18n("active curve") + " \"" + m_activeCurve->name() + "\""); + emit statusInfo(i18n("%1, active curve \"%2\"", this->name(), m_activeCurve->name())); emit requestUpdateActions(); } else { const DatapickerCurve* curve = aspect->ancestor(); index= indexOfChild(curve); ++index; //+1 because of the hidden plot image being the first child and shown in the first tab in the view } emit datapickerItemSelected(index); } /*! this slot is called when a worksheet element is deselected in the project explorer. */ void Datapicker::childDeselected(const AbstractAspect* aspect) { Q_UNUSED(aspect); } /*! * Emits the signal to select or to deselect the datapicker item (spreadsheet or image) with the index \c index * in the project explorer, if \c selected=true or \c selected=false, respectively. * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. * This function is called in \c DatapickerView when the current tab was changed */ void Datapicker::setChildSelectedInView(int index, bool selected) { //select/deselect the datapicker itself if the first tab "representing" the plot image and the curves was selected in the view if (index==0) { if (selected) emit childAspectSelectedInView(this); else { emit childAspectDeselectedInView(this); //deselect also all curves (they don't have any tab index in the view) that were potentially selected before for (const auto* curve : children()) emit childAspectDeselectedInView(curve); } return; } --index; //-1 because of the first tab in the view being reserved for the plot image and curves //select/deselect the data spreadhseets QVector spreadsheets = children(AbstractAspect::Recursive); const AbstractAspect* aspect = spreadsheets.at(index); if (selected) { emit childAspectSelectedInView(aspect); //deselect the datapicker in the project explorer, if a child (spreadsheet or image) was selected. //prevents unwanted multiple selection with datapicker if it was selected before. emit childAspectDeselectedInView(this); } else { emit childAspectDeselectedInView(aspect); //deselect also all children that were potentially selected before (columns of a spreadsheet) for (const auto* child : aspect->children()) emit childAspectDeselectedInView(child); } } /*! Selects or deselects the datapicker or its current active curve in the project explorer. This function is called in \c DatapickerImageView. */ void Datapicker::setSelectedInView(const bool b) { if (b) emit childAspectSelectedInView(this); else emit childAspectDeselectedInView(this); } void Datapicker::addNewPoint(const QPointF& pos, AbstractAspect* parentAspect) { QVector childPoints = parentAspect->children(AbstractAspect::IncludeHidden); if (childPoints.isEmpty()) beginMacro(i18n("%1: add new point", parentAspect->name())); else beginMacro(i18n("%1: add new point %2", parentAspect->name(), childPoints.count())); DatapickerPoint* newPoint = new DatapickerPoint(i18n("%1 Point", parentAspect->name())); newPoint->setPosition(pos); newPoint->setHidden(true); parentAspect->addChild(newPoint); newPoint->retransform(); DatapickerCurve* datapickerCurve = dynamic_cast(parentAspect); if (m_image == parentAspect) { DatapickerImage::ReferencePoints points = m_image->axisPoints(); points.scenePos[childPoints.count()].setX(pos.x()); points.scenePos[childPoints.count()].setY(pos.y()); m_image->setAxisPoints(points); } else if (datapickerCurve) { newPoint->initErrorBar(datapickerCurve->curveErrorTypes()); datapickerCurve->updateData(newPoint); } endMacro(); emit requestUpdateActions(); } QVector3D Datapicker::mapSceneToLogical(const QPointF& point) const { return m_transform->mapSceneToLogical(point, m_image->axisPoints()); } QVector3D Datapicker::mapSceneLengthToLogical(const QPointF& point) const { return m_transform->mapSceneLengthToLogical(point, m_image->axisPoints()); } void Datapicker::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const DatapickerCurve* curve = qobject_cast(aspect); if (curve) { //clear scene QVector childPoints = curve->children(IncludeHidden); for (auto* point : childPoints) handleChildAspectAboutToBeRemoved(point); if (curve==m_activeCurve) { m_activeCurve = 0; emit statusInfo(""); } } else handleChildAspectAboutToBeRemoved(aspect); emit requestUpdateActions(); } void Datapicker::handleAspectAdded(const AbstractAspect* aspect) { const DatapickerPoint* addedPoint = qobject_cast(aspect); const DatapickerCurve* curve = qobject_cast(aspect); if (addedPoint) handleChildAspectAdded(addedPoint); else if (curve) { QVector childPoints = curve->children(IncludeHidden); for (auto* point : childPoints) handleChildAspectAdded(point); } else return; qreal zVal = 0; QVector childPoints = m_image->children(IncludeHidden); for (auto* point : childPoints) point->graphicsItem()->setZValue(zVal++); for (const auto* curve : children()) { for (auto* point : curve->children(IncludeHidden)) point->graphicsItem()->setZValue(zVal++); } emit requestUpdateActions(); } void Datapicker::handleChildAspectAboutToBeRemoved(const AbstractAspect* aspect) { const DatapickerPoint *removedPoint = qobject_cast(aspect); if (removedPoint) { QGraphicsItem *item = removedPoint->graphicsItem(); Q_ASSERT(item != NULL); Q_ASSERT(m_image != NULL); m_image->scene()->removeItem(item); } } void Datapicker::handleChildAspectAdded(const AbstractAspect* aspect) { const DatapickerPoint* addedPoint = qobject_cast(aspect); if (addedPoint) { QGraphicsItem *item = addedPoint->graphicsItem(); Q_ASSERT(item != NULL); Q_ASSERT(m_image != NULL); m_image->scene()->addItem(item); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Datapicker::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "datapicker" ); writeBasicAttributes(writer); writeCommentElement(writer); //serialize all children for (auto* child : children(IncludeHidden)) child->save(writer); writer->writeEndElement(); // close "datapicker" section } //! Load from XML bool Datapicker::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "datapicker") { reader->raiseError(i18n("no datapicker element found")); return false; } if (!readBasicAttributes(reader)) return false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "datapicker") break; if (!reader->isStartElement()) continue; if (reader->name() == "datapickerImage") { DatapickerImage* plot = new DatapickerImage(0, i18n("Plot"), true); if (!plot->load(reader, preview)) { delete plot; return false; } else { plot->setHidden(true); addChild(plot); m_image = plot; } } else if (reader->name() == "datapickerCurve") { DatapickerCurve* curve = new DatapickerCurve(""); if (!curve->load(reader, preview)) { delete curve; return false; } else addChild(curve); } else { // unknown element reader->raiseWarning(i18n("unknown datapicker element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } for (auto* aspect : children(IncludeHidden)) { for (auto* point : aspect->children(IncludeHidden)) handleAspectAdded(point); } return true; } diff --git a/src/backend/datapicker/DatapickerCurve.cpp b/src/backend/datapicker/DatapickerCurve.cpp index 6261321a5..685ca9a94 100644 --- a/src/backend/datapicker/DatapickerCurve.cpp +++ b/src/backend/datapicker/DatapickerCurve.cpp @@ -1,637 +1,638 @@ /*************************************************************************** File : DatapickerCurve.cpp Project : LabPlot Description : container for Curve-Point and Datasheet/Spreadsheet of datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@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 "DatapickerCurve.h" #include "backend/datapicker/DatapickerCurvePrivate.h" #include "backend/datapicker/Datapicker.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/commandtemplates.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/datapicker/DatapickerPoint.h" #include #include -#include +#include +#include #include /** * \class DatapickerCurve * \brief Top-level container for Curve-Point and Datasheet/Spreadsheet of datapicker. * \ingroup backend */ DatapickerCurve::DatapickerCurve(const QString &name) : AbstractAspect(name), d_ptr(new DatapickerCurvePrivate(this)) { init(); } DatapickerCurve::DatapickerCurve(const QString &name, DatapickerCurvePrivate *dd) : AbstractAspect(name), d_ptr(dd) { init(); } DatapickerCurve::~DatapickerCurve() { delete d_ptr; } void DatapickerCurve::init() { Q_D(DatapickerCurve); KConfig config; KConfigGroup group; group = config.group("DatapickerCurve"); d->posXColumn = NULL; d->posYColumn = NULL; d->posZColumn = NULL; d->plusDeltaXColumn = NULL; d->minusDeltaXColumn = NULL; d->plusDeltaYColumn = NULL; d->minusDeltaYColumn = NULL; d->curveErrorTypes.x = (ErrorType) group.readEntry("CurveErrorType_X", (int) NoError); d->curveErrorTypes.y = (ErrorType) group.readEntry("CurveErrorType_X", (int) NoError); // point properties d->pointStyle = (Symbol::Style)group.readEntry("PointStyle", (int)Symbol::Cross); d->pointSize = group.readEntry("Size", Worksheet::convertToSceneUnits(7, Worksheet::Point)); d->pointRotationAngle = group.readEntry("Rotation", 0.0); d->pointOpacity = group.readEntry("Opacity", 1.0); d->pointBrush.setStyle( (Qt::BrushStyle)group.readEntry("FillingStyle", (int)Qt::NoBrush) ); d->pointBrush.setColor( group.readEntry("FillingColor", QColor(Qt::black)) ); d->pointPen.setStyle( (Qt::PenStyle)group.readEntry("BorderStyle", (int)Qt::SolidLine) ); d->pointPen.setColor( group.readEntry("BorderColor", QColor(Qt::red)) ); d->pointPen.setWidthF( group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Point)) ); d->pointErrorBarSize = group.readEntry("ErrorBarSize", Worksheet::convertToSceneUnits(8, Worksheet::Point)); d->pointErrorBarBrush.setStyle( (Qt::BrushStyle)group.readEntry("ErrorBarFillingStyle", (int)Qt::NoBrush) ); d->pointErrorBarBrush.setColor( group.readEntry("ErrorBarFillingColor", QColor(Qt::black)) ); d->pointErrorBarPen.setStyle( (Qt::PenStyle)group.readEntry("ErrorBarBorderStyle", (int)Qt::SolidLine) ); d->pointErrorBarPen.setColor( group.readEntry("ErrorBarBorderColor", QColor(Qt::black)) ); d->pointErrorBarPen.setWidthF( group.readEntry("ErrorBarBorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Point)) ); d->pointVisibility = group.readEntry("PointVisibility", true); this->initAction(); } void DatapickerCurve::initAction() { updateDatasheetAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Update Spreadsheet"), this); connect(updateDatasheetAction, &QAction::triggered, this, &DatapickerCurve::updateDatasheet); } /*! Returns an icon to be used in the project explorer. */ QIcon DatapickerCurve::icon() const { return QIcon::fromTheme("labplot-xy-curve"); } /*! Return a new context menu */ QMenu* DatapickerCurve::createContextMenu() { QMenu *menu = AbstractAspect::createContextMenu(); Q_ASSERT(menu); QAction* firstAction = 0; if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, updateDatasheetAction); return menu; } Column* DatapickerCurve::appendColumn(const QString& name) { Column* col = new Column(i18n("Column"), AbstractColumn::Numeric); col->insertRows(0, m_datasheet->rowCount()); col->setName(name); m_datasheet->addChild(col); return col; } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(DatapickerCurve, DatapickerCurve::Errors, curveErrorTypes, curveErrorTypes) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, Symbol::Style, pointStyle, pointStyle) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointOpacity, pointOpacity) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointRotationAngle, pointRotationAngle) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointSize, pointSize) CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QBrush, pointBrush, pointBrush) CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QPen, pointPen, pointPen) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, qreal, pointErrorBarSize, pointErrorBarSize) CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QBrush, pointErrorBarBrush, pointErrorBarBrush) CLASS_SHARED_D_READER_IMPL(DatapickerCurve, QPen, pointErrorBarPen, pointErrorBarPen) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, bool, pointVisibility, pointVisibility) BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posXColumn, posXColumn) QString& DatapickerCurve::posXColumnPath() const { return d_ptr->posXColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posYColumn, posYColumn) QString& DatapickerCurve::posYColumnPath() const { return d_ptr->posYColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, posZColumn, posZColumn) QString& DatapickerCurve::posZColumnPath() const { return d_ptr->posZColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, plusDeltaXColumn, plusDeltaXColumn) QString& DatapickerCurve::plusDeltaXColumnPath() const { return d_ptr->plusDeltaXColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, minusDeltaXColumn, minusDeltaXColumn) QString& DatapickerCurve::minusDeltaXColumnPath() const { return d_ptr->minusDeltaXColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, plusDeltaYColumn, plusDeltaYColumn) QString& DatapickerCurve::plusDeltaYColumnPath() const { return d_ptr->plusDeltaYColumnPath; } BASIC_SHARED_D_READER_IMPL(DatapickerCurve, AbstractColumn*, minusDeltaYColumn, minusDeltaYColumn) QString& DatapickerCurve::minusDeltaYColumnPath() const { return d_ptr->minusDeltaYColumnPath; } //############################################################################## //######################### setter methods ################################### //############################################################################## void DatapickerCurve::addDatasheet(DatapickerImage::GraphType type) { Q_D(DatapickerCurve); m_datasheet = new Spreadsheet(0, i18n("Data")); addChild(m_datasheet); QString xLabel('x'); QString yLabel('y'); if (type == DatapickerImage::PolarInDegree) { xLabel = QLatin1String("r"); yLabel = QLatin1String("y(deg)"); } else if (type == DatapickerImage::PolarInRadians) { xLabel = QLatin1String("r"); yLabel = QLatin1String("y(rad)"); } else if (type == DatapickerImage::LogarithmicX) { xLabel = QLatin1String("log(x)"); yLabel = QLatin1String("y"); } else if (type == DatapickerImage::LogarithmicY) { xLabel = QLatin1String("x"); yLabel = QLatin1String("log(y)"); } if (type == DatapickerImage::Ternary) d->posZColumn = appendColumn(i18n("c")); d->posXColumn = m_datasheet->column(0); d->posXColumn->setName(xLabel); d->posYColumn = m_datasheet->column(1); d->posYColumn->setName(yLabel); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetCurveErrorTypes, DatapickerCurve::Errors, curveErrorTypes) void DatapickerCurve::setCurveErrorTypes(const DatapickerCurve::Errors errors) { Q_D(DatapickerCurve); if (d->curveErrorTypes.x != errors.x || d->curveErrorTypes.y != errors.y) { beginMacro(i18n("%1: set xy-error type", name())); - exec(new DatapickerCurveSetCurveErrorTypesCmd(d, errors, i18n("%1: set xy-error type"))); + exec(new DatapickerCurveSetCurveErrorTypesCmd(d, errors, ki18n("%1: set xy-error type"))); if ( errors.x != NoError && !d->plusDeltaXColumn ) setPlusDeltaXColumn(appendColumn(QLatin1String("+delta_x"))); else if ( d->plusDeltaXColumn && errors.x == NoError ) { d->plusDeltaXColumn->remove(); d->plusDeltaXColumn = 0; } if ( errors.x == AsymmetricError && !d->minusDeltaXColumn ) setMinusDeltaXColumn(appendColumn(QLatin1String("-delta_x"))); else if ( d->minusDeltaXColumn && errors.x != AsymmetricError ) { d->minusDeltaXColumn->remove(); d->minusDeltaXColumn = 0; } if ( errors.y != NoError && !d->plusDeltaYColumn ) setPlusDeltaYColumn(appendColumn(QLatin1String("+delta_y"))); else if ( d->plusDeltaYColumn && errors.y == NoError ) { d->plusDeltaYColumn->remove(); d->plusDeltaYColumn = 0; } if ( errors.y == AsymmetricError && !d->minusDeltaYColumn ) setMinusDeltaYColumn(appendColumn(QLatin1String("-delta_y"))); else if ( d->minusDeltaYColumn && errors.y != AsymmetricError ) { d->minusDeltaYColumn->remove(); d->minusDeltaYColumn = 0; } endMacro(); } } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosXColumn, AbstractColumn*, posXColumn) void DatapickerCurve::setPosXColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->posXColumn != column) - exec(new DatapickerCurveSetPosXColumnCmd(d, column, i18n("%1: set position X column"))); + exec(new DatapickerCurveSetPosXColumnCmd(d, column, ki18n("%1: set position X column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosYColumn, AbstractColumn*, posYColumn) void DatapickerCurve::setPosYColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->posYColumn != column) - exec(new DatapickerCurveSetPosYColumnCmd(d, column, i18n("%1: set position Y column"))); + exec(new DatapickerCurveSetPosYColumnCmd(d, column, ki18n("%1: set position Y column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPosZColumn, AbstractColumn*, posZColumn) void DatapickerCurve::setPosZColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->posZColumn != column) - exec(new DatapickerCurveSetPosZColumnCmd(d, column, i18n("%1: set position Z column"))); + exec(new DatapickerCurveSetPosZColumnCmd(d, column, ki18n("%1: set position Z column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPlusDeltaXColumn, AbstractColumn*, plusDeltaXColumn) void DatapickerCurve::setPlusDeltaXColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->plusDeltaXColumn != column) - exec(new DatapickerCurveSetPlusDeltaXColumnCmd(d, column, i18n("%1: set +delta_X column"))); + exec(new DatapickerCurveSetPlusDeltaXColumnCmd(d, column, ki18n("%1: set +delta_X column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetMinusDeltaXColumn, AbstractColumn*, minusDeltaXColumn) void DatapickerCurve::setMinusDeltaXColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->minusDeltaXColumn != column) - exec(new DatapickerCurveSetMinusDeltaXColumnCmd(d, column, i18n("%1: set -delta_X column"))); + exec(new DatapickerCurveSetMinusDeltaXColumnCmd(d, column, ki18n("%1: set -delta_X column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetPlusDeltaYColumn, AbstractColumn*, plusDeltaYColumn) void DatapickerCurve::setPlusDeltaYColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->plusDeltaYColumn != column) - exec(new DatapickerCurveSetPlusDeltaYColumnCmd(d, column, i18n("%1: set +delta_Y column"))); + exec(new DatapickerCurveSetPlusDeltaYColumnCmd(d, column, ki18n("%1: set +delta_Y column"))); } STD_SETTER_CMD_IMPL_S(DatapickerCurve, SetMinusDeltaYColumn, AbstractColumn*, minusDeltaYColumn) void DatapickerCurve::setMinusDeltaYColumn(AbstractColumn* column) { Q_D(DatapickerCurve); if (d->minusDeltaYColumn != column) - exec(new DatapickerCurveSetMinusDeltaYColumnCmd(d, column, i18n("%1: set -delta_Y column"))); + exec(new DatapickerCurveSetMinusDeltaYColumnCmd(d, column, ki18n("%1: set -delta_Y column"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointStyle, Symbol::Style, pointStyle, retransform) void DatapickerCurve::setPointStyle(Symbol::Style newStyle) { Q_D(DatapickerCurve); if (newStyle != d->pointStyle) - exec(new DatapickerCurveSetPointStyleCmd(d, newStyle, i18n("%1: set point's style"))); + exec(new DatapickerCurveSetPointStyleCmd(d, newStyle, ki18n("%1: set point's style"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointSize, qreal, pointSize, retransform) void DatapickerCurve::setPointSize(qreal value) { Q_D(DatapickerCurve); if (!qFuzzyCompare(1 + value, 1 + d->pointSize)) - exec(new DatapickerCurveSetPointSizeCmd(d, value, i18n("%1: set point's size"))); + exec(new DatapickerCurveSetPointSizeCmd(d, value, ki18n("%1: set point's size"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointRotationAngle, qreal, pointRotationAngle, retransform) void DatapickerCurve::setPointRotationAngle(qreal angle) { Q_D(DatapickerCurve); if (!qFuzzyCompare(1 + angle, 1 + d->pointRotationAngle)) - exec(new DatapickerCurveSetPointRotationAngleCmd(d, angle, i18n("%1: rotate point"))); + exec(new DatapickerCurveSetPointRotationAngleCmd(d, angle, ki18n("%1: rotate point"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointBrush, QBrush, pointBrush, retransform) void DatapickerCurve::setPointBrush(const QBrush& newBrush) { Q_D(DatapickerCurve); if (newBrush != d->pointBrush) - exec(new DatapickerCurveSetPointBrushCmd(d, newBrush, i18n("%1: set point's filling"))); + exec(new DatapickerCurveSetPointBrushCmd(d, newBrush, ki18n("%1: set point's filling"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointPen, QPen, pointPen, retransform) void DatapickerCurve::setPointPen(const QPen &newPen) { Q_D(DatapickerCurve); if (newPen != d->pointPen) - exec(new DatapickerCurveSetPointPenCmd(d, newPen, i18n("%1: set outline style"))); + exec(new DatapickerCurveSetPointPenCmd(d, newPen, ki18n("%1: set outline style"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointOpacity, qreal, pointOpacity, retransform) void DatapickerCurve::setPointOpacity(qreal newOpacity) { Q_D(DatapickerCurve); if (newOpacity != d->pointOpacity) - exec(new DatapickerCurveSetPointOpacityCmd(d, newOpacity, i18n("%1: set point's opacity"))); + exec(new DatapickerCurveSetPointOpacityCmd(d, newOpacity, ki18n("%1: set point's opacity"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarSize, qreal, pointErrorBarSize, retransform) void DatapickerCurve::setPointErrorBarSize(qreal size) { Q_D(DatapickerCurve); if (size != d->pointErrorBarSize) - exec(new DatapickerCurveSetPointErrorBarSizeCmd(d, size, i18n("%1: set error bar size"))); + exec(new DatapickerCurveSetPointErrorBarSizeCmd(d, size, ki18n("%1: set error bar size"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarBrush, QBrush, pointErrorBarBrush, retransform) void DatapickerCurve::setPointErrorBarBrush(const QBrush &brush) { Q_D(DatapickerCurve); if (brush != d->pointErrorBarBrush) - exec(new DatapickerCurveSetPointErrorBarBrushCmd(d, brush, i18n("%1: set error bar filling"))); + exec(new DatapickerCurveSetPointErrorBarBrushCmd(d, brush, ki18n("%1: set error bar filling"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointErrorBarPen, QPen, pointErrorBarPen, retransform) void DatapickerCurve::setPointErrorBarPen(const QPen &pen) { Q_D(DatapickerCurve); if (pen != d->pointErrorBarPen) - exec(new DatapickerCurveSetPointErrorBarPenCmd(d, pen, i18n("%1: set error bar outline style"))); + exec(new DatapickerCurveSetPointErrorBarPenCmd(d, pen, ki18n("%1: set error bar outline style"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerCurve, SetPointVisibility, bool, pointVisibility, retransform) void DatapickerCurve::setPointVisibility(bool on) { Q_D(DatapickerCurve); if (on != d->pointVisibility) - exec(new DatapickerCurveSetPointVisibilityCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new DatapickerCurveSetPointVisibilityCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } void DatapickerCurve::setPrinting(bool on) { for (auto* point : children(IncludeHidden)) point->setPrinting(on); } /*! Selects or deselects the Datapicker/Curve in the project explorer. This function is called in \c DatapickerImageView. */ void DatapickerCurve::setSelectedInView(bool b) { if (b) emit childAspectSelectedInView(this); else emit childAspectDeselectedInView(this); } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void DatapickerCurve::updateDatasheet() { beginMacro(i18n("%1: update datasheet", name())); for (auto* point : children(IncludeHidden)) updateData(point); endMacro(); } /*! Update datasheet for corresponding curve-point, it is called every time whenever there is any change in position of curve-point or its error-bar so keep it undo unaware no need to create extra entry in undo stack */ void DatapickerCurve::updateData(const DatapickerPoint* point) { Q_D(DatapickerCurve); Datapicker* datapicker = dynamic_cast(parentAspect()); if (!datapicker) return; int row = indexOfChild(point, AbstractAspect::IncludeHidden); QVector3D data = datapicker->mapSceneToLogical(point->position()); if(d->posXColumn) d->posXColumn->setValueAt(row, data.x()); if(d->posYColumn) d->posYColumn->setValueAt(row, data.y()); if(d->posZColumn) d->posZColumn->setValueAt(row, data.y()); if (d->plusDeltaXColumn) { data = datapicker->mapSceneLengthToLogical(QPointF(point->plusDeltaXPos().x(), 0)); d->plusDeltaXColumn->setValueAt(row, qAbs(data.x())); } if (d->minusDeltaXColumn) { data = datapicker->mapSceneLengthToLogical(QPointF(point->minusDeltaXPos().x(), 0)); d->minusDeltaXColumn->setValueAt(row, qAbs(data.x())); } if (d->plusDeltaYColumn) { data = datapicker->mapSceneLengthToLogical(QPointF(0, point->plusDeltaYPos().y())); d->plusDeltaYColumn->setValueAt(row, qAbs(data.y())); } if (d->minusDeltaYColumn) { data = datapicker->mapSceneLengthToLogical(QPointF(0, point->minusDeltaYPos().y())); d->minusDeltaYColumn->setValueAt(row, qAbs(data.y())); } } //############################################################################## //####################### Private implementation ############################### //############################################################################## DatapickerCurvePrivate::DatapickerCurvePrivate(DatapickerCurve *curve) : q(curve) { } QString DatapickerCurvePrivate::name() const { return q->name(); } void DatapickerCurvePrivate::retransform() { QVector childrenPoints = q->children(AbstractAspect::IncludeHidden); for (auto* point : childrenPoints) point->retransform(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void DatapickerCurve::save(QXmlStreamWriter* writer) const { Q_D(const DatapickerCurve); writer->writeStartElement( "datapickerCurve" ); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement( "general" ); WRITE_COLUMN(d->posXColumn, posXColumn); WRITE_COLUMN(d->posYColumn, posYColumn); WRITE_COLUMN(d->posZColumn, posZColumn); WRITE_COLUMN(d->plusDeltaXColumn, plusDeltaXColumn); WRITE_COLUMN(d->minusDeltaXColumn, minusDeltaXColumn); WRITE_COLUMN(d->plusDeltaYColumn, plusDeltaYColumn); WRITE_COLUMN(d->minusDeltaYColumn, minusDeltaYColumn); writer->writeAttribute( "curveErrorType_X", QString::number(d->curveErrorTypes.x) ); writer->writeAttribute( "curveErrorType_Y", QString::number(d->curveErrorTypes.y) ); writer->writeEndElement(); //symbol properties writer->writeStartElement( "symbolProperties" ); writer->writeAttribute( "pointRotationAngle", QString::number(d->pointRotationAngle) ); writer->writeAttribute( "pointOpacity", QString::number(d->pointOpacity) ); writer->writeAttribute( "pointSize", QString::number(d->pointSize) ); writer->writeAttribute( "pointStyle", QString::number(d->pointStyle) ); writer->writeAttribute( "pointVisibility", QString::number(d->pointVisibility) ); WRITE_QBRUSH(d->pointBrush); WRITE_QPEN(d->pointPen); writer->writeEndElement(); //error bar properties writer->writeStartElement( "errorBarProperties" ); writer->writeAttribute( "pointErrorBarSize", QString::number(d->pointErrorBarSize) ); WRITE_QBRUSH(d->pointErrorBarBrush); WRITE_QPEN(d->pointErrorBarPen); writer->writeEndElement(); //serialize all children for (auto* child : children(IncludeHidden)) child->save(writer); writer->writeEndElement(); // close section } //! Load from XML bool DatapickerCurve::load(XmlStreamReader* reader, bool preview) { Q_D(DatapickerCurve); if(!reader->isStartElement() || reader->name() != "datapickerCurve") { reader->raiseError(i18n("no dataPicker curve element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "datapickerCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("curveErrorType_X").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("curveErrorType_X")); + reader->raiseWarning(attributeWarning.subs("curveErrorType_X").toString()); else d->curveErrorTypes.x = ErrorType(str.toInt()); str = attribs.value("curveErrorType_Y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("curveErrorType_Y")); + reader->raiseWarning(attributeWarning.subs("curveErrorType_Y").toString()); else d->curveErrorTypes.y = ErrorType(str.toInt()); READ_COLUMN(posXColumn); READ_COLUMN(posYColumn); READ_COLUMN(posZColumn); READ_COLUMN(plusDeltaXColumn); READ_COLUMN(minusDeltaXColumn); READ_COLUMN(plusDeltaYColumn); READ_COLUMN(minusDeltaYColumn); } else if(!preview && reader->name() == "symbolProperties") { attribs = reader->attributes(); str = attribs.value("pointRotationAngle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointRotationAngle")); + reader->raiseWarning(attributeWarning.subs("pointRotationAngle").toString()); else d->pointRotationAngle = str.toFloat(); str = attribs.value("pointOpacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointOpacity")); + reader->raiseWarning(attributeWarning.subs("pointOpacity").toString()); else d->pointOpacity = str.toFloat(); str = attribs.value("pointSize").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointSize")); + reader->raiseWarning(attributeWarning.subs("pointSize").toString()); else d->pointSize = str.toFloat(); str = attribs.value("pointStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointStyle")); + reader->raiseWarning(attributeWarning.subs("pointStyle").toString()); else d->pointStyle = (Symbol::Style)str.toInt(); str = attribs.value("pointVisibility").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointVisibility")); + reader->raiseWarning(attributeWarning.subs("pointVisibility").toString()); else d->pointVisibility = (bool)str.toInt(); READ_QBRUSH(d->pointBrush); READ_QPEN(d->pointPen); } else if(!preview && reader->name() == "errorBarProperties") { attribs = reader->attributes(); str = attribs.value("pointErrorBarSize").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointErrorBarSize")); + reader->raiseWarning(attributeWarning.subs("pointErrorBarSize").toString()); else d->pointErrorBarSize = str.toFloat(); READ_QBRUSH(d->pointErrorBarBrush); READ_QPEN(d->pointErrorBarPen); } else if (reader->name() == "datapickerPoint") { DatapickerPoint* curvePoint = new DatapickerPoint(""); curvePoint->setHidden(true); if (!curvePoint->load(reader, preview)) { delete curvePoint; return false; } else { addChild(curvePoint); curvePoint->initErrorBar(curveErrorTypes()); } } else if (reader->name() == "spreadsheet") { Spreadsheet* datasheet = new Spreadsheet(0, "spreadsheet", true); if (!datasheet->load(reader, preview)) { delete datasheet; return false; } else { addChild(datasheet); m_datasheet = datasheet; } } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } d->retransform(); return true; } diff --git a/src/backend/datapicker/DatapickerImage.cpp b/src/backend/datapicker/DatapickerImage.cpp index 5e9d04165..012fec444 100644 --- a/src/backend/datapicker/DatapickerImage.cpp +++ b/src/backend/datapicker/DatapickerImage.cpp @@ -1,823 +1,824 @@ /*************************************************************************** File : DatapickerImage.cpp Project : LabPlot Description : Worksheet for Datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "DatapickerImage.h" #include "DatapickerImagePrivate.h" #include "backend/datapicker/ImageEditor.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "backend/datapicker/DatapickerPoint.h" #include "backend/datapicker/Segments.h" #include "backend/worksheet/Worksheet.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include "kdefrontend/worksheet/ExportWorksheetDialog.h" #include #include #include #include #include #include +#include #include -#include +#include /** * \class DatapickerImage * \brief container to open image/plot. * * Top-level container for DatapickerPoint. * * * \ingroup datapicker */ DatapickerImage::DatapickerImage(AbstractScriptingEngine* engine, const QString& name, bool loading) : AbstractPart(name), scripted(engine), isLoaded(false), foregroundBins( new int[ImageEditor::colorAttributeMax(Foreground) + 1]), hueBins( new int[ImageEditor::colorAttributeMax(Hue) + 1]), saturationBins( new int[ImageEditor::colorAttributeMax(Saturation) + 1]), valueBins( new int[ImageEditor::colorAttributeMax(Value) + 1]), intensityBins( new int[ImageEditor::colorAttributeMax(Intensity) + 1]), m_magnificationWindow(nullptr), d(new DatapickerImagePrivate(this)), m_view(nullptr), m_segments(new Segments(this)) { if (!loading) init(); } DatapickerImage::~DatapickerImage() { delete [] hueBins; delete [] saturationBins; delete [] valueBins; delete [] intensityBins; delete [] foregroundBins; delete m_segments; delete d; } void DatapickerImage::init() { KConfig config; KConfigGroup group = config.group( "DatapickerImage" ); d->fileName = group.readEntry("FileName", QString()); d->rotationAngle = group.readEntry("RotationAngle", 0.0); d->minSegmentLength = group.readEntry("MinSegmentLength", 30); d->pointSeparation = group.readEntry("PointSeparation", 30); d->axisPoints.type = (DatapickerImage::GraphType) group.readEntry("GraphType", (int) DatapickerImage::Cartesian); d->axisPoints.ternaryScale = group.readEntry("TernaryScale", 1); d->settings.foregroundThresholdHigh = group.readEntry("ForegroundThresholdHigh", 90); d->settings.foregroundThresholdLow = group.readEntry("ForegroundThresholdLow", 30); d->settings.hueThresholdHigh = group.readEntry("HueThresholdHigh", 360); d->settings.hueThresholdLow = group.readEntry("HueThresholdLow", 0); d->settings.intensityThresholdHigh = group.readEntry("IntensityThresholdHigh", 100); d->settings.intensityThresholdLow = group.readEntry("IntensityThresholdLow", 20); d->settings.saturationThresholdHigh = group.readEntry("SaturationThresholdHigh", 100); d->settings.saturationThresholdLow = group.readEntry("SaturationThresholdLow", 30); d->settings.valueThresholdHigh = group.readEntry("ValueThresholdHigh", 90); d->settings.valueThresholdLow = group.readEntry("ValueThresholdLow", 30); d->plotPointsType = (DatapickerImage::PointsType) group.readEntry("PlotPointsType", (int) DatapickerImage::AxisPoints); d->plotImageType = DatapickerImage::OriginalImage; // point properties d->pointStyle = (Symbol::Style)group.readEntry("PointStyle", (int)Symbol::Cross); d->pointSize = group.readEntry("Size", Worksheet::convertToSceneUnits(7, Worksheet::Point)); d->pointRotationAngle = group.readEntry("Rotation", 0.0); d->pointOpacity = group.readEntry("Opacity", 1.0); d->pointBrush.setStyle( (Qt::BrushStyle)group.readEntry("FillingStyle", (int)Qt::NoBrush) ); d->pointBrush.setColor( group.readEntry("FillingColor", QColor(Qt::black)) ); d->pointPen.setStyle( (Qt::PenStyle)group.readEntry("BorderStyle", (int)Qt::SolidLine) ); d->pointPen.setColor( group.readEntry("BorderColor", QColor(Qt::red)) ); d->pointPen.setWidthF( group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1, Worksheet::Point)) ); d->pointVisibility = group.readEntry("PointVisibility", true); } /*! Returns an icon to be used in the project explorer. */ QIcon DatapickerImage::icon() const { return QIcon::fromTheme("image-x-generic"); } /*! Return a new context menu */ QMenu* DatapickerImage::createContextMenu() { QMenu* menu = new QMenu(0); emit requestProjectContextMenu(menu); return menu; } void DatapickerImage::createContextMenu(QMenu* menu) { emit requestProjectContextMenu(menu); } //! Construct 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* DatapickerImage::view() const { if (!m_partView) { m_view = new DatapickerImageView(const_cast(this)); m_partView = m_view; connect(m_view, &DatapickerImageView::statusInfo, this, &DatapickerImage::statusInfo); } return m_partView; } bool DatapickerImage::exportView() const { ExportWorksheetDialog* dlg = new ExportWorksheetDialog(m_view); dlg->setFileName(name()); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { const QString path = dlg->path(); const WorksheetView::ExportFormat format = dlg->exportFormat(); const int resolution = dlg->exportResolution(); WAIT_CURSOR; m_view->exportToFile(path, format, resolution); RESET_CURSOR; } delete dlg; return ret; } bool DatapickerImage::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); bool ret; - dlg->setWindowTitle(i18n("Print Datapicker Image")); + dlg->setWindowTitle(i18nc("@title:window", "Print Datapicker Image")); if ( (ret = (dlg->exec() == QDialog::Accepted)) ) m_view->print(&printer); delete dlg; return ret; } bool DatapickerImage::printPreview() const { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &DatapickerImageView::print); return dlg->exec(); } /*! Selects or deselects the Datapicker/DatapickerImage in the project explorer. This function is called in \c DatapickerImageView. The DatapickerImage gets deselected if there are selected items in the view, and selected if there are no selected items in the view. */ void DatapickerImage::setSelectedInView(const bool b) { if (b) emit childAspectSelectedInView(this); else emit childAspectDeselectedInView(this); } void DatapickerImage::setSegmentsHoverEvent(const bool on) { m_segments->setAcceptHoverEvents(on); } QGraphicsScene* DatapickerImage::scene() const { return d->m_scene; } QRectF DatapickerImage::pageRect() const { return d->m_scene->sceneRect(); } void DatapickerImage::setPlotImageType(const DatapickerImage::PlotImageType type) { d->plotImageType = type; if (d->plotImageType == DatapickerImage::ProcessedImage) d->discretize(); emit requestUpdate(); } DatapickerImage::PlotImageType DatapickerImage::plotImageType() { return d->plotImageType; } void DatapickerImage::initSceneParameters() { setRotationAngle(0.0); setminSegmentLength(30); setPointSeparation(30); ReferencePoints axisPoints = d->axisPoints; axisPoints.ternaryScale = 1; axisPoints.type = DatapickerImage::Cartesian; setAxisPoints(axisPoints); EditorSettings settings; settings.foregroundThresholdHigh = 90; settings.foregroundThresholdLow = 30; settings.hueThresholdHigh = 360; settings.hueThresholdLow = 0; settings.intensityThresholdHigh = 100; settings.intensityThresholdLow = 20; settings.saturationThresholdHigh = 100; settings.saturationThresholdLow = 30; settings.valueThresholdHigh = 90; settings.valueThresholdLow = 30; setSettings(settings); DatapickerImage::PointsType plotPointsType = DatapickerImage::AxisPoints; setPlotPointsType(plotPointsType); } /* =============================== getter methods for background options ================================= */ CLASS_D_READER_IMPL(DatapickerImage, QString, fileName, fileName) CLASS_D_READER_IMPL(DatapickerImage, DatapickerImage::ReferencePoints, axisPoints, axisPoints) CLASS_D_READER_IMPL(DatapickerImage, DatapickerImage::EditorSettings, settings, settings) BASIC_D_READER_IMPL(DatapickerImage, float, rotationAngle, rotationAngle) BASIC_D_READER_IMPL(DatapickerImage, DatapickerImage::PointsType, plotPointsType, plotPointsType) BASIC_D_READER_IMPL(DatapickerImage, int, pointSeparation, pointSeparation) BASIC_D_READER_IMPL(DatapickerImage, int, minSegmentLength, minSegmentLength) BASIC_D_READER_IMPL(DatapickerImage, Symbol::Style, pointStyle, pointStyle) BASIC_D_READER_IMPL(DatapickerImage, qreal, pointOpacity, pointOpacity) BASIC_D_READER_IMPL(DatapickerImage, qreal, pointRotationAngle, pointRotationAngle) BASIC_D_READER_IMPL(DatapickerImage, qreal, pointSize, pointSize) CLASS_D_READER_IMPL(DatapickerImage, QBrush, pointBrush, pointBrush) CLASS_D_READER_IMPL(DatapickerImage, QPen, pointPen, pointPen) BASIC_D_READER_IMPL(DatapickerImage, bool, pointVisibility, pointVisibility) /* ============================ setter methods and undo commands for background options ================= */ STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetFileName, QString, fileName, updateFileName) void DatapickerImage::setFileName(const QString& fileName) { if (fileName!= d->fileName) { beginMacro(i18n("%1: upload new image", name())); - exec(new DatapickerImageSetFileNameCmd(d, fileName, i18n("%1: upload image"))); + exec(new DatapickerImageSetFileNameCmd(d, fileName, ki18n("%1: upload image"))); endMacro(); } } STD_SETTER_CMD_IMPL_S(DatapickerImage, SetRotationAngle, float, rotationAngle) void DatapickerImage::setRotationAngle(float angle) { if (angle != d->rotationAngle) - exec(new DatapickerImageSetRotationAngleCmd(d, angle, i18n("%1: set rotation angle"))); + exec(new DatapickerImageSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle"))); } STD_SETTER_CMD_IMPL_S(DatapickerImage, SetAxisPoints, DatapickerImage::ReferencePoints, axisPoints) void DatapickerImage::setAxisPoints(const DatapickerImage::ReferencePoints& points) { if (memcmp(&points, &d->axisPoints, sizeof(points)) != 0) - exec(new DatapickerImageSetAxisPointsCmd(d, points, i18n("%1: set Axis points"))); + exec(new DatapickerImageSetAxisPointsCmd(d, points, ki18n("%1: set Axis points"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetSettings, DatapickerImage::EditorSettings, settings, discretize) void DatapickerImage::setSettings(const DatapickerImage::EditorSettings& editorSettings) { if (memcmp(&editorSettings, &d->settings, sizeof(editorSettings)) != 0) - exec(new DatapickerImageSetSettingsCmd(d, editorSettings, i18n("%1: set editor settings"))); + exec(new DatapickerImageSetSettingsCmd(d, editorSettings, ki18n("%1: set editor settings"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetMinSegmentLength, int, minSegmentLength, makeSegments) void DatapickerImage::setminSegmentLength(const int value) { if (d->minSegmentLength != value) - exec(new DatapickerImageSetMinSegmentLengthCmd(d, value, i18n("%1: set minimum segment length"))); ; + exec(new DatapickerImageSetMinSegmentLengthCmd(d, value, ki18n("%1: set minimum segment length"))); ; } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointStyle, Symbol::Style, pointStyle, retransform) void DatapickerImage::setPointStyle(Symbol::Style newStyle) { if (newStyle != d->pointStyle) - exec(new DatapickerImageSetPointStyleCmd(d, newStyle, i18n("%1: set point's style"))); + exec(new DatapickerImageSetPointStyleCmd(d, newStyle, ki18n("%1: set point's style"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointSize, qreal, pointSize, retransform) void DatapickerImage::setPointSize(qreal value) { if (!qFuzzyCompare(1 + value, 1 + d->pointSize)) - exec(new DatapickerImageSetPointSizeCmd(d, value, i18n("%1: set point's size"))); + exec(new DatapickerImageSetPointSizeCmd(d, value, ki18n("%1: set point's size"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointRotationAngle, qreal, pointRotationAngle, retransform) void DatapickerImage::setPointRotationAngle(qreal angle) { if (!qFuzzyCompare(1 + angle, 1 + d->pointRotationAngle)) - exec(new DatapickerImageSetPointRotationAngleCmd(d, angle, i18n("%1: rotate point"))); + exec(new DatapickerImageSetPointRotationAngleCmd(d, angle, ki18n("%1: rotate point"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointBrush, QBrush, pointBrush, retransform) void DatapickerImage::setPointBrush(const QBrush& newBrush) { if (newBrush != d->pointBrush) - exec(new DatapickerImageSetPointBrushCmd(d, newBrush, i18n("%1: set point's filling"))); + exec(new DatapickerImageSetPointBrushCmd(d, newBrush, ki18n("%1: set point's filling"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointPen, QPen, pointPen, retransform) void DatapickerImage::setPointPen(const QPen &newPen) { if (newPen != d->pointPen) - exec(new DatapickerImageSetPointPenCmd(d, newPen, i18n("%1: set outline style"))); + exec(new DatapickerImageSetPointPenCmd(d, newPen, ki18n("%1: set outline style"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointOpacity, qreal, pointOpacity, retransform) void DatapickerImage::setPointOpacity(qreal newOpacity) { if (newOpacity != d->pointOpacity) - exec(new DatapickerImageSetPointOpacityCmd(d, newOpacity, i18n("%1: set point's opacity"))); + exec(new DatapickerImageSetPointOpacityCmd(d, newOpacity, ki18n("%1: set point's opacity"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerImage, SetPointVisibility, bool, pointVisibility, retransform) void DatapickerImage::setPointVisibility(const bool on) { if (on != d->pointVisibility) - exec(new DatapickerImageSetPointVisibilityCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new DatapickerImageSetPointVisibilityCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } void DatapickerImage::setPrinting(bool on) const { QVector childPoints = parentAspect()->children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* point : childPoints) point->setPrinting(on); } void DatapickerImage::setPlotPointsType(const PointsType pointsType) { d->plotPointsType = pointsType; if (pointsType == DatapickerImage::AxisPoints) { //clear image int childCount = this->childCount(AbstractAspect::IncludeHidden); if (childCount) { beginMacro(i18n("%1: remove all axis points", name())); QVector childrenPoints = children(AbstractAspect::IncludeHidden); for (auto* point : childrenPoints) point->remove(); endMacro(); } m_segments->setSegmentsVisible(false); } else if (pointsType==DatapickerImage::CurvePoints) m_segments->setSegmentsVisible(false); else if (pointsType==DatapickerImage::SegmentPoints) { d->makeSegments(); m_segments->setSegmentsVisible(true); } } void DatapickerImage::setPointSeparation(const int value) { d->pointSeparation = value; } //############################################################################## //###################### Private implementation ############################### //############################################################################## DatapickerImagePrivate::DatapickerImagePrivate(DatapickerImage *owner):q(owner), pageRect(0, 0, 1500, 1500), m_scene(new QGraphicsScene(pageRect)) { } QString DatapickerImagePrivate::name() const { return q->name(); } void DatapickerImagePrivate::retransform() { QVector childrenPoints = q->children(AbstractAspect::IncludeHidden); for (auto* point : childrenPoints) point->retransform(); } bool DatapickerImagePrivate::uploadImage(const QString& address) { bool rc = q->originalPlotImage.load(address); if (rc) { //convert the image to 32bit-format if this is not the case yet QImage::Format format = q->originalPlotImage.format(); if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32 && format != QImage::Format_ARGB32_Premultiplied) q->originalPlotImage = q->originalPlotImage.convertToFormat(QImage::Format_RGB32); q->processedPlotImage = q->originalPlotImage; q->background = ImageEditor::findBackgroundColor(&q->originalPlotImage); //upload Histogram ImageEditor::uploadHistogram(q->intensityBins, &q->originalPlotImage, q->background, DatapickerImage::Intensity); ImageEditor::uploadHistogram(q->foregroundBins, &q->originalPlotImage, q->background, DatapickerImage::Foreground); ImageEditor::uploadHistogram(q->hueBins, &q->originalPlotImage, q->background, DatapickerImage::Hue); ImageEditor::uploadHistogram(q->saturationBins, &q->originalPlotImage, q->background, DatapickerImage::Saturation); ImageEditor::uploadHistogram(q->valueBins, &q->originalPlotImage, q->background, DatapickerImage::Value); discretize(); //resize the screen double w = Worksheet::convertToSceneUnits(q->originalPlotImage.width(), Worksheet::Inch)/QApplication::desktop()->physicalDpiX(); double h = Worksheet::convertToSceneUnits(q->originalPlotImage.height(), Worksheet::Inch)/QApplication::desktop()->physicalDpiX(); m_scene->setSceneRect(0, 0, w, h); q->isLoaded = true; } return rc; } void DatapickerImagePrivate::discretize() { if (plotImageType != DatapickerImage::ProcessedImage) return; ImageEditor::discretize(&q->processedPlotImage, &q->originalPlotImage, settings, q->background); if (plotPointsType != DatapickerImage::SegmentPoints) emit q->requestUpdate(); else makeSegments(); } void DatapickerImagePrivate::makeSegments() { if (plotPointsType != DatapickerImage::SegmentPoints) return; q->m_segments->makeSegments(q->processedPlotImage); q->m_segments->setSegmentsVisible(true); emit q->requestUpdate(); } DatapickerImagePrivate::~DatapickerImagePrivate() { delete m_scene; } void DatapickerImagePrivate::updateFileName() { WAIT_CURSOR; q->isLoaded = false; const QString& address = fileName.trimmed(); if ( !address.isEmpty() ) { if (uploadImage(address)) { q->initSceneParameters(); fileName = address; } } else { //hide segments if they are visible q->m_segments->setSegmentsVisible(false); } QVector childPoints = q->parentAspect()->children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); if (childPoints.count()) { for (auto* point : childPoints) point->remove(); } emit q->requestUpdate(); emit q->requestUpdateActions(); RESET_CURSOR; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void DatapickerImage::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "datapickerImage" ); writeBasicAttributes(writer); writeCommentElement(writer); //general properties writer->writeStartElement( "general" ); writer->writeAttribute( "fileName", d->fileName ); writer->writeAttribute( "plotPointsType", QString::number(d->plotPointsType) ); writer->writeEndElement(); writer->writeStartElement( "axisPoint" ); writer->writeAttribute( "graphType", QString::number(d->axisPoints.type) ); writer->writeAttribute( "ternaryScale", QString::number(d->axisPoints.ternaryScale) ); writer->writeAttribute( "axisPointLogicalX1", QString::number(d->axisPoints.logicalPos[0].x()) ); writer->writeAttribute( "axisPointLogicalY1", QString::number(d->axisPoints.logicalPos[0].y()) ); writer->writeAttribute( "axisPointLogicalX2", QString::number(d->axisPoints.logicalPos[1].x()) ); writer->writeAttribute( "axisPointLogicalY2", QString::number(d->axisPoints.logicalPos[1].y()) ); writer->writeAttribute( "axisPointLogicalX3", QString::number(d->axisPoints.logicalPos[2].x()) ); writer->writeAttribute( "axisPointLogicalY3", QString::number(d->axisPoints.logicalPos[2].y()) ); writer->writeAttribute( "axisPointLogicalZ1", QString::number(d->axisPoints.logicalPos[0].z()) ); writer->writeAttribute( "axisPointLogicalZ2", QString::number(d->axisPoints.logicalPos[1].z()) ); writer->writeAttribute( "axisPointLogicalZ3", QString::number(d->axisPoints.logicalPos[2].z()) ); writer->writeAttribute( "axisPointSceneX1", QString::number(d->axisPoints.scenePos[0].x()) ); writer->writeAttribute( "axisPointSceneY1", QString::number(d->axisPoints.scenePos[0].y()) ); writer->writeAttribute( "axisPointSceneX2", QString::number(d->axisPoints.scenePos[1].x()) ); writer->writeAttribute( "axisPointSceneY2", QString::number(d->axisPoints.scenePos[1].y()) ); writer->writeAttribute( "axisPointSceneX3", QString::number(d->axisPoints.scenePos[2].x()) ); writer->writeAttribute( "axisPointSceneY3", QString::number(d->axisPoints.scenePos[2].y()) ); writer->writeEndElement(); //editor and segment settings writer->writeStartElement( "editorSettings" ); writer->writeAttribute( "plotImageType", QString::number(d->plotImageType) ); writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) ); writer->writeAttribute( "minSegmentLength", QString::number(d->minSegmentLength) ); writer->writeAttribute( "pointSeparation", QString::number(d->pointSeparation) ); writer->writeAttribute( "foregroundThresholdHigh", QString::number(d->settings.foregroundThresholdHigh) ); writer->writeAttribute( "foregroundThresholdLow", QString::number(d->settings.foregroundThresholdLow) ); writer->writeAttribute( "hueThresholdHigh", QString::number(d->settings.hueThresholdHigh) ); writer->writeAttribute( "hueThresholdLow", QString::number(d->settings.hueThresholdLow) ); writer->writeAttribute( "intensityThresholdHigh", QString::number(d->settings.intensityThresholdHigh) ); writer->writeAttribute( "intensityThresholdLow", QString::number(d->settings.intensityThresholdLow) ); writer->writeAttribute( "saturationThresholdHigh", QString::number(d->settings.saturationThresholdHigh) ); writer->writeAttribute( "saturationThresholdLow", QString::number(d->settings.saturationThresholdLow) ); writer->writeAttribute( "valueThresholdHigh", QString::number(d->settings.valueThresholdHigh) ); writer->writeAttribute( "valueThresholdLow", QString::number(d->settings.valueThresholdLow) ); writer->writeEndElement(); //symbol properties writer->writeStartElement( "symbolProperties" ); writer->writeAttribute( "pointRotationAngle", QString::number(d->pointRotationAngle) ); writer->writeAttribute( "pointOpacity", QString::number(d->pointOpacity) ); writer->writeAttribute( "pointSize", QString::number(d->pointSize) ); writer->writeAttribute( "pointStyle", QString::number(d->pointStyle) ); writer->writeAttribute( "pointVisibility", QString::number(d->pointVisibility) ); WRITE_QBRUSH(d->pointBrush); WRITE_QPEN(d->pointPen); writer->writeEndElement(); //serialize all children for (auto* child : children(IncludeHidden)) child->save(writer); writer->writeEndElement(); } //! Load from XML bool DatapickerImage::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "datapickerImage") { reader->raiseError(i18n("no image element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "datapickerImage") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); d->fileName = str; str = attribs.value("plotPointsType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("plotPointsType")); + reader->raiseWarning(attributeWarning.subs("plotPointsType").toString()); else d->plotPointsType = DatapickerImage::PointsType(str.toInt()); } else if (!preview && reader->name() == "axisPoint") { attribs = reader->attributes(); str = attribs.value("graphType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("graphType")); + reader->raiseWarning(attributeWarning.subs("graphType").toString()); else d->axisPoints.type = DatapickerImage::GraphType(str.toInt()); str = attribs.value("ternaryScale").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("ternaryScale")); + reader->raiseWarning(attributeWarning.subs("ternaryScale").toString()); else d->axisPoints.ternaryScale = str.toDouble(); str = attribs.value("axisPointLogicalX1").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalX1")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalX1").toString()); else d->axisPoints.logicalPos[0].setX(str.toDouble()); str = attribs.value("axisPointLogicalY1").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalY1")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalY1").toString()); else d->axisPoints.logicalPos[0].setY(str.toDouble()); str = attribs.value("axisPointLogicalZ1").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalZ1")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ1").toString()); else d->axisPoints.logicalPos[0].setZ(str.toDouble()); str = attribs.value("axisPointLogicalX2").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalX2")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalX2").toString()); else d->axisPoints.logicalPos[1].setX(str.toDouble()); str = attribs.value("axisPointLogicalY2").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalY2")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalY2").toString()); else d->axisPoints.logicalPos[1].setY(str.toDouble()); str = attribs.value("axisPointLogicalZ2").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalZ2")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ2").toString()); else d->axisPoints.logicalPos[1].setZ(str.toDouble()); str = attribs.value("axisPointLogicalX3").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalX3")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalX3").toString()); else d->axisPoints.logicalPos[2].setX(str.toDouble()); str = attribs.value("axisPointLogicalY3").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalY3")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalY3").toString()); else d->axisPoints.logicalPos[2].setY(str.toDouble()); str = attribs.value("axisPointLogicalZ3").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointLogicalZ3")); + reader->raiseWarning(attributeWarning.subs("axisPointLogicalZ3").toString()); else d->axisPoints.logicalPos[2].setZ(str.toDouble()); str = attribs.value("axisPointSceneX1").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneX1")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneX1").toString()); else d->axisPoints.scenePos[0].setX(str.toDouble()); str = attribs.value("axisPointSceneY1").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneY1")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneY1").toString()); else d->axisPoints.scenePos[0].setY(str.toDouble()); str = attribs.value("axisPointSceneX2").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneX2")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneX2").toString()); else d->axisPoints.scenePos[1].setX(str.toDouble()); str = attribs.value("axisPointSceneY2").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneY2")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneY2").toString()); else d->axisPoints.scenePos[1].setY(str.toDouble()); str = attribs.value("axisPointSceneX3").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneX3")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneX3").toString()); else d->axisPoints.scenePos[2].setX(str.toDouble()); str = attribs.value("axisPointSceneY3").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("axisPointSceneY3")); + reader->raiseWarning(attributeWarning.subs("axisPointSceneY3").toString()); else d->axisPoints.scenePos[2].setY(str.toDouble()); } else if (!preview && reader->name() == "editorSettings") { attribs = reader->attributes(); str = attribs.value("plotImageType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("plotImageType")); + reader->raiseWarning(attributeWarning.subs("plotImageType").toString()); else d->plotImageType = DatapickerImage::PlotImageType(str.toInt()); str = attribs.value("rotationAngle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("rotationAngle")); + reader->raiseWarning(attributeWarning.subs("rotationAngle").toString()); else d->rotationAngle = str.toFloat(); str = attribs.value("minSegmentLength").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("minSegmentLength")); + reader->raiseWarning(attributeWarning.subs("minSegmentLength").toString()); else d->minSegmentLength = str.toInt(); str = attribs.value("pointSeparation").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointSeparation")); + reader->raiseWarning(attributeWarning.subs("pointSeparation").toString()); else d->pointSeparation = str.toInt(); str = attribs.value("foregroundThresholdHigh").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("foregroundThresholdHigh")); + reader->raiseWarning(attributeWarning.subs("foregroundThresholdHigh").toString()); else d->settings.foregroundThresholdHigh = str.toInt(); str = attribs.value("foregroundThresholdLow").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("foregroundThresholdLow")); + reader->raiseWarning(attributeWarning.subs("foregroundThresholdLow").toString()); else d->settings.foregroundThresholdLow = str.toInt(); str = attribs.value("hueThresholdHigh").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("hueThresholdHigh")); + reader->raiseWarning(attributeWarning.subs("hueThresholdHigh").toString()); else d->settings.hueThresholdHigh = str.toInt(); str = attribs.value("hueThresholdLow").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("hueThresholdLow")); + reader->raiseWarning(attributeWarning.subs("hueThresholdLow").toString()); else d->settings.hueThresholdLow = str.toInt(); str = attribs.value("intensityThresholdHigh").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("intensityThresholdHigh")); + reader->raiseWarning(attributeWarning.subs("intensityThresholdHigh").toString()); else d->settings.intensityThresholdHigh = str.toInt(); str = attribs.value("intensityThresholdLow").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("intensityThresholdLow")); + reader->raiseWarning(attributeWarning.subs("intensityThresholdLow").toString()); else d->settings.intensityThresholdLow = str.toInt(); str = attribs.value("saturationThresholdHigh").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("saturationThresholdHigh")); + reader->raiseWarning(attributeWarning.subs("saturationThresholdHigh").toString()); else d->settings.saturationThresholdHigh = str.toInt(); str = attribs.value("saturationThresholdLow").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("saturationThresholdLow")); + reader->raiseWarning(attributeWarning.subs("saturationThresholdLow").toString()); else d->settings.saturationThresholdLow = str.toInt(); str = attribs.value("valueThresholdHigh").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("valueThresholdHigh")); + reader->raiseWarning(attributeWarning.subs("valueThresholdHigh").toString()); else d->settings.valueThresholdHigh = str.toInt(); str = attribs.value("valueThresholdLow").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("valueThresholdLow")); + reader->raiseWarning(attributeWarning.subs("valueThresholdLow").toString()); else d->settings.valueThresholdLow = str.toInt(); } else if(!preview && reader->name() == "symbolProperties") { attribs = reader->attributes(); str = attribs.value("pointRotationAngle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointRotationAngle")); + reader->raiseWarning(attributeWarning.subs("pointRotationAngle").toString()); else d->pointRotationAngle = str.toFloat(); str = attribs.value("pointOpacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointOpacity")); + reader->raiseWarning(attributeWarning.subs("pointOpacity").toString()); else d->pointOpacity = str.toFloat(); str = attribs.value("pointSize").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointSize")); + reader->raiseWarning(attributeWarning.subs("pointSize").toString()); else d->pointSize = str.toFloat(); str = attribs.value("pointStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointStyle")); + reader->raiseWarning(attributeWarning.subs("pointStyle").toString()); else d->pointStyle = (Symbol::Style)str.toInt(); str = attribs.value("pointVisibility").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("pointVisibility")); + reader->raiseWarning(attributeWarning.subs("pointVisibility").toString()); else d->pointVisibility = (bool)str.toInt(); READ_QBRUSH(d->pointBrush); READ_QPEN(d->pointPen); } else if(reader->name() == "datapickerPoint") { DatapickerPoint* datapickerPoint = new DatapickerPoint(""); datapickerPoint->setHidden(true); if (!datapickerPoint->load(reader, preview)) { delete datapickerPoint; return false; } else addChild(datapickerPoint); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } d->uploadImage(d->fileName); d->retransform(); return true; } diff --git a/src/backend/datapicker/DatapickerPoint.cpp b/src/backend/datapicker/DatapickerPoint.cpp index 77b89ccc2..e4d950990 100644 --- a/src/backend/datapicker/DatapickerPoint.cpp +++ b/src/backend/datapicker/DatapickerPoint.cpp @@ -1,556 +1,556 @@ /*************************************************************************** File : DatapickerPoint.cpp Project : LabPlot Description : Graphic Item for coordinate points of Datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@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 "DatapickerPoint.h" #include "backend/worksheet/Worksheet.h" #include "DatapickerPointPrivate.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "backend/datapicker/DatapickerCurve.h" #include #include #include #include #include #include -#include +#include QPen DatapickerPoint::selectedPen = QPen(Qt::darkBlue, 3, Qt::SolidLine); float DatapickerPoint::selectedOpacity = 0.3f; /** * \class ErrorBarItem * \brief A customizable error-bar for DatapickerPoint. */ ErrorBarItem::ErrorBarItem(DatapickerPoint *parent, const ErrorBarType& type) : QGraphicsRectItem(parent->graphicsItem()), barLineItem(new QGraphicsLineItem(parent->graphicsItem())), m_type(type), m_parentItem(parent) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); initRect(); } void ErrorBarItem::initRect() { QRectF xBarRect(-0.15, -0.5, 0.3, 1); QRectF yBarRect(-0.5, -0.15, 1, 0.3); if (m_type == PlusDeltaX || m_type == MinusDeltaX) m_rect = xBarRect; else m_rect = yBarRect; } void ErrorBarItem::setPosition(const QPointF& position) { setPos(position); barLineItem->setLine(0, 0, position.x(), position.y()); } void ErrorBarItem::setRectSize(const qreal size) { QMatrix matrix; matrix.scale(size, size); setRect(matrix.mapRect(m_rect)); } void ErrorBarItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (m_type == PlusDeltaX) m_parentItem->setPlusDeltaXPos(pos()); else if (m_type == MinusDeltaX) m_parentItem->setMinusDeltaXPos(pos()); else if (m_type == PlusDeltaY) m_parentItem->setPlusDeltaYPos(pos()); else if (m_type == MinusDeltaY) m_parentItem->setMinusDeltaYPos(pos()); QGraphicsItem::mouseReleaseEvent(event); } QVariant ErrorBarItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemPositionChange) { QPointF newPos = value.toPointF(); barLineItem->setLine(0, 0, newPos.x(), newPos.y()); } return QGraphicsRectItem::itemChange(change, value); } /** * \class Datapicker-Point * \brief A customizable symbol supports error-bars. * * The datapicker-Point is aligned relative to the specified position. * The position can be either specified by mouse events or by providing the * x- and y- coordinates in parent's coordinate system, or by specifying one * of the predefined position flags (\ca HorizontalPosition, \ca VerticalPosition). */ DatapickerPoint::DatapickerPoint(const QString& name):AbstractAspect(name), d_ptr(new DatapickerPointPrivate(this)) { init(); } DatapickerPoint::DatapickerPoint(const QString& name, DatapickerPointPrivate *dd):AbstractAspect(name), d_ptr(dd) { init(); } DatapickerPoint::~DatapickerPoint() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void DatapickerPoint::init() { Q_D(DatapickerPoint); KConfig config; KConfigGroup group; group = config.group("DatapickerPoint"); d->position.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->plusDeltaXPos = group.readEntry("PlusDeltaXPos", QPointF(30, 0)); d->minusDeltaXPos = group.readEntry("MinusDeltaXPos", QPointF(-30, 0)); d->plusDeltaYPos = group.readEntry("PlusDeltaYPos", QPointF(0, -30)); d->minusDeltaYPos = group.readEntry("MinusDeltaYPos", QPointF(0, 30)); } void DatapickerPoint::initErrorBar(const DatapickerCurve::Errors& errors) { m_errorBarItemList.clear(); if (errors.x != DatapickerCurve::NoError) { ErrorBarItem* plusDeltaXItem = new ErrorBarItem(this, ErrorBarItem::PlusDeltaX); plusDeltaXItem->setPosition(plusDeltaXPos()); connect(this, &DatapickerPoint::plusDeltaXPosChanged, plusDeltaXItem, &ErrorBarItem::setPosition); ErrorBarItem* minusDeltaXItem = new ErrorBarItem(this, ErrorBarItem::MinusDeltaX); minusDeltaXItem->setPosition(minusDeltaXPos()); connect(this, &DatapickerPoint::minusDeltaXPosChanged, minusDeltaXItem, &ErrorBarItem::setPosition); m_errorBarItemList<setPosition(plusDeltaYPos()); connect(this, &DatapickerPoint::plusDeltaYPosChanged, plusDeltaYItem, &ErrorBarItem::setPosition); ErrorBarItem* minusDeltaYItem = new ErrorBarItem(this, ErrorBarItem::MinusDeltaY); minusDeltaYItem->setPosition(minusDeltaYPos()); connect(this, &DatapickerPoint::minusDeltaYPosChanged, minusDeltaYItem, &ErrorBarItem::setPosition); m_errorBarItemList<setParentItem(item); } void DatapickerPoint::retransform() { Q_D(DatapickerPoint); d->retransform(); } /* ============================ getter methods ================= */ //point CLASS_SHARED_D_READER_IMPL(DatapickerPoint, QPointF, position, position) //error-bar CLASS_SHARED_D_READER_IMPL(DatapickerPoint, QPointF, plusDeltaXPos, plusDeltaXPos) CLASS_SHARED_D_READER_IMPL(DatapickerPoint, QPointF, minusDeltaXPos, minusDeltaXPos) CLASS_SHARED_D_READER_IMPL(DatapickerPoint, QPointF, plusDeltaYPos, plusDeltaYPos) CLASS_SHARED_D_READER_IMPL(DatapickerPoint, QPointF, minusDeltaYPos, minusDeltaYPos) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(DatapickerPoint, SetPosition, QPointF, position, retransform) void DatapickerPoint::setPosition(const QPointF& pos) { Q_D(DatapickerPoint); if (pos!=d->position) - exec(new DatapickerPointSetPositionCmd(d, pos, i18n("%1: set position"))); + exec(new DatapickerPointSetPositionCmd(d, pos, ki18n("%1: set position"))); } STD_SETTER_CMD_IMPL_F_S(DatapickerPoint, SetPlusDeltaXPos, QPointF, plusDeltaXPos, updateData) void DatapickerPoint::setPlusDeltaXPos(const QPointF& pos) { Q_D(DatapickerPoint); if ( pos != d->plusDeltaXPos ) { DatapickerCurve* curve = dynamic_cast(parentAspect()); if (!curve) return; beginMacro(i18n("%1: set +delta_X position", name())); if (curve->curveErrorTypes().x == DatapickerCurve::SymmetricError) { - exec(new DatapickerPointSetPlusDeltaXPosCmd(d, pos, i18n("%1: set +delta X position"))); + exec(new DatapickerPointSetPlusDeltaXPosCmd(d, pos, ki18n("%1: set +delta X position"))); setMinusDeltaXPos(QPointF(-qAbs(pos.x()), pos.y())); } else - exec(new DatapickerPointSetPlusDeltaXPosCmd(d, pos, i18n("%1: set +delta X position"))); + exec(new DatapickerPointSetPlusDeltaXPosCmd(d, pos, ki18n("%1: set +delta X position"))); endMacro(); } } STD_SETTER_CMD_IMPL_F_S(DatapickerPoint, SetMinusDeltaXPos, QPointF, minusDeltaXPos, updateData) void DatapickerPoint::setMinusDeltaXPos(const QPointF& pos) { Q_D(DatapickerPoint); if ( pos != d->minusDeltaXPos ) { DatapickerCurve* curve = dynamic_cast(parentAspect()); if (!curve) return; beginMacro(i18n("%1: set -delta_X position", name())); if (curve->curveErrorTypes().x == DatapickerCurve::SymmetricError) { - exec(new DatapickerPointSetMinusDeltaXPosCmd(d, pos, i18n("%1: set -delta_X position"))); + exec(new DatapickerPointSetMinusDeltaXPosCmd(d, pos, ki18n("%1: set -delta_X position"))); setPlusDeltaXPos(QPointF(qAbs(pos.x()), pos.y())); } else - exec(new DatapickerPointSetMinusDeltaXPosCmd(d, pos, i18n("%1: set -delta_X position"))); + exec(new DatapickerPointSetMinusDeltaXPosCmd(d, pos, ki18n("%1: set -delta_X position"))); endMacro(); } } STD_SETTER_CMD_IMPL_F_S(DatapickerPoint, SetPlusDeltaYPos, QPointF, plusDeltaYPos, updateData) void DatapickerPoint::setPlusDeltaYPos(const QPointF& pos) { Q_D(DatapickerPoint); if ( pos != d->plusDeltaYPos ) { DatapickerCurve* curve = dynamic_cast(parentAspect()); if (!curve) return; beginMacro(i18n("%1: set +delta_Y position", name())); if (curve->curveErrorTypes().y == DatapickerCurve::SymmetricError) { - exec(new DatapickerPointSetPlusDeltaYPosCmd(d, pos, i18n("%1: set +delta_Y position"))); + exec(new DatapickerPointSetPlusDeltaYPosCmd(d, pos, ki18n("%1: set +delta_Y position"))); setMinusDeltaYPos(QPointF(pos.x(), qAbs(pos.y()))); } else - exec(new DatapickerPointSetPlusDeltaYPosCmd(d, pos, i18n("%1: set +delta_Y position"))); + exec(new DatapickerPointSetPlusDeltaYPosCmd(d, pos, ki18n("%1: set +delta_Y position"))); endMacro(); } } STD_SETTER_CMD_IMPL_F_S(DatapickerPoint, SetMinusDeltaYPos, QPointF, minusDeltaYPos, updateData) void DatapickerPoint::setMinusDeltaYPos(const QPointF& pos) { Q_D(DatapickerPoint); if ( pos != d->minusDeltaYPos ) { DatapickerCurve* curve = dynamic_cast(parentAspect()); if (!curve) return; beginMacro(i18n("%1: set -delta_Y position", name())); if (curve->curveErrorTypes().y == DatapickerCurve::SymmetricError) { - exec(new DatapickerPointSetMinusDeltaYPosCmd(d, pos, i18n("%1: set -delta_Y position"))); + exec(new DatapickerPointSetMinusDeltaYPosCmd(d, pos, ki18n("%1: set -delta_Y position"))); setPlusDeltaYPos(QPointF(pos.x(), -qAbs(pos.y()))); } else - exec(new DatapickerPointSetMinusDeltaYPosCmd(d, pos, i18n("%1: set -delta_Y position"))); + exec(new DatapickerPointSetMinusDeltaYPosCmd(d, pos, ki18n("%1: set -delta_Y position"))); endMacro(); } } void DatapickerPoint::setPrinting(bool on) { Q_D(DatapickerPoint); d->m_printing = on; } //############################################################################## //####################### Private implementation ############################### //############################################################################## DatapickerPointPrivate::DatapickerPointPrivate(DatapickerPoint* owner) : m_printing(false), q(owner) { setFlag(QGraphicsItem::ItemSendsGeometryChanges); setFlag(QGraphicsItem::ItemIsSelectable); setAcceptHoverEvents(true); } QString DatapickerPointPrivate::name() const { return q->name(); } /*! calculates the position and the bounding box of the item/point. Called on geometry or properties changes. */ void DatapickerPointPrivate::retransform() { updatePropeties(); setPos(position); QPainterPath path = Symbol::pathFromStyle(pointStyle); boundingRectangle = path.boundingRect(); recalcShapeAndBoundingRect(); retransformErrorBar(); updateData(); } /*! update color and size of all error-bar. */ void DatapickerPointPrivate::retransformErrorBar() { for(auto* item : q->m_errorBarItemList) { if (item) { item->setBrush(errorBarBrush); item->setPen(errorBarPen); item->setRectSize(errorBarSize); } } } /*! update datasheet on any change in position of Datapicker-Point or it's error-bar. */ void DatapickerPointPrivate::updateData() { DatapickerCurve* curve = dynamic_cast(q->parentAspect()); if (curve) curve->updateData(q); } void DatapickerPointPrivate::updatePropeties() { DatapickerCurve* curve = dynamic_cast(q->parentAspect()); DatapickerImage* image = dynamic_cast(q->parentAspect()); if (image) { rotationAngle = image->pointRotationAngle(); pointStyle = image->pointStyle(); brush = image->pointBrush(); pen = image->pointPen(); opacity = image->pointOpacity(); size = image->pointSize(); setVisible(image->pointVisibility()); } else if (curve) { rotationAngle = curve->pointRotationAngle(); pointStyle = curve->pointStyle(); brush = curve->pointBrush(); pen = curve->pointPen(); opacity = curve->pointOpacity(); size = curve->pointSize(); errorBarBrush = curve->pointErrorBarBrush(); errorBarPen = curve->pointErrorBarPen(); errorBarSize = curve->pointErrorBarSize(); setVisible(curve->pointVisibility()); } } /*! Returns the outer bounds of the item as a rectangle. */ QRectF DatapickerPointPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath DatapickerPointPrivate::shape() const { return itemShape; } /*! recalculates the outer bounds and the shape of the item. */ void DatapickerPointPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); QMatrix matrix; matrix.scale(size, size); matrix.rotate(-rotationAngle); transformedBoundingRectangle = matrix.mapRect(boundingRectangle); itemShape = QPainterPath(); itemShape.addRect(transformedBoundingRectangle); } void DatapickerPointPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget) { Q_UNUSED(option) Q_UNUSED(widget) QPainterPath path = Symbol::pathFromStyle(pointStyle); QTransform trafo; trafo.scale(size, size); path = trafo.map(path); trafo.reset(); if (rotationAngle != 0) { trafo.rotate(-rotationAngle); path = trafo.map(path); } painter->save(); painter->setPen(pen); painter->setBrush(brush); painter->setOpacity(opacity); painter->drawPath(path); painter->restore(); if (isSelected() && !m_printing) { painter->setPen(q->selectedPen); painter->setOpacity(q->selectedOpacity); painter->drawPath(itemShape); } } void DatapickerPointPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void DatapickerPoint::save(QXmlStreamWriter* writer) const { Q_D(const DatapickerPoint); writer->writeStartElement( "datapickerPoint" ); writeBasicAttributes(writer); writeCommentElement(writer); //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->position.x()) ); writer->writeAttribute( "y", QString::number(d->position.y()) ); writer->writeEndElement(); writer->writeStartElement( "errorBar" ); writer->writeAttribute( "plusDeltaXPos_x", QString::number(d->plusDeltaXPos.x()) ); writer->writeAttribute( "plusDeltaXPos_y", QString::number(d->plusDeltaXPos.y()) ); writer->writeAttribute( "minusDeltaXPos_x", QString::number(d->minusDeltaXPos.x()) ); writer->writeAttribute( "minusDeltaXPos_y", QString::number(d->minusDeltaXPos.y()) ); writer->writeAttribute( "plusDeltaYPos_x", QString::number(d->plusDeltaYPos.x()) ); writer->writeAttribute( "plusDeltaYPos_y", QString::number(d->plusDeltaYPos.y()) ); writer->writeAttribute( "minusDeltaYPos_x", QString::number(d->minusDeltaYPos.x()) ); writer->writeAttribute( "minusDeltaYPos_y", QString::number(d->minusDeltaYPos.y()) ); writer->writeEndElement(); writer->writeEndElement(); // close "DatapickerPoint" section } //! Load from XML bool DatapickerPoint::load(XmlStreamReader* reader, bool preview) { Q_D(DatapickerPoint); if(!reader->isStartElement() || reader->name() != "datapickerPoint") { reader->raiseError(i18n("no datapicker-Point element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "datapickerPoint") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.setX(str.toDouble()); str = attribs.value("y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.setY(str.toDouble()); } else if (!preview && reader->name() == "errorBar") { attribs = reader->attributes(); str = attribs.value("plusDeltaXPos_x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'plusDeltaXPos_x'")); + reader->raiseWarning(attributeWarning.subs("plusDeltaXPos_x").toString()); else d->plusDeltaXPos.setX(str.toDouble()); str = attribs.value("plusDeltaXPos_y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'plusDeltaXPos_y'")); + reader->raiseWarning(attributeWarning.subs("plusDeltaXPos_y").toString()); else d->plusDeltaXPos.setY(str.toDouble()); str = attribs.value("minusDeltaXPos_x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'minusDeltaXPos_x'")); + reader->raiseWarning(attributeWarning.subs("minusDeltaXPos_x").toString()); else d->minusDeltaXPos.setX(str.toDouble()); str = attribs.value("minusDeltaXPos_y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'minusDeltaXPos_y'")); + reader->raiseWarning(attributeWarning.subs("minusDeltaXPos_y").toString()); else d->minusDeltaXPos.setY(str.toDouble()); str = attribs.value("plusDeltaYPos_x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'plusDeltaYPos_x'")); + reader->raiseWarning(attributeWarning.subs("plusDeltaYPos_x").toString()); else d->plusDeltaYPos.setX(str.toDouble()); str = attribs.value("plusDeltaYPos_y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'plusDeltaYPos_y'")); + reader->raiseWarning(attributeWarning.subs("plusDeltaYPos_y").toString()); else d->plusDeltaYPos.setY(str.toDouble()); str = attribs.value("minusDeltaYPos_x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'minusDeltaYPos_x'")); + reader->raiseWarning(attributeWarning.subs("minusDeltaYPos_x").toString()); else d->minusDeltaYPos.setX(str.toDouble()); str = attribs.value("minusDeltaYPos_y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'minusDeltaYPos_y'")); + reader->raiseWarning(attributeWarning.subs("minusDeltaYPos_y").toString()); else d->minusDeltaYPos.setY(str.toDouble()); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } retransform(); return true; } diff --git a/src/backend/datapicker/Segment.cpp b/src/backend/datapicker/Segment.cpp index 5d6532479..aeda6b631 100644 --- a/src/backend/datapicker/Segment.cpp +++ b/src/backend/datapicker/Segment.cpp @@ -1,210 +1,210 @@ /*************************************************************************** File : Segment.cpp Project : LabPlot Description : Graphics-item for curve of Datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@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 "Segment.h" #include "SegmentPrivate.h" #include "backend/datapicker/DatapickerImage.h" #include "backend/datapicker/DatapickerPoint.h" #include "backend/worksheet/Worksheet.h" #include "backend/datapicker/Datapicker.h" #include #include #include -#include +#include /** * \class Segment * \brief graphics-item class for curve-segment */ Segment::Segment(DatapickerImage* image): yLast(0), length(0), m_image(image), d_ptr(new SegmentPrivate(this)) { m_image->scene()->addItem(this->graphicsItem()); } QGraphicsItem* Segment::graphicsItem() const { return d_ptr; } void Segment::setParentGraphicsItem(QGraphicsItem* item) { Q_D(Segment); d->setParentItem(item); } void Segment::retransform() { Q_D(Segment); d->retransform(); } bool Segment::isVisible() const { Q_D(const Segment); return d->isVisible(); } void Segment::setVisible(bool on) { Q_D(Segment); d->setVisible(on); } //############################################################################## //####################### Private implementation ############################### //############################################################################## SegmentPrivate::SegmentPrivate(Segment *owner) : scaleFactor(Worksheet::convertToSceneUnits(1, Worksheet::Inch)/QApplication::desktop()->physicalDpiX()), m_hovered(false), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); setVisible(false); } /*! calculates the position and the bounding box of the item. Called on geometry or properties changes. */ void SegmentPrivate::retransform() { for (auto* line : q->path) { linePath.moveTo(line->p1()); linePath.lineTo(line->p2()); } boundingRectangle = linePath.boundingRect(); recalcShapeAndBoundingRect(); } /*! Returns the outer bounds of the item as a rectangle. */ QRectF SegmentPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath SegmentPrivate::shape() const { return itemShape; } /*! recalculates the outer bounds and the shape of the item. */ void SegmentPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); QMatrix matrix; matrix.scale(scaleFactor, scaleFactor); transformedBoundingRectangle = matrix.mapRect(boundingRectangle); itemShape = QPainterPath(); itemShape.addRect(transformedBoundingRectangle); } void SegmentPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget) { Q_UNUSED(option) Q_UNUSED(widget) QPen pen(Qt::green); QPen hoveredPen = QPen(QColor(128,179,255), 3, Qt::SolidLine); qreal hoveredOpacity = 0.6; QPen selectedPen = QPen(Qt::darkBlue, 3, Qt::SolidLine); qreal selectedOpacity = 0.3; painter->save(); painter->setPen(pen); painter->scale(scaleFactor, scaleFactor); painter->drawPath(linePath); painter->restore(); if (m_hovered && !isSelected()) { painter->setPen(hoveredPen); painter->setOpacity(hoveredOpacity); painter->drawPath(itemShape); } if (isSelected()) { painter->setPen(selectedPen); painter->setOpacity(selectedOpacity); painter->drawPath(itemShape); } } void SegmentPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; update(); } } void SegmentPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; update(); } } QVariant SegmentPrivate::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { if ( change == QGraphicsItem::ItemSelectedChange && value == true ) { Datapicker* datapicker = dynamic_cast(q->m_image->parentAspect()); Q_ASSERT(datapicker); if (datapicker->activeCurve()) { int count = 0; QList posList; posList.clear(); for (QLine* line : q->path) { const int l = (line->y1() > line->y2())?line->y2():line->y1(); const int h = (line->y1() > line->y2())?line->y1():line->y2(); for (int i = l; i <= h; ++i) { if (count%q->m_image->pointSeparation() == 0) { bool positionUsed = false; const QVector curvePointsList = datapicker->activeCurve()->children(AbstractAspect::IncludeHidden); for (const auto* point : curvePointsList) { if ( point->position() == QPoint(line->x1(), i)*scaleFactor ) positionUsed = true; } if (!positionUsed) posList<x1(), i)*scaleFactor; } count++; } } if (!posList.isEmpty()) { datapicker->activeCurve()->beginMacro(i18n("%1: draw points over segment", datapicker->activeCurve()->name())); for (const QPointF& pos : posList) datapicker->addNewPoint(pos, datapicker->activeCurve()); datapicker->activeCurve()->endMacro(); } } //no need to keep segment selected return false; } return QGraphicsItem::itemChange(change, value); } diff --git a/src/backend/datasources/LiveDataSource.cpp b/src/backend/datasources/LiveDataSource.cpp index c18107e55..5f29a1697 100644 --- a/src/backend/datasources/LiveDataSource.cpp +++ b/src/backend/datasources/LiveDataSource.cpp @@ -1,1043 +1,1028 @@ /*************************************************************************** File : LiveDataSource.cpp Project : LabPlot Description : Represents live data source -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) -Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) +Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) +Copyright : (C) 2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/core/Project.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include /*! \class LiveDataSource \brief Represents data stored in a file. Reading and writing is done with the help of appropriate I/O-filters. \ingroup datasources */ LiveDataSource::LiveDataSource(AbstractScriptingEngine* engine, const QString& name, bool loading) : Spreadsheet(engine, name, loading), m_fileType(Ascii), m_fileWatched(false), m_fileLinked(false), m_paused(false), m_prepared(false), m_keepLastValues(false), m_bytesRead(0), m_filter(nullptr), m_updateTimer(new QTimer(this)), m_fileSystemWatcher(nullptr), m_file(nullptr), m_localSocket(nullptr), m_tcpSocket(nullptr), m_udpSocket(nullptr), m_serialPort(nullptr), m_device(nullptr) { initActions(); connect(m_updateTimer, &QTimer::timeout, this, &LiveDataSource::read); } LiveDataSource::~LiveDataSource() { + //stop reading before deleting the objects + pauseReading(); + if (m_filter) delete m_filter; if (m_fileSystemWatcher) delete m_fileSystemWatcher; if (m_file) delete m_file; if (m_localSocket) delete m_localSocket; if (m_tcpSocket) delete m_tcpSocket; if (m_serialPort) delete m_serialPort; delete m_updateTimer; } /*! * depending on the update type, periodically or on data changes, starts the timer or activates the file watchers, respectively. */ void LiveDataSource::ready() { if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else watch(); } void LiveDataSource::initActions() { m_reloadAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), this); connect(m_reloadAction, &QAction::triggered, this, &LiveDataSource::read); m_toggleLinkAction = new QAction(i18n("Link the file"), this); m_toggleLinkAction->setCheckable(true); connect(m_toggleLinkAction, &QAction::triggered, this, &LiveDataSource::linkToggled); m_plotDataAction = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); connect(m_plotDataAction, &QAction::triggered, this, &LiveDataSource::plotData); } QWidget* LiveDataSource::view() const { if (!m_partView) m_partView = new SpreadsheetView(const_cast(this), true); return m_partView; } /*! * \brief Returns a list with the names of the available ports */ QStringList LiveDataSource::availablePorts() { QStringList ports; qDebug() << "available ports count:" << QSerialPortInfo::availablePorts().size(); for(const QSerialPortInfo& sp : QSerialPortInfo::availablePorts()) { ports.append(sp.portName()); qDebug() << sp.description(); qDebug() << sp.manufacturer(); qDebug() << sp.portName(); qDebug() << sp.serialNumber(); qDebug() << sp.systemLocation(); } return ports; } /*! * \brief Returns a list with the supported baud rates */ QStringList LiveDataSource::supportedBaudRates() { QStringList baudRates; for(const auto& baud : QSerialPortInfo::standardBaudRates()) baudRates.append(QString::number(baud)); return baudRates; } /*! * \brief Updates this data source at this moment */ void LiveDataSource::updateNow() { m_updateTimer->stop(); read(); //restart the timer after update if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); } /*! * \brief Continue reading from the live data source after it was paused. */ void LiveDataSource::continueReading() { m_paused = false; if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else if (m_updateType == NewData) connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } /*! * \brief Pause the reading of the live data source. */ void LiveDataSource::pauseReading() { m_paused = true; if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } /*! returns the list with all supported data file formats. */ QStringList LiveDataSource::fileTypes() { // see LiveDataSource::FileType return (QStringList()<< i18n("ASCII data") << i18n("Binary data") << i18n("Image") << i18n("Hierarchical Data Format 5 (HDF5)") << i18n("Network Common Data Format (NetCDF)") // << "CDF" << i18n("Flexible Image Transport System Data Format (FITS)") << i18n("JSON data") // << i18n("Sound") ); } void LiveDataSource::setFileName(const QString& name) { m_fileName = name; } QString LiveDataSource::fileName() const { return m_fileName; } /*! * \brief Sets the local socket's server name to name * \param name */ void LiveDataSource::setLocalSocketName(const QString& name) { m_localSocketName = name; } QString LiveDataSource::localSocketName() const { return m_localSocketName; } void LiveDataSource::setFileType(FileType type) { m_fileType = type; } LiveDataSource::FileType LiveDataSource::fileType() const { return m_fileType; } void LiveDataSource::setFilter(AbstractFileFilter* f) { m_filter = f; } AbstractFileFilter* LiveDataSource::filter() const { return m_filter; } /*! sets whether the file should be watched or not. In the first case the data source will be automatically updated on file changes. */ void LiveDataSource::setFileWatched(bool b) { m_fileWatched = b; } bool LiveDataSource::isFileWatched() const { return m_fileWatched; } /*! * \brief Sets whether we'll keep the last values or append it to the previous ones * \param keepLastValues */ void LiveDataSource::setKeepLastValues(bool keepLastValues) { m_keepLastValues = keepLastValues; } bool LiveDataSource::keepLastValues() const { return m_keepLastValues; } /*! * \brief Sets the serial port's baud rate * \param baudrate */ void LiveDataSource::setBaudRate(int baudrate) { m_baudRate = baudrate; } int LiveDataSource::baudRate() const { return m_baudRate; } /*! * \brief Sets the source's update interval to \c interval * \param interval */ void LiveDataSource::setUpdateInterval(int interval) { m_updateInterval = interval; m_updateTimer->start(m_updateInterval); } int LiveDataSource::updateInterval() const { return m_updateInterval; } /*! * \brief Sets how many values we should store * \param keepnvalues */ void LiveDataSource::setKeepNvalues(int keepnvalues) { m_keepNvalues = keepnvalues; } int LiveDataSource::keepNvalues() const { return m_keepNvalues; } /*! * \brief Sets the network socket's port to port * \param port */ void LiveDataSource::setPort(quint16 port) { m_port = port; } void LiveDataSource::setBytesRead(qint64 bytes) { m_bytesRead = bytes; } int LiveDataSource::bytesRead() const { return m_bytesRead; } int LiveDataSource::port() const { return m_port; } /*! * \brief Sets the serial port's name to name * \param name */ void LiveDataSource::setSerialPort(const QString& name) { m_serialPortName = name; } QString LiveDataSource::serialPortName() const { return m_serialPortName; } bool LiveDataSource::isPaused() const { return m_paused; } /*! * \brief Sets the sample rate to samplerate * \param samplerate */ void LiveDataSource::setSampleRate(int samplerate) { m_sampleRate = samplerate; } int LiveDataSource::sampleRate() const { return m_sampleRate; } /*! * \brief Sets the source's type to sourcetype * \param sourcetype */ void LiveDataSource::setSourceType(SourceType sourcetype) { m_sourceType = sourcetype; } LiveDataSource::SourceType LiveDataSource::sourceType() const { return m_sourceType; } /*! * \brief Sets the source's reading type to readingType * \param readingType */ void LiveDataSource::setReadingType(ReadingType readingType) { m_readingType = readingType; } LiveDataSource::ReadingType LiveDataSource::readingType() const { return m_readingType; } /*! * \brief Sets the source's update type to updatetype and handles this change * \param updatetype */ void LiveDataSource::setUpdateType(UpdateType updatetype) { if (updatetype == NewData) { m_updateTimer->stop(); if (m_fileSystemWatcher == nullptr) watch(); else connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } else { if (m_fileSystemWatcher) disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } m_updateType = updatetype; } LiveDataSource::UpdateType LiveDataSource::updateType() const { return m_updateType; } /*! * \brief Sets the network socket's host * \param host */ void LiveDataSource::setHost(const QString& host) { m_host = host; } QString LiveDataSource::host() const { return m_host; } /*! sets whether only a link to the file is saved in the project file (\c b=true) or the whole content of the file (\c b=false). */ void LiveDataSource::setFileLinked(bool b) { m_fileLinked = b; } /*! returns \c true if only a link to the file is saved in the project file. \c false otherwise. */ bool LiveDataSource::isFileLinked() const { return m_fileLinked; } QIcon LiveDataSource::icon() const { QIcon icon; if (m_fileType == LiveDataSource::Ascii) icon = QIcon::fromTheme("text-plain"); else if (m_fileType == LiveDataSource::Binary) icon = QIcon::fromTheme("application-octet-stream"); else if (m_fileType == LiveDataSource::Image) icon = QIcon::fromTheme("image-x-generic"); // TODO: HDF5, NetCDF, FITS, etc. return icon; } QMenu* LiveDataSource::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); QAction* firstAction = 0; // 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) + if (menu->actions().size() > 1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, m_plotDataAction); menu->insertSeparator(firstAction); //TODO: doesnt' always make sense... // if (!m_fileWatched) // menu->insertAction(firstAction, m_reloadAction); // // m_toggleWatchAction->setChecked(m_fileWatched); // menu->insertAction(firstAction, m_toggleWatchAction); // // m_toggleLinkAction->setChecked(m_fileLinked); // menu->insertAction(firstAction, m_toggleLinkAction); return menu; } //############################################################################## //################################# SLOTS #################################### //############################################################################## /* * called periodically or on new data changes (file changed, new data in the socket, etc.) */ void LiveDataSource::read() { + DEBUG("LiveDataSource::read()"); if (m_filter == nullptr) return; //initialize the device (file, socket, serial port), when calling this function for the first time if (!m_prepared) { + DEBUG(" preparing device"); switch (m_sourceType) { case FileOrPipe: m_file = new QFile(m_fileName); m_device = m_file; break; case NetworkTcpSocket: m_tcpSocket = new QTcpSocket(this); - m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); m_device = m_tcpSocket; + m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); - qDebug() << "socket state before preparing: " << m_tcpSocket->state(); connect(m_tcpSocket, &QTcpSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_tcpSocket, static_cast(&QTcpSocket::error), this, &LiveDataSource::tcpSocketError); - qDebug() << "socket state after preparing: " << m_tcpSocket->state(); break; case NetworkUdpSocket: m_udpSocket = new QUdpSocket(this); - + m_device = m_udpSocket; m_udpSocket->bind(QHostAddress(m_host), m_port); - qDebug() << "socket state before preparing: " << m_udpSocket->state(); - m_udpSocket->connectToHost(m_host, m_port); + m_udpSocket->connectToHost(m_host, 0, QUdpSocket::ReadOnly); - m_device = m_udpSocket; connect(m_udpSocket, &QUdpSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_udpSocket, static_cast(&QUdpSocket::error), this, &LiveDataSource::tcpSocketError); - qDebug() << "socket state after preparing: " << m_udpSocket->state(); break; case LocalSocket: m_localSocket = new QLocalSocket(this); - qDebug() << "socket state before preparing: " << m_localSocket->state(); + m_device = m_localSocket; m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); - qDebug() << "socket state after preparing: " << m_localSocket->state(); - m_device = m_localSocket; connect(m_localSocket, &QLocalSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_localSocket, static_cast(&QLocalSocket::error), this, &LiveDataSource::localSocketError); break; case SerialPort: m_serialPort = new QSerialPort; m_device = m_serialPort; m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); connect(m_serialPort, static_cast(&QSerialPort::error), this, &LiveDataSource::serialPortError); connect(m_serialPort, &QSerialPort::readyRead, this, &LiveDataSource::readyRead); break; } m_prepared = true; } qint64 bytes = 0; switch (m_sourceType) { case FileOrPipe: switch (m_fileType) { case Ascii: - qDebug() << "Reading live ascii file.." ; - if (m_readingType == LiveDataSource::ReadingType::WholeFile) { - dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, 0); - } else { - bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); - m_bytesRead += bytes; - } - qDebug() << "Read " << bytes << " bytes, in total: " << m_bytesRead; + DEBUG("Reading live ascii file .."); + if (m_readingType == LiveDataSource::ReadingType::WholeFile) { + dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, 0); + } else { + bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); + m_bytesRead += bytes; + } + DEBUG("Read " << bytes << " bytes, in total: " << m_bytesRead); break; case Binary: //bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; case Image: case HDF5: case NETCDF: case FITS: case Json: break; } break; case NetworkTcpSocket: - DEBUG("reading from a TCP socket"); - qDebug() << "reading from a TCP socket before abort: " << m_tcpSocket->state(); + DEBUG("reading from TCP socket. state before abort = " << m_tcpSocket->state()); m_tcpSocket->abort(); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); - qDebug() << "reading from a TCP socket after reconnect: " << m_tcpSocket->state(); - + DEBUG("reading from TCP socket. state after reconnect = " << m_tcpSocket->state()); break; case NetworkUdpSocket: - DEBUG("reading from a UDP socket"); - qDebug() << "reading from a UDP socket before abort: " << m_udpSocket->state(); + DEBUG("reading from UDP socket. state before abort = " << m_udpSocket->state()); m_udpSocket->abort(); m_udpSocket->bind(QHostAddress(m_host), m_port); - m_udpSocket->connectToHost(m_host, m_port); - - qDebug() << "reading from a UDP socket after reconnect: " << m_udpSocket->state(); - + m_udpSocket->connectToHost(m_host, 0, QUdpSocket::ReadOnly); + DEBUG("reading from UDP socket. state after reconnect = " << m_udpSocket->state()); break; case LocalSocket: - DEBUG("reading from a local socket"); - qDebug() << "reading from a local socket before abort: " << m_localSocket->state(); + DEBUG("reading from local socket. state before abort = " << m_localSocket->state()); m_localSocket->abort(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); - qDebug() << "reading from a local socket after reconnect: " << m_localSocket->state(); + DEBUG("reading from local socket. state after reconnect = " << m_localSocket->state()); break; case SerialPort: DEBUG("reading from the serial port"); m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); m_device = m_serialPort; //TODO break; } } /*! * Slot for the signal that is emitted once every time new data is available for reading from the device. * It will only be emitted again once new data is available, such as when a new payload of network data has arrived on the network socket, * or when a new block of data has been appended to your device. */ void LiveDataSource::readyRead() { DEBUG("Got new data from the device"); - qDebug()<< "Got new data from the device"; if (m_fileType == Ascii) dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); // else if (m_fileType == Binary) // dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); //since we won't have the timer to call read() where we create new connections //for sequencial devices in read() we just request data/connect to servers if (m_updateType == NewData) read(); } void LiveDataSource::localSocketError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError); /*disconnect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); disconnect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));*/ /*switch (socketError) { case QLocalSocket::ServerNotFoundError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket was not found. Please check the socket name.")); break; case QLocalSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The connection was refused by the peer")); break; case QLocalSocket::PeerClosedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket has closed the connection.")); break; default: QMessageBox::critical(0, i18n("Local Socket Error"), - i18n("The following error occurred: %1.").arg(m_localSocket->errorString())); + i18n("The following error occurred: %1.", m_localSocket->errorString())); }*/ } void LiveDataSource::tcpSocketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); /*switch (socketError) { case QAbstractSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The connection was refused by the peer. Make sure the server is running and check the host name and port settings.")); break; case QAbstractSocket::RemoteHostClosedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The remote host closed the connection.")); break; case QAbstractSocket::HostNotFoundError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The host was not found. Please check the host name and port settings.")); break; default: QMessageBox::critical(0, i18n("TCP Socket Error"), - i18n("The following error occurred: %1.").arg(m_tcpSocket->errorString())); + i18n("The following error occurred: %1.", m_tcpSocket->errorString())); }*/ } void LiveDataSource::serialPortError(QSerialPort::SerialPortError serialPortError) { switch (serialPortError) { case QSerialPort::DeviceNotFoundError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("Failed to open the device.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device.")); break; case QSerialPort::PermissionError: QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("Failed to open the device. Please check your permissions on this device.")); + i18n("Failed to open the device. Please check your permissions on this device.")); break; case QSerialPort::OpenError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("Device already opened.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Device already opened.")); break; case QSerialPort::NotOpenError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("The device is not opened.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device is not opened.")); break; case QSerialPort::ReadError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("Failed to read data.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data.")); break; case QSerialPort::ResourceError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("Failed to read data. The device is removed.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data. The device is removed.")); break; case QSerialPort::TimeoutError: - QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("The device timed out.")); + QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device timed out.")); break; #ifndef _MSC_VER //MSVC complains about the usage of deprecated enums, g++ and clang complain about missing enums case QSerialPort::ParityError: case QSerialPort::FramingError: case QSerialPort::BreakConditionError: #endif case QSerialPort::WriteError: case QSerialPort::UnsupportedOperationError: case QSerialPort::UnknownError: QMessageBox::critical(0, i18n("Serial Port Error"), - i18n("The following error occurred: %1.").arg(m_serialPort->errorString())); + i18n("The following error occurred: %1.", m_serialPort->errorString())); break; case QSerialPort::NoError: break; } } void LiveDataSource::watchToggled() { m_fileWatched = !m_fileWatched; watch(); project()->setChanged(true); } void LiveDataSource::linkToggled() { m_fileLinked = !m_fileLinked; project()->setChanged(true); } //watch the file upon reading for changes if required void LiveDataSource::watch() { if (m_fileWatched) { if (!m_fileSystemWatcher) { m_fileSystemWatcher = new QFileSystemWatcher; connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } if ( !m_fileSystemWatcher->files().contains(m_fileName) ) m_fileSystemWatcher->addPath(m_fileName); } else { if (m_fileSystemWatcher) m_fileSystemWatcher->removePath(m_fileName); } } /*! returns a string containing the general information about the file \c name and some content specific information (number of columns and lines for ASCII, color-depth for images etc.). */ QString LiveDataSource::fileInfoString(const QString &name) { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; #ifdef Q_OS_WIN - if (name.at(1) != QLatin1Char(':')) { + if (name.at(1) != QLatin1Char(':')) fileName = QDir::homePath() + name; - } else { + else fileName = name; - } #else if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; #endif if(file==0) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; //general information about the file infoStrings << "" + fileName + "
"; fileInfo.setFile(fileName); infoStrings << i18n("Readable: %1", fileInfo.isReadable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Writable: %1", fileInfo.isWritable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Executable: %1", fileInfo.isExecutable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Created: %1", fileInfo.created().toString()); infoStrings << i18n("Last modified: %1", fileInfo.lastModified().toString()); infoStrings << i18n("Last read: %1", fileInfo.lastRead().toString()); infoStrings << i18n("Owner: %1", fileInfo.owner()); infoStrings << i18n("Group: %1", fileInfo.group()); infoStrings << i18n("Size: %1", i18np("%1 cByte", "%1 cBytes", fileInfo.size())); #ifdef HAVE_FITS if (fileName.endsWith(QLatin1String(".fits"))) { infoStrings << i18n("Images: %1", QString::number(FITSFilter::imagesCount(fileName) )); infoStrings << i18n("Tables: %1", QString::number(FITSFilter::tablesCount(fileName) )); } #endif // file type and type specific information about the file #ifdef Q_OS_LINUX QProcess *proc = new QProcess(); QStringList args; args<<"-b"<start( "file", args); if(proc->waitForReadyRead(1000) == false) infoStrings << i18n("Could not open file %1 for reading.", fileName); else { fileTypeString = proc->readLine(); if( fileTypeString.contains(i18n("cannot open")) ) fileTypeString=""; else { fileTypeString.remove(fileTypeString.length()-1,1); // remove '\n' } } infoStrings << i18n("File type: %1", fileTypeString); #endif //TODO depending on the file type, generate additional information about the file: //Number of lines for ASCII, color-depth for images etc. Use the specific filters here. // port the old labplot1.6 code. if( fileTypeString.contains("ASCII")) { infoStrings << "
"; //TODO: consider choosen separator infoStrings << i18n("Number of columns: %1", AsciiFilter::columnNumber(fileName)); infoStrings << i18n("Number of lines: %1", AsciiFilter::lineNumber(fileName)); } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } void LiveDataSource::plotData() { PlotDataDialog* dlg = new PlotDataDialog(this); dlg->exec(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void LiveDataSource::save(QXmlStreamWriter* writer) const { writer->writeStartElement("LiveDataSource"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement("general"); writer->writeAttribute("fileName", m_fileName); writer->writeAttribute("fileType", QString::number(m_fileType)); writer->writeAttribute("fileWatched", QString::number(m_fileWatched)); writer->writeAttribute("fileLinked", QString::number(m_fileLinked)); writer->writeAttribute("updateType", QString::number(m_updateType)); writer->writeAttribute("readingType", QString::number(m_readingType)); writer->writeAttribute("sourceType", QString::number(m_sourceType)); writer->writeAttribute("keepValues", QString::number(m_keepNvalues)); if (m_updateType == TimeInterval) writer->writeAttribute("updateInterval", QString::number(m_updateInterval)); if (m_readingType != TillEnd) writer->writeAttribute("sampleRate", QString::number(m_sampleRate)); switch (m_sourceType) { case SerialPort: writer->writeAttribute("baudRate", QString::number(m_baudRate)); writer->writeAttribute("serialPortName", m_serialPortName); break; case NetworkTcpSocket: case NetworkUdpSocket: writer->writeAttribute("host", m_host); writer->writeAttribute("port", QString::number(m_port)); break; case FileOrPipe: break; case LocalSocket: break; default: break; } writer->writeEndElement(); //filter m_filter->save(writer); //columns if (!m_fileLinked) { for (auto* col : children(IncludeHidden)) col->save(writer); } writer->writeEndElement(); // "LiveDataSource" } /*! Loads from XML. */ bool LiveDataSource::load(XmlStreamReader* reader, bool preview) { if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "LiveDataSource") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'fileName'")); + reader->raiseWarning(attributeWarning.subs("fileName").toString()); else m_fileName = str; str = attribs.value("fileType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'fileType'")); + reader->raiseWarning(attributeWarning.subs("fileType").toString()); else m_fileType = (FileType)str.toInt(); str = attribs.value("fileWatched").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'fileWatched'")); + reader->raiseWarning(attributeWarning.subs("fileWatched").toString()); else m_fileWatched = str.toInt(); str = attribs.value("fileLinked").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'fileLinked'")); + reader->raiseWarning(attributeWarning.subs("fileLinked").toString()); else m_fileLinked = str.toInt(); str = attribs.value("updateType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'updateType'")); + reader->raiseWarning(attributeWarning.subs("updateType").toString()); else m_updateType = static_cast(str.toInt()); str = attribs.value("sourceType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'sourceType'")); + reader->raiseWarning(attributeWarning.subs("sourceType").toString()); else m_sourceType = static_cast(str.toInt()); str = attribs.value("readingType").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'readingType'")); + reader->raiseWarning(attributeWarning.subs("readingType").toString()); else m_readingType = static_cast(str.toInt()); if (m_updateType == TimeInterval) { str = attribs.value("updateInterval").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'updateInterval'")); + reader->raiseWarning(attributeWarning.subs("updateInterval").toString()); else m_updateInterval = str.toInt(); } if (m_readingType != TillEnd) { str = attribs.value("sampleRate").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'sampleRate'")); + reader->raiseWarning(attributeWarning.subs("sampleRate").toString()); else m_sampleRate = str.toInt(); } switch (m_sourceType) { case SerialPort: str = attribs.value("baudRate").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'baudRate'")); + reader->raiseWarning(attributeWarning.subs("baudRate").toString()); else m_baudRate = str.toInt(); str = attribs.value("serialPortName").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'serialPortName'")); + reader->raiseWarning(attributeWarning.subs("serialPortName").toString()); else m_serialPortName = str; break; case NetworkTcpSocket: case NetworkUdpSocket: str = attribs.value("host").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'host'")); + reader->raiseWarning(attributeWarning.subs("host").toString()); else m_host = str; str = attribs.value("port").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'port'")); + reader->raiseWarning(attributeWarning.subs("port").toString()); else m_host = str; break; case FileOrPipe: break; case LocalSocket: break; default: break; } } else if (reader->name() == "asciiFilter") { m_filter = new AsciiFilter(); if (!m_filter->load(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Text); if (!column->load(reader, preview)) { delete column; setColumnCount(0); return false; } addChild(column); } else {// unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } //read the content of the file if it was only linked if (m_fileLinked) this->read(); return !reader->hasError(); } diff --git a/src/backend/datasources/LiveDataSource.h b/src/backend/datasources/LiveDataSource.h index 345297e13..8cc9cf0ef 100644 --- a/src/backend/datasources/LiveDataSource.h +++ b/src/backend/datasources/LiveDataSource.h @@ -1,212 +1,212 @@ /*************************************************************************** File : LiveDataSource.h Project : LabPlot Description : File data source -------------------------------------------------------------------- Copyright : (C) 2012-2013 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 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 * * * ***************************************************************************/ #ifndef LIVEDATASOURCE_H #define LIVEDATASOURCE_H #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include #include #include class QString; class AbstractFileFilter; class QFileSystemWatcher; class QAction; class QTcpSocket; class QUdpSocket; class QFile; class LiveDataSource : public Spreadsheet { Q_OBJECT Q_ENUMS(FileType) public: enum FileType {Ascii, Binary, Image, HDF5, NETCDF, FITS, Json}; enum SourceType { FileOrPipe = 0, NetworkTcpSocket, NetworkUdpSocket, LocalSocket, SerialPort }; enum UpdateType { TimeInterval = 0, NewData }; enum ReadingType { - ContinousFixed = 0, + ContinuousFixed = 0, FromEnd, - TillEnd, - WholeFile + TillEnd, + WholeFile }; LiveDataSource(AbstractScriptingEngine*, const QString& name, bool loading = false); ~LiveDataSource() override; void ready(); static QStringList supportedBaudRates(); static QStringList availablePorts(); static QStringList fileTypes(); static QString fileInfoString(const QString&); void setFileType(const FileType); FileType fileType() const; UpdateType updateType() const; void setUpdateType(UpdateType); SourceType sourceType() const; void setSourceType(SourceType); ReadingType readingType() const; void setReadingType(ReadingType); int sampleRate() const; void setSampleRate(int); void setBytesRead(qint64 bytes); int bytesRead() const; int port() const; void setPort(quint16); bool isPaused() const; void setSerialPort(const QString& name); QString serialPortName() const; QString host() const; void setHost(const QString&); int baudRate() const; void setBaudRate(int); void setUpdateInterval(int); int updateInterval() const; void setKeepNvalues(int); int keepNvalues() const; void setKeepLastValues(bool); bool keepLastValues() const; void setFileWatched(bool); bool isFileWatched() const; void setFileLinked(bool); bool isFileLinked() const; void setFileName(const QString&); QString fileName() const; void setLocalSocketName(const QString&); QString localSocketName() const; void updateNow(); void pauseReading(); void continueReading(); void setFilter(AbstractFileFilter*); AbstractFileFilter* filter() const; QIcon icon() const override; QMenu* createContextMenu() override; QWidget* view() const override; void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; private: void initActions(); void watch(); QString m_fileName; QString m_serialPortName; QString m_localSocketName; QString m_host; FileType m_fileType; UpdateType m_updateType; SourceType m_sourceType; ReadingType m_readingType; bool m_fileWatched; bool m_fileLinked; bool m_paused; bool m_prepared; bool m_keepLastValues; int m_sampleRate; int m_keepNvalues; int m_updateInterval; quint16 m_port; int m_baudRate; qint64 m_bytesRead; AbstractFileFilter* m_filter; QTimer* m_updateTimer; QFileSystemWatcher* m_fileSystemWatcher; QFile* m_file; QLocalSocket* m_localSocket; QTcpSocket* m_tcpSocket; QUdpSocket* m_udpSocket; QSerialPort* m_serialPort; QIODevice* m_device; QAction* m_reloadAction; QAction* m_toggleLinkAction; QAction* m_showEditorAction; QAction* m_showSpreadsheetAction; QAction* m_plotDataAction; public slots: void read(); private slots: void watchToggled(); void linkToggled(); void plotData(); void readyRead(); void localSocketError(QLocalSocket::LocalSocketError); void tcpSocketError(QAbstractSocket::SocketError); void serialPortError(QSerialPort::SerialPortError); }; #endif diff --git a/src/backend/datasources/filters/AbstractFileFilter.cpp b/src/backend/datasources/filters/AbstractFileFilter.cpp index 8120569b9..49d1fa92c 100644 --- a/src/backend/datasources/filters/AbstractFileFilter.cpp +++ b/src/backend/datasources/filters/AbstractFileFilter.cpp @@ -1,87 +1,87 @@ /*************************************************************************** File : AbstractFileFilter.h Project : LabPlot Description : file I/O-filter related interface -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/datasources/filters/AbstractFileFilter.h" #include "backend/lib/macros.h" #include #include -#include +#include bool AbstractFileFilter::isNan(QString s) { QStringList nanStrings; nanStrings << "NA" << "NAN" << "N/A" << "-NA" << "-NAN" << "NULL"; if (nanStrings.contains(s, Qt::CaseInsensitive)) return true; return false; } AbstractColumn::ColumnMode AbstractFileFilter::columnMode(const QString& valueString, const QString& dateTimeFormat, QLocale::Language lang) { QLocale locale(lang); if (valueString.size() == 0) // empty string treated as integer (meaning the non-empty strings will determine the data type) return AbstractColumn::Integer; if (isNan(valueString)) return AbstractColumn::Numeric; // check if integer first bool ok; locale.toInt(valueString, &ok); DEBUG("string " << valueString.toStdString() << ": toInt " << locale.toInt(valueString, &ok) << "?:" << ok); if (ok || isNan(valueString)) return AbstractColumn::Integer; //try to convert to a double AbstractColumn::ColumnMode mode = AbstractColumn::Numeric; locale.toDouble(valueString, &ok); DEBUG("string " << valueString.toStdString() << ": toDouble " << locale.toDouble(valueString, &ok) << "?:" << ok); //if not a number, check datetime. if that fails: string if (!ok) { QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); if (valueDateTime.isValid()) mode = AbstractColumn::DateTime; else mode = AbstractColumn::Text; } return mode; } /* returns the list of all supported locales for numeric data */ QStringList AbstractFileFilter::numberFormats() { QStringList formats; for (int l = 0; l < ENUM_COUNT(QLocale, Language); ++l) formats << QLocale::languageToString((QLocale::Language)l); return formats; } diff --git a/src/backend/datasources/filters/AsciiFilter.cpp b/src/backend/datasources/filters/AsciiFilter.cpp index a0d37a5f7..777c5db66 100644 --- a/src/backend/datasources/filters/AsciiFilter.cpp +++ b/src/backend/datasources/filters/AsciiFilter.cpp @@ -1,1553 +1,1619 @@ /*************************************************************************** File : AsciiFilter.cpp Project : LabPlot Description : ASCII I/O-filter -------------------------------------------------------------------- -Copyright : (C) 2009-2017 Stefan Gerlach (stefan.gerlach@uni.kn) +Copyright : (C) 2009-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2017 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 "backend/datasources/LiveDataSource.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/AsciiFilterPrivate.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include -#include +#include #include #include #include /*! \class AsciiFilter \brief Manages the import/export of data organized as columns (vectors) from/to an ASCII-file. \ingroup datasources */ AsciiFilter::AsciiFilter() : AbstractFileFilter(), d(new AsciiFilterPrivate(this)) {} AsciiFilter::~AsciiFilter() {} /*! reads the content of the device \c device. */ void AsciiFilter::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromDevice(device, dataSource, importMode, lines); } void AsciiFilter::readFromLiveDeviceNotFile(QIODevice &device, AbstractDataSource * dataSource) { d->readFromLiveDevice(device, dataSource); } qint64 AsciiFilter::readFromLiveDevice(QIODevice& device, AbstractDataSource* dataSource, qint64 from) { return d->readFromLiveDevice(device, dataSource, from); } /*! reads the content of the file \c fileName. */ QVector AsciiFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromFile(fileName, dataSource, importMode, lines); return QVector(); //TODO: remove this later once all read*-functions in the filter classes don't return any preview strings anymore } QVector AsciiFilter::preview(const QString& fileName, int lines) { return d->preview(fileName, lines); } QVector AsciiFilter::preview(QIODevice &device) { return d->preview(device); } /*! reads the content of the file \c fileName to the data source \c dataSource. */ //void AsciiFilter::read(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { // d->read(fileName, dataSource, importMode); //} /*! writes the content of the data source \c dataSource to the file \c fileName. */ void AsciiFilter::write(const QString& fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); // emit() } /*! loads the predefined filter settings for \c filterName */ void AsciiFilter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void AsciiFilter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /*! returns the list with the names of all saved (system wide or user defined) filter settings. */ QStringList AsciiFilter::predefinedFilters() { return QStringList(); } /*! returns the list of all predefined separator characters. */ QStringList AsciiFilter::separatorCharacters() { return (QStringList() << "auto" << "TAB" << "SPACE" << "," << ";" << ":" << ",TAB" << ";TAB" << ":TAB" << ",SPACE" << ";SPACE" << ":SPACE" << "2xSPACE" << "3xSPACE" << "4xSPACE" << "2xTAB"); } /*! returns the list of all predefined comment characters. */ QStringList AsciiFilter::commentCharacters() { return (QStringList() << "#" << "!" << "//" << "+" << "c" << ":" << ";"); } /*! returns the list of all predefined data types. */ QStringList AsciiFilter::dataTypes() { const QMetaObject& mo = AbstractColumn::staticMetaObject; const QMetaEnum& me = mo.enumerator(mo.indexOfEnumerator("ColumnMode")); QStringList list; for (int i = 0; i <= 100; ++i) // me.keyCount() does not work because we have holes in enum if (me.valueToKey(i)) list << me.valueToKey(i); return list; } /*! returns the number of columns in the file \c fileName. */ int AsciiFilter::columnNumber(const QString& fileName, const QString& separator) { KFilterDev device(fileName); if (!device.open(QIODevice::ReadOnly)) { DEBUG("Could not open file " << fileName.toStdString() << " for determining number of columns"); return -1; } QString line = device.readLine(); line.remove(QRegExp("[\\n\\r]")); QStringList lineStringList; if (separator.length() > 0) lineStringList = line.split(separator); else lineStringList = line.split(QRegExp("\\s+")); DEBUG("number of columns : " << lineStringList.size()); return lineStringList.size(); } size_t AsciiFilter::lineNumber(const QString& fileName) { KFilterDev device(fileName); if (!device.open(QIODevice::ReadOnly)) { - DEBUG("Could not open file " << fileName.toStdString() << " for determining number of lines"); + DEBUG("Could not open file " << fileName.toStdString() << " to determine number of lines"); return 0; } + if (!device.canReadLine()) + return -1; size_t lineCount = 0; while (!device.atEnd()) { device.readLine(); lineCount++; } //TODO: wc is much faster but not portable /* QElapsedTimer myTimer; myTimer.start(); QProcess wc; wc.start(QString("wc"), QStringList() << "-l" << fileName); size_t lineCount = 0; while (wc.waitForReadyRead()) lineCount = wc.readLine().split(' ')[0].toInt(); lineCount++; // last line not counted DEBUG(" Elapsed time counting lines : " << myTimer.elapsed() << " ms"); */ return lineCount; } /*! - returns the number of lines in the device \c device or 0 if not available. + returns the number of lines in the device \c device and 0 if sequential. resets the position to 0! */ size_t AsciiFilter::lineNumber(QIODevice &device) { - // device.hasReadLine() always returns 0 for KFilterDev! if (device.isSequential()) return 0; + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilter::lineNumber(): device cannot 'readLine()' but using it anyway."); size_t lineCount = 0; device.seek(0); while (!device.atEnd()) { device.readLine(); lineCount++; } device.seek(0); return lineCount; } void AsciiFilter::setCommentCharacter(const QString& s) { d->commentCharacter = s; } QString AsciiFilter::commentCharacter() const { return d->commentCharacter; } void AsciiFilter::setSeparatingCharacter(const QString& s) { d->separatingCharacter = s; } QString AsciiFilter::separatingCharacter() const { return d->separatingCharacter; } void AsciiFilter::setDateTimeFormat(const QString &f) { d->dateTimeFormat = f; } QString AsciiFilter::dateTimeFormat() const { return d->dateTimeFormat; } void AsciiFilter::setNumberFormat(QLocale::Language lang) { d->numberFormat = lang; } QLocale::Language AsciiFilter::numberFormat() const { return d->numberFormat; } void AsciiFilter::setAutoModeEnabled(const bool b) { d->autoModeEnabled = b; } bool AsciiFilter::isAutoModeEnabled() const { return d->autoModeEnabled; } void AsciiFilter::setHeaderEnabled(const bool b) { d->headerEnabled = b; } bool AsciiFilter::isHeaderEnabled() const { return d->headerEnabled; } void AsciiFilter::setSkipEmptyParts(const bool b) { d->skipEmptyParts = b; } bool AsciiFilter::skipEmptyParts() const { return d->skipEmptyParts; } void AsciiFilter::setCreateIndexEnabled(bool b) { d->createIndexEnabled = b; } void AsciiFilter::setSimplifyWhitespacesEnabled(bool b) { d->simplifyWhitespacesEnabled = b; } bool AsciiFilter::simplifyWhitespacesEnabled() const { return d->simplifyWhitespacesEnabled; } void AsciiFilter::setNaNValueToZero(bool b) { if (b) d->nanValue = 0; else d->nanValue = NAN; } bool AsciiFilter::NaNValueToZeroEnabled() const { if (d->nanValue == 0) return true; return false; } void AsciiFilter::setRemoveQuotesEnabled(bool b) { d->removeQuotesEnabled = b; } bool AsciiFilter::removeQuotesEnabled() const { return d->removeQuotesEnabled; } void AsciiFilter::setVectorNames(const QString& s) { d->vectorNames.clear(); if (!s.simplified().isEmpty()) d->vectorNames = s.simplified().split(' '); } QStringList AsciiFilter::vectorNames() const { return d->vectorNames; } QVector AsciiFilter::columnModes() { return d->columnModes; } void AsciiFilter::setStartRow(const int r) { d->startRow = r; } int AsciiFilter::startRow() const { return d->startRow; } void AsciiFilter::setEndRow(const int r) { d->endRow = r; } int AsciiFilter::endRow() const { return d->endRow; } void AsciiFilter::setStartColumn(const int c) { d->startColumn = c; } int AsciiFilter::startColumn() const { return d->startColumn; } void AsciiFilter::setEndColumn(const int c) { d->endColumn = c; } int AsciiFilter::endColumn() const { return d->endColumn; } //##################################################################### //################### Private implementation ########################## //##################################################################### AsciiFilterPrivate::AsciiFilterPrivate(AsciiFilter* owner) : q(owner), commentCharacter("#"), separatingCharacter("auto"), numberFormat(QLocale::C), autoModeEnabled(true), headerEnabled(true), skipEmptyParts(false), simplifyWhitespacesEnabled(true), nanValue(NAN), removeQuotesEnabled(false), createIndexEnabled(false), startRow(1), endRow(-1), startColumn(1), endColumn(-1), m_actualStartRow(1), m_actualRows(0), m_actualCols(0), m_prepared(false), m_columnOffset(0) { } +/*! + * get a single line from device + */ QStringList AsciiFilterPrivate::getLineString(QIODevice& device) { QString line; do { // skip comment lines in data lines + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilterPrivate::getLineString(): device cannot 'readLine()' but using it anyway."); +// line = device.readAll(); line = device.readLine(); } while (line.startsWith(commentCharacter)); line.remove(QRegExp("[\\n\\r]")); // remove any newline if (simplifyWhitespacesEnabled) line = line.simplified(); DEBUG("data line : \'" << line.toStdString() << '\''); QStringList lineStringList = line.split(m_separator, (QString::SplitBehavior)skipEmptyParts); //TODO: remove quotes here? QDEBUG("data line, parsed: " << lineStringList); return lineStringList; } /*! * returns -1 if the device couldn't be opened, 1 if the current read position in the device is at the end and 0 otherwise. */ int AsciiFilterPrivate::prepareDeviceToRead(QIODevice& device) { - DEBUG("device is sequential = " << device.isSequential()); + DEBUG("AsciiFilterPrivate::prepareDeviceToRead(): is sequential = " << device.isSequential() << ", can readLine = " << device.canReadLine()); if (!device.open(QIODevice::ReadOnly)) return -1; if (device.atEnd() && !device.isSequential()) // empty file return 1; ///////////////////////////////////////////////////////////////// // Find first data line (ignoring comment lines) - DEBUG("Skipping " << startRow - 1 << " lines"); + DEBUG(" Skipping " << startRow - 1 << " lines"); for (int i = 0; i < startRow - 1; ++i) { - QString line = device.readLine(); + QString line; + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilterPrivate::prepareDeviceToRead(): device cannot 'readLine()' but using it anyway."); + line = device.readLine(); + DEBUG(" line = " << line.toStdString()); if (device.atEnd()) { if (device.isSequential()) break; else return 1; } - //TOOD: this logic seems to be wrong. If the user asks to read from line startRow, we should start here independen of any comments + //TOOD: this logic seems to be wrong. If the user asks to read from line startRow, we should start here independent of any comments if (line.startsWith(commentCharacter)) // ignore commented lines before startRow i--; } - // Parse the first line: // Determine the number of columns, create the columns and use (if selected) the first row to name them QString firstLine; do { // skip comment lines + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilterPrivate::prepareDeviceToRead(): device cannot 'readLine()' but using it anyway."); + firstLine = device.readLine(); + if (device.atEnd()) { if (device.isSequential()) break; else return 1; } } while (firstLine.startsWith(commentCharacter)); DEBUG(" device position after first line and comments = " << device.pos()); firstLine.remove(QRegExp("[\\n\\r]")); // remove any newline if (simplifyWhitespacesEnabled) firstLine = firstLine.simplified(); DEBUG("First line: \'" << firstLine.toStdString() << '\''); // determine separator and split first line QStringList firstLineStringList; if (separatingCharacter == "auto") { DEBUG("automatic separator"); QRegExp regExp("(\\s+)|(,\\s+)|(;\\s+)|(:\\s+)"); firstLineStringList = firstLine.split(regExp, (QString::SplitBehavior)skipEmptyParts); if (!firstLineStringList.isEmpty()) { int length1 = firstLineStringList.at(0).length(); if (firstLineStringList.size() > 1) { int pos2 = firstLine.indexOf(firstLineStringList.at(1), length1); m_separator = firstLine.mid(length1, pos2 - length1); } else { //old: separator = line.right(line.length() - length1); m_separator = ' '; } } } else { // use given separator // replace symbolic "TAB" with '\t' m_separator = separatingCharacter.replace(QLatin1String("2xTAB"), "\t\t", Qt::CaseInsensitive); m_separator = separatingCharacter.replace(QLatin1String("TAB"), "\t", Qt::CaseInsensitive); // replace symbolic "SPACE" with ' ' m_separator = m_separator.replace(QLatin1String("2xSPACE"), QLatin1String(" "), Qt::CaseInsensitive); m_separator = m_separator.replace(QLatin1String("3xSPACE"), QLatin1String(" "), Qt::CaseInsensitive); m_separator = m_separator.replace(QLatin1String("4xSPACE"), QLatin1String(" "), Qt::CaseInsensitive); m_separator = m_separator.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); firstLineStringList = firstLine.split(m_separator, (QString::SplitBehavior)skipEmptyParts); } DEBUG("separator: \'" << m_separator.toStdString() << '\''); DEBUG("number of columns: " << firstLineStringList.size()); QDEBUG("first line: " << firstLineStringList); DEBUG("headerEnabled = " << headerEnabled); //optionally, remove potential spaces in the first line if (simplifyWhitespacesEnabled) { for (int i = 0; i < firstLineStringList.size(); ++i) firstLineStringList[i] = firstLineStringList[i].simplified(); } if (headerEnabled) { // use first line to name vectors vectorNames = firstLineStringList; QDEBUG("vector names =" << vectorNames); m_actualStartRow = startRow + 1; } else m_actualStartRow = startRow; // set range to read if (endColumn == -1) { if (headerEnabled || vectorNames.size() == 0) endColumn = firstLineStringList.size(); // last column else //number of vector names provided in the import dialog (not more than the maximal number of columns in the file) endColumn = qMin(vectorNames.size(), firstLineStringList.size()); } if (createIndexEnabled) { - vectorNames.prepend("index"); + vectorNames.prepend(i18n("Index")); endColumn++; } m_actualCols = endColumn - startColumn + 1; //TEST: readline-seek-readline fails /* qint64 testpos = device.pos(); DEBUG("read data line @ pos " << testpos << " : " << device.readLine().toStdString()); device.seek(testpos); testpos = device.pos(); DEBUG("read data line again @ pos " << testpos << " : " << device.readLine().toStdString()); */ ///////////////////////////////////////////////////////////////// // parse first data line to determine data type for each column if (!device.isSequential()) firstLineStringList = getLineString(device); columnModes.resize(m_actualCols); int col = 0; if (createIndexEnabled) { columnModes[0] = AbstractColumn::Integer; col = 1; } for (auto& valueString: firstLineStringList) { // parse columns available in first data line if (simplifyWhitespacesEnabled) valueString = valueString.simplified(); if (col == m_actualCols) break; columnModes[col++] = AbstractFileFilter::columnMode(valueString, dateTimeFormat, numberFormat); } // parsing more lines to better determine data types for (unsigned int i = 0; i < m_dataTypeLines; ++i) { firstLineStringList = getLineString(device); if (createIndexEnabled) col = 1; else col = 0; for (auto& valueString: firstLineStringList) { if (simplifyWhitespacesEnabled) valueString = valueString.simplified(); if (col == m_actualCols) break; AbstractColumn::ColumnMode mode = AbstractFileFilter::columnMode(valueString, dateTimeFormat, numberFormat); // numeric: integer -> numeric if (mode == AbstractColumn::Numeric && columnModes[col] == AbstractColumn::Integer) columnModes[col] = mode; // text: non text -> text if (mode == AbstractColumn::Text && columnModes[col] != AbstractColumn::Text) columnModes[col] = mode; col++; } } QDEBUG("column modes = " << columnModes); // ATTENTION: This resets the position in the device to 0 m_actualRows = (int)AsciiFilter::lineNumber(device); // reset to start of file //TODO: seems to be redundant since it's already done in the lineNumber() call above if (!device.isSequential()) device.seek(0); ///////////////////////////////////////////////////////////////// int actualEndRow = endRow; - DEBUG("endRow = " << endRow); + DEBUG("endRow(actualEndRow) = " << endRow << ", m_actualRows = " << m_actualRows); if (endRow == -1 || endRow > m_actualRows) actualEndRow = m_actualRows; if (m_actualRows > actualEndRow) m_actualRows = actualEndRow; - DEBUG("start/end column: " << startColumn << ' ' << endColumn); DEBUG("start/end row: " << m_actualStartRow << ' ' << actualEndRow); DEBUG("actual cols/rows (w/o header incl. start rows): " << m_actualCols << ' ' << m_actualRows); if (m_actualRows == 0 && !device.isSequential()) return 1; return 0; } /*! reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. */ void AsciiFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { DEBUG("AsciiFilterPrivate::readDataFromFile(): fileName = \'" << fileName.toStdString() << "\', dataSource = " << dataSource << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, importMode) << ", lines = " << lines); KFilterDev device(fileName); readDataFromDevice(device, dataSource, importMode, lines); } qint64 AsciiFilterPrivate::readFromLiveDevice(QIODevice& device, AbstractDataSource* dataSource, qint64 from) { + DEBUG("AsciiFilterPrivate::readFromLiveDevice(): bytes available = " << device.bytesAvailable() << ", from = " << from); if (!(device.bytesAvailable() > 0)) { - DEBUG("No new data available"); + DEBUG(" No new data available"); return 0; } LiveDataSource* spreadsheet = dynamic_cast(dataSource); if (spreadsheet->sourceType() != LiveDataSource::SourceType::FileOrPipe) if (device.isSequential() && device.bytesAvailable() < (int)sizeof(quint16)) return 0; if (!m_prepared) { - const int deviceError = prepareDeviceToRead(device); - if (deviceError != 0) { - DEBUG("Device error = " << deviceError); - return 0; + DEBUG(" Preparing .."); + + switch (spreadsheet->sourceType()) { + case LiveDataSource::SourceType::FileOrPipe: { + const int deviceError = prepareDeviceToRead(device); + if (deviceError != 0) { + DEBUG("Device error = " << deviceError); + return 0; + } + break; + } + case LiveDataSource::SourceType::NetworkTcpSocket: + case LiveDataSource::SourceType::NetworkUdpSocket: + case LiveDataSource::SourceType::LocalSocket: + case LiveDataSource::SourceType::SerialPort: + m_actualRows = 1; + if (createIndexEnabled) { + m_actualCols = 2; + columnModes << AbstractColumn::Integer << AbstractColumn::Numeric; + vectorNames << i18n("Index") << i18n("Value"); + } else { + m_actualCols = 1; + columnModes << AbstractColumn::Numeric; + vectorNames << i18n("Value"); + } + QDEBUG(" vector names = " << vectorNames); } // prepare import for spreadsheet spreadsheet->setUndoAware(false); spreadsheet->resize(AbstractFileFilter::Replace, vectorNames, m_actualCols); - qDebug() << "fds resized to col: " << m_actualCols; - qDebug() << "fds rowCount: " << spreadsheet->rowCount(); + DEBUG(" data source resized to col: " << m_actualCols); + DEBUG(" data source rowCount: " << spreadsheet->rowCount()); //columns in a file data source don't have any manual changes. //make the available columns undo unaware and suppress the "data changed" signal. //data changes will be propagated via an explicit Column::setChanged() call once new data was read. for (int i = 0; i < spreadsheet->childCount(); i++) { spreadsheet->child(i)->setUndoAware(false); spreadsheet->child(i)->setSuppressDataChangedSignal(true); } //also here we need a cheaper version of this if (!spreadsheet->keepLastValues()) spreadsheet->setRowCount(m_actualRows > 1 ? m_actualRows : 1); else { spreadsheet->setRowCount(spreadsheet->keepNvalues()); m_actualRows = spreadsheet->keepNvalues(); } m_dataContainer.resize(m_actualCols); + DEBUG(" Setting data .."); for (int n = 0; n < m_actualCols; ++n) { // data() returns a void* which is a pointer to any data type (see ColumnPrivate.cpp) spreadsheet->child(n)->setColumnMode(columnModes[n]); switch (columnModes[n]) { case AbstractColumn::Numeric: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Integer: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Text: { QVector* vector = static_cast*>(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::DateTime: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } //TODO case AbstractColumn::Month: case AbstractColumn::Day: break; } } - qDebug() << "prepared!"; + DEBUG("prepared!"); } qint64 bytesread = 0; #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportTotal: "); #endif LiveDataSource::ReadingType readingType; - if (!m_prepared) + if (!m_prepared) { readingType = LiveDataSource::ReadingType::TillEnd; - else { + } else { //we have to read all the data when reading from end //so we set readingType to TillEnd if (spreadsheet->readingType() == LiveDataSource::ReadingType::FromEnd) readingType = LiveDataSource::ReadingType::TillEnd; - //if we read the whole file we just start from the beginning of it - //and read till end - else if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - readingType = LiveDataSource::ReadingType::TillEnd; - } + //if we read the whole file we just start from the beginning of it + //and read till end + else if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) + readingType = LiveDataSource::ReadingType::TillEnd; else readingType = spreadsheet->readingType(); } //move to the last read position, from == total bytes read //since the other source types are sequencial we cannot seek on them if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) device.seek(from); - qDebug() <<"available bytes: " << device.bytesAvailable(); + DEBUG(" bytes available: " << device.bytesAvailable()); //count the new lines, increase actualrows on each //now we read all the new lines, if we want to use sample rate //then here we can do it, if we have actually sample rate number of lines :-? int newLinesForSampleRateNotTillEnd = 0; int newLinesTillEnd = 0; QVector newData; - if (readingType != LiveDataSource::ReadingType::TillEnd) { - newData.reserve(spreadsheet->sampleRate()); + if (readingType != LiveDataSource::ReadingType::TillEnd) newData.resize(spreadsheet->sampleRate()); - } int newDataIdx = 0; { #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportReadingFromFile: "); #endif while (!device.atEnd()) { - if (readingType != LiveDataSource::ReadingType::TillEnd) - newData[newDataIdx++] = device.readLine(); - else - newData.push_back(device.readLine()); + if (readingType != LiveDataSource::ReadingType::TillEnd) { + // local socket and UDP socket needs readAll(), all other need readLine() + //TODO: check serial port + if (spreadsheet->sourceType() == LiveDataSource::SourceType::LocalSocket + || spreadsheet->sourceType() == LiveDataSource::SourceType::NetworkUdpSocket) + newData[newDataIdx++] = device.readAll(); + else { + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilterPrivate::readFromLiveDevice(): device cannot 'readLine()' but using it anyway."); + newData[newDataIdx++] = device.readLine(); + } + } else { + if (spreadsheet->sourceType() == LiveDataSource::SourceType::LocalSocket + || spreadsheet->sourceType() == LiveDataSource::SourceType::NetworkUdpSocket) + newData.push_back(device.readAll()); + else { + if (!device.canReadLine()) + DEBUG("WARNING in AsciiFilterPrivate::readFromLiveDevice(): device cannot 'readLine()' but using it anyway."); + newData.push_back(device.readLine()); + } + } newLinesTillEnd++; if (readingType != LiveDataSource::ReadingType::TillEnd) { newLinesForSampleRateNotTillEnd++; - //for Continous reading and FromEnd we read sample rate number of lines if possible + //for Continuous reading and FromEnd we read sample rate number of lines if possible + //here TillEnd and Whole file behave the same if (newLinesForSampleRateNotTillEnd == spreadsheet->sampleRate()) break; } } + QDEBUG(" data read: " << newData); } //now we reset the readingType if (spreadsheet->readingType() == LiveDataSource::ReadingType::FromEnd) readingType = spreadsheet->readingType(); //we had less new lines than the sample rate specified if (readingType != LiveDataSource::ReadingType::TillEnd) qDebug() << "Removed empty lines: " << newData.removeAll(""); //back to the last read position before counting when reading from files if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) device.seek(from); const int spreadsheetRowCountBeforeResize = spreadsheet->rowCount(); int currentRow = 0; // indexes the position in the vector(column) int linesToRead = 0; if (m_prepared) { //increase row count if we don't have a fixed size //but only after the preparation step if (!spreadsheet->keepLastValues()) { if (readingType != LiveDataSource::ReadingType::TillEnd) m_actualRows += qMin(newData.size(), spreadsheet->sampleRate()); - else { - //we don't increase it if we reread the whole file, we reset it - if (!(spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile)) { - m_actualRows += newData.size(); - } else { - m_actualRows = newData.size(); - } - } + else { + //we don't increase it if we reread the whole file, we reset it + if (!(spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile)) + m_actualRows += newData.size(); + else + m_actualRows = newData.size(); + } } //fixed size if (spreadsheet->keepLastValues()) { if (readingType == LiveDataSource::ReadingType::TillEnd) { //we had more lines than the fixed size, so we read m_actualRows number of lines if (newLinesTillEnd > m_actualRows) { linesToRead = m_actualRows; //TODO after reading we should skip the next data lines //because it's TillEnd actually } else linesToRead = newLinesTillEnd; } else { //we read max sample rate number of lines when the reading mode - //is ContinouslyFixed or FromEnd + //is ContinuouslyFixed or FromEnd, WholeFile is disabled linesToRead = qMin(spreadsheet->sampleRate(), newLinesTillEnd); } } else { //appending - if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - linesToRead = m_actualRows; - } else { - linesToRead = m_actualRows - spreadsheetRowCountBeforeResize; - } + if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) + linesToRead = m_actualRows; + else + linesToRead = m_actualRows - spreadsheetRowCountBeforeResize; } if (linesToRead == 0) return 0; - } else + } else { linesToRead = newLinesTillEnd; + if (headerEnabled) + --m_actualRows; + } //new rows/resize columns if we don't have a fixed size //TODO if the user changes this value..m_resizedToFixedSize..setResizedToFixedSize if (!spreadsheet->keepLastValues()) { #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportResizing: "); #endif if (spreadsheet->rowCount() < m_actualRows) spreadsheet->setRowCount(m_actualRows); if (!m_prepared) currentRow = 0; else { // indexes the position in the vector(column) - if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - currentRow = 0; - } else { - currentRow = spreadsheetRowCountBeforeResize; - } + if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) + currentRow = 0; + else + currentRow = spreadsheetRowCountBeforeResize; } // if we have fixed size, we do this only once in preparation, here we can use // m_prepared and we need something to decide whether it has a fixed size or increasing for (int n = 0; n < m_actualCols; ++n) { // data() returns a void* which is a pointer to any data type (see ColumnPrivate.cpp) switch (columnModes[n]) { case AbstractColumn::Numeric: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Integer: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Text: { QVector* vector = static_cast*>(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } case AbstractColumn::DateTime: { QVector* vector = static_cast* >(spreadsheet->child(n)->data()); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[n] = static_cast(vector); break; } //TODO case AbstractColumn::Month: case AbstractColumn::Day: break; } } } else { //when we have a fixed size we have to pop sampleRate number of lines if specified //here popping, setting currentRow - if (!m_prepared) - if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - currentRow = 0; - } else - currentRow = m_actualRows - qMin(newLinesTillEnd, m_actualRows); - else { + if (!m_prepared) { + if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) + currentRow = 0; + else + currentRow = m_actualRows - qMin(newLinesTillEnd, m_actualRows); + } else { if (readingType == LiveDataSource::ReadingType::TillEnd) { - if (newLinesTillEnd > m_actualRows) + if (newLinesTillEnd > m_actualRows) { currentRow = 0; - else { - if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - currentRow = 0; - } else { - currentRow = m_actualRows - newLinesTillEnd; - } - } + } else { + if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) + currentRow = 0; + else + currentRow = m_actualRows - newLinesTillEnd; + } } else { //we read max sample rate number of lines when the reading mode - //is ContinouslyFixed or FromEnd + //is ContinuouslyFixed or FromEnd currentRow = m_actualRows - qMin(spreadsheet->sampleRate(), newLinesTillEnd); } } if (m_prepared) { #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportPopping: "); #endif - for (int row = 0; row < linesToRead; ++row) { + for (int row = 0; row < linesToRead; ++row) { for (int col = 0; col < m_actualCols; ++col) { switch (columnModes[col]) { case AbstractColumn::Numeric: { QVector* vector = static_cast* >(spreadsheet->child(col)->data()); vector->pop_front(); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[col] = static_cast(vector); break; } case AbstractColumn::Integer: { QVector* vector = static_cast* >(spreadsheet->child(col)->data()); vector->pop_front(); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[col] = static_cast(vector); break; } case AbstractColumn::Text: { QVector* vector = static_cast*>(spreadsheet->child(col)->data()); vector->pop_front(); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[col] = static_cast(vector); break; } case AbstractColumn::DateTime: { QVector* vector = static_cast* >(spreadsheet->child(col)->data()); vector->pop_front(); - vector->reserve(m_actualRows); vector->resize(m_actualRows); m_dataContainer[col] = static_cast(vector); break; } //TODO case AbstractColumn::Month: case AbstractColumn::Day: break; } } } } } // from the last row we read the new data in the spreadsheet qDebug() << "reading from line: " << currentRow << " lines till end: " << newLinesTillEnd; - qDebug() << "Lines to read: " << linesToRead <<" actual rows: " << m_actualRows; + qDebug() << "Lines to read: " << linesToRead <<" actual rows: " << m_actualRows << ", actual cols: " << m_actualCols; newDataIdx = 0; if (readingType == LiveDataSource::ReadingType::FromEnd) { if (m_prepared) { if (newData.size() > spreadsheet->sampleRate()) newDataIdx = newData.size() - spreadsheet->sampleRate(); //since we skip a couple of lines, we need to count those bytes too for (int i = 0; i < newDataIdx; ++i) bytesread += newData.at(i).size(); } } qDebug() << "newDataIdx: " << newDataIdx; //TODO static int indexColumnIdx = 0; { #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportFillingContainers: "); #endif - int row = 0; - //skip the header - if (spreadsheet->readingType() == LiveDataSource::ReadingType::WholeFile) { - row = 1; - } - for (; row < linesToRead; ++row) { + int row = 0; + + if (readingType == LiveDataSource::ReadingType::TillEnd || (readingType == LiveDataSource::ReadingType::ContinuousFixed)) { + if (headerEnabled) { + if (!m_prepared) { + row = 1; + bytesread += newData.at(0).size(); + } + } + } + if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) { + if (readingType == LiveDataSource::ReadingType::WholeFile) { + if (headerEnabled) { + row = 1; + bytesread += newData.at(0).size(); + } + } + } + + for (; row < linesToRead; ++row) { + DEBUG(" row = " << row); QString line; if (readingType == LiveDataSource::ReadingType::FromEnd) line = newData.at(newDataIdx++); else - line = newData.at(row); - //when we read the whole file we don't care about the previous position - //so we don't have to count those bytes - if (readingType != LiveDataSource::ReadingType::WholeFile) { - if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) { - bytesread += line.size(); - } - } + line = newData.at(row); + //when we read the whole file we don't care about the previous position + //so we don't have to count those bytes + if (readingType != LiveDataSource::ReadingType::WholeFile) { + if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) { + bytesread += line.size(); + } + } //qDebug() << "line bytes: " << line.size() << " line: " << line; //qDebug() << "reading in row: " << currentRow; if (simplifyWhitespacesEnabled) line = line.simplified(); if (line.isEmpty() || line.startsWith(commentCharacter)) // skip empty or commented lines continue; QLocale locale(numberFormat); - QStringList lineStringList = line.split(m_separator, (QString::SplitBehavior)skipEmptyParts); - QDEBUG(" line = " << lineStringList); + QStringList lineStringList; + // only FileOrPipe support multiple columns + if (spreadsheet->sourceType() == LiveDataSource::SourceType::FileOrPipe) + lineStringList = line.split(m_separator, (QString::SplitBehavior)skipEmptyParts); + else + lineStringList << line; + QDEBUG(" line = " << lineStringList << ", separator = \'" << m_separator << "\'"); if (createIndexEnabled) { if (spreadsheet->keepLastValues()) lineStringList.prepend(QString::number(indexColumnIdx++)); else lineStringList.prepend(QString::number(currentRow)); } for (int n = 0; n < m_actualCols; ++n) { if (n < lineStringList.size()) { QString valueString = lineStringList.at(n); // set value depending on data type switch (columnModes[n]) { case AbstractColumn::Numeric: { bool isNumber; const double value = locale.toDouble(valueString, &isNumber); static_cast*>(m_dataContainer[n])->operator[](currentRow) = (isNumber ? value : nanValue); //qDebug() << "dataContainer[" << n << "] size:" << static_cast*>(m_dataContainer[n])->size(); break; } case AbstractColumn::Integer: { bool isNumber; const int value = locale.toInt(valueString, &isNumber); static_cast*>(m_dataContainer[n])->operator[](currentRow) = (isNumber ? value : 0); //qDebug() << "dataContainer[" << n << "] size:" << static_cast*>(m_dataContainer[n])->size(); break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); static_cast*>(m_dataContainer[n])->operator[](currentRow) = valueDateTime.isValid() ? valueDateTime : QDateTime(); break; } case AbstractColumn::Text: if (removeQuotesEnabled) valueString.remove(QRegExp("[\"\']")); static_cast*>(m_dataContainer[n])->operator[](currentRow) = valueString; break; case AbstractColumn::Month: //TODO break; case AbstractColumn::Day: //TODO break; } } else { // missing columns in this line switch (columnModes[n]) { case AbstractColumn::Numeric: static_cast*>(m_dataContainer[n])->operator[](currentRow) = nanValue; break; case AbstractColumn::Integer: static_cast*>(m_dataContainer[n])->operator[](currentRow) = 0; break; case AbstractColumn::DateTime: static_cast*>(m_dataContainer[n])->operator[](currentRow) = QDateTime(); break; case AbstractColumn::Text: static_cast*>(m_dataContainer[n])->operator[](currentRow) = ""; break; case AbstractColumn::Month: //TODO break; case AbstractColumn::Day: //TODO break; } } } currentRow++; } } if (m_prepared) { //notify all affected columns and plots about the changes PERFTRACE("AsciiLiveDataImport, notify affected columns and plots"); const Project* project = spreadsheet->project(); QVector curves = project->children(AbstractAspect::Recursive); QVector plots; for (int n = 0; n < m_actualCols; ++n) { Column* column = spreadsheet->column(n); //determine the plots where the column is consumed for (const auto* curve: curves) { if (curve->xColumn() == column || curve->yColumn() == column) { CartesianPlot* plot = dynamic_cast(curve->parentAspect()); if (plots.indexOf(plot) == -1) { plots << plot; plot->setSuppressDataChangedSignal(true); } } } column->setChanged(); } //loop over all affected plots and retransform them for (auto* plot: plots) { - //TODO setting this back to true triggers again a lot of retransforms in the plot (one for each curve). -// plot->setSuppressDataChangedSignal(false); + plot->setSuppressDataChangedSignal(false); plot->dataChanged(); } } m_prepared = true; return bytesread; } /*! reads the content of device \c device to the data source \c dataSource. Uses the settings defined in the data source. */ void AsciiFilterPrivate::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { DEBUG("AsciiFilterPrivate::readDataFromDevice(): dataSource = " << dataSource << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, importMode) << ", lines = " << lines); if (!m_prepared) { const int deviceError = prepareDeviceToRead(device); if (deviceError != 0) { DEBUG("Device error = " << deviceError); return; } // matrix data has only one column mode (which is not text) if (dynamic_cast(dataSource)) { auto mode = columnModes[0]; if (mode == AbstractColumn::Text) mode = AbstractColumn::Numeric; for (auto& c: columnModes) if (c != mode) c = mode; } m_columnOffset = dataSource->prepareImport(m_dataContainer, importMode, m_actualRows - m_actualStartRow + 1, m_actualCols, vectorNames, columnModes); m_prepared = true; } DEBUG("locale = " << QLocale::languageToString(numberFormat).toStdString()); QLocale locale(numberFormat); // Read the data int currentRow = 0; // indexes the position in the vector(column) if (lines == -1) lines = m_actualRows; DEBUG("reading " << qMin(lines, m_actualRows) << " lines"); for (int i = 0; i < qMin(lines, m_actualRows); ++i) { QString line = device.readLine(); // skip start lines if (m_actualStartRow > 1) { m_actualStartRow--; continue; } line.remove(QRegExp("[\\n\\r]")); // remove any newline if (simplifyWhitespacesEnabled) line = line.simplified(); if (line.isEmpty() || line.startsWith(commentCharacter)) // skip empty or commented lines continue; QStringList lineStringList = line.split(m_separator, (QString::SplitBehavior)skipEmptyParts); //prepend the index if required //TODO: come up maybe with a solution with adding the index inside of the loop below, //without conversion to string, prepending to the list and then conversion back to integer. if (createIndexEnabled) lineStringList.prepend(QString::number(i+1)); // remove left white spaces if (skipEmptyParts) { for (int n = 0; n < lineStringList.size(); ++n) { QString valueString = lineStringList.at(n); if (!QString::compare(valueString, " ")) { lineStringList.removeAt(n); n--; } } } for (int n = 0; n < m_actualCols; ++n) { if (n < lineStringList.size()) { QString valueString = lineStringList.at(n); // set value depending on data type switch (columnModes[n]) { case AbstractColumn::Numeric: { bool isNumber; const double value = locale.toDouble(valueString, &isNumber); static_cast*>(m_dataContainer[n])->operator[](currentRow) = (isNumber ? value : nanValue); break; } case AbstractColumn::Integer: { bool isNumber; const int value = locale.toInt(valueString, &isNumber); static_cast*>(m_dataContainer[n])->operator[](currentRow) = (isNumber ? value : 0); break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); static_cast*>(m_dataContainer[n])->operator[](currentRow) = valueDateTime.isValid() ? valueDateTime : QDateTime(); break; } case AbstractColumn::Text: if (removeQuotesEnabled) valueString.remove(QRegExp("[\"\']")); static_cast*>(m_dataContainer[n])->operator[](currentRow) = valueString; break; case AbstractColumn::Month: // never happens case AbstractColumn::Day: break; } } else { // missing columns in this line switch (columnModes[n]) { case AbstractColumn::Numeric: static_cast*>(m_dataContainer[n])->operator[](currentRow) = nanValue; break; case AbstractColumn::Integer: static_cast*>(m_dataContainer[n])->operator[](currentRow) = 0; break; case AbstractColumn::DateTime: static_cast*>(m_dataContainer[n])->operator[](currentRow) = QDateTime(); break; case AbstractColumn::Text: static_cast*>(m_dataContainer[n])->operator[](currentRow) = ""; break; case AbstractColumn::Month: // never happens case AbstractColumn::Day: break; } } } currentRow++; emit q->completed(100 * currentRow/m_actualRows); } dataSource->finalizeImport(m_columnOffset, startColumn, endColumn, dateTimeFormat, importMode); } QVector AsciiFilterPrivate::preview(QIODevice &device) { + DEBUG("AsciiFilterPrivate::preview(): bytesAvailable = " << device.bytesAvailable() << ", isSequential = " << device.isSequential()); QVector dataStrings; if (!(device.bytesAvailable() > 0)) { DEBUG("No new data available"); return dataStrings; } if (device.isSequential() && device.bytesAvailable() < (int)sizeof(quint16)) return dataStrings; #ifdef PERFTRACE_LIVE_IMPORT PERFTRACE("AsciiLiveDataImportTotal: "); #endif int linesToRead = 0; QVector newData; while (!device.atEnd()) { - newData.push_back(device.readLine()); + if (device.canReadLine()) + newData.push_back(device.readLine()); + else // UDP fails otherwise + newData.push_back(device.readAll()); linesToRead++; } + QDEBUG(" data = " << newData); if (linesToRead == 0) return dataStrings; int col = 0; int colMax = newData.at(0).size(); if (createIndexEnabled) colMax++; columnModes.resize(colMax); if (createIndexEnabled) { columnModes[0] = AbstractColumn::ColumnMode::Integer; col = 1; - vectorNames.prepend("index"); + vectorNames.prepend(i18n("Index")); } if (headerEnabled) { int i = 0; if (createIndexEnabled) i = 1; for (; i < vectorNames.size(); ++i) vectorNames[i] = "Column " + QString::number(i); } for (const auto& valueString: newData.at(0).split(' ', QString::SkipEmptyParts)) { if (col == colMax) break; columnModes[col++] = AbstractFileFilter::columnMode(valueString, dateTimeFormat, numberFormat); } for (int i = 0; i < linesToRead; ++i) { QString line = newData.at(i); if (simplifyWhitespacesEnabled) line = line.simplified(); if (line.isEmpty() || line.startsWith(commentCharacter)) // skip empty or commented lines continue; QLocale locale(numberFormat); QStringList lineStringList = line.split(' ', QString::SkipEmptyParts); if (createIndexEnabled) lineStringList.prepend(QString::number(i)); QStringList lineString; for (int n = 0; n < lineStringList.size(); ++n) { if (n < lineStringList.size()) { QString valueString = lineStringList.at(n); switch (columnModes[n]) { case AbstractColumn::Numeric: { bool isNumber; const double value = locale.toDouble(valueString, &isNumber); lineString += QString::number(isNumber ? value : nanValue, 'g', 16); break; } case AbstractColumn::Integer: { bool isNumber; const int value = locale.toInt(valueString, &isNumber); lineString += QString::number(isNumber ? value : 0); break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); lineString += valueDateTime.isValid() ? valueDateTime.toString(dateTimeFormat) : QLatin1String(" "); break; } case AbstractColumn::Text: if (removeQuotesEnabled) valueString.remove(QRegExp("[\"\']")); lineString += valueString; break; case AbstractColumn::Month: // never happens case AbstractColumn::Day: break; } } else // missing columns in this line lineString += QLatin1String(""); } dataStrings << lineString; } return dataStrings; } /*! * generates the preview for the file \c fileName reading the provided number of \c lines. */ QVector AsciiFilterPrivate::preview(const QString& fileName, int lines) { QVector dataStrings; KFilterDev device(fileName); const int deviceError = prepareDeviceToRead(device); if (deviceError != 0) { DEBUG("Device error = " << deviceError); return dataStrings; } //number formatting DEBUG("locale = " << QLocale::languageToString(numberFormat).toStdString()); QLocale locale(numberFormat); // Read the data if (lines == -1) lines = m_actualRows; DEBUG("generating preview for " << qMin(lines, m_actualRows) << " lines"); for (int i = 0; i < qMin(lines, m_actualRows); ++i) { QString line = device.readLine(); // skip start lines if (m_actualStartRow > 1) { m_actualStartRow--; continue; } line.remove(QRegExp("[\\n\\r]")); // remove any newline if (simplifyWhitespacesEnabled) line = line.simplified(); if (line.isEmpty() || line.startsWith(commentCharacter)) // skip empty or commented lines continue; QStringList lineStringList = line.split(m_separator, (QString::SplitBehavior)skipEmptyParts); QDEBUG(" line = " << lineStringList); //prepend index if required if (createIndexEnabled) lineStringList.prepend(QString::number(i+1)); QStringList lineString; for (int n = 0; n < m_actualCols; ++n) { if (n < lineStringList.size()) { QString valueString = lineStringList.at(n); - DEBUG(" valueString = " << valueString.toStdString()); + //DEBUG(" valueString = " << valueString.toStdString()); if (skipEmptyParts && !QString::compare(valueString, " ")) // handle left white spaces continue; // set value depending on data type switch (columnModes[n]) { case AbstractColumn::Numeric: { bool isNumber; const double value = locale.toDouble(valueString, &isNumber); - lineString += QString::number(isNumber ? value : nanValue, 'g', 16); + lineString += QString::number(isNumber ? value : nanValue, 'g', 15); break; } case AbstractColumn::Integer: { bool isNumber; const int value = locale.toInt(valueString, &isNumber); lineString += QString::number(isNumber ? value : 0); break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); lineString += valueDateTime.isValid() ? valueDateTime.toString(dateTimeFormat) : QLatin1String(" "); break; } case AbstractColumn::Text: if (removeQuotesEnabled) valueString.remove(QRegExp("[\"\']")); lineString += valueString; break; case AbstractColumn::Month: // never happens case AbstractColumn::Day: break; } } else // missing columns in this line lineString += QLatin1String(""); } dataStrings << lineString; } return dataStrings; } /*! writes the content of \c dataSource to the file \c fileName. */ void AsciiFilterPrivate::write(const QString & fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO: save data to ascii file } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void AsciiFilter::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "asciiFilter"); writer->writeAttribute( "commentCharacter", d->commentCharacter); writer->writeAttribute( "separatingCharacter", d->separatingCharacter); writer->writeAttribute( "autoMode", QString::number(d->autoModeEnabled)); writer->writeAttribute( "createIndex", QString::number(d->createIndexEnabled)); writer->writeAttribute( "header", QString::number(d->headerEnabled)); writer->writeAttribute( "vectorNames", d->vectorNames.join(' ')); writer->writeAttribute( "skipEmptyParts", QString::number(d->skipEmptyParts)); writer->writeAttribute( "simplifyWhitespaces", QString::number(d->simplifyWhitespacesEnabled)); writer->writeAttribute( "nanValue", QString::number(d->nanValue)); writer->writeAttribute( "removeQuotes", QString::number(d->removeQuotesEnabled)); writer->writeAttribute( "startRow", QString::number(d->startRow)); writer->writeAttribute( "endRow", QString::number(d->endRow)); writer->writeAttribute( "startColumn", QString::number(d->startColumn)); writer->writeAttribute( "endColumn", QString::number(d->endColumn)); writer->writeEndElement(); } /*! Loads from XML. */ bool AsciiFilter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "asciiFilter") { reader->raiseError(i18n("no ascii filter element found")); return false; } - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value("commentCharacter").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'commentCharacter'")); + reader->raiseWarning(attributeWarning.subs("commentCharacter").toString()); else d->commentCharacter = str; str = attribs.value("separatingCharacter").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'separatingCharacter'")); + reader->raiseWarning(attributeWarning.subs("separatingCharacter").toString()); else d->separatingCharacter = str; str = attribs.value("createIndex").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'createIndex'")); + reader->raiseWarning(attributeWarning.subs("createIndex").toString()); else d->createIndexEnabled = str.toInt(); str = attribs.value("autoMode").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoMode'")); + reader->raiseWarning(attributeWarning.subs("autoMode").toString()); else d->autoModeEnabled = str.toInt(); str = attribs.value("header").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'header'")); + reader->raiseWarning(attributeWarning.subs("header").toString()); else d->headerEnabled = str.toInt(); str = attribs.value("vectorNames").toString(); d->vectorNames = str.split(' '); //may be empty str = attribs.value("simplifyWhitespaces").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'simplifyWhitespaces'")); + reader->raiseWarning(attributeWarning.subs("simplifyWhitespaces").toString()); else d->simplifyWhitespacesEnabled = str.toInt(); str = attribs.value("nanValue").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'nanValue'")); + reader->raiseWarning(attributeWarning.subs("nanValue").toString()); else d->nanValue = str.toDouble(); str = attribs.value("removeQuotes").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'removeQuotes'")); + reader->raiseWarning(attributeWarning.subs("removeQuotes").toString()); else d->removeQuotesEnabled = str.toInt(); str = attribs.value("skipEmptyParts").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'skipEmptyParts'")); + reader->raiseWarning(attributeWarning.subs("skipEmptyParts").toString()); else d->skipEmptyParts = str.toInt(); str = attribs.value("startRow").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'startRow'")); + reader->raiseWarning(attributeWarning.subs("startRow").toString()); else d->startRow = str.toInt(); str = attribs.value("endRow").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'endRow'")); + reader->raiseWarning(attributeWarning.subs("endRow").toString()); else d->endRow = str.toInt(); str = attribs.value("startColumn").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'startColumn'")); + reader->raiseWarning(attributeWarning.subs("startColumn").toString()); else d->startColumn = str.toInt(); str = attribs.value("endColumn").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'endColumn'")); + reader->raiseWarning(attributeWarning.subs("endColumn").toString()); else d->endColumn = str.toInt(); return true; } diff --git a/src/backend/datasources/filters/BinaryFilter.cpp b/src/backend/datasources/filters/BinaryFilter.cpp index d0278f8f1..c5ed3d5ae 100644 --- a/src/backend/datasources/filters/BinaryFilter.cpp +++ b/src/backend/datasources/filters/BinaryFilter.cpp @@ -1,622 +1,622 @@ /*************************************************************************** File : BinaryFilter.cpp Project : LabPlot Description : Binary I/O-filter -------------------------------------------------------------------- Copyright : (C) 2015-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 "backend/datasources/filters/BinaryFilter.h" #include "backend/datasources/filters/BinaryFilterPrivate.h" #include "backend/datasources/AbstractDataSource.h" #include "backend/core/column/Column.h" #include -#include +#include #include #include /*! \class BinaryFilter \brief Manages the import/export of data organized as columns (vectors) from/to a binary file. \ingroup datasources */ BinaryFilter::BinaryFilter():AbstractFileFilter(), d(new BinaryFilterPrivate(this)) {} BinaryFilter::~BinaryFilter() {} /*! reads the content of the file \c fileName. */ QVector BinaryFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromFile(fileName, dataSource, importMode, lines); return QVector(); //TODO: remove this later once all read*-functions in the filter classes don't return any preview strings anymore } /*! reads the content of the device \c device. */ void BinaryFilter::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromDevice(device, dataSource, importMode, lines); } QVector BinaryFilter::preview(const QString& fileName, int lines) { return d->preview(fileName, lines); } /*! writes the content of the data source \c dataSource to the file \c fileName. */ void BinaryFilter::write(const QString & fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); // emit() } /*! returns the list of all predefined data formats. */ QStringList BinaryFilter::dataTypes() { return (QStringList()<<"int8 (8 bit signed integer)"<<"int16 (16 bit signed integer)"<<"int32 (32 bit signed integer)"<<"int64 (64 bit signed integer)" <<"uint8 (8 bit unsigned integer)"<<"uint16 (16 bit unsigned integer)"<<"uint32 (32 bit unsigned integer)"<<"uint64 (64 bit unsigned integer)" <<"real32 (single precision floats)"<<"real64 (double precision floats)"); } /*! returns the list of all predefined byte order. */ QStringList BinaryFilter::byteOrders() { return (QStringList() << "Little endian" << "Big endian"); } /*! returns the size of the predefined data types */ int BinaryFilter::dataSize(BinaryFilter::DataType type) { int sizes[] = {1,2,4,8,1,2,4,8,4,8}; return sizes[(int)type]; } /*! returns the number of rows (length of vectors) in the file \c fileName. */ size_t BinaryFilter::rowNumber(const QString& fileName, const size_t vectors, const BinaryFilter::DataType type) { KFilterDev device(fileName); if (!device.open(QIODevice::ReadOnly)) return 0; size_t rows = 0; while (!device.atEnd()) { // one row for (size_t i = 0; i < vectors; ++i) { for (int j = 0; j < BinaryFilter::dataSize(type); ++j) device.read(1); } rows++; } return rows; } /////////////////////////////////////////////////////////////////////// /*! loads the predefined filter settings for \c filterName */ void BinaryFilter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void BinaryFilter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /////////////////////////////////////////////////////////////////////// void BinaryFilter::setVectors(const size_t v) { d->vectors = v; } size_t BinaryFilter::vectors() const { return d->vectors; } void BinaryFilter::setDataType(const BinaryFilter::DataType t) { d->dataType = t; } BinaryFilter::DataType BinaryFilter::dataType() const { return d->dataType; } void BinaryFilter::setByteOrder(const BinaryFilter::ByteOrder b) { d->byteOrder = b; } BinaryFilter::ByteOrder BinaryFilter::byteOrder() const { return d->byteOrder; } void BinaryFilter::setSkipStartBytes(const size_t s) { d->skipStartBytes = s; } size_t BinaryFilter::skipStartBytes() const { return d->skipStartBytes; } void BinaryFilter::setStartRow(const int s) { d->startRow = s; } int BinaryFilter::startRow() const { return d->startRow; } void BinaryFilter::setEndRow(const int e) { d->endRow = e; } int BinaryFilter::endRow() const { return d->endRow; } void BinaryFilter::setSkipBytes(const size_t s) { d->skipBytes = s; } size_t BinaryFilter::skipBytes() const { return d->skipBytes; } void BinaryFilter::setCreateIndexEnabled(bool b) { d->createIndexEnabled = b; } void BinaryFilter::setAutoModeEnabled(bool b) { d->autoModeEnabled = b; } bool BinaryFilter::isAutoModeEnabled() const { return d->autoModeEnabled; } //##################################################################### //################### Private implementation ########################## //##################################################################### BinaryFilterPrivate::BinaryFilterPrivate(BinaryFilter* owner) : q(owner), vectors(2), dataType(BinaryFilter::INT8), byteOrder(BinaryFilter::LittleEndian), startRow(1), endRow(-1), numRows(0), skipStartBytes(0), skipBytes(0), createIndexEnabled(false), autoModeEnabled(true), m_actualRows(0), m_actualCols(0) { } /*! reads the content of the device \c device to the data source \c dataSource or return as string for preview. Uses the settings defined in the data source. */ void BinaryFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { DEBUG("readDataFromFile()"); KFilterDev device(fileName); numRows = BinaryFilter::rowNumber(fileName, vectors, dataType); if (! device.open(QIODevice::ReadOnly)) { DEBUG(" could not open file " << fileName.toStdString()); return; } readDataFromDevice(device, dataSource, importMode, lines); } /*! * returns 1 if the current read position in the device is at the end and 0 otherwise. */ int BinaryFilterPrivate::prepareStreamToRead(QDataStream& in) { DEBUG("prepareStreamToRead()"); if (byteOrder == BinaryFilter::BigEndian) in.setByteOrder(QDataStream::BigEndian); else if (byteOrder == BinaryFilter::LittleEndian) in.setByteOrder(QDataStream::LittleEndian); // catch case that skipStartBytes or startRow is bigger than file if (skipStartBytes >= BinaryFilter::dataSize(dataType) * vectors * numRows || startRow > (int)numRows) return 1; // skip bytes at start for (size_t i = 0; i < skipStartBytes; ++i) { qint8 tmp; in >> tmp; } // skip until start row for (size_t i = 0; i < (startRow-1) * vectors; ++i) { for (int j = 0; j < BinaryFilter::dataSize(dataType); ++j) { qint8 tmp; in >> tmp; } } // set range of rows if (endRow == -1) m_actualRows = (int)numRows - startRow + 1; else if (endRow > (int)numRows - startRow + 1) m_actualRows = (int)numRows; else m_actualRows = endRow - startRow + 1; m_actualCols = (int)vectors; DEBUG("numRows = " << numRows); DEBUG("endRow = " << endRow); DEBUG("actual rows = " << m_actualRows); DEBUG("actual cols = " << m_actualCols); return 0; } /*! reads \c lines lines of the device \c device and return as string for preview. */ QVector BinaryFilterPrivate::preview(const QString& fileName, int lines) { DEBUG("BinaryFilterPrivate::preview( " << fileName.toStdString() << ", " << lines << ")"); QVector dataStrings; KFilterDev device(fileName); if (! device.open(QIODevice::ReadOnly)) return dataStrings << (QStringList() << i18n("could not open device")); numRows = BinaryFilter::rowNumber(fileName, vectors, dataType); QDataStream in(&device); const int deviceError = prepareStreamToRead(in); if(deviceError) return dataStrings << (QStringList() << i18n("data selection empty")); //TODO: support other modes columnModes.resize(m_actualCols); //TODO: use given names QStringList vectorNames; if (createIndexEnabled) - vectorNames.prepend("index"); + vectorNames.prepend(i18n("Index")); if (lines == -1) lines = m_actualRows; // read data //TODO: use ColumnMode ? DEBUG("generating preview for " << qMin(lines, m_actualRows) << " lines"); for (int i = 0; i < qMin(m_actualRows, lines); ++i) { QStringList lineString; //prepend the index if required if (createIndexEnabled) lineString << QString::number(i+1); for (int n = 0; n < m_actualCols; ++n) { switch (dataType) { case BinaryFilter::INT8: { qint8 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::INT16: { qint16 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::INT32: { qint32 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::INT64: { qint64 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::UINT8: { quint8 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::UINT16: { quint16 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::UINT32: { quint32 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::UINT64: { quint64 value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::REAL32: { float value; in >> value; lineString << QString::number(value); break; } case BinaryFilter::REAL64: { double value; in >> value; lineString << QString::number(value); break; } } } dataStrings << lineString; emit q->completed(100*i/m_actualRows); } return dataStrings; } /*! reads the content of the file \c fileName to the data source \c dataSource or return as string for preview. Uses the settings defined in the data source. */ void BinaryFilterPrivate::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { DEBUG("BinaryFilterPrivate::readDataFromDevice()"); QDataStream in(&device); const int deviceError = prepareStreamToRead(in); if (deviceError) { dataSource->clear(); DEBUG("device error"); return; } if (createIndexEnabled) m_actualCols++; QVector dataContainer; int columnOffset = 0; //TODO: support other modes columnModes.resize(m_actualCols); //TODO: use given names QStringList vectorNames; if (createIndexEnabled) { - vectorNames.prepend("index"); + vectorNames.prepend(i18n("Index")); columnModes[0] = AbstractColumn::Integer; } columnOffset = dataSource->prepareImport(dataContainer, importMode, m_actualRows, m_actualCols, vectorNames, columnModes); if (lines == -1) lines = m_actualRows; // start column int startColumn = 0; if (createIndexEnabled) startColumn++; // read data //TODO: use ColumnMode ? DEBUG("reading " << qMin(lines, m_actualRows) << " lines"); for (int i = 0; i < qMin(m_actualRows, lines); ++i) { DEBUG("reading row " << i); //prepend the index if required if (createIndexEnabled) static_cast*>(dataContainer[0])->operator[](i) = i+1; for (int n = startColumn; n < m_actualCols; ++n) { DEBUG("reading column " << n); switch (dataType) { case BinaryFilter::INT8: { qint8 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::INT16: { qint16 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::INT32: { qint32 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::INT64: { qint64 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::UINT8: { quint8 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::UINT16: { quint16 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::UINT32: { quint32 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::UINT64: { quint64 value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::REAL32: { float value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } case BinaryFilter::REAL64: { double value; in >> value; static_cast*>(dataContainer[n])->operator[](i) = value; break; } } } if (m_actualRows > 0) emit q->completed(100*i/m_actualRows); } dataSource->finalizeImport(columnOffset, 1, m_actualCols, "", importMode); } /*! writes the content of \c dataSource to the file \c fileName. */ void BinaryFilterPrivate::write(const QString & fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void BinaryFilter::save(QXmlStreamWriter* writer) const { writer->writeStartElement("binaryFilter"); writer->writeAttribute("vectors", QString::number(d->vectors) ); writer->writeAttribute("dataType", QString::number(d->dataType) ); writer->writeAttribute("byteOrder", QString::number(d->byteOrder) ); writer->writeAttribute("autoMode", QString::number(d->autoModeEnabled) ); writer->writeAttribute("startRow", QString::number(d->startRow) ); writer->writeAttribute("endRow", QString::number(d->endRow) ); writer->writeAttribute("skipStartBytes", QString::number(d->skipStartBytes) ); writer->writeAttribute("skipBytes", QString::number(d->skipBytes) ); writer->writeAttribute( "createIndex", QString::number(d->createIndexEnabled) ); writer->writeEndElement(); } /*! Loads from XML. */ bool BinaryFilter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "binaryFilter") { reader->raiseError(i18n("no binary filter element found")); return false; } - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs = reader->attributes(); // read attributes QString str = attribs.value("vectors").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'vectors'")); + reader->raiseWarning(attributeWarning.subs("vectors").toString()); else d->vectors = (size_t)str.toULong(); str = attribs.value("dataType").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'dataType'")); + reader->raiseWarning(attributeWarning.subs("dataType").toString()); else d->dataType = (BinaryFilter::DataType) str.toInt(); str = attribs.value("byteOrder").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'byteOrder'")); + reader->raiseWarning(attributeWarning.subs("byteOrder").toString()); else d->byteOrder = (BinaryFilter::ByteOrder) str.toInt(); str = attribs.value("autoMode").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoMode'")); + reader->raiseWarning(attributeWarning.subs("autoMode").toString()); else d->autoModeEnabled = str.toInt(); str = attribs.value("startRow").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'startRow'")); + reader->raiseWarning(attributeWarning.subs("startRow").toString()); else d->startRow = str.toInt(); str = attribs.value("endRow").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'endRow'")); + reader->raiseWarning(attributeWarning.subs("endRow").toString()); else d->endRow = str.toInt(); str = attribs.value("skipStartBytes").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'skipStartBytes'")); + reader->raiseWarning(attributeWarning.subs("skipStartBytes").toString()); else d->skipStartBytes = (size_t)str.toULong(); str = attribs.value("skipBytes").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'skipBytes'")); + reader->raiseWarning(attributeWarning.subs("skipBytes").toString()); else d->skipBytes = (size_t)str.toULong(); str = attribs.value("createIndex").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'createIndex'")); + reader->raiseWarning(attributeWarning.subs("createIndex").toString()); else d->createIndexEnabled = str.toInt(); return true; } diff --git a/src/backend/datasources/filters/FITSFilter.cpp b/src/backend/datasources/filters/FITSFilter.cpp index f2beca854..86fb9c690 100644 --- a/src/backend/datasources/filters/FITSFilter.cpp +++ b/src/backend/datasources/filters/FITSFilter.cpp @@ -1,1636 +1,1636 @@ /*************************************************************************** File : FITSFilter.cpp Project : LabPlot Description : FITS I/O-filter -------------------------------------------------------------------- Copyright : (C) 2016 by Fabian Kristof (fkristofszabolcs@gmail.com) Copyright : (C) 2017 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 "FITSFilter.h" #include "FITSFilterPrivate.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnStringIO.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/matrix/MatrixModel.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/datasources/AbstractDataSource.h" #include "backend/matrix/Matrix.h" #include "commonfrontend/matrix/MatrixView.h" #include #include #include /*! \class FITSFilter * \brief Manages the import/export of data from/to a FITS file. * \since 2.2.0 * \ingroup datasources */ FITSFilter::FITSFilter():AbstractFileFilter(), d(new FITSFilterPrivate(this)) {} FITSFilter::~FITSFilter() {} QVector FITSFilter::readDataFromFile(const QString &fileName, AbstractDataSource *dataSource, AbstractFileFilter::ImportMode importMode, int lines) { Q_UNUSED(lines); return d->readCHDU(fileName, dataSource, importMode); } QVector FITSFilter::readChdu(const QString &fileName, bool* okToMatrix, int lines) { return d->readCHDU(fileName, NULL, AbstractFileFilter::Replace, okToMatrix, lines); } void FITSFilter::write(const QString &fileName, AbstractDataSource *dataSource) { d->writeCHDU(fileName, dataSource); } void FITSFilter::addNewKeyword(const QString &filename, const QList &keywords) { d->addNewKeyword(filename, keywords); } void FITSFilter::updateKeywords(const QString &fileName, const QList& originals, const QVector& updates) { d->updateKeywords(fileName, originals, updates); } void FITSFilter::deleteKeyword(const QString &fileName, const QList& keywords) { d->deleteKeyword(fileName, keywords); } void FITSFilter::addKeywordUnit(const QString &fileName, const QList &keywords) { d->addKeywordUnit(fileName, keywords); } void FITSFilter::removeExtensions(const QStringList &extensions) { d->removeExtensions(extensions); } void FITSFilter::parseHeader(const QString &fileName, QTableWidget *headerEditTable, bool readKeys, const QList &keys) { d->parseHeader(fileName, headerEditTable, readKeys, keys); } void FITSFilter::parseExtensions(const QString &fileName, QTreeWidget *tw, bool checkPrimary) { d->parseExtensions(fileName, tw, checkPrimary); } QList FITSFilter::chduKeywords(const QString &fileName) { return d->chduKeywords(fileName); } void FITSFilter::loadFilterSettings(const QString& fileName) { Q_UNUSED(fileName) } void FITSFilter::saveFilterSettings(const QString& fileName) const { Q_UNUSED(fileName) } /*! * \brief contains the {StandardKeywords \ MandatoryKeywords} keywords * \return A list of keywords */ QStringList FITSFilter::standardKeywords() { return QStringList() << QLatin1String("(blank)") << QLatin1String("CROTA") << QLatin1String("EQUINOX") << QLatin1String("NAXIS") << QLatin1String("TBCOL") << QLatin1String("TUNIT") << QLatin1String("AUTHOR") << QLatin1String("CRPIX") << QLatin1String("EXTEND") << QLatin1String("OBJECT") << QLatin1String("TDIM") << QLatin1String("TZERO") << QLatin1String("BITPIX") << QLatin1String("CRVAL") << QLatin1String("EXTLEVEL") << QLatin1String("OBSERVER") << QLatin1String("TDISP") << QLatin1String("XTENSION") << QLatin1String("BLANK") << QLatin1String("CTYPE") << QLatin1String("EXTNAME") << QLatin1String("ORIGIN") << QLatin1String("TELESCOP") << QLatin1String("BLOCKED") << QLatin1String("DATAMAX") << QLatin1String("EXTVER") << QLatin1String("BSCALE") << QLatin1String("DATAMIN") << QLatin1String("PSCAL") << QLatin1String("TFORM") << QLatin1String("BUNIT") << QLatin1String("DATE") << QLatin1String("GROUPS") << QLatin1String("PTYPE") << QLatin1String("THEAP") << QLatin1String("BZERO") << QLatin1String("DATE-OBS") << QLatin1String("HISTORY") << QLatin1String("PZERO") << QLatin1String("TNULL") << QLatin1String("CDELT") << QLatin1String("INSTRUME") << QLatin1String("REFERENC") << QLatin1String("TSCAL") << QLatin1String("COMMENT") << QLatin1String("EPOCH") << QLatin1String("NAXIS") << QLatin1String("SIMPLE") << QLatin1String("TTYPE"); } /*! * \brief Returns a list of keywords, that are mandatory for an image extension of a FITS file * see: * https://archive.stsci.edu/fits/fits_standard/node64.html * \return A list of keywords */ QStringList FITSFilter::mandatoryImageExtensionKeywords() { return QStringList() << QLatin1String("XTENSION") << QLatin1String("BITPIX") << QLatin1String("NAXIS") << QLatin1String("PCOUNT") << QLatin1String("GCOUNT") << QLatin1String("END"); } /*! * \brief Returns a list of keywords, that are mandatory for a table extension (ascii or bintable) * of a FITS file * see: * https://archive.stsci.edu/fits/fits_standard/node58.html * https://archive.stsci.edu/fits/fits_standard/node68.html * \return A list of keywords */ QStringList FITSFilter::mandatoryTableExtensionKeywords() { return QStringList() << QLatin1String("XTENSION") << QLatin1String("BITPIX") << QLatin1String("NAXIS") << QLatin1String("NAXIS1") << QLatin1String("NAXIS2") << QLatin1String("PCOUNT") << QLatin1String("GCOUNT") << QLatin1String("TFIELDS") << QLatin1String("END"); } /*! * \brief Returns a list of strings that represent units which are used for autocompletion when adding * keyword units to keywords * \return A list of strings that represent units */ QStringList FITSFilter::units() { return QStringList() << QLatin1String("m (Metre)") << QLatin1String("kg (Kilogram)") << QLatin1String("s (Second)") << QString("M☉ (Solar mass)") << QLatin1String("AU (Astronomical unit") << QLatin1String("l.y (Light year)") << QLatin1String("km (Kilometres") << QLatin1String("pc (Parsec)") << QLatin1String("K (Kelvin)") << QLatin1String("mol (Mole)") << QLatin1String("cd (Candela)"); } /*! * \brief Sets the startColumn to \a column * \param column the column to be set */ void FITSFilter::setStartColumn(const int column) { d->startColumn = column; } /*! * \brief Returns startColumn * \return The startColumn */ int FITSFilter::startColumn() const { return d->startColumn; } /*! * \brief Sets the endColumn to \a column * \param column the column to be set */ void FITSFilter::setEndColumn(const int column) { d->endColumn = column; } /*! * \brief Returns endColumn * \return The endColumn */ int FITSFilter::endColumn() const { return d->endColumn; } /*! * \brief Sets the startRow to \a row * \param row the row to be set */ void FITSFilter::setStartRow(const int row) { d->startRow = row; } /*! * \brief Returns startRow * \return The startRow */ int FITSFilter::startRow() const { return d->startRow; } /*! * \brief Sets the endRow to \a row * \param row the row to be set */ void FITSFilter::setEndRow(const int row) { d->endRow = row; } /*! * \brief Returns endRow * \return The endRow */ int FITSFilter::endRow() const { return d->endRow; } /*! * \brief Sets commentsAsUnits to \a commentsAsUnits * * This is used when spreadsheets are exported to FITS table extensions and comments are used as the * units of the table's columns. * \param commentsAsUnits */ void FITSFilter::setCommentsAsUnits(const bool commentsAsUnits) { d->commentsAsUnits = commentsAsUnits; } /*! * \brief Sets exportTo to \a exportTo * * This is used to decide whether the container should be exported to a FITS image or a FITS table * For an image \a exportTo should be 0, for a table 1 * \param exportTo */ void FITSFilter::setExportTo(const int exportTo) { d->exportTo = exportTo; } int FITSFilter::imagesCount(const QString &fileName) { return FITSFilterPrivate::extensionNames(fileName).values(QLatin1String("IMAGES")).size(); } int FITSFilter::tablesCount(const QString &fileName) { return FITSFilterPrivate::extensionNames(fileName).values(QLatin1String("TABLES")).size(); } //##################################################################### //################### Private implementation ########################## //##################################################################### FITSFilterPrivate::FITSFilterPrivate(FITSFilter* owner) : q(owner), startRow(-1), endRow(-1), startColumn(-1), endColumn(-1), commentsAsUnits(false), exportTo(0) { #ifdef HAVE_FITS m_fitsFile = 0; #endif } /*! * \brief Read the current header data unit from file \a filename in data source \a dataSource in \a importMode import mode * \param fileName the name of the file to be read * \param dataSource the data source to be filled * \param importMode */ QVector FITSFilterPrivate::readCHDU(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, bool* okToMatrix, int lines) { DEBUG("FITSFilterPrivate::readCHDU() file name = " << fileName.toStdString()); QVector dataStrings; #ifdef HAVE_FITS int status = 0; if(fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status)) { DEBUG(" ERROR opening file " << fileName.toStdString()); printError(status); return dataStrings; } int chduType; if (fits_get_hdu_type(m_fitsFile, &chduType, &status)) { printError(status); return dataStrings; } long actualRows; int actualCols; int columnOffset = 0; bool noDataSource = (dataSource == NULL); if(chduType == IMAGE_HDU) { DEBUG("IMAGE_HDU"); int maxdim = 2; int bitpix; int naxis; long naxes[2]; if (fits_get_img_param(m_fitsFile, maxdim, &bitpix, &naxis, naxes, &status)) { printError(status); return dataStrings; } if (naxis == 0) return dataStrings; actualRows = naxes[1]; actualCols = naxes[0]; if (lines == -1) lines = actualRows; else { if (lines > actualRows) lines = actualRows; } if (endRow != -1) { if (!noDataSource) lines = endRow; } if (endColumn != -1) actualCols = endColumn; if (noDataSource) dataStrings.reserve(lines); int i = 0; int j = 0; if (startRow != 1) i = startRow; if (startColumn != 1) j = startColumn; const int jstart = j; //TODO: support other modes QVector columnModes; columnModes.resize(actualCols - j); QStringList vectorNames; QVector dataContainer; if (!noDataSource) { dataContainer.reserve(actualCols - j); columnOffset = dataSource->prepareImport(dataContainer, importMode, lines - i, actualCols - j, vectorNames, columnModes); } long pixelCount = lines * actualCols; double* data = new double[pixelCount]; if (!data) { - qDebug() << i18n("Not enough memory for data"); + qDebug() << "Not enough memory for data"; return dataStrings; } if (fits_read_img(m_fitsFile, TDOUBLE, 1, pixelCount, NULL, data, NULL, &status)) { printError(status); return dataStrings << (QStringList() << QString("Error")); } int ii = 0; DEBUG(" Import " << lines << " lines"); for (; i < lines; ++i) { int jj = 0; QStringList line; line.reserve(actualCols - j); for (; j < actualCols; ++j) { if (noDataSource) line << QString::number(data[i*naxes[0] +j]); else static_cast*>(dataContainer[jj++])->operator[](ii) = data[i* naxes[0] + j]; } dataStrings << line; j = jstart; ii++; } delete[] data; if (dataSource) dataSource->finalizeImport(columnOffset, 1, actualCols, "", importMode); fits_close_file(m_fitsFile, &status); return dataStrings; } else if ((chduType == ASCII_TBL) || (chduType == BINARY_TBL)) { DEBUG("ASCII_TBL or BINARY_TBL"); if (endColumn != -1) actualCols = endColumn; else fits_get_num_cols(m_fitsFile, &actualCols, &status); if (endRow != -1) actualRows = endRow; else fits_get_num_rows(m_fitsFile, &actualRows, &status); QStringList columnNames; QList columnsWidth; QStringList columnUnits; columnUnits.reserve(actualCols); columnsWidth.reserve(actualCols); columnNames.reserve(actualCols); int colWidth; char keyword[FLEN_KEYWORD]; char value[FLEN_VALUE]; int col = 1; if (startColumn != 1) { if (startColumn != 0) col = startColumn; } for (; col <=actualCols; ++col) { status = 0; fits_make_keyn("TTYPE", col, keyword, &status); fits_read_key(m_fitsFile, TSTRING, keyword, value, NULL, &status); columnNames.append(QLatin1String(value)); fits_make_keyn("TUNIT", col, keyword, &status); fits_read_key(m_fitsFile, TSTRING, keyword, value, NULL, &status); columnUnits.append(QLatin1String(value)); fits_get_col_display_width(m_fitsFile, col, &colWidth, &status); columnsWidth.append(colWidth); } status = 0; if (lines == -1) lines = actualRows; else if (lines > actualRows) lines = actualRows; if (endRow != -1) lines = endRow; QVector stringDataPointers; QVector numericDataPointers; QList columnNumericTypes; int startCol = 0; if (startColumn != 1) startCol = startColumn; int startRrow = 0; if (startRow != 1) startRrow = startRow; columnNumericTypes.reserve(actualCols); int datatype; int c = 1; if (startColumn != 1) { if (startColumn != 0) c = startColumn; } QList matrixNumericColumnIndices; for (; c <= actualCols; ++c) { fits_get_coltype(m_fitsFile, c, &datatype, NULL, NULL, &status); switch (datatype) { case TSTRING: columnNumericTypes.append(false); break; case TSHORT: columnNumericTypes.append(true); break; case TLONG: columnNumericTypes.append(true); break; case TFLOAT: columnNumericTypes.append(true); break; case TDOUBLE: columnNumericTypes.append(true); break; case TLOGICAL: columnNumericTypes.append(false); break; case TBIT: columnNumericTypes.append(true); break; case TBYTE: columnNumericTypes.append(true); break; case TCOMPLEX: columnNumericTypes.append(true); break; default: columnNumericTypes.append(false); break; } if ((datatype != TSTRING) && (datatype != TLOGICAL)) matrixNumericColumnIndices.append(c); } if (noDataSource) *okToMatrix = matrixNumericColumnIndices.isEmpty() ? false : true; if (!noDataSource) { DEBUG("HAS DataSource"); Spreadsheet* spreadsheet = dynamic_cast(dataSource); if(spreadsheet) { numericDataPointers.reserve(actualCols - startCol); stringDataPointers.reserve(actualCols - startCol); spreadsheet->setUndoAware(false); columnOffset = spreadsheet->resize(importMode, columnNames, actualCols - startCol); if (importMode == AbstractFileFilter::Replace) { spreadsheet->clear(); spreadsheet->setRowCount(lines - startRrow); } else { if (spreadsheet->rowCount() < (lines - startRrow)) spreadsheet->setRowCount(lines - startRrow); } DEBUG("Reading columns ..."); for (int n = 0; n < actualCols - startCol; ++n) { if (columnNumericTypes.at(n)) { spreadsheet->column(columnOffset+ n)->setColumnMode(AbstractColumn::Numeric); QVector* datap = static_cast* >(spreadsheet->column(columnOffset+n)->data()); numericDataPointers.push_back(datap); if (importMode == AbstractFileFilter::Replace) datap->clear(); } else { spreadsheet->column(columnOffset+ n)->setColumnMode(AbstractColumn::Text); QStringList* list = static_cast(spreadsheet->column(columnOffset+n)->data()); stringDataPointers.push_back(list); if (importMode == AbstractFileFilter::Replace) list->clear(); } } DEBUG(" ... DONE"); stringDataPointers.squeeze(); } else { numericDataPointers.reserve(matrixNumericColumnIndices.size()); columnOffset = dataSource->prepareImport(numericDataPointers, importMode, lines - startRrow, matrixNumericColumnIndices.size()); } numericDataPointers.squeeze(); } char* array = new char[1000]; //TODO: why 1000? int row = 1; if (startRow != 1) { if (startRow != 0) row = startRow; } int coll = 1; if (startColumn != 1) { if (startColumn != 0) coll = startColumn; } bool isMatrix = false; if (dynamic_cast(dataSource)) { isMatrix = true; coll = matrixNumericColumnIndices.first(); actualCols = matrixNumericColumnIndices.last(); if (importMode == AbstractFileFilter::Replace) { for (auto* col: numericDataPointers) static_cast*>(col)->clear(); } } for (; row <= lines; ++row) { int numericixd = 0; int stringidx = 0; QStringList line; line.reserve(actualCols-coll); for (int col = coll; col <= actualCols; ++col) { if (isMatrix) { if (!matrixNumericColumnIndices.contains(col)) continue; } if(fits_read_col_str(m_fitsFile, col, row, 1, 1, NULL, &array, NULL, &status)) printError(status); if (!noDataSource) { const QString& str = QString::fromLatin1(array); if (str.isEmpty()) { if (columnNumericTypes.at(col - 1)) static_cast*>(numericDataPointers[numericixd++])->push_back(0); else stringDataPointers[stringidx++]->append(QLatin1String("NULL")); } else { if (columnNumericTypes.at(col - 1)) static_cast*>(numericDataPointers[numericixd++])->push_back(str.toDouble()); else { if (!stringDataPointers.isEmpty()) stringDataPointers[stringidx++]->operator<<(str.simplified()); } } } else { QString tmpColstr = QString::fromLatin1(array); tmpColstr = tmpColstr.simplified(); if (tmpColstr.isEmpty()) line << QLatin1String("NULL"); else line << tmpColstr; } } dataStrings << line; } delete[] array; if (!noDataSource) dataSource->finalizeImport(columnOffset, 1, actualCols, "", importMode); fits_close_file(m_fitsFile, &status); return dataStrings; } else - qDebug() << i18n("Incorrect header type"); + qDebug() << "Incorrect header type"; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(dataSource) Q_UNUSED(importMode) Q_UNUSED(okToMatrix) Q_UNUSED(lines) #endif return dataStrings; } /*! * \brief Export from data source \a dataSource to file \a fileName * \param fileName the name of the file to be exported to * \param dataSource the datasource whose data is exported */ void FITSFilterPrivate::writeCHDU(const QString &fileName, AbstractDataSource *dataSource) { #ifdef HAVE_FITS if (!fileName.endsWith(QLatin1String(".fits"))) return; int status = 0; bool existed = false; if (!QFile::exists(fileName)) { if (fits_create_file(&m_fitsFile, fileName.toLatin1(), &status)) { printError(status); qDebug() << fileName; return; } } else { if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } else existed = true; } Matrix* const matrix = dynamic_cast(dataSource); if (matrix) { //FITS image if (exportTo == 0) { long naxes[2] = { matrix->columnCount(), matrix->rowCount() }; if (fits_create_img(m_fitsFile, FLOAT_IMG, 2, naxes, &status)) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); return; } const long nelem = naxes[0] * naxes[1]; double* const array = new double[nelem]; const QVector >* const data = static_cast>*>(matrix->data()); for (int col = 0; col < naxes[0]; ++col) for (int row = 0; row < naxes[1]; ++row) array[row * naxes[0] + col] = data->at(row).at(col); if (fits_write_img(m_fitsFile, TDOUBLE, 1, nelem, array, &status )) { printError(status); status = 0; } fits_close_file(m_fitsFile, &status); delete[] array; //FITS table } else { const int nrows = matrix->rowCount(); const int tfields = matrix->columnCount(); QVector columnNames; columnNames.resize(tfields); columnNames.reserve(tfields); QVector tform; tform.resize(tfields); tform.reserve(tfields); QVector tunit; tunit.resize(tfields); tunit.reserve(tfields); //TODO: mode const QVector>* const matrixData = static_cast>*>(matrix->data()); QVector column; const MatrixModel* matrixModel = static_cast(matrix->view())->model(); const int precision = matrix->precision(); for (int i = 0; i < tfields; ++i) { column = matrixData->at(i); const QString& columnName = matrixModel->headerData(i, Qt::Horizontal).toString(); columnNames[i] = new char[columnName.size()]; strcpy(columnNames[i], columnName.toLatin1().data()); tunit[i] = new char[1]; strcpy(tunit[i], ""); int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (matrix->text(row, i).size() > maxSize) maxSize = matrix->text(row, i).size(); } QString tformn; if (precision > 0) { tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".") + QString::number(precision); } else tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".0"); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); } //TODO extension name containing[] ? if (fits_create_tbl(m_fitsFile, ASCII_TBL, nrows, tfields, columnNames.data(), tform.data(), tunit.data(), matrix->name().toLatin1().data(),&status )) { printError(status); qDeleteAll(tform); qDeleteAll(tunit); qDeleteAll(columnNames); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } qDeleteAll(tform); qDeleteAll(tunit); qDeleteAll(columnNames); double* columnNumeric = new double[nrows]; for (int col = 1; col <= tfields; ++col) { column = matrixData->at(col-1); for (int r = 0; r < column.size(); ++r) columnNumeric[r] = column.at(r); fits_write_col(m_fitsFile, TDOUBLE, col, 1, 1, nrows, columnNumeric, &status); if (status) { printError(status); delete[] columnNumeric; status = 0; if (!existed) { QFile file(fileName); file.remove(); } fits_close_file(m_fitsFile, &status); return; } } delete[] columnNumeric; fits_close_file(m_fitsFile, &status); } return; } Spreadsheet* const spreadsheet = dynamic_cast(dataSource); if (spreadsheet) { //FITS image if (exportTo == 0) { int maxRowIdx = -1; //don't export lots of empty lines if all of those contain nans // TODO: option? for (int c = 0; c < spreadsheet->columnCount(); ++c) { const Column* const col = spreadsheet->column(c); int currMaxRoxIdx = -1; for (int r = col->rowCount(); r >= 0; --r) { if (col->isValid(r)) { currMaxRoxIdx = r; break; } } if (currMaxRoxIdx > maxRowIdx) { maxRowIdx = currMaxRoxIdx; } } long naxes[2] = { spreadsheet->columnCount(), maxRowIdx + 1}; if (fits_create_img(m_fitsFile, FLOAT_IMG, 2, naxes, &status)) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } const long nelem = naxes[0] * naxes[1]; double* array = new double[nelem]; for (int row = 0; row < naxes[1]; ++row) { for (int col = 0; col < naxes[0]; ++col) array[row * naxes[0] + col] = spreadsheet->column(col)->valueAt(row); } if (fits_write_img(m_fitsFile, TDOUBLE, 1, nelem, array, &status )) { printError(status); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } fits_close_file(m_fitsFile, &status); delete[] array; } else { const int nrows = spreadsheet->rowCount(); const int tfields = spreadsheet->columnCount(); QVector columnNames; columnNames.resize(tfields); columnNames.reserve(tfields); QVector tform; tform.resize(tfields); tform.reserve(tfields); QVector tunit; tunit.resize(tfields); tunit.reserve(tfields); for (int i = 0; i < tfields; ++i) { const Column* const column = spreadsheet->column(i); columnNames[i] = new char[column->name().size()]; strcpy(columnNames[i], column->name().toLatin1().data()); if (commentsAsUnits) { tunit[i] = new char[column->comment().size()]; strcpy(tunit[i], column->comment().toLatin1().constData()); } else { tunit[i] = new char[2]; strcpy(tunit[i], ""); } switch (column->columnMode()) { case AbstractColumn::Numeric: { int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (QString::number(column->valueAt(row)).size() > maxSize) maxSize = QString::number(column->valueAt(row)).size(); } const Double2StringFilter* const filter = static_cast(column->outputFilter()); bool decimals = false; for (int ii = 0; ii < nrows; ++ii) { bool ok; QString cell = column->asStringColumn()->textAt(ii); double val = cell.toDouble(&ok); if (cell.size() > QString::number(val).size() + 1) { decimals = true; break; } } QString tformn; if (decimals) { int maxStringSize = -1; for (int row = 0; row < nrows; ++row) { if (column->asStringColumn()->textAt(row).size() > maxStringSize) maxStringSize = column->asStringColumn()->textAt(row).size(); } const int diff = abs(maxSize - maxStringSize); maxSize+= diff; tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".") + QString::number(filter->numDigits()); } else tformn = QLatin1String("F")+ QString::number(maxSize) + QLatin1String(".0"); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); break; } case AbstractColumn::Text: { int maxSize = -1; for (int row = 0; row < nrows; ++row) { if (column->textAt(row).size() > maxSize) maxSize = column->textAt(row).size(); } const QString& tformn = QLatin1String("A") + QString::number(maxSize); tform[i] = new char[tformn.size()]; strcpy(tform[i], tformn.toLatin1().data()); break; } case AbstractColumn::Integer: //TODO case AbstractColumn::DateTime: case AbstractColumn::Day: case AbstractColumn::Month: break; } } //TODO extension name containing[] ? if (fits_create_tbl(m_fitsFile, ASCII_TBL, nrows, tfields, columnNames.data(), tform.data(), tunit.data(), spreadsheet->name().toLatin1().data(),&status )) { printError(status); qDeleteAll(tform); qDeleteAll(tunit); qDeleteAll(columnNames); status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } qDeleteAll(tform); qDeleteAll(tunit); qDeleteAll(columnNames); QVector column; column.resize(nrows); column.reserve(nrows); double* columnNumeric = new double[nrows]; bool hadTextColumn = false; for (int col = 1; col <= tfields; ++col) { const Column* c = spreadsheet->column(col-1); AbstractColumn::ColumnMode columnMode = c->columnMode(); if (columnMode == AbstractColumn::Numeric) { for (int row = 0; row < nrows; ++row) columnNumeric[row] = c->valueAt(row); fits_write_col(m_fitsFile, TDOUBLE, col, 1, 1, nrows, columnNumeric, &status); if (status) { printError(status); delete[] columnNumeric; status = 0; fits_close_file(m_fitsFile, &status); if (!existed) { QFile file(fileName); file.remove(); } return; } } else { hadTextColumn = true; for (int row = 0; row < nrows; ++row) { column[row] = new char[c->textAt(row).size()]; strcpy(column[row], c->textAt(row).toLatin1().data()); } fits_write_col(m_fitsFile, TSTRING, col, 1, 1, nrows, column.data(), &status); if (status) { printError(status); qDeleteAll(column); status = 0; fits_close_file(m_fitsFile, &status); return; } } } delete[] columnNumeric; if (hadTextColumn) qDeleteAll(column); status = 0; fits_close_file(m_fitsFile, &status); } } #else Q_UNUSED(fileName) Q_UNUSED(dataSource) #endif } /*! * \brief Return a map of the available extensions names in file \a filename * The keys of the map are the extension types, the values are the names * \param fileName the name of the FITS file to be analyzed */ QMultiMap FITSFilterPrivate::extensionNames(const QString& fileName) { DEBUG("FITSFilterPrivate::extensionNames() file name = " << fileName.toStdString()); #ifdef HAVE_FITS QMultiMap extensions; int status = 0; fitsfile* fitsFile = 0; if (fits_open_file(&fitsFile, fileName.toLatin1(), READONLY, &status )) return QMultiMap(); int hduCount; if (fits_get_num_hdus(fitsFile, &hduCount, &status)) return QMultiMap(); int imageCount = 0; int asciiTableCount = 0; int binaryTableCount = 0; for (int currentHDU = 1; (currentHDU <= hduCount) && !status; ++currentHDU) { int hduType; status = 0; fits_get_hdu_type(fitsFile, &hduType, &status); switch (hduType) { case IMAGE_HDU: imageCount++; break; case ASCII_TBL: asciiTableCount++; break; case BINARY_TBL: binaryTableCount++; break; } char* keyVal = new char[FLEN_VALUE]; QString extName; if (!fits_read_keyword(fitsFile,"EXTNAME", keyVal, NULL, &status)) { extName = QLatin1String(keyVal); extName = extName.mid(1, extName.length() -2).simplified(); } else { status = 0; if (!fits_read_keyword(fitsFile,"HDUNAME", keyVal, NULL, &status)) { extName = QLatin1String(keyVal); extName = extName.mid(1, extName.length() -2).simplified(); } else { status = 0; switch (hduType) { case IMAGE_HDU: if (imageCount == 1) extName = i18n("Primary header"); else - extName = i18n("IMAGE #%1").arg(imageCount); + extName = i18n("IMAGE #%1", imageCount); break; case ASCII_TBL: - extName = i18n("ASCII_TBL #%1").arg(asciiTableCount); + extName = i18n("ASCII_TBL #%1", asciiTableCount); break; case BINARY_TBL: - extName = i18n("BINARY_TBL #%1").arg(binaryTableCount); + extName = i18n("BINARY_TBL #%1", binaryTableCount); break; } } } delete[] keyVal; status = 0; extName = extName.trimmed(); switch (hduType) { case IMAGE_HDU: extensions.insert(QLatin1String("IMAGES"), extName); break; case ASCII_TBL: extensions.insert(QLatin1String("TABLES"), extName); break; case BINARY_TBL: extensions.insert(QLatin1String("TABLES"), extName); break; } fits_movrel_hdu(fitsFile, 1, NULL, &status); } if (status == END_OF_FILE) status = 0; fits_close_file(fitsFile, &status); return extensions; #else Q_UNUSED(fileName) return QMultiMap(); #endif } /*! * \brief Prints the error text corresponding to the status code \a status * \param status the status code of the error */ void FITSFilterPrivate::printError(int status) const { #ifdef HAVE_FITS if (status) { char errorText[FLEN_ERRMSG]; fits_get_errstatus(status, errorText ); qDebug() << QLatin1String(errorText); } #else Q_UNUSED(status) #endif } /*! * \brief Add the keywords \a keywords to the current header unit * \param keywords the keywords to be added * \param fileName the name of the FITS file (extension) in which the keywords are added */ void FITSFilterPrivate::addNewKeyword(const QString& fileName, const QList& keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } for (const FITSFilter::Keyword& keyword: keywords) { status = 0; if (!keyword.key.compare(QLatin1String("COMMENT"))) { if (fits_write_comment(m_fitsFile, keyword.value.toLatin1(), &status)) printError(status); } else if (!keyword.key.compare(QLatin1String("HISTORY"))) { if (fits_write_history(m_fitsFile, keyword.value.toLatin1(), &status)) printError(status); } else if (!keyword.key.compare(QLatin1String("DATE"))) { if (fits_write_date(m_fitsFile, &status)) printError(status); } else { int ok = 0; if (keyword.key.length() <= FLEN_KEYWORD) { ok++; if (keyword.value.length() <= FLEN_VALUE) { ok++; if(keyword.comment.length() <= FLEN_COMMENT) ok++; } } if (ok == 3) { bool ok; double val = keyword.value.toDouble(&ok); if (ok) { if (fits_write_key(m_fitsFile, TDOUBLE, keyword.key.toLatin1().data(), &val, keyword.comment.toLatin1().data(), &status)) printError(status); } else { if (fits_write_key(m_fitsFile, TSTRING, keyword.key.toLatin1().data(), keyword.value.toLatin1().data(), keyword.comment.toLatin1().data(), &status)) printError(status); } } else if ( ok == 2) { //comment too long } else if ( ok == 1) { //value too long } else { //keyword too long } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(keywords) Q_UNUSED(fileName) #endif } /*! * \brief Update keywords in the current header unit * \param fileName The name of the FITS file (extension) in which the keywords will be updated * \param originals The original keywords of the FITS file (extension) * \param updates The keywords that contain the updated values */ void FITSFilterPrivate::updateKeywords(const QString& fileName, const QList& originals, const QVector& updates) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } FITSFilter::Keyword updatedKeyword; FITSFilter::Keyword originalKeyword; FITSFilter::KeywordUpdate keywordUpdate; for (int i = 0; i < updates.size(); ++i) { updatedKeyword = updates.at(i); originalKeyword = originals.at(i); keywordUpdate = originals.at(i).updates; if (keywordUpdate.keyUpdated && keywordUpdate.valueUpdated && keywordUpdate.commentUpdated) { if (updatedKeyword.isEmpty()) { if (fits_delete_key(m_fitsFile, originalKeyword.key.toLatin1(), &status)) { printError(status); status = 0; } continue; } } if (!updatedKeyword.key.isEmpty()) { if (fits_modify_name(m_fitsFile, originalKeyword.key.toLatin1(), updatedKeyword.key.toLatin1(), &status )) { printError(status); status = 0; } } if (!updatedKeyword.value.isEmpty()) { bool ok; int intValue; double doubleValue; bool updated = false; doubleValue = updatedKeyword.value.toDouble(&ok); if (ok) { if (fits_update_key(m_fitsFile,TDOUBLE, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), &doubleValue, NULL, &status)) printError(status); else updated = true; } if (!updated) { intValue = updatedKeyword.value.toInt(&ok); if (ok) { if (fits_update_key(m_fitsFile,TINT, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), &intValue, NULL, &status)) printError(status); else updated = true; } } if (!updated) { if (fits_update_key(m_fitsFile,TSTRING, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), updatedKeyword.value.toLatin1().data(), NULL, &status)) printError(status); } } else { if (keywordUpdate.valueUpdated) { if (fits_update_key_null(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), NULL, &status)) { printError(status); status = 0; } } } if (!updatedKeyword.comment.isEmpty()) { if (fits_modify_comment(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), updatedKeyword.comment.toLatin1().data(), &status)) { printError(status); status = 0; } } else { if (keywordUpdate.commentUpdated) { if (fits_modify_comment(m_fitsFile, keywordUpdate.keyUpdated ? updatedKeyword.key.toLatin1() : originalKeyword.key.toLatin1(), QString("").toLatin1().data(), &status)) { printError(status); status = 0; } } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(originals) Q_UNUSED(updates) #endif } /*! * \brief Delete the keywords \a keywords from the current header unit * \param fileName the name of the FITS file (extension) in which the keywords will be deleted. * \param keywords the keywords to deleted */ void FITSFilterPrivate::deleteKeyword(const QString& fileName, const QList &keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } for (const auto& keyword : keywords) { if (!keyword.key.isEmpty()) { status = 0; if (fits_delete_key(m_fitsFile, keyword.key.toLatin1(), &status)) printError(status); } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(keywords) Q_UNUSED(fileName) #endif } /*! * \brief FITSFilterPrivate::addKeywordUnit * \param fileName the FITS file (extension) in which the keyword units are updated/added * \param keywords the keywords whose units were modified/added */ void FITSFilterPrivate::addKeywordUnit(const QString &fileName, const QList &keywords) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READWRITE, &status )) { printError(status); return; } for(const FITSFilter::Keyword& keyword : keywords) { if (keyword.updates.unitUpdated) { if (fits_write_key_unit(m_fitsFile, keyword.key.toLatin1(), keyword.unit.toLatin1().data(), &status)) { printError(status); status = 0; } } } status = 0; fits_close_file(m_fitsFile, &status); #else Q_UNUSED(fileName) Q_UNUSED(keywords) #endif } /*! * \brief Remove extensions from a FITS file * \param extensions The extensions to be removed from the FITS file */ void FITSFilterPrivate::removeExtensions(const QStringList &extensions) { #ifdef HAVE_FITS int status = 0; for (const auto& ext : extensions) { status = 0; if (fits_open_file(&m_fitsFile, ext.toLatin1(), READWRITE, &status )) { printError(status); continue; } if (fits_delete_hdu(m_fitsFile, NULL, &status)) printError(status); status = 0; fits_close_file(m_fitsFile, &status); } #else Q_UNUSED(extensions) #endif } /*! * \brief Returns a list of keywords in the current header of \a fileName * \param fileName the name of the FITS file (extension) to be opened * \return A list of keywords */ QList FITSFilterPrivate::chduKeywords(const QString& fileName) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status )) { printError(status); return QList (); } int numberOfKeys; if (fits_get_hdrspace(m_fitsFile, &numberOfKeys, NULL, &status)) { printError(status); return QList (); } QList keywords; keywords.reserve(numberOfKeys); char* key = new char[FLEN_KEYWORD]; char* value = new char[FLEN_VALUE]; char* comment = new char[FLEN_COMMENT]; char* unit = new char[FLEN_VALUE]; for (int i = 1; i <= numberOfKeys; ++i) { QStringList recordValues; FITSFilter::Keyword keyword; if (fits_read_keyn(m_fitsFile, i, key, value, comment, &status)) { printError(status); status = 0; continue; } fits_read_key_unit(m_fitsFile, key, unit, &status); recordValues << QLatin1String(key) << QLatin1String(value) << QLatin1String(comment) << QLatin1String(unit); keyword.key = recordValues[0].simplified(); keyword.value = recordValues[1].simplified(); keyword.comment = recordValues[2].simplified(); keyword.unit = recordValues[3].simplified(); keywords.append(keyword); } delete[] key; delete[] value; delete[] comment; delete[] unit; fits_close_file(m_fitsFile, &status); return keywords; #else Q_UNUSED(fileName) return QList(); #endif } /*! * \brief Builds the table \a headerEditTable from the keywords \a keys * \param fileName The name of the FITS file from which the keys are read if \a readKeys is \c true * \param headerEditTable The table to be built * \param readKeys It's used to determine whether the keywords are provided or they should be read from * file \a fileName * \param keys The keywords that are provided if the keywords were read already. */ void FITSFilterPrivate::parseHeader(const QString &fileName, QTableWidget *headerEditTable, bool readKeys, const QList& keys) { #ifdef HAVE_FITS QList keywords; if (readKeys) keywords = chduKeywords(fileName); else keywords = keys; headerEditTable->setRowCount(keywords.size()); QTableWidgetItem* item; for (int i = 0; i < keywords.size(); ++i) { const FITSFilter::Keyword& keyword = keywords.at(i); const bool mandatory = FITSFilter::mandatoryImageExtensionKeywords().contains(keyword.key) || FITSFilter::mandatoryTableExtensionKeywords().contains(keyword.key); item = new QTableWidgetItem(keyword.key); const QString& itemText = item->text(); const bool notEditableKey = mandatory || itemText.contains(QLatin1String("TFORM")) || itemText.contains(QLatin1String("TTYPE")) || itemText.contains(QLatin1String("TUNIT")) || itemText.contains(QLatin1String("TDISP")) || itemText.contains(QLatin1String("TBCOL")) || itemText.contains(QLatin1String("TZERO")); const bool notEditableValue= mandatory || itemText.contains(QLatin1String("TFORM")) || itemText.contains(QLatin1String("TDISP")) || itemText.contains(QLatin1String("TBCOL")) || itemText.contains(QLatin1String("TZERO")); if (notEditableKey) item->setFlags(item->flags() & ~Qt::ItemIsEditable); else item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 0, item ); item = new QTableWidgetItem(keyword.value); if (notEditableValue) item->setFlags(item->flags() & ~Qt::ItemIsEditable); else item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 1, item ); QString commentFieldText; if (!keyword.unit.isEmpty()) { if (keyword.updates.unitUpdated) { const QString& comment = keyword.comment.right( keyword.comment.size() - keyword.comment.indexOf(QChar(']'))-1); commentFieldText = QLatin1String("[") + keyword.unit + QLatin1String("] ") + comment; } else { if (keyword.comment.at(0) == QLatin1Char('[')) commentFieldText = keyword.comment; else commentFieldText = QLatin1String("[") + keyword.unit + QLatin1String("] ") + keyword.comment; } } else commentFieldText = keyword.comment; item = new QTableWidgetItem(commentFieldText); item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); headerEditTable->setItem(i, 2, item ); } headerEditTable->resizeColumnsToContents(); #else Q_UNUSED(fileName) Q_UNUSED(headerEditTable) Q_UNUSED(readKeys) Q_UNUSED(keys) #endif } /*! * \brief Helper function to return the value of the key \a key * \param fileName The name of the FITS file (extension) in which the keyword with key \a key should exist * \param key The key of the keyword whose value it's returned * \return The value of the keyword as a string */ const QString FITSFilterPrivate::valueOf(const QString& fileName, const char *key) { #ifdef HAVE_FITS int status = 0; if (fits_open_file(&m_fitsFile, fileName.toLatin1(), READONLY, &status )) { printError(status); return QString (); } char* keyVal = new char[FLEN_VALUE]; QString keyValue; if (!fits_read_keyword(m_fitsFile, key, keyVal, NULL, &status)) { keyValue = QLatin1String(keyVal); keyValue = keyValue.simplified(); } else { printError(status); delete[] keyVal; fits_close_file(m_fitsFile, &status); return QString(); } delete[] keyVal; status = 0; fits_close_file(m_fitsFile, &status); return keyValue; #else Q_UNUSED(fileName) Q_UNUSED(key) return QString(); #endif } /*! * \brief Build the extensions tree from FITS file (extension) \a fileName * \param fileName The name of the FITS file to be opened * \param tw The QTreeWidget to be built * \param checkPrimary Used to determine whether the tree will be used for import or the header edit, * if it's \c true and if the primary array it's empty, then the item won't be added to the tree */ void FITSFilterPrivate::parseExtensions(const QString &fileName, QTreeWidget *tw, bool checkPrimary) { DEBUG("FITSFilterPrivate::parseExtensions()"); #ifdef HAVE_FITS const QMultiMap& extensions = extensionNames(fileName); const QStringList& imageExtensions = extensions.values(QLatin1String("IMAGES")); const QStringList& tableExtensions = extensions.values(QLatin1String("TABLES")); QTreeWidgetItem* root = tw->invisibleRootItem(); //TODO: fileName may contain any data type: check if it's a FITS file QTreeWidgetItem* treeNameItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << fileName); root->addChild(treeNameItem); treeNameItem->setExpanded(true); QTreeWidgetItem* imageExtensionItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << i18n("Images")); imageExtensionItem->setFlags(imageExtensionItem->flags() & ~Qt::ItemIsSelectable ); QString primaryHeaderNaxis = valueOf(fileName, "NAXIS"); const int naxis = primaryHeaderNaxis.toInt(); bool noImage = false; for (const QString& ext : imageExtensions) { QTreeWidgetItem* treeItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << ext); if (ext == i18n("Primary header")) { if (checkPrimary && naxis == 0) continue; } imageExtensionItem->addChild(treeItem); } if (imageExtensionItem->childCount() > 0) { treeNameItem->addChild(imageExtensionItem); imageExtensionItem->setIcon(0,QIcon::fromTheme("view-preview")); imageExtensionItem->setExpanded(true); imageExtensionItem->child(0)->setSelected(true); tw->setCurrentItem(imageExtensionItem->child(0)); } else noImage = true; if (tableExtensions.size() > 0) { QTreeWidgetItem* tableExtensionItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << i18n("Tables")); tableExtensionItem->setFlags(tableExtensionItem->flags() & ~Qt::ItemIsSelectable ); for (const QString& ext : tableExtensions) { QTreeWidgetItem* treeItem = new QTreeWidgetItem((QTreeWidgetItem*)0, QStringList() << ext); tableExtensionItem->addChild(treeItem); } if (tableExtensionItem->childCount() > 0) { treeNameItem->addChild(tableExtensionItem); tableExtensionItem->setIcon(0,QIcon::fromTheme("x-office-spreadsheet")); tableExtensionItem->setExpanded(true); if (noImage) { tableExtensionItem->child(0)->setSelected(true); tw->setCurrentItem(tableExtensionItem->child(0)); } } } #else Q_UNUSED(fileName) Q_UNUSED(tw) Q_UNUSED(checkPrimary) #endif DEBUG("FITSFilterPrivate::parseExtensions() DONE"); } /*! * \brief FITSFilterPrivate::~FITSFilterPrivate */ FITSFilterPrivate::~FITSFilterPrivate() { } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void FITSFilter::save(QXmlStreamWriter * writer) const { Q_UNUSED(writer) } /*! Loads from XML. */ bool FITSFilter::load(XmlStreamReader * loader) { Q_UNUSED(loader) return false; } diff --git a/src/backend/datasources/filters/HDF5Filter.cpp b/src/backend/datasources/filters/HDF5Filter.cpp index aebe0aa50..c0ae5fba7 100644 --- a/src/backend/datasources/filters/HDF5Filter.cpp +++ b/src/backend/datasources/filters/HDF5Filter.cpp @@ -1,1721 +1,1721 @@ /*************************************************************************** File : HDF5Filter.cpp Project : LabPlot Description : HDF5 I/O-filter -------------------------------------------------------------------- Copyright : (C) 2015-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /* TODO: * Feature: implement missing data types and ranks * Performance: only fill dataPointer or dataStrings (not both) */ #include "backend/datasources/filters/HDF5Filter.h" #include "backend/datasources/filters/HDF5FilterPrivate.h" #include "backend/datasources/LiveDataSource.h" #include "backend/core/column/Column.h" -#include +#include #include #include +#include /*! \class HDF5Filter \brief Manages the import/export of data from/to a HDF5 file. \ingroup datasources */ HDF5Filter::HDF5Filter():AbstractFileFilter(), d(new HDF5FilterPrivate(this)) {} HDF5Filter::~HDF5Filter() {} /*! parses the content of the file \c fileName. */ void HDF5Filter::parse(const QString & fileName, QTreeWidgetItem* rootItem) { d->parse(fileName, rootItem); } /*! reads the content of the data set \c dataSet from file \c fileName. */ QVector HDF5Filter::readCurrentDataSet(const QString& fileName, AbstractDataSource* dataSource, bool &ok, AbstractFileFilter::ImportMode importMode, int lines) { return d->readCurrentDataSet(fileName, dataSource, ok, importMode, lines); } /*! reads the content of the file \c fileName to the data source \c dataSource. */ QVector HDF5Filter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { return d->readDataFromFile(fileName, dataSource, mode, lines); } /*! writes the content of the data source \c dataSource to the file \c fileName. */ void HDF5Filter::write(const QString& fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); } /////////////////////////////////////////////////////////////////////// /*! loads the predefined filter settings for \c filterName */ void HDF5Filter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void HDF5Filter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /////////////////////////////////////////////////////////////////////// void HDF5Filter::setCurrentDataSetName(const QString& ds) { d->currentDataSetName = ds; } const QString HDF5Filter::currentDataSetName() const { return d->currentDataSetName; } void HDF5Filter::setStartRow(const int s) { d->startRow = s; } int HDF5Filter::startRow() const { return d->startRow; } void HDF5Filter::setEndRow(const int e) { d->endRow = e; } int HDF5Filter::endRow() const { return d->endRow; } void HDF5Filter::setStartColumn(const int c) { d->startColumn = c; } int HDF5Filter::startColumn() const { return d->startColumn; } void HDF5Filter::setEndColumn(const int c) { d->endColumn = c; } int HDF5Filter::endColumn() const { return d->endColumn; } //##################################################################### //################### Private implementation ########################## //##################################################################### HDF5FilterPrivate::HDF5FilterPrivate(HDF5Filter* owner) : q(owner),currentDataSetName(""),startRow(1), endRow(-1), startColumn(1), endColumn(-1) { #ifdef HAVE_HDF5 m_status = 0; #endif } #ifdef HAVE_HDF5 void HDF5FilterPrivate::handleError(int err, const QString& function, const QString& arg) { #ifdef NDEBUG Q_UNUSED(err) Q_UNUSED(function) Q_UNUSED(arg) #else if (err < 0) DEBUG("ERROR " << err << ":" << function.toStdString() << "() - " << arg.toStdString()); #endif } QString HDF5FilterPrivate::translateHDF5Order(H5T_order_t o) { QString order; switch (o) { case H5T_ORDER_LE: order = "LE"; break; case H5T_ORDER_BE: order = "BE"; break; case H5T_ORDER_VAX: order = "VAX"; break; case H5T_ORDER_MIXED: order = "MIXED"; break; case H5T_ORDER_NONE: order = "NONE"; break; case H5T_ORDER_ERROR: order = "ERROR"; break; } return order; } QString HDF5FilterPrivate::translateHDF5Type(hid_t t) { QString type; if (H5Tequal(t, H5T_STD_I8LE) || H5Tequal(t, H5T_STD_I8BE)) type = "CHAR"; else if (H5Tequal(t, H5T_STD_U8LE) || H5Tequal(t, H5T_STD_U8BE)) type = "UCHAR"; else if (H5Tequal(t, H5T_STD_I16LE) || H5Tequal(t, H5T_STD_I16BE)) type = "SHORT"; else if (H5Tequal(t, H5T_STD_U16LE) || H5Tequal(t, H5T_STD_U16BE)) type = "USHORT"; else if (H5Tequal(t, H5T_STD_I32LE) || H5Tequal(t, H5T_STD_I32BE)) type = "INT"; else if (H5Tequal(t, H5T_STD_U32LE) || H5Tequal(t, H5T_STD_U32BE)) type = "UINT"; else if (H5Tequal(t, H5T_NATIVE_LONG)) type = "LONG"; else if (H5Tequal(t, H5T_NATIVE_ULONG)) type = "ULONG"; else if (H5Tequal(t, H5T_STD_I64LE) || H5Tequal(t, H5T_STD_I64BE)) type = "LLONG"; else if (H5Tequal(t, H5T_STD_U64LE) || H5Tequal(t, H5T_STD_U64BE)) type = "ULLONG"; else if (H5Tequal(t, H5T_IEEE_F32LE) || H5Tequal(t, H5T_IEEE_F32BE)) type = "FLOAT"; else if (H5Tequal(t, H5T_IEEE_F64LE) || H5Tequal(t, H5T_IEEE_F64BE)) type = "DOUBLE"; else if (H5Tequal(t, H5T_NATIVE_LDOUBLE)) type = "LDOUBLE"; else type = "UNKNOWN"; return type; } QString HDF5FilterPrivate::translateHDF5Class(H5T_class_t c) { QString dclass; switch (c) { case H5T_INTEGER: dclass = "INTEGER"; break; case H5T_FLOAT: dclass = "FLOAT"; break; case H5T_STRING: dclass = "STRING"; break; case H5T_BITFIELD: dclass = "BITFIELD"; break; case H5T_OPAQUE: dclass = "OPAQUE"; break; case H5T_COMPOUND: dclass = "COMPOUND"; break; case H5T_ARRAY: dclass = "ARRAY"; break; case H5T_ENUM: dclass = "ENUM"; break; case H5T_REFERENCE: dclass = "REFERENCE"; break; case H5T_VLEN: dclass = "VLEN"; break; case H5T_TIME: dclass = "TIME"; break; case H5T_NCLASSES: dclass = "NCLASSES"; break; case H5T_NO_CLASS: dclass = "NOCLASS"; break; } return dclass; } QStringList HDF5FilterPrivate::readHDF5Compound(hid_t tid) { size_t typeSize = H5Tget_size(tid); QString line; line += QLatin1String("COMPOUND(") + QString::number(typeSize) + QLatin1String(") : ("); int members = H5Tget_nmembers(tid); handleError(members, "H5Tget_nmembers"); for (int i=0; i < members; ++i) { H5T_class_t mclass = H5Tget_member_class(tid, i); handleError((int)mclass, "H5Tget_member_class"); hid_t mtype = H5Tget_member_type(tid, i); handleError((int)mtype, "H5Tget_member_type"); size_t size = H5Tget_size(mtype); handleError((int)size, "H5Tget_size"); QString typeString = translateHDF5Class(mclass); if (mclass == H5T_INTEGER || mclass == H5T_FLOAT) typeString = translateHDF5Type(mtype); line += H5Tget_member_name(tid, i) + QLatin1String("[") + typeString + QLatin1String("(") + QString::number(size) + QLatin1String(")]"); if (i == members-1) line += QLatin1String(")"); else line += QLatin1String(","); m_status = H5Tclose(mtype); handleError(m_status, "H5Tclose"); } QStringList dataString; dataString << line; return dataString; } template QStringList HDF5FilterPrivate::readHDF5Data1D(hid_t dataset, hid_t type, int rows, int lines, void* dataContainer) { DEBUG("readHDF5Data1D() rows =" << rows << "lines =" << lines); QStringList dataString; // we read all rows of data T* data = new T[rows]; m_status = H5Dread(dataset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); handleError(m_status, "H5Dread"); DEBUG(" startRow =" << startRow << "endRow =" << endRow); // DEBUG("dataContainer =" << dataContainer); for (int i = startRow-1; i < qMin(endRow, lines+startRow-1); ++i) { if (dataContainer) // read to data source static_cast*>(dataContainer)->operator[](i-startRow+1) = data[i]; else // for preview dataString << QString::number(static_cast(data[i])); } delete[] data; return dataString; } QStringList HDF5FilterPrivate::readHDF5CompoundData1D(hid_t dataset, hid_t tid, int rows, int lines, QVector& dataContainer) { DEBUG("HDF5FilterPrivate::readHDF5CompoundData1D()"); DEBUG(" dataContainer size = " << dataContainer.size()); int members = H5Tget_nmembers(tid); handleError(members, "H5Tget_nmembers"); DEBUG(" # members = " << members); QStringList dataString; if (!dataContainer[0]) { for (int i = 0; i < qMin(rows, lines); ++i) dataString << QLatin1String("("); dataContainer.resize(members); // avoid "index out of range" for preview } for (int m = 0; m < members; ++m) { hid_t mtype = H5Tget_member_type(tid, m); handleError((int)mtype, "H5Tget_member_type"); size_t msize = H5Tget_size(mtype); handleError((int)msize, "H5Tget_size"); hid_t ctype = H5Tcreate(H5T_COMPOUND, msize); handleError((int)ctype, "H5Tcreate"); m_status = H5Tinsert(ctype, H5Tget_member_name(tid, m), 0, mtype); handleError(m_status, "H5Tinsert"); QStringList mdataString; if (H5Tequal(mtype, H5T_STD_I8LE) || H5Tequal(mtype, H5T_STD_I8BE)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_NATIVE_CHAR)) { switch (sizeof(H5T_NATIVE_CHAR)) { case 1: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 2: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 4: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 8: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; } } else if (H5Tequal(mtype, H5T_STD_U8LE) || H5Tequal(mtype, H5T_STD_U8BE)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_NATIVE_UCHAR)) { switch (sizeof(H5T_NATIVE_UCHAR)) { case 1: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 2: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 4: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; case 8: mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); break; } } else if (H5Tequal(mtype, H5T_STD_I16LE) || H5Tequal(mtype, H5T_STD_I16BE) || H5Tequal(mtype, H5T_NATIVE_SHORT)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_STD_U16LE) || H5Tequal(mtype, H5T_STD_U16BE) || H5Tequal(mtype, H5T_NATIVE_SHORT)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_STD_I32LE) || H5Tequal(mtype, H5T_STD_I32BE) || H5Tequal(mtype, H5T_NATIVE_INT)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_STD_U32LE) || H5Tequal(mtype, H5T_STD_U32BE) || H5Tequal(mtype, H5T_NATIVE_UINT)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_NATIVE_LONG)) mdataString = readHDF5Data1D(dataset, ctype, rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_NATIVE_ULONG)) mdataString = readHDF5Data1D(dataset, ctype, rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_STD_I64LE) || H5Tequal(mtype, H5T_STD_I64BE) || H5Tequal(mtype, H5T_NATIVE_LLONG)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_STD_U64LE) || H5Tequal(mtype, H5T_STD_U64BE) || H5Tequal(mtype, H5T_NATIVE_ULLONG)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_IEEE_F32LE) || H5Tequal(mtype, H5T_IEEE_F32BE)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_IEEE_F64LE) || H5Tequal(mtype, H5T_IEEE_F64BE)) mdataString = readHDF5Data1D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, lines, dataContainer[m]); else if (H5Tequal(mtype, H5T_NATIVE_LDOUBLE)) mdataString = readHDF5Data1D(dataset, ctype, rows, lines, dataContainer[m]); else { if (dataContainer[m]) { for (int i = startRow-1; i < qMin(endRow, lines+startRow-1); ++i) static_cast*>(dataContainer[m])->operator[](i-startRow+1) = 0; } else { for (int i = 0; i < qMin(rows, lines); ++i) mdataString << QLatin1String("_"); } H5T_class_t mclass = H5Tget_member_class(tid, m); handleError((int)mclass, "H5Tget_member_class"); DEBUG("unsupported type of class " << translateHDF5Class(mclass).toStdString()); } if (!dataContainer[0]) { for (int i = 0; i < qMin(rows, lines); ++i) { dataString[i] += mdataString[i]; if (m < members-1) dataString[i] += QLatin1String(","); } } H5Tclose(ctype); } if (!dataContainer[0]) { for (int i = 0; i < qMin(rows, lines); ++i) dataString[i] += QLatin1String(")"); } return dataString; } template QVector HDF5FilterPrivate::readHDF5Data2D(hid_t dataset, hid_t type, int rows, int cols, int lines, QVector& dataPointer) { DEBUG("readHDF5Data2D() rows = " << rows << ", cols =" << cols << ", lines =" << lines); QVector dataStrings; T** data = (T**) malloc(rows*sizeof(T*)); data[0] = (T*) malloc(cols*rows*sizeof(T)); for (int i = 1; i < rows; ++i) data[i] = data[0]+i*cols; m_status = H5Dread(dataset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data[0][0]); handleError(m_status,"H5Dread"); for (int i = 0; i < qMin(rows, lines); ++i) { QStringList line; line.reserve(cols); for (int j = 0; j < cols; ++j) { if (dataPointer[0]) static_cast*>(dataPointer[j-startColumn+1])->operator[](i-startRow+1) = data[i][j]; else line << QString::number(static_cast(data[i][j])); } dataStrings << line; } free(data[0]); free(data); QDEBUG(dataStrings); return dataStrings; } QVector HDF5FilterPrivate::readHDF5CompoundData2D(hid_t dataset, hid_t tid, int rows, int cols, int lines) { DEBUG("readHDF5CompoundData2D() rows =" << rows << "cols =" << cols << "lines =" << lines); int members = H5Tget_nmembers(tid); handleError(members, "H5Tget_nmembers"); DEBUG(" # members =" << members); QVector dataStrings; for (int i = 0; i < qMin(rows, lines); ++i) { QStringList lineStrings; for (int j = 0; j < cols; ++j) lineStrings << QLatin1String("("); dataStrings << lineStrings; } //QStringList* data = new QStringList[members]; for (int m = 0; m < members; ++m) { hid_t mtype = H5Tget_member_type(tid, m); handleError((int)mtype, "H5Tget_member_type"); size_t msize = H5Tget_size(mtype); handleError((int)msize, "H5Tget_size"); hid_t ctype = H5Tcreate(H5T_COMPOUND, msize); handleError((int)ctype, "H5Tcreate"); m_status = H5Tinsert(ctype, H5Tget_member_name(tid, m), 0, mtype); handleError(m_status, "H5Tinsert"); // dummy container for all data columns // initially contains one pointer set to NULL QVector dummy(1, nullptr); QVector mdataStrings; if (H5Tequal(mtype, H5T_STD_I8LE) || H5Tequal(mtype, H5T_STD_I8BE)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_NATIVE_CHAR)) { switch (sizeof(H5T_NATIVE_CHAR)) { case 1: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 2: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 4: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 8: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; } } else if (H5Tequal(mtype, H5T_STD_U8LE) || H5Tequal(mtype, H5T_STD_U8BE)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_NATIVE_UCHAR)) { switch (sizeof(H5T_NATIVE_UCHAR)) { case 1: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 2: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 4: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; case 8: mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); break; } } else if (H5Tequal(mtype, H5T_STD_I16LE) || H5Tequal(mtype, H5T_STD_I16BE)|| H5Tequal(mtype, H5T_NATIVE_SHORT)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_STD_U16LE) || H5Tequal(mtype, H5T_STD_U16BE) || H5Tequal(mtype, H5T_NATIVE_USHORT)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_STD_I32LE) || H5Tequal(mtype, H5T_STD_I32BE) || H5Tequal(mtype, H5T_NATIVE_INT)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_STD_U32LE) || H5Tequal(mtype, H5T_STD_U32BE) || H5Tequal(mtype, H5T_NATIVE_UINT)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_NATIVE_LONG)) mdataStrings = readHDF5Data2D(dataset, ctype, rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_NATIVE_ULONG)) mdataStrings = readHDF5Data2D(dataset, ctype, rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_STD_I64LE) || H5Tequal(mtype, H5T_STD_I64BE) || H5Tequal(mtype, H5T_NATIVE_LLONG)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_STD_U64LE) || H5Tequal(mtype, H5T_STD_U64BE) || H5Tequal(mtype, H5T_NATIVE_ULLONG)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_IEEE_F32LE) || H5Tequal(mtype, H5T_IEEE_F32BE)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_IEEE_F64LE) || H5Tequal(mtype, H5T_IEEE_F64BE)) mdataStrings = readHDF5Data2D(dataset, H5Tget_native_type(ctype, H5T_DIR_DEFAULT), rows, cols, lines, dummy); else if (H5Tequal(mtype, H5T_NATIVE_LDOUBLE)) mdataStrings = readHDF5Data2D(dataset, ctype, rows, cols, lines, dummy); else { for (int i = 0; i < qMin(rows, lines); ++i) { QStringList lineString; for (int j = 0; j < cols; ++j) lineString << QLatin1String("_"); mdataStrings << lineString; } #ifndef NDEBUG H5T_class_t mclass = H5Tget_member_class(tid, m); #endif DEBUG("unsupported class " << translateHDF5Class(mclass).toStdString()); } m_status = H5Tclose(ctype); handleError(m_status, "H5Tclose"); for (int i = 0; i < qMin(rows, lines); i++) { for (int j = 0; j < cols; j++) { dataStrings[i][j] += mdataStrings[i][j]; if (m < members-1) dataStrings[i][j] += QLatin1String(","); } } } for (int i = 0; i < qMin(rows, lines); ++i) { for (int j = 0; j < cols; ++j) dataStrings[i][j] += QLatin1String(")"); } QDEBUG("dataStrings =" << dataStrings); return dataStrings; } QStringList HDF5FilterPrivate::readHDF5Attr(hid_t aid) { QStringList attr; char name[MAXNAMELENGTH]; m_status = H5Aget_name(aid, MAXNAMELENGTH, name); handleError(m_status, "H5Aget_name"); attr << QString(name); // DEBUG(" name =" << QString(name)); hid_t aspace = H5Aget_space(aid); // the dimensions of the attribute data handleError((int)aspace, "H5Aget_space"); hid_t atype = H5Aget_type(aid); handleError((int)atype, "H5Aget_type"); hid_t aclass = H5Tget_class(atype); handleError((int)aclass, "H5Aget_class"); if (aclass == H5T_STRING) { char buf[MAXSTRINGLENGTH]; // buffer to read attr value hid_t amem = H5Tget_native_type(atype, H5T_DIR_ASCEND); handleError((int)amem, "H5Tget_native_type"); m_status = H5Aread(aid, amem, buf); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString(buf); m_status = H5Tclose(amem); handleError(m_status, "H5Tclose"); } else if (aclass == H5T_INTEGER) { if (H5Tequal(atype, H5T_STD_I8LE)) { qint8 value; m_status = H5Aread(aid, H5T_STD_I8LE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_I8BE)) { qint8 value; m_status = H5Aread(aid, H5T_STD_I8BE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_NATIVE_CHAR)) { switch (sizeof(H5T_NATIVE_CHAR)) { case 1: { qint8 value; m_status = H5Aread(aid, H5T_NATIVE_CHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 2: { qint16 value; m_status = H5Aread(aid, H5T_NATIVE_CHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 4: { qint32 value; m_status = H5Aread(aid, H5T_NATIVE_CHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 8: { qint64 value; m_status = H5Aread(aid, H5T_NATIVE_CHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } default: DEBUG("unknown size " << sizeof(H5T_NATIVE_CHAR) << " of H5T_NATIVE_CHAR"); return QStringList(""); } } else if (H5Tequal(atype, H5T_STD_U8LE)) { uint8_t value; m_status = H5Aread(aid, H5T_STD_U8LE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_U8BE)) { uint8_t value; m_status = H5Aread(aid, H5T_STD_U8BE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_NATIVE_UCHAR)) { switch (sizeof(H5T_NATIVE_UCHAR)) { case 1: { uint8_t value; m_status = H5Aread(aid, H5T_NATIVE_UCHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 2: { uint16_t value; m_status = H5Aread(aid, H5T_NATIVE_UCHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 4: { uint32_t value; m_status = H5Aread(aid, H5T_NATIVE_UCHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } case 8: { uint64_t value; m_status = H5Aread(aid, H5T_NATIVE_UCHAR, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); break; } default: DEBUG("unknown size " << sizeof(H5T_NATIVE_UCHAR) << " of H5T_NATIVE_UCHAR"); return QStringList(""); } } else if (H5Tequal(atype, H5T_STD_I16LE) || H5Tequal(atype, H5T_STD_I16BE) || H5Tequal(atype, H5T_NATIVE_SHORT)) { short value; m_status = H5Aread(aid, H5T_NATIVE_SHORT, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_U16LE) || H5Tequal(atype, H5T_STD_U16BE) || H5Tequal(atype, H5T_NATIVE_USHORT)) { unsigned short value; m_status = H5Aread(aid, H5T_NATIVE_USHORT, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_I32LE) || H5Tequal(atype, H5T_STD_I32BE) || H5Tequal(atype, H5T_NATIVE_INT)) { int value; m_status = H5Aread(aid, H5T_NATIVE_INT, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_U32LE) || H5Tequal(atype, H5T_STD_U32BE) || H5Tequal(atype, H5T_NATIVE_UINT)) { unsigned int value; m_status = H5Aread(aid, H5T_NATIVE_UINT, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_NATIVE_LONG)) { long value; m_status = H5Aread(aid, H5T_NATIVE_LONG, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_NATIVE_ULONG)) { unsigned long value; m_status = H5Aread(aid, H5T_NATIVE_ULONG, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_I64LE) || H5Tequal(atype, H5T_STD_I64BE) || H5Tequal(atype, H5T_NATIVE_LLONG)) { long long value; m_status = H5Aread(aid, H5T_NATIVE_LLONG, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_STD_U64LE) || H5Tequal(atype, H5T_STD_U64BE) || H5Tequal(atype, H5T_NATIVE_ULLONG)) { unsigned long long value; m_status = H5Aread(aid, H5T_NATIVE_ULLONG, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else attr<<" (unknown integer)"; } else if (aclass == H5T_FLOAT) { if (H5Tequal(atype, H5T_IEEE_F32LE) || H5Tequal(atype, H5T_IEEE_F32BE)) { float value; m_status = H5Aread(aid, H5T_NATIVE_FLOAT, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_IEEE_F64LE) || H5Tequal(atype, H5T_IEEE_F64BE)) { double value; m_status = H5Aread(aid, H5T_NATIVE_DOUBLE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number(value); } else if (H5Tequal(atype, H5T_NATIVE_LDOUBLE)) { long double value; m_status = H5Aread(aid, H5T_NATIVE_LDOUBLE, &value); handleError(m_status, "H5Aread"); attr << QLatin1String("=") << QString::number((double)value); } else attr<<" (unknown float)"; } m_status = H5Tclose(atype); handleError(m_status, "H5Tclose"); m_status = H5Sclose(aspace); handleError(m_status, "H5Sclose"); return attr; } QStringList HDF5FilterPrivate::scanHDF5Attrs(hid_t oid) { QStringList attrList; int numAttr = H5Aget_num_attrs(oid); handleError(numAttr, "H5Aget_num_attrs"); DEBUG("number of attr =" << numAttr); for (int i = 0; i < numAttr; ++i) { hid_t aid = H5Aopen_idx(oid, i); handleError((int)aid, "H5Aopen_idx"); attrList << readHDF5Attr(aid); if (i < numAttr-1) attrList << QLatin1String(", "); m_status = H5Aclose(aid); handleError(m_status, "H5Aclose"); } return attrList; } QStringList HDF5FilterPrivate::readHDF5DataType(hid_t tid) { H5T_class_t typeClass = H5Tget_class(tid); handleError((int)typeClass, "H5Tget_class"); QStringList typeProps; QString typeString = translateHDF5Class(typeClass); if (typeClass == H5T_INTEGER || typeClass == H5T_FLOAT) typeString = translateHDF5Type(tid); typeProps<setIcon(0, QIcon::fromTheme("accessories-calculator")); dataTypeItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(dataTypeItem); } void HDF5FilterPrivate::scanHDF5DataSet(hid_t did, char *dataSetName, QTreeWidgetItem* parentItem) { QString attr = scanHDF5Attrs(did).join(""); char link[MAXNAMELENGTH]; m_status = H5Iget_name(did, link, MAXNAMELENGTH); handleError(m_status, "H5Iget_name"); QStringList dataSetProps; hsize_t size = H5Dget_storage_size(did); handleError((int)size, "H5Dget_storage_size"); hid_t datatype = H5Dget_type(did); handleError((int)datatype, "H5Dget_type"); size_t typeSize = H5Tget_size(datatype); handleError((int)typeSize, "H5Dget_size"); dataSetProps << readHDF5DataType(datatype); hid_t dataspace = H5Dget_space(did); int rank = H5Sget_simple_extent_ndims(dataspace); handleError(rank, "H5Sget_simple_extent_ndims"); unsigned int rows = 1, cols = 1, regs = 1; if (rank == 1) { hsize_t dims_out[1]; m_status = H5Sget_simple_extent_dims(dataspace, dims_out, NULL); handleError(m_status, "H5Sget_simple_extent_dims"); rows = dims_out[0]; dataSetProps << QLatin1String(", ") << QString::number(rows) << QLatin1String(" (") << QString::number(size/typeSize) << QLatin1String(")"); } else if (rank == 2) { hsize_t dims_out[2]; m_status = H5Sget_simple_extent_dims(dataspace, dims_out, NULL); handleError(m_status, "H5Sget_simple_extent_dims"); rows = dims_out[0]; cols = dims_out[1]; dataSetProps << QLatin1String(", ") << QString::number(rows) << QLatin1String("x") << QString::number(cols) << QLatin1String(" (") << QString::number(size/typeSize) << QLatin1String(")"); } else if (rank == 3) { hsize_t dims_out[3]; m_status = H5Sget_simple_extent_dims(dataspace, dims_out, NULL); handleError(m_status, "H5Sget_simple_extent_dims"); rows = dims_out[0]; cols = dims_out[1]; regs = dims_out[2]; dataSetProps << QLatin1String(", ") << QString::number(rows) << QLatin1String("x") << QString::number(cols) << QLatin1String("x") << QString::number(regs) << QLatin1String(" (") << QString::number(size/typeSize) << QLatin1String(")"); } else - dataSetProps << QLatin1String(", ") << i18n("rank %1 not supported yet").arg(rank); + dataSetProps << QLatin1String(", ") << i18n("rank %1 not supported yet", rank); hid_t pid = H5Dget_create_plist(did); handleError((int)pid, "H5Dget_create_plist"); dataSetProps << ", " << readHDF5PropertyList(pid).join(""); QTreeWidgetItem* dataSetItem = new QTreeWidgetItem(QStringList()<setIcon(0, QIcon::fromTheme("x-office-spreadsheet")); for (int i = 0; i < dataSetItem->columnCount(); ++i) { if (rows > 0 && cols > 0 && regs > 0) { dataSetItem->setBackground(i, QColor(192,255,192)); dataSetItem->setForeground(i, Qt::black); dataSetItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); } else dataSetItem->setFlags(Qt::NoItemFlags); } parentItem->addChild(dataSetItem); } void HDF5FilterPrivate::scanHDF5Link(hid_t gid, char *linkName, QTreeWidgetItem* parentItem) { char target[MAXNAMELENGTH]; m_status = H5Gget_linkval(gid, linkName, MAXNAMELENGTH, target) ; handleError(m_status, "H5Gget_linkval"); - QTreeWidgetItem* linkItem = new QTreeWidgetItem(QStringList() << QString(linkName) << i18n("symbolic link") << i18n("link to") + QString(target)); + QTreeWidgetItem* linkItem = new QTreeWidgetItem(QStringList() << QString(linkName) << i18n("symbolic link") << i18n("link to %1", QFile::decodeName(target))); linkItem->setIcon(0, QIcon::fromTheme("emblem-symbolic-link")); linkItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(linkItem); } void HDF5FilterPrivate::scanHDF5Group(hid_t gid, char *groupName, QTreeWidgetItem* parentItem) { DEBUG("HDF5FilterPrivate::scanHDF5Group()"); //check for hard link H5G_stat_t statbuf; m_status = H5Gget_objinfo(gid, ".", true, &statbuf); handleError(m_status, "H5Gget_objinfo"); if (statbuf.nlink > 1) { if (m_multiLinkList.contains(statbuf.objno[0])) { QTreeWidgetItem* objectItem = new QTreeWidgetItem(QStringList()<setIcon(0, QIcon::fromTheme("link")); objectItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(objectItem); return; } else { m_multiLinkList.append(statbuf.objno[0]); DEBUG(" group multiple links: "<setIcon(0, QIcon::fromTheme("folder")); groupItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(groupItem); hsize_t numObj; m_status = H5Gget_num_objs(gid, &numObj); handleError(m_status, "H5Gget_num_objs"); for (unsigned int i = 0; i < numObj; ++i) { char memberName[MAXNAMELENGTH]; m_status = H5Gget_objname_by_idx(gid, (hsize_t)i, memberName, (size_t)MAXNAMELENGTH ); handleError(m_status, "H5Gget_objname_by_idx"); int otype = H5Gget_objtype_by_idx(gid, (size_t)i ); handleError(otype, "H5Gget_objtype_by_idx"); switch (otype) { case H5G_LINK: { scanHDF5Link(gid, memberName, groupItem); break; } case H5G_GROUP: { hid_t grpid = H5Gopen(gid, memberName, H5P_DEFAULT); handleError((int)grpid, "H5Gopen"); scanHDF5Group(grpid, memberName, groupItem); m_status = H5Gclose(grpid); handleError(m_status, "H5Gclose"); break; } case H5G_DATASET: { hid_t dsid = H5Dopen(gid, memberName, H5P_DEFAULT); handleError((int)dsid, "H5Dopen"); scanHDF5DataSet(dsid, memberName, groupItem); m_status = H5Dclose(dsid); handleError(m_status, "H5Dclose"); break; } case H5G_TYPE: { hid_t tid = H5Topen(gid, memberName, H5P_DEFAULT); handleError((int)tid, "H5Topen"); scanHDF5DataType(tid, memberName, groupItem); m_status = H5Tclose(tid); handleError(m_status, "H5Tclose"); break; } default: QTreeWidgetItem* objectItem = new QTreeWidgetItem(QStringList() << QString(memberName) << i18n("unknown")); objectItem->setFlags(Qt::ItemIsEnabled); groupItem->addChild(objectItem); break; } } } #endif /*! parses the content of the file \c fileName and fill the tree using rootItem. */ void HDF5FilterPrivate::parse(const QString& fileName, QTreeWidgetItem* rootItem) { DEBUG("HDF5FilterPrivate::parse()"); #ifdef HAVE_HDF5 QByteArray bafileName = fileName.toLatin1(); DEBUG("fileName = " << bafileName.data()); // check file type first htri_t isHdf5 = H5Fis_hdf5(bafileName.data()); if (isHdf5 == 0) { DEBUG(bafileName.data() << " is not a HDF5 file! Giving up."); return; } if (isHdf5 < 0) { DEBUG("H5Fis_hdf5() failed on " << bafileName.data() << "! Giving up."); return; } // open file hid_t file = H5Fopen(bafileName.data(), H5F_ACC_RDONLY, H5P_DEFAULT); handleError((int)file, "H5Fopen", fileName); if (file < 0) { DEBUG("Opening file " << bafileName.data() << " failed! Giving up."); return; } char rootName[] = "/"; hid_t group = H5Gopen(file, rootName, H5P_DEFAULT); handleError((int)group, "H5Gopen", rootName); // CRASHES multiLinkList.clear(); scanHDF5Group(group, rootName, rootItem); m_status = H5Gclose(group); handleError(m_status, "H5Gclose", ""); m_status = H5Fclose(file); handleError(m_status, "H5Fclose", ""); #else DEBUG("HDF5 not available"); Q_UNUSED(fileName) Q_UNUSED(rootItem) #endif } /*! reads the content of the date set in the file \c fileName to a string (for preview) or to the data source. */ QVector HDF5FilterPrivate::readCurrentDataSet(const QString& fileName, AbstractDataSource* dataSource, bool &ok, AbstractFileFilter::ImportMode mode, int lines) { DEBUG("HDF5Filter::readCurrentDataSet()"); QVector dataStrings; if (currentDataSetName.isEmpty()) { //return QString("No data set selected").replace(' ',QChar::Nbsp); ok = false; return dataStrings << (QStringList() << i18n("No data set selected")); } DEBUG(" current data set =" << currentDataSetName.toStdString()); #ifdef HAVE_HDF5 QByteArray bafileName = fileName.toLatin1(); hid_t file = H5Fopen(bafileName.data(), H5F_ACC_RDONLY, H5P_DEFAULT); handleError((int)file, "H5Fopen", fileName); QByteArray badataSet = currentDataSetName.toLatin1(); hid_t dataset = H5Dopen2(file, badataSet.data(), H5P_DEFAULT); handleError((int)file, "H5Dopen2", currentDataSetName); // Get datatype and dataspace hid_t dtype = H5Dget_type(dataset); handleError((int)dtype, "H5Dget_type"); H5T_class_t dclass = H5Tget_class(dtype); handleError((int)dclass, "H5Dget_class"); size_t typeSize = H5Tget_size(dtype); handleError((int)(typeSize-1), "H5Dget_size"); hid_t dataspace = H5Dget_space(dataset); handleError((int)dataspace, "H5Dget_space"); int rank = H5Sget_simple_extent_ndims(dataspace); handleError(rank, "H5Dget_simple_extent_ndims"); DEBUG(" rank =" << rank); int columnOffset = 0; // offset to import data int actualRows = 0, actualCols = 0; // rows and cols to read // dataContainer is used to store the data read from the dataSource // it contains the pointers of all columns // initially there is one pointer set to nullptr // check for dataContainer[0] != nullptr to decide if dataSource can be used QVector dataContainer(1, nullptr); // rank= 0: single value, 1: vector, 2: matrix, 3: 3D data, ... switch (rank) { case 0: { // single value actualCols = 1; switch (dclass) { case H5T_STRING: { char* data = (char *) malloc(typeSize * sizeof(char)); hid_t memtype = H5Tcopy(H5T_C_S1); handleError((int)memtype, "H5Tcopy"); m_status = H5Tset_size(memtype, typeSize); handleError(m_status, "H5Tset_size"); m_status = H5Dread(dataset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); handleError(m_status, "H5Tread"); dataStrings << (QStringList() << data); free(data); break; } case H5T_INTEGER: case H5T_FLOAT: case H5T_TIME: case H5T_BITFIELD: case H5T_OPAQUE: case H5T_COMPOUND: case H5T_REFERENCE: case H5T_ENUM: case H5T_VLEN: case H5T_ARRAY: case H5T_NO_CLASS: case H5T_NCLASSES: { ok = false; - dataStrings << (QStringList() << i18n("rank 0 not implemented yet for type %1").arg(translateHDF5Class(dclass))); + dataStrings << (QStringList() << i18n("rank 0 not implemented yet for type %1", translateHDF5Class(dclass))); qDebug() << dataStrings; } default: break; } break; } case 1: { // vector hsize_t size, maxSize; m_status = H5Sget_simple_extent_dims(dataspace, &size, &maxSize); handleError(m_status, "H5Sget_simple_extent_dims"); int rows = size; if (endRow == -1) endRow = rows; if (lines == -1) lines = endRow; actualRows = endRow-startRow+1; actualCols = 1; #ifndef NDEBUG H5T_order_t order = H5Tget_order(dtype); handleError((int)order, "H5Sget_order"); #endif QDEBUG(translateHDF5Class(dclass) << '(' << typeSize << ')' << translateHDF5Order(order) << ", rows:" << rows << " max:" << maxSize); //TODO: support other modes QVector columnModes; columnModes.resize(actualCols); //TODO: use given names? QStringList vectorNames; if (dataSource) columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes); QStringList dataString; // data saved in a list switch (dclass) { case H5T_STRING: { DEBUG("rank 1 H5T_STRING"); hid_t memtype = H5Tcopy(H5T_C_S1); handleError((int)memtype, "H5Tcopy"); char** data = (char **) malloc(rows * sizeof (char *)); if (H5Tis_variable_str(dtype)) { m_status = H5Tset_size(memtype, H5T_VARIABLE); handleError((int)memtype, "H5Tset_size"); m_status = H5Dread(dataset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); handleError(m_status, "H5Dread"); } else { data[0] = (char *) malloc(rows * typeSize * sizeof (char)); for (int i = 1; i < rows; ++i) data[i] = data[0] + i * typeSize; m_status = H5Tset_size(memtype, typeSize); handleError((int)memtype, "H5Tset_size"); m_status = H5Dread(dataset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, data[0]); handleError(m_status, "H5Dread"); } for (int i = startRow-1; i < qMin(endRow, lines+startRow-1); ++i) dataString << data[i]; free(data); break; } case H5T_INTEGER: { if (H5Tequal(dtype, H5T_STD_I8LE)) dataString = readHDF5Data1D(dataset, H5T_STD_I8LE, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_I8BE)) dataString = readHDF5Data1D(dataset, H5T_STD_I8BE, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_NATIVE_CHAR)) { switch (sizeof(H5T_NATIVE_CHAR)) { case 1: dataString = readHDF5Data1D(dataset, H5T_NATIVE_CHAR, rows, lines, dataContainer[0]); break; case 2: dataString = readHDF5Data1D(dataset, H5T_NATIVE_CHAR, rows, lines, dataContainer[0]); break; case 4: dataString = readHDF5Data1D(dataset, H5T_NATIVE_CHAR, rows, lines, dataContainer[0]); break; case 8: dataString = readHDF5Data1D(dataset, H5T_NATIVE_CHAR, rows, lines, dataContainer[0]); break; } } else if (H5Tequal(dtype, H5T_STD_U8LE)) dataString = readHDF5Data1D(dataset, H5T_STD_U8LE, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_U8BE)) dataString = readHDF5Data1D(dataset, H5T_STD_U8BE, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_NATIVE_UCHAR)) { switch (sizeof(H5T_NATIVE_UCHAR)) { case 1: dataString = readHDF5Data1D(dataset, H5T_NATIVE_UCHAR, rows, lines, dataContainer[0]); break; case 2: dataString = readHDF5Data1D(dataset, H5T_NATIVE_UCHAR, rows, lines, dataContainer[0]); break; case 4: dataString = readHDF5Data1D(dataset, H5T_NATIVE_UCHAR, rows, lines, dataContainer[0]); break; case 8: dataString = readHDF5Data1D(dataset, H5T_NATIVE_UCHAR, rows, lines, dataContainer[0]); break; } } else if (H5Tequal(dtype, H5T_STD_I16LE) || H5Tequal(dtype, H5T_STD_I16BE) || H5Tequal(dtype, H5T_NATIVE_SHORT)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_SHORT, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_U16LE) || H5Tequal(dtype, H5T_STD_U16BE) || H5Tequal(dtype, H5T_NATIVE_USHORT)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_USHORT, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_I32LE) || H5Tequal(dtype, H5T_STD_I32BE) || H5Tequal(dtype, H5T_NATIVE_INT)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_INT, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_U32LE) || H5Tequal(dtype, H5T_STD_U32BE) || H5Tequal(dtype, H5T_NATIVE_UINT)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_UINT, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_NATIVE_LONG)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_LONG, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_NATIVE_ULONG)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_ULONG, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_I64LE) || H5Tequal(dtype, H5T_STD_I64BE) || H5Tequal(dtype, H5T_NATIVE_LLONG)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_LLONG, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_STD_U64LE) || H5Tequal(dtype, H5T_STD_U64BE) || H5Tequal(dtype, H5T_NATIVE_ULLONG)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_ULLONG, rows, lines, dataContainer[0]); else { ok = false; dataString = (QStringList() << i18n("unsupported integer type for rank 1")); qDebug() << dataString; } break; } case H5T_FLOAT: { if (H5Tequal(dtype, H5T_IEEE_F32LE) || H5Tequal(dtype, H5T_IEEE_F32BE)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_FLOAT, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_IEEE_F64LE) || H5Tequal(dtype, H5T_IEEE_F64BE)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_DOUBLE, rows, lines, dataContainer[0]); else if (H5Tequal(dtype, H5T_NATIVE_LDOUBLE)) dataString = readHDF5Data1D(dataset, H5T_NATIVE_LDOUBLE, rows, lines, dataContainer[0]); else { ok = false; dataString = (QStringList() << i18n("unsupported float type for rank 1")); qDebug() << dataString; } break; } case H5T_COMPOUND: { int members = H5Tget_nmembers(dtype); handleError(members, "H5Tget_nmembers"); if (dataSource) { // re-create data pointer dataContainer.clear(); dataSource->prepareImport(dataContainer, mode, actualRows, members, vectorNames, columnModes); } else dataStrings << readHDF5Compound(dtype); dataString = readHDF5CompoundData1D(dataset, dtype, rows, lines, dataContainer); break; } case H5T_TIME: case H5T_BITFIELD: case H5T_OPAQUE: case H5T_REFERENCE: case H5T_ENUM: case H5T_VLEN: case H5T_ARRAY: case H5T_NO_CLASS: case H5T_NCLASSES: { ok = false; - dataString = (QStringList() << i18n("rank 1 not implemented yet for type %1").arg(translateHDF5Class(dclass))); + dataString = (QStringList() << i18n("rank 1 not implemented yet for type %1", translateHDF5Class(dclass))); qDebug() << dataString; } default: break; } if (dataSource == NULL) { QDEBUG("dataString =" << dataString); for (int i = 0; i < qMin(rows, lines); ++i) dataStrings << (QStringList() << dataString[i]); } break; } case 2: { // matrix hsize_t dims_out[2]; m_status = H5Sget_simple_extent_dims(dataspace, dims_out, NULL); handleError(m_status, "H5Sget_simple_extent_dims"); int rows = dims_out[0]; int cols = dims_out[1]; if (endRow == -1) endRow=rows; if (lines == -1) lines = endRow; if (endColumn == -1) endColumn = cols; actualRows = endRow-startRow+1; actualCols = endColumn-startColumn+1; #ifndef NDEBUG H5T_order_t order = H5Tget_order(dtype); handleError((int)order, "H5Tget_order"); #endif QDEBUG(translateHDF5Class(dclass) << '(' << typeSize << ')' << translateHDF5Order(order) << "," << rows << "x" << cols); DEBUG("startRow/endRow" << startRow << endRow); DEBUG("startColumn/endColumn" << startColumn << endColumn); DEBUG("actual rows/cols" << actualRows << actualCols); DEBUG("lines" << lines); //TODO: support other modes QVector columnModes; columnModes.resize(actualCols); //TODO: use given names? QStringList vectorNames; if (dataSource) columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes); // read data switch (dclass) { case H5T_INTEGER: { if (H5Tequal(dtype, H5T_STD_I8LE)) dataStrings << readHDF5Data2D(dataset, H5T_STD_I8LE, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_I8BE)) dataStrings << readHDF5Data2D(dataset, H5T_STD_I8BE, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_NATIVE_CHAR)) { switch (sizeof(H5T_NATIVE_CHAR)) { case 1: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_CHAR, rows, cols, lines, dataContainer); break; case 2: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_CHAR, rows, cols, lines, dataContainer); break; case 4: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_CHAR, rows, cols, lines, dataContainer); break; case 8: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_CHAR, rows, cols, lines, dataContainer); break; } } else if (H5Tequal(dtype, H5T_STD_U8LE)) dataStrings << readHDF5Data2D(dataset, H5T_STD_U8LE, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_U8BE)) dataStrings << readHDF5Data2D(dataset, H5T_STD_U8BE, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_NATIVE_UCHAR)) { switch (sizeof(H5T_NATIVE_UCHAR)) { case 1: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_UCHAR, rows, cols, lines, dataContainer); break; case 2: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_UCHAR, rows, cols, lines, dataContainer); break; case 4: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_UCHAR, rows, cols, lines, dataContainer); break; case 8: dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_UCHAR, rows, cols, lines, dataContainer); break; } } else if (H5Tequal(dtype, H5T_STD_I16LE) || H5Tequal(dtype, H5T_STD_I16BE) || H5Tequal(dtype, H5T_NATIVE_SHORT)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_SHORT, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_U16LE) || H5Tequal(dtype, H5T_STD_U16BE) || H5Tequal(dtype, H5T_NATIVE_USHORT)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_USHORT, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_I32LE) || H5Tequal(dtype, H5T_STD_I32BE) || H5Tequal(dtype, H5T_NATIVE_INT)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_INT, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_U32LE) || H5Tequal(dtype, H5T_STD_U32BE) || H5Tequal(dtype, H5T_NATIVE_UINT)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_UINT, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_NATIVE_LONG)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_LONG, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_NATIVE_ULONG)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_ULONG, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_I64LE) || H5Tequal(dtype, H5T_STD_I64BE) || H5Tequal(dtype, H5T_NATIVE_LLONG)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_LLONG, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_STD_U64LE) || H5Tequal(dtype, H5T_STD_U64BE) || H5Tequal(dtype, H5T_NATIVE_ULLONG)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_ULLONG, rows, cols, lines, dataContainer); else { ok=false; dataStrings << (QStringList() << i18n("unsupported integer type for rank 2")); qDebug() << dataStrings; } break; } case H5T_FLOAT: { if (H5Tequal(dtype, H5T_IEEE_F32LE) || H5Tequal(dtype, H5T_IEEE_F32BE)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_FLOAT, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_IEEE_F64LE) || H5Tequal(dtype, H5T_IEEE_F64BE)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_DOUBLE, rows, cols, lines, dataContainer); else if (H5Tequal(dtype, H5T_NATIVE_LDOUBLE)) dataStrings << readHDF5Data2D(dataset, H5T_NATIVE_LDOUBLE, rows, cols, lines, dataContainer); else { ok = false; dataStrings << (QStringList() << i18n("unsupported float type for rank 2")); qDebug() << dataStrings; } break; } case H5T_COMPOUND: { dataStrings << readHDF5Compound(dtype); qDebug() << dataStrings; dataStrings << readHDF5CompoundData2D(dataset,dtype,rows,cols,lines); break; } case H5T_STRING: { // TODO: implement this ok = false; - dataStrings << (QStringList() << i18n("rank 2 not implemented yet for type %1").arg(translateHDF5Class(dclass)) - + ", " + i18n("size = %1").arg(typeSize)); + dataStrings << (QStringList() << i18n("rank 2 not implemented yet for type %1, size = %2", translateHDF5Class(dclass), typeSize)); qDebug() << dataStrings; break; } case H5T_TIME: case H5T_BITFIELD: case H5T_OPAQUE: case H5T_REFERENCE: case H5T_ENUM: case H5T_VLEN: case H5T_ARRAY: case H5T_NO_CLASS: case H5T_NCLASSES: { ok = false; - dataStrings << (QStringList() << i18n("rank 2 not implemented yet for type %1").arg(translateHDF5Class(dclass))); + dataStrings << (QStringList() << i18n("rank 2 not implemented yet for type %1", translateHDF5Class(dclass))); qDebug() << dataStrings; } default: break; } break; } default: { // 3D or more data ok = false; - dataStrings << (QStringList() << i18n("rank %1 not implemented yet for type %2").arg(rank).arg(translateHDF5Class(dclass))); + dataStrings << (QStringList() << i18n("rank %1 not implemented yet for type %2", rank, translateHDF5Class(dclass))); qDebug() << dataStrings; } } m_status = H5Sclose(dataspace); handleError(m_status, "H5Sclose"); m_status = H5Tclose(dtype); handleError(m_status, "H5Tclose"); m_status = H5Dclose(dataset); handleError(m_status, "H5Dclose"); m_status = H5Fclose(file); handleError(m_status, "H5Fclose"); if (!dataSource) return dataStrings; dataSource->finalizeImport(columnOffset, 1, actualCols, "", mode); #else Q_UNUSED(fileName) Q_UNUSED(dataSource) Q_UNUSED(mode) Q_UNUSED(lines) #endif return dataStrings; } /*! reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. */ QVector HDF5FilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { Q_UNUSED(lines); DEBUG("HDF5Filter::read()"); QVector dataStrings; if (currentDataSetName.isEmpty()) { DEBUG("No data set selected"); return dataStrings; } bool ok = true; return readCurrentDataSet(fileName, dataSource, ok, mode); } /*! writes the content of \c dataSource to the file \c fileName. */ void HDF5FilterPrivate::write(const QString & fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO: writing HDF5 not implemented yet } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void HDF5Filter::save(QXmlStreamWriter* writer) const { writer->writeStartElement("hdfFilter"); writer->writeEndElement(); } /*! Loads from XML. */ bool HDF5Filter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "hdfFilter") { reader->raiseError(i18n("no hdf filter element found")); return false; } -// QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); +// KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); // QXmlStreamAttributes attribs = reader->attributes(); return true; } diff --git a/src/backend/datasources/filters/ImageFilter.cpp b/src/backend/datasources/filters/ImageFilter.cpp index 64824631d..cf4841cb0 100644 --- a/src/backend/datasources/filters/ImageFilter.cpp +++ b/src/backend/datasources/filters/ImageFilter.cpp @@ -1,295 +1,295 @@ /*************************************************************************** File : ImageFilter.cpp Project : LabPlot Description : Image I/O-filter -------------------------------------------------------------------- Copyright : (C) 2015 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/datasources/filters/ImageFilter.h" #include "backend/datasources/filters/ImageFilterPrivate.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/core/column/Column.h" #include -#include +#include /*! \class ImageFilter \brief Manages the import/export of data from/to an image file. \ingroup datasources */ ImageFilter::ImageFilter():AbstractFileFilter(), d(new ImageFilterPrivate(this)) {} ImageFilter::~ImageFilter() {} /*! returns the list of all predefined import formats. */ QStringList ImageFilter::importFormats() { return (QStringList() << i18n("Matrix (grayscale)") << i18n("XYZ (grayscale)") << i18n("XYRGB")); } /*! reads the content of the file \c fileName to the data source \c dataSource. */ QVector ImageFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { return d->readDataFromFile(fileName, dataSource, importMode, lines); } /*! writes the content of the data source \c dataSource to the file \c fileName. */ void ImageFilter::write(const QString & fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); // emit() } /////////////////////////////////////////////////////////////////////// /*! loads the predefined filter settings for \c filterName */ void ImageFilter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void ImageFilter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /////////////////////////////////////////////////////////////////////// void ImageFilter::setImportFormat(const ImageFilter::ImportFormat f) { d->importFormat = f; } ImageFilter::ImportFormat ImageFilter::importFormat() const { return d->importFormat; } void ImageFilter::setStartRow(const int s) { d->startRow = s; } int ImageFilter::startRow() const { return d->startRow; } void ImageFilter::setEndRow(const int e) { d->endRow = e; } int ImageFilter::endRow() const { return d->endRow; } void ImageFilter::setStartColumn(const int s) { d->startColumn = s; } int ImageFilter::startColumn() const { return d->startColumn; } void ImageFilter::setEndColumn(const int e) { d->endColumn = e; } int ImageFilter::endColumn() const { return d->endColumn; } //##################################################################### //################### Private implementation ########################## //##################################################################### ImageFilterPrivate::ImageFilterPrivate(ImageFilter* owner) : q(owner), importFormat(ImageFilter::MATRIX), startRow(1), endRow(-1), startColumn(1), endColumn(-1) { } /*! reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. */ QVector ImageFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { Q_UNUSED(lines); QVector dataStrings; QImage image = QImage(fileName); if (image.isNull() || image.format() == QImage::Format_Invalid) { #ifdef QT_DEBUG qDebug()<<"failed to read image"<*>(dataContainer[j])->operator[](i) = value; } emit q->completed(100*i/actualRows); } break; } case ImageFilter::XYZ: { int currentRow = 0; for (int i = startRow-1; i < endRow; ++i) { for (int j = startColumn-1; j < endColumn; ++j) { QRgb color=image.pixel(j, i); static_cast*>(dataContainer[0])->operator[](currentRow) = i+1; static_cast*>(dataContainer[1])->operator[](currentRow) = j+1; static_cast*>(dataContainer[2])->operator[](currentRow) = qGray(color); currentRow++; } emit q->completed(100*i/actualRows); } break; } case ImageFilter::XYRGB: { int currentRow = 0; for (int i = startRow-1; i < endRow; ++i) { for ( int j = startColumn-1; j < endColumn; ++j) { QRgb color = image.pixel(j, i); static_cast*>(dataContainer[0])->operator[](currentRow) = i+1; static_cast*>(dataContainer[1])->operator[](currentRow) = j+1; static_cast*>(dataContainer[2])->operator[](currentRow) = qRed(color); static_cast*>(dataContainer[3])->operator[](currentRow) = qGreen(color); static_cast*>(dataContainer[4])->operator[](currentRow) = qBlue(color); currentRow++; } emit q->completed(100*i/actualRows); } break; } } Spreadsheet* spreadsheet = dynamic_cast(dataSource); if (spreadsheet) { QString comment = i18np("numerical data, %1 element", "numerical data, %1 elements", rows); for ( int n = 0; n < actualCols; ++n) { Column* column = spreadsheet->column(columnOffset+n); column->setComment(comment); column->setUndoAware(true); if (mode == AbstractFileFilter::Replace) { column->setSuppressDataChangedSignal(false); column->setChanged(); } } } dataSource->finalizeImport(); return dataStrings; } /*! writes the content of \c dataSource to the file \c fileName. */ void ImageFilterPrivate::write(const QString & fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void ImageFilter::save(QXmlStreamWriter* writer) const { writer->writeStartElement("imageFilter"); writer->writeEndElement(); } /*! Loads from XML. */ bool ImageFilter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "imageFilter") { reader->raiseError(i18n("no binary filter element found")); return false; } -// QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); +// KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); // QXmlStreamAttributes attribs = reader->attributes(); return true; } diff --git a/src/backend/datasources/filters/JsonFilter.cpp b/src/backend/datasources/filters/JsonFilter.cpp index 7e3b9a933..072736479 100644 --- a/src/backend/datasources/filters/JsonFilter.cpp +++ b/src/backend/datasources/filters/JsonFilter.cpp @@ -1,776 +1,776 @@ #include "backend/datasources/filters/JsonFilter.h" #include "backend/datasources/filters/JsonFilterPrivate.h" #include "backend/datasources/AbstractDataSource.h" #include "backend/core/column/Column.h" #include #include #include #include #include -#include +#include #include /*! \class JsonFilter \brief Manages the import/export of data from/to a file formatted using JSON. \ingroup datasources */ JsonFilter::JsonFilter() : AbstractFileFilter(), d(new JsonFilterPrivate(this)) {} JsonFilter::~JsonFilter() {} /*! reads the content of the device \c device. */ void JsonFilter::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromDevice(device, dataSource, importMode, lines); } /*! reads the content of the file \c fileName. */ QVector JsonFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { d->readDataFromFile(fileName, dataSource, importMode, lines); return QVector(); //TODO: remove this later once all read*-functions in the filter classes don't return any preview strings anymore } QVector JsonFilter::preview(const QString& fileName) { return d->preview(fileName); } QVector JsonFilter::preview(QIODevice& device) { return d->preview(device); } QVector JsonFilter::preview(QJsonDocument& doc) { return d->preview(doc); } /*! writes the content of the data source \c dataSource to the file \c fileName. */ void JsonFilter::write(const QString& fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); } /////////////////////////////////////////////////////////////////////// /*! loads the predefined filter settings for \c filterName */ void JsonFilter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void JsonFilter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /*! returns the list of all predefined data types. */ QStringList JsonFilter::dataTypes() { const QMetaObject& mo = AbstractColumn::staticMetaObject; const QMetaEnum& me = mo.enumerator(mo.indexOfEnumerator("ColumnMode")); QStringList list; for (int i = 0; i <= 100; ++i) // me.keyCount() does not work because we have holes in enum if (me.valueToKey(i)) list << me.valueToKey(i); return list; } /*! returns the list of all predefined data row types. */ QStringList JsonFilter::dataRowTypes() { return (QStringList() << "Array" << "Object"); } void JsonFilter::setDataRowType(QJsonValue::Type type) { d->rowType = type; } QJsonValue::Type JsonFilter::dataRowType() const { return d->rowType; } void JsonFilter::setModelRows(QVector rows){ d->modelRows = rows; } QVector JsonFilter::modelRows() const { return d->modelRows; } void JsonFilter::setDateTimeFormat(const QString &f) { d->dateTimeFormat = f; } QString JsonFilter::dateTimeFormat() const { return d->dateTimeFormat; } void JsonFilter::setNumberFormat(QLocale::Language lang) { d->numberFormat = lang; } QLocale::Language JsonFilter::numberFormat() const { return d->numberFormat; } void JsonFilter::setNaNValueToZero(bool b) { if (b) d->nanValue = 0; else d->nanValue = NAN; } bool JsonFilter::NaNValueToZeroEnabled() const { if (d->nanValue == 0) return true; return false; } void JsonFilter::setCreateIndexEnabled(bool b){ d->createIndexEnabled = b; } void JsonFilter::setVectorNames(const QString& s) { d->vectorNames.clear(); if (!s.simplified().isEmpty()) d->vectorNames = s.simplified().split(' '); } QStringList JsonFilter::vectorNames() const { return d->vectorNames; } QVector JsonFilter::columnModes() { return d->columnModes; } void JsonFilter::setStartRow(const int r) { d->startRow = r; } int JsonFilter::startRow() const { return d->startRow; } void JsonFilter::setEndRow(const int r) { d->endRow = r; } int JsonFilter::endRow() const { return d->endRow; } void JsonFilter::setStartColumn(const int c) { d->startColumn = c; } int JsonFilter::startColumn() const { return d->startColumn; } void JsonFilter::setEndColumn(const int c) { d->endColumn = c; } int JsonFilter::endColumn() const { return d->endColumn; } //##################################################################### //################### Private implementation ########################## //##################################################################### JsonFilterPrivate::JsonFilterPrivate(JsonFilter* owner) : q(owner), model(new QJsonModel()), containerType(JsonFilter::Object), rowType(QJsonValue::Object), numberFormat(QLocale::C), createIndexEnabled(false), vectorNames(), startRow(1), endRow(-1), startColumn(1), endColumn(-1), m_actualRows(0), m_actualCols(0), m_prepared(false), m_columnOffset(0) { } //TODO: delete model from memory /*! returns 1 if row is invalid and 0 otherwise. */ int JsonFilterPrivate::checkRow(QJsonValueRef value, int& countCols) { switch(rowType){ //TODO: implement other value types case QJsonValue::Array: { QJsonArray row = value.toArray(); if(row.isEmpty()) return 1; countCols = (countCols == -1 || countCols > row.count()) ? row.count() : countCols; break; } case QJsonValue::Object: { QJsonObject row = value.toObject(); if(row.isEmpty()) return 1; countCols = (countCols == -1 || countCols > row.count()) ? row.count() : countCols; break; } case QJsonValue::Double: case QJsonValue::String: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: return 1; } return 0; } /*! returns -1 if a parse error has occurred, 1 if the current row type not supported and 0 otherwise. */ int JsonFilterPrivate::parseColumnModes(QJsonValue row) { columnModes.resize(m_actualCols); int colIndexInContainer = startColumn - 1; for(int i = 0; i < m_actualCols; i++){ if(createIndexEnabled && i == 0){ columnModes[i] = AbstractColumn::Integer; continue; } QJsonValue columnValue; switch (rowType) { //TODO: implement other value types case QJsonValue::Array: { QJsonArray arr = row.toArray(); if(arr.count() < colIndexInContainer + 1) return -1; columnValue = *(row.toArray().begin() + colIndexInContainer); break; } case QJsonValue::Object: { QJsonObject obj = row.toObject(); if(obj.count() < colIndexInContainer + 1) return -1; if(vectorNames.count() == 0) vectorNames = row.toObject().keys(); columnValue = *(row.toObject().begin() + colIndexInContainer); break; } case QJsonValue::Double: case QJsonValue::String: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: return 1; } switch (columnValue.type()) { case QJsonValue::Double: columnModes[i] = AbstractColumn::Numeric; break; case QJsonValue::String: columnModes[i] = AbstractFileFilter::columnMode(columnValue.toString(), dateTimeFormat, numberFormat); break; case QJsonValue::Array: case QJsonValue::Object: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: return -1; } colIndexInContainer++; } if(createIndexEnabled) vectorNames.prepend("index"); return 0; } void JsonFilterPrivate::setEmptyValue(int column, int row){ switch (columnModes[column]) { case AbstractColumn::Numeric: static_cast*>(m_dataContainer[column])->operator[](row) = nanValue; break; case AbstractColumn::Integer: static_cast*>(m_dataContainer[column])->operator[](row) = 0; break; case AbstractColumn::DateTime: static_cast*>(m_dataContainer[column])->operator[](row) = QDateTime(); break; case AbstractColumn::Text: static_cast*>(m_dataContainer[column])->operator[](row) = ""; break; case AbstractColumn::Month: case AbstractColumn::Day: break; } } void JsonFilterPrivate::setValueFromString(int column, int row, QString valueString){ QLocale locale(numberFormat); switch (columnModes[column]) { case AbstractColumn::Numeric: { bool isNumber; const double value = locale.toDouble(valueString, &isNumber); static_cast*>(m_dataContainer[column])->operator[](row) = isNumber ? value : nanValue; break; } case AbstractColumn::Integer: { bool isNumber; const int value = locale.toInt(valueString, &isNumber); static_cast*>(m_dataContainer[column])->operator[](row) = isNumber ? value : 0; break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); static_cast*>(m_dataContainer[column])->operator[](row) = valueDateTime.isValid() ? valueDateTime : QDateTime(); break; } case AbstractColumn::Text: static_cast*>(m_dataContainer[column])->operator[](row) = valueString; break; case AbstractColumn::Month: case AbstractColumn::Day: break; } } /*! returns -1 if the device couldn't be opened, 1 if the current read position in the device is at the end */ int JsonFilterPrivate::prepareDeviceToRead(QIODevice& device) { DEBUG("device is sequential = " << device.isSequential()); if (!device.open(QIODevice::ReadOnly)) return -1; if (device.atEnd() && !device.isSequential()) // empty file return 1; QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(device.readAll(), &err); if(err.error != QJsonParseError::NoError || doc.isEmpty()) return 1; if(prepareDocumentToRead(doc) != 0) return 2; // reset to start of file if (!device.isSequential()) device.seek(0); return 0; } /*! returns 2 if a parse error has occurred and 0 otherwise. */ int JsonFilterPrivate::prepareDocumentToRead(const QJsonDocument& doc) { model->loadJson(doc); if(modelRows.isEmpty()) m_preparedDoc = doc; else { QModelIndex index; for(auto it = modelRows.begin(); it != modelRows.end(); ++it){ index = model->index(*it, 0, index); } m_preparedDoc = model->genJsonByIndex(index); } if(!m_preparedDoc.isEmpty()){ if(m_preparedDoc.isArray()) containerType = JsonFilter::Array; else if(m_preparedDoc.isObject()) containerType = JsonFilter::Object; else return 2; } else return 2; int countRows = 0; int countCols = -1; QJsonValue firstRow; switch(containerType) { case JsonFilter::Array: { QJsonArray arr = m_preparedDoc.array(); if(arr.count() < startRow) return 2; int endRowOffset = (endRow == -1 || endRow > arr.count()) ? arr.count() : endRow; firstRow = *(arr.begin() + (startRow - 1)); for(QJsonArray::iterator it = arr.begin() + (startRow - 1); it != arr.begin() + endRowOffset; ++it) { if(checkRow(*it, countCols) != 0) return 2; countRows++; } break; } case JsonFilter::Object: { QJsonObject obj = m_preparedDoc.object(); if(obj.count() < startRow) return 2; int startRowOffset = startRow - 1; int endRowOffset = (endRow == -1 || endRow > obj.count()) ? obj.count() : endRow; firstRow = *(obj.begin() + startRowOffset); for(QJsonObject::iterator it = obj.begin() + startRowOffset; it != obj.begin() + endRowOffset; ++it) { if(checkRow(*it, countCols) != 0) return 2; countRows++; } break; } } if(endColumn == -1 || endColumn > countCols) endColumn = countCols; m_actualRows = countRows; m_actualCols = endColumn - startColumn + 1 + createIndexEnabled; if(parseColumnModes(firstRow) != 0) return 2; DEBUG("start/end column: = " << startColumn << ' ' << endColumn); DEBUG("start/end rows = " << startRow << ' ' << endRow); DEBUG("actual cols/rows = " << m_actualCols << ' ' << m_actualRows); return 0; } /*! reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. */ void JsonFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { KFilterDev device(fileName); readDataFromDevice(device, dataSource, importMode, lines); } /*! reads the content of device \c device to the data source \c dataSource. Uses the settings defined in the data source. */ void JsonFilterPrivate::readDataFromDevice(QIODevice& device, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { if(!m_prepared) { const int deviceError = prepareDeviceToRead(device); if(deviceError != 0){ DEBUG("Device error = " << deviceError); return; } //TODO: support other modes and vector names m_prepared = true; } importData(dataSource, importMode, lines); } /*! reads the content of document \c doc to the data source \c dataSource. Uses the settings defined in the data source. */ void JsonFilterPrivate::readDataFromDocument(const QJsonDocument& doc, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { if(!m_prepared) { const int docError = prepareDocumentToRead(doc); if(docError != 0){ DEBUG("Document parse error = " << docError); return; } //TODO: support other modes and vector names m_prepared = true; } importData(dataSource, importMode, lines); } /*! import the content of document \c m_preparedDoc to the data source \c dataSource. Uses the settings defined in the data source. */ void JsonFilterPrivate::importData(AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { Q_UNUSED(lines) m_columnOffset = dataSource->prepareImport(m_dataContainer, importMode, m_actualRows, m_actualCols, vectorNames, columnModes); int rowOffset = startRow - 1; DEBUG("reading " << m_actualRows << " lines"); for(int i = 0; i < m_actualRows; ++i) { QJsonValue row; switch (containerType) { case JsonFilter::Array: row = *(m_preparedDoc.array().begin() + rowOffset + i); break; case JsonFilter::Object: row = *(m_preparedDoc.object().begin() + rowOffset + i); break; } int colIndex = 0; for(int n = 0; n < m_actualCols; ++n) { if(createIndexEnabled && n == 0) { static_cast*>(m_dataContainer[n])->operator[](i) = i + 1; continue; } QJsonValue value; switch(rowType){ //TODO: implement other value types case QJsonValue::Array: { value = *(row.toArray().begin() + colIndex); break; } case QJsonValue::Object: { value = *(row.toObject().begin() + colIndex); break; } case QJsonValue::Double: case QJsonValue::String: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: break; } switch(value.type()) { case QJsonValue::Double: if(columnModes[n] == AbstractColumn::Numeric) static_cast*>(m_dataContainer[n])->operator[](i) = value.toDouble(); else setEmptyValue(n, i + startRow - 1); break; case QJsonValue::String: setValueFromString(n, i, value.toString()); break; case QJsonValue::Array: case QJsonValue::Object: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: setEmptyValue(n, i + startRow - 1); break; } colIndex++; } emit q->completed(100 * i/m_actualRows); } //TODO: fix (startColumn + m_actualCols - 1) dataSource->finalizeImport(m_columnOffset, startColumn, startColumn + m_actualCols - 1, dateTimeFormat, importMode); } /*! generates the preview for the file \c fileName. */ QVector JsonFilterPrivate::preview(const QString& fileName) { KFilterDev device(fileName); return preview(device); } /*! generates the preview for device \c device. */ QVector JsonFilterPrivate::preview(QIODevice &device) { const int deviceError = prepareDeviceToRead(device); if (deviceError != 0) { DEBUG("Device error = " << deviceError); return QVector(); } return preview(); } /*! generates the preview for document \c doc. */ QVector JsonFilterPrivate::preview(QJsonDocument &doc) { if(prepareDocumentToRead(doc) != 0) return QVector(); return preview(); } /*! generates the preview for document \c m_preparedDoc. */ QVector JsonFilterPrivate::preview() { QVector dataStrings; int rowOffset = startRow - 1; DEBUG("reading " << m_actualRows << " lines"); for(int i = 0; i < m_actualRows; ++i) { QJsonValue row; switch (containerType) { case JsonFilter::Object: row = *(m_preparedDoc.object().begin() + rowOffset + i); break; case JsonFilter::Array: row = *(m_preparedDoc.array().begin() + rowOffset + i); break; } QStringList lineString; int colIndex = 0; for(int n = 0; n < m_actualCols; ++n) { if(createIndexEnabled && n == 0) { lineString += QString::number(i + 1); continue; } QJsonValue value; switch(rowType){ case QJsonValue::Object: { value = *(row.toObject().begin() + colIndex); break; } case QJsonValue::Array: { value = *(row.toArray().begin() + colIndex); break; } //TODO: implement other value types case QJsonValue::Double: case QJsonValue::String: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: break; } switch(value.type()) { case QJsonValue::Double: if(columnModes[n] == AbstractColumn::Numeric) lineString += QString::number(value.toDouble(), 'g', 16); else lineString += lineString += QLatin1String(""); break; case QJsonValue::String: { //TODO: add parsing string before appending lineString += value.toString(); break; } case QJsonValue::Array: case QJsonValue::Object: case QJsonValue::Bool: case QJsonValue::Null: case QJsonValue::Undefined: lineString += QLatin1String(""); break; } colIndex++; } dataStrings << lineString; emit q->completed(100 * i/m_actualRows); } return dataStrings; } /*! writes the content of \c dataSource to the file \c fileName. */ void JsonFilterPrivate::write(const QString& fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO: save data to json file } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void JsonFilter::save(QXmlStreamWriter* writer) const { writer->writeStartElement("jsonFilter"); writer->writeAttribute("rowType", QString::number(d->rowType)); writer->writeAttribute("dateTimeFormat", d->dateTimeFormat); writer->writeAttribute("numberFormat", QString::number(d->numberFormat)); writer->writeAttribute("createIndex", QString::number(d->createIndexEnabled)); writer->writeAttribute("nanValue", QString::number(d->nanValue)); writer->writeAttribute("startRow", QString::number(d->startRow)); writer->writeAttribute("endRow", QString::number(d->endRow)); writer->writeAttribute("startColumn", QString::number(d->startColumn)); writer->writeAttribute("endColumn", QString::number(d->endColumn)); QStringList list; for(auto it = modelRows().begin(); it != modelRows().end(); ++it){ list.append(QString::number(*it)); } writer->writeAttribute("modelRows", list.join(';')); writer->writeEndElement(); DEBUG("JsonFilter save params"); } /*! Loads from XML. */ bool JsonFilter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "jsonFilter") { reader->raiseError(i18n("no json filter element found")); return false; } QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs = reader->attributes(); QString str = attribs.value("rowType").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'rowType'")); else d->rowType = static_cast(str.toInt()); str = attribs.value("dateTimeFormat").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'dateTimeFormat'")); else d->dateTimeFormat = str; str = attribs.value("numberFormat").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'numberFormat'")); else d->numberFormat = static_cast(str.toInt()); str = attribs.value("createIndex").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'createIndex'")); else d->createIndexEnabled = str.toInt(); str = attribs.value("nanValue").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'nanValue'")); else d->nanValue = str.toDouble(); str = attribs.value("startRow").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'startRow'")); else d->startRow = str.toInt(); str = attribs.value("endRow").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'endRow'")); else d->endRow = str.toInt(); str = attribs.value("startColumn").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'startColumn'")); else d->startColumn = str.toInt(); str = attribs.value("endColumn").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'endColumn'")); else d->endColumn = str.toInt(); QStringList list = attribs.value("modelRows").toString().split(';'); if (list.isEmpty()) reader->raiseWarning(attributeWarning.arg("'modelRows'")); else{ d->modelRows = QVector(); for(auto it = list.begin(); it !=list.end(); ++it) d->modelRows.append((*it).toInt()); } DEBUG("JsonFilter load params"); return true; } \ No newline at end of file diff --git a/src/backend/datasources/filters/NetCDFFilter.cpp b/src/backend/datasources/filters/NetCDFFilter.cpp index cbcdc904f..18dd88aff 100644 --- a/src/backend/datasources/filters/NetCDFFilter.cpp +++ b/src/backend/datasources/filters/NetCDFFilter.cpp @@ -1,695 +1,695 @@ /*************************************************************************** File : NetCDFFilter.cpp Project : LabPlot Description : NetCDF I/O-filter -------------------------------------------------------------------- Copyright : (C) 2015-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 "backend/datasources/filters/NetCDFFilter.h" #include "backend/datasources/filters/NetCDFFilterPrivate.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/core/column/Column.h" -#include +#include #include /*! \class NetCDFFilter \brief Manages the import/export of data from/to a NetCDF file. \ingroup datasources */ NetCDFFilter::NetCDFFilter():AbstractFileFilter(), d(new NetCDFFilterPrivate(this)) {} NetCDFFilter::~NetCDFFilter() {} /*! parses the content of the file \c ileName. */ void NetCDFFilter::parse(const QString & fileName, QTreeWidgetItem* rootItem) { d->parse(fileName, rootItem); } /*! reads the content of the selected attribute from file \c fileName. */ QString NetCDFFilter::readAttribute(const QString & fileName, const QString & name, const QString & varName) { return d->readAttribute(fileName, name, varName); } /*! reads the content of the current variable from file \c fileName. */ QVector NetCDFFilter::readCurrentVar(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode, int lines) { return d->readCurrentVar(fileName, dataSource, importMode, lines); } /*! reads the content of the file \c fileName to the data source \c dataSource. */ QVector NetCDFFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { return d->readDataFromFile(fileName, dataSource, mode, lines); } /*! writes the content of the data source \c dataSource to the file \c fileName. */ void NetCDFFilter::write(const QString & fileName, AbstractDataSource* dataSource) { d->write(fileName, dataSource); // emit() } /////////////////////////////////////////////////////////////////////// /*! loads the predefined filter settings for \c filterName */ void NetCDFFilter::loadFilterSettings(const QString& filterName) { Q_UNUSED(filterName); } /*! saves the current settings as a new filter with the name \c filterName */ void NetCDFFilter::saveFilterSettings(const QString& filterName) const { Q_UNUSED(filterName); } /////////////////////////////////////////////////////////////////////// void NetCDFFilter::setCurrentVarName(const QString& ds) { d->currentVarName = ds; } const QString NetCDFFilter::currentVarName() const { return d->currentVarName; } void NetCDFFilter::setStartRow(const int s) { d->startRow = s; } int NetCDFFilter::startRow() const { return d->startRow; } void NetCDFFilter::setEndRow(const int e) { d->endRow = e; } int NetCDFFilter::endRow() const { return d->endRow; } void NetCDFFilter::setStartColumn(const int c) { d->startColumn=c; } int NetCDFFilter::startColumn() const { return d->startColumn; } void NetCDFFilter::setEndColumn(const int c) { d->endColumn = c; } int NetCDFFilter::endColumn() const { return d->endColumn; } //##################################################################### //################### Private implementation ########################## //##################################################################### NetCDFFilterPrivate::NetCDFFilterPrivate(NetCDFFilter* owner) : q(owner), startRow(1), endRow(-1), startColumn(1), endColumn(-1) { #ifdef HAVE_NETCDF m_status = 0; #endif } #ifdef HAVE_NETCDF void NetCDFFilterPrivate::handleError(int err, const QString& function) { if (err != NC_NOERR) qDebug() << "NETCDF ERROR:" << function << "() - " << nc_strerror(m_status); } QString NetCDFFilterPrivate::translateDataType(nc_type type) { QString typeString; switch (type) { case NC_BYTE: typeString = "BYTE"; break; case NC_UBYTE: typeString = "UBYTE"; break; case NC_CHAR: typeString = "CHAR"; break; case NC_SHORT: typeString = "SHORT"; break; case NC_USHORT: typeString = "USHORT"; break; case NC_INT: typeString = "INT"; break; case NC_UINT: typeString = "UINT"; break; case NC_INT64: typeString = "INT64"; break; case NC_UINT64: typeString = "UINT64"; break; case NC_FLOAT: typeString = "FLOAT"; break; case NC_DOUBLE: typeString = "DOUBLE"; break; case NC_STRING: typeString = "STRING"; break; default: typeString = "UNKNOWN"; } return typeString; } QString NetCDFFilterPrivate::scanAttrs(int ncid, int varid, int attid, QTreeWidgetItem* parentItem) { char name[NC_MAX_NAME + 1]; int nattr, nstart = 0; if (attid == -1) { m_status = nc_inq_varnatts(ncid, varid, &nattr); handleError(m_status, "nc_inq_varnatts"); } else { nstart = attid; nattr = attid+1; } nc_type type; size_t len; QStringList valueString; for (int i = nstart; i < nattr; i++) { valueString.clear(); m_status = nc_inq_attname(ncid, varid, i, name); handleError(m_status, "nc_inq_attname"); m_status = nc_inq_att(ncid, varid, name, &type, &len); handleError(m_status, "nc_inq_att"); QDEBUG(" attr" << i+1 << "name/type/len =" << name << translateDataType(type) << len); //read attribute switch (type) { case NC_BYTE: { signed char *value = (signed char *)malloc(len*sizeof(signed char)); m_status = nc_get_att_schar(ncid, varid, name, value); handleError(m_status, "nc_get_att_schar"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_UBYTE: { unsigned char *value = (unsigned char *)malloc(len*sizeof(unsigned char)); m_status = nc_get_att_uchar(ncid, varid, name, value); handleError(m_status, "nc_get_att_uchar"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_CHAR: { char *value = (char *)malloc((len+1)*sizeof(char)); m_status = nc_get_att_text(ncid, varid, name, value); handleError(m_status, "nc_get_att_text"); value[len] = 0; valueString << value; free(value); break; } case NC_SHORT: { short *value = (short *)malloc(len*sizeof(short)); m_status = nc_get_att_short(ncid, varid, name, value); handleError(m_status, "nc_get_att_short"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_USHORT: { unsigned short *value = (unsigned short *)malloc(len*sizeof(unsigned short)); m_status = nc_get_att_ushort(ncid, varid, name, value); handleError(m_status, "nc_get_att_ushort"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_INT: { int *value = (int *)malloc(len*sizeof(int)); m_status = nc_get_att_int(ncid, varid, name, value); handleError(m_status, "nc_get_att_int"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_UINT: { unsigned int *value = (unsigned int *)malloc(len*sizeof(unsigned int)); m_status = nc_get_att_uint(ncid, varid, name, value); handleError(m_status, "nc_get_att_uint"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_INT64: { long long *value = (long long *)malloc(len*sizeof(long long)); m_status = nc_get_att_longlong(ncid, varid, name, value); handleError(m_status, "nc_get_att_longlong"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_UINT64: { unsigned long long *value = (unsigned long long *)malloc(len*sizeof(unsigned long long)); m_status = nc_get_att_ulonglong(ncid, varid, name, value); handleError(m_status, "nc_get_att_ulonglong"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_FLOAT: { float *value = (float *)malloc(len*sizeof(float)); m_status = nc_get_att_float(ncid, varid, name, value); handleError(m_status, "nc_get_att_float"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } case NC_DOUBLE: { double *value = (double *)malloc(len*sizeof(double)); m_status = nc_get_att_double(ncid, varid, name, value); handleError(m_status, "nc_get_att_double"); for (unsigned int l = 0; l < len; l++) valueString << QString::number(value[l]); free(value); break; } default: valueString << "not supported"; } if (parentItem != NULL) { QString typeName; if (varid == NC_GLOBAL) typeName = i18n("global attribute"); else { char varName[NC_MAX_NAME + 1]; m_status = nc_inq_varname(ncid, varid, varName); - typeName = QString(varName) + ' ' + i18n("attribute"); + typeName = i18n("%1 attribute", QString(varName)); } QStringList props; props << translateDataType(type) << " (" << QString::number(len) << ")"; QTreeWidgetItem *attrItem = new QTreeWidgetItem(QStringList() << QString(name) << typeName << props.join("") << valueString.join(", ")); attrItem->setIcon(0, QIcon::fromTheme("accessories-calculator")); attrItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(attrItem); } } return valueString.join("\n"); } void NetCDFFilterPrivate::scanDims(int ncid, int ndims, QTreeWidgetItem* parentItem) { int ulid; m_status = nc_inq_unlimdim(ncid, &ulid); handleError(m_status, "nc_inq_att"); char name[NC_MAX_NAME + 1]; size_t len; for (int i = 0; i < ndims; i++) { m_status = nc_inq_dim(ncid, i, name, &len); handleError(m_status, "nc_inq_att"); DEBUG(" dim" << i+1 << ": name/len =" << name << len); QStringList props; props<setIcon(0, QIcon::fromTheme("accessories-calculator")); attrItem->setFlags(Qt::ItemIsEnabled); parentItem->addChild(attrItem); } } void NetCDFFilterPrivate::scanVars(int ncid, int nvars, QTreeWidgetItem* parentItem) { char name[NC_MAX_NAME + 1]; nc_type type; int ndims, nattrs; int dimids[NC_MAX_VAR_DIMS]; for (int i = 0; i < nvars; i++) { m_status = nc_inq_var(ncid, i, name, &type, &ndims, dimids, &nattrs); handleError(m_status, "nc_inq_att"); QDEBUG(" var" << i+1 << ": name/type=" << name << translateDataType(type)); DEBUG(" ndims/nattr" << ndims << nattrs); QStringList props; props << translateDataType(type); char dname[NC_MAX_NAME + 1]; size_t dlen; props << "("; for (int j = 0; j < ndims; j++) { m_status = nc_inq_dim(ncid, dimids[j], dname, &dlen); if (j != 0) props << "x"; props << QString::number(dlen); } props << ")"; QTreeWidgetItem *varItem = new QTreeWidgetItem(QStringList() << QString(name) << i18n("variable") << props.join("")<<""); varItem->setIcon(0, QIcon::fromTheme("x-office-spreadsheet")); varItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // highlight item for (int c = 0; c < varItem->columnCount(); c++) { varItem->setBackground(c, QColor(192, 255, 192)); varItem->setForeground(c, Qt::black); } parentItem->addChild(varItem); scanAttrs(ncid, i, -1, varItem); } } #endif /*! parses the content of the file \c fileName and fill the tree using rootItem. */ void NetCDFFilterPrivate::parse(const QString & fileName, QTreeWidgetItem* rootItem) { #ifdef HAVE_NETCDF QByteArray bafileName = fileName.toLatin1(); int ncid; m_status = nc_open(bafileName.data(), NC_NOWRITE, &ncid); handleError(m_status, "nc_open"); int ndims, nvars, nattr, uldid; m_status = nc_inq(ncid, &ndims, &nvars, &nattr, &uldid); handleError(m_status, "nc_inq"); DEBUG(" nattr/ndims/nvars =" << nattr << ndims << nvars); QTreeWidgetItem *attrItem = new QTreeWidgetItem(QStringList() << QString(i18n("Attributes"))); attrItem->setIcon(0,QIcon::fromTheme("folder")); attrItem->setFlags(Qt::ItemIsEnabled); rootItem->addChild(attrItem); scanAttrs(ncid, NC_GLOBAL, -1, attrItem); QTreeWidgetItem *dimItem = new QTreeWidgetItem(QStringList() << QString(i18n("Dimensions"))); dimItem->setIcon(0, QIcon::fromTheme("folder")); dimItem->setFlags(Qt::ItemIsEnabled); rootItem->addChild(dimItem); scanDims(ncid, ndims, dimItem); QTreeWidgetItem *varItem = new QTreeWidgetItem(QStringList() << QString(i18n("Variables"))); varItem->setIcon(0, QIcon::fromTheme("folder")); varItem->setFlags(Qt::ItemIsEnabled); rootItem->addChild(varItem); scanVars(ncid, nvars, varItem); #else Q_UNUSED(fileName) Q_UNUSED(rootItem) #endif } QString NetCDFFilterPrivate::readAttribute(const QString & fileName, const QString & name, const QString & varName) { #ifdef HAVE_NETCDF int ncid; QByteArray bafileName = fileName.toLatin1(); m_status = nc_open(bafileName.data(), NC_NOWRITE, &ncid); handleError(m_status, "nc_open"); // get varid int varid; if (varName == "global") varid = NC_GLOBAL; else { QByteArray bavarName = varName.toLatin1(); m_status = nc_inq_varid(ncid, bavarName.data(), &varid); handleError(m_status, "nc_inq_varid"); } // attribute 'name' int attid; QByteArray baName = name.toLatin1(); m_status = nc_inq_attid(ncid, varid, baName.data(), &attid); handleError(m_status, "nc_inq_attid"); return scanAttrs(ncid, varid, attid); #else Q_UNUSED(fileName) Q_UNUSED(name) Q_UNUSED(varName) return QString(); #endif } /*! reads the content of the variable in the file \c fileName to a string (for preview) or to the data source. */ QVector NetCDFFilterPrivate::readCurrentVar(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { QVector dataStrings; if (currentVarName.isEmpty()) return dataStrings << (QStringList() << i18n("No variable selected")); QDEBUG(" current variable =" << currentVarName); #ifdef HAVE_NETCDF int ncid; QByteArray bafileName = fileName.toLatin1(); m_status = nc_open(bafileName.data(), NC_NOWRITE, &ncid); handleError(m_status, "nc_open"); int varid; QByteArray baVarName = currentVarName.toLatin1(); m_status = nc_inq_varid(ncid, baVarName.data(), &varid); handleError(m_status, "nc_inq_varid"); int ndims; nc_type type; m_status = nc_inq_varndims(ncid, varid, &ndims); handleError(m_status, "nc_inq_varndims"); m_status = nc_inq_vartype(ncid, varid, &type); handleError(m_status, "nc_inq_type"); int* dimids = (int *) malloc(ndims * sizeof(int)); m_status = nc_inq_vardimid(ncid, varid, dimids); handleError(m_status, "nc_inq_vardimid"); int actualRows = 0, actualCols = 0; int columnOffset = 0; QVector dataContainer; switch (ndims) { case 0: dataStrings << (QStringList() << i18n("zero dimensions")); qDebug() << dataStrings; break; case 1: { size_t size; m_status = nc_inq_dimlen(ncid, dimids[0], &size); handleError(m_status, "nc_inq_dimlen"); if (endRow == -1) endRow = (int)size; if (lines == -1) lines = endRow; actualRows = endRow-startRow+1; actualCols = 1; DEBUG("start/end row" << startRow << endRow); DEBUG("act rows/cols" << actualRows << actualCols); //TODO: support other modes QVector columnModes; columnModes.resize(actualCols); //TODO: use given names? QStringList vectorNames; if (dataSource) columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes); double* data = nullptr; if (dataSource) data = static_cast*>(dataContainer[0])->data(); else data = new double[(unsigned int)actualRows]; size_t start = (size_t)(startRow-1), count = (size_t)actualRows; m_status = nc_get_vara_double(ncid, varid, &start, &count, data); handleError(m_status, "nc_get_vara_double"); if (!dataSource) { for (int i = 0; i < qMin(actualRows, lines); i++) dataStrings << (QStringList() << QString::number(data[i])); delete[] data; } break; } case 2: { size_t rows, cols; m_status = nc_inq_dimlen(ncid, dimids[0], &rows); handleError(m_status, "nc_inq_dimlen"); m_status = nc_inq_dimlen(ncid, dimids[1], &cols); handleError(m_status, "nc_inq_dimlen"); if (endRow == -1) endRow = (int)rows; if (lines == -1) lines = endRow; if (endColumn == -1) endColumn = (int)cols; actualRows = endRow-startRow+1; actualCols = endColumn-startColumn+1; DEBUG("dim =" << rows << "x" << cols); DEBUG("startRow/endRow:" << startRow << endRow); DEBUG("startColumn/endColumn:" << startColumn << endColumn); DEBUG("actual rows/cols:" << actualRows << actualCols); DEBUG("lines:" << lines); //TODO: support other modes QVector columnModes; columnModes.resize(actualCols); //TODO: use given names? QStringList vectorNames; if (dataSource) columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes); double** data = (double**) malloc(rows * sizeof(double*)); data[0] = (double*)malloc( cols * rows * sizeof(double) ); for (unsigned int i = 1; i < rows; i++) data[i] = data[0] + i*cols; m_status = nc_get_var_double(ncid, varid, &data[0][0]); handleError(m_status, "nc_get_var_double"); for (int i = 0; i < qMin((int)rows, lines); i++) { QStringList line; for (size_t j = 0; j < cols; j++) { if (dataContainer[0]) static_cast*>(dataContainer[(int)(j-(size_t)startColumn+1)])->operator[](i-startRow+1) = data[i][(int)j]; else line << QString::number(data[i][j]); } dataStrings << line; emit q->completed(100*i/actualRows); } free(data[0]); free(data); break; } default: - dataStrings << (QStringList() << i18n("%1 dimensional data of type %2 not supported yet").arg(ndims).arg(translateDataType(type))); + dataStrings << (QStringList() << i18n("%1 dimensional data of type %2 not supported yet", ndims, translateDataType(type))); qDebug() << dataStrings; } free(dimids); if (dataSource) dataSource->finalizeImport(columnOffset, 1, actualCols, "", mode); #else Q_UNUSED(fileName) Q_UNUSED(dataSource) Q_UNUSED(mode) Q_UNUSED(lines) #endif return dataStrings; } /*! reads the content of the current selected variable from file \c fileName to the data source \c dataSource. Uses the settings defined in the data source. */ QVector NetCDFFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode, int lines) { Q_UNUSED(lines); QVector dataStrings; if (currentVarName.isEmpty()) { DEBUG(" No variable selected"); return dataStrings; } DEBUG(" current variable =" << currentVarName.toStdString()); return readCurrentVar(fileName, dataSource, mode); } /*! writes the content of \c dataSource to the file \c fileName. */ void NetCDFFilterPrivate::write(const QString & fileName, AbstractDataSource* dataSource) { Q_UNUSED(fileName); Q_UNUSED(dataSource); //TODO: not implemented yet } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void NetCDFFilter::save(QXmlStreamWriter* writer) const { writer->writeStartElement("netcdfFilter"); writer->writeEndElement(); } /*! Loads from XML. */ bool NetCDFFilter::load(XmlStreamReader* reader) { if (!reader->isStartElement() || reader->name() != "netcdfFilter") { reader->raiseError(i18n("no netcdf filter element found")); return false; } -// QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); +// KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); // QXmlStreamAttributes attribs = reader->attributes(); return true; } diff --git a/src/backend/datasources/projects/OriginProjectParser.cpp b/src/backend/datasources/projects/OriginProjectParser.cpp index aa52b306f..df31ba160 100644 --- a/src/backend/datasources/projects/OriginProjectParser.cpp +++ b/src/backend/datasources/projects/OriginProjectParser.cpp @@ -1,2037 +1,2155 @@ /*************************************************************************** File : OriginProjectParser.h Project : LabPlot Description : parser for Origin projects -------------------------------------------------------------------- Copyright : (C) 2017-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017-2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/datasources/projects/OriginProjectParser.h" #include "backend/core/column/Column.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "backend/core/Project.h" #include "backend/core/Workbook.h" #include "backend/matrix/Matrix.h" #include "backend/note/Note.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.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/XYEquationCurve.h" #include "backend/worksheet/TextLabel.h" #include -#include +#include #include #include #include /*! \class OriginProjectParser \brief parser for Origin projects. \ingroup datasources */ OriginProjectParser::OriginProjectParser() : ProjectParser(), m_originFile(nullptr), m_importUnusedObjects(false) { m_topLevelClasses << "Folder" << "Workbook" << "Spreadsheet" << "Matrix" << "Worksheet" << "Note"; } bool OriginProjectParser::isOriginProject(const QString& fileName) { //TODO add opju later when liborigin supports it return fileName.endsWith(QLatin1String(".opj"), Qt::CaseInsensitive); } void OriginProjectParser::setImportUnusedObjects(bool importUnusedObjects) { m_importUnusedObjects = importUnusedObjects; } bool OriginProjectParser::hasUnusedObjects() { if (m_originFile) delete m_originFile; m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit()); if (!m_originFile->parse()) return false; for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { const Origin::SpreadSheet& spread = m_originFile->spread(i); if (spread.objectID < 0) return true; } for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { const Origin::Excel& excel = m_originFile->excel(i); if (excel.objectID < 0) return true; } for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { const Origin::Matrix& originMatrix = m_originFile->matrix(i); if (originMatrix.objectID < 0) return true; } return false; } QString OriginProjectParser::supportedExtensions() { //TODO add opju later when liborigin supports it static const QString extensions = "*.opj *.OPJ"; return extensions; } unsigned int OriginProjectParser::findSpreadByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { const Origin::SpreadSheet& spread = m_originFile->spread(i); if (spread.name == name.toStdString()) { m_spreadNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findMatrixByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { const Origin::Matrix& originMatrix = m_originFile->matrix(i); if (originMatrix.name == name.toStdString()) { m_matrixNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findExcelByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { const Origin::Excel& excel = m_originFile->excel(i); if (excel.name == name.toStdString()) { m_excelNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findGraphByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->graphCount(); i++) { const Origin::Graph& graph = m_originFile->graph(i); if (graph.name == name.toStdString()) { m_graphNameList << name; return i; } } return 0; } unsigned int OriginProjectParser::findNoteByName(const QString& name) { for (unsigned int i = 0; i < m_originFile->noteCount(); i++) { const Origin::Note& originNote = m_originFile->note(i); if (originNote.name == name.toStdString()) { m_noteNameList << name; return i; } } return 0; } //############################################################################## //############## Deserialization from Origin's project tree #################### //############################################################################## bool OriginProjectParser::load(Project* project, bool preview) { DEBUG("OriginProjectParser::load()"); //read and parse the m_originFile-file if (m_originFile) delete m_originFile; m_originFile = new OriginFile((const char*)m_projectFileName.toLocal8Bit()); if (!m_originFile->parse()) return false; //Origin project tree and the iterator pointing to the root node const tree* projectTree = m_originFile->project(); tree::iterator projectIt = projectTree->begin(projectTree->begin()); m_spreadNameList.clear(); m_excelNameList.clear(); m_matrixNameList.clear(); m_graphNameList.clear(); m_noteNameList.clear(); //convert the project tree from liborigin's representation to LabPlot's project object project->setIsLoading(true); if (projectIt.node) { // only opj files from version >= 6.0 do have project tree QString name(QString::fromLatin1(projectIt->name.c_str())); project->setName(name); project->setCreationTime(creationTime(projectIt)); loadFolder(project, projectIt, preview); } else { // for lower versions put all windows on rootfolder int pos = m_projectFileName.lastIndexOf(QDir::separator()) + 1; project->setName((const char*)m_projectFileName.mid(pos).toLocal8Bit()); } // imports all loose windows (like prior version 6 which has no project tree) handleLooseWindows(project, preview); //restore column pointers: //1. extend the pathes to contain the parent structures first //2. restore the pointers from the pathes const QVector columns = project->children(AbstractAspect::Recursive); const QVector spreadsheets = project->children(AbstractAspect::Recursive); for (auto* curve : project->children(AbstractAspect::Recursive)) { curve->suppressRetransform(true); //x-column QString spreadsheetName = curve->xColumnPath().left(curve->xColumnPath().indexOf(QLatin1Char('/'))); for (const auto* spreadsheet : spreadsheets) { if (spreadsheet->name() == spreadsheetName) { const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->xColumnPath(); curve->setXColumnPath(newPath); for (auto* column : columns) { if (!column) continue; if (column->path() == newPath) { curve->setXColumn(column); break; } } break; } } //x-column spreadsheetName = curve->yColumnPath().left(curve->yColumnPath().indexOf(QLatin1Char('/'))); for (const auto* spreadsheet : spreadsheets) { if (spreadsheet->name() == spreadsheetName) { const QString& newPath = spreadsheet->parentAspect()->path() + '/' + curve->yColumnPath(); curve->setYColumnPath(newPath); for (auto* column : columns) { if (!column) continue; if (column->path() == newPath) { curve->setYColumn(column); break; } } break; } } //TODO: error columns curve->suppressRetransform(false); } if (!preview) { - for (auto* plot : project->children(AbstractAspect::Recursive)) { + for (auto* plot : project->children(AbstractAspect::Recursive)) { plot->setIsLoading(false); plot->retransform(); } } emit project->loaded(); project->setIsLoading(false); return true; } bool OriginProjectParser::loadFolder(Folder* folder, tree::iterator baseIt, bool preview) { DEBUG("OriginProjectParser::loadFolder()\n--------------------------------"); const tree* projectTree = m_originFile->project(); //load folder's children: logic for reading the selected objects only is similar to Folder::readChildAspectElement for (tree::sibling_iterator it = projectTree->begin(baseIt); it != projectTree->end(baseIt); ++it) { QString name(QString::fromLatin1(it->name.c_str())); //name of the current child DEBUG(" * folder item name = " << name.toStdString()); //check whether we need to skip the loading of the current child if (!folder->pathesToLoad().isEmpty()) { //child's path is not available yet (child not added yet) -> construct the path manually const QString childPath = folder->path() + '/' + name; //skip the current child aspect it is not in the list of aspects to be loaded if (folder->pathesToLoad().indexOf(childPath) == -1) continue; } //load top-level children AbstractAspect* aspect = nullptr; switch (it->type) { case Origin::ProjectNode::Folder: { DEBUG(" top level folder"); Folder* f = new Folder(name); if (!folder->pathesToLoad().isEmpty()) { //a child folder to be read -> provide the list of aspects to be loaded to the child folder, too. //since the child folder and all its children are not added yet (path() returns empty string), //we need to remove the path of the current child folder from the full pathes provided in pathesToLoad. //E.g. we want to import the path "Project/Folder/Spreadsheet" in the following project // Project // \Spreadsheet // \Folder // \Spreadsheet // //Here, we remove the part "Project/Folder/" and proceed for this child folder with "Spreadsheet" only. //With this the logic above where it is determined whether to import the child aspect or not works out. //manually construct the path of the child folder to be read const QString& curFolderPath = folder->path() + '/' + name; //remove the path of the current child folder QStringList pathesToLoadNew; for (const auto& path : folder->pathesToLoad()) { if (path.startsWith(curFolderPath)) pathesToLoadNew << path.right(path.length() - curFolderPath.length()); } f->setPathesToLoad(pathesToLoadNew); } loadFolder(f, it, preview); aspect = f; break; } case Origin::ProjectNode::SpreadSheet: { DEBUG(" top level spreadsheet"); Spreadsheet* spreadsheet = new Spreadsheet(0, name); loadSpreadsheet(spreadsheet, preview, name); aspect = spreadsheet; break; } case Origin::ProjectNode::Graph: { DEBUG(" top level graph"); Worksheet* worksheet = new Worksheet(0, name); worksheet->setIsLoading(true); worksheet->setTheme(""); loadWorksheet(worksheet, preview); aspect = worksheet; break; } case Origin::ProjectNode::Matrix: { DEBUG(" top level matrix"); const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(name)); DEBUG(" matrix name = " << originMatrix.name); DEBUG(" number of sheets = " << originMatrix.sheets.size()); if (originMatrix.sheets.size() == 1) { // single sheet -> load into a matrix Matrix* matrix = new Matrix(0, name); loadMatrix(matrix, preview); aspect = matrix; } else { // multiple sheets -> load into a workbook Workbook* workbook = new Workbook(0, name); loadMatrixWorkbook(workbook, preview); aspect = workbook; } break; } case Origin::ProjectNode::Excel: { DEBUG(" top level excel"); Workbook* workbook = new Workbook(0, name); loadWorkbook(workbook, preview); aspect = workbook; break; } case Origin::ProjectNode::Note: { DEBUG("top level note"); Note* note = new Note(name); loadNote(note, preview); aspect = note; break; } case Origin::ProjectNode::Graph3D: default: //TODO: add UnsupportedAspect break; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(creationTime(it)); aspect->setIsLoading(false); } } // ResultsLog QString resultsLog = QString::fromLatin1(m_originFile->resultsLogString().c_str()); if (resultsLog.length() > 0) { DEBUG("Results log:\t\tyes"); Note* note = new Note("ResultsLog"); if (preview) folder->addChildFast(note); else { //only import the log if it is in the list of aspects to be loaded const QString childPath = folder->path() + '/' + note->name(); if (folder->pathesToLoad().indexOf(childPath) != -1) { note->setText(resultsLog); folder->addChildFast(note); } } } else DEBUG("Results log:\t\tno"); return folder; } void OriginProjectParser::handleLooseWindows(Folder* folder, bool preview) { DEBUG("OriginProjectParser::handleLooseWindows()"); QDEBUG("pathes to load:" << folder->pathesToLoad()); m_spreadNameList.removeDuplicates(); m_excelNameList.removeDuplicates(); m_matrixNameList.removeDuplicates(); m_graphNameList.removeDuplicates(); m_noteNameList.removeDuplicates(); QDEBUG(" spreads =" << m_spreadNameList); QDEBUG(" excels =" << m_excelNameList); QDEBUG(" matrices =" << m_matrixNameList); QDEBUG(" graphs =" << m_graphNameList); QDEBUG(" notes =" << m_noteNameList); DEBUG("Number of spreads loaded:\t" << m_spreadNameList.size() << ", in file: " << m_originFile->spreadCount()); DEBUG("Number of excels loaded:\t" << m_excelNameList.size() << ", in file: " << m_originFile->excelCount()); DEBUG("Number of matrices loaded:\t" << m_matrixNameList.size() << ", in file: " << m_originFile->matrixCount()); DEBUG("Number of graphs loaded:\t" << m_graphNameList.size() << ", in file: " << m_originFile->graphCount()); DEBUG("Number of notes loaded:\t\t" << m_noteNameList.size() << ", in file: " << m_originFile->noteCount()); // loop over all spreads to find loose ones for (unsigned int i = 0; i < m_originFile->spreadCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::SpreadSheet& spread = m_originFile->spread(i); QString name = QString::fromStdString(spread.name); DEBUG(" spread.objectId = " << spread.objectID); // skip unused spreads if selected if (spread.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose spread: " << name.toStdString()); continue; } const QString childPath = folder->path() + '/' + name; // we could also use spread.loose if (!m_spreadNameList.contains(name) && (preview || (!preview && folder->pathesToLoad().indexOf(childPath) != -1))) { DEBUG(" Adding loose spread: " << name.toStdString()); Spreadsheet* spreadsheet = new Spreadsheet(0, name); loadSpreadsheet(spreadsheet, preview, name); aspect = spreadsheet; } if (aspect) { folder->addChildFast(aspect); DEBUG(" creation time as reported by liborigin: " << spread.creationDate); aspect->setCreationTime(QDateTime::fromTime_t(spread.creationDate)); } } // loop over all excels to find loose ones for (unsigned int i = 0; i < m_originFile->excelCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Excel& excel = m_originFile->excel(i); QString name = QString::fromStdString(excel.name); DEBUG(" excel.objectId = " << excel.objectID); // skip unused data sets if selected if (excel.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose excel: " << name.toStdString()); continue; } const QString childPath = folder->path() + '/' + name; // we could also use excel.loose if (!m_excelNameList.contains(name) && (preview || (!preview && folder->pathesToLoad().indexOf(childPath) != -1))) { DEBUG(" Adding loose excel: " << name.toStdString()); DEBUG(" containing number of sheets = " << excel.sheets.size()); Workbook* workbook = new Workbook(0, name); loadWorkbook(workbook, preview); aspect = workbook; } if (aspect) { folder->addChildFast(aspect); DEBUG(" creation time as reported by liborigin: " << excel.creationDate); aspect->setCreationTime(QDateTime::fromTime_t(excel.creationDate)); } } // loop over all matrices to find loose ones for (unsigned int i = 0; i < m_originFile->matrixCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Matrix& originMatrix = m_originFile->matrix(i); QString name = QString::fromStdString(originMatrix.name); DEBUG(" originMatrix.objectId = " << originMatrix.objectID); // skip unused data sets if selected if (originMatrix.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose matrix: " << name.toStdString()); continue; } const QString childPath = folder->path() + '/' + name; if (!m_matrixNameList.contains(name) && (preview || (!preview && folder->pathesToLoad().indexOf(childPath) != -1))) { DEBUG(" Adding loose matrix: " << name.toStdString()); DEBUG(" containing number of sheets = " << originMatrix.sheets.size()); if (originMatrix.sheets.size() == 1) { // single sheet -> load into a matrix Matrix* matrix = new Matrix(0, name); loadMatrix(matrix, preview); aspect = matrix; } else { // multiple sheets -> load into a workbook Workbook* workbook = new Workbook(0, name); loadMatrixWorkbook(workbook, preview); aspect = workbook; } } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(originMatrix.creationDate)); } } // handle loose graphs (is this even possible?) for (unsigned int i = 0; i < m_originFile->graphCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Graph& graph = m_originFile->graph(i); QString name = QString::fromStdString(graph.name); DEBUG(" graph.objectId = " << graph.objectID); // skip unused graph if selected if (graph.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose graph: " << name.toStdString()); continue; } const QString childPath = folder->path() + '/' + name; if (!m_graphNameList.contains(name) && (preview || (!preview && folder->pathesToLoad().indexOf(childPath) != -1))) { DEBUG(" Adding loose graph: " << name.toStdString()); Worksheet* worksheet = new Worksheet(0, name); loadWorksheet(worksheet, preview); aspect = worksheet; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(graph.creationDate)); } } // handle loose notes (is this even possible?) for (unsigned int i = 0; i < m_originFile->noteCount(); i++) { AbstractAspect* aspect = nullptr; const Origin::Note& originNote = m_originFile->note(i); QString name = QString::fromStdString(originNote.name); DEBUG(" originNote.objectId = " << originNote.objectID); // skip unused notes if selected if (originNote.objectID < 0 && !m_importUnusedObjects) { DEBUG(" Dropping unused loose note: " << name.toStdString()); continue; } const QString childPath = folder->path() + '/' + name; if (!m_noteNameList.contains(name) && (preview || (!preview && folder->pathesToLoad().indexOf(childPath) != -1))) { DEBUG(" Adding loose note: " << name.toStdString()); Note* note = new Note(name); loadNote(note, preview); aspect = note; } if (aspect) { folder->addChildFast(aspect); aspect->setCreationTime(QDateTime::fromTime_t(originNote.creationDate)); } } } bool OriginProjectParser::loadWorkbook(Workbook* workbook, bool preview) { DEBUG("loadWorkbook()"); //load workbook sheets const Origin::Excel& excel = m_originFile->excel(findExcelByName(workbook->name())); DEBUG(" excel name = " << excel.name); DEBUG(" number of sheets = " << excel.sheets.size()); for (unsigned int s = 0; s < excel.sheets.size(); ++s) { Spreadsheet* spreadsheet = new Spreadsheet(0, QString::fromLatin1(excel.sheets[s].name.c_str())); loadSpreadsheet(spreadsheet, preview, workbook->name(), s); workbook->addChildFast(spreadsheet); } return true; } // load spreadsheet from spread (sheetIndex == -1) or from excel (only sheet sheetIndex) bool OriginProjectParser::loadSpreadsheet(Spreadsheet* spreadsheet, bool preview, const QString& name, int sheetIndex) { DEBUG("loadSpreadsheet() sheetIndex = " << sheetIndex); //load spreadsheet data Origin::SpreadSheet spread; Origin::Excel excel; if (sheetIndex == -1) // spread spread = m_originFile->spread(findSpreadByName(name)); else { excel = m_originFile->excel(findExcelByName(name)); spread = excel.sheets[sheetIndex]; } const size_t cols = spread.columns.size(); int rows = 0; for (size_t j = 0; j < cols; ++j) rows = std::max((int)spread.columns[j].data.size(), rows); // alternative: int rows = excel.maxRows; DEBUG("loadSpreadsheet() cols/maxRows = " << cols << "/" << rows); //TODO QLocale locale = mw->locale(); spreadsheet->setRowCount(rows); spreadsheet->setColumnCount((int)cols); if (sheetIndex == -1) spreadsheet->setComment(QString::fromLatin1(spread.label.c_str())); else spreadsheet->setComment(QString::fromLatin1(excel.label.c_str())); //in Origin column width is measured in characters, we need to convert to pixels //TODO: determine the font used in Origin in order to get the same column width as in Origin QFont font; QFontMetrics fm(font); const int scaling_factor = fm.maxWidth(); for (size_t j = 0; j < cols; ++j) { Origin::SpreadColumn column = spread.columns[j]; Column* col = spreadsheet->column((int)j); QString name(column.name.c_str()); col->setName(name.replace(QRegExp(".*_"),"")); if (preview) continue; //TODO: we don't support any formulas for cells yet. // if (column.command.size() > 0) // col->setFormula(Interval(0, rows), QString(column.command.c_str())); col->setComment(QString::fromLatin1(column.comment.c_str())); col->setWidth((int)column.width * scaling_factor); //plot designation switch (column.type) { case Origin::SpreadColumn::X: col->setPlotDesignation(AbstractColumn::X); break; case Origin::SpreadColumn::Y: col->setPlotDesignation(AbstractColumn::Y); break; case Origin::SpreadColumn::Z: col->setPlotDesignation(AbstractColumn::Z); break; case Origin::SpreadColumn::XErr: col->setPlotDesignation(AbstractColumn::XError); break; case Origin::SpreadColumn::YErr: col->setPlotDesignation(AbstractColumn::YError); break; case Origin::SpreadColumn::Label: case Origin::SpreadColumn::NONE: default: col->setPlotDesignation(AbstractColumn::NoDesignation); } QString format; switch(column.valueType) { case Origin::Numeric: { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const double value = column.data.at(i).as_double(); if (value != _ONAN) col->setValueAt(i, value); } loadColumnNumericFormat(column, col); break; } case Origin::TextNumeric: { //A TextNumeric column can contain numeric and string values, there is no equivalent column mode in LabPlot. // -> Set the column mode as 'Numeric' or 'Text' depending on the type of first non-empty element in column. for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const Origin::variant value(column.data.at(i)); if (value.type() == Origin::Variant::V_DOUBLE) { if (value.as_double() != _ONAN) break; } else { if (value.as_string() != NULL) { col->setColumnMode(AbstractColumn::Text); break; } } } if (col->columnMode() == AbstractColumn::Numeric) { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const double value = column.data.at(i).as_double(); if (column.data.at(i).type() == Origin::Variant::V_DOUBLE && value != _ONAN) col->setValueAt(i, value); } loadColumnNumericFormat(column, col); } else { for (unsigned int i = column.beginRow; i < column.endRow; ++i) { const Origin::variant value(column.data.at(i)); if (value.type() == Origin::Variant::V_STRING) { if (value.as_string() != NULL) col->setTextAt(i, value.as_string()); } else { if (value.as_double() != _ONAN) col->setTextAt(i, QString::number(value.as_double())); } } } break; } case Origin::Text: col->setColumnMode(AbstractColumn::Text); for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setTextAt(i, column.data[i].as_string()); break; case Origin::Time: { switch (column.valueTypeSpecification + 128) { case Origin::TIME_HH_MM: format="hh:mm"; break; case Origin::TIME_HH: format="hh"; break; case Origin::TIME_HH_MM_SS: format="hh:mm:ss"; break; case Origin::TIME_HH_MM_SS_ZZ: format="hh:mm:ss.zzz"; break; case Origin::TIME_HH_AP: format="hh ap"; break; case Origin::TIME_HH_MM_AP: format="hh:mm ap"; break; case Origin::TIME_MM_SS: format="mm:ss"; break; case Origin::TIME_MM_SS_ZZ: format="mm:ss.zzz"; break; case Origin::TIME_HHMM: format="hhmm"; break; case Origin::TIME_HHMMSS: format="hhmmss"; break; case Origin::TIME_HH_MM_SS_ZZZ: format="hh:mm:ss.zzz"; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::DateTime); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Date: { switch (column.valueTypeSpecification) { case Origin::DATE_DD_MM_YYYY: format="dd/MM/yyyy"; break; case Origin::DATE_DD_MM_YYYY_HH_MM: format="dd/MM/yyyy HH:mm"; break; case Origin::DATE_DD_MM_YYYY_HH_MM_SS: format="dd/MM/yyyy HH:mm:ss"; break; case Origin::DATE_DDMMYYYY: case Origin::DATE_DDMMYYYY_HH_MM: case Origin::DATE_DDMMYYYY_HH_MM_SS: format="dd.MM.yyyy"; break; case Origin::DATE_MMM_D: format="MMM d"; break; case Origin::DATE_M_D: format="M/d"; break; case Origin::DATE_D: format='d'; break; case Origin::DATE_DDD: case Origin::DATE_DAY_LETTER: format="ddd"; break; case Origin::DATE_YYYY: format="yyyy"; break; case Origin::DATE_YY: format="yy"; break; case Origin::DATE_YYMMDD: case Origin::DATE_YYMMDD_HH_MM: case Origin::DATE_YYMMDD_HH_MM_SS: case Origin::DATE_YYMMDD_HHMM: case Origin::DATE_YYMMDD_HHMMSS: format="yyMMdd"; break; case Origin::DATE_MMM: case Origin::DATE_MONTH_LETTER: format="MMM"; break; case Origin::DATE_M_D_YYYY: format="M-d-yyyy"; break; default: format="dd.MM.yyyy"; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::DateTime); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Month: { switch (column.valueTypeSpecification) { case Origin::MONTH_MMM: format = "MMM"; break; case Origin::MONTH_MMMM: format = "MMMM"; break; case Origin::MONTH_LETTER: format = 'M'; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::Month); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::Day: { switch (column.valueTypeSpecification) { case Origin::DAY_DDD: format = "ddd"; break; case Origin::DAY_DDDD: format = "dddd"; break; case Origin::DAY_LETTER: format = 'd'; break; } for (int i = 0; i < min((int)column.data.size(), rows); ++i) col->setValueAt(i, column.data[i].as_double()); col->setColumnMode(AbstractColumn::Day); DateTime2StringFilter *filter = static_cast(col->outputFilter()); filter->setFormat(format); break; } case Origin::ColumnHeading: case Origin::TickIndexedDataset: case Origin::Categorical: break; } } //TODO: "hidden" not supporrted yet // if (spread.hidden || spread.loose) // mw->hideWindow(spreadsheet); return true; } void OriginProjectParser::loadColumnNumericFormat(const Origin::SpreadColumn& originColumn, Column* column) const { if (originColumn.numericDisplayType != 0) { int fi = 0; switch (originColumn.valueTypeSpecification) { case Origin::Decimal: fi = 1; break; case Origin::Scientific: fi = 2; break; case Origin::Engineering: case Origin::DecimalWithMarks: break; } Double2StringFilter* filter = static_cast(column->outputFilter()); filter->setNumericFormat(fi); filter->setNumDigits(originColumn.decimalPlaces); } } bool OriginProjectParser::loadMatrixWorkbook(Workbook* workbook, bool preview) { DEBUG("loadMatrixWorkbook()"); //load matrix workbook sheets const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(workbook->name())); for (size_t s = 0; s < originMatrix.sheets.size(); ++s) { Matrix* matrix = new Matrix(0, QString::fromLatin1(originMatrix.sheets[s].name.c_str())); loadMatrix(matrix, preview, s, workbook->name()); workbook->addChildFast(matrix); } return true; } bool OriginProjectParser::loadMatrix(Matrix* matrix, bool preview, size_t sheetIndex, const QString& mwbName) { DEBUG("loadMatrix()"); //import matrix data const Origin::Matrix& originMatrix = m_originFile->matrix(findMatrixByName(mwbName)); if (preview) return true; //in Origin column width is measured in characters, we need to convert to pixels //TODO: determine the font used in Origin in order to get the same column width as in Origin QFont font; QFontMetrics fm(font); const int scaling_factor = fm.maxWidth(); const Origin::MatrixSheet& layer = originMatrix.sheets[sheetIndex]; const int colCount = layer.columnCount; const int rowCount = layer.rowCount; matrix->setRowCount(rowCount); matrix->setColumnCount(colCount); matrix->setFormula(layer.command.c_str()); //TODO: how to handle different widths for different columns? for (int j = 0; j < colCount; j++) matrix->setColumnWidth(j, layer.width * scaling_factor); //TODO: check colum major vs. row major to improve the performance here for (int i = 0; i < rowCount; i++) { for (int j = 0; j < colCount; j++) matrix->setCell(i, j, layer.data[j + i*colCount]); } char format = 'g'; //TODO: prec not support by Matrix //int prec = 6; switch (layer.valueTypeSpecification) { case 0: //Decimal 1000 format='f'; // prec = layer.decimalPlaces; break; case 1: //Scientific format='e'; // prec = layer.decimalPlaces; break; case 2: //Engineering case 3: //Decimal 1,000 format='g'; // prec = layer.significantDigits; break; } matrix->setNumericFormat(format); return true; } bool OriginProjectParser::loadWorksheet(Worksheet* worksheet, bool preview) { DEBUG("OriginProjectParser::loadWorksheet()"); //load worksheet data const Origin::Graph& graph = m_originFile->graph(findGraphByName(worksheet->name())); DEBUG(" graph name = " << graph.name); worksheet->setComment(graph.label.c_str()); //TODO: width, height, view mode (print view, page view, window view, draft view) //Origin allows to freely resize the window and ajusts the size of the plot (layer) automatically //by keeping a certain width-to-height ratio. It's not clear what the actual size of the plot/layer is and how to handle this. //For now we simply create a new wokrsheet here with it's default size and make it using the whole view size. //Later we can decide to use one of the following properties: // 1) Window.frameRect gives Rect-corner coordinates (in pixels) of the Window object // 2) GraphLayer.clientRect gives Rect-corner coordinates (pixels) of the Layer inside the (printer?) page. // 3) Graph.width, Graph.height give the (printer?) page size in pixels. +// const QRectF size(0, 0, +// Worksheet::convertToSceneUnits(graph.width/600., Worksheet::Inch), +// Worksheet::convertToSceneUnits(graph.height/600., Worksheet::Inch)); +// worksheet->setPageRect(size); worksheet->setUseViewSize(true); + QMap textLabelPositions; // worksheet background color const Origin::ColorGradientDirection bckgColorGradient = graph.windowBackgroundColorGradient; const Origin::Color bckgBaseColor = graph.windowBackgroundColorBase; const Origin::Color bckgEndColor = graph.windowBackgroundColorEnd; worksheet->setBackgroundColorStyle(backgroundColorStyle(bckgColorGradient)); switch (bckgColorGradient) { case Origin::ColorGradientDirection::NoGradient: case Origin::ColorGradientDirection::TopLeft: case Origin::ColorGradientDirection::Left: case Origin::ColorGradientDirection::BottomLeft: case Origin::ColorGradientDirection::Top: worksheet->setBackgroundFirstColor(color(bckgEndColor)); worksheet->setBackgroundSecondColor(color(bckgBaseColor)); break; case Origin::ColorGradientDirection::Center: break; case Origin::ColorGradientDirection::Bottom: case Origin::ColorGradientDirection::TopRight: case Origin::ColorGradientDirection::Right: case Origin::ColorGradientDirection::BottomRight: worksheet->setBackgroundFirstColor(color(bckgBaseColor)); worksheet->setBackgroundSecondColor(color(bckgEndColor)); } //TODO: do we need changes on the worksheet layout? //add plots int index = 1; for (const auto& layer: graph.layers) { if (!layer.is3D()) { - CartesianPlot* plot = new CartesianPlot(i18n("Plot") + QString::number(index)); + CartesianPlot* plot = new CartesianPlot(i18n("Plot%1", QString::number(index))); + worksheet->addChildFast(plot); plot->setIsLoading(true); //TODO: width, height //background color const Origin::Color& regColor = layer.backgroundColor; if (regColor.type == Origin::Color::None) plot->plotArea()->setBackgroundOpacity(0); else plot->plotArea()->setBackgroundFirstColor(color(regColor)); //border if (layer.borderType == Origin::BorderType::None) plot->plotArea()->setBorderPen(QPen(Qt::NoPen)); else plot->plotArea()->setBorderPen(QPen(Qt::SolidLine)); //ranges plot->setAutoScaleX(false); plot->setAutoScaleY(false); const Origin::GraphAxis& originXAxis = layer.xAxis; const Origin::GraphAxis& originYAxis = layer.yAxis; plot->setXMin(originXAxis.min); plot->setXMax(originXAxis.max); plot->setYMin(originYAxis.min); plot->setYMax(originYAxis.max); //scales switch(originXAxis.scale) { case Origin::GraphAxis::Linear: plot->setXScale(CartesianPlot::ScaleLinear); break; case Origin::GraphAxis::Log10: plot->setXScale(CartesianPlot::ScaleLog10); break; case Origin::GraphAxis::Ln: plot->setXScale(CartesianPlot::ScaleLn); break; case Origin::GraphAxis::Log2: plot->setXScale(CartesianPlot::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: plot->setXScale(CartesianPlot::ScaleLinear); break; } switch(originYAxis.scale) { case Origin::GraphAxis::Linear: plot->setYScale(CartesianPlot::ScaleLinear); break; case Origin::GraphAxis::Log10: plot->setYScale(CartesianPlot::ScaleLog10); break; case Origin::GraphAxis::Ln: plot->setYScale(CartesianPlot::ScaleLn); break; case Origin::GraphAxis::Log2: plot->setYScale(CartesianPlot::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: plot->setYScale(CartesianPlot::ScaleLinear); break; } //axes //x bottom - if (layer.curves.size()){ + if (layer.curves.size()) { Origin::GraphCurve originCurve = layer.curves[0]; + QString xColumnName = QString::fromLatin1(originCurve.xColumnName.c_str()); + //TODO: "Partikelgrö" + DEBUG(" xColumnName = " << xColumnName.toStdString()); + QDEBUG(" UTF8 xColumnName = " << xColumnName.toUtf8()); + QString yColumnName = QString::fromLatin1(originCurve.yColumnName.c_str()); + if (!originXAxis.formatAxis[0].hidden) { Axis* axis = new Axis("x", Axis::AxisHorizontal); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisBottom); plot->addChildFast(axis); - loadAxis(originXAxis, axis, 0, QString::fromLatin1(originCurve.xColumnName.c_str())); + loadAxis(originXAxis, axis, 0, xColumnName); axis->setSuppressRetransform(false); } //x top if (!originXAxis.formatAxis[1].hidden) { Axis* axis = new Axis("x top", Axis::AxisHorizontal); axis->setPosition(Axis::AxisTop); axis->setSuppressRetransform(true); plot->addChildFast(axis); - loadAxis(originXAxis, axis, 1, QString::fromLatin1(originCurve.xColumnName.c_str())); + loadAxis(originXAxis, axis, 1, xColumnName); axis->setSuppressRetransform(false); } //y left if (!originYAxis.formatAxis[0].hidden) { Axis* axis = new Axis("y", Axis::AxisVertical); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisLeft); plot->addChildFast(axis); - loadAxis(originYAxis, axis, 0, QString::fromLatin1(originCurve.yColumnName.c_str())); + loadAxis(originYAxis, axis, 0, yColumnName); axis->setSuppressRetransform(false); } //y right if (!originYAxis.formatAxis[1].hidden) { Axis* axis = new Axis("y right", Axis::AxisVertical); axis->setSuppressRetransform(true); axis->setPosition(Axis::AxisRight); plot->addChildFast(axis); - loadAxis(originYAxis, axis, 1, QString::fromLatin1(originCurve.yColumnName.c_str())); + loadAxis(originYAxis, axis, 1, yColumnName); axis->setSuppressRetransform(false); } } else { //TODO: ? } //range breaks //TODO //add legend if available const Origin::TextBox& originLegend = layer.legend; const QString& legendText = QString::fromLatin1(originLegend.text.c_str()); DEBUG(" legend text = " << legendText.toStdString()); if (!originLegend.text.empty()) { CartesianPlotLegend* legend = new CartesianPlotLegend(plot, i18n("legend")); //Origin's legend uses "\l(...)" or "\L(...)" string to format the legend symbol // and "%(...) to format the legend text for each curve //s. a. https://www.originlab.com/doc/Origin-Help/Legend-ManualControl //the text before these formatting tags, if available, is interpreted as the legend title QString legendTitle; //search for the first occurance of the legend symbol substring int index = legendText.indexOf(QLatin1String("\\l("), 0, Qt::CaseInsensitive); if (index != -1) legendTitle = legendText.left(index); else { //check legend text index = legendText.indexOf(QLatin1String("%(")); if (index != -1) legendTitle = legendText.left(index); } legendTitle = legendTitle.trimmed(); if (!legendTitle.isEmpty()) legendTitle = parseOriginText(legendTitle); DEBUG(" legend title = " << legendTitle.toStdString()); legend->title()->setText(legendTitle); //TODO: text color //const Origin::Color& originColor = originLegend.color; //position - //TODO: for the first release with OPJ support we put the leget to the bottom left corner, + //TODO: for the first release with OPJ support we put the legend to the bottom left corner, //in the next release we'll evaluate originLegend.clientRect giving the position inside of the whole page in Origin. //In Origin the legend can be placed outside of the plot which is not possible in LabPlot. //To achieve this we'll need to increase padding area in the plot and to place the legend outside of the plot area. CartesianPlotLegend::PositionWrapper position; position.horizontalPosition = CartesianPlotLegend::hPositionRight; position.verticalPosition = CartesianPlotLegend::vPositionBottom; legend->setPosition(position); //TODO: rotation //legend->setRotationAngle(originLegend.rotation); //border line if (originLegend.borderType == Origin::BorderType::None) legend->setBorderPen(QPen(Qt::NoPen)); else legend->setBorderPen(QPen(Qt::SolidLine)); //background color, determine it with the help of the border type if (originLegend.borderType == Origin::BorderType::DarkMarble) legend->setBackgroundFirstColor(Qt::darkGray); else if (originLegend.borderType == Origin::BorderType::BlackOut) legend->setBackgroundFirstColor(Qt::black); else legend->setBackgroundFirstColor(Qt::white); - plot->addChildFast(legend); + plot->addLegend(legend); } //texts for (const auto& s: layer.texts) { DEBUG("EXTRA TEXT = " << s.text.c_str()); TextLabel* label = new TextLabel("text label"); - label->setText(parseOriginText(QString::fromLocal8Bit(s.text.c_str()))); + label->setText(parseOriginText(QString::fromLatin1(s.text.c_str()))); plot->addChild(label); label->setParentGraphicsItem(plot->graphicsItem()); - - //TODO: positioning + //position + //determine the relative position inside of the layer rect + const float horRatio = (float)(s.clientRect.left-layer.clientRect.left)/(layer.clientRect.right-layer.clientRect.left); + const float vertRatio = (float)(s.clientRect.top-layer.clientRect.top)/(layer.clientRect.bottom-layer.clientRect.top); + textLabelPositions[label] = QSizeF(horRatio, vertRatio); + DEBUG("horizontal/vertical ratio = " << horRatio << ", " << vertRatio); //rotation label->setRotationAngle(s.rotation); //TODO: // Color color; // unsigned short fontSize; // int tab; // BorderType borderType; // Attach attach; - } //curves int curveIndex = 1; for (const auto& originCurve: layer.curves) { QString data(originCurve.dataName.c_str()); switch(data[0].toAscii()) { case 'T': case 'E': { if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol || originCurve.type == Origin::GraphCurve::ErrorBar || originCurve.type == Origin::GraphCurve::XErrorBar) { // parse and use legend text // find substring between %c{curveIndex} and %c{curveIndex+1} int pos1 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex)) + 5; int pos2 = legendText.indexOf(QString("\\c{%1}").arg(curveIndex+1)); QString curveText = legendText.mid(pos1, pos2 - pos1); // replace %(1), %(2), etc. with curve name curveText.replace(QString("%(%1)").arg(curveIndex), QString::fromLatin1(originCurve.yColumnName.c_str())); curveText = curveText.trimmed(); DEBUG(" curve " << curveIndex << " text = " << curveText.toStdString()); - //XYCurve* xyCurve = new XYCurve(i18n("Curve") + QString::number(curveIndex)); + //XYCurve* xyCurve = new XYCurve(i18n("Curve%1", QString::number(curveIndex))); //TODO: curve (legend) does not support HTML text yet. //XYCurve* xyCurve = new XYCurve(curveText); XYCurve* curve = new XYCurve(QString::fromLatin1(originCurve.yColumnName.c_str())); const QString& tableName = data.right(data.length() - 2); curve->setXColumnPath(tableName + '/' + originCurve.xColumnName.c_str()); curve->setYColumnPath(tableName + '/' + originCurve.yColumnName.c_str()); curve->suppressRetransform(true); if (!preview) loadCurve(originCurve, curve); plot->addChildFast(curve); curve->suppressRetransform(false); } else if (originCurve.type == Origin::GraphCurve::Column) { //vertical bars } else if (originCurve.type == Origin::GraphCurve::Bar) { //horizontal bars } else if (originCurve.type == Origin::GraphCurve::Histogram) { } } break; case 'F': { Origin::Function function; const vector::difference_type funcIndex = m_originFile->functionIndex(data.right(data.length()-2).toStdString().c_str()); if (funcIndex < 0) { ++curveIndex; continue; } function = m_originFile->function(funcIndex); XYEquationCurve* xyEqCurve = new XYEquationCurve(function.name.c_str()); XYEquationCurve::EquationData eqData; eqData.count = function.totalPoints; eqData.expression1 = QString(function.formula.c_str()); if(function.type == Origin::Function::Polar) { eqData.type = XYEquationCurve::Polar; //replace 'x' by 'phi' eqData.expression1 = eqData.expression1.replace('x', "phi"); //convert from degrees to radians eqData.min = QString::number(function.begin/180) + QLatin1String("*pi"); eqData.max = QString::number(function.end/180) + QLatin1String("*pi"); } else { eqData.expression1 = QString(function.formula.c_str()); eqData.min = QString::number(function.begin); eqData.max = QString::number(function.end); } xyEqCurve->suppressRetransform(true); xyEqCurve->setEquationData(eqData); if (!preview) loadCurve(originCurve, xyEqCurve); plot->addChildFast(xyEqCurve); xyEqCurve->suppressRetransform(false); } } ++curveIndex; } - - worksheet->addChildFast(plot); } else { //no support for 3D plots yet //TODO: add an "UnsupportedAspect" here } ++index; } - if (!preview) + if (!preview) { worksheet->updateLayout(); + //worksheet and plots got their sizes, + //-> position all text labels inside the plots correctly by converting + //the relative positions determined above to the absolute values + QMap::const_iterator it = textLabelPositions.constBegin(); + while (it != textLabelPositions.constEnd()) { + TextLabel* label = it.key(); + const QSizeF& ratios = it.value(); + const CartesianPlot* plot = static_cast(label->parentAspect()); + + TextLabel::PositionWrapper position = label->position(); + position.point.setX(plot->dataRect().width()*(ratios.width()-0.5)); + position.point.setY(plot->dataRect().height()*(ratios.height()-0.5)); + label->setPosition(position); + + ++it; + } + } + return true; } /* * sets the axis properties (format and ticks) as defined in \c originAxis in \c axis, * \c index being 0 or 1 for "top" and "bottom" or "left" and "right" for horizontal or vertical axes, respectively. */ void OriginProjectParser::loadAxis(const Origin::GraphAxis& originAxis, Axis* axis, int index, const QString& axisTitle) const { // int axisPosition; // possible values: // 0: Axis is at default position // 1: Axis is at (axisPositionValue)% from standard position // 2: Axis is at (axisPositionValue) position of ortogonal axis // double axisPositionValue; // bool zeroLine; // bool oppositeLine; //ranges axis->setStart(originAxis.min); axis->setEnd(originAxis.max); //ticks axis->setMajorTicksType(Axis::TicksIncrement); axis->setMajorTicksIncrement(originAxis.step); axis->setMinorTicksType(Axis::TicksTotalNumber); axis->setMinorTicksNumber(originAxis.minorTicks); //scale switch(originAxis.scale) { case Origin::GraphAxis::Linear: axis->setScale(Axis::ScaleLinear); break; case Origin::GraphAxis::Log10: axis->setScale(Axis::ScaleLog10); break; case Origin::GraphAxis::Ln: axis->setScale(Axis::ScaleLn); break; case Origin::GraphAxis::Log2: axis->setScale(Axis::ScaleLog2); break; case Origin::GraphAxis::Probability: case Origin::GraphAxis::Probit: case Origin::GraphAxis::Reciprocal: case Origin::GraphAxis::OffsetReciprocal: case Origin::GraphAxis::Logit: //TODO: axis->setScale(Axis::ScaleLinear); break; } //major grid const Origin::GraphGrid& majorGrid = originAxis.majorGrid; QPen gridPen = axis->majorGridPen(); Qt::PenStyle penStyle(Qt::NoPen); if (!majorGrid.hidden) { switch (majorGrid.style) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } } gridPen.setStyle(penStyle); Origin::Color gridColor; gridColor.type = Origin::Color::ColorType::Regular; gridColor.regular = majorGrid.color; gridPen.setColor(OriginProjectParser::color(gridColor)); gridPen.setWidthF(Worksheet::convertToSceneUnits(majorGrid.width, Worksheet::Point)); axis->setMajorGridPen(gridPen); //minor grid const Origin::GraphGrid& minorGrid = originAxis.minorGrid; gridPen = axis->minorGridPen(); penStyle = Qt::NoPen; if (!minorGrid.hidden) { switch (minorGrid.style) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } } gridPen.setStyle(penStyle); gridColor.regular = minorGrid.color; gridPen.setColor(OriginProjectParser::color(gridColor)); gridPen.setWidthF(Worksheet::convertToSceneUnits(minorGrid.width, Worksheet::Point)); axis->setMinorGridPen(gridPen); //process Origin::GraphAxisFormat const Origin::GraphAxisFormat& axisFormat = originAxis.formatAxis[index]; QPen pen; Origin::Color color; color.type = Origin::Color::ColorType::Regular; color.regular = axisFormat.color; pen.setColor(OriginProjectParser::color(color)); pen.setWidthF(Worksheet::convertToSceneUnits(axisFormat.thickness, Worksheet::Point)); axis->setLinePen(pen); axis->setMajorTicksLength( Worksheet::convertToSceneUnits(axisFormat.majorTickLength, Worksheet::Point) ); axis->setMajorTicksDirection( (Axis::TicksFlags) axisFormat.majorTicksType); axis->setMajorTicksPen(pen); axis->setMinorTicksLength( axis->majorTicksLength()/2); // minorTicksLength is half of majorTicksLength axis->setMinorTicksDirection( (Axis::TicksFlags) axisFormat.minorTicksType); axis->setMinorTicksPen(pen); - - QString titleText = parseOriginText(QString::fromLocal8Bit(axisFormat.label.text.c_str())); + QString titleText = parseOriginText(QString::fromLatin1(axisFormat.label.text.c_str())); DEBUG(" axis title text = " << titleText.toStdString()); //TODO: parseOriginText() returns html formatted string. What is axisFormat.color used for? //TODO: use axisFormat.fontSize to override the global font size for the hmtl string? + //TODO: convert special character here too DEBUG(" curve name = " << axisTitle.toStdString()); titleText.replace("%(?X)", axisTitle); titleText.replace("%(?Y)", axisTitle); DEBUG(" axis title = " << titleText.toStdString()); axis->title()->setText(titleText); axis->title()->setRotationAngle(axisFormat.label.rotation); axis->setLabelsPrefix(axisFormat.prefix.c_str()); axis->setLabelsSuffix(axisFormat.suffix.c_str()); //TODO: handle string factor member in GraphAxisFormat //process Origin::GraphAxisTick const Origin::GraphAxisTick& tickAxis = originAxis.tickAxis[index]; if (tickAxis.showMajorLabels) { color.type = Origin::Color::ColorType::Regular; color.regular = tickAxis.color; axis->setLabelsColor(OriginProjectParser::color(color)); //TODO: how to set labels position (top vs. bottom)? } else { axis->setLabelsPosition(Axis::LabelsPosition::NoLabels); } //TODO: handle ValueType valueType member in GraphAxisTick //TODO: handle int valueTypeSpecification in GraphAxisTick //precision if (tickAxis.decimalPlaces == -1) axis->setLabelsAutoPrecision(true); else { axis->setLabelsPrecision(tickAxis.decimalPlaces); axis->setLabelsAutoPrecision(false); } QFont font; //TODO: font family? font.setPixelSize( Worksheet::convertToSceneUnits(tickAxis.fontSize, Worksheet::Point) ); font.setBold(tickAxis.fontBold); axis->setLabelsFont(font); //TODO: handle string dataName member in GraphAxisTick //TODO: handle string columnName member in GraphAxisTick axis->setLabelsRotationAngle(tickAxis.rotation); } void OriginProjectParser::loadCurve(const Origin::GraphCurve& originCurve, XYCurve* curve) const { //line properties QPen pen = curve->linePen(); Qt::PenStyle penStyle(Qt::NoPen); if (originCurve.type == Origin::GraphCurve::Line || originCurve.type == Origin::GraphCurve::LineSymbol) { switch(originCurve.lineConnect) { case Origin::GraphCurve::NoLine: curve->setLineType(XYCurve::NoLine); break; case Origin::GraphCurve::Straight: curve->setLineType(XYCurve::Line); break; case Origin::GraphCurve::TwoPointSegment: curve->setLineType(XYCurve::Segments2); break; case Origin::GraphCurve::ThreePointSegment: curve->setLineType(XYCurve::Segments3); break; case Origin::GraphCurve::BSpline: case Origin::GraphCurve::Bezier: case Origin::GraphCurve::Spline: curve->setLineType(XYCurve::SplineCubicNatural); break; case Origin::GraphCurve::StepHorizontal: curve->setLineType(XYCurve::StartHorizontal); break; case Origin::GraphCurve::StepVertical: curve->setLineType(XYCurve::StartVertical); break; case Origin::GraphCurve::StepHCenter: curve->setLineType(XYCurve::MidpointHorizontal); break; case Origin::GraphCurve::StepVCenter: curve->setLineType(XYCurve::MidpointVertical); break; } switch (originCurve.lineStyle) { case Origin::GraphCurve::Solid: penStyle = Qt::SolidLine; break; case Origin::GraphCurve::Dash: case Origin::GraphCurve::ShortDash: penStyle = Qt::DashLine; break; case Origin::GraphCurve::Dot: case Origin::GraphCurve::ShortDot: penStyle = Qt::DotLine; break; case Origin::GraphCurve::DashDot: case Origin::GraphCurve::ShortDashDot: penStyle = Qt::DashDotLine; break; case Origin::GraphCurve::DashDotDot: penStyle = Qt::DashDotDotLine; break; } pen.setStyle(penStyle); pen.setWidthF( Worksheet::convertToSceneUnits(originCurve.lineWidth, Worksheet::Point) ); pen.setColor(color(originCurve.lineColor)); curve->setLineOpacity(1 - originCurve.lineTransparency/255); //TODO: handle unsigned char boxWidth of Origin::GraphCurve } pen.setStyle(penStyle); curve->setLinePen(pen); //symbol properties if (originCurve.type == Origin::GraphCurve::Scatter || originCurve.type == Origin::GraphCurve::LineSymbol) { //try to map the different symbols, mapping is not exact curve->setSymbolsRotationAngle(0); switch(originCurve.symbolShape) { case 0: //NoSymbol curve->setSymbolsStyle(Symbol::NoSymbols); break; case 1: //Rect curve->setSymbolsStyle(Symbol::Square); break; case 2: //Ellipse case 20://Sphere curve->setSymbolsStyle(Symbol::Circle); break; case 3: //UTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 4: //DTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 5: //Diamond curve->setSymbolsStyle(Symbol::Diamond); break; case 6: //Cross + curve->setSymbolsStyle(Symbol::Cross); break; case 7: //Cross x curve->setSymbolsStyle(Symbol::Cross); break; case 8: //Snow curve->setSymbolsStyle(Symbol::Star4); break; case 9: //Horizontal - curve->setSymbolsStyle(Symbol::Line); curve->setSymbolsRotationAngle(90); break; case 10: //Vertical | curve->setSymbolsStyle(Symbol::Line); break; case 15: //LTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 16: //RTriangle curve->setSymbolsStyle(Symbol::EquilateralTriangle); break; case 17: //Hexagon case 19: //Pentagon curve->setSymbolsStyle(Symbol::Square); break; case 18: //Star curve->setSymbolsStyle(Symbol::Star5); break; default: curve->setSymbolsStyle(Symbol::NoSymbols); } //symbol size curve->setSymbolsSize(Worksheet::convertToSceneUnits(originCurve.symbolSize, Worksheet::Point)); //symbol fill color QBrush brush = curve->symbolsBrush(); if (originCurve.symbolFillColor.type == Origin::Color::ColorType::Automatic) { //"automatic" color -> the color of the line, if available, has to be used, black otherwise if (curve->lineType() != XYCurve::NoLine) brush.setColor(curve->linePen().color()); else brush.setColor(Qt::black); } else brush.setColor(color(originCurve.symbolFillColor)); curve->setSymbolsBrush(brush); //symbol border/edge color and width QPen pen = curve->symbolsPen(); if (originCurve.symbolColor.type == Origin::Color::ColorType::Automatic) { //"automatic" color -> the color of the line, if available, has to be used, black otherwise if (curve->lineType() != XYCurve::NoLine) pen.setColor(curve->linePen().color()); else pen.setColor(Qt::black); } else pen.setColor(color(originCurve.symbolColor)); //border width (edge thickness in Origin) is given by percentage of the symbol radius pen.setWidthF(originCurve.symbolThickness/100.*curve->symbolsSize()/2.); curve->setSymbolsPen(pen); //handle unsigned char pointOffset member //handle bool connectSymbols member } else { curve->setSymbolsStyle(Symbol::NoSymbols); } //filling properties if(originCurve.fillArea) { //TODO: handle unsigned char fillAreaType; //with 'fillAreaType'=0x10 the area between the curve and the x-axis is filled //with 'fillAreaType'=0x14 the area included inside the curve is filled. First and last curve points are joined by a line to close the otherwise open area. //with 'fillAreaType'=0x12 the area excluded outside the curve is filled. The inverse of fillAreaType=0x14 is filled. //At the moment we only support the first type, so set it to XYCurve::FillingBelow curve->setFillingPosition(XYCurve::FillingBelow); if (originCurve.fillAreaPattern == 0) { curve->setFillingType(PlotArea::Color); } else { curve->setFillingType(PlotArea::Pattern); //map different patterns in originCurve.fillAreaPattern (has the values of Origin::FillPattern) to Qt::BrushStyle; switch(originCurve.fillAreaPattern) { case 0: curve->setFillingBrushStyle(Qt::NoBrush); break; case 1: case 2: case 3: curve->setFillingBrushStyle(Qt::BDiagPattern); break; case 4: case 5: case 6: curve->setFillingBrushStyle(Qt::FDiagPattern); break; case 7: case 8: case 9: curve->setFillingBrushStyle(Qt::DiagCrossPattern); break; case 10: case 11: case 12: curve->setFillingBrushStyle(Qt::HorPattern); break; case 13: case 14: case 15: curve->setFillingBrushStyle(Qt::VerPattern); break; case 16: case 17: case 18: curve->setFillingBrushStyle(Qt::CrossPattern); break; } } curve->setFillingFirstColor(color(originCurve.fillAreaColor)); curve->setFillingOpacity(1 - originCurve.fillAreaTransparency/255); //Color fillAreaPatternColor - color for the pattern lines, not supported //double fillAreaPatternWidth - width of the pattern lines, not supported //bool fillAreaWithLineTransparency - transparency of the pattern lines indepetendent of the area transparency, not supported //TODO: //unsigned char fillAreaPatternBorderStyle; //Color fillAreaPatternBorderColor; //double fillAreaPatternBorderWidth; //The Border properties are used only in "Column/Bar" (histogram) plots. Those properties are: //fillAreaPatternBorderStyle for the line style (use enum Origin::LineStyle here) //fillAreaPatternBorderColor for the line color //fillAreaPatternBorderWidth for the line width } else curve->setFillingPosition(XYCurve::NoFilling); } bool OriginProjectParser::loadNote(Note* note, bool preview) { DEBUG("OriginProjectParser::loadNote()"); //load note data const Origin::Note& originNote = m_originFile->note(findNoteByName(note->name())); if (preview) return true; note->setComment(originNote.label.c_str()); note->setNote(QString::fromLatin1(originNote.text.c_str())); return true; } //############################################################################## //########################### Helper functions ################################ //############################################################################## QDateTime OriginProjectParser::creationTime(tree::iterator it) const { //this logic seems to be correct only for the first node (project node). For other nodes the current time is returned. char time_str[21]; strftime(time_str, sizeof(time_str), "%F %T", gmtime(&(*it).creationDate)); return QDateTime::fromString(QString(time_str), Qt::ISODate); } QString OriginProjectParser::parseOriginText(const QString &str) const { DEBUG("parseOriginText()"); QStringList lines = str.split('\n'); QString text = ""; for (int i = 0; i < lines.size(); ++i) { if (i > 0) text.append("
"); text.append(parseOriginTags(lines[i])); } DEBUG(" PARSED TEXT = " << text.toStdString()); return text; } QColor OriginProjectParser::color(Origin::Color color) const { switch (color.type) { case Origin::Color::ColorType::Regular: switch (color.regular) { case Origin::Color::Black: return QColor(Qt::black); case Origin::Color::Red: return QColor(Qt::red); case Origin::Color::Green: return QColor(Qt::green); case Origin::Color::Blue: return QColor(Qt::blue); case Origin::Color::Cyan: return QColor(Qt::cyan); case Origin::Color::Magenta: return QColor(Qt::magenta); case Origin::Color::Yellow: return QColor(Qt::yellow); case Origin::Color::DarkYellow: return QColor(Qt::darkYellow); case Origin::Color::Navy: return QColor(0, 0, 128); case Origin::Color::Purple: return QColor(128, 0, 128); case Origin::Color::Wine: return QColor(128, 0, 0); case Origin::Color::Olive: return QColor(0, 128, 0); case Origin::Color::DarkCyan: return QColor(Qt::darkCyan); case Origin::Color::Royal: return QColor(0, 0, 160); case Origin::Color::Orange: return QColor(255, 128, 0); case Origin::Color::Violet: return QColor(128, 0, 255); case Origin::Color::Pink: return QColor(255, 0, 128); case Origin::Color::White: return QColor(Qt::white); case Origin::Color::LightGray: return QColor(Qt::lightGray); case Origin::Color::Gray: return QColor(Qt::gray); case Origin::Color::LTYellow: return QColor(255, 0, 128); case Origin::Color::LTCyan: return QColor(128, 255, 255); case Origin::Color::LTMagenta: return QColor(255, 128, 255); case Origin::Color::DarkGray: return QColor(Qt::darkGray); case Origin::Color::SpecialV7Axis: return QColor(Qt::black); } break; case Origin::Color::ColorType::Custom: return QColor(color.custom[0], color.custom[1], color.custom[2]); case Origin::Color::ColorType::None: case Origin::Color::ColorType::Automatic: case Origin::Color::ColorType::Increment: case Origin::Color::ColorType::Indexing: case Origin::Color::ColorType::RGB: case Origin::Color::ColorType::Mapping: break; } return QColor(Qt::white); } PlotArea::BackgroundColorStyle OriginProjectParser::backgroundColorStyle(Origin::ColorGradientDirection colorGradient) const { switch (colorGradient) { case Origin::ColorGradientDirection::NoGradient: return PlotArea::BackgroundColorStyle::SingleColor; case Origin::ColorGradientDirection::TopLeft: return PlotArea::BackgroundColorStyle::TopLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Left: return PlotArea::BackgroundColorStyle::HorizontalLinearGradient; case Origin::ColorGradientDirection::BottomLeft: return PlotArea::BackgroundColorStyle::BottomLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Top: return PlotArea::BackgroundColorStyle::VerticalLinearGradient; case Origin::ColorGradientDirection::Center: return PlotArea::BackgroundColorStyle::RadialGradient; case Origin::ColorGradientDirection::Bottom: return PlotArea::BackgroundColorStyle::VerticalLinearGradient; case Origin::ColorGradientDirection::TopRight: return PlotArea::BackgroundColorStyle::BottomLeftDiagonalLinearGradient; case Origin::ColorGradientDirection::Right: return PlotArea::BackgroundColorStyle::HorizontalLinearGradient; case Origin::ColorGradientDirection::BottomRight: return PlotArea::BackgroundColorStyle::TopLeftDiagonalLinearGradient; } return PlotArea::BackgroundColorStyle::SingleColor; } QString strreverse(const QString &str) { //QString reversing QByteArray ba = str.toLocal8Bit(); std::reverse(ba.begin(), ba.end()); return QString(ba); } +QList> OriginProjectParser::charReplacementList() const { + QList> replacements; + + // TODO: probably missed some. Is there any generic method? + replacements << qMakePair(QString("ä"), QString("ä")); + replacements << qMakePair(QString("ö"), QString("ö")); + replacements << qMakePair(QString("ü"), QString("ü")); + replacements << qMakePair(QString("Ä"), QString("Ä")); + replacements << qMakePair(QString("Ö"), QString("Ö")); + replacements << qMakePair(QString("Ü"), QString("Ü")); + replacements << qMakePair(QString("ß"), QString("ß")); + replacements << qMakePair(QString("€"), QString("€")); + replacements << qMakePair(QString("£"), QString("£")); + replacements << qMakePair(QString("¥"), QString("¥")); + replacements << qMakePair(QString("¤"), QString("¤")); + replacements << qMakePair(QString("¦"), QString("¦")); + replacements << qMakePair(QString("§"), QString("§")); + replacements << qMakePair(QString("µ"), QString("µ")); + replacements << qMakePair(QString("¹"), QString("¹")); + replacements << qMakePair(QString("²"), QString("²")); + replacements << qMakePair(QString("³"), QString("³")); + replacements << qMakePair(QString("¶"), QString("¶")); + replacements << qMakePair(QString("ø"), QString("ø")); + replacements << qMakePair(QString("æ"), QString("æ")); + replacements << qMakePair(QString("ð"), QString("ð")); + replacements << qMakePair(QString("ħ"), QString("ℏ")); + replacements << qMakePair(QString("ĸ"), QString("κ")); + replacements << qMakePair(QString("¢"), QString("¢")); + replacements << qMakePair(QString("¼"), QString("¼")); + replacements << qMakePair(QString("½"), QString("½")); + replacements << qMakePair(QString("¾"), QString("¾")); + replacements << qMakePair(QString("¬"), QString("¬")); + replacements << qMakePair(QString("©"), QString("©")); + replacements << qMakePair(QString("®"), QString("®")); + replacements << qMakePair(QString("ª"), QString("ª")); + replacements << qMakePair(QString("º"), QString("º")); + replacements << qMakePair(QString("±"), QString("±")); + replacements << qMakePair(QString("¿"), QString("¿")); + replacements << qMakePair(QString("×"), QString("×")); + replacements << qMakePair(QString("°"), QString("°")); + replacements << qMakePair(QString("«"), QString("«")); + replacements << qMakePair(QString("»"), QString("»")); + replacements << qMakePair(QString("¯"), QString("¯")); + replacements << qMakePair(QString("¸"), QString("¸")); + replacements << qMakePair(QString("À"), QString("À")); + replacements << qMakePair(QString("Á"), QString("Á")); + replacements << qMakePair(QString("Â"), QString("Â")); + replacements << qMakePair(QString("Ã"), QString("Ã")); + replacements << qMakePair(QString("Å"), QString("Å")); + replacements << qMakePair(QString("Æ"), QString("Æ")); + replacements << qMakePair(QString("Ç"), QString("Ç")); + replacements << qMakePair(QString("È"), QString("È")); + replacements << qMakePair(QString("É"), QString("É")); + replacements << qMakePair(QString("Ê"), QString("Ê")); + replacements << qMakePair(QString("Ë"), QString("Ë")); + replacements << qMakePair(QString("Ì"), QString("Ì")); + replacements << qMakePair(QString("Í"), QString("Í")); + replacements << qMakePair(QString("Î"), QString("Î")); + replacements << qMakePair(QString("Ï"), QString("Ï")); + replacements << qMakePair(QString("Ð"), QString("Ð")); + replacements << qMakePair(QString("Ñ"), QString("Ñ")); + replacements << qMakePair(QString("Ò"), QString("Ò")); + replacements << qMakePair(QString("Ó"), QString("Ó")); + replacements << qMakePair(QString("Ô"), QString("Ô")); + replacements << qMakePair(QString("Õ"), QString("Õ")); + replacements << qMakePair(QString("Ù"), QString("Ù")); + replacements << qMakePair(QString("Ú"), QString("Ú")); + replacements << qMakePair(QString("Û"), QString("Û")); + replacements << qMakePair(QString("Ý"), QString("Ý")); + replacements << qMakePair(QString("Þ"), QString("Þ")); + replacements << qMakePair(QString("à"), QString("à")); + replacements << qMakePair(QString("á"), QString("á")); + replacements << qMakePair(QString("â"), QString("â")); + replacements << qMakePair(QString("ã"), QString("ã")); + replacements << qMakePair(QString("å"), QString("å")); + replacements << qMakePair(QString("ç"), QString("ç")); + replacements << qMakePair(QString("è"), QString("è")); + replacements << qMakePair(QString("é"), QString("é")); + replacements << qMakePair(QString("ê"), QString("ê")); + replacements << qMakePair(QString("ë"), QString("ë")); + replacements << qMakePair(QString("ì"), QString("ì")); + replacements << qMakePair(QString("í"), QString("í")); + replacements << qMakePair(QString("î"), QString("î")); + replacements << qMakePair(QString("ï"), QString("ï")); + replacements << qMakePair(QString("ñ"), QString("ñ")); + replacements << qMakePair(QString("ò"), QString("ò")); + replacements << qMakePair(QString("ó"), QString("ó")); + replacements << qMakePair(QString("ô"), QString("ô")); + replacements << qMakePair(QString("õ"), QString("õ")); + replacements << qMakePair(QString("÷"), QString("÷")); + replacements << qMakePair(QString("ù"), QString("ù")); + replacements << qMakePair(QString("ú"), QString("ú")); + replacements << qMakePair(QString("û"), QString("û")); + replacements << qMakePair(QString("ý"), QString("ý")); + replacements << qMakePair(QString("þ"), QString("þ")); + replacements << qMakePair(QString("ÿ"), QString("ÿ")); + replacements << qMakePair(QString("Œ"), QString("Œ")); + replacements << qMakePair(QString("œ"), QString("œ")); + replacements << qMakePair(QString("Š"), QString("Š")); + replacements << qMakePair(QString("š"), QString("š")); + replacements << qMakePair(QString("Ÿ"), QString("Ÿ")); + replacements << qMakePair(QString("†"), QString("†")); + replacements << qMakePair(QString("‡"), QString("‡")); + replacements << qMakePair(QString("…"), QString("…")); + replacements << qMakePair(QString("‰"), QString("‰")); + replacements << qMakePair(QString("™"), QString("™")); + + return replacements; +} + +QString OriginProjectParser::replaceSpecialChars(QString text) const { + QString t = text; + for (auto const &r : charReplacementList()) + t.replace(r.first, r.second); + return t; +} + // taken from SciDAVis QString OriginProjectParser::parseOriginTags(const QString &str) const { DEBUG("parseOriginTags()"); DEBUG(" string: " << str.toStdString()); + QDEBUG(" UTF8 string: " << str.toUtf8()); QString line = str; //replace \l(...) and %(...) tags QRegExp rxline("\\\\\\s*l\\s*\\(\\s*\\d+\\s*\\)"); // QRegExp rxcol("\\%\\(\\d+\\)"); int pos = rxline.indexIn(line); while (pos > -1) { QString value = rxline.cap(0); int len = value.length(); value.replace(QRegExp(" "),""); value = "\\c{" + value.mid(3, value.length()-4) + '}'; line.replace(pos, len, value); pos = rxline.indexIn(line); } // replace umlauts etc. - // TODO: probably missed some. Is there any generic method? - line.replace("ä", "ä"); - line.replace("ö", "ö"); - line.replace("ü", "ü"); - line.replace("Ä", "Ä"); - line.replace("Ö", "Ö"); - line.replace("Ü", "Ü"); - line.replace("ß", "ß"); - line.replace("€", "€"); - line.replace("£", "£"); - line.replace("¥", "¥"); - line.replace("§", "§"); - line.replace("µ", "µ"); - line.replace("¹", "¹"); - line.replace("²", "²"); - line.replace("³", "³"); - line.replace("¶", "¶"); - line.replace("ø", "ø"); - line.replace("æ", "æ"); - line.replace("ð", "ð"); - line.replace("ħ", "ℏ"); - line.replace("ĸ", "κ"); - line.replace("¢", "¢"); - line.replace("¼", "¼"); - line.replace("½", "½"); - line.replace("¾", "¾"); - line.replace("¬", "¬"); - line.replace("©", "©"); - line.replace("±", "±"); - line.replace("¿", "¿"); - line.replace("×", "×"); - line.replace("°", "°"); + line = replaceSpecialChars(line); // replace tabs (not really supported) line.replace("\t", "        "); //Lookbehind conditions are not supported - so need to reverse string QRegExp rx("\\)[^\\)\\(]*\\((?!\\s*[buig\\+\\-]\\s*\\\\)"); QRegExp rxfont("\\)[^\\)\\(]*\\((?![^\\:]*\\:f\\s*\\\\)"); QString linerev = strreverse(line); QString lBracket = strreverse("&lbracket;"); QString rBracket = strreverse("&rbracket;"); QString ltagBracket = strreverse("<agbracket;"); QString rtagBracket = strreverse("&rtagbracket;"); int pos1 = rx.indexIn(linerev); int pos2 = rxfont.indexIn(linerev); while (pos1 > -1 || pos2 > -1) { if (pos1 == pos2) { QString value = rx.cap(0); int len = value.length(); value = rBracket + value.mid(1, len-2) + lBracket; linerev.replace(pos1, len, value); } else if ((pos1 > pos2 && pos2 != -1) || pos1 == -1) { QString value = rxfont.cap(0); int len = value.length(); value = rtagBracket + value.mid(1, len-2) + ltagBracket; linerev.replace(pos2, len, value); } else if ((pos2 > pos1 && pos1 != -1) || pos2 == -1) { QString value = rx.cap(0); int len = value.length(); value = rtagBracket + value.mid(1, len-2) + ltagBracket; linerev.replace(pos1, len, value); } pos1 = rx.indexIn(linerev); pos2 = rxfont.indexIn(linerev); } linerev.replace(ltagBracket, "("); linerev.replace(rtagBracket, ")"); line = strreverse(linerev); //replace \b(...), \i(...), \u(...), \g(...), \+(...), \-(...), \f:font(...) tags const QString rxstr[] = { "\\\\\\s*b\\s*\\(", "\\\\\\s*i\\s*\\(", "\\\\\\s*u\\s*\\(", "\\\\\\s*g\\s*\\(", "\\\\\\s*\\+\\s*\\(", "\\\\\\s*\\-\\s*\\(", "\\\\\\s*f\\:[^\\(]*\\("}; int postag[] = {0, 0, 0, 0, 0, 0, 0}; QString ltag[] = {"","","","","","",""}; QString rtag[] = {"","","","","","",""}; QRegExp rxtags[7]; for (int i = 0; i < 7; ++i) rxtags[i].setPattern(rxstr[i]+"[^\\(\\)]*\\)"); bool flag = true; while (flag) { for (int i = 0; i < 7; ++i) { postag[i] = rxtags[i].indexIn(line); while (postag[i] > -1) { QString value = rxtags[i].cap(0); int len = value.length(); pos2 = value.indexOf("("); if (i < 6) value = ltag[i] + value.mid(pos2+1, len-pos2-2) + rtag[i]; else { int posfont = value.indexOf("f:"); value = ltag[i].arg(value.mid(posfont+2, pos2-posfont-2)) + value.mid(pos2+1, len-pos2-2) + rtag[i]; } line.replace(postag[i], len, value); postag[i] = rxtags[i].indexIn(line); } } flag = false; for (int i = 0; i < 7; ++i) { if (rxtags[i].indexIn(line) > -1) { flag = true; break; } } } //replace unclosed tags for (int i = 0; i < 6; ++i) line.replace(QRegExp(rxstr[i]), ltag[i]); rxfont.setPattern(rxstr[6]); pos = rxfont.indexIn(line); while (pos > -1) { QString value = rxfont.cap(0); int len = value.length(); int posfont = value.indexOf("f:"); value = ltag[6].arg(value.mid(posfont+2, len-posfont-3)); line.replace(pos, len, value); pos = rxfont.indexIn(line); } line.replace("&lbracket;", "("); line.replace("&rbracket;", ")"); // special characters QRegExp rxs("\\\\\\((\\d+)\\)"); line.replace(rxs, "&#\\1;"); DEBUG(" result: " << line.toStdString()); return line; } diff --git a/src/backend/datasources/projects/OriginProjectParser.h b/src/backend/datasources/projects/OriginProjectParser.h index 8f679111d..475f8ea2a 100644 --- a/src/backend/datasources/projects/OriginProjectParser.h +++ b/src/backend/datasources/projects/OriginProjectParser.h @@ -1,93 +1,96 @@ /*************************************************************************** File : OriginProjectParser.h Project : LabPlot Description : parser for Origin projects -------------------------------------------------------------------- Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 ORIGINPROJECTPARSER_H #define ORIGINPROJECTPARSER_H #include "backend/worksheet/plots/PlotArea.h" #include "backend/datasources/projects/ProjectParser.h" #include class Axis; class Column; class Project; class Workbook; class Spreadsheet; class Matrix; class Worksheet; class Note; class XYCurve; class OriginProjectParser : public ProjectParser { Q_OBJECT public: OriginProjectParser(); static bool isOriginProject(const QString& fileName); static QString supportedExtensions(); void setImportUnusedObjects(bool); bool hasUnusedObjects(); private: bool loadFolder(Folder*, tree::iterator, bool preview); bool loadWorkbook(Workbook*, bool preview); bool loadSpreadsheet(Spreadsheet*, bool preview, const QString& wbName = QString(), int sheetIndex = -1); void loadColumnNumericFormat(const Origin::SpreadColumn& originColumn, Column* column) const; bool loadMatrixWorkbook(Workbook*, bool preview); bool loadMatrix(Matrix*, bool preview, size_t sheetIndex = 0, const QString& mwbName = QString()); bool loadWorksheet(Worksheet*, bool preview); void loadAxis(const Origin::GraphAxis&, Axis*, int index, const QString& axisTitle = QString()) const; void loadCurve(const Origin::GraphCurve&, XYCurve*) const; bool loadNote(Note*, bool preview); void handleLooseWindows(Folder*, bool preview); unsigned int findSpreadByName(const QString&); unsigned int findMatrixByName(const QString&); unsigned int findExcelByName(const QString&); unsigned int findGraphByName(const QString&); unsigned int findNoteByName(const QString&); QString parseOriginText(const QString&) const; QString parseOriginTags(const QString&) const; QDateTime creationTime(tree::iterator) const; QColor color(Origin::Color) const; PlotArea::BackgroundColorStyle backgroundColorStyle(Origin::ColorGradientDirection) const; + QList> charReplacementList() const; + QString replaceSpecialChars(QString text) const; + OriginFile* m_originFile; QStringList m_spreadNameList; QStringList m_excelNameList; QStringList m_matrixNameList; QStringList m_graphNameList; QStringList m_noteNameList; bool m_importUnusedObjects; protected: bool load(Project*, bool) override; }; #endif // ORIGINPROJECTPARSER_H diff --git a/src/backend/datasources/projects/ProjectParser.cpp b/src/backend/datasources/projects/ProjectParser.cpp index b9d6bb7bc..6ce23742c 100644 --- a/src/backend/datasources/projects/ProjectParser.cpp +++ b/src/backend/datasources/projects/ProjectParser.cpp @@ -1,164 +1,164 @@ /*************************************************************************** File : ProjectParser.h Project : LabPlot Description : base class for project parsers -------------------------------------------------------------------- Copyright : (C) 2017 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 "ProjectParser.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/lib/trace.h" -#include +#include /*! \class ProjectParser \brief base class for project parsers \ingroup datasources */ ProjectParser::ProjectParser() : QObject(), m_project(nullptr) { } ProjectParser::~ProjectParser() { if (m_project != nullptr) delete m_project; } void ProjectParser::setProjectFileName(const QString& name) { m_projectFileName = name; //delete the previous project object if (m_project) { delete m_project; m_project = nullptr; } } const QString& ProjectParser::projectFileName() const { return m_projectFileName; } QList ProjectParser::topLevelClasses() const { return m_topLevelClasses; } QAbstractItemModel* ProjectParser::model() { WAIT_CURSOR; PERFTRACE("project model for preview created"); if (!m_project) m_project = new Project(); AspectTreeModel* model = nullptr; bool rc = load(m_project, true); if (rc) { model = new AspectTreeModel(m_project); model->setReadOnly(true); } RESET_CURSOR; return model; } void ProjectParser::importTo(Folder* targetFolder, const QStringList& selectedPathes) { QDEBUG("Starting the import of " + m_projectFileName); //import the selected objects into a temporary project Project* project = new Project(); project->setPathesToLoad(selectedPathes); load(project, false); //determine the first child of the last top level child in the list of the imported objects //we want to navigate to in the project explorer after the import AbstractAspect* lastTopLevelChild = project->child(project->childCount()-1); AbstractAspect* childToNavigate = nullptr; if (lastTopLevelChild->childCount() > 0) { childToNavigate = lastTopLevelChild->child(0); //we don't want to select columns, select rather their parent spreadsheet if (dynamic_cast(childToNavigate)) childToNavigate = lastTopLevelChild; } else { childToNavigate = lastTopLevelChild; } //move all children from the temp project to the target folder - targetFolder->beginMacro(i18n("%1: Import from %2").arg(targetFolder->name(), m_projectFileName)); + targetFolder->beginMacro(i18n("%1: Import from %2", targetFolder->name(), m_projectFileName)); for (auto* child : project->children()) { Folder* folder = dynamic_cast(child); if (folder) { moveFolder(targetFolder, folder); } else { project->removeChild(child); //remove the object to be imported in the target folder if it's already existing AbstractAspect* targetChild = targetFolder->child(child->name()); if (targetChild) targetFolder->removeChild(targetChild); targetFolder->addChild(child); } } targetFolder->endMacro(); delete project; targetFolder->project()->navigateTo(childToNavigate->path()); QDEBUG("Import of " + m_projectFileName + " done."); } /* * moved \c sourceChildFolderToMove from its parten folder to \c targetParentFolder * keeping (not overwriting ) the sub-folder structure. */ void ProjectParser::moveFolder(Folder* targetParentFolder, Folder* sourceChildFolderToMove) const { Folder* targetChildFolder = targetParentFolder->child(sourceChildFolderToMove->name()); if (targetChildFolder) { //folder exists already in the target parent folder, //-> recursively move its children from source into target parent folder for (auto* child : sourceChildFolderToMove->children()) { Folder* folder = dynamic_cast(child); if (folder) { moveFolder(targetChildFolder, folder); } else { sourceChildFolderToMove->removeChild(child); //remove the object to be imported in the target folder if it's already existing AbstractAspect* targetChild = targetChildFolder->child(child->name()); if (targetChild) targetChildFolder->removeChild(targetChild); targetChildFolder->addChild(child); } } } else { //folder doesn't exist yet in the target parent folder -> simply move it Folder* sourceParentFolder = dynamic_cast(sourceChildFolderToMove->parentAspect()); sourceParentFolder->removeChild(sourceChildFolderToMove); targetParentFolder->addChild(sourceChildFolderToMove); } } diff --git a/src/backend/gsl/ExpressionParser.cpp b/src/backend/gsl/ExpressionParser.cpp index 4f24c19e7..ea839da73 100644 --- a/src/backend/gsl/ExpressionParser.cpp +++ b/src/backend/gsl/ExpressionParser.cpp @@ -1,1581 +1,1586 @@ /*************************************************************************** File : ExpressionParser.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2014-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Description : C++ wrapper for the bison generated parser. ***************************************************************************/ /*************************************************************************** * * * 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 "backend/lib/macros.h" #include "backend/gsl/ExpressionParser.h" -#include +#include #include #include extern "C" { #include #include #include #include #include #include "backend/gsl/parser.h" } ExpressionParser* ExpressionParser::instance = NULL; ExpressionParser::ExpressionParser() { init_table(); initFunctions(); initConstants(); } void ExpressionParser::initFunctions() { //functions (sync with functions.h!) for (int i = 0; _functions[i].name != 0; i++) m_functions << _functions[i].name; m_functionsGroups << i18n("Standard Mathematical functions"); //http://www.gnu.org/software/gsl/manual/html_node/Special-Functions.html m_functionsGroups << i18n("Airy Functions and Derivatives"); m_functionsGroups << i18n("Bessel Functions"); m_functionsGroups << i18n("Clausen Functions"); m_functionsGroups << i18n("Coulomb Functions"); // m_functionsGroups << i18n("Coupling Coefficients"); m_functionsGroups << i18n("Dawson Function"); m_functionsGroups << i18n("Debye Functions"); m_functionsGroups << i18n("Dilogarithm"); // m_functionsGroups << i18n("Elementary Operations"); m_functionsGroups << i18n("Elliptic Integrals"); // m_functionsGroups << i18n("Elliptic Functions (Jacobi)"); #ifndef _MSC_VER m_functionsGroups << i18n("Error Functions and Related Functions"); #else m_functionsGroups << i18n("Error Functions"); #endif m_functionsGroups << i18n("Exponential Functions"); m_functionsGroups << i18n("Exponential Integrals"); m_functionsGroups << i18n("Fermi-Dirac Function"); m_functionsGroups << i18n("Gamma and Beta Functions"); m_functionsGroups << i18n("Gegenbauer Functions"); #if (GSL_MAJOR_VERSION > 2) || (GSL_MAJOR_VERSION == 2) && (GSL_MINOR_VERSION >= 4) m_functionsGroups << i18n("Hermite Polynomials and Functions"); #endif m_functionsGroups << i18n("Hypergeometric Functions"); m_functionsGroups << i18n("Laguerre Functions"); m_functionsGroups << i18n("Lambert W Functions"); m_functionsGroups << i18n("Legendre Functions and Spherical Harmonics"); m_functionsGroups << i18n("Logarithm and Related Functions"); // m_functionsGroups << i18n("Mathieu Functions"); m_functionsGroups << i18n("Power Function"); m_functionsGroups << i18n("Psi (Digamma) Function"); m_functionsGroups << i18n("Synchrotron Functions"); m_functionsGroups << i18n("Transport Functions"); m_functionsGroups << i18n("Trigonometric Functions"); m_functionsGroups << i18n("Zeta Functions"); // GSL random distribution functions m_functionsGroups << i18n("Gaussian Distribution"); m_functionsGroups << i18n("Exponential Distribution"); m_functionsGroups << i18n("Laplace Distribution"); m_functionsGroups << i18n("Exponential Power Distribution"); m_functionsGroups << i18n("Cauchy Distribution"); m_functionsGroups << i18n("Rayleigh Distribution"); m_functionsGroups << i18n("Landau Distribution"); m_functionsGroups << i18n("Gamma Distribution"); m_functionsGroups << i18n("Flat (Uniform) Distribution"); m_functionsGroups << i18n("Lognormal Distribution"); m_functionsGroups << i18n("Chi-squared Distribution"); m_functionsGroups << i18n("F-distribution"); m_functionsGroups << i18n("t-distribution"); m_functionsGroups << i18n("Beta Distribution"); m_functionsGroups << i18n("Logistic Distribution"); m_functionsGroups << i18n("Pareto Distribution"); m_functionsGroups << i18n("Weibull Distribution"); m_functionsGroups << i18n("Gumbel Distribution"); m_functionsGroups << i18n("Poisson Distribution"); m_functionsGroups << i18n("Bernoulli Distribution"); m_functionsGroups << i18n("Binomial Distribution"); m_functionsGroups << i18n("Pascal Distribution"); m_functionsGroups << i18n("Geometric Distribution"); m_functionsGroups << i18n("Hypergeometric Distribution"); m_functionsGroups << i18n("Logarithmic Distribution"); int index = 0; // Standard mathematical functions m_functionsNames << i18n("pseudo-random integer [0,RAND_MAX]"); m_functionsNames << i18n("nonlinear additive feedback rng [0,RAND_MAX]"); m_functionsNames << i18n("nonlinear additive feedback rng [0,1]"); m_functionsNames << i18n("Smallest integral value not less"); m_functionsNames << i18n("Absolute value"); m_functionsNames << i18n("Base 10 logarithm"); m_functionsNames << i18n("Power function [x^y]"); m_functionsNames << i18n("Nonnegative square root"); m_functionsNames << i18n("Sign function"); m_functionsNames << i18n("Heavyside theta function"); m_functionsNames << i18n("Harmonic number function"); #ifndef HAVE_WINDOWS m_functionsNames << i18n("Cube root"); m_functionsNames << i18n("Extract the exponent"); m_functionsNames << i18n("Round to an integer value"); m_functionsNames << i18n("Round to the nearest integer"); m_functionsNames << i18n("Round to the nearest integer"); #endif m_functionsNames << QString("log(1+x)"); m_functionsNames << QString("x * 2^e"); m_functionsNames << QString("x^n"); m_functionsNames << QString("x^2"); m_functionsNames << QString("x^3"); m_functionsNames << QString("x^4"); m_functionsNames << QString("x^5"); m_functionsNames << QString("x^6"); m_functionsNames << QString("x^7"); m_functionsNames << QString("x^8"); m_functionsNames << QString("x^9"); #ifndef HAVE_WINDOWS for (int i = 0; i < 27; i++) #else for (int i = 0; i < 22; i++) #endif m_functionsGroupIndex << index; // Airy Functions and Derivatives m_functionsNames << i18n("Airy function of the first kind"); m_functionsNames << i18n("Airy function of the second kind"); m_functionsNames << i18n("Scaled Airy function of the first kind"); m_functionsNames << i18n("Scaled Airy function of the second kind"); m_functionsNames << i18n("Airy function derivative of the first kind"); m_functionsNames << i18n("Airy function derivative of the second kind"); m_functionsNames << i18n("Scaled Airy function derivative of the first kind"); m_functionsNames << i18n("Scaled Airy function derivative of the second kind"); m_functionsNames << i18n("n-th zero of the Airy function of the first kind"); m_functionsNames << i18n("n-th zero of the Airy function of the second kind"); m_functionsNames << i18n("n-th zero of the Airy function derivative of the first kind"); m_functionsNames << i18n("n-th zero of the Airy function derivative of the second kind"); index++; for (int i = 0; i < 12; i++) m_functionsGroupIndex << index; // Bessel Functions m_functionsNames << i18n("Regular cylindrical Bessel function of zeroth order"); m_functionsNames << i18n("Regular cylindrical Bessel function of first order"); m_functionsNames << i18n("Regular cylindrical Bessel function of order n"); m_functionsNames << i18n("Irregular cylindrical Bessel function of zeroth order"); m_functionsNames << i18n("Irregular cylindrical Bessel function of first order"); m_functionsNames << i18n("Irregular cylindrical Bessel function of order n"); m_functionsNames << i18n("Regular modified cylindrical Bessel function of zeroth order"); m_functionsNames << i18n("Regular modified cylindrical Bessel function of first order"); m_functionsNames << i18n("Regular modified cylindrical Bessel function of order n"); m_functionsNames << i18n("Scaled regular modified cylindrical Bessel function of zeroth order exp(-|x|) I0(x)"); m_functionsNames << i18n("Scaled regular modified cylindrical Bessel function of first order exp(-|x|) I1(x)"); m_functionsNames << i18n("Scaled regular modified cylindrical Bessel function of order n exp(-|x|) In(x)"); m_functionsNames << i18n("Irregular modified cylindrical Bessel function of zeroth order"); m_functionsNames << i18n("Irregular modified cylindrical Bessel function of first order"); m_functionsNames << i18n("Irregular modified cylindrical Bessel function of order n"); m_functionsNames << i18n("Scaled irregular modified cylindrical Bessel function of zeroth order exp(x) K0(x)"); m_functionsNames << i18n("Scaled irregular modified cylindrical Bessel function of first order exp(x) K1(x)"); m_functionsNames << i18n("Scaled irregular modified cylindrical Bessel function of order n exp(x) Kn(x)"); m_functionsNames << i18n("Regular spherical Bessel function of zeroth order"); m_functionsNames << i18n("Regular spherical Bessel function of first order"); m_functionsNames << i18n("Regular spherical Bessel function of second order"); m_functionsNames << i18n("Regular spherical Bessel function of order l"); m_functionsNames << i18n("Irregular spherical Bessel function of zeroth order"); m_functionsNames << i18n("Irregular spherical Bessel function of first order"); m_functionsNames << i18n("Irregular spherical Bessel function of second order"); m_functionsNames << i18n("Irregular spherical Bessel function of order l"); m_functionsNames << i18n("Scaled regular modified spherical Bessel function of zeroth order, exp(-|x|) i0(x)"); m_functionsNames << i18n("Scaled regular modified spherical Bessel function of first order, exp(-|x|) i1(x)"); m_functionsNames << i18n("Scaled regular modified spherical Bessel function of second order, exp(-|x|) i2(x)"); m_functionsNames << i18n("Scaled regular modified spherical Bessel function of order l, exp(-|x|) il(x)"); m_functionsNames << i18n("Scaled irregular modified spherical Bessel function of zeroth order, exp(x) k0(x)"); m_functionsNames << i18n("Scaled irregular modified spherical Bessel function of first order, exp(-|x|) k1(x)"); m_functionsNames << i18n("Scaled irregular modified spherical Bessel function of second order, exp(-|x|) k2(x)"); m_functionsNames << i18n("Scaled irregular modified spherical Bessel function of order l, exp(-|x|) kl(x)"); m_functionsNames << i18n("Regular cylindrical Bessel function of fractional order"); m_functionsNames << i18n("Irregular cylindrical Bessel function of fractional order"); m_functionsNames << i18n("Regular modified Bessel function of fractional order"); m_functionsNames << i18n("Scaled regular modified Bessel function of fractional order"); m_functionsNames << i18n("Irregular modified Bessel function of fractional order"); m_functionsNames << i18n("Logarithm of irregular modified Bessel function of fractional order"); m_functionsNames << i18n("Scaled irregular modified Bessel function of fractional order"); m_functionsNames << i18n("n-th positive zero of the Bessel function J0"); m_functionsNames << i18n("n-th positive zero of the Bessel function J1"); m_functionsNames << i18n("n-th positive zero of the Bessel function Jnu"); index++; for (int i = 0; i < 44; i++) m_functionsGroupIndex << index; // Clausen Functions m_functionsNames << i18n("Clausen function"); index++; m_functionsGroupIndex << index; // Coulomb Functions m_functionsNames << i18n("Lowest-order normalized hydrogenic bound state radial wavefunction"); m_functionsNames << i18n("n-th normalized hydrogenic bound state radial wavefunction"); index++; for (int i = 0; i < 2; i++) m_functionsGroupIndex << index; // Dawson Function m_functionsNames << i18n("Dawson integral"); index++; m_functionsGroupIndex << index; // Debye Functions m_functionsNames << i18n("First-order Debye function"); m_functionsNames << i18n("Second-order Debye function"); m_functionsNames << i18n("Third-order Debye function"); m_functionsNames << i18n("Fourth-order Debye function"); m_functionsNames << i18n("Fifth-order Debye function"); m_functionsNames << i18n("Sixth-order Debye function"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; // Dilogarithm m_functionsNames << i18n("Dilogarithm for a real argument"); index++; m_functionsGroupIndex << index; // Elliptic Integrals m_functionsNames << i18n("Legendre form of complete elliptic integral K"); m_functionsNames << i18n("Legendre form of complete elliptic integral E"); m_functionsNames << i18n("Legendre form of complete elliptic integral Pi"); m_functionsNames << i18n("Legendre form of incomplete elliptic integral F"); m_functionsNames << i18n("Legendre form of incomplete elliptic integral E"); m_functionsNames << i18n("Legendre form of incomplete elliptic integral P"); m_functionsNames << i18n("Legendre form of incomplete elliptic integral D"); m_functionsNames << i18n("Carlson form of incomplete elliptic integral RC"); m_functionsNames << i18n("Carlson form of incomplete elliptic integral RD"); m_functionsNames << i18n("Carlson form of incomplete elliptic integral RF"); m_functionsNames << i18n("Carlson form of incomplete elliptic integral RJ"); index++; for (int i = 0; i < 11; i++) m_functionsGroupIndex << index; // Error Functions m_functionsNames << i18n("Error function"); m_functionsNames << i18n("Complementary error function"); m_functionsNames << i18n("Logarithm of complementary error function"); m_functionsNames << i18n("Gaussian probability density function Z"); m_functionsNames << i18n("Upper tail of the Gaussian probability function Q"); m_functionsNames << i18n("Hazard function for the normal distribution Z/Q"); int count = 6; #ifndef _MSC_VER m_functionsNames << i18n("Underflow-compensating function exp(x^2) erfc(x) for real x"); m_functionsNames << i18n("Imaginary error function erfi(x) = -i erf(ix) for real x"); m_functionsNames << i18n("Imaginary part of Faddeeva's scaled complex error function w(x) = exp(-x^2) erfc(-ix) for real x"); m_functionsNames << i18n("Dawson's integral D(z) = sqrt(pi)/2 * exp(-z^2) * erfi(z)"); m_functionsNames << i18n("Voigt profile"); count += 5; #endif + m_functionsNames << i18n("Pseudo-Voigt profile (same width)"); + count += 1; index++; for (int i = 0; i < count; i++) m_functionsGroupIndex << index; // Exponential Functions m_functionsNames << i18n("Exponential function"); m_functionsNames << i18n("exponentiate x and multiply by y"); m_functionsNames << QString("exp(x) - 1"); m_functionsNames << QString("(exp(x)-1)/x"); m_functionsNames << QString("2(exp(x)-1-x)/x^2"); m_functionsNames << i18n("n-relative exponential"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; // Exponential Integrals m_functionsNames << i18n("Exponential integral"); m_functionsNames << i18n("Second order exponential integral"); m_functionsNames << i18n("Exponential integral of order n"); m_functionsNames << i18n("Exponential integral Ei"); m_functionsNames << i18n("Hyperbolic integral Shi"); m_functionsNames << i18n("Hyperbolic integral Chi"); m_functionsNames << i18n("Third-order exponential integral"); m_functionsNames << i18n("Sine integral"); m_functionsNames << i18n("Cosine integral"); m_functionsNames << i18n("Arctangent integral"); index++; for (int i = 0; i < 10; i++) m_functionsGroupIndex << index; // Fermi-Dirac Function m_functionsNames << i18n("Complete Fermi-Dirac integral with index -1"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index 0"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index 1"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index 2"); m_functionsNames << i18n("Complete Fermi-Dirac integral with integer index j"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index -1/2"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index 1/2"); m_functionsNames << i18n("Complete Fermi-Dirac integral with index 3/2"); m_functionsNames << i18n("Incomplete Fermi-Dirac integral with index zero"); index++; for (int i = 0; i < 9; i++) m_functionsGroupIndex << index; // Gamma and Beta Functions m_functionsNames << i18n("Gamma function"); m_functionsNames << i18n("Gamma function"); m_functionsNames << i18n("Logarithm of the gamma function"); m_functionsNames << i18n("Logarithm of the gamma function"); m_functionsNames << i18n("Regulated gamma function"); m_functionsNames << i18n("Reciprocal of the gamma function"); m_functionsNames << i18n("Factorial n!"); m_functionsNames << i18n("Double factorial n!!"); m_functionsNames << i18n("Logarithm of the factorial"); m_functionsNames << i18n("Logarithm of the double factorial"); m_functionsNames << i18n("Combinatorial factor"); m_functionsNames << i18n("Logarithm of the combinatorial factor"); m_functionsNames << i18n("Taylor coefficient"); m_functionsNames << i18n("Pochhammer symbol"); m_functionsNames << i18n("Logarithm of the Pochhammer symbol"); m_functionsNames << i18n("Relative Pochhammer symbol"); m_functionsNames << i18n("Unnormalized incomplete gamma function"); m_functionsNames << i18n("Normalized incomplete gamma function"); m_functionsNames << i18n("Complementary normalized incomplete gamma function"); m_functionsNames << i18n("Beta function"); m_functionsNames << i18n("Logarithm of the beta function"); m_functionsNames << i18n("Normalized incomplete beta function"); index++; for (int i = 0; i < 22; i++) m_functionsGroupIndex << index; // Gegenbauer Functions m_functionsNames << i18n("Gegenbauer polynomial C_1"); m_functionsNames << i18n("Gegenbauer polynomial C_2"); m_functionsNames << i18n("Gegenbauer polynomial C_3"); m_functionsNames << i18n("Gegenbauer polynomial C_n"); index++; for (int i = 0; i < 4; i++) m_functionsGroupIndex << index; #if (GSL_MAJOR_VERSION > 2) || (GSL_MAJOR_VERSION == 2) && (GSL_MINOR_VERSION >= 4) // Hermite Polynomials and Functions m_functionsNames << i18n("Hermite polynomials physicists version"); m_functionsNames << i18n("Hermite polynomials probabilists version"); m_functionsNames << i18n("Hermite functions"); m_functionsNames << i18n("Derivatives of Hermite polynomials physicists version"); m_functionsNames << i18n("Derivatives of Hermite polynomials probabilists version"); m_functionsNames << i18n("Derivatives of Hermite functions"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; #endif // Hypergeometric Functions m_functionsNames << i18n("Hypergeometric function 0F1"); m_functionsNames << i18n("Confluent hypergeometric function 1F1 for integer parameters"); m_functionsNames << i18n("Confluent hypergeometric function 1F1 for general parameters"); m_functionsNames << i18n("Confluent hypergeometric function U for integer parameters"); m_functionsNames << i18n("Confluent hypergeometric function U"); m_functionsNames << i18n("Gauss hypergeometric function 2F1"); m_functionsNames << i18n("Gauss hypergeometric function 2F1 with complex parameters"); m_functionsNames << i18n("Renormalized Gauss hypergeometric function 2F1"); m_functionsNames << i18n("Renormalized Gauss hypergeometric function 2F1 with complex parameters"); m_functionsNames << i18n("Hypergeometric function 2F0"); index++; for (int i = 0; i < 10; i++) m_functionsGroupIndex << index; // Laguerre Functions m_functionsNames << i18n("generalized Laguerre polynomials L_1"); m_functionsNames << i18n("generalized Laguerre polynomials L_2"); m_functionsNames << i18n("generalized Laguerre polynomials L_3"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Lambert W Functions m_functionsNames << i18n("Principal branch of the Lambert W function"); m_functionsNames << i18n("Secondary real-valued branch of the Lambert W function"); index++; for (int i = 0; i < 2; i++) m_functionsGroupIndex << index; // Legendre Functions and Spherical Harmonics m_functionsNames << i18n("Legendre polynomial P_1"); m_functionsNames << i18n("Legendre polynomial P_2"); m_functionsNames << i18n("Legendre polynomial P_3"); m_functionsNames << i18n("Legendre polynomial P_l"); m_functionsNames << i18n("Legendre function Q_0"); m_functionsNames << i18n("Legendre function Q_1"); m_functionsNames << i18n("Legendre function Q_l"); m_functionsNames << i18n("Associated Legendre polynomial"); m_functionsNames << i18n("Normalized associated Legendre polynomial"); m_functionsNames << i18n("Irregular spherical conical function P^1/2"); m_functionsNames << i18n("Regular spherical conical function P^(-1/2)"); m_functionsNames << i18n("Conical function P^0"); m_functionsNames << i18n("Conical function P^1"); m_functionsNames << i18n("Regular spherical conical function P^(-1/2-l)"); m_functionsNames << i18n("Regular cylindrical conical function P^(-m)"); m_functionsNames << i18n("Zeroth radial eigenfunction of the Laplacian on the 3-dimensional hyperbolic space"); m_functionsNames << i18n("First radial eigenfunction of the Laplacian on the 3-dimensional hyperbolic space"); m_functionsNames << i18n("l-th radial eigenfunction of the Laplacian on the 3-dimensional hyperbolic space"); index++; for (int i = 0; i < 18; i++) m_functionsGroupIndex << index; // Logarithm and Related Functions m_functionsNames << i18n("Logarithm"); m_functionsNames << i18n("Logarithm of the magnitude"); m_functionsNames << QString("log(1+x)"); m_functionsNames << QString("log(1+x) - x"); index++; for (int i = 0; i < 4; i++) m_functionsGroupIndex << index; // Power Function m_functionsNames << i18n("x^n for integer n with an error estimate"); index++; m_functionsGroupIndex << index; // Psi (Digamma) Function m_functionsNames << i18n("Digamma function for positive integer n"); m_functionsNames << i18n("Digamma function"); m_functionsNames << i18n("Real part of the digamma function on the line 1+i y"); m_functionsNames << i18n("Trigamma function psi' for positive integer n"); m_functionsNames << i18n("Trigamma function psi'"); m_functionsNames << i18n("Polygamma function psi^(n)"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; // Synchrotron Functions m_functionsNames << i18n("First synchrotron function"); m_functionsNames << i18n("Second synchrotron function"); index++; for (int i = 0; i < 2; i++) m_functionsGroupIndex << index; // Transport Functions m_functionsNames << i18n("Transport function"); m_functionsNames << i18n("Transport function"); m_functionsNames << i18n("Transport function"); m_functionsNames << i18n("Transport function"); index++; for (int i = 0; i < 4; i++) m_functionsGroupIndex << index; // Trigonometric Functions m_functionsNames << i18n("Sine"); m_functionsNames << i18n("Cosine"); m_functionsNames << i18n("Tangent"); m_functionsNames << i18n("Inverse sine"); m_functionsNames << i18n("Inverse cosine"); m_functionsNames << i18n("Inverse tangent"); m_functionsNames << i18n("Inverse tangent using sign"); m_functionsNames << i18n("Hyperbolic sine"); m_functionsNames << i18n("Hyperbolic cosine"); m_functionsNames << i18n("Hyperbolic tangent"); m_functionsNames << i18n("Inverse hyperbolic cosine"); m_functionsNames << i18n("Inverse hyperbolic sine"); m_functionsNames << i18n("Inverse hyperbolic tangent"); m_functionsNames << i18n("Secant"); m_functionsNames << i18n("Cosecant"); m_functionsNames << i18n("Cotangent"); m_functionsNames << i18n("Inverse secant"); m_functionsNames << i18n("Inverse cosecant"); m_functionsNames << i18n("Inverse cotangent"); m_functionsNames << i18n("Hyperbolic secant"); m_functionsNames << i18n("Hyperbolic cosecant"); m_functionsNames << i18n("Hyperbolic cotangent"); m_functionsNames << i18n("Inverse hyperbolic secant"); m_functionsNames << i18n("Inverse hyperbolic cosecant"); m_functionsNames << i18n("Inverse hyperbolic cotangent"); m_functionsNames << i18n("Sinc function sin(x)/x"); m_functionsNames << QString("log(sinh(x))"); m_functionsNames << QString("log(cosh(x))"); m_functionsNames << i18n("Hypotenuse function"); m_functionsNames << i18n("Three component hypotenuse function"); m_functionsNames << i18n("restrict to [-pi,pi]"); m_functionsNames << i18n("restrict to [0,2 pi]"); index++; for (int i = 0; i < 32; i++) m_functionsGroupIndex << index; // Zeta Functions m_functionsNames << i18n("Riemann zeta function for integer n"); m_functionsNames << i18n("Riemann zeta function"); m_functionsNames << i18n("zeta(n)-1 for integer n"); m_functionsNames << i18n("zeta(x)-1"); m_functionsNames << i18n("Hurwitz zeta function"); m_functionsNames << i18n("Eta function for integer n"); m_functionsNames << i18n("Eta function"); index++; for (int i = 0; i < 7; i++) m_functionsGroupIndex << index; // GSL Random Number Distributions: see http://www.gnu.org/software/gsl/manual/html_node/Random-Number-Distributions.html // Gaussian Distribution m_functionsNames << i18n("Probability density for a Gaussian distribution"); m_functionsNames << i18n("Probability density for a unit Gaussian distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); m_functionsNames << i18n("Cumulative unit distribution function P"); m_functionsNames << i18n("Cumulative unit distribution function Q"); m_functionsNames << i18n("Inverse cumulative unit distribution function P"); m_functionsNames << i18n("Inverse cumulative unit distribution function Q"); m_functionsNames << i18n("Probability density for Gaussian tail distribution"); m_functionsNames << i18n("Probability density for unit Gaussian tail distribution"); m_functionsNames << i18n("Probability density for a bivariate Gaussian distribution"); index++; for (int i = 0; i < 13; i++) m_functionsGroupIndex << index; // Exponential Distribution m_functionsNames << i18n("Probability density for an exponential distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Laplace Distribution m_functionsNames << i18n("Probability density for a Laplace distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Exponential Power Distribution m_functionsNames << i18n("Probability density for an exponential power distribution"); m_functionsNames << i18n("cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Cauchy Distribution m_functionsNames << i18n("Probability density for a Cauchy distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Rayleigh Distribution m_functionsNames << i18n("Probability density for a Rayleigh distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); m_functionsNames << i18n("Probability density for a Rayleigh tail distribution"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; // Landau Distribution m_functionsNames << i18n("Probability density for a Landau distribution"); index++; m_functionsGroupIndex << index; // Gamma Distribution m_functionsNames << i18n("Probability density for a gamma distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Flat (Uniform) Distribution m_functionsNames << i18n("Probability density for a uniform distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Lognormal Distribution m_functionsNames << i18n("Probability density for a lognormal distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Chi-squared Distribution m_functionsNames << i18n("Probability density for a chi squared distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // F-distribution m_functionsNames << i18n("Probability density for a F-distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // t-distribution m_functionsNames << i18n("Probability density for a t-distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Beta Distribution m_functionsNames << i18n("Probability density for a beta distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Logistic Distribution m_functionsNames << i18n("Probability density for a logistic distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Pareto Distribution m_functionsNames << i18n("Probability density for a Pareto distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Weibull Distribution m_functionsNames << i18n("Probability density for a Weibull distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 5; i++) m_functionsGroupIndex << index; // Gumbel Distribution m_functionsNames << i18n("Probability density for a Type-1 Gumbel distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); m_functionsNames << i18n("Probability density for a Type-2 Gumbel distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Inverse cumulative distribution function P"); m_functionsNames << i18n("Inverse cumulative distribution function Q"); index++; for (int i = 0; i < 10; i++) m_functionsGroupIndex << index; // Poisson Distribution m_functionsNames << i18n("Probability density for a Poisson distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Bernoulli Distribution m_functionsNames << i18n("Probability density for a Bernoulli distribution"); index++; m_functionsGroupIndex << index; // Binomial Distribution m_functionsNames << i18n("Probability density for a binomial distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); m_functionsNames << i18n("Probability density for a negative binomial distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 6; i++) m_functionsGroupIndex << index; // Pascal Distribution m_functionsNames << i18n("Probability density for a Pascal distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Geometric Distribution m_functionsNames << i18n("Probability density for a geometric distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Hypergeometric Distribution m_functionsNames << i18n("Probability density for a hypergeometric distribution"); m_functionsNames << i18n("Cumulative distribution function P"); m_functionsNames << i18n("Cumulative distribution function Q"); index++; for (int i = 0; i < 3; i++) m_functionsGroupIndex << index; // Logarithmic Distribution m_functionsNames << i18n("Probability density for a logarithmic distribution"); index++; m_functionsGroupIndex << index; } //TODO: decide whether we want to have i18n here in the backend part of the code void ExpressionParser::initConstants() { for (int i = 0; _constants[i].name != 0; i++) m_constants << _constants[i].name; //groups m_constantsGroups << i18n("Mathematical constants"); m_constantsGroups << i18n("Fundamental constants"); m_constantsGroups << i18n("Astronomy and Astrophysics"); m_constantsGroups << i18n("Atomic and Nuclear Physics"); m_constantsGroups << i18n("Measurement of Time"); m_constantsGroups << i18n("Imperial Units"); m_constantsGroups << i18n("Speed and Nautical Units"); m_constantsGroups << i18n("Printers Units"); m_constantsGroups << i18n("Volume, Area and Length"); m_constantsGroups << i18n("Mass and Weight"); m_constantsGroups << i18n("Thermal Energy and Power"); m_constantsGroups << i18n("Pressure"); m_constantsGroups << i18n("Viscosity"); m_constantsGroups << i18n("Light and Illumination"); m_constantsGroups << i18n("Radioactivity"); m_constantsGroups << i18n("Force and Energy"); //Mathematical constants - m_constantsNames << i18n("Euler constant"); + m_constantsNames << i18n("Base of exponentials"); m_constantsValues << QString::number(M_E,'g',15); m_constantsUnits << ""; m_constantsNames << i18n("Pi"); m_constantsValues << QString::number(M_PI,'g',15); m_constantsUnits << ""; + m_constantsNames << i18n("Euler's constant"); + m_constantsValues << QString::number(M_EULER,'g',15); + m_constantsUnits << ""; - for (int i = 0; i < 2; i++) + for (int i = 0; i < 3; i++) m_constantsGroupIndex << 0; //Fundamental constants m_constantsNames << i18n("Speed of light"); m_constantsValues << QString::number(GSL_CONST_MKSA_SPEED_OF_LIGHT,'g',15); m_constantsUnits << "m / s"; m_constantsNames << i18n("Vacuum permeability"); m_constantsValues << QString::number(GSL_CONST_MKSA_VACUUM_PERMEABILITY,'g',15); m_constantsUnits << "kg m / A^2 s^2"; m_constantsNames << i18n("Vacuum permittivity"); m_constantsValues << QString::number(GSL_CONST_MKSA_VACUUM_PERMITTIVITY,'g',15); m_constantsUnits << "A^2 s^4 / kg m^3"; m_constantsNames << i18n("Planck constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_PLANCKS_CONSTANT_H,'g',15); m_constantsUnits << "kg m^2 / s"; m_constantsNames << i18n("Reduced Planck constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_PLANCKS_CONSTANT_HBAR,'g',15); m_constantsUnits << "kg m^2 / s"; m_constantsNames << i18n("Avogadro constant"); m_constantsValues << QString::number(GSL_CONST_NUM_AVOGADRO,'g',15); m_constantsUnits << "1 / mol"; m_constantsNames << i18n("Faraday"); m_constantsValues << QString::number(GSL_CONST_MKSA_FARADAY,'g',15); m_constantsUnits << "A s / mol"; m_constantsNames << i18n("Boltzmann constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_BOLTZMANN,'g',15); m_constantsUnits << "kg m^2 / K s^2"; m_constantsNames << i18n("Molar gas"); m_constantsValues << QString::number(GSL_CONST_MKSA_MOLAR_GAS,'g',15); m_constantsUnits << "kg m^2 / K mol s^2"; m_constantsNames << i18n("Standard gas volume"); m_constantsValues << QString::number(GSL_CONST_MKSA_STANDARD_GAS_VOLUME,'g',15); m_constantsUnits << "m^3 / mol"; m_constantsNames << i18n("Stefan-Boltzmann constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_STEFAN_BOLTZMANN_CONSTANT,'g',15); m_constantsUnits << "kg / K^4 s^3"; m_constantsNames << i18n("Gauss"); m_constantsValues << QString::number(GSL_CONST_MKSA_GAUSS,'g',15); m_constantsUnits << "kg / A s^2"; for (int i = 0; i < 12; i++) m_constantsGroupIndex << 1; // Astronomy and Astrophysics m_constantsNames << i18n("Astronomical unit"); m_constantsValues << QString::number(GSL_CONST_MKSA_ASTRONOMICAL_UNIT,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Gravitational constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_GRAVITATIONAL_CONSTANT,'g',15); m_constantsUnits << "m^3 / kg s^2"; m_constantsNames << i18n("Light year"); m_constantsValues << QString::number(GSL_CONST_MKSA_LIGHT_YEAR,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Parsec"); m_constantsValues << QString::number(GSL_CONST_MKSA_PARSEC,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Gravitational acceleration"); m_constantsValues << QString::number(GSL_CONST_MKSA_GRAV_ACCEL,'g',15); m_constantsUnits << "m / s^2"; m_constantsNames << i18n("Solar mass"); m_constantsValues << QString::number(GSL_CONST_MKSA_SOLAR_MASS,'g',15); m_constantsUnits << "kg"; for (int i = 0; i < 6; i++) m_constantsGroupIndex << 2; // Atomic and Nuclear Physics; m_constantsNames << i18n("Charge of the electron"); m_constantsValues << QString::number(GSL_CONST_MKSA_ELECTRON_CHARGE,'g',15); m_constantsUnits << "A s"; m_constantsNames << i18n("Energy of 1 electron volt"); m_constantsValues << QString::number(GSL_CONST_MKSA_ELECTRON_VOLT,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Unified atomic mass"); m_constantsValues << QString::number(GSL_CONST_MKSA_UNIFIED_ATOMIC_MASS,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of the electron"); m_constantsValues << QString::number(GSL_CONST_MKSA_MASS_ELECTRON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of the muon"); m_constantsValues << QString::number(GSL_CONST_MKSA_MASS_MUON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of the proton"); m_constantsValues << QString::number(GSL_CONST_MKSA_MASS_PROTON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of the neutron"); m_constantsValues << QString::number(GSL_CONST_MKSA_MASS_NEUTRON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Electromagnetic fine structure constant"); m_constantsValues << QString::number(GSL_CONST_NUM_FINE_STRUCTURE,'g',15); m_constantsUnits << ""; m_constantsNames << i18n("Rydberg constant"); m_constantsValues << QString::number(GSL_CONST_MKSA_RYDBERG,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Bohr radius"); m_constantsValues << QString::number(GSL_CONST_MKSA_BOHR_RADIUS,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1 angstrom"); m_constantsValues << QString::number(GSL_CONST_MKSA_ANGSTROM,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Area of 1 barn"); m_constantsValues << QString::number(GSL_CONST_MKSA_BARN,'g',15); m_constantsUnits << "m^2"; m_constantsNames << i18n("Bohr Magneton"); m_constantsValues << QString::number(GSL_CONST_MKSA_BOHR_MAGNETON,'g',15); m_constantsUnits << "A m^2"; m_constantsNames << i18n("Nuclear Magneton"); m_constantsValues << QString::number(GSL_CONST_MKSA_NUCLEAR_MAGNETON,'g',15); m_constantsUnits << "A m^2"; m_constantsNames << i18n("Magnetic moment of the electron [absolute value]"); m_constantsValues << QString::number(GSL_CONST_MKSA_ELECTRON_MAGNETIC_MOMENT,'g',15); m_constantsUnits << "A m^2"; m_constantsNames << i18n("Magnetic moment of the proton"); m_constantsValues << QString::number(GSL_CONST_MKSA_PROTON_MAGNETIC_MOMENT,'g',15); m_constantsUnits << "A m^2"; m_constantsNames << i18n("Thomson cross section"); m_constantsValues << QString::number(GSL_CONST_MKSA_THOMSON_CROSS_SECTION,'g',15); m_constantsUnits << "m^2"; m_constantsNames << i18n("Electric dipole moment of 1 Debye"); m_constantsValues << QString::number(GSL_CONST_MKSA_DEBYE,'g',15); m_constantsUnits << "A s^2 / m^2"; for (int i = 0; i < 18; i++) m_constantsGroupIndex << 3; // Measurement of Time m_constantsNames << i18n("Number of seconds in 1 minute"); m_constantsValues << QString::number(GSL_CONST_MKSA_MINUTE,'g',15); m_constantsUnits << "s"; m_constantsNames << i18n("Number of seconds in 1 hour"); m_constantsValues << QString::number(GSL_CONST_MKSA_HOUR,'g',15); m_constantsUnits << "s"; m_constantsNames << i18n("Number of seconds in 1 day"); m_constantsValues << QString::number(GSL_CONST_MKSA_DAY,'g',15); m_constantsUnits << "s"; m_constantsNames << i18n("Number of seconds in 1 week"); m_constantsValues << QString::number(GSL_CONST_MKSA_WEEK,'g',15); m_constantsUnits << "s"; for (int i = 0; i < 4; i++) m_constantsGroupIndex << 4; // Imperial Units m_constantsNames << i18n("Length of 1 inch"); m_constantsValues << QString::number(GSL_CONST_MKSA_INCH,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1 foot"); m_constantsValues << QString::number(GSL_CONST_MKSA_FOOT,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1 yard"); m_constantsValues << QString::number(GSL_CONST_MKSA_YARD,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1 mile"); m_constantsValues << QString::number(GSL_CONST_MKSA_MILE,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1/1000th of an inch"); m_constantsValues << QString::number(GSL_CONST_MKSA_MIL,'g',15); m_constantsUnits << "m"; for (int i = 0; i < 5; i++) m_constantsGroupIndex << 5; // Speed and Nautical Units m_constantsNames << i18n("Speed of 1 kilometer per hour"); m_constantsValues << QString::number(GSL_CONST_MKSA_KILOMETERS_PER_HOUR,'g',15); m_constantsUnits << "m / s"; m_constantsNames << i18n("Speed of 1 mile per hour"); m_constantsValues << QString::number(GSL_CONST_MKSA_MILES_PER_HOUR,'g',15); m_constantsUnits << "m / s"; m_constantsNames << i18n("Length of 1 nautical mile"); m_constantsValues << QString::number(GSL_CONST_MKSA_NAUTICAL_MILE,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Length of 1 fathom"); m_constantsValues << QString::number(GSL_CONST_MKSA_FATHOM,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Speed of 1 knot"); m_constantsValues << QString::number(GSL_CONST_MKSA_KNOT,'g',15); m_constantsUnits << "m / s"; for (int i = 0; i < 5; i++) m_constantsGroupIndex << 6; // Printers Units m_constantsNames << i18n("length of 1 printer's point [1/72 inch]"); m_constantsValues << QString::number(GSL_CONST_MKSA_POINT,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("length of 1 TeX point [1/72.27 inch]"); m_constantsValues << QString::number(GSL_CONST_MKSA_TEXPOINT,'g',15); m_constantsUnits << "m"; for (int i = 0; i < 2; i++) m_constantsGroupIndex << 7; // Volume, Area and Length m_constantsNames << i18n("Length of 1 micron"); m_constantsValues << QString::number(GSL_CONST_MKSA_MICRON,'g',15); m_constantsUnits << "m"; m_constantsNames << i18n("Area of 1 hectare"); m_constantsValues << QString::number(GSL_CONST_MKSA_HECTARE,'g',15); m_constantsUnits << "m^2"; m_constantsNames << i18n("Area of 1 acre"); m_constantsValues << QString::number(GSL_CONST_MKSA_ACRE,'g',15); m_constantsUnits << "m^2"; m_constantsNames << i18n("Volume of 1 liter"); m_constantsValues << QString::number(GSL_CONST_MKSA_LITER,'g',15); m_constantsUnits << "m^3"; m_constantsNames << i18n("Volume of 1 US gallon"); m_constantsValues << QString::number(GSL_CONST_MKSA_US_GALLON,'g',15); m_constantsUnits << "m^3"; m_constantsNames << i18n("Volume of 1 Canadian gallon"); m_constantsValues << QString::number(GSL_CONST_MKSA_CANADIAN_GALLON,'g',15); m_constantsUnits << "m^3"; m_constantsNames << i18n("Volume of 1 UK gallon"); m_constantsValues << QString::number(GSL_CONST_MKSA_UK_GALLON,'g',15); m_constantsUnits << "m^3"; m_constantsNames << i18n("Volume of 1 quart"); m_constantsValues << QString::number(GSL_CONST_MKSA_QUART,'g',15); m_constantsUnits << "m^3"; m_constantsNames << i18n("Volume of 1 pint"); m_constantsValues << QString::number(GSL_CONST_MKSA_PINT,'g',15); m_constantsUnits << "m^3"; for (int i = 0; i < 9; i++) m_constantsGroupIndex << 8; // Mass and Weight m_constantsNames << i18n("Mass of 1 pound"); m_constantsValues << QString::number(GSL_CONST_MKSA_POUND_MASS,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 ounce"); m_constantsValues << QString::number(GSL_CONST_MKSA_OUNCE_MASS,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 ton"); m_constantsValues << QString::number(GSL_CONST_MKSA_TON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 metric ton [1000 kg]"); m_constantsValues << QString::number(GSL_CONST_MKSA_METRIC_TON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 UK ton"); m_constantsValues << QString::number(GSL_CONST_MKSA_UK_TON,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 troy ounce"); m_constantsValues << QString::number(GSL_CONST_MKSA_TROY_OUNCE,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Mass of 1 carat"); m_constantsValues << QString::number(GSL_CONST_MKSA_CARAT,'g',15); m_constantsUnits << "kg"; m_constantsNames << i18n("Force of 1 gram weight"); m_constantsValues << QString::number(GSL_CONST_MKSA_GRAM_FORCE,'g',15); m_constantsUnits << "kg m / s^2"; m_constantsNames << i18n("Force of 1 pound weight"); m_constantsValues << QString::number(GSL_CONST_MKSA_POUND_FORCE,'g',15); m_constantsUnits << "kg m / s^2"; m_constantsNames << i18n("Force of 1 kilopound weight"); m_constantsValues << QString::number(GSL_CONST_MKSA_KILOPOUND_FORCE,'g',15); m_constantsUnits << "kg m / s^2"; m_constantsNames << i18n("Force of 1 poundal"); m_constantsValues << QString::number(GSL_CONST_MKSA_POUNDAL,'g',15); m_constantsUnits << "kg m / s^2"; for (int i = 0; i < 11; i++) m_constantsGroupIndex << 9; // Thermal Energy and Power m_constantsNames << i18n("Energy of 1 calorie"); m_constantsValues << QString::number(GSL_CONST_MKSA_CALORIE,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Energy of 1 British Thermal Unit"); m_constantsValues << QString::number(GSL_CONST_MKSA_BTU,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Energy of 1 Therm"); m_constantsValues << QString::number(GSL_CONST_MKSA_THERM,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Power of 1 horsepower"); m_constantsValues << QString::number(GSL_CONST_MKSA_HORSEPOWER,'g',15); m_constantsUnits << "kg m^2 / s^3"; for (int i = 0; i < 4; i++) m_constantsGroupIndex << 10; // Pressure m_constantsNames << i18n("Pressure of 1 bar"); m_constantsValues << QString::number(GSL_CONST_MKSA_BAR,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 standard atmosphere"); m_constantsValues << QString::number(GSL_CONST_MKSA_STD_ATMOSPHERE,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 torr"); m_constantsValues << QString::number(GSL_CONST_MKSA_TORR,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 meter of mercury"); m_constantsValues << QString::number(GSL_CONST_MKSA_METER_OF_MERCURY,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 inch of mercury"); m_constantsValues << QString::number(GSL_CONST_MKSA_INCH_OF_MERCURY,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 inch of water"); m_constantsValues << QString::number(GSL_CONST_MKSA_INCH_OF_WATER,'g',15); m_constantsUnits << "kg / m s^2"; m_constantsNames << i18n("Pressure of 1 pound per square inch"); m_constantsValues << QString::number(GSL_CONST_MKSA_PSI,'g',15); m_constantsUnits << "kg / m s^2"; for (int i = 0; i < 7; i++) m_constantsGroupIndex << 11; // Viscosity m_constantsNames << i18n("Dynamic viscosity of 1 poise"); m_constantsValues << QString::number(GSL_CONST_MKSA_POISE,'g',15); m_constantsUnits << "kg / m s"; m_constantsNames << i18n("Kinematic viscosity of 1 stokes"); m_constantsValues << QString::number(GSL_CONST_MKSA_STOKES,'g',15); m_constantsUnits << "m^2 / s"; for (int i = 0; i < 2; i++) m_constantsGroupIndex << 12; // Light and Illumination m_constantsNames << i18n("Luminance of 1 stilb"); m_constantsValues << QString::number(GSL_CONST_MKSA_STILB,'g',15); m_constantsUnits << "cd / m^2"; m_constantsNames << i18n("Luminous flux of 1 lumen"); m_constantsValues << QString::number(GSL_CONST_MKSA_LUMEN,'g',15); m_constantsUnits << "cd sr"; m_constantsNames << i18n("Illuminance of 1 lux"); m_constantsValues << QString::number(GSL_CONST_MKSA_LUX,'g',15); m_constantsUnits << "cd sr / m^2"; m_constantsNames << i18n("Illuminance of 1 phot"); m_constantsValues << QString::number(GSL_CONST_MKSA_PHOT,'g',15); m_constantsUnits << "cd sr / m^2"; m_constantsNames << i18n("Illuminance of 1 footcandle"); m_constantsValues << QString::number(GSL_CONST_MKSA_FOOTCANDLE,'g',15); m_constantsUnits << "cd sr / m^2"; m_constantsNames << i18n("Luminance of 1 lambert"); m_constantsValues << QString::number(GSL_CONST_MKSA_LAMBERT,'g',15); m_constantsUnits << "cd sr / m^2"; m_constantsNames << i18n("Luminance of 1 footlambert"); m_constantsValues << QString::number(GSL_CONST_MKSA_FOOTLAMBERT,'g',15); m_constantsUnits << "cd sr / m^2"; for (int i = 0; i < 7; i++) m_constantsGroupIndex << 13; // Radioactivity m_constantsNames << i18n("Activity of 1 curie"); m_constantsValues << QString::number(GSL_CONST_MKSA_CURIE,'g',15); m_constantsUnits << "1 / s"; m_constantsNames << i18n("Exposure of 1 roentgen"); m_constantsValues << QString::number(GSL_CONST_MKSA_ROENTGEN,'g',15); m_constantsUnits << "A s / kg"; m_constantsNames << i18n("Absorbed dose of 1 rad"); m_constantsValues << QString::number(GSL_CONST_MKSA_RAD,'g',15); m_constantsUnits << "m^2 / s^2"; for (int i = 0; i < 3; i++) m_constantsGroupIndex << 14; // Force and Energy m_constantsNames << i18n("SI unit of force"); m_constantsValues << QString::number(GSL_CONST_MKSA_NEWTON,'g',15); m_constantsUnits << "kg m / s^2"; m_constantsNames << i18n("Force of 1 Dyne"); m_constantsValues << QString::number(GSL_CONST_MKSA_DYNE,'g',15); m_constantsUnits << "kg m / s^2"; m_constantsNames << i18n("SI unit of energy"); m_constantsValues << QString::number(GSL_CONST_MKSA_JOULE,'g',15); m_constantsUnits << "kg m^2 / s^2"; m_constantsNames << i18n("Energy 1 erg"); m_constantsValues << QString::number(GSL_CONST_MKSA_ERG,'g',15); m_constantsUnits << "kg m^2 / s^2"; for (int i = 0; i < 4; i++) m_constantsGroupIndex << 15; } ExpressionParser::~ExpressionParser() { delete_table(); } ExpressionParser* ExpressionParser::getInstance() { if (!instance) instance = new ExpressionParser(); return instance; } const QStringList& ExpressionParser::functions() { return m_functions; } const QStringList& ExpressionParser::functionsGroups() { return m_functionsGroups; } const QStringList& ExpressionParser::functionsNames() { return m_functionsNames; } const QVector& ExpressionParser::functionsGroupIndices() { return m_functionsGroupIndex; } const QStringList& ExpressionParser::constants() { return m_constants; } const QStringList& ExpressionParser::constantsGroups() { return m_constantsGroups; } const QStringList& ExpressionParser::constantsNames() { return m_constantsNames; } const QStringList& ExpressionParser::constantsValues() { return m_constantsValues; } const QStringList& ExpressionParser::constantsUnits() { return m_constantsUnits; } const QVector& ExpressionParser::constantsGroupIndices() { return m_constantsGroupIndex; } bool ExpressionParser::isValid(const QString& expr, const QStringList& vars) { for (int i = 0; i < vars.size(); ++i) { QByteArray varba = vars.at(i).toLatin1(); assign_variable(varba.constData(), 0); } QByteArray funcba = expr.toLatin1(); const char* data = funcba.constData(); gsl_set_error_handler_off(); parse(data); return !(parse_errors() > 0); } QStringList ExpressionParser::getParameter(const QString& expr, const QStringList& vars) { DEBUG("ExpressionParser::getParameter()"); QDEBUG("variables:" << vars); QStringList parameters; QStringList strings = expr.split(QRegExp("\\W+"), QString::SkipEmptyParts); QDEBUG("found strings:" << strings); for (int i = 0; i < strings.size(); ++i) { // check if token is not a known constant/function/variable or number if (constants().indexOf(strings[i]) == -1 && functions().indexOf(strings[i]) == -1 && vars.indexOf(strings[i]) == -1 && QRegExp("[0-9]*").exactMatch(strings[i]) == 0) parameters << strings[i]; else QDEBUG(strings[i] << ':' << constants().indexOf(strings[i]) << ' ' << functions().indexOf(strings[i]) << ' ' << vars.indexOf(strings[i]) << ' ' << QRegExp("[0-9]*").exactMatch(strings[i])); } parameters.removeDuplicates(); QDEBUG("parameters found:" << parameters); return parameters; } bool ExpressionParser::evaluateCartesian(const QString& expr, const QString& min, const QString& max, int count, QVector* xVector, QVector* yVector, const QStringList& paramNames, const QVector& paramValues) { QByteArray xminba = min.toLatin1(); const double xMin = parse(xminba.constData()); QByteArray xmaxba = max.toLatin1(); const double xMax = parse(xmaxba.constData()); const double step = (xMax - xMin)/(double)(count - 1); QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double x, y; gsl_set_error_handler_off(); for (int i = 0; i < paramNames.size(); ++i) { QByteArray paramba = paramNames.at(i).toLatin1(); assign_variable(paramba.constData(), paramValues.at(i)); } for (int i = 0; i < count; i++) { x = xMin + step * i; assign_variable("x", x); y = parse(func); if (parse_errors() > 0) return false; (*xVector)[i] = x; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } bool ExpressionParser::evaluateCartesian(const QString& expr, const QString& min, const QString& max, int count, QVector* xVector, QVector* yVector) { QByteArray xminba = min.toLatin1(); const double xMin = parse(xminba.constData()); QByteArray xmaxba = max.toLatin1(); const double xMax = parse(xmaxba.constData()); const double step = (xMax - xMin)/(double)(count - 1); QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double x, y; gsl_set_error_handler_off(); for (int i = 0; i < count; i++) { x = xMin + step*i; assign_variable("x", x); y = parse(func); if (parse_errors() > 0) return false; (*xVector)[i] = x; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } bool ExpressionParser::evaluateCartesian(const QString& expr, QVector* xVector, QVector* yVector) { QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double x, y; gsl_set_error_handler_off(); for (int i = 0; i < xVector->count(); i++) { x = xVector->at(i); assign_variable("x", x); y = parse(func); if (parse_errors() > 0) return false; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } bool ExpressionParser::evaluateCartesian(const QString& expr, QVector* xVector, QVector* yVector, const QStringList& paramNames, const QVector& paramValues) { QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double x, y; gsl_set_error_handler_off(); for (int i = 0; i < paramNames.size(); ++i) { QByteArray paramba = paramNames.at(i).toLatin1(); assign_variable(paramba.constData(), paramValues.at(i)); } for (int i = 0; i < xVector->count(); i++) { x = xVector->at(i); assign_variable("x", x); y = parse(func); if (parse_errors() > 0) return false; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } /*! evaluates multivariate function y=f(x_1, x_2, ...). Variable names (x_1, x_2, ...) are stored in \c vars. Data is stored in \c dataVectors. */ bool ExpressionParser::evaluateCartesian(const QString& expr, const QStringList& vars, const QVector*>& xVectors, QVector* yVector) { Q_ASSERT(vars.size() == xVectors.size()); QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double y, varValue; QString varName; gsl_set_error_handler_off(); bool stop = false; for (int i = 0; i < yVector->size(); i++) { //stop iterating over i if one of the x-vectors has no elements anymore. for (int n = 0; n < xVectors.size(); ++n) { if (i == xVectors.at(n)->size()) { stop = true; break; } } if (stop) break; for (int n = 0; n < vars.size(); ++n) { varName = vars.at(n); varValue = xVectors.at(n)->at(i); QByteArray varba = varName.toLatin1(); assign_variable(varba.constData(), varValue); } y = parse(func); if (parse_errors() > 0) return false; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } bool ExpressionParser::evaluatePolar(const QString& expr, const QString& min, const QString& max, int count, QVector* xVector, QVector* yVector) { QByteArray minba = min.toLatin1(); const double minValue = parse(minba.constData()); QByteArray maxba = max.toLatin1(); const double maxValue = parse(maxba.constData()); const double step = (maxValue - minValue)/(double)(count - 1); QByteArray funcba = expr.toLatin1(); const char* func = funcba.constData(); double r, phi; gsl_set_error_handler_off(); for (int i = 0; i < count; i++) { phi = minValue + step * i; assign_variable("phi", phi); r = parse(func); if (parse_errors() > 0) return false; if (std::isfinite(r)) { (*xVector)[i] = r*cos(phi); (*yVector)[i] = r*sin(phi); } else { (*xVector)[i] = NAN; (*yVector)[i] = NAN; } } return true; } bool ExpressionParser::evaluateParametric(const QString& expr1, const QString& expr2, const QString& min, const QString& max, int count, QVector* xVector, QVector* yVector) { QByteArray minba = min.toLatin1(); const double minValue = parse(minba.constData()); QByteArray maxba = max.toLatin1(); const double maxValue = parse(maxba.constData()); const double step = (maxValue - minValue)/(double)(count - 1); QByteArray xfuncba = expr1.toLatin1(); const char* xFunc = xfuncba.constData(); QByteArray yfuncba = expr2.toLatin1(); const char* yFunc = yfuncba.constData(); double x, y, t; gsl_set_error_handler_off(); for (int i = 0; i < count; i++) { t = minValue + step*i; assign_variable("t", t); x = parse(xFunc); if (parse_errors() > 0) return false; if (std::isfinite(x)) (*xVector)[i] = x; else (*xVector)[i] = NAN; y = parse(yFunc); if (parse_errors() > 0) return false; if (std::isfinite(y)) (*yVector)[i] = y; else (*yVector)[i] = NAN; } return true; } diff --git a/src/backend/gsl/constants.h b/src/backend/gsl/constants.h index f548a2df4..5994450cf 100644 --- a/src/backend/gsl/constants.h +++ b/src/backend/gsl/constants.h @@ -1,181 +1,183 @@ /*************************************************************************** File : constans.h Project : LabPlot Description : definition of mathematical and physical constants -------------------------------------------------------------------- Copyright : (C) 2014 by Alexander Semke (alexander.semke@web.de) - Copyright : (C) 2014-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) + Copyright : (C) 2014-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 GSL_CONSTANTS_H #define GSL_CONSTANTS_H #include #include #include #include "parser.h" +/* sync with ExpressionParser.cpp */ struct con _constants[] = { /* Mathematical constants */ {"e", M_E}, {"pi", M_PI}, + {"euler", M_EULER}, /* Physical constants: http://www.gnu.org/software/gsl/manual/html_node/Physical-Constants.html */ /* Physical constants in MKSA system */ /* Fundamental Constants */ {"cL", GSL_CONST_MKSA_SPEED_OF_LIGHT}, {"mu0", GSL_CONST_MKSA_VACUUM_PERMEABILITY}, {"e0", GSL_CONST_MKSA_VACUUM_PERMITTIVITY}, {"hPlanck", GSL_CONST_MKSA_PLANCKS_CONSTANT_H}, {"hbar", GSL_CONST_MKSA_PLANCKS_CONSTANT_HBAR}, {"NA", GSL_CONST_NUM_AVOGADRO}, {"Faraday", GSL_CONST_MKSA_FARADAY}, {"kB", GSL_CONST_MKSA_BOLTZMANN}, {"r0", GSL_CONST_MKSA_MOLAR_GAS}, {"v0", GSL_CONST_MKSA_STANDARD_GAS_VOLUME}, {"sigma", GSL_CONST_MKSA_STEFAN_BOLTZMANN_CONSTANT}, {"Gauss", GSL_CONST_MKSA_GAUSS}, /* Astronomy and Astrophysics */ {"au", GSL_CONST_MKSA_ASTRONOMICAL_UNIT}, {"G", GSL_CONST_MKSA_GRAVITATIONAL_CONSTANT}, {"ly", GSL_CONST_MKSA_LIGHT_YEAR}, {"pc", GSL_CONST_MKSA_PARSEC}, {"gg", GSL_CONST_MKSA_GRAV_ACCEL}, {"ms", GSL_CONST_MKSA_SOLAR_MASS}, /* Atomic and Nuclear Physics */ {"ee", GSL_CONST_MKSA_ELECTRON_CHARGE}, {"ev", GSL_CONST_MKSA_ELECTRON_VOLT}, {"amu", GSL_CONST_MKSA_UNIFIED_ATOMIC_MASS}, {"me", GSL_CONST_MKSA_MASS_ELECTRON}, {"mmu", GSL_CONST_MKSA_MASS_MUON}, {"mp", GSL_CONST_MKSA_MASS_PROTON}, {"mn", GSL_CONST_MKSA_MASS_NEUTRON}, {"alpha", GSL_CONST_NUM_FINE_STRUCTURE}, {"Ry", GSL_CONST_MKSA_RYDBERG}, {"aB", GSL_CONST_MKSA_BOHR_RADIUS}, {"ao", GSL_CONST_MKSA_ANGSTROM}, {"barn", GSL_CONST_MKSA_BARN}, {"muB", GSL_CONST_MKSA_BOHR_MAGNETON}, {"mun", GSL_CONST_MKSA_NUCLEAR_MAGNETON}, {"mue", GSL_CONST_MKSA_ELECTRON_MAGNETIC_MOMENT}, {"mup", GSL_CONST_MKSA_PROTON_MAGNETIC_MOMENT}, {"sigmaT", GSL_CONST_MKSA_THOMSON_CROSS_SECTION}, {"pD", GSL_CONST_MKSA_DEBYE}, /* Measurement of Time */ {"min", GSL_CONST_MKSA_MINUTE}, {"hour", GSL_CONST_MKSA_HOUR}, {"day", GSL_CONST_MKSA_DAY}, {"week", GSL_CONST_MKSA_WEEK}, /* Imperial Units */ {"in", GSL_CONST_MKSA_INCH}, {"ft", GSL_CONST_MKSA_FOOT}, {"yard", GSL_CONST_MKSA_YARD}, {"mile", GSL_CONST_MKSA_MILE}, {"mil", GSL_CONST_MKSA_MIL}, /* Speed and Nautical Units */ {"v_km_per_h", GSL_CONST_MKSA_KILOMETERS_PER_HOUR}, {"v_mile_per_h", GSL_CONST_MKSA_MILES_PER_HOUR}, {"nmile", GSL_CONST_MKSA_NAUTICAL_MILE}, {"fathom", GSL_CONST_MKSA_FATHOM}, {"knot", GSL_CONST_MKSA_KNOT}, /* Printers Units */ {"pt", GSL_CONST_MKSA_POINT}, {"texpt", GSL_CONST_MKSA_TEXPOINT}, /* Volume, Area and Length */ {"micron", GSL_CONST_MKSA_MICRON}, {"hectare", GSL_CONST_MKSA_HECTARE}, {"acre", GSL_CONST_MKSA_ACRE}, {"liter", GSL_CONST_MKSA_LITER}, {"us_gallon", GSL_CONST_MKSA_US_GALLON}, {"can_gallon", GSL_CONST_MKSA_CANADIAN_GALLON}, {"uk_gallon", GSL_CONST_MKSA_UK_GALLON}, {"quart", GSL_CONST_MKSA_QUART}, {"pint", GSL_CONST_MKSA_PINT}, /* Mass and Weight */ {"pound", GSL_CONST_MKSA_POUND_MASS}, {"ounce", GSL_CONST_MKSA_OUNCE_MASS}, {"ton", GSL_CONST_MKSA_TON}, {"mton", GSL_CONST_MKSA_METRIC_TON}, {"uk_ton", GSL_CONST_MKSA_UK_TON}, {"troy_ounce", GSL_CONST_MKSA_TROY_OUNCE}, {"carat", GSL_CONST_MKSA_CARAT}, {"gram_force", GSL_CONST_MKSA_GRAM_FORCE}, {"pound_force", GSL_CONST_MKSA_POUND_FORCE}, {"kilepound_force", GSL_CONST_MKSA_KILOPOUND_FORCE}, {"poundal", GSL_CONST_MKSA_POUNDAL}, /* Thermal Energy and Power */ {"cal", GSL_CONST_MKSA_CALORIE}, {"btu", GSL_CONST_MKSA_BTU}, {"therm", GSL_CONST_MKSA_THERM}, {"hp", GSL_CONST_MKSA_HORSEPOWER}, /* Pressure */ {"bar", GSL_CONST_MKSA_BAR}, {"atm", GSL_CONST_MKSA_STD_ATMOSPHERE}, {"torr", GSL_CONST_MKSA_TORR}, {"mhg", GSL_CONST_MKSA_METER_OF_MERCURY}, {"inhg", GSL_CONST_MKSA_INCH_OF_MERCURY}, {"inh2o", GSL_CONST_MKSA_INCH_OF_WATER}, {"psi", GSL_CONST_MKSA_PSI}, /* Viscosity */ {"poise", GSL_CONST_MKSA_POISE}, {"stokes", GSL_CONST_MKSA_STOKES}, /* Light and Illumination */ {"stilb", GSL_CONST_MKSA_STILB}, {"lumen", GSL_CONST_MKSA_LUMEN}, {"lux", GSL_CONST_MKSA_LUX}, {"phot", GSL_CONST_MKSA_PHOT}, {"ftcandle", GSL_CONST_MKSA_FOOTCANDLE}, {"lambert", GSL_CONST_MKSA_LAMBERT}, {"ftlambert", GSL_CONST_MKSA_FOOTLAMBERT}, /* Radioactivity */ {"Curie", GSL_CONST_MKSA_CURIE}, {"Roentgen", GSL_CONST_MKSA_ROENTGEN}, {"rad", GSL_CONST_MKSA_RAD}, /* Force and Energy */ {"Newton", GSL_CONST_MKSA_NEWTON}, {"dyne", GSL_CONST_MKSA_DYNE}, {"Joule", GSL_CONST_MKSA_JOULE}, {"erg", GSL_CONST_MKSA_ERG}, /* ignore '...' */ {"...", 0}, {0, 0} }; #endif /* CONSTANTS_H */ diff --git a/src/backend/gsl/errors.h b/src/backend/gsl/errors.h index 917e0b49a..a72045c38 100644 --- a/src/backend/gsl/errors.h +++ b/src/backend/gsl/errors.h @@ -1,111 +1,111 @@ /*************************************************************************** File : errors.h Project : LabPlot Description : Translatable strings for GSL error codes -------------------------------------------------------------------- Copyright : (C) 2017 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 GSL_ERRORS_H #define GSL_ERRORS_H #include -#include +#include namespace { QString gslErrorToString(int status) { switch (status) { case GSL_SUCCESS: return i18n("Success"); case GSL_FAILURE: return i18n("Failure"); case GSL_CONTINUE: return i18n("Iteration has not converged"); case GSL_EDOM: return i18n("Input domain error, e.g sqrt(-1)"); case GSL_ERANGE: return i18n("Output range error, e.g. exp(1e100)"); case GSL_EFAULT: return i18n("Invalid pointer"); case GSL_EINVAL: return i18n("Invalid argument supplied"); case GSL_EFAILED: return i18n("Generic failure"); case GSL_EFACTOR: return i18n("Factorization failed"); case GSL_ENOMEM: return i18n("Failed to allocate memory"); case GSL_EBADFUNC: return i18n("Problem with supplied function"); case GSL_ERUNAWAY: return i18n("Iterative process is out of control"); case GSL_EMAXITER: return i18n("Exceeded max number of iterations"); case GSL_EZERODIV: return i18n("Tried to divide by zero"); case GSL_EBADTOL: return i18n("Invalid tolerance specified"); case GSL_ETOL: return i18n("Failed to reach the specified tolerance"); case GSL_EUNDRFLW: return i18n("Underflow"); case GSL_EOVRFLW: return i18n("Overflow"); case GSL_ELOSS: return i18n("Loss of accuracy"); case GSL_EROUND: return i18n("Failed because of roundoff error"); case GSL_EBADLEN: return i18n("Matrix, vector lengths are not conformant"); case GSL_ENOTSQR: return i18n("Matrix not square"); case GSL_ESING: return i18n("Apparent singularity detected"); case GSL_EDIVERGE: return i18n("Integral or series is divergent"); case GSL_EUNSUP: return i18n("Requested feature is not supported by the hardware"); case GSL_EUNIMPL: return i18n("Requested feature not (yet) implemented"); case GSL_ECACHE: return i18n("Cache limit exceeded"); case GSL_ETABLE: return i18n("Table limit exceeded"); case GSL_ENOPROG: return i18n("Iteration is not making progress towards solution"); case GSL_ENOPROGJ: return i18n("Jacobian evaluations are not improving the solution"); case GSL_ETOLF: return i18n("Cannot reach the specified tolerance in F"); case GSL_ETOLX: return i18n("Cannot reach the specified tolerance in X"); case GSL_ETOLG: return i18n("Cannot reach the specified tolerance in gradient"); case GSL_EOF: return i18n("End of file"); default: return QString(gsl_strerror(status));; } } } #endif diff --git a/src/backend/gsl/functions.h b/src/backend/gsl/functions.h index eafa330b2..83e59834a 100644 --- a/src/backend/gsl/functions.h +++ b/src/backend/gsl/functions.h @@ -1,491 +1,492 @@ /*************************************************************************** File : functions.h Project : LabPlot Description : definition of functions -------------------------------------------------------------------- Copyright : (C) 2014 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2014-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 FUNCTIONS_H #define FUNCTIONS_H #include #include #include #include #include #include "backend/nsl/nsl_sf_basic.h" #ifdef _MSC_VER /* avoid intrinsics */ #pragma function(ceil, floor) #endif /* sync with ExpressionParser.cpp */ struct func _functions[] = { /* standard functions */ {"rand", (func_t)nsl_sf_rand}, {"random", (func_t)nsl_sf_random}, {"drand", (func_t)nsl_sf_drand}, /* math.h */ {"ceil", (func_t)ceil}, {"fabs", (func_t)fabs}, {"log10", (func_t)log10}, {"pow", (func_t)pow}, {"sqrt", (func_t)sqrt}, {"sgn", (func_t)nsl_sf_sgn}, {"theta", (func_t)nsl_sf_theta}, {"harmonic", (func_t)nsl_sf_harmonic}, #ifndef _WIN32 {"cbrt", (func_t)cbrt}, {"logb", (func_t)logb}, {"rint", (func_t)rint}, {"round", (func_t)round}, {"trunc", (func_t)trunc}, #endif /* GSL mathematical functions: see http://www.gnu.org/software/gsl/manual/gsl-ref.html#Mathematical-Functions */ {"log1p", (func_t)gsl_log1p}, {"ldexp", (func_t)nsl_sf_ldexp}, {"powint", (func_t)nsl_sf_powint}, {"pow2", (func_t)gsl_pow_2}, {"pow3", (func_t)gsl_pow_3}, {"pow4", (func_t)gsl_pow_4}, {"pow5", (func_t)gsl_pow_5}, {"pow6", (func_t)gsl_pow_6}, {"pow7", (func_t)gsl_pow_7}, {"pow8", (func_t)gsl_pow_8}, {"pow9", (func_t)gsl_pow_9}, /* GSL special functions: see http://www.gnu.org/software/gsl/manual/html_node/Special-Functions.html */ /* Airy Functions and Derivatives */ {"Ai", (func_t)nsl_sf_airy_Ai}, {"Bi", (func_t)nsl_sf_airy_Bi}, {"Ais", (func_t)nsl_sf_airy_Ais}, {"Bis", (func_t)nsl_sf_airy_Bis}, {"Aid", (func_t)nsl_sf_airy_Aid}, {"Bid", (func_t)nsl_sf_airy_Bid}, {"Aids", (func_t)nsl_sf_airy_Aids}, {"Bids", (func_t)nsl_sf_airy_Bids}, {"Ai0", (func_t)nsl_sf_airy_0_Ai}, {"Bi0", (func_t)nsl_sf_airy_0_Bi}, {"Aid0", (func_t)nsl_sf_airy_0_Aid}, {"Bid0", (func_t)nsl_sf_airy_0_Bid}, /* Bessel Functions */ {"J0", (func_t)gsl_sf_bessel_J0}, {"J1", (func_t)gsl_sf_bessel_J1}, {"Jn", (func_t)nsl_sf_bessel_Jn}, {"Y0", (func_t)gsl_sf_bessel_Y0}, {"Y1", (func_t)gsl_sf_bessel_Y1}, {"Yn", (func_t)nsl_sf_bessel_Yn}, {"I0", (func_t)gsl_sf_bessel_I0}, {"I1", (func_t)gsl_sf_bessel_I1}, {"In", (func_t)nsl_sf_bessel_In}, {"I0s", (func_t)gsl_sf_bessel_I0_scaled}, {"I1s", (func_t)gsl_sf_bessel_I1_scaled}, {"Ins", (func_t)nsl_sf_bessel_Ins}, {"K0", (func_t)gsl_sf_bessel_K0}, {"K1", (func_t)gsl_sf_bessel_K1}, {"Kn", (func_t)nsl_sf_bessel_Kn}, {"K0s", (func_t)gsl_sf_bessel_K0_scaled}, {"K1s", (func_t)gsl_sf_bessel_K1_scaled}, {"Kns", (func_t)nsl_sf_bessel_Kns}, {"j0", (func_t)gsl_sf_bessel_j0}, {"j1", (func_t)gsl_sf_bessel_j1}, {"j2", (func_t)gsl_sf_bessel_j2}, {"jl", (func_t)nsl_sf_bessel_jl}, {"y0", (func_t)gsl_sf_bessel_y0}, {"y1", (func_t)gsl_sf_bessel_y1}, {"y2", (func_t)gsl_sf_bessel_y2}, {"yl", (func_t)nsl_sf_bessel_yl}, {"i0s", (func_t)gsl_sf_bessel_i0_scaled}, {"i1s", (func_t)gsl_sf_bessel_i1_scaled}, {"i2s", (func_t)gsl_sf_bessel_i2_scaled}, {"ils", (func_t)nsl_sf_bessel_ils}, {"k0s", (func_t)gsl_sf_bessel_k0_scaled}, {"k1s", (func_t)gsl_sf_bessel_k1_scaled}, {"k2s", (func_t)gsl_sf_bessel_k2_scaled}, {"kls", (func_t)nsl_sf_bessel_kls}, {"Jnu", (func_t)gsl_sf_bessel_Jnu}, {"Ynu", (func_t)gsl_sf_bessel_Ynu}, {"Inu", (func_t)gsl_sf_bessel_Inu}, {"Inus", (func_t)gsl_sf_bessel_Inu_scaled}, {"Knu", (func_t)gsl_sf_bessel_Knu}, {"lnKnu", (func_t)gsl_sf_bessel_lnKnu}, {"Knus", (func_t)gsl_sf_bessel_Knu_scaled}, {"J0_0", (func_t)nsl_sf_bessel_0_J0}, {"J1_0", (func_t)nsl_sf_bessel_0_J1}, {"Jnu_0", (func_t)nsl_sf_bessel_0_Jnu}, /* Clausen functions */ {"clausen", (func_t)gsl_sf_clausen}, /* Coulomb functions */ {"hydrogenicR_1", (func_t)gsl_sf_hydrogenicR_1}, {"hydrogenicR", (func_t)nsl_sf_hydrogenicR}, {"dawson", (func_t)gsl_sf_dawson}, /* Debye functions */ {"D1", (func_t)gsl_sf_debye_1}, {"D2", (func_t)gsl_sf_debye_2}, {"D3", (func_t)gsl_sf_debye_3}, {"D4", (func_t)gsl_sf_debye_4}, {"D5", (func_t)gsl_sf_debye_5}, {"D6", (func_t)gsl_sf_debye_6}, {"Li2", (func_t)gsl_sf_dilog}, /* Elliptic integrals */ {"Kc", (func_t)nsl_sf_ellint_Kc}, {"Ec", (func_t)nsl_sf_ellint_Ec}, {"Pc", (func_t)nsl_sf_ellint_Pc}, {"F", (func_t)nsl_sf_ellint_F}, {"E", (func_t)nsl_sf_ellint_E}, {"P", (func_t)nsl_sf_ellint_P}, {"D", (func_t)nsl_sf_ellint_D}, {"RC", (func_t)nsl_sf_ellint_RC}, {"RD", (func_t)nsl_sf_ellint_RD}, {"RF", (func_t)nsl_sf_ellint_RF}, {"RJ", (func_t)nsl_sf_ellint_RJ}, /* Error functions */ {"erf", (func_t)gsl_sf_erf}, {"erfc", (func_t)gsl_sf_erfc}, {"log_erfc", (func_t)gsl_sf_log_erfc}, {"erf_Z", (func_t)gsl_sf_erf_Z}, {"erf_Q", (func_t)gsl_sf_erf_Q}, {"hazard", (func_t)gsl_sf_hazard}, #ifndef _MSC_VER {"erfcx", (func_t)nsl_sf_erfcx}, {"erfi", (func_t)nsl_sf_erfi}, {"im_w_of_x", (func_t)nsl_sf_im_w_of_x}, {"dawson", (func_t)nsl_sf_dawson}, {"voigt", (func_t)nsl_sf_voigt}, #endif + {"pseudovoigt1", (func_t)nsl_sf_pseudovoigt1}, /* Exponential Functions */ {"exp", (func_t)gsl_sf_exp}, {"exp_mult", (func_t)gsl_sf_exp_mult}, {"expm1", (func_t)gsl_expm1}, {"exprel", (func_t)gsl_sf_exprel}, {"exprel2", (func_t)gsl_sf_exprel_2}, {"expreln", (func_t)nsl_sf_exprel_n}, /* Exponential Integrals */ {"E1", (func_t)gsl_sf_expint_E1}, {"E2", (func_t)gsl_sf_expint_E2}, {"En", (func_t)gsl_sf_expint_En}, {"Ei", (func_t)gsl_sf_expint_Ei}, {"Shi", (func_t)gsl_sf_Shi}, {"Chi", (func_t)gsl_sf_Chi}, {"Ei3", (func_t)gsl_sf_expint_3}, {"Si", (func_t)gsl_sf_Si}, {"Ci", (func_t)gsl_sf_Ci}, {"Atanint", (func_t)gsl_sf_atanint}, /* Fermi-Dirac Function */ {"Fm1", (func_t)gsl_sf_fermi_dirac_m1}, {"F0", (func_t)gsl_sf_fermi_dirac_0}, {"F1", (func_t)gsl_sf_fermi_dirac_1}, {"F2", (func_t)gsl_sf_fermi_dirac_2}, {"Fj", (func_t)nsl_sf_fermi_dirac_int}, {"Fmhalf", (func_t)gsl_sf_fermi_dirac_mhalf}, {"Fhalf", (func_t)gsl_sf_fermi_dirac_half}, {"F3half", (func_t)gsl_sf_fermi_dirac_3half}, {"Finc0", (func_t)gsl_sf_fermi_dirac_inc_0}, /* Gamma and Beta Functions */ {"gamma", (func_t)gsl_sf_gamma}, {"tgamma", (func_t)gsl_sf_gamma}, {"lgamma", (func_t)gsl_sf_lngamma}, {"lngamma", (func_t)gsl_sf_lngamma}, {"gammastar", (func_t)gsl_sf_gammastar}, {"gammainv", (func_t)gsl_sf_gammainv}, {"fact", (func_t)nsl_sf_fact}, {"doublefact", (func_t)nsl_sf_doublefact}, {"lnfact", (func_t)nsl_sf_lnfact}, {"lndoublefact", (func_t)nsl_sf_lndoublefact}, {"choose", (func_t)nsl_sf_choose}, {"lnchoose", (func_t)nsl_sf_lnchoose}, {"taylor", (func_t)nsl_sf_taylorcoeff}, {"poch", (func_t)gsl_sf_poch}, {"lnpoch", (func_t)gsl_sf_lnpoch}, {"pochrel", (func_t)gsl_sf_pochrel}, {"gammainc", (func_t)gsl_sf_gamma_inc}, {"gammaincQ", (func_t)gsl_sf_gamma_inc_Q}, {"gammaincP", (func_t)gsl_sf_gamma_inc_P}, {"beta", (func_t)gsl_sf_beta}, {"lnbeta", (func_t)gsl_sf_lnbeta}, {"betainc", (func_t)gsl_sf_beta_inc}, /* Gegenbauer Functions */ {"C1", (func_t)gsl_sf_gegenpoly_1}, {"C2", (func_t)gsl_sf_gegenpoly_2}, {"C3", (func_t)gsl_sf_gegenpoly_3}, {"Cn", (func_t)nsl_sf_gegenpoly_n}, #if (GSL_MAJOR_VERSION > 2) || (GSL_MAJOR_VERSION == 2) && (GSL_MINOR_VERSION >= 4) /* Hermite polynomials and functions */ {"Hen", (func_t)nsl_sf_hermite_prob}, {"Hn", (func_t)nsl_sf_hermite_phys}, {"Hfn", (func_t)nsl_sf_hermite_func}, {"Hend", (func_t)nsl_sf_hermite_prob_der}, {"Hnd", (func_t)nsl_sf_hermite_phys_der}, {"Hfnd", (func_t)nsl_sf_hermite_func_der}, #endif /* Hypergeometric Functions */ {"hyperg_0F1", (func_t)gsl_sf_hyperg_0F1}, {"hyperg_1F1i", (func_t)nsl_sf_hyperg_1F1i}, {"hyperg_1F1", (func_t)gsl_sf_hyperg_1F1}, {"hyperg_Ui", (func_t)nsl_sf_hyperg_Ui}, {"hyperg_U", (func_t)gsl_sf_hyperg_U}, {"hyperg_2F1", (func_t)gsl_sf_hyperg_2F1}, {"hyperg_2F1c", (func_t)gsl_sf_hyperg_2F1_conj}, {"hyperg_2F1r", (func_t)gsl_sf_hyperg_2F1_renorm}, {"hyperg_2F1cr", (func_t)gsl_sf_hyperg_2F1_conj_renorm}, {"hyperg_2F0", (func_t)gsl_sf_hyperg_2F0}, /* Laguerre Functions */ {"L1", (func_t)gsl_sf_laguerre_1}, {"L2", (func_t)gsl_sf_laguerre_2}, {"L3", (func_t)gsl_sf_laguerre_3}, /* Lambert W Functions */ {"W0", (func_t)gsl_sf_lambert_W0}, {"Wm1", (func_t)gsl_sf_lambert_Wm1}, /* Legendre Functions and Spherical Harmonics */ {"P1", (func_t)gsl_sf_legendre_P1}, {"P2", (func_t)gsl_sf_legendre_P2}, {"P3", (func_t)gsl_sf_legendre_P3}, {"Pl", (func_t)nsl_sf_legendre_Pl}, {"Q0", (func_t)gsl_sf_legendre_Q0}, {"Q1", (func_t)gsl_sf_legendre_Q1}, {"Ql", (func_t)nsl_sf_legendre_Ql}, {"Plm", (func_t)nsl_sf_legendre_Plm}, {"Pslm", (func_t)nsl_sf_legendre_sphPlm}, {"Phalf", (func_t)gsl_sf_conicalP_half}, {"Pmhalf", (func_t)gsl_sf_conicalP_mhalf}, {"Pc0", (func_t)gsl_sf_conicalP_0}, {"Pc1", (func_t)gsl_sf_conicalP_1}, {"Psr", (func_t)nsl_sf_conicalP_sphreg}, {"Pcr", (func_t)nsl_sf_conicalP_cylreg}, {"H3d0", (func_t)gsl_sf_legendre_H3d_0}, {"H3d1", (func_t)gsl_sf_legendre_H3d_1}, {"H3d", (func_t)nsl_sf_legendre_H3d}, /* Logarithm and Related Functions */ {"log", (func_t)gsl_sf_log}, {"logabs", (func_t)gsl_sf_log_abs}, {"logp", (func_t)gsl_sf_log_1plusx}, {"logpm", (func_t)gsl_sf_log_1plusx_mx}, /* Power Function */ {"gsl_powint", (func_t)nsl_sf_powint}, /* Psi (Digamma) Function */ {"psiint", (func_t)nsl_sf_psiint}, {"psi", (func_t)gsl_sf_psi}, {"psi1piy", (func_t)gsl_sf_psi_1piy}, {"psi1int", (func_t)nsl_sf_psi1int}, {"psi1", (func_t)gsl_sf_psi_1}, {"psin", (func_t)nsl_sf_psin}, /* Synchrotron Functions */ {"synchrotron1", (func_t)gsl_sf_synchrotron_1}, {"synchrotron2", (func_t)gsl_sf_synchrotron_2}, /* Transport Functions */ {"J2", (func_t)gsl_sf_transport_2}, {"J3", (func_t)gsl_sf_transport_3}, {"J4", (func_t)gsl_sf_transport_4}, {"J5", (func_t)gsl_sf_transport_5}, /* Trigonometric Functions */ {"sin", (func_t)gsl_sf_sin}, {"cos", (func_t)gsl_sf_cos}, {"tan", (func_t)tan}, {"asin", (func_t)asin}, {"acos", (func_t)acos}, {"atan", (func_t)atan}, {"atan2", (func_t)atan2}, {"sinh", (func_t)sinh}, {"cosh", (func_t)cosh}, {"tanh", (func_t)tanh}, {"acosh", (func_t)gsl_acosh}, {"asinh", (func_t)gsl_asinh}, {"atanh", (func_t)gsl_atanh}, {"sec", (func_t)nsl_sf_sec}, {"csc", (func_t)nsl_sf_csc}, {"cot", (func_t)nsl_sf_cot}, {"asec", (func_t)nsl_sf_asec}, {"acsc", (func_t)nsl_sf_acsc}, {"acot", (func_t)nsl_sf_acot}, {"sech", (func_t)nsl_sf_sech}, {"csch", (func_t)nsl_sf_csch}, {"coth", (func_t)nsl_sf_coth}, {"asech", (func_t)nsl_sf_asech}, {"acsch", (func_t)nsl_sf_acsch}, {"acoth", (func_t)nsl_sf_acoth}, {"sinc", (func_t)gsl_sf_sinc}, {"logsinh", (func_t)gsl_sf_lnsinh}, {"logcosh", (func_t)gsl_sf_lncosh}, {"hypot", (func_t)gsl_sf_hypot}, {"hypot3", (func_t)gsl_hypot3}, {"anglesymm", (func_t)gsl_sf_angle_restrict_symm}, {"anglepos", (func_t)gsl_sf_angle_restrict_pos}, /* Zeta Functions */ {"zetaint", (func_t)nsl_sf_zetaint}, {"zeta", (func_t)gsl_sf_zeta}, {"zetam1int", (func_t)nsl_sf_zetam1int}, {"zetam1", (func_t)gsl_sf_zetam1}, {"hzeta", (func_t)gsl_sf_hzeta}, {"etaint", (func_t)nsl_sf_etaint}, {"eta", (func_t)gsl_sf_eta}, /* GSL Random Number Distributions: see http://www.gnu.org/software/gsl/manual/html_node/Random-Number-Distributions.html */ /* Gaussian Distribution */ {"gaussian", (func_t)gsl_ran_gaussian_pdf}, {"ugaussian", (func_t)gsl_ran_ugaussian_pdf}, {"gaussianP", (func_t)gsl_cdf_gaussian_P}, {"gaussianQ", (func_t)gsl_cdf_gaussian_Q}, {"gaussianPinv", (func_t)gsl_cdf_gaussian_Pinv}, {"gaussianQinv", (func_t)gsl_cdf_gaussian_Qinv}, {"ugaussianP", (func_t)gsl_cdf_ugaussian_P}, {"ugaussianQ", (func_t)gsl_cdf_ugaussian_Q}, {"ugaussianPinv", (func_t)gsl_cdf_ugaussian_Pinv}, {"ugaussianQinv", (func_t)gsl_cdf_ugaussian_Qinv}, {"gaussiantail", (func_t)gsl_ran_gaussian_tail_pdf}, {"ugaussiantail", (func_t)gsl_ran_ugaussian_tail_pdf}, {"gaussianbi", (func_t)gsl_ran_bivariate_gaussian_pdf}, /* Exponential Distribution */ {"exponential", (func_t)gsl_ran_exponential_pdf}, {"exponentialP", (func_t)gsl_cdf_exponential_P}, {"exponentialQ", (func_t)gsl_cdf_exponential_Q}, {"exponentialPinv", (func_t)gsl_cdf_exponential_Pinv}, {"exponentialQinv", (func_t)gsl_cdf_exponential_Qinv}, /* Laplace Distribution */ {"laplace", (func_t)gsl_ran_laplace_pdf}, {"laplaceP", (func_t)gsl_cdf_laplace_P}, {"laplaceQ", (func_t)gsl_cdf_laplace_Q}, {"laplacePinv", (func_t)gsl_cdf_laplace_Pinv}, {"laplaceQinv", (func_t)gsl_cdf_laplace_Qinv}, /* Exponential Power Distribution */ {"exppow", (func_t)gsl_ran_exppow_pdf}, {"exppowP", (func_t)gsl_cdf_exppow_P}, {"exppowQ", (func_t)gsl_cdf_exppow_Q}, /* Cauchy Distribution */ {"cauchy", (func_t)gsl_ran_cauchy_pdf}, {"cauchyP", (func_t)gsl_cdf_cauchy_P}, {"cauchyQ", (func_t)gsl_cdf_cauchy_Q}, {"cauchyPinv", (func_t)gsl_cdf_cauchy_Pinv}, {"cauchyQinv", (func_t)gsl_cdf_cauchy_Qinv}, /* Rayleigh Distribution */ {"rayleigh", (func_t)gsl_ran_rayleigh_pdf}, {"rayleighP", (func_t)gsl_cdf_rayleigh_P}, {"rayleighQ", (func_t)gsl_cdf_rayleigh_Q}, {"rayleighPinv", (func_t)gsl_cdf_rayleigh_Pinv}, {"rayleighQinv", (func_t)gsl_cdf_rayleigh_Qinv}, {"rayleigh_tail", (func_t)gsl_ran_rayleigh_tail_pdf}, /* Landau Distribution */ {"landau", (func_t)gsl_ran_landau_pdf}, /* Gamma Distribution */ {"gammapdf", (func_t)gsl_ran_gamma_pdf}, {"gammaP", (func_t)gsl_cdf_gamma_P}, {"gammaQ", (func_t)gsl_cdf_gamma_Q}, {"gammaPinv", (func_t)gsl_cdf_gamma_Pinv}, {"gammaQinv", (func_t)gsl_cdf_gamma_Qinv}, /* Flat (Uniform) Distribution */ {"flat", (func_t)gsl_ran_flat_pdf}, {"flatP", (func_t)gsl_cdf_flat_P}, {"flatQ", (func_t)gsl_cdf_flat_Q}, {"flatPinv", (func_t)gsl_cdf_flat_Pinv}, {"flatQinv", (func_t)gsl_cdf_flat_Qinv}, /* Lognormal Distribution */ {"lognormal", (func_t)gsl_ran_lognormal_pdf}, {"lognormalP", (func_t)gsl_cdf_lognormal_P}, {"lognormalQ", (func_t)gsl_cdf_lognormal_Q}, {"lognormalPinv", (func_t)gsl_cdf_lognormal_Pinv}, {"lognormalQinv", (func_t)gsl_cdf_lognormal_Qinv}, /* Chi-squared Distribution */ {"chisq", (func_t)gsl_ran_chisq_pdf}, {"chisqP", (func_t)gsl_cdf_chisq_P}, {"chisqQ", (func_t)gsl_cdf_chisq_Q}, {"chisqPinv", (func_t)gsl_cdf_chisq_Pinv}, {"chisqQinv", (func_t)gsl_cdf_chisq_Qinv}, /* F-distribution */ {"fdist", (func_t)gsl_ran_fdist_pdf}, {"fdistP", (func_t)gsl_cdf_fdist_P}, {"fdistQ", (func_t)gsl_cdf_fdist_Q}, {"fdistPinv", (func_t)gsl_cdf_fdist_Pinv}, {"fdistQinv", (func_t)gsl_cdf_fdist_Qinv}, /* t-distribution */ {"tdist", (func_t)gsl_ran_tdist_pdf}, {"tdistP", (func_t)gsl_cdf_tdist_P}, {"tdistQ", (func_t)gsl_cdf_tdist_Q}, {"tdistPinv", (func_t)gsl_cdf_tdist_Pinv}, {"tdistQinv", (func_t)gsl_cdf_tdist_Qinv}, /* Beta Distribution */ {"betapdf", (func_t)gsl_ran_beta_pdf}, {"betaP", (func_t)gsl_cdf_beta_P}, {"betaQ", (func_t)gsl_cdf_beta_Q}, {"betaPinv", (func_t)gsl_cdf_beta_Pinv}, {"betaQinv", (func_t)gsl_cdf_beta_Qinv}, /* Logistic Distribution */ {"logistic", (func_t)gsl_ran_logistic_pdf}, {"logisticP", (func_t)gsl_cdf_logistic_P}, {"logisticQ", (func_t)gsl_cdf_logistic_Q}, {"logisticPinv", (func_t)gsl_cdf_logistic_Pinv}, {"logisticQinv", (func_t)gsl_cdf_logistic_Qinv}, /* Pareto Distribution */ {"pareto", (func_t)gsl_ran_pareto_pdf}, {"paretoP", (func_t)gsl_cdf_pareto_P}, {"paretoQ", (func_t)gsl_cdf_pareto_Q}, {"paretoPinv", (func_t)gsl_cdf_pareto_Pinv}, {"paretoQinv", (func_t)gsl_cdf_pareto_Qinv}, /* Weibull Distribution */ {"weibull", (func_t)gsl_ran_weibull_pdf}, {"weibullP", (func_t)gsl_cdf_weibull_P}, {"weibullQ", (func_t)gsl_cdf_weibull_Q}, {"weibullPinv", (func_t)gsl_cdf_weibull_Pinv}, {"weibullQinv", (func_t)gsl_cdf_weibull_Qinv}, /* Gumbel Distribution */ {"gumbel1", (func_t)gsl_ran_gumbel1_pdf}, {"gumbel1P", (func_t)gsl_cdf_gumbel1_P}, {"gumbel1Q", (func_t)gsl_cdf_gumbel1_Q}, {"gumbel1Pinv", (func_t)gsl_cdf_gumbel1_Pinv}, {"gumbel1Qinv", (func_t)gsl_cdf_gumbel1_Qinv}, {"gumbel2", (func_t)gsl_ran_gumbel2_pdf}, {"gumbel2P", (func_t)gsl_cdf_gumbel2_P}, {"gumbel2Q", (func_t)gsl_cdf_gumbel2_Q}, {"gumbel2Pinv", (func_t)gsl_cdf_gumbel2_Pinv}, {"gumbel2Qinv", (func_t)gsl_cdf_gumbel2_Qinv}, /* Poisson Distribution */ {"poisson", (func_t)nsl_sf_poisson}, {"poissonP", (func_t)gsl_cdf_poisson_P}, {"poissonQ", (func_t)gsl_cdf_poisson_Q}, /* Bernoulli Distribution */ {"bernoulli", (func_t)nsl_sf_bernoulli}, /* Binomial Distribution */ {"binomial", (func_t)nsl_sf_binomial}, {"binomialP", (func_t)gsl_cdf_binomial_P}, {"binomialQ", (func_t)gsl_cdf_binomial_Q}, {"negative_binomial", (func_t)nsl_sf_negative_binomial}, {"negative_binomialP", (func_t)gsl_cdf_negative_binomial_P}, {"negative_binomialQ", (func_t)gsl_cdf_negative_binomial_Q}, /* Pascal Distribution */ {"pascal", (func_t)nsl_sf_pascal}, {"pascalP", (func_t)gsl_cdf_pascal_P}, {"pascalQ", (func_t)gsl_cdf_pascal_Q}, /* Geometric Distribution */ {"geometric", (func_t)nsl_sf_geometric}, {"geometricP", (func_t)gsl_cdf_geometric_P}, {"geometricQ", (func_t)gsl_cdf_geometric_Q}, /* Hypergeometric Distribution */ {"hypergeometric", (func_t)nsl_sf_hypergeometric}, {"hypergeometricP", (func_t)gsl_cdf_hypergeometric_P}, {"hypergeometricQ", (func_t)gsl_cdf_hypergeometric_Q}, /* Logarithmic Distribution */ {"logarithmic", (func_t)nsl_sf_logarithmic}, {0, (func_t)0} }; #endif /*FUNCTIONS_H*/ diff --git a/src/backend/lib/XmlStreamReader.cpp b/src/backend/lib/XmlStreamReader.cpp index c6a968812..689f86feb 100644 --- a/src/backend/lib/XmlStreamReader.cpp +++ b/src/backend/lib/XmlStreamReader.cpp @@ -1,133 +1,131 @@ /*************************************************************************** File : XmlStreamReader.cpp Project : LabPlot Description : XML stream parser that supports errors and warnings -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2015-2016 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 "backend/lib/XmlStreamReader.h" -#include +#include /** * \class XmlStreamReader * \brief XML stream parser that supports errors as well as warnings. * This class also adds line and column numbers to the error message. */ XmlStreamReader::XmlStreamReader() { } XmlStreamReader::XmlStreamReader(QIODevice* device) : QXmlStreamReader(device) { } XmlStreamReader::XmlStreamReader(const QByteArray& data) : QXmlStreamReader(data) { } XmlStreamReader::XmlStreamReader(const QString& data) : QXmlStreamReader(data) { } XmlStreamReader::XmlStreamReader(const char* data) : QXmlStreamReader(data) { } QStringList XmlStreamReader::warningStrings() const { return m_warnings; } bool XmlStreamReader::hasWarnings() const { return !(m_warnings.isEmpty()); } void XmlStreamReader::raiseError(const QString & message) { - QString prefix = QString(i18n("line %1, column %2: ", lineNumber(), columnNumber())); - QXmlStreamReader::raiseError(prefix+message); + QXmlStreamReader::raiseError(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), message)); } void XmlStreamReader::raiseWarning(const QString & message) { - QString prefix = QString(i18n("line %1, column %2: ", lineNumber(), columnNumber())); - m_warnings.append(prefix+message); + m_warnings.append(i18n("line %1, column %2: %3", lineNumber(), columnNumber(), message)); } /*! * Go to the next start or end element tag * If the end of the document is reached, an error is raised. * \return false if end of document reached, otherwise true */ bool XmlStreamReader::skipToNextTag() { if (atEnd()) { raiseError(i18n("unexpected end of document")); return false; } do { readNext(); } while (!(isStartElement() || isEndElement() || atEnd())); if (atEnd()) { raiseError(i18n("unexpected end of document")); return false; } return true; } /*! * Go to the end element tag of the current element * If the end of the document is reached, an error is raised. * \return false if end of document reached, otherwise true */ bool XmlStreamReader::skipToEndElement() { int depth = 1; if (atEnd()) { raiseError(i18n("unexpected end of document")); return false; } do { readNext(); if (isEndElement()) depth--; if (isStartElement()) depth++; } while (!((isEndElement() && depth == 0) || atEnd())); if (atEnd()) { raiseError(i18n("unexpected end of document")); return false; } return true; } /*! * Read an XML attribute and convert it to int * \param name attribute name * \param ok pointer to report back whether the attribute value could be determined (may be NULL) * \return the attriute value if found and converted, otherwise zero (in this case *ok is false) */ int XmlStreamReader::readAttributeInt(const QString& name, bool* ok) { QString str = attributes().value(namespaceUri().toString(), name).toString(); if (str.isEmpty()) { if (ok) *ok = false; return 0; } return str.toInt(ok); } diff --git a/src/backend/lib/commandtemplates.h b/src/backend/lib/commandtemplates.h index 26059b097..3f755a5e5 100644 --- a/src/backend/lib/commandtemplates.h +++ b/src/backend/lib/commandtemplates.h @@ -1,123 +1,125 @@ /*************************************************************************** File : commandtemplates.h Project : LabPlot Description : Undo/Redo command templates -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2017 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 COMMANDTEMPLATES_H #define COMMANDTEMPLATES_H #include +#include + template class StandardSetterCmd : public QUndoCommand { public: - StandardSetterCmd(target_class* target, value_type target_class::* field, value_type newValue, const QString& description) // use i18n("%1: ...") for last arg + StandardSetterCmd(target_class* target, value_type target_class::* field, value_type newValue, const KLocalizedString& description) // use ki18n("%1: ...") : m_target(target), m_field(field), m_otherValue(newValue) { - setText(description.arg(m_target->name())); + setText(description.subs(m_target->name()).toString()); } virtual void initialize() {}; virtual void finalize() {}; void redo() override { initialize(); value_type tmp = *m_target.*m_field; *m_target.*m_field = m_otherValue; m_otherValue = tmp; finalize(); } void undo() override { redo(); } protected: target_class* m_target; value_type target_class::*m_field; value_type m_otherValue; }; template class StandardMacroSetterCmd : public QUndoCommand { public: - StandardMacroSetterCmd(target_class* target, value_type target_class::*field, value_type newValue, const QString& description) // use i18n("%1: ...") for last arg + StandardMacroSetterCmd(target_class* target, value_type target_class::*field, value_type newValue, const KLocalizedString& description) // use ki18n("%1: ...") : m_target(target), m_field(field), m_otherValue(newValue) { - setText(description.arg(m_target->name())); + setText(description.subs(m_target->name()).toString()); } virtual void initialize() {}; virtual void finalize() {}; virtual void finalizeUndo() {}; void redo() override { initialize(); value_type tmp = *m_target.*m_field; *m_target.*m_field = m_otherValue; m_otherValue = tmp; finalize(); } //call finalizeUndo() at the end where only the signal is emmited //and no actual finalize-method is called that can potentially //cause new entries on the undo-stack void undo() override { initialize(); value_type tmp = *m_target.*m_field; *m_target.*m_field = m_otherValue; m_otherValue = tmp; finalizeUndo(); } protected: target_class* m_target; value_type target_class::*m_field; value_type m_otherValue; }; template class StandardSwapMethodSetterCmd : public QUndoCommand { public: - StandardSwapMethodSetterCmd(target_class* target, value_type (target_class::*method)(value_type), value_type newValue, const QString& description) // use i18n("%1: ...") for last arg + StandardSwapMethodSetterCmd(target_class* target, value_type (target_class::*method)(value_type), value_type newValue, const KLocalizedString& description) // use ki18n("%1: ...") : m_target(target), m_method(method), m_otherValue(newValue) { - setText(description.arg(m_target->name())); + setText(description.subs(m_target->name()).toString()); } virtual void initialize() {}; virtual void finalize() {}; void redo() override { initialize(); m_otherValue = (*m_target.*m_method)(m_otherValue); finalize(); } void undo() override { redo(); } protected: target_class* m_target; value_type (target_class::*m_method)(value_type); value_type m_otherValue; }; #endif diff --git a/src/backend/lib/macros.h b/src/backend/lib/macros.h index 480266169..df45402b3 100644 --- a/src/backend/lib/macros.h +++ b/src/backend/lib/macros.h @@ -1,497 +1,497 @@ /*************************************************************************** File : macros.h Project : LabPlot Description : Various preprocessor macros -------------------------------------------------------------------- Copyright : (C) 2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2013-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 MACROS_H #define MACROS_H #include #include // C++ style warning (works on Windows) #include #define WARN(x) std::cout << x << std::endl #ifndef NDEBUG #include #define QDEBUG(x) qDebug() << x // C++ style debugging (works on Windows) #include #define DEBUG(x) std::cout << x << std::endl #else #define QDEBUG(x) {} #define DEBUG(x) {} #endif #ifdef Q_OS_WIN #define UTF8_QSTRING(str) QString::fromWCharArray(L##str) #else #define UTF8_QSTRING(str) QString::fromUtf8(str) #endif #define ENUM_TO_STRING(class, enum, value) \ (class::staticMetaObject.enumerator(class::staticMetaObject.indexOfEnumerator(#enum)).valueToKey(value)) #define ENUM_COUNT(class, enum) \ (class::staticMetaObject.enumerator(class::staticMetaObject.indexOfEnumerator(#enum)).keyCount()) #define BASIC_ACCESSOR(type, var, method, Method) \ type method() const { return var; }; \ void set ## Method(const type value) { var = value; } #define CLASS_ACCESSOR(type, var, method, Method) \ type method() const { return var; }; \ void set ## Method(const type & value) { var = value; } #define BASIC_D_ACCESSOR_DECL(type, method, Method) \ type method() const; \ void set ## Method(const type value); #define BASIC_D_ACCESSOR_IMPL(classname, type, method, Method, var) \ void classname::set ## Method(const type value) \ { \ d->var = value; \ } \ type classname::method() const \ { \ return d->var; \ } #define BASIC_D_READER_IMPL(classname, type, method, var) \ type classname::method() const \ { \ return d->var; \ } #define BASIC_SHARED_D_READER_IMPL(classname, type, method, var) \ type classname::method() const \ { \ Q_D(const classname); \ return d->var; \ } #define CLASS_D_ACCESSOR_DECL(type, method, Method) \ type method() const; \ void set ## Method(const type & value); #define CLASS_D_ACCESSOR_IMPL(classname, type, method, Method, var) \ void classname::set ## Method(const type & value) \ { \ d->var = value; \ } \ type classname::method() const \ { \ return d->var; \ } #define CLASS_D_READER_IMPL(classname, type, method, var) \ type classname::method() const \ { \ return d->var; \ } #define CLASS_SHARED_D_READER_IMPL(classname, type, method, var) \ type classname::method() const \ { \ Q_D(const classname); \ return d->var; \ } #define POINTER_D_ACCESSOR_DECL(type, method, Method) \ type *method() const; \ void set ## Method(type *ptr); #define FLAG_D_ACCESSOR_DECL(Method) \ bool is ## Method() const; \ bool has ## Method() const; \ void set ## Method(const bool value=true); \ void enable ## Method(const bool value=true); #define FLAG_D_ACCESSOR_IMPL(classname, Method, var) \ void classname::set ## Method(const bool value) \ { \ d->var = value; \ } \ void classname::enable ## Method(const bool value) \ { \ d->var = value; \ } \ bool classname::is ## Method() const \ { \ return d->var; \ } \ bool classname::has ## Method() const \ { \ return d->var; \ } #define WAIT_CURSOR QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)) #define RESET_CURSOR QApplication::restoreOverrideCursor() #define STD_SETTER_CMD_IMPL(class_name, cmd_name, value_type, field_name) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ }; #define STD_SETTER_CMD_IMPL_F(class_name, cmd_name, value_type, field_name, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void finalize() { m_target->finalize_method(); } \ }; // setter class with finalize() and signal emmiting. #define STD_SETTER_CMD_IMPL_S(class_name, cmd_name, value_type, field_name) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void finalize() { emit m_target->q->field_name##Changed(m_target->*m_field); } \ }; #define STD_SETTER_CMD_IMPL_F_S(class_name, cmd_name, value_type, field_name, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void finalize() { m_target->finalize_method(); emit m_target->q->field_name##Changed(m_target->*m_field); } \ }; // setter class with finalize() and signal emmiting for changing several properties in one single step (embedded in beginMacro/endMacro) #define STD_SETTER_CMD_IMPL_M_F_S(class_name, cmd_name, value_type, field_name, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardMacroSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardMacroSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void finalize() { m_target->finalize_method(); emit m_target->q->field_name##Changed(m_target->*m_field); } \ virtual void finalizeUndo() { emit m_target->q->field_name##Changed(m_target->*m_field); } \ }; #define STD_SETTER_CMD_IMPL_I(class_name, cmd_name, value_type, field_name, init_method) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void initialize() { m_target->init_method(); } \ }; #define STD_SETTER_CMD_IMPL_IF(class_name, cmd_name, value_type, field_name, init_method, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSetterCmd(target, &class_name::Private::field_name, newValue, description) {} \ virtual void initialize() { m_target->init_method(); } \ virtual void finalize() { m_target->finalize_method(); } \ }; #define STD_SWAP_METHOD_SETTER_CMD_IMPL(class_name, cmd_name, value_type, method_name) \ class class_name ## cmd_name ## Cmd: public StandardSwapMethodSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSwapMethodSetterCmd(target, &class_name::Private::method_name, newValue, description) {} \ }; #define STD_SWAP_METHOD_SETTER_CMD_IMPL_F(class_name, cmd_name, value_type, method_name, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardSwapMethodSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSwapMethodSetterCmd(target, &class_name::Private::method_name, newValue, description) {} \ virtual void finalize() { m_target->finalize_method(); } \ }; #define STD_SWAP_METHOD_SETTER_CMD_IMPL_I(class_name, cmd_name, value_type, method_name, init_method) \ class class_name ## cmd_name ## Cmd: public StandardSwapMethodSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSwapMethodSetterCmd(target, &class_name::Private::method_name, newValue, description) {} \ virtual void initialize() { m_target->init_method(); } \ }; #define STD_SWAP_METHOD_SETTER_CMD_IMPL_IF(class_name, cmd_name, value_type, method_name, init_method, finalize_method) \ class class_name ## cmd_name ## Cmd: public StandardSwapMethodSetterCmd { \ public: \ - class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const QString &description) \ + class_name ## cmd_name ## Cmd(class_name::Private *target, value_type newValue, const KLocalizedString &description) \ : StandardSwapMethodSetterCmd(target, &class_name::Private::method_name, newValue, description) {} \ virtual void initialize() { m_target->init_method(); } \ virtual void finalize() { m_target->finalize_method(); } \ }; //xml-serialization/deserialization //QColor #define WRITE_QCOLOR(color) \ do { \ writer->writeAttribute( "color_r", QString::number(color.red()) ); \ writer->writeAttribute( "color_g", QString::number(color.green()) ); \ writer->writeAttribute( "color_b", QString::number(color.blue()) ); \ } while (0) #define READ_QCOLOR(color) \ do { \ str = attribs.value("color_r").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_r'")); \ + reader->raiseWarning(attributeWarning.subs("color_r").toString()); \ else \ color.setRed( str.toInt() ); \ \ str = attribs.value("color_g").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_g'")); \ + reader->raiseWarning(attributeWarning.subs("color_g").toString()); \ else \ color.setGreen( str.toInt() ); \ \ str = attribs.value("color_b").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_b'")); \ + reader->raiseWarning(attributeWarning.subs("color_b").toString()); \ else \ color.setBlue( str.toInt() ); \ } while(0) //QPen #define WRITE_QPEN(pen) \ do { \ writer->writeAttribute( "style", QString::number(pen.style()) ); \ writer->writeAttribute( "color_r", QString::number(pen.color().red()) ); \ writer->writeAttribute( "color_g", QString::number(pen.color().green()) ); \ writer->writeAttribute( "color_b", QString::number(pen.color().blue()) ); \ writer->writeAttribute( "width", QString::number(pen.widthF()) ); \ } while (0) #define READ_QPEN(pen) \ do { \ str = attribs.value("style").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'style'")); \ + reader->raiseWarning(attributeWarning.subs("style").toString()); \ else \ pen.setStyle( (Qt::PenStyle)str.toInt() ); \ \ QColor color; \ str = attribs.value("color_r").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_r'")); \ + reader->raiseWarning(attributeWarning.subs("color_r").toString()); \ else \ color.setRed( str.toInt() ); \ \ str = attribs.value("color_g").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_g'")); \ + reader->raiseWarning(attributeWarning.subs("color_g").toString()); \ else \ color.setGreen( str.toInt() ); \ \ str = attribs.value("color_b").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'color_b'")); \ + reader->raiseWarning(attributeWarning.subs("color_b").toString()); \ else \ color.setBlue( str.toInt() ); \ \ pen.setColor(color); \ \ str = attribs.value("width").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'width'")); \ + reader->raiseWarning(attributeWarning.subs("width").toString()); \ else \ pen.setWidthF( str.toDouble() ); \ } while(0) //QFont #define WRITE_QFONT(font) \ do { \ writer->writeAttribute( "fontFamily", font.family() ); \ writer->writeAttribute( "fontSize", QString::number(font.pixelSize()) ); \ writer->writeAttribute( "fontPointSize", QString::number(font.pointSize()));\ writer->writeAttribute( "fontWeight", QString::number(font.weight()) ); \ writer->writeAttribute( "fontItalic", QString::number(font.italic()) ); \ } while(0) #define READ_QFONT(font) \ do { \ str = attribs.value("fontFamily").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'fontFamily'")); \ + reader->raiseWarning(attributeWarning.subs("fontFamily").toString()); \ else \ font.setFamily( str ); \ \ str = attribs.value("fontSize").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'fontSize'")); \ + reader->raiseWarning(attributeWarning.subs("fontSize").toString()); \ else { \ int size = str.toInt(); \ if (size != -1) \ font.setPixelSize(size); \ } \ \ str = attribs.value("fontPointSize").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'fontPointSize'")); \ + reader->raiseWarning(attributeWarning.subs("fontPointSize").toString()); \ else { \ int size = str.toInt(); \ if (size != -1) \ font.setPointSize(size); \ } \ \ str = attribs.value("fontWeight").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'fontWeight'")); \ + reader->raiseWarning(attributeWarning.subs("fontWeight").toString()); \ else \ font.setWeight( str.toInt() ); \ \ str = attribs.value("fontItalic").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'fontItalic'")); \ + reader->raiseWarning(attributeWarning.subs("fontItalic").toString()); \ else \ font.setItalic( str.toInt() ); \ } while(0) //QBrush #define WRITE_QBRUSH(brush) \ do { \ writer->writeAttribute("brush_style", QString::number(brush.style()) ); \ writer->writeAttribute("brush_color_r", QString::number(brush.color().red())); \ writer->writeAttribute("brush_color_g", QString::number(brush.color().green()));\ writer->writeAttribute("brush_color_b", QString::number(brush.color().blue())); \ } while(0) #define READ_QBRUSH(brush) \ do { \ str = attribs.value("brush_style").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'brush_style'")); \ + reader->raiseWarning(attributeWarning.subs("brush_style").toString()); \ else \ brush.setStyle( (Qt::BrushStyle)str.toInt() ); \ \ QColor color; \ str = attribs.value("brush_color_r").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'brush_color_r'")); \ + reader->raiseWarning(attributeWarning.subs("brush_color_r").toString()); \ else \ color.setRed( str.toInt() ); \ \ str = attribs.value("brush_color_g").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'brush_color_g'")); \ + reader->raiseWarning(attributeWarning.subs("brush_color_g").toString()); \ else \ color.setGreen( str.toInt() ); \ \ str = attribs.value("brush_color_b").toString(); \ if(str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg("'brush_color_b'")); \ + reader->raiseWarning(attributeWarning.subs("brush_color_b").toString()); \ else \ color.setBlue( str.toInt() ); \ \ brush.setColor(color); \ } while(0) //Column #define WRITE_COLUMN(column, columnName) \ do { \ if (column){ \ writer->writeAttribute( #columnName, column->path() ); \ } else { \ writer->writeAttribute( #columnName, "" ); \ } \ } while(0) //column names can be empty in case no columns were used before save //the actual pointers to the x- and y-columns are restored in Project::load() #define READ_COLUMN(columnName) \ do { \ str = attribs.value(#columnName).toString(); \ d->columnName ##Path = str; \ } while(0) #define READ_INT_VALUE(name, var, type) \ str = attribs.value(name).toString(); \ if (str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg(name)); \ + reader->raiseWarning(attributeWarning.subs(name).toString()); \ else \ d->var = (type)str.toInt(); #define READ_DOUBLE_VALUE(name, var) \ str = attribs.value(name).toString(); \ if (str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg(name)); \ + reader->raiseWarning(attributeWarning.subs(name).toString()); \ else \ d->var = str.toDouble(); #define READ_STRING_VALUE(name, var) \ str = attribs.value(name).toString(); \ if (str.isEmpty()) \ - reader->raiseWarning(attributeWarning.arg(name)); \ + reader->raiseWarning(attributeWarning.subs(name).toString()); \ else \ d->var = str; //used in Project::load() #define RESTORE_COLUMN_POINTER(obj, col, Col) \ do { \ if (!obj->col ##Path().isEmpty()) { \ for (Column* column : columns) { \ if (!column) continue; \ if (column->path() == obj->col ##Path()) { \ obj->set## Col(column); \ break; \ } \ } \ } \ } while(0) #define WRITE_PATH(obj, name) \ do { \ if (obj){ \ writer->writeAttribute( #name, obj->path() ); \ } else { \ writer->writeAttribute( #name, "" ); \ } \ } while(0) #define READ_PATH(name) \ do { \ str = attribs.value(#name).toString(); \ d->name ##Path = str; \ } while(0) #define RESTORE_POINTER(obj, name, Name, Type, list) \ do { \ if (!obj->name ##Path().isEmpty()) { \ for (AbstractAspect* aspect : list) { \ if (aspect->path() == obj->name ##Path()) { \ Type * a = dynamic_cast(aspect); \ if (!a) continue; \ obj->set## Name(a); \ break; \ } \ } \ } \ } while(0) #endif // MACROS_H diff --git a/src/backend/matrix/Matrix.cpp b/src/backend/matrix/Matrix.cpp index 27ae5f294..68e9e540a 100644 --- a/src/backend/matrix/Matrix.cpp +++ b/src/backend/matrix/Matrix.cpp @@ -1,1293 +1,1294 @@ /*************************************************************************** File : Matrix.cpp Project : Matrix Description : Spreadsheet with a MxN matrix data model -------------------------------------------------------------------- Copyright : (C) 2008-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2015-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017-2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "Matrix.h" #include "MatrixPrivate.h" #include "matrixcommands.h" #include "backend/matrix/MatrixModel.h" #include "backend/core/Folder.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "commonfrontend/matrix/MatrixView.h" #include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h" #include #include #include #include +#include #include -#include +#include /*! This class manages matrix based data (i.e., mathematically a MxN matrix with M rows, N columns). This data is typically used to for 3D plots. The values of the matrix are stored as generic values. Each column of the matrix is stored in a QVector objects. \ingroup backend */ Matrix::Matrix(AbstractScriptingEngine* engine, int rows, int cols, const QString& name, const AbstractColumn::ColumnMode mode) : AbstractDataSource(engine, name), d(new MatrixPrivate(this, mode)), m_model(nullptr), m_view(nullptr) { //set initial number of rows and columns appendColumns(cols); appendRows(rows); init(); } Matrix::Matrix(AbstractScriptingEngine* engine, const QString& name, bool loading, const AbstractColumn::ColumnMode mode) : AbstractDataSource(engine, name), d(new MatrixPrivate(this, mode)), m_model(nullptr), m_view(nullptr) { if (!loading) init(); } Matrix::~Matrix() { delete d; } void Matrix::init() { KConfig config; KConfigGroup group = config.group("Matrix"); //matrix dimension int rows = group.readEntry("RowCount", 10); int cols = group.readEntry("ColumnCount", 10); appendRows(rows); appendColumns(cols); //mapping to logical x- and y-coordinates d->xStart = group.readEntry("XStart", 0.0); d->xEnd = group.readEntry("XEnd", 1.0); d->yStart = group.readEntry("YStart", 0.0); d->yEnd = group.readEntry("YEnd", 1.0); //format QByteArray formatba = group.readEntry("NumericFormat", "f").toLatin1(); d->numericFormat = *formatba.data(); d->precision = group.readEntry("Precision", 3); d->headerFormat = (Matrix::HeaderFormat)group.readEntry("HeaderFormat", (int)Matrix::HeaderRowsColumns); } /*! Returns an icon to be used for decorating my views. */ QIcon Matrix::icon() const { return QIcon::fromTheme("labplot-matrix"); } /*! Returns a new context menu. The caller takes ownership of the menu. */ QMenu* Matrix::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); emit requestProjectContextMenu(menu); return menu; } QWidget* Matrix::view() const { if (!m_partView) { m_view= new MatrixView(const_cast(this)); m_partView = m_view; m_model = m_view->model(); } return m_partView; } bool Matrix::exportView() const { ExportSpreadsheetDialog* dlg = new ExportSpreadsheetDialog(m_view); dlg->setFileName(name()); dlg->setMatrixMode(true); //TODO FITS filter to decide if it can be exported to both dlg->setExportTo(QStringList() << i18n("FITS image") << i18n("FITS table")); if (m_view->selectedColumnCount() == 0) { dlg->setExportSelection(false); } bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { const QString path = dlg->path(); WAIT_CURSOR; if (dlg->format() == ExportSpreadsheetDialog::LaTeX) { const bool verticalHeader = dlg->matrixVerticalHeader(); const bool horizontalHeader = dlg->matrixHorizontalHeader(); const bool latexHeader = dlg->exportHeader(); const bool gridLines = dlg->gridLines(); const bool entire = dlg->entireSpreadheet(); const bool captions = dlg->captions(); m_view->exportToLaTeX(path, verticalHeader, horizontalHeader, latexHeader, gridLines, entire, captions); } else if (dlg->format() == ExportSpreadsheetDialog::FITS) { const int exportTo = dlg->exportToFits(); m_view->exportToFits(path, exportTo ); } else { const QString separator = dlg->separator(); m_view->exportToFile(path, separator); } RESET_CURSOR; } delete dlg; return ret; } bool Matrix::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); bool ret; - dlg->setWindowTitle(i18n("Print Matrix")); + dlg->setWindowTitle(i18nc("@title:window", "Print Matrix")); if ( (ret = (dlg->exec() == QDialog::Accepted)) ) m_view->print(&printer); delete dlg; return ret; } bool Matrix::printPreview() const { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &MatrixView::print); return dlg->exec(); } //############################################################################## //########################## getter methods ################################## //############################################################################## void* Matrix::data() const { return d->data; } BASIC_D_READER_IMPL(Matrix, AbstractColumn::ColumnMode, mode, mode) BASIC_D_READER_IMPL(Matrix, int, rowCount, rowCount) BASIC_D_READER_IMPL(Matrix, int, columnCount, columnCount) BASIC_D_READER_IMPL(Matrix, double, xStart, xStart) BASIC_D_READER_IMPL(Matrix, double, xEnd, xEnd) BASIC_D_READER_IMPL(Matrix, double, yStart, yStart) BASIC_D_READER_IMPL(Matrix, double, yEnd, yEnd) BASIC_D_READER_IMPL(Matrix, char, numericFormat, numericFormat) BASIC_D_READER_IMPL(Matrix, int, precision, precision) BASIC_D_READER_IMPL(Matrix, Matrix::HeaderFormat, headerFormat, headerFormat) CLASS_D_READER_IMPL(Matrix, QString, formula, formula) void Matrix::setSuppressDataChangedSignal(bool b) { if (m_model) m_model->setSuppressDataChangedSignal(b); } void Matrix::setChanged() { if (m_model) m_model->setChanged(); } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## void Matrix::setRowCount(int count) { if (count == d->rowCount) return; const int diff = count - d->rowCount; if (diff > 0) appendRows(diff); else if (diff < 0) removeRows(rowCount() + diff, -diff); } void Matrix::setColumnCount(int count) { if (count == d->columnCount) return; const int diff = count - columnCount(); if (diff > 0) appendColumns(diff); else if (diff < 0) removeColumns(columnCount() + diff, -diff); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetXStart, double, xStart, updateViewHeader) void Matrix::setXStart(double xStart) { if (xStart != d->xStart) - exec(new MatrixSetXStartCmd(d, xStart, i18n("%1: x-start changed"))); + exec(new MatrixSetXStartCmd(d, xStart, ki18n("%1: x-start changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetXEnd, double, xEnd, updateViewHeader) void Matrix::setXEnd(double xEnd) { if (xEnd != d->xEnd) - exec(new MatrixSetXEndCmd(d, xEnd, i18n("%1: x-end changed"))); + exec(new MatrixSetXEndCmd(d, xEnd, ki18n("%1: x-end changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetYStart, double, yStart, updateViewHeader) void Matrix::setYStart(double yStart) { if (yStart != d->yStart) - exec(new MatrixSetYStartCmd(d, yStart, i18n("%1: y-start changed"))); + exec(new MatrixSetYStartCmd(d, yStart, ki18n("%1: y-start changed"))); } STD_SETTER_CMD_IMPL_F_S(Matrix, SetYEnd, double, yEnd, updateViewHeader) void Matrix::setYEnd(double yEnd) { if (yEnd != d->yEnd) - exec(new MatrixSetYEndCmd(d, yEnd, i18n("%1: y-end changed"))); + exec(new MatrixSetYEndCmd(d, yEnd, ki18n("%1: y-end changed"))); } STD_SETTER_CMD_IMPL_S(Matrix, SetNumericFormat, char, numericFormat) void Matrix::setNumericFormat(char format) { if (format != d->numericFormat) - exec(new MatrixSetNumericFormatCmd(d, format, i18n("%1: numeric format changed"))); + exec(new MatrixSetNumericFormatCmd(d, format, ki18n("%1: numeric format changed"))); } STD_SETTER_CMD_IMPL_S(Matrix, SetPrecision, int, precision) void Matrix::setPrecision(int precision) { if (precision != d->precision) - exec(new MatrixSetPrecisionCmd(d, precision, i18n("%1: precision changed"))); + exec(new MatrixSetPrecisionCmd(d, precision, ki18n("%1: precision changed"))); } //TODO: make this undoable? void Matrix::setHeaderFormat(Matrix::HeaderFormat format) { d->headerFormat = format; m_model->updateHeader(); if (m_view) m_view->resizeHeaders(); emit headerFormatChanged(format); } //columns void Matrix::insertColumns(int before, int count) { if (count < 1 || before < 0 || before > columnCount()) return; WAIT_CURSOR; exec(new MatrixInsertColumnsCmd(d, before, count)); RESET_CURSOR; } void Matrix::appendColumns(int count) { insertColumns(columnCount(), count); } void Matrix::removeColumns(int first, int count) { if (count < 1 || first < 0 || first+count > columnCount()) return; WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Text: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Integer: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixRemoveColumnsCmd(d, first, count)); break; } RESET_CURSOR; } void Matrix::clearColumn(int c) { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Text: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Integer: exec(new MatrixClearColumnCmd(d, c)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixClearColumnCmd(d, c)); break; } RESET_CURSOR; } //rows void Matrix::insertRows(int before, int count) { if (count < 1 || before < 0 || before > rowCount()) return; WAIT_CURSOR; exec(new MatrixInsertRowsCmd(d, before, count)); RESET_CURSOR; } void Matrix::appendRows(int count) { insertRows(rowCount(), count); } void Matrix::removeRows(int first, int count) { if (count < 1 || first < 0 || first+count > rowCount()) return; WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Text: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Integer: exec(new MatrixRemoveRowsCmd(d, first, count)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixRemoveRowsCmd(d, first, count)); break; } RESET_CURSOR; } void Matrix::clearRow(int r) { switch (d->mode) { case AbstractColumn::Numeric: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, 0.0)); break; case AbstractColumn::Text: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, QString())); break; case AbstractColumn::Integer: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, 0)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int c = 0; c < columnCount(); ++c) exec(new MatrixSetCellValueCmd(d, r, c, QDateTime())); break; } } //! Return the value in the given cell (needs explicit instantiation) template T Matrix::cell(int row, int col) const { return d->cell(row, col); } template double Matrix::cell(int row, int col) const; template int Matrix::cell(int row, int col) const; template QDateTime Matrix::cell(int row, int col) const; template QString Matrix::cell(int row, int col) const; //! Return the text displayed in the given cell (needs explicit instantiation) template QString Matrix::text(int row, int col) { return QLocale().toString(cell(row,col)); } // special cases template <> QString Matrix::text(int row, int col) { return QLocale().toString(cell(row,col), d->numericFormat, d->precision); } template <> QString Matrix::text(int row, int col) { return cell(row,col); } template QString Matrix::text(int row, int col); template QString Matrix::text(int row, int col); //! Set the value of the cell (needs explicit instantiation) template void Matrix::setCell(int row, int col, T value) { if(row < 0 || row >= rowCount()) return; if(col < 0 || col >= columnCount()) return; exec(new MatrixSetCellValueCmd(d, row, col, value)); } template void Matrix::setCell(int row, int col, double value); template void Matrix::setCell(int row, int col, int value); template void Matrix::setCell(int row, int col, QDateTime value); template void Matrix::setCell(int row, int col, QString value); void Matrix::clearCell(int row, int col) { switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixSetCellValueCmd(d, row, col, 0.0)); break; case AbstractColumn::Text: exec(new MatrixSetCellValueCmd(d, row, col, QString())); break; case AbstractColumn::Integer: exec(new MatrixSetCellValueCmd(d, row, col, 0)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixSetCellValueCmd(d, row, col, QDateTime())); break; } } void Matrix::setDimensions(int rows, int cols) { if( (rows < 0) || (cols < 0 ) || (rows == rowCount() && cols == columnCount()) ) return; WAIT_CURSOR; beginMacro(i18n("%1: set matrix size to %2x%3", name(), rows, cols)); int col_diff = cols - columnCount(); if (col_diff > 0) insertColumns(columnCount(), col_diff); else if (col_diff < 0) removeColumns(columnCount() + col_diff, -col_diff); int row_diff = rows - rowCount(); if(row_diff > 0) appendRows(row_diff); else if (row_diff < 0) removeRows(rowCount() + row_diff, -row_diff); endMacro(); RESET_CURSOR; } void Matrix::copy(Matrix* other) { WAIT_CURSOR; beginMacro(i18n("%1: copy %2", name(), other->name())); int rows = other->rowCount(); int columns = other->columnCount(); setDimensions(rows, columns); for (int i=0; irowHeight(i)); for (int i=0; icolumnWidth(i)); d->suppressDataChange = true; switch (d->mode) { case AbstractColumn::Numeric: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Text: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Integer: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int i = 0; i < columns; i++) setColumnCells(i, 0, rows-1, other->columnCells(i, 0, rows-1)); break; } setCoordinates(other->xStart(), other->xEnd(), other->yStart(), other->yEnd()); setNumericFormat(other->numericFormat()); setPrecision(other->precision()); d->formula = other->formula(); d->suppressDataChange = false; emit dataChanged(0, 0, rows-1, columns-1); if (m_view) m_view->adjustHeaders(); endMacro(); RESET_CURSOR; } //! Duplicate the matrix inside its folder void Matrix::duplicate() { Matrix* matrix = new Matrix(0, rowCount(), columnCount(), name()); matrix->copy(this); if (folder()) folder()->addChild(matrix); } void Matrix::addRows() { if (!m_view) return; WAIT_CURSOR; int count = m_view->selectedRowCount(false); beginMacro(i18np("%1: add %2 rows", "%1: add %2 rows", name(), count)); exec(new MatrixInsertRowsCmd(d, rowCount(), count)); endMacro(); RESET_CURSOR; } void Matrix::addColumns() { if (!m_view) return; WAIT_CURSOR; int count = m_view->selectedRowCount(false); beginMacro(i18np("%1: add %2 column", "%1: add %2 columns", name(), count)); exec(new MatrixInsertColumnsCmd(d, columnCount(), count)); endMacro(); RESET_CURSOR; } void Matrix::setCoordinates(double x1, double x2, double y1, double y2) { exec(new MatrixSetCoordinatesCmd(d, x1, x2, y1, y2)); } void Matrix::setFormula(const QString& formula) { exec(new MatrixSetFormulaCmd(d, formula)); } //! This method should only be called by the view. /** This method does not change the view, it only changes the * values that are saved when the matrix is saved. The view * has to take care of reading and applying these values */ void Matrix::setRowHeight(int row, int height) { d->setRowHeight(row, height); } //! This method should only be called by the view. /** This method does not change the view, it only changes the * values that are saved when the matrix is saved. The view * has to take care of reading and applying these values */ void Matrix::setColumnWidth(int col, int width) { d->setColumnWidth(col, width); } int Matrix::rowHeight(int row) const { return d->rowHeight(row); } int Matrix::columnWidth(int col) const { return d->columnWidth(col); } //! Return the values in the given cells as vector template QVector Matrix::columnCells(int col, int first_row, int last_row) { return d->columnCells(col, first_row, last_row); } //! Set the values in the given cells from a double vector template void Matrix::setColumnCells(int col, int first_row, int last_row, const QVector& values) { WAIT_CURSOR; exec(new MatrixSetColumnCellsCmd(d, col, first_row, last_row, values)); RESET_CURSOR; } //! Return the values in the given cells as vector (needs explicit instantiation) template QVector Matrix::rowCells(int row, int first_column, int last_column) { return d->rowCells(row, first_column, last_column); } template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); template QVector Matrix::rowCells(int row, int first_column, int last_column); //! Set the values in the given cells from a double vector template void Matrix::setRowCells(int row, int first_column, int last_column, const QVector& values) { WAIT_CURSOR; exec(new MatrixSetRowCellsCmd(d, row, first_column, last_column, values)); RESET_CURSOR; } void Matrix::setData(void* data) { bool isEmpty = false; switch (d->mode) { case AbstractColumn::Numeric: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Text: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Integer: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: if (static_cast>*>(data)->isEmpty()) isEmpty = true; break; } if (!isEmpty) exec(new MatrixReplaceValuesCmd(d, data)); } //############################################################################## //######################### Public slots ##################################### //############################################################################## //! Clear the whole matrix (i.e. reset all cells) void Matrix::clear() { WAIT_CURSOR; beginMacro(i18n("%1: clear", name())); switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Text: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixClearCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixClearCmd(d)); break; } endMacro(); RESET_CURSOR; } void Matrix::transpose() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Text: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixTransposeCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixTransposeCmd(d)); break; } RESET_CURSOR; } void Matrix::mirrorHorizontally() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Text: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixMirrorHorizontallyCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixMirrorHorizontallyCmd(d)); break; } RESET_CURSOR; } void Matrix::mirrorVertically() { WAIT_CURSOR; switch (d->mode) { case AbstractColumn::Numeric: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Text: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Integer: exec(new MatrixMirrorVerticallyCmd(d)); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: exec(new MatrixMirrorVerticallyCmd(d)); break; } RESET_CURSOR; } //############################################################################## //###################### Private implementation ############################### //############################################################################## MatrixPrivate::MatrixPrivate(Matrix* owner, const AbstractColumn::ColumnMode m) : q(owner), data(0), mode(m), rowCount(0), columnCount(0), suppressDataChange(false) { switch (mode) { case AbstractColumn::Numeric: data = new QVector>(); break; case AbstractColumn::Text: data = new QVector>(); break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: data = new QVector>(); break; case AbstractColumn::Integer: data = new QVector>(); break; } } MatrixPrivate::~MatrixPrivate() { if (data) { switch (mode) { case AbstractColumn::Numeric: delete static_cast>*>(data); break; case AbstractColumn::Text: delete static_cast>*>(data); break; case AbstractColumn::Integer: delete static_cast>*>(data); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: delete static_cast>*>(data); break; } } } void MatrixPrivate::updateViewHeader() { q->m_view->model()->updateHeader(); } /*! Insert \count columns before column number \c before */ void MatrixPrivate::insertColumns(int before, int count) { Q_ASSERT(before >= 0); Q_ASSERT(before <= columnCount); emit q->columnsAboutToBeInserted(before, count); switch (mode) { case AbstractColumn::Numeric: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Text: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Integer: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int i = 0; i < count; i++) { static_cast>*>(data)->insert(before+i, QVector(rowCount)); columnWidths.insert(before+i, 0); } break; } columnCount += count; emit q->columnsInserted(before, count); } /*! Remove \c count columns starting with column index \c first */ void MatrixPrivate::removeColumns(int first, int count) { emit q->columnsAboutToBeRemoved(first, count); Q_ASSERT(first >= 0); Q_ASSERT(first + count <= columnCount); switch (mode) { case AbstractColumn::Numeric: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Text: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Integer: (static_cast>*>(data))->remove(first, count); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: (static_cast>*>(data))->remove(first, count); break; } for (int i = 0; i < count; i++) columnWidths.remove(first); columnCount -= count; emit q->columnsRemoved(first, count); } /*! Insert \c count rows before row with the index \c before */ void MatrixPrivate::insertRows(int before, int count) { emit q->rowsAboutToBeInserted(before, count); Q_ASSERT(before >= 0); Q_ASSERT(before <= rowCount); switch (mode) { case AbstractColumn::Numeric: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, 0.0); break; case AbstractColumn::Text: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, QString()); break; case AbstractColumn::Integer: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, 0); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int col = 0; col < columnCount; col++) for (int i = 0; i < count; i++) (static_cast>*>(data))->operator[](col).insert(before+i, QDateTime()); } for(int i=0; irowsInserted(before, count); } /*! Remove \c count columns starting from the column with index \c first */ void MatrixPrivate::removeRows(int first, int count) { emit q->rowsAboutToBeRemoved(first, count); Q_ASSERT(first >= 0); Q_ASSERT(first+count <= rowCount); switch (mode) { case AbstractColumn::Numeric: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Text: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Integer: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int col = 0; col < columnCount; col++) (static_cast>*>(data))->operator[](col).remove(first, count); break; } for (int i = 0; i < count; i++) rowHeights.remove(first); rowCount -= count; emit q->rowsRemoved(first, count); } //! Fill column with zeroes void MatrixPrivate::clearColumn(int col) { switch (mode) { case AbstractColumn::Numeric: static_cast>*>(data)->operator[](col).fill(0.0); break; case AbstractColumn::Text: static_cast>*>(data)->operator[](col).fill(QString()); break; case AbstractColumn::Integer: static_cast>*>(data)->operator[](col).fill(0); break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: static_cast>*>(data)->operator[](col).fill(QDateTime()); break; } if (!suppressDataChange) emit q->dataChanged(0, col, rowCount-1, col); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## void Matrix::save(QXmlStreamWriter* writer) const { writer->writeStartElement("matrix"); writeBasicAttributes(writer); writeCommentElement(writer); //formula writer->writeStartElement("formula"); writer->writeCharacters(d->formula); writer->writeEndElement(); //format writer->writeStartElement("format"); writer->writeAttribute("mode", QString::number(d->mode)); writer->writeAttribute("headerFormat", QString::number(d->headerFormat)); writer->writeAttribute("numericFormat", QString(QChar(d->numericFormat))); writer->writeAttribute("precision", QString::number(d->precision)); writer->writeEndElement(); //dimensions writer->writeStartElement("dimension"); writer->writeAttribute("columns", QString::number(d->columnCount)); writer->writeAttribute("rows", QString::number(d->rowCount)); writer->writeAttribute("x_start", QString::number(d->xStart)); writer->writeAttribute("x_end", QString::number(d->xEnd)); writer->writeAttribute("y_start", QString::number(d->yStart)); writer->writeAttribute("y_end", QString::number(d->yEnd)); writer->writeEndElement(); //vector with row heights writer->writeStartElement("row_heights"); const char* data = reinterpret_cast(d->rowHeights.constData()); int size = d->rowHeights.size() * sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data,size).toBase64()); writer->writeEndElement(); //vector with column widths writer->writeStartElement("column_widths"); data = reinterpret_cast(d->columnWidths.constData()); size = d->columnWidths.size()*sizeof(int); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); //columns switch (d->mode) { case AbstractColumn::Numeric: size = d->rowCount*sizeof(double); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Text: size = d->rowCount*sizeof(QString); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Integer: size = d->rowCount*sizeof(int); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: size = d->rowCount*sizeof(QDateTime); for (int i = 0; i < d->columnCount; ++i) { data = reinterpret_cast(static_cast>*>(d->data)->at(i).constData()); writer->writeStartElement("column"); writer->writeCharacters(QByteArray::fromRawData(data, size).toBase64()); writer->writeEndElement(); } break; } writer->writeEndElement(); // "matrix" } bool Matrix::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "matrix") { reader->raiseError(i18n("no matrix element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "matrix") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if(!preview && reader->name() == "formula") { d->formula = reader->text().toString().trimmed(); } else if (!preview && reader->name() == "format") { attribs = reader->attributes(); str = attribs.value("mode").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'mode'")); + reader->raiseWarning(attributeWarning.subs("mode").toString()); else d->mode = AbstractColumn::ColumnMode(str.toInt()); str = attribs.value("headerFormat").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'headerFormat'")); + reader->raiseWarning(attributeWarning.subs("headerFormat").toString()); else d->headerFormat = Matrix::HeaderFormat(str.toInt()); str = attribs.value("numericFormat").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'numericFormat'")); + reader->raiseWarning(attributeWarning.subs("numericFormat").toString()); else { QByteArray formatba = str.toLatin1(); d->numericFormat = *formatba.data(); } str = attribs.value("precision").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'precision'")); + reader->raiseWarning(attributeWarning.subs("precision").toString()); else d->precision = str.toInt(); } else if (!preview && reader->name() == "dimension") { attribs = reader->attributes(); str = attribs.value("columns").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'columns'")); + reader->raiseWarning(attributeWarning.subs("columns").toString()); else d->columnCount = str.toInt(); str = attribs.value("rows").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'rows'")); + reader->raiseWarning(attributeWarning.subs("rows").toString()); else d->rowCount = str.toInt(); str = attribs.value("x_start").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x_start'")); + reader->raiseWarning(attributeWarning.subs("x_start").toString()); else d->xStart = str.toDouble(); str = attribs.value("x_end").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x_end'")); + reader->raiseWarning(attributeWarning.subs("x_end").toString()); else d->xEnd = str.toDouble(); str = attribs.value("y_start").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y_start'")); + reader->raiseWarning(attributeWarning.subs("y_start").toString()); else d->yStart = str.toDouble(); str = attribs.value("y_end").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y_end'")); + reader->raiseWarning(attributeWarning.subs("y_end").toString()); else d->yEnd = str.toDouble(); } else if (!preview && reader->name() == "row_heights") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); int count = bytes.size()/sizeof(int); d->rowHeights.resize(count); memcpy(d->rowHeights.data(), bytes.data(), count*sizeof(int)); } else if (!preview && reader->name() == "column_widths") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); int count = bytes.size()/sizeof(int); d->columnWidths.resize(count); memcpy(d->columnWidths.data(), bytes.data(), count*sizeof(int)); } else if (!preview && reader->name() == "column") { //TODO: parallelize reading of columns? reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray bytes = QByteArray::fromBase64(content.toAscii()); switch (d->mode) { case AbstractColumn::Numeric: { int count = bytes.size()/sizeof(double); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(double)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Text: { int count = bytes.size()/sizeof(QString); QVector column; column.resize(count); //TODO: warning (GCC8): writing to an object of type 'class QDateTime' with no trivial copy-assignment; use copy-assignment or copy-initialization instead memcpy(column.data(), bytes.data(), count*sizeof(QString)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Integer: { int count = bytes.size()/sizeof(int); QVector column; column.resize(count); memcpy(column.data(), bytes.data(), count*sizeof(int)); static_cast>*>(d->data)->append(column); break; } case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: { int count = bytes.size()/sizeof(QDateTime); QVector column; column.resize(count); //TODO: warning (GCC8): writing to an object of type 'class QDateTime' with no trivial copy-assignment; use copy-assignment or copy-initialization instead memcpy(column.data(), bytes.data(), count*sizeof(QDateTime)); static_cast>*>(d->data)->append(column); break; } } } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } //############################################################################## //######################## Data Import ####################################### //############################################################################## int Matrix::prepareImport(QVector& dataContainer, AbstractFileFilter::ImportMode mode, int actualRows, int actualCols, QStringList colNameList, QVector columnMode) { QDEBUG("prepareImport() rows =" << actualRows << " cols =" << actualCols); Q_UNUSED(colNameList); int columnOffset = 0; setUndoAware(false); setSuppressDataChangedSignal(true); // resize the matrix if (mode == AbstractFileFilter::Replace) { clear(); setDimensions(actualRows, actualCols); } else { if (rowCount() < actualRows) setDimensions(actualRows, actualCols); else setDimensions(rowCount(), actualCols); } // data() returns a void* which is a pointer to a matrix of any data type (see ColumnPrivate.cpp) dataContainer.resize(actualCols); switch (columnMode[0]) { // only columnMode[0] is used case AbstractColumn::Numeric: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } d->mode = AbstractColumn::Numeric; break; case AbstractColumn::Integer: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } d->mode = AbstractColumn::Integer; break; case AbstractColumn::Text: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } d->mode = AbstractColumn::Text; break; case AbstractColumn::Day: case AbstractColumn::Month: case AbstractColumn::DateTime: for (int n = 0; n < actualCols; n++) { QVector* vector = &(static_cast>*>(data())->operator[](n)); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); } d->mode = AbstractColumn::DateTime; break; } return columnOffset; } void Matrix::finalizeImport(int columnOffset, int startColumn, int endColumn, const QString& dateTimeFormat, AbstractFileFilter::ImportMode importMode) { DEBUG("Matrix::finalizeImport()"); Q_UNUSED(columnOffset); Q_UNUSED(startColumn); Q_UNUSED(endColumn); Q_UNUSED(dateTimeFormat); Q_UNUSED(importMode); setSuppressDataChangedSignal(false); setChanged(); setUndoAware(true); DEBUG("Matrix::finalizeImport() DONE"); } diff --git a/src/backend/matrix/matrixcommands.h b/src/backend/matrix/matrixcommands.h index dda7c5d4b..18e45cc95 100644 --- a/src/backend/matrix/matrixcommands.h +++ b/src/backend/matrix/matrixcommands.h @@ -1,410 +1,410 @@ /*************************************************************************** File : matrixcommands.h Project : LabPlot Description : Commands used in Matrix (part of the undo/redo framework) -------------------------------------------------------------------- Copyright : (C) 2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 MATRIX_COMMANDS_H #define MATRIX_COMMANDS_H #include #include -#include +#include #include "Matrix.h" #include "MatrixPrivate.h" //! Insert columns class MatrixInsertColumnsCmd : public QUndoCommand { public: MatrixInsertColumnsCmd(MatrixPrivate*, int before, int count, QUndoCommand* = 0); void redo() override; void undo() override; private: MatrixPrivate* m_private_obj; int m_before; //! Column to insert before int m_count; //! The number of new columns }; //! Insert rows class MatrixInsertRowsCmd : public QUndoCommand { public: MatrixInsertRowsCmd(MatrixPrivate*, int before, int count, QUndoCommand* = 0); void redo() override; void undo() override; private: MatrixPrivate* m_private_obj; int m_before; //! Row to insert before int m_count; //! The number of new rows }; //! Remove columns template class MatrixRemoveColumnsCmd : public QUndoCommand { public: MatrixRemoveColumnsCmd(MatrixPrivate* private_obj, int first, int count, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_first(first), m_count(count) { setText(i18np("%1: remove %2 column", "%1: remove %2 columns", m_private_obj->name(), m_count)); } void redo() override { if(m_backups.isEmpty()) { int last_row = m_private_obj->rowCount-1; for (int i = 0; i < m_count; i++) m_backups.append(m_private_obj->columnCells(m_first+i, 0, last_row)); } m_private_obj->removeColumns(m_first, m_count); emit m_private_obj->q->columnCountChanged(m_private_obj->columnCount); } void undo() override { m_private_obj->insertColumns(m_first, m_count); int last_row = m_private_obj->rowCount-1; //TODO: use memcopy to copy from the backup vector for (int i = 0; i < m_count; i++) m_private_obj->setColumnCells(m_first+i, 0, last_row, m_backups.at(i)); emit m_private_obj->q->columnCountChanged(m_private_obj->columnCount); } private: MatrixPrivate* m_private_obj; int m_first; //! First column to remove int m_count; //! The number of columns to remove QVector> m_backups; //! Backups of the removed columns }; //! Remove rows template class MatrixRemoveRowsCmd : public QUndoCommand { public: MatrixRemoveRowsCmd(MatrixPrivate* private_obj, int first, int count, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_first(first), m_count(count) { setText(i18np("%1: remove %2 row", "%1: remove %2 rows", m_private_obj->name(), m_count)); } void redo() override { if(m_backups.isEmpty()) { int last_row = m_first+m_count-1; for (int col = 0; col < m_private_obj->columnCount; col++) m_backups.append(m_private_obj->columnCells(col, m_first, last_row)); } m_private_obj->removeRows(m_first, m_count); emit m_private_obj->q->rowCountChanged(m_private_obj->rowCount); } void undo() override { m_private_obj->insertRows(m_first, m_count); int last_row = m_first+m_count-1; for (int col = 0; col < m_private_obj->columnCount; col++) m_private_obj->setColumnCells(col, m_first, last_row, m_backups.at(col)); emit m_private_obj->q->rowCountChanged(m_private_obj->rowCount); } private: MatrixPrivate* m_private_obj; int m_first; //! First row to remove int m_count; //! The number of rows to remove QVector< QVector > m_backups; //! Backups of the removed rows }; //! Clear matrix template class MatrixClearCmd : public QUndoCommand { public: explicit MatrixClearCmd(MatrixPrivate* private_obj, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj) { setText(i18n("%1: clear", m_private_obj->name())); } void redo() override { if(m_backups.isEmpty()) { int last_row = m_private_obj->rowCount-1; for (int i = 0; i < m_private_obj->columnCount; i++) m_backups.append(m_private_obj->columnCells(i, 0, last_row)); } for (int i = 0; i < m_private_obj->columnCount; i++) m_private_obj->clearColumn(i); } void undo() override { int last_row = m_private_obj->rowCount-1; for (int i = 0; i < m_private_obj->columnCount; i++) m_private_obj->setColumnCells(i, 0, last_row, m_backups.at(i)); } private: MatrixPrivate* m_private_obj; QVector> m_backups; //! Backups of the cleared cells }; //! Clear matrix column template class MatrixClearColumnCmd : public QUndoCommand { public: MatrixClearColumnCmd(MatrixPrivate* private_obj, int col, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_col(col) { setText(i18n("%1: clear column %2", m_private_obj->name(), m_col+1)); } void redo() override { if(m_backup.isEmpty()) m_backup = m_private_obj->columnCells(m_col, 0, m_private_obj->rowCount-1); m_private_obj->clearColumn(m_col); } void undo() override { m_private_obj->setColumnCells(m_col, 0, m_private_obj->rowCount-1, m_backup); } private: MatrixPrivate* m_private_obj; int m_col; //! The index of the column QVector m_backup; //! Backup of the cleared column }; // Set cell value template class MatrixSetCellValueCmd : public QUndoCommand { public: MatrixSetCellValueCmd(MatrixPrivate* private_obj, int row, int col, T value, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_row(row), m_col(col), m_value(value) { // remark: don't use many QString::arg() calls in ctors of commands that might be called often, // they use a lot of execution time setText(i18n("%1: set cell value", m_private_obj->name())); } void redo() override { m_old_value = m_private_obj->cell(m_row, m_col); m_private_obj->setCell(m_row, m_col, m_value); } void undo() override { m_private_obj->setCell(m_row, m_col, m_old_value); } private: MatrixPrivate* m_private_obj; int m_row; //! The index of the row int m_col; //! The index of the column T m_value; //! New cell value T m_old_value; //! Backup of the changed value }; // Set matrix coordinates class MatrixSetCoordinatesCmd : public QUndoCommand { public: MatrixSetCoordinatesCmd(MatrixPrivate*, double x1, double x2, double y1, double y2, QUndoCommand* = 0); void redo() override; void undo() override; private: MatrixPrivate* m_private_obj; double m_new_x1; double m_new_x2; double m_new_y1; double m_new_y2; double m_old_x1; double m_old_x2; double m_old_y1; double m_old_y2; }; //! Set matrix formula class MatrixSetFormulaCmd : public QUndoCommand { public: MatrixSetFormulaCmd(MatrixPrivate*, QString formula); void redo() override; void undo() override; private: MatrixPrivate* m_private_obj; QString m_other_formula; }; // Set cell values for (a part of) a column at once template class MatrixSetColumnCellsCmd : public QUndoCommand { public: MatrixSetColumnCellsCmd(MatrixPrivate* private_obj, int col, int first_row, int last_row, const QVector& values, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_col(col), m_first_row(first_row), m_last_row(last_row), m_values(values) { setText(i18n("%1: set cell values", m_private_obj->name())); } void redo() override { if (m_old_values.isEmpty()) m_old_values = m_private_obj->columnCells(m_col, m_first_row, m_last_row); m_private_obj->setColumnCells(m_col, m_first_row, m_last_row, m_values); } void undo() override { m_private_obj->setColumnCells(m_col, m_first_row, m_last_row, m_old_values); } private: MatrixPrivate* m_private_obj; int m_col; //! The index of the column int m_first_row; //! The index of the first row int m_last_row; //! The index of the last row QVector m_values; //! New cell values QVector m_old_values; //! Backup of the changed values }; //! Set cell values for (a part of) a row at once template class MatrixSetRowCellsCmd : public QUndoCommand { public: MatrixSetRowCellsCmd(MatrixPrivate* private_obj, int row, int first_column, int last_column, const QVector& values, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj), m_row(row), m_first_column(first_column), m_last_column(last_column), m_values(values) { setText(i18n("%1: set cell values", m_private_obj->name())); } void redo() override { if (m_old_values.isEmpty()) m_old_values = m_private_obj->rowCells(m_row, m_first_column, m_last_column); m_private_obj->setRowCells(m_row, m_first_column, m_last_column, m_values); } void undo() override { m_private_obj->setRowCells(m_row, m_first_column, m_last_column, m_old_values); } private: MatrixPrivate* m_private_obj; int m_row; //! The index of the row int m_first_column; //! The index of the first column int m_last_column; //! The index of the last column QVector m_values; //! New cell values QVector m_old_values; //! Backup of the changed values }; //! Transpose the matrix template class MatrixTransposeCmd : public QUndoCommand { public: explicit MatrixTransposeCmd(MatrixPrivate* private_obj, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj) { setText(i18n("%1: transpose", m_private_obj->name())); } void redo() override { int rows = m_private_obj->rowCount; int cols = m_private_obj->columnCount; int temp_size = qMax(rows, cols); m_private_obj->suppressDataChange = true; if (cols < rows) m_private_obj->insertColumns(cols, temp_size - cols); else if (cols > rows) m_private_obj->insertRows(rows, temp_size - rows); for (int i = 1; i < temp_size; i++) { QVector row = m_private_obj->rowCells(i, 0, i-1); QVector col = m_private_obj->columnCells(i, 0, i-1); m_private_obj->setRowCells(i, 0, i-1, col); m_private_obj->setColumnCells(i, 0, i-1, row); } if (cols < rows) m_private_obj->removeRows(cols, temp_size - cols); else if (cols > rows) m_private_obj->removeColumns(rows, temp_size - rows); m_private_obj->suppressDataChange = false; m_private_obj->emitDataChanged(0, 0, m_private_obj->rowCount-1, m_private_obj->columnCount-1); } void undo() override { redo(); } private: MatrixPrivate* m_private_obj; }; //! Mirror the matrix horizontally template class MatrixMirrorHorizontallyCmd : public QUndoCommand { public: explicit MatrixMirrorHorizontallyCmd(MatrixPrivate* private_obj, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj) { setText(i18n("%1: mirror horizontally", m_private_obj->name())); } void redo() override { int rows = m_private_obj->rowCount; int cols = m_private_obj->columnCount; int middle = cols/2; m_private_obj->suppressDataChange = true; for (int i = 0; i temp = m_private_obj->columnCells(i, 0, rows-1); m_private_obj->setColumnCells(i, 0, rows-1, m_private_obj->columnCells(cols-i-1, 0, rows-1)); m_private_obj->setColumnCells(cols-i-1, 0, rows-1, temp); } m_private_obj->suppressDataChange = false; m_private_obj->emitDataChanged(0, 0, rows-1, cols-1); } void undo() override { redo(); } private: MatrixPrivate* m_private_obj; }; // Mirror the matrix vertically template class MatrixMirrorVerticallyCmd : public QUndoCommand { public: explicit MatrixMirrorVerticallyCmd(MatrixPrivate* private_obj, QUndoCommand* parent = 0) : QUndoCommand(parent), m_private_obj(private_obj) { setText(i18n("%1: mirror vertically", m_private_obj->name())); } void redo() override { int rows = m_private_obj->rowCount; int cols = m_private_obj->columnCount; int middle = rows/2; m_private_obj->suppressDataChange = true; for (int i = 0; i < middle; i++) { QVector temp = m_private_obj->rowCells(i, 0, cols-1); m_private_obj->setRowCells(i, 0, cols-1, m_private_obj->rowCells(rows-i-1, 0, cols-1)); m_private_obj->setRowCells(rows-i-1, 0, cols-1, temp); } m_private_obj->suppressDataChange = false; m_private_obj->emitDataChanged(0, 0, rows-1, cols-1); } void undo() override { redo(); } private: MatrixPrivate* m_private_obj; }; // Replace matrix values class MatrixReplaceValuesCmd : public QUndoCommand { public: explicit MatrixReplaceValuesCmd(MatrixPrivate*, void* new_values, QUndoCommand* = 0); void redo() override; void undo() override; private: MatrixPrivate* m_private_obj; void* m_old_values; void* m_new_values; }; #endif // MATRIX_COMMANDS_H diff --git a/src/backend/note/Note.cpp b/src/backend/note/Note.cpp index 5a71542a1..4536a037b 100644 --- a/src/backend/note/Note.cpp +++ b/src/backend/note/Note.cpp @@ -1,180 +1,180 @@ /*************************************************************************** File : Notes.cpp Project : LabPlot Description : Notes Widget for taking notes -------------------------------------------------------------------- Copyright : (C) 2009-2015 Garvit Khatri (garvitdelhi@gmail.com) Copyright : (C) 2016 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 "Note.h" #include "commonfrontend/note/NoteView.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" #include #include #include #include #include #include -#include +#include Note::Note(const QString& name) : AbstractPart(name), m_view(nullptr) { KConfig config; KConfigGroup group = config.group("Notes"); m_backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::yellow)); m_textColor = group.readEntry("TextColor", QColor(Qt::black)); m_textFont = group.readEntry("TextFont", QFont()); } QIcon Note::icon() const { return QIcon::fromTheme("document-new"); } bool Note::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); - dlg->setWindowTitle(i18n("Print Worksheet")); + dlg->setWindowTitle(i18nc("@title:window", "Print Worksheet")); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) m_view->print(&printer); delete dlg; return ret; } bool Note::printPreview() const { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &NoteView::print); return dlg->exec(); } bool Note::exportView() const { return false; } void Note::setNote(const QString& note) { m_note = note; } const QString& Note::note() const { return m_note; } void Note::setBackgroundColor(const QColor& color) { m_backgroundColor = color; emit backgroundColorChanged(color); } const QColor& Note::backgroundColor() const { return m_backgroundColor; } void Note::setTextColor(const QColor& color) { m_textColor = color; emit textColorChanged(color); } const QColor& Note::textColor() const{ return m_textColor; } void Note::setTextFont(const QFont& font) { m_textFont = font; emit textFontChanged(font); } const QFont& Note::textFont() const { return m_textFont; } QWidget* Note::view() const { if (!m_partView) { m_view = new NoteView(const_cast(this)); m_partView = m_view; } return m_partView; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Note::save(QXmlStreamWriter* writer) const { writer->writeStartElement("note"); writeBasicAttributes(writer); writeCommentElement(writer); writer->writeStartElement("background"); WRITE_QCOLOR(m_backgroundColor); writer->writeEndElement(); writer->writeStartElement("text"); WRITE_QCOLOR(m_textColor); WRITE_QFONT(m_textFont); writer->writeAttribute("text", m_note); writer->writeEndElement(); writer->writeEndElement(); // close "note" section } bool Note::load(XmlStreamReader* reader, bool preview) { if (!reader->isStartElement() || reader->name() != "note") { reader->raiseError(i18n("no note element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "note") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); READ_QCOLOR(m_backgroundColor); } else if (!preview && reader->name() == "text") { attribs = reader->attributes(); READ_QCOLOR(m_textColor); READ_QFONT(m_textFont); m_note = attribs.value("text").toString(); } } return true; } diff --git a/src/backend/nsl/nsl_fit.c b/src/backend/nsl/nsl_fit.c index a3022fb61..6f4600e12 100644 --- a/src/backend/nsl/nsl_fit.c +++ b/src/backend/nsl/nsl_fit.c @@ -1,670 +1,686 @@ /*************************************************************************** File : nsl_fit.c Project : LabPlot Description : NSL (non)linear fit functions -------------------------------------------------------------------- - Copyright : (C) 2016-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) + Copyright : (C) 2016-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "nsl_fit.h" #include "nsl_common.h" #include "nsl_sf_basic.h" #include #include #include #include #include const char* nsl_fit_model_category_name[] = {i18n("Basic functions"), i18n("Peak functions"), i18n("Growth (sigmoidal)"), i18n("Statistics (distributions)"), i18n("Custom")}; const char* nsl_fit_model_basic_name[] = {i18n("Polynomial"), i18n("Power"), i18n("Exponential"), i18n("Inverse exponential"), i18n("Fourier")}; const char* nsl_fit_model_basic_equation[] = {"c0 + c1*x", "a*x^b", "a*exp(b*x)", "a*(1-exp(b*x)) + c", "a0 + (a1*cos(w*x) + b1*sin(w*x))"}; const char* nsl_fit_model_basic_pic_name[] = {"polynom", "power", "exponential", "inv_exponential", "fourier"}; -const char* nsl_fit_model_peak_name[] = {i18n("Gaussian (normal)"), i18n("Cauchy-Lorentz"), i18n("Hyperbolic secant (sech)"), i18n("Logistic (sech squared)"), i18n("Voigt profile")}; +const char* nsl_fit_model_peak_name[] = {i18n("Gaussian (normal)"), i18n("Cauchy-Lorentz"), i18n("Hyperbolic secant (sech)"), i18n("Logistic (sech squared)"), + i18n("Voigt profile"), i18n("Pseudo-Voigt (same width)")}; const char* nsl_fit_model_peak_equation[] = {"a/sqrt(2*pi)/s * exp(-((x-mu)/s)^2/2)", "a/pi * g/(g^2+(x-mu)^2)", "a/pi/s * sech((x-mu)/s)", - "a/4/s * sech((x-mu)/2/s)**2", "a*voigt(x - mu, s, g)"}; -const char* nsl_fit_model_peak_pic_name[] = {"gaussian", "cauchy_lorentz", "sech", "logistic", "voigt"}; + "a/4/s * sech((x-mu)/2/s)**2", "a*voigt(x - mu, s, g)", "a*pseudovoigt1(x - mu, et, w)"}; // eta is already used as function +const char* nsl_fit_model_peak_pic_name[] = {"gaussian", "cauchy_lorentz", "sech", "logistic", "voigt", "pseudovoigt1"}; const char* nsl_fit_model_growth_name[] = {i18n("Inverse tangent"), i18n("Hyperbolic tangent"), i18n("Algebraic sigmoid"), i18n("Logistic function"), i18n("Error function (erf)"), i18n("Hill"), i18n("Gompertz"), i18n("Gudermann (gd)")}; const char* nsl_fit_model_growth_equation[] = {"a * atan((x-mu)/s)", "a * tanh((x-mu)/s)", "a * (x-mu)/s/sqrt(1+((x-mu)/s)^2)", "a/(1+exp(-k*(x-mu)))", "a/2 * erf((x-mu)/s/sqrt(2))", "a * x^n/(s^n + x^n)", "a*exp(-b*exp(-c*x))", "a * asin(tanh((x-mu)/s))"}; const char* nsl_fit_model_growth_pic_name[] = {"atan", "tanh", "alg_sigmoid", "logistic_function", "erf", "hill", "gompertz", "gd"}; const char* nsl_fit_weight_type_name[] = {"No", "Instrumental (1/col^2)", "Direct (col)", "Inverse (1/col)", "Statistical (1/data)", "Statistical (Fit)", "Relative (1/data^2)", "Relative (Fit)"}; /* see http://seal.web.cern.ch/seal/documents/minuit/mnusersguide.pdf and https://lmfit.github.io/lmfit-py/bounds.html */ double nsl_fit_map_bound(double x, double min, double max) { if (max <= min) { printf("given bounds must fulfill max > min (min = %g, max = %g)! Giving up.\n", min, max); return DBL_MAX; } /* not bounded */ if (min == -DBL_MAX && max == DBL_MAX) return x; /* open bounds */ if (min == -DBL_MAX) return max + 1. - sqrt(x*x + 1.); if (max == DBL_MAX) return min - 1. + sqrt(x*x + 1.); return min + sin(x + 1.) * (max - min)/2.; /* alternative transformation for closed bounds return min + (max - min)/(1. + exp(-x)); */ } /* see http://seal.web.cern.ch/seal/documents/minuit/mnusersguide.pdf and https://lmfit.github.io/lmfit-py/bounds.html */ double nsl_fit_map_unbound(double x, double min, double max) { if (max <= min) { printf("given bounds must fulfill max > min (min = %g, max = %g)! Giving up.\n", min, max); return DBL_MAX; } if (x < min || x > max) { printf("given value must be within bounds! Giving up.\n"); return -DBL_MAX; } /* not bounded */ if (min == -DBL_MAX && max == DBL_MAX) return x; /* open bounds */ if (min == -DBL_MAX) return sqrt(gsl_pow_2(max - x + 1.) - 1.); if (max == DBL_MAX) return sqrt(gsl_pow_2(x - min + 1.) - 1.); return asin(2. * (x - min)/(max - min) - 1.); /* alternative transformation for closed bounds return -log((max - x)/(x - min)); */ } /********************** parameter derivatives ******************/ /* basic */ double nsl_fit_model_polynomial_param_deriv(double x, int j, double weight) { return sqrt(weight)*pow(x, j); } double nsl_fit_model_power1_param_deriv(unsigned int param, double x, double a, double b, double weight) { if (param == 0) return sqrt(weight)*pow(x, b); if (param == 1) return sqrt(weight)*a*pow(x, b)*log(x); return 0; } double nsl_fit_model_power2_param_deriv(unsigned int param, double x, double b, double c, double weight) { if (param == 0) return sqrt(weight); if (param == 1) return sqrt(weight)*pow(x, c); if (param == 2) return sqrt(weight)*b*pow(x, c)*log(x); return 0; } double nsl_fit_model_exponentialn_param_deriv(unsigned int param, double x, double *p, double weight) { if (param % 2 == 0) return sqrt(weight)*exp(p[param+1]*x); else return sqrt(weight)*p[param-1]*x*exp(p[param]*x); } double nsl_fit_model_inverse_exponential_param_deriv(unsigned int param, double x, double a, double b, double weight) { if (param == 0) return sqrt(weight)*(1. - exp(b*x)); if (param == 1) return -sqrt(weight)*a*x*exp(b*x); if (param == 2) return sqrt(weight); return 0; } double nsl_fit_model_fourier_param_deriv(unsigned int param, unsigned int degree, double x, double w, double weight) { if (param == 0) return sqrt(weight)*cos(degree*w*x); if (param == 1) return sqrt(weight)*sin(degree*w*x); return 0; } /* peak */ double nsl_fit_model_gaussian_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double s2 = s*s, norm = sqrt(weight)/sqrt(2.*M_PI)/s, efactor = exp(-(x-mu)*(x-mu)/(2.*s2)); if (param == 0) return norm * efactor; if (param == 1) return A * norm/(s*s2) * ((x-mu)*(x-mu) - s2) * efactor; if (param == 2) return A * norm/s2 * (x-mu) * efactor; return 0; } double nsl_fit_model_lorentz_param_deriv(unsigned int param, double x, double A, double s, double t, double weight) { double norm = sqrt(weight)/M_PI, denom = s*s+(x-t)*(x-t); if (param == 0) return norm * s/denom; if (param == 1) return A * norm * ((x-t)*(x-t) - s*s)/(denom*denom); if (param == 2) return A * norm * 2.*s*(x-t)/(denom*denom); return 0; } double nsl_fit_model_sech_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double y = (x-mu)/s, norm = sqrt(weight)/M_PI/s; if (param == 0) return norm/cosh(y); if (param == 1) return A/s * norm * (y*tanh(y)-1.)/cosh(y); if (param == 2) return A/s * norm * tanh(y)/cosh(y); return 0; } double nsl_fit_model_logistic_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double y = (x-mu)/2./s, norm = sqrt(weight)/4./s; if (param == 0) return norm/cosh(y)/cosh(y); if (param == 1) return A/s * norm * (2.*y*tanh(y)-1.)/cosh(y); if (param == 2) return A/s * norm * tanh(y)/cosh(y)/cosh(y); return 0; } double nsl_fit_model_voigt_param_deriv(unsigned int param, double x, double a, double mu, double s, double g, double weight) { +#if !defined(_MSC_VER) if (s <= 0 || g < 0) return 0; double y = x - mu, norm = a * sqrt(weight/2./M_PI)/(s*s*s); -#if !defined(_MSC_VER) double v = nsl_sf_voigt(y, s, g), im_w = nsl_sf_im_w_of_z(y); if (param == 0) return sqrt(weight) * v; if (param == 1) return a*sqrt(weight)*y/(s*s) * v - norm * g * im_w; if (param == 2) // return a*sqrt(weight)*g/M_PI/(s*s*s) + norm*sqrt(2.*M_PI)*v * (y*y+mu*mu-s*s) - norm/s*im_w*2.*g*y; - return a/(s*s*s)*sqrt(weight)*(g/M_PI + v*(y*y -g*g -s*s) + im_w*2*g*y/s); + return a/(s*s*s)*sqrt(weight)*(g/M_PI + v*(y*y -g*g -s*s) + im_w*2.*g*y/s); if (param == 3) return -a*sqrt(weight)/M_PI/(s*s) + norm*sqrt(2.*M_PI)*s*v*g + im_w; #endif return 0; } +double nsl_fit_model_pseudovoigt1_param_deriv(unsigned int param, double x, double a, double eta, double w, double mu, double weight) { + double y = x - mu, norm = sqrt(weight); + double sigma = w/sqrt(2.*M_LN2); + + if (param == 0) + return norm * nsl_sf_pseudovoigt1(y, eta, w); + if (param == 1) + return a * norm * (gsl_ran_cauchy_pdf(y, w) - gsl_ran_gaussian_pdf(y, sigma)); + if (param == 2) + return a/w * norm * (eta*(1.-2.*w*w)*gsl_ran_cauchy_pdf(y, w) + (eta-1.)*gsl_ran_gaussian_pdf(y, sigma)*(1.-2.*M_LN2*y*y/w/w)); + if (param == 3) + return 2.*a*y/w/w * norm * (eta*M_PI*w*gsl_pow_2(gsl_ran_cauchy_pdf(y, w)) + (1.-eta)*M_LN2*gsl_ran_gaussian_pdf(y, sigma)); + + return 0; +} /* growth */ double nsl_fit_model_atan_param_deriv(unsigned int param, double x, double A, double mu, double s, double weight) { double norm = sqrt(weight), y = (x-mu)/s; if (param == 0) return norm * atan(y); if (param == 1) return -A/s * norm * 1./(1+y*y); if (param == 2) return -A/s * norm * y/(1.+y*y); return 0; } double nsl_fit_model_tanh_param_deriv(unsigned int param, double x, double A, double mu, double s, double weight) { double norm = sqrt(weight), y = (x-mu)/s; if (param == 0) return norm * tanh(y); if (param == 1) return -A/s * norm * 1./cosh(y)/cosh(y); if (param == 2) return -A/s * norm * y/cosh(y)/cosh(y); return 0; } double nsl_fit_model_algebraic_sigmoid_param_deriv(unsigned int param, double x, double A, double mu, double s, double weight) { double norm = sqrt(weight), y = (x-mu)/s, y2 = y*y; if (param == 0) return norm * y/sqrt(1.+y2); if (param == 1) return -A/s * norm * 1./pow(1.+y2, 1.5); if (param == 2) return -A/s * norm * y/pow(1.+y2, 1.5); return 0; } double nsl_fit_model_sigmoid_param_deriv(unsigned int param, double x, double A, double mu, double k, double weight) { double norm = sqrt(weight), y = k*(x-mu); if (param == 0) return norm/(1. + exp(-y)); if (param == 1) return -A*k * norm * exp(-y)/gsl_pow_2(1. + exp(-y)); if (param == 2) return A/k * norm * y*exp(-y)/gsl_pow_2(1. + exp(-y)); return 0; } double nsl_fit_model_erf_param_deriv(unsigned int param, double x, double A, double mu, double s, double weight) { double norm = sqrt(weight), y = (x-mu)/(sqrt(2.)*s); if (param == 0) return norm/2. * gsl_sf_erf(y); if (param == 1) return -A/sqrt(2.*M_PI)/s * norm * exp(-y*y); if (param == 2) return -A/sqrt(M_PI)/s * norm * y*exp(-y*y); return 0; } double nsl_fit_model_hill_param_deriv(unsigned int param, double x, double A, double n, double s, double weight) { double norm = sqrt(weight), y = x/s; if (param == 0) return norm * pow(y, n)/(1.+pow(y, n)); if (param == 1) return A * norm * log(y)*pow(y, n)/gsl_pow_2(1.+pow(y, n)); if (param == 2) return -A*n/s * norm * pow(y, n)/gsl_pow_2(1.+pow(y, n)); return 0; } double nsl_fit_model_gompertz_param_deriv(unsigned int param, double x, double a, double b, double c, double weight) { if (param == 0) return sqrt(weight)*exp(-b*exp(-c*x)); if (param == 1) return -sqrt(weight)*a*exp(-c*x-b*exp(-c*x)); if (param == 2) return sqrt(weight)*a*b*x*exp(-c*x-b*exp(-c*x)); return 0; } double nsl_fit_model_gudermann_param_deriv(unsigned int param, double x, double A, double mu, double s, double weight) { double norm = sqrt(weight), y = (x-mu)/s; if (param == 0) return -asin(tanh(y)); if (param == 1) return -A/s * norm * 1./cosh(y); if (param == 2) return -A/s * norm * y/cosh(y); return 0; } /* distributions */ double nsl_fit_model_gaussian_tail_param_deriv(unsigned int param, double x, double A, double s, double a, double mu, double weight) { if (x < a) return 0; double s2 = s*s, N = erfc(a/s/M_SQRT2)/2., norm = sqrt(weight)/sqrt(2.*M_PI)/s/N, efactor = exp(-(x-mu)*(x-mu)/(2.*s2)); if (param == 0) return norm * efactor; if (param == 1) return A * norm/(s*s2) * ((x-mu)*(x-mu) - s2) * efactor; if (param == 2) return A/norm/norm * efactor * exp(-a*a/(2.*s2)); if (param == 3) return A * norm/s2 * (x-mu) * efactor; return 0; } double nsl_fit_model_exponential_param_deriv(unsigned int param, double x, double A, double l, double mu, double weight) { if (x < mu) return 0; double y = l*(x-mu), efactor = exp(-y); if (param == 0) return sqrt(weight) * l * efactor; if (param == 1) return sqrt(weight) * A * (1. - y) * efactor; if (param == 2) return sqrt(weight) * A * gsl_pow_2(l) * efactor; return 0; } double nsl_fit_model_laplace_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double norm = sqrt(weight)/(2.*s), y = fabs((x-mu)/s), efactor = exp(-y); if (param == 0) return norm * efactor; if (param == 1) return A/s*norm * (y-1.) * efactor; if (param == 2) return A/(s*s)*norm * (x-mu)/y * efactor; return 0; } double nsl_fit_model_exp_pow_param_deriv(unsigned int param, double x, double a, double s, double b, double mu, double weight) { double norm = sqrt(weight)/2./s/gsl_sf_gamma(1.+1./b), y = (x-mu)/s, efactor = exp(-pow(fabs(y), b)); if (param == 0) return norm * efactor; if (param == 1) return norm * a/s * efactor * (b * y * pow(fabs(1./y), 1.-b) * GSL_SIGN(y) - 1.); if (param == 2) return norm * a/b * gsl_sf_gamma(1.+1./b)/gsl_sf_gamma(1./b) * efactor * (gsl_sf_psi(1.+1./b) - gsl_pow_2(b) * pow(fabs(y), b) * log(fabs(y))); if (param == 3) return norm * a*b/s * efactor * pow(fabs(y), b-1.) * GSL_SIGN(y); return 0; } double nsl_fit_model_maxwell_param_deriv(unsigned int param, double x, double a, double s, double weight) { double s2 = s*s, s3 = s*s2, norm = sqrt(weight)*sqrt(2./M_PI)/s3, x2 = x*x, efactor = exp(-x2/2./s2); if (param == 0) return norm * x2 * efactor; if (param == 1) return a * norm * x2*(x2-3.*s2)/s3 * efactor; return 0; } double nsl_fit_model_poisson_param_deriv(unsigned int param, double x, double A, double l, double weight) { double norm = sqrt(weight)*pow(l, x)/gsl_sf_gamma(x+1.); if (param == 0) return norm * exp(-l); if (param == 1) return A/l * norm *(x-l)*exp(-l); return 0; } double nsl_fit_model_lognormal_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double norm = sqrt(weight)/sqrt(2.*M_PI)/(x*s), y = log(x)-mu, efactor = exp(-(y/s)*(y/s)/2.); if (param == 0) return norm * efactor; if (param == 1) return A * norm * (y*y - s*s) * efactor; if (param == 2) return A * norm * y/(s*s) * efactor; return 0; } double nsl_fit_model_gamma_param_deriv(unsigned int param, double x, double A, double k, double t, double weight) { double factor = sqrt(weight)*pow(x, k-1.)/pow(t, k)/gsl_sf_gamma(k), efactor = exp(-x/t); if (param == 0) return factor * efactor; if (param == 1) return A * factor * (log(x/t) - gsl_sf_psi(k)) * efactor; if (param == 2) return A * factor/t * (x/t-k) * efactor; return 0; } double nsl_fit_model_flat_param_deriv(unsigned int param, double x, double A, double b, double a, double weight) { if (x < a || x > b) return 0; if (param == 0) return sqrt(weight)/(b-a); if (param == 1) return - sqrt(weight) * A/gsl_pow_2(a-b); if (param == 2) return sqrt(weight) * A/gsl_pow_2(a-b); return 0; } double nsl_fit_model_rayleigh_param_deriv(unsigned int param, double x, double A, double s, double weight) { double y=x/s, norm = sqrt(weight)*y/s, efactor = exp(-y*y/2.); if (param == 0) return norm * efactor; if (param == 1) return A*y/(s*s) * (y*y-2.)*efactor; return 0; } double nsl_fit_model_rayleigh_tail_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double norm = sqrt(weight)*x/(s*s), y = (mu*mu - x*x)/2./(s*s); if (param == 0) return norm * exp(y); if (param == 1) return -2. * A * norm/s * (1. + y) * exp(y); if (param == 2) return A * mu * norm/(s*s) * exp(y); return 0; } double nsl_fit_model_levy_param_deriv(unsigned int param, double x, double A, double g, double mu, double weight) { double y=x-mu, norm = sqrt(weight)*sqrt(g/(2.*M_PI))/pow(y, 1.5), efactor = exp(-g/2./y); if (param == 0) return norm * efactor; if (param == 1) return A/2.*norm/g/y * (y - g) * efactor; if (param == 2) return A/2.*norm/y/y * (3.*y - g) * efactor; return 0; } double nsl_fit_model_landau_param_deriv(unsigned int param, double x, double weight) { if (param == 0) return sqrt(weight) * gsl_ran_landau_pdf(x); return 0; } double nsl_fit_model_chi_square_param_deriv(unsigned int param, double x, double A, double n, double weight) { double y=n/2., norm = sqrt(weight)*pow(x, y-1.)/pow(2., y)/gsl_sf_gamma(y), efactor = exp(-x/2.); if (param == 0) return norm * efactor; if (param == 1) return A/2. * norm * (log(x/2.) - gsl_sf_psi(y)) * efactor; return 0; } double nsl_fit_model_students_t_param_deriv(unsigned int param, double x, double A, double n, double weight) { if (param == 0) return sqrt(weight) * gsl_ran_tdist_pdf(x, n); if (param == 1) return sqrt(weight) * A * gsl_sf_gamma((n+1.)/2.)/2./pow(n, 1.5)/sqrt(M_PI)/gsl_sf_gamma(n/2.) * pow(1.+x*x/n, - (n+3.)/2.) * (x*x - 1. - (n+x*x)*log(1.+x*x/n) + (n+x*x)*(gsl_sf_psi((n+1.)/2.) - gsl_sf_psi(n/2.)) ) ; return 0; } double nsl_fit_model_fdist_param_deriv(unsigned int param, double x, double A, double n1, double n2, double weight) { double norm = sqrt(weight) * gsl_sf_gamma((n1+n2)/2.)/gsl_sf_gamma(n1/2.)/gsl_sf_gamma(n2/2.) * pow(n1, n1/2.) * pow(n2, n2/2.) * pow(x, n1/2.-1.); double y = n2+n1*x; if (param == 0) return sqrt(weight) * gsl_ran_fdist_pdf(x, n1, n2); if (param == 1) return A/2. * norm * pow(y, -(n1+n2+2.)/2.) * (n2*(1.-x) + y*(log(n1) + log(x) - log(y) + gsl_sf_psi((n1+n2)/2.) - gsl_sf_psi(n1/2.))); if (param == 2) return A/2. * norm * pow(y, -(n1+n2+2.)/2.) * (n1*(x-1.) + y*(log(n2) - log(y) + gsl_sf_psi((n1+n2)/2.) - gsl_sf_psi(n2/2.))); return 0; } double nsl_fit_model_beta_param_deriv(unsigned int param, double x, double A, double a, double b, double weight) { double norm = sqrt(weight) * A * gsl_sf_gamma(a+b)/gsl_sf_gamma(a)/gsl_sf_gamma(b) * pow(x, a-1.) * pow(1.-x, b-1.); if (param == 0) return sqrt(weight) * gsl_ran_beta_pdf(x, a, b); if (param == 1) return norm * (log(x) - gsl_sf_psi(a) + gsl_sf_psi(a+b)); if (param == 2) return norm * (log(1.-x) - gsl_sf_psi(b) + gsl_sf_psi(a+b)); return 0; } double nsl_fit_model_pareto_param_deriv(unsigned int param, double x, double A, double a, double b, double weight) { if (x < b) return 0; double norm = sqrt(weight) * A; if (param == 0) return sqrt(weight) * gsl_ran_pareto_pdf(x, a, b); if (param == 1) return norm * pow(b/x, a) * (1. + a * log(b/x))/x; if (param == 2) return norm * a*a * pow(b/x, a-1.)/x/x; return 0; } double nsl_fit_model_weibull_param_deriv(unsigned int param, double x, double A, double k, double l, double mu, double weight) { double y = (x-mu)/l, z = pow(y, k), efactor = exp(-z); if (param == 0) return sqrt(weight) * k/l * z/y * efactor; if (param == 1) return sqrt(weight) * A/l * z/y*(k*log(y)*(1.-z) + 1.) * efactor; if (param == 2) return sqrt(weight) * A*k*k/l/l * z/y*(z-1.) * efactor; if (param == 3) return sqrt(weight) * A*k/l/l * z/y/y*(k*z + 1. - k) * efactor; return 0; } double nsl_fit_model_frechet_param_deriv(unsigned int param, double x, double A, double g, double s, double mu, double weight) { double y = (x-mu)/s, efactor = exp(-pow(y, -g)); if (param == 0) return g * sqrt(weight)/s * pow(y, -g-1.) * efactor; if (param == 1) return sqrt(weight) * A/s * pow(y, -2.*g-1.) * (g*log(y)*(1.-pow(y, g))+pow(y, g)) * efactor; if (param == 2) return A * sqrt(weight) * gsl_pow_2(g/s)*pow(y, -2.*g-1.) * (pow(y, g)-1.) * efactor; if (param == 3) return A * sqrt(weight) * g/(s*s)*pow(y, -g-2.) * (g+1.-g*pow(y, -g)) * efactor; return 0; } double nsl_fit_model_gumbel1_param_deriv(unsigned int param, double x, double A, double s, double mu, double b, double weight) { double norm = sqrt(weight)/s, y = (x-mu)/s, efactor = exp(-y - b*exp(-y)); if (param == 0) return norm * efactor; if (param == 1) return A/s * norm * (y - 1. - b*exp(-y)) * efactor; if (param == 2) return A/s * norm * (1. - b*exp(-y)) * efactor; if (param == 3) return -A * norm * exp(-y) * efactor; return 0; } double nsl_fit_model_gumbel2_param_deriv(unsigned int param, double x, double A, double a, double b, double mu, double weight) { double y = x - mu, norm = A * sqrt(weight) * exp(-b * pow(y, -a)); if (param == 0) return sqrt(weight) * gsl_ran_gumbel2_pdf(y, a, b); if (param == 1) return norm * b * pow(y, -1. -2.*a) * (pow(y, a) -a*(pow(y, a)-b)*log(y)); if (param == 2) return norm * a * pow(y, -1. -2.*a) * (pow(y, a) - b); if (param == 3) return norm * a * b * pow(y, -2.*(a + 1.)) * ((1. + a)*pow(y, a) - a*b); return 0; } double nsl_fit_model_binomial_param_deriv(unsigned int param, double k, double A, double p, double n, double weight) { if (k < 0 || k > n || n < 0 || p < 0 || p > 1.) return 0; k = round(k); n = round(n); double norm = sqrt(weight) * gsl_sf_fact((unsigned int)n)/gsl_sf_fact((unsigned int)(n-k))/gsl_sf_fact((unsigned int)(k)); if (param == 0) return sqrt(weight) * gsl_ran_binomial_pdf((unsigned int)k, p, (unsigned int)n); if (param == 1) return A * norm * pow(p, k-1.) * pow(1.-p, n-k-1.) * (k-n*p); if (param == 2) return A * norm * pow(p, k) * pow(1.-p, n-k) * (log(1.-p) + gsl_sf_psi(n+1.) - gsl_sf_psi(n-k+1.)); return 0; } double nsl_fit_model_negative_binomial_param_deriv(unsigned int param, double k, double A, double p, double n, double weight) { if (k < 0 || k > n || n < 0 || p < 0 || p > 1.) return 0; double norm = A * sqrt(weight) * gsl_sf_gamma(n+k)/gsl_sf_gamma(k+1.)/gsl_sf_gamma(n); if (param == 0) return sqrt(weight) * gsl_ran_negative_binomial_pdf((unsigned int)k, p, n); if (param == 1) return - norm * pow(p, n-1.) * pow(1.-p, k-1.) * (n*(p-1.) + k*p); if (param == 2) return norm * pow(p, n) * pow(1.-p, k) * (log(p) - gsl_sf_psi(n) + gsl_sf_psi(n+k)); return 0; } double nsl_fit_model_pascal_param_deriv(unsigned int param, double k, double A, double p, double n, double weight) { return nsl_fit_model_negative_binomial_param_deriv(param, k, A, p, round(n), weight); } double nsl_fit_model_geometric_param_deriv(unsigned int param, double k, double A, double p, double weight) { if (param == 0) return sqrt(weight) * gsl_ran_geometric_pdf((unsigned int)k, p); if (param == 1) return A * sqrt(weight) * pow(1.-p, k-2.) * (1.-k*p); return 0; } double nsl_fit_model_hypergeometric_param_deriv(unsigned int param, double k, double A, double n1, double n2, double t, double weight) { if (t > n1 + n2) return 0; double norm = sqrt(weight) * gsl_ran_hypergeometric_pdf((unsigned int)k, (unsigned int)n1, (unsigned int)n2, (unsigned int)t); if (param == 0) return norm; if (param == 1) return A * norm * (gsl_sf_psi(n1+1.) - gsl_sf_psi(n1-k+1.) - gsl_sf_psi(n1+n2+1.) + gsl_sf_psi(n1+n2-t+1.)); if (param == 2) return A * norm * (gsl_sf_psi(n2+1.) - gsl_sf_psi(n2+k-t+1.) - gsl_sf_psi(n1+n2+1.) + gsl_sf_psi(n1+n2-t+1.)); if (param == 3) return A * norm * (gsl_sf_psi(n2+k-t+1.) - gsl_sf_psi(n1+n2-t+1.) - gsl_sf_psi(t-k+1.) + gsl_sf_psi(t+1.)); return 0; } double nsl_fit_model_logarithmic_param_deriv(unsigned int param, double k, double A, double p, double weight) { if (param == 0) return sqrt(weight) * gsl_ran_logarithmic_pdf((unsigned int)k, p); if (param == 1) return A * sqrt(weight) * pow(1.-p, k-2.) * (1.-k*p); return 0; } double nsl_fit_model_sech_dist_param_deriv(unsigned int param, double x, double A, double s, double mu, double weight) { double norm = sqrt(weight)/2./s, y = M_PI/2.*(x-mu)/s; if (param == 0) return norm * 1./cosh(y); if (param == 1) return -A/s * norm * (y*tanh(y)+1.)/cosh(y); if (param == 2) return A*M_PI/2./s * norm * tanh(y)/cosh(y); return 0; } diff --git a/src/backend/nsl/nsl_fit.h b/src/backend/nsl/nsl_fit.h index ad627c586..03bcdccc7 100644 --- a/src/backend/nsl/nsl_fit.h +++ b/src/backend/nsl/nsl_fit.h @@ -1,127 +1,128 @@ /*************************************************************************** File : nsl_fit.h Project : LabPlot Description : NSL (non)linear fitting functions -------------------------------------------------------------------- Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 NSL_FIT_H #define NSL_FIT_H #define NSL_FIT_MODEL_CATEGORY_COUNT 5 typedef enum {nsl_fit_model_basic, nsl_fit_model_peak, nsl_fit_model_growth, nsl_fit_model_distribution, nsl_fit_model_custom=99} nsl_fit_model_category; #define NSL_FIT_MODEL_BASIC_COUNT 5 typedef enum {nsl_fit_model_polynomial, nsl_fit_model_power, nsl_fit_model_exponential, nsl_fit_model_inverse_exponential, nsl_fit_model_fourier} nsl_fit_model_type_basic; extern const char* nsl_fit_model_basic_pic_name[]; -#define NSL_FIT_MODEL_PEAK_COUNT 5 -typedef enum {nsl_fit_model_gaussian, nsl_fit_model_lorentz, nsl_fit_model_sech, nsl_fit_model_logistic, nsl_fit_model_voigt} nsl_fit_model_type_peak; +#define NSL_FIT_MODEL_PEAK_COUNT 6 +typedef enum {nsl_fit_model_gaussian, nsl_fit_model_lorentz, nsl_fit_model_sech, nsl_fit_model_logistic, nsl_fit_model_voigt, nsl_fit_model_pseudovoigt1} nsl_fit_model_type_peak; extern const char* nsl_fit_model_peak_pic_name[]; #define NSL_FIT_MODEL_GROWTH_COUNT 8 typedef enum {nsl_fit_model_atan, nsl_fit_model_tanh, nsl_fit_model_algebraic_sigmoid, nsl_fit_model_sigmoid, nsl_fit_model_erf, nsl_fit_model_hill, nsl_fit_model_gompertz, nsl_fit_model_gudermann} nsl_fit_model_type_growth; extern const char* nsl_fit_model_growth_pic_name[]; extern const char* nsl_fit_model_category_name[]; extern const char* nsl_fit_model_basic_name[]; extern const char* nsl_fit_model_peak_name[]; extern const char* nsl_fit_model_growth_name[]; extern const char* nsl_fit_model_basic_equation[]; extern const char* nsl_fit_model_peak_equation[]; extern const char* nsl_fit_model_growth_equation[]; #define NSL_FIT_WEIGHT_TYPE_COUNT 8 typedef enum {nsl_fit_weight_no, /* w = 1 */ nsl_fit_weight_instrumental, /* w = 1/c^2 (Gaussian, Given errors): default */ nsl_fit_weight_direct, /* w = c */ nsl_fit_weight_inverse, /* w = 1/c */ nsl_fit_weight_statistical, /* w = 1/y (Poisson) */ nsl_fit_weight_statistical_fit, /* w = 1/Y (Poisson) */ nsl_fit_weight_relative, /* w = 1/y^2 (Variance) */ nsl_fit_weight_relative_fit, /* w = 1/Y^2 (Variance) */ } nsl_fit_weight_type; extern const char* nsl_fit_weight_type_name[]; /* convert unbounded variable x to bounded variable where bounds are [min, max] */ double nsl_fit_map_bound(double x, double min, double max); /* convert bounded variable x to unbounded variable where bounds are [min, max] */ double nsl_fit_map_unbound(double x, double min, double max); /* model parameter derivatives */ /* basic */ double nsl_fit_model_polynomial_param_deriv(double x, int j, double weight); double nsl_fit_model_power1_param_deriv(unsigned int param, double x, double a, double b, double weight); double nsl_fit_model_power2_param_deriv(unsigned int param, double x, double b, double c, double weight); double nsl_fit_model_exponentialn_param_deriv(unsigned int param, double x, double *p, double weight); double nsl_fit_model_inverse_exponential_param_deriv(unsigned int param, double x, double a, double b, double weight); double nsl_fit_model_fourier_param_deriv(unsigned int param, unsigned int degree, double x, double w, double weight); /* peak */ double nsl_fit_model_gaussian_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_lorentz_param_deriv(unsigned int param, double x, double a, double s, double t, double weight); double nsl_fit_model_sech_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_logistic_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_voigt_param_deriv(unsigned int param, double x, double a, double mu, double s, double g, double weight); +double nsl_fit_model_pseudovoigtA_param_deriv(unsigned int param, double x, double a, double eta, double w, double mu, double weight); /* growth */ double nsl_fit_model_atan_param_deriv(unsigned int param, double x, double a, double mu, double s, double weight); double nsl_fit_model_tanh_param_deriv(unsigned int param, double x, double a, double mu, double s, double weight); double nsl_fit_model_algebraic_sigmoid_param_deriv(unsigned int param, double x, double a, double mu, double s, double weight); double nsl_fit_model_sigmoid_param_deriv(unsigned int param, double x, double a, double mu, double k, double weight); double nsl_fit_model_erf_param_deriv(unsigned int param, double x, double a, double mu, double s, double weight); double nsl_fit_model_hill_param_deriv(unsigned int param, double x, double a, double n, double s, double weight); double nsl_fit_model_gompertz_param_deriv(unsigned int param, double x, double a, double b, double c, double weight); double nsl_fit_model_gudermann_param_deriv(unsigned int param, double x, double a, double mu, double s, double weight); /* distributions */ double nsl_fit_model_gaussian_tail_param_deriv(unsigned int param, double x, double A, double s, double a, double mu, double weight); double nsl_fit_model_exponential_param_deriv(unsigned int param, double x, double a, double l, double mu, double weight); double nsl_fit_model_laplace_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_exp_pow_param_deriv(unsigned int param, double x, double a, double s, double b, double mu, double weight); double nsl_fit_model_poisson_param_deriv(unsigned int param, double x, double a, double l, double weight); double nsl_fit_model_lognormal_param_deriv(unsigned int param, double x, double a, double b, double mu, double weight); double nsl_fit_model_gamma_param_deriv(unsigned int param, double x, double a, double k, double t, double weight); double nsl_fit_model_flat_param_deriv(unsigned int param, double x, double A, double b, double a, double weight); double nsl_fit_model_rayleigh_param_deriv(unsigned int param, double x, double a, double s, double weight); double nsl_fit_model_rayleigh_tail_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_landau_param_deriv(unsigned int param, double x, double weight); double nsl_fit_model_chi_square_param_deriv(unsigned int param, double x, double a, double n, double weight); double nsl_fit_model_students_t_param_deriv(unsigned int param, double x, double a, double n, double weight); double nsl_fit_model_fdist_param_deriv(unsigned int param, double x, double a, double n1, double n2, double weight); double nsl_fit_model_beta_param_deriv(unsigned int param, double x, double A, double a, double b, double weight); double nsl_fit_model_pareto_param_deriv(unsigned int param, double x, double A, double a, double b, double weight); double nsl_fit_model_weibull_param_deriv(unsigned int param, double x, double a, double k, double l, double mu, double weight); double nsl_fit_model_gumbel1_param_deriv(unsigned int param, double x, double a, double s, double mu, double b, double weight); double nsl_fit_model_gumbel2_param_deriv(unsigned int param, double x, double A, double a, double b, double mu, double weight); double nsl_fit_model_binomial_param_deriv(unsigned int param, double x, double A, double p, double n, double weight); double nsl_fit_model_negative_binomial_param_deriv(unsigned int param, double k, double A, double p, double n, double weight); double nsl_fit_model_pascal_param_deriv(unsigned int param, double k, double A, double p, double n, double weight); double nsl_fit_model_geometric_param_deriv(unsigned int param, double k, double A, double p, double weight); double nsl_fit_model_hypergeometric_param_deriv(unsigned int param, double k, double A, double n1, double n2, double t, double weight); double nsl_fit_model_logarithmic_param_deriv(unsigned int param, double k, double A, double p, double weight); double nsl_fit_model_maxwell_param_deriv(unsigned int param, double x, double a, double s, double weight); double nsl_fit_model_sech_dist_param_deriv(unsigned int param, double x, double a, double s, double mu, double weight); double nsl_fit_model_levy_param_deriv(unsigned int param, double x, double a, double g, double mu, double weight); double nsl_fit_model_frechet_param_deriv(unsigned int param, double x, double A, double g, double s, double mu, double weight); #endif /* NSL_FIT_H */ diff --git a/src/backend/nsl/nsl_sf_basic.c b/src/backend/nsl/nsl_sf_basic.c index 2a8afa65d..44ce569c2 100644 --- a/src/backend/nsl/nsl_sf_basic.c +++ b/src/backend/nsl/nsl_sf_basic.c @@ -1,259 +1,277 @@ /*************************************************************************** File : nsl_sf_basic.c Project : LabPlot Description : NSL special basic functions -------------------------------------------------------------------- Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "nsl_sf_basic.h" #include #include #include #include #include #ifdef HAVE_LIBCERF #include #elif !defined(_MSC_VER) #include "Faddeeva.h" #endif /* stdlib.h */ double nsl_sf_rand(void) { return rand(); } +#if defined(_MSC_VER) // MSVC has no random() function +double nsl_sf_random(void) { return rand(); } +double nsl_sf_drand(void) { return rand()/(double)RAND_MAX; } +#else double nsl_sf_random(void) { return random(); } double nsl_sf_drand(void) { return random()/(double)RAND_MAX; } +#endif double nsl_sf_sgn(double x) { #ifndef _WIN32 return copysign(1.0, x); #else if (x > 0) return 1; else if (x < 0) return -1; else return 0; #endif } double nsl_sf_theta(double x) { if (x >= 0) return 1; else return 0; } double nsl_sf_sec(double x) { return 1./cos(x); } double nsl_sf_csc(double x) { return 1./sin(x); } double nsl_sf_cot(double x) { return 1./tan(x); } double nsl_sf_asec(double x) { return acos(1./x); } double nsl_sf_acsc(double x) { return asin(1./x); } double nsl_sf_acot(double x) { if (x > 0) return atan(1./x); else return atan(1./x) + M_PI; } double nsl_sf_sech(double x) { return 1./cosh(x); } double nsl_sf_csch(double x) { return 1./sinh(x); } double nsl_sf_coth(double x) { return 1./tanh(x); } double nsl_sf_asech(double x) { return gsl_acosh(1./x); } double nsl_sf_acsch(double x) { return gsl_asinh(1./x); } double nsl_sf_acoth(double x) { return gsl_atanh(1./x); } double nsl_sf_harmonic(double x) { // check if x is a negative integer if (x < 0 && !gsl_fcmp(round(x) - x, 0., 1.e-16)) return GSL_POSINF; return gsl_sf_psi(x + 1) + M_EULER; } /* error functions and related */ double nsl_sf_erfcx(double x) { #ifdef HAVE_LIBCERF return erfcx(x); #elif defined(_MSC_VER) return 0.; // not supported yet #else return Faddeeva_erfcx_re(x); #endif } double nsl_sf_erfi(double x) { #ifdef HAVE_LIBCERF return erfi(x); #elif defined(_MSC_VER) return 0.; // not supported yet #else return Faddeeva_erfi_re(x); #endif } double nsl_sf_im_w_of_x(double x) { #ifdef HAVE_LIBCERF return im_w_of_x(x); #elif defined(_MSC_VER) return 0.; // not supported yet #else return Faddeeva_w_im(x); #endif } #if !defined(_MSC_VER) double nsl_sf_im_w_of_z(complex double z) { #ifdef HAVE_LIBCERF return cimag(w_of_z(z)); #else return cimag(Faddeeva_w(z, 0)); #endif } #endif double nsl_sf_dawson(double x) { #ifdef HAVE_LIBCERF return dawson(x); #elif defined(_MSC_VER) return 0.; // not supported yet #else return Faddeeva_Dawson_re(x); #endif } double nsl_sf_voigt(double x, double sigma, double gamma) { #ifdef HAVE_LIBCERF return voigt(x, sigma, gamma); #elif defined(_MSC_VER) return 0.; // not supported yet #else double complex z = (x + I*gamma)/(sqrt(2.)*sigma); return creal(Faddeeva_w(z, 0))/(sqrt(2.*M_PI)*sigma); #endif } +double nsl_sf_pseudovoigt(double x, double eta, double sigma, double gamma) { + if (sigma == 0 || gamma == 0) + return 0; + //TODO: what if eta < 0 or > 1? + + return (1. - eta) * gsl_ran_gaussian_pdf(x, sigma) + eta * gsl_ran_cauchy_pdf(x, gamma); +} + +double nsl_sf_pseudovoigt1(double x, double eta, double w) { + // 2w - FWHM, sigma_G = w/sqrt(2ln(2)) + return nsl_sf_pseudovoigt(x, eta, w/sqrt(2.*log(2.)), w); +} + /* wrapper for GSL functions with integer parameters */ #define MODE GSL_PREC_DOUBLE /* mathematical functions */ double nsl_sf_ldexp(double x, double expo) { return gsl_ldexp(x, (int)round(expo)); } double nsl_sf_powint(double x, double n) { return gsl_sf_pow_int(x, (int)round(n)); } /* Airy functions */ double nsl_sf_airy_Ai(double x) { return gsl_sf_airy_Ai(x, MODE); } double nsl_sf_airy_Bi(double x) { return gsl_sf_airy_Bi(x, MODE); } double nsl_sf_airy_Ais(double x) { return gsl_sf_airy_Ai_scaled(x, MODE); } double nsl_sf_airy_Bis(double x) { return gsl_sf_airy_Bi_scaled(x, MODE); } double nsl_sf_airy_Aid(double x) { return gsl_sf_airy_Ai_deriv(x, MODE); } double nsl_sf_airy_Bid(double x) { return gsl_sf_airy_Bi_deriv(x, MODE); } double nsl_sf_airy_Aids(double x) { return gsl_sf_airy_Ai_deriv_scaled(x, MODE); } double nsl_sf_airy_Bids(double x) { return gsl_sf_airy_Bi_deriv_scaled(x, MODE); } double nsl_sf_airy_0_Ai(double s) { return gsl_sf_airy_zero_Ai((unsigned int)round(s)); } double nsl_sf_airy_0_Bi(double s) { return gsl_sf_airy_zero_Bi((unsigned int)round(s)); } double nsl_sf_airy_0_Aid(double s) { return gsl_sf_airy_zero_Ai_deriv((unsigned int)round(s)); } double nsl_sf_airy_0_Bid(double s) { return gsl_sf_airy_zero_Bi_deriv((unsigned int)round(s)); } /* Bessel functions */ double nsl_sf_bessel_Jn(double n, double x) { return gsl_sf_bessel_Jn((int)round(n), x); } double nsl_sf_bessel_Yn(double n, double x) { return gsl_sf_bessel_Yn((int)round(n), x); } double nsl_sf_bessel_In(double n, double x) { return gsl_sf_bessel_In((int)round(n), x); } double nsl_sf_bessel_Ins(double n, double x) { return gsl_sf_bessel_In_scaled((int)round(n), x); } double nsl_sf_bessel_Kn(double n, double x) { return gsl_sf_bessel_Kn((int)round(n), x); } double nsl_sf_bessel_Kns(double n, double x) { return gsl_sf_bessel_Kn_scaled((int)round(n), x); } double nsl_sf_bessel_jl(double l, double x) { return gsl_sf_bessel_jl((int)round(l), x); } double nsl_sf_bessel_yl(double l, double x) { return gsl_sf_bessel_yl((int)round(l), x); } double nsl_sf_bessel_ils(double l, double x) { return gsl_sf_bessel_il_scaled((int)round(l), x); } double nsl_sf_bessel_kls(double l, double x) { return gsl_sf_bessel_kl_scaled((int)round(l), x); } double nsl_sf_bessel_0_J0(double s) { return gsl_sf_bessel_zero_J0((unsigned int)round(s)); } double nsl_sf_bessel_0_J1(double s) { return gsl_sf_bessel_zero_J1((unsigned int)round(s)); } double nsl_sf_bessel_0_Jnu(double nu, double s) { return gsl_sf_bessel_zero_Jnu(nu, (unsigned int)round(s)); } double nsl_sf_hydrogenicR(double n, double l, double z, double r) { return gsl_sf_hydrogenicR((int)round(n), (int)round(l), z, r); } /* elliptic integrals */ double nsl_sf_ellint_Kc(double x) { return gsl_sf_ellint_Kcomp(x, MODE); } double nsl_sf_ellint_Ec(double x) { return gsl_sf_ellint_Ecomp(x, MODE); } double nsl_sf_ellint_Pc(double x, double n) { return gsl_sf_ellint_Pcomp(x, n, MODE); } double nsl_sf_ellint_F(double phi, double k) { return gsl_sf_ellint_F(phi, k, MODE); } double nsl_sf_ellint_E(double phi, double k) { return gsl_sf_ellint_E(phi, k, MODE); } double nsl_sf_ellint_P(double phi, double k, double n) { return gsl_sf_ellint_P(phi, k, n, MODE); } double nsl_sf_ellint_D(double phi, double k) { #if GSL_MAJOR_VERSION >= 2 return gsl_sf_ellint_D(phi,k,MODE); #else return gsl_sf_ellint_D(phi,k,0.0,MODE); #endif } double nsl_sf_ellint_RC(double x, double y) { return gsl_sf_ellint_RC(x, y, MODE); } double nsl_sf_ellint_RD(double x, double y, double z) { return gsl_sf_ellint_RD(x, y, z, MODE); } double nsl_sf_ellint_RF(double x, double y, double z) { return gsl_sf_ellint_RF(x, y, z, MODE); } double nsl_sf_ellint_RJ(double x, double y, double z, double p) { return gsl_sf_ellint_RJ(x, y, z, p, MODE); } double nsl_sf_exprel_n(double n, double x) { return gsl_sf_exprel_n((int)round(n), x); } double nsl_sf_fermi_dirac_int(double j, double x) { return gsl_sf_fermi_dirac_int((int)round(j), x); } /* Gamma */ double nsl_sf_fact(double n) { return gsl_sf_fact((unsigned int)round(n)); } double nsl_sf_doublefact(double n) { return gsl_sf_doublefact((unsigned int)round(n)); } double nsl_sf_lnfact(double n) { return gsl_sf_lnfact((unsigned int)round(n)); } double nsl_sf_lndoublefact(double n) { return gsl_sf_lndoublefact((unsigned int)round(n)); } double nsl_sf_choose(double n, double m) { return gsl_sf_choose((unsigned int)round(n), (unsigned int)round(m)); } double nsl_sf_lnchoose(double n, double m) { return gsl_sf_lnchoose((unsigned int)round(n), (unsigned int)round(m)); } double nsl_sf_taylorcoeff(double n, double x) { return gsl_sf_taylorcoeff((int)round(n), x); } double nsl_sf_gegenpoly_n(double n, double l, double x) { return gsl_sf_gegenpoly_n((int)round(n), l, x); } #if (GSL_MAJOR_VERSION > 2) || (GSL_MAJOR_VERSION == 2) && (GSL_MINOR_VERSION >= 4) double nsl_sf_hermite_prob(double n, double x) { return gsl_sf_hermite_prob(round(n), x); } double nsl_sf_hermite_phys(double n, double x) { return gsl_sf_hermite_phys(round(n), x); } double nsl_sf_hermite_func(double n, double x) { return gsl_sf_hermite_func(round(n), x); } double nsl_sf_hermite_prob_der(double m, double n, double x) { return gsl_sf_hermite_prob_der(round(m), round(n), x); } double nsl_sf_hermite_phys_der(double m, double n, double x) { return gsl_sf_hermite_phys_der(round(m), round(n), x); } double nsl_sf_hermite_func_der(double m, double n, double x) { return gsl_sf_hermite_func_der(round(m), round(n), x); } #endif double nsl_sf_hyperg_1F1i(double m, double n, double x) { return gsl_sf_hyperg_1F1_int((int)round(m), (int)round(n), x); } double nsl_sf_hyperg_Ui(double m, double n, double x) { return gsl_sf_hyperg_U_int((int)round(m), (int)round(n), x); } double nsl_sf_laguerre_n(double n, double a, double x) { return gsl_sf_laguerre_n((int)round(n), a, x); } double nsl_sf_legendre_Pl(double l, double x) { return gsl_sf_legendre_Pl((int)round(l), x); } double nsl_sf_legendre_Ql(double l, double x) { return gsl_sf_legendre_Ql((int)round(l), x); } double nsl_sf_legendre_Plm(double l, double m, double x) { return gsl_sf_legendre_Plm((int)round(l), (int)round(m), x); } double nsl_sf_legendre_sphPlm(double l, double m, double x) { return gsl_sf_legendre_sphPlm((int)round(l), (int)round(m), x); } double nsl_sf_conicalP_sphreg(double l, double L, double x) { return gsl_sf_conicalP_sph_reg((int)round(l), L, x); } double nsl_sf_conicalP_cylreg(double m, double l, double x) { return gsl_sf_conicalP_sph_reg((int)round(m), l, x); } double nsl_sf_legendre_H3d(double l, double L, double e) { return gsl_sf_legendre_H3d((int)round(l), L, e); } double nsl_sf_psiint(double n) { return gsl_sf_psi_int((int)round(n)); } double nsl_sf_psi1int(double n) { return gsl_sf_psi_1_int((int)round(n)); } double nsl_sf_psin(double n, double x) { return gsl_sf_psi_n((int)round(n), x); } double nsl_sf_zetaint(double n) { return gsl_sf_zeta_int((int)round(n)); } double nsl_sf_zetam1int(double n) { return gsl_sf_zetam1_int((int)round(n)); } double nsl_sf_etaint(double n) { return gsl_sf_eta_int((int)round(n)); } /* random number distributions */ double nsl_sf_poisson(double k, double m) { return gsl_ran_poisson_pdf((unsigned int)round(k), m); } double nsl_sf_bernoulli(double k, double p) { return gsl_ran_bernoulli_pdf((unsigned int)round(k), p); } double nsl_sf_binomial(double k, double p, double n) { return gsl_ran_binomial_pdf((unsigned int)round(k), p, (unsigned int)round(n)); } double nsl_sf_negative_binomial(double k, double p, double n) { return gsl_ran_negative_binomial_pdf((unsigned int)round(k), p, n); } double nsl_sf_pascal(double k, double p, double n) { return gsl_ran_pascal_pdf((unsigned int)round(k), p, (unsigned int)round(n)); } double nsl_sf_geometric(double k, double p) { return gsl_ran_geometric_pdf((unsigned int)round(k), p); } double nsl_sf_hypergeometric(double k, double n1, double n2, double t) { return gsl_ran_hypergeometric_pdf((unsigned int)round(k), (unsigned int)round(n1), (unsigned int)round(n2), (unsigned int)round(t)); } double nsl_sf_logarithmic(double k, double p) { return gsl_ran_logarithmic_pdf((unsigned int)round(k), p); } diff --git a/src/backend/nsl/nsl_sf_basic.h b/src/backend/nsl/nsl_sf_basic.h index 16ba1d9d8..256b94898 100644 --- a/src/backend/nsl/nsl_sf_basic.h +++ b/src/backend/nsl/nsl_sf_basic.h @@ -1,172 +1,175 @@ /*************************************************************************** File : nsl_sf_basic.h Project : LabPlot Description : NSL special basic functions -------------------------------------------------------------------- Copyright : (C) 2017-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 NSL_SF_BASIC_H #define NSL_SF_BASIC_H #include #if !defined(_MSC_VER) #include #endif /* random functions */ double nsl_sf_rand(void); double nsl_sf_random(void); double nsl_sf_drand(void); /* signum function */ double nsl_sf_sgn(double x); /* Heavyside theta function */ double nsl_sf_theta(double x); /* more trig. functions */ double nsl_sf_sec(double x); double nsl_sf_csc(double x); double nsl_sf_cot(double x); double nsl_sf_asec(double x); double nsl_sf_acsc(double x); double nsl_sf_acot(double x); double nsl_sf_sech(double x); double nsl_sf_csch(double x); double nsl_sf_coth(double x); double nsl_sf_asech(double x); double nsl_sf_acsch(double x); double nsl_sf_acoth(double x); /* harmonic numbers (extended to non-integers) */ double nsl_sf_harmonic(double x); /* error function and related wrapper */ double nsl_sf_erfcx(double x); double nsl_sf_erfi(double x); double nsl_sf_im_w_of_x(double x); #if !defined(_MSC_VER) double nsl_sf_im_w_of_z(complex double z); #endif double nsl_sf_dawson(double x); double nsl_sf_voigt(double x, double sigma, double gamma); +double nsl_sf_pseudovoigt(double x, double eta, double sigma, double gamma); +/* same width pseudo Voigt*/ +double nsl_sf_pseudovoigt1(double x, double eta, double w); /* wrapper for GSL functions with integer parameters */ /* mathematical functions */ double nsl_sf_ldexp(double x, double expo); double nsl_sf_powint(double x, double n); /* Airy functions */ double nsl_sf_airy_Ai(double x); double nsl_sf_airy_Bi(double x); double nsl_sf_airy_Ais(double x); double nsl_sf_airy_Bis(double x); double nsl_sf_airy_Aid(double x); double nsl_sf_airy_Bid(double x); double nsl_sf_airy_Aids(double x); double nsl_sf_airy_Bids(double x); double nsl_sf_airy_0_Ai(double s); double nsl_sf_airy_0_Bi(double s); double nsl_sf_airy_0_Aid(double s); double nsl_sf_airy_0_Bid(double s); /* Bessel functions */ double nsl_sf_bessel_Jn(double n, double x); double nsl_sf_bessel_Yn(double n, double x); double nsl_sf_bessel_In(double n, double x); double nsl_sf_bessel_Ins(double n, double x); double nsl_sf_bessel_Kn(double n, double x); double nsl_sf_bessel_Kns(double n, double x); double nsl_sf_bessel_jl(double l, double x); double nsl_sf_bessel_yl(double l, double x); double nsl_sf_bessel_ils(double l, double x); double nsl_sf_bessel_kls(double l, double x); double nsl_sf_bessel_0_J0(double s); double nsl_sf_bessel_0_J1(double s); double nsl_sf_bessel_0_Jnu(double nu, double s); double nsl_sf_hydrogenicR(double n, double l, double z, double r); /* elliptic integrals */ double nsl_sf_ellint_Kc(double x); double nsl_sf_ellint_Ec(double x); double nsl_sf_ellint_Pc(double x, double n); double nsl_sf_ellint_F(double phi, double k); double nsl_sf_ellint_E(double phi, double k); double nsl_sf_ellint_P(double phi, double k, double n); double nsl_sf_ellint_D(double phi, double k); double nsl_sf_ellint_RC(double x, double y); double nsl_sf_ellint_RD(double x, double y, double z); double nsl_sf_ellint_RF(double x, double y, double z); double nsl_sf_ellint_RJ(double x, double y, double z, double p); double nsl_sf_exprel_n(double n, double x); double nsl_sf_fermi_dirac_int(double j, double x); /* Gamma */ double nsl_sf_fact(double n); double nsl_sf_doublefact(double n); double nsl_sf_lnfact(double n); double nsl_sf_lndoublefact(double n); double nsl_sf_choose(double n, double m); double nsl_sf_lnchoose(double n, double m); double nsl_sf_taylorcoeff(double n, double x); double nsl_sf_gegenpoly_n(double n, double l, double x); #if (GSL_MAJOR_VERSION > 2) || (GSL_MAJOR_VERSION == 2) && (GSL_MINOR_VERSION >= 4) double nsl_sf_hermite_prob(double n, double x); double nsl_sf_hermite_phys(double n, double x); double nsl_sf_hermite_func(double n, double x); double nsl_sf_hermite_prob_der(double m, double n, double x); double nsl_sf_hermite_phys_der(double m, double n, double x); double nsl_sf_hermite_func_der(double m, double n, double x); #endif double nsl_sf_hyperg_1F1i(double m, double n, double x); double nsl_sf_hyperg_Ui(double m, double n, double x); double nsl_sf_laguerre_n(double n, double a, double x); double nsl_sf_legendre_Pl(double l, double x); double nsl_sf_legendre_Ql(double l, double x); double nsl_sf_legendre_Plm(double l, double m, double x); double nsl_sf_legendre_sphPlm(double l, double m, double x); double nsl_sf_conicalP_sphreg(double l, double L, double x); double nsl_sf_conicalP_cylreg(double m, double l, double x); double nsl_sf_legendre_H3d(double l, double L, double e); double nsl_sf_psiint(double n); double nsl_sf_psi1int(double n); double nsl_sf_psin(double n, double x); double nsl_sf_zetaint(double n); double nsl_sf_zetam1int(double n); double nsl_sf_etaint(double n); /* random number distributions */ double nsl_sf_poisson(double k, double m); double nsl_sf_bernoulli(double k, double p); double nsl_sf_binomial(double k, double p, double n); double nsl_sf_negative_binomial(double k, double p, double n); double nsl_sf_pascal(double k, double p, double n); double nsl_sf_geometric(double k, double p); double nsl_sf_hypergeometric(double k, double n1, double n2, double t); double nsl_sf_logarithmic(double k, double p); #endif /* NSL_SF_BASIC_H */ diff --git a/src/backend/spreadsheet/Spreadsheet.cpp b/src/backend/spreadsheet/Spreadsheet.cpp index ef18c1ae5..0cf37144d 100644 --- a/src/backend/spreadsheet/Spreadsheet.cpp +++ b/src/backend/spreadsheet/Spreadsheet.cpp @@ -1,942 +1,945 @@ /*************************************************************************** File : Spreadsheet.cpp Project : LabPlot Description : Aspect providing a spreadsheet table with column logic -------------------------------------------------------------------- Copyright : (C) 2006-2008 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2006-2009 Knut Franke (knut.franke@gmx.de) Copyright : (C) 2012-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "Spreadsheet.h" #include "backend/core/AspectPrivate.h" #include "backend/core/AbstractAspect.h" #include "backend/core/column/ColumnStringIO.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include #include -#include +#include +#include + +#include /*! \class Spreadsheet \brief Aspect providing a spreadsheet table with column logic. Spreadsheet is a container object for columns with no data of its own. By definition, it's columns are all of its children inheriting from class Column. Thus, the basic API is already defined by AbstractAspect (managing the list of columns, notification of column insertion/removal) and Column (changing and monitoring state of the actual data). Spreadsheet stores a pointer to its primary view of class SpreadsheetView. SpreadsheetView calls the Spreadsheet API but Spreadsheet only notifies SpreadsheetView by signals without calling its API directly. This ensures a maximum independence of UI and backend. SpreadsheetView can be easily replaced by a different class. User interaction is completely handled in SpreadsheetView and translated into Spreadsheet API calls (e.g., when a user edits a cell this will be handled by the delegate of SpreadsheetView and Spreadsheet will not know whether a script or a user changed the data.). All actions, menus etc. for the user interaction are handled SpreadsheetView, e.g., via a context menu. Selections are also handled by SpreadsheetView. The view itself is created by the first call to view(); \ingroup backend */ Spreadsheet::Spreadsheet(AbstractScriptingEngine* engine, const QString& name, bool loading) : AbstractDataSource(engine, name), m_view(nullptr) { if (!loading) init(); } /*! initializes the spreadsheet with the default number of columns and rows */ void Spreadsheet::init() { KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); const int columns = group.readEntry(QLatin1String("ColumnCount"), 2); const int rows = group.readEntry(QLatin1String("RowCount"), 100); for (int i = 0; i < columns; i++) { Column* new_col = new Column(QString::number(i+1), AbstractColumn::Numeric); new_col->setPlotDesignation(i == 0 ? AbstractColumn::X : AbstractColumn::Y); addChild(new_col); } setRowCount(rows); } /*! 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* Spreadsheet::view() const { if (!m_partView) { m_view = new SpreadsheetView(const_cast(this)); m_partView = m_view; } return m_partView; } bool Spreadsheet::exportView() const { return m_view->exportView(); } bool Spreadsheet::printView() { return m_view->printView(); } bool Spreadsheet::printPreview() const { return m_view->printPreview(); } /*! Returns the maximum number of rows in the spreadsheet. */ int Spreadsheet::rowCount() const { int col_rows, result = 0; for (auto* col: children()) if ((col_rows = col->rowCount()) > result) result = col_rows; return result; } void Spreadsheet::removeRows(int first, int count) { if( count < 1 || first < 0 || first+count > rowCount()) return; WAIT_CURSOR; beginMacro( i18np("%1: remove 1 row", "%1: remove %2 rows", name(), count) ); for (auto* col: children(IncludeHidden)) col->removeRows(first, count); endMacro(); RESET_CURSOR; } void Spreadsheet::insertRows(int before, int count) { if( count < 1 || before < 0 || before > rowCount()) return; WAIT_CURSOR; beginMacro( i18np("%1: insert 1 row", "%1: insert %2 rows", name(), count) ); for (auto* col: children(IncludeHidden)) col->insertRows(before, count); endMacro(); RESET_CURSOR; } void Spreadsheet::appendRows(int count) { insertRows(rowCount(), count); } void Spreadsheet::appendRow() { insertRows(rowCount(), 1); } void Spreadsheet::appendColumns(int count) { insertColumns(columnCount(), count); } void Spreadsheet::appendColumn() { insertColumns(columnCount(), 1); } void Spreadsheet::prependColumns(int count) { insertColumns(0, count); } /*! Sets the number of rows of the spreadsheet to \c new_size */ void Spreadsheet::setRowCount(int new_size) { int current_size = rowCount(); if (new_size > current_size) insertRows(current_size, new_size-current_size); if (new_size < current_size && new_size >= 0) removeRows(new_size, current_size-new_size); } /*! Returns the column with the number \c index. Shallow wrapper around \sa AbstractAspect::child() - see there for caveat. */ Column* Spreadsheet::column(int index) const { return child(index); } /*! Returns the column with the name \c name. */ Column* Spreadsheet::column(const QString &name) const { return child(name); } /*! Returns the total number of columns in the spreadsheet. */ int Spreadsheet::columnCount() const { return childCount(); } /*! Returns the number of columns matching the given designation. */ int Spreadsheet::columnCount(AbstractColumn::PlotDesignation pd) const { int count = 0; for (auto* col: children()) if (col->plotDesignation() == pd) count++; return count; } void Spreadsheet::removeColumns(int first, int count) { if( count < 1 || first < 0 || first+count > columnCount()) return; WAIT_CURSOR; beginMacro( i18np("%1: remove 1 column", "%1: remove %2 columns", name(), count) ); for (int i = 0; i < count; i++) child(first)->remove(); endMacro(); RESET_CURSOR; } void Spreadsheet::insertColumns(int before, int count) { WAIT_CURSOR; beginMacro( i18np("%1: insert 1 column", "%1: insert %2 columns", name(), count) ); Column * before_col = column(before); int rows = rowCount(); for (int i = 0; i < count; i++) { Column * new_col = new Column(QString::number(i+1), AbstractColumn::Numeric); new_col->setPlotDesignation(AbstractColumn::Y); new_col->insertRows(0, rows); insertChildBefore(new_col, before_col); } endMacro(); RESET_CURSOR; } /*! Sets the number of columns to \c new_size */ void Spreadsheet::setColumnCount(int new_size) { int old_size = columnCount(); if ( old_size == new_size || new_size < 0 ) return; if (new_size < old_size) removeColumns(new_size, old_size-new_size); else insertColumns(old_size, new_size-old_size); } /*! Clears the whole spreadsheet. */ void Spreadsheet::clear() { WAIT_CURSOR; beginMacro(i18n("%1: clear", name())); for (auto* col: children()) col->clear(); endMacro(); RESET_CURSOR; } /*! Clears all mask in the spreadsheet. */ void Spreadsheet::clearMasks() { WAIT_CURSOR; beginMacro(i18n("%1: clear all masks", name())); for (auto* col: children()) col->clearMasks(); endMacro(); RESET_CURSOR; } /*! Returns a new context menu. The caller takes ownership of the menu. */ QMenu* Spreadsheet::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } void Spreadsheet::moveColumn(int from, int to) { Column* col = child(from); beginMacro(i18n("%1: move column %2 from position %3 to %4.", name(), col->name(), from+1, to+1)); col->remove(); insertChildBefore(col, child(to)); endMacro(); } void Spreadsheet::copy(Spreadsheet* other) { WAIT_CURSOR; beginMacro(i18n("%1: copy %2", name(), other->name())); for (auto* col: children()) col->remove(); for (auto* src_col: other->children()) { Column * new_col = new Column(src_col->name(), src_col->columnMode()); new_col->copy(src_col); new_col->setPlotDesignation(src_col->plotDesignation()); QVector< Interval > masks = src_col->maskedIntervals(); for (const auto& iv: masks) new_col->setMasked(iv); QVector< Interval > formulas = src_col->formulaIntervals(); for (const auto& iv: formulas) new_col->setFormula(iv, src_col->formula(iv.start())); new_col->setWidth(src_col->width()); addChild(new_col); } setComment(other->comment()); endMacro(); RESET_CURSOR; } // FIXME: replace index-based API with Column*-based one /*! Determines the corresponding X column. */ int Spreadsheet::colX(int col) { for(int i = col-1; i >= 0; i--) { if (column(i)->plotDesignation() == AbstractColumn::X) return i; } int cols = columnCount(); for(int i = col+1; i < cols; i++) { if (column(i)->plotDesignation() == AbstractColumn::X) return i; } return -1; } /*! Determines the corresponding Y column. */ int Spreadsheet::colY(int col) { int cols = columnCount(); if (column(col)->plotDesignation() == AbstractColumn::XError || column(col)->plotDesignation() == AbstractColumn::YError) { // look to the left first for(int i=col-1; i>=0; i--) { if (column(i)->plotDesignation() == AbstractColumn::Y) return i; } for(int i=col+1; iplotDesignation() == AbstractColumn::Y) return i; } } else { // look to the right first for(int i=col+1; iplotDesignation() == AbstractColumn::Y) return i; } for(int i=col-1; i>=0; i--) { if (column(i)->plotDesignation() == AbstractColumn::Y) return i; } } return -1; } /*! Sorts the given list of column. If 'leading' is a null pointer, each column is sorted separately. */ void Spreadsheet::sortColumns(Column* leading, QVector cols, bool ascending) { if(cols.isEmpty()) return; // the normal QPair comparison does not work properly with descending sorting // thefore we use our own compare functions class CompareFunctions { public: static bool doubleLess(const QPair& a, const QPair& b) { return a.first < b.first; } static bool doubleGreater(const QPair& a, const QPair& b) { return a.first > b.first; } static bool integerLess(const QPair& a, const QPair& b) { return a.first < b.first; } static bool integerGreater(const QPair& a, const QPair& b) { return a.first > b.first; } static bool QStringLess(const QPair& a, const QPair& b) { return a < b; } static bool QStringGreater(const QPair& a, const QPair& b) { return a > b; } static bool QDateTimeLess(const QPair& a, const QPair& b) { return a < b; } static bool QDateTimeGreater(const QPair& a, const QPair& b) { return a > b; } }; WAIT_CURSOR; beginMacro(i18n("%1: sort columns", name())); if(leading == 0) { // sort separately for (auto* col: cols) { switch (col->columnMode()) { case AbstractColumn::Numeric: { int rows = col->rowCount(); QVector< QPair > map; for(int j=0; j(col->valueAt(j), j)); if(ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::doubleLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::doubleGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleGreater); QVectorIterator< QPair > it(map); Column *temp_col = new Column("temp", col->columnMode()); int k=0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, k, 1); temp_col->setMasked(col->isMasked(it.next().second)); k++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; break; } case AbstractColumn::Integer: { int rows = col->rowCount(); QVector< QPair > map; for (int j = 0; j < rows; j++) map.append(QPair(col->valueAt(j), j)); if (ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::doubleLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::doubleGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleGreater); QVectorIterator> it(map); Column* temp_col = new Column("temp", col->columnMode()); int k = 0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, k, 1); temp_col->setMasked(col->isMasked(it.next().second)); k++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; break; } case AbstractColumn::Text: { int rows = col->rowCount(); QVector> map; for (int j = 0; j < rows; j++) map.append(QPair(col->textAt(j), j)); if (ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::QStringLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QStringLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::QStringGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QStringGreater); QVectorIterator< QPair > it(map); Column* temp_col = new Column("temp", col->columnMode()); int k = 0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, k, 1); temp_col->setMasked(col->isMasked(it.next().second)); k++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; break; } case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: { int rows = col->rowCount(); QVector< QPair > map; for(int j=0; j(col->dateTimeAt(j), j)); if(ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::QDateTimeLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QDateTimeLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::QDateTimeGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QDateTimeGreater); QVectorIterator< QPair > it(map); Column *temp_col = new Column("temp", col->columnMode()); int k=0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, k, 1); temp_col->setMasked(col->isMasked(it.next().second)); k++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; break; } } } } else { // sort with leading column switch (leading->columnMode()) { case AbstractColumn::Numeric: { QVector> map; int rows = leading->rowCount(); for (int i = 0; i < rows; i++) map.append(QPair(leading->valueAt(i), i)); if (ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::doubleLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::doubleGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::doubleGreater); QVectorIterator> it(map); for (auto* col: cols) { Column *temp_col = new Column("temp", col->columnMode()); it.toFront(); int j = 0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, j, 1); temp_col->setMasked(col->isMasked(it.next().second)); j++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; } break; } case AbstractColumn::Integer: { QVector> map; int rows = leading->rowCount(); for (int i = 0; i < rows; i++) map.append(QPair(leading->valueAt(i), i)); if (ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::integerLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::integerLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::integerGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::integerGreater); QVectorIterator> it(map); for (auto* col: cols) { Column *temp_col = new Column("temp", col->columnMode()); it.toFront(); int j = 0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, j, 1); temp_col->setMasked(col->isMasked(it.next().second)); j++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; } break; } case AbstractColumn::Text: { QVector> map; int rows = leading->rowCount(); for (int i = 0; i < rows; i++) map.append(QPair(leading->textAt(i), i)); if(ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::QStringLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QStringLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::QStringGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QStringGreater); QVectorIterator> it(map); for (auto* col: cols) { Column *temp_col = new Column("temp", col->columnMode()); it.toFront(); int j = 0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, j, 1); temp_col->setMasked(col->isMasked(it.next().second)); j++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; } break; } case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: { QVector> map; int rows = leading->rowCount(); for (int i = 0; i < rows; i++) map.append(QPair(leading->dateTimeAt(i), i)); if (ascending) - qStableSort(map.begin(), map.end(), CompareFunctions::QDateTimeLess); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QDateTimeLess); else - qStableSort(map.begin(), map.end(), CompareFunctions::QDateTimeGreater); + std::stable_sort(map.begin(), map.end(), CompareFunctions::QDateTimeGreater); QVectorIterator> it(map); for (auto* col: cols) { Column *temp_col = new Column("temp", col->columnMode()); it.toFront(); int j=0; // put the values in the right order into temp_col while(it.hasNext()) { temp_col->copy(col, it.peekNext().second, j, 1); temp_col->setMasked(col->isMasked(it.next().second)); j++; } // copy the sorted column col->copy(temp_col, 0, 0, rows); delete temp_col; } break; } } } endMacro(); RESET_CURSOR; } // end of sortColumns() /*! Returns an icon to be used for decorating my views. */ QIcon Spreadsheet::icon() const { return QIcon::fromTheme("labplot-spreadsheet"); } /*! Returns the text displayed in the given cell. */ QString Spreadsheet::text(int row, int col) const { Column* c = column(col); if(!c) return QString(); return c->asStringColumn()->textAt(row); } /*! * This slot is, indirectly, called when a child of \c Spreadsheet (i.e. column) was selected in \c ProjectExplorer. * Emits the signal \c columnSelected that is handled in \c SpreadsheetView. */ void Spreadsheet::childSelected(const AbstractAspect* aspect) { const Column* column = qobject_cast(aspect); if (column) { int index = indexOfChild(column); emit columnSelected(index); } } /*! * This slot is, indirectly, called when a child of \c Spreadsheet (i.e. column) was deselected in \c ProjectExplorer. * Emits the signal \c columnDeselected that is handled in \c SpreadsheetView. */ void Spreadsheet::childDeselected(const AbstractAspect* aspect) { const Column* column = qobject_cast(aspect); if (column) { int index = indexOfChild(column); emit columnDeselected(index); } } /*! * Emits the signal to select or to deselect the column number \c index in the project explorer, * if \c selected=true or \c selected=false, respectively. * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. * This function is called in \c SpreadsheetView upon selection changes. */ void Spreadsheet::setColumnSelectedInView(int index, bool selected) { if (selected) { emit childAspectSelectedInView(child(index)); //deselect the spreadsheet in the project explorer, if a child (column) was selected. //prevents unwanted multiple selection with spreadsheet (if it was selected before). emit childAspectDeselectedInView(this); } else emit childAspectDeselectedInView(child(index)); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void Spreadsheet::save(QXmlStreamWriter* writer) const { writer->writeStartElement("spreadsheet"); writeBasicAttributes(writer); writeCommentElement(writer); //columns for (auto* col: children(IncludeHidden)) col->save(writer); writer->writeEndElement(); // "spreadsheet" } /*! Loads from XML. */ bool Spreadsheet::load(XmlStreamReader* reader, bool preview) { if(reader->isStartElement() && reader->name() == "spreadsheet") { if (!readBasicAttributes(reader)) return false; // read child elements while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement()) break; if (reader->isStartElement()) { if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column(""); if (!column->load(reader, preview)) { delete column; setColumnCount(0); return false; } addChildFast(column); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } } } else // no spreadsheet element reader->raiseError(i18n("no spreadsheet element found")); return !reader->hasError(); } //############################################################################## //######################## Data Import ####################################### //############################################################################## int Spreadsheet::prepareImport(QVector& dataContainer, AbstractFileFilter::ImportMode importMode, int actualRows, int actualCols, QStringList colNameList, QVector columnMode) { DEBUG("Spreadsheet::prepareImport()"); DEBUG(" create() rows = " << actualRows << " cols = " << actualCols); int columnOffset = 0; setUndoAware(false); //make the available columns undo unaware before we resize and rename them below, //the same will be done for new columns in this->resize(). for (int i = 0; i < childCount(); i++) child(i)->setUndoAware(false); columnOffset = this->resize(importMode, colNameList, actualCols); // resize the spreadsheet if (importMode == AbstractFileFilter::Replace) { clear(); setRowCount(actualRows); } else { if (rowCount() < actualRows) setRowCount(actualRows); } if (columnMode.size() < actualCols) { qWarning("columnMode[] size is too small! Giving up."); return -1; } dataContainer.resize(actualCols); for (int n = 0; n < actualCols; n++) { // data() returns a void* which is a pointer to any data type (see ColumnPrivate.cpp) Column* column = this->child(columnOffset+n); DEBUG(" column " << n << " columnMode = " << columnMode[n]); column->setColumnMode(columnMode[n]); //in the most cases the first imported column is meant to be used as x-data. //Other columns provide mostly y-data or errors. //TODO: this has to be configurable for the user in the import widget, //it should be possible to specify x-error plot designation, etc. AbstractColumn::PlotDesignation desig = (n == 0) ? AbstractColumn::X : AbstractColumn::Y; column->setPlotDesignation(desig); switch (columnMode[n]) { case AbstractColumn::Numeric: { QVector* vector = static_cast*>(column->data()); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Integer: { QVector* vector = static_cast*>(column->data()); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Text: { QVector* vector = static_cast*>(column->data()); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); break; } case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { QVector* vector = static_cast* >(column->data()); vector->reserve(actualRows); vector->resize(actualRows); dataContainer[n] = static_cast(vector); break; } } } // QDEBUG("dataPointers =" << dataPointers); DEBUG("Spreadsheet::prepareImport() DONE"); return columnOffset; } /*! resize data source to cols columns returns column offset depending on import mode */ int Spreadsheet::resize(AbstractFileFilter::ImportMode mode, QStringList colNameList, int cols) { // name additional columns for (int k = colNameList.size(); k < cols; k++ ) colNameList.append( "Column " + QString::number(k+1) ); int columnOffset = 0; //indexes the "start column" in the spreadsheet. Starting from this column the data will be imported. Column* newColumn = 0; if (mode == AbstractFileFilter::Append) { columnOffset = childCount(); for (int n = 0; n < cols; n++ ) { newColumn = new Column(colNameList.at(n), AbstractColumn::Numeric); newColumn->setUndoAware(false); addChildFast(newColumn); } } else if (mode == AbstractFileFilter::Prepend) { Column* firstColumn = child(0); for (int n = 0; n < cols; n++ ) { newColumn = new Column(colNameList.at(n), AbstractColumn::Numeric); newColumn->setUndoAware(false); insertChildBeforeFast(newColumn, firstColumn); } } else if (mode == AbstractFileFilter::Replace) { //replace completely the previous content of the data source with the content to be imported. int columns = childCount(); if (columns > cols) { //there're more columns in the data source then required -> remove the superfluous columns for (int i = 0; i < columns-cols; i++) removeChild(child(0)); } else { //create additional columns if needed for (int i = columns; i < cols; i++) { newColumn = new Column(colNameList.at(i), AbstractColumn::Numeric); newColumn->setUndoAware(false); addChildFast(newColumn); } } //rename the columns that are already available and supress the dataChanged signal for them for (int i = 0; i < childCount(); i++) { if (mode == AbstractFileFilter::Replace) child(i)->setSuppressDataChangedSignal(true); child(i)->setName(colNameList.at(i)); } } return columnOffset; } void Spreadsheet::finalizeImport(int columnOffset, int startColumn, int endColumn, const QString& dateTimeFormat, AbstractFileFilter::ImportMode importMode) { DEBUG("Spreadsheet::finalizeImport()"); // set the comments for each of the columns if datasource is a spreadsheet const int rows = rowCount(); for (int n = startColumn; n <= endColumn; n++) { Column* column = this->column(columnOffset + n - startColumn); //DEBUG(" column " << n << " of type " << column->columnMode()); QString comment; switch (column->columnMode()) { case AbstractColumn::Numeric: comment = i18np("numerical data, %1 element", "numerical data, %1 elements", rows); break; case AbstractColumn::Integer: comment = i18np("integer data, %1 element", "integer data, %1 elements", rows); break; case AbstractColumn::Text: comment = i18np("text data, %1 element", "text data, %1 elements", rows); break; case AbstractColumn::Month: comment = i18np("month data, %1 element", "month data, %1 elements", rows); break; case AbstractColumn::Day: comment = i18np("day data, %1 element", "day data, %1 elements", rows); break; case AbstractColumn::DateTime: comment = i18np("date and time data, %1 element", "date and time data, %1 elements", rows); // set same datetime format in column DateTime2StringFilter* filter = static_cast(column->outputFilter()); filter->setFormat(dateTimeFormat); } column->setComment(comment); if (importMode == AbstractFileFilter::Replace) { column->setSuppressDataChangedSignal(false); column->setChanged(); } } //make the spreadsheet and all its children undo aware again setUndoAware(true); for (int i = 0; i < childCount(); i++) child(i)->setUndoAware(true); if (m_partView != nullptr) m_view->resizeHeader(); DEBUG("Spreadsheet::finalizeImport() DONE"); } diff --git a/src/backend/spreadsheet/SpreadsheetModel.cpp b/src/backend/spreadsheet/SpreadsheetModel.cpp index 13a85817e..a41a5754f 100644 --- a/src/backend/spreadsheet/SpreadsheetModel.cpp +++ b/src/backend/spreadsheet/SpreadsheetModel.cpp @@ -1,467 +1,467 @@ /*************************************************************************** File : SpreadsheetModel.cpp Project : LabPlot Description : Model for the access to a Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2007 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2009 Knut Franke (knut.franke@gmx.de) Copyright : (C) 2013-2017 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 "backend/spreadsheet/Spreadsheet.h" #include "backend/spreadsheet/SpreadsheetModel.h" #include "backend/core/datatypes/Double2StringFilter.h" #include #include -#include +#include #include /*! \class SpreadsheetModel \brief Model for the access to a Spreadsheet This is a model in the sense of Qt4 model/view framework which is used to access a Spreadsheet object from any of Qt4s view classes, typically a QTableView. Its main purposes are translating Spreadsheet signals into QAbstractItemModel signals and translating calls to the QAbstractItemModel read/write API into calls in the public API of Spreadsheet. In many cases a pointer to the addressed column is obtained by calling Spreadsheet::column() and the manipulation is done using the public API of column. \ingroup backend */ SpreadsheetModel::SpreadsheetModel(Spreadsheet* spreadsheet) : QAbstractItemModel(0), m_spreadsheet(spreadsheet), m_formula_mode(false) { updateVerticalHeader(); updateHorizontalHeader(); connect(m_spreadsheet, &Spreadsheet::aspectAboutToBeAdded, this, &SpreadsheetModel::handleAspectAboutToBeAdded); connect(m_spreadsheet, &Spreadsheet::aspectAdded, this, &SpreadsheetModel::handleAspectAdded); connect(m_spreadsheet, &Spreadsheet::aspectAboutToBeRemoved, this, &SpreadsheetModel::handleAspectAboutToBeRemoved); connect(m_spreadsheet, &Spreadsheet::aspectRemoved, this, &SpreadsheetModel::handleAspectRemoved); connect(m_spreadsheet, &Spreadsheet::aspectDescriptionChanged, this, &SpreadsheetModel::handleDescriptionChange); for (int i = 0; i < spreadsheet->columnCount(); ++i) { beginInsertColumns(QModelIndex(), i, i); handleAspectAdded(spreadsheet->column(i)); } } Qt::ItemFlags SpreadsheetModel::flags(const QModelIndex& index) const { if (index.isValid()) return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; else return Qt::ItemIsEnabled; } QVariant SpreadsheetModel::data(const QModelIndex& index, int role) const { if( !index.isValid() ) return QVariant(); const int row = index.row(); const int col = index.column(); Column* col_ptr = m_spreadsheet->column(col); if(!col_ptr) return QVariant(); switch(role) { case Qt::ToolTipRole: if(col_ptr->isValid(row)) { if(col_ptr->isMasked(row)) - return QVariant(i18n("%1, masked (ignored in all operations)").arg(col_ptr->asStringColumn()->textAt(row))); + return QVariant(i18n("%1, masked (ignored in all operations)", col_ptr->asStringColumn()->textAt(row))); else return QVariant(col_ptr->asStringColumn()->textAt(row)); } else { if(col_ptr->isMasked(row)) return QVariant(i18n("invalid cell, masked (ignored in all operations)")); else return QVariant(i18n("invalid cell (ignored in all operations)")); } case Qt::EditRole: if(col_ptr->isValid(row)) return QVariant(col_ptr->asStringColumn()->textAt(row)); //m_formula_mode is not used at the moment //if(m_formula_mode) // return QVariant(col_ptr->formula(row)); break; case Qt::DisplayRole: if(!col_ptr->isValid(row)) return QVariant("-"); //m_formula_mode is not used at the moment //if(m_formula_mode) // return QVariant(col_ptr->formula(row)); return QVariant(col_ptr->asStringColumn()->textAt(row)); case Qt::ForegroundRole: if(!col_ptr->isValid(index.row())) return QVariant(QBrush(Qt::red)); break; case MaskingRole: return QVariant(col_ptr->isMasked(row)); case FormulaRole: return QVariant(col_ptr->formula(row)); // case Qt::DecorationRole: // if(m_formula_mode) // return QIcon(QPixmap(":/equals.png")); //TODO } return QVariant(); } QVariant SpreadsheetModel::headerData(int section, Qt::Orientation orientation, int role) const { if ( (orientation == Qt::Horizontal && section > m_spreadsheet->columnCount()-1) || (orientation == Qt::Vertical && section > m_spreadsheet->rowCount()-1) ) return QVariant(); switch(orientation) { case Qt::Horizontal: switch(role) { case Qt::DisplayRole: case Qt::ToolTipRole: case Qt::EditRole: return m_horizontal_header_data.at(section); case Qt::DecorationRole: return m_spreadsheet->child(section)->icon(); case SpreadsheetModel::CommentRole: return m_spreadsheet->child(section)->comment(); } break; case Qt::Vertical: switch(role) { case Qt::DisplayRole: case Qt::ToolTipRole: return m_vertical_header_data.at(section); } } return QVariant(); } int SpreadsheetModel::rowCount(const QModelIndex& parent) const { Q_UNUSED(parent) return m_spreadsheet->rowCount(); } int SpreadsheetModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent) return m_spreadsheet->columnCount(); } bool SpreadsheetModel::setData(const QModelIndex& index, const QVariant& value, int role) { if (!index.isValid()) return false; int row = index.row(); Column* column = m_spreadsheet->column(index.column()); //don't do anything if no new value was provided if (column->columnMode() == AbstractColumn::Numeric) { bool ok; QLocale locale; double new_value = locale.toDouble(value.toString(), &ok); if (ok) { if (column->valueAt(row) == new_value ) return false; } else { //an empty (non-numeric value) was provided if (std::isnan(column->valueAt(row))) return false; } } else { if (column->asStringColumn()->textAt(row) == value.toString()) return false; } switch(role) { case Qt::EditRole: { // remark: the validity of the cell is determined by the input filter if (m_formula_mode) column->setFormula(row, value.toString()); else column->asStringColumn()->setTextAt(row, value.toString()); return true; } case MaskingRole: { m_spreadsheet->column(index.column())->setMasked(row, value.toBool()); return true; } case FormulaRole: { m_spreadsheet->column(index.column())->setFormula(row, value.toString()); return true; } } return false; } QModelIndex SpreadsheetModel::index(int row, int column, const QModelIndex& parent) const { Q_UNUSED(parent) return createIndex(row, column); } QModelIndex SpreadsheetModel::parent(const QModelIndex& child) const { Q_UNUSED(child) return QModelIndex(); } bool SpreadsheetModel::hasChildren(const QModelIndex& parent) const { Q_UNUSED(parent) return false; } void SpreadsheetModel::handleAspectAboutToBeAdded(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* new_child) { const Column* col = qobject_cast(new_child); if (!col || parent != static_cast(m_spreadsheet)) return; //TODO: breaks undo/redo Q_UNUSED(before); // int index = before ? m_spreadsheet->indexOfChild(before) : 0; //beginInsertColumns(QModelIndex(), index, index); } void SpreadsheetModel::handleAspectAdded(const AbstractAspect * aspect) { const Column* col = qobject_cast(aspect); if (!col || aspect->parentAspect() != static_cast(m_spreadsheet)) return; updateVerticalHeader(); updateHorizontalHeader(); connect(col, &Column::plotDesignationChanged, this, &SpreadsheetModel::handlePlotDesignationChange); connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleDataChange); connect(col, &Column::dataChanged, this, &SpreadsheetModel::handleDataChange); connect(col, &Column::modeChanged, this, &SpreadsheetModel::handleModeChange); connect(col, &Column::rowsInserted, this, &SpreadsheetModel::handleRowsInserted); connect(col, &Column::rowsRemoved, this, &SpreadsheetModel::handleRowsRemoved); connect(col, &Column::maskingChanged, this, &SpreadsheetModel::handleDataChange); connect(col->outputFilter(), &AbstractSimpleFilter::digitsChanged, this, &SpreadsheetModel::handleDigitsChange); beginResetModel(); //TODO: breaks undo/redo //endInsertColumns(); endResetModel(); m_spreadsheet->emitColumnCountChanged(); } void SpreadsheetModel::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const Column* col = qobject_cast(aspect); if (!col || aspect->parentAspect() != static_cast(m_spreadsheet)) return; beginResetModel(); disconnect(col, 0, this, 0); } void SpreadsheetModel::handleAspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child) { Q_UNUSED(before) const Column* col = qobject_cast(child); if (!col || parent != static_cast(m_spreadsheet)) return; updateVerticalHeader(); updateHorizontalHeader(); endResetModel(); m_spreadsheet->emitColumnCountChanged(); } void SpreadsheetModel::handleDescriptionChange(const AbstractAspect* aspect) { const Column* col = qobject_cast(aspect); if (!col || aspect->parentAspect() != static_cast(m_spreadsheet)) return; updateHorizontalHeader(); int index = m_spreadsheet->indexOfChild(col); emit headerDataChanged(Qt::Horizontal, index, index); } void SpreadsheetModel::handleModeChange(const AbstractColumn* col) { updateHorizontalHeader(); int index = m_spreadsheet->indexOfChild(col); emit headerDataChanged(Qt::Horizontal, index, index); handleDataChange(col); //output filter was changed after the mode change, update the signal-slot connection disconnect(0, SIGNAL(digitsChanged()), this, SLOT(handledigitsChange())); connect(dynamic_cast(col)->outputFilter(), SIGNAL(digitsChanged()), this, SLOT(handleDigitsChange())); } void SpreadsheetModel::handleDigitsChange() { const Double2StringFilter* filter = dynamic_cast(QObject::sender()); if (!filter) return; const AbstractColumn* col = filter->output(0); handleDataChange(col); } void SpreadsheetModel::handlePlotDesignationChange(const AbstractColumn* col) { updateHorizontalHeader(); int index = m_spreadsheet->indexOfChild(col); emit headerDataChanged(Qt::Horizontal, index, m_spreadsheet->columnCount()-1); } void SpreadsheetModel::handleDataChange(const AbstractColumn* col) { int i = m_spreadsheet->indexOfChild(col); emit dataChanged(index(0, i), index(col->rowCount()-1, i)); } void SpreadsheetModel::handleRowsInserted(const AbstractColumn* col, int before, int count) { Q_UNUSED(before) Q_UNUSED(count) updateVerticalHeader(); int i = m_spreadsheet->indexOfChild(col); emit dataChanged(index(0, i), index(col->rowCount()-1, i)); m_spreadsheet->emitRowCountChanged(); } void SpreadsheetModel::handleRowsRemoved(const AbstractColumn* col, int first, int count) { Q_UNUSED(first) Q_UNUSED(count) updateVerticalHeader(); int i = m_spreadsheet->indexOfChild(col); emit dataChanged(index(0, i), index(col->rowCount()-1, i)); m_spreadsheet->emitRowCountChanged(); } void SpreadsheetModel::updateVerticalHeader() { int old_rows = m_vertical_header_data.size(); int new_rows = m_spreadsheet->rowCount(); if (new_rows > old_rows) { beginInsertRows(QModelIndex(), old_rows, new_rows-1); for(int i=old_rows+1; i<=new_rows; i++) m_vertical_header_data << i; endInsertRows(); } else if (new_rows < old_rows) { beginRemoveRows(QModelIndex(), new_rows, old_rows-1); while (m_vertical_header_data.size() > new_rows) m_vertical_header_data.removeLast(); endRemoveRows(); } } void SpreadsheetModel::updateHorizontalHeader() { int column_count = m_spreadsheet->childCount(); while(m_horizontal_header_data.size() < column_count) m_horizontal_header_data << QString(); while(m_horizontal_header_data.size() > column_count) m_horizontal_header_data.removeLast(); for (int i = 0; i < column_count; i++) { Column* col = m_spreadsheet->child(i); QString type; switch(col->columnMode()) { case AbstractColumn::Numeric: type = QLatin1String(" {") + i18n("Numeric") + QLatin1Char('}'); break; case AbstractColumn::Integer: type = QLatin1String(" {") + i18n("Integer") + QLatin1Char('}'); break; case AbstractColumn::Text: type = QLatin1String(" {") + i18n("Text") + QLatin1Char('}'); break; case AbstractColumn::Month: type = QLatin1String(" {") + i18n("Month names") + QLatin1Char('}'); break; case AbstractColumn::Day: type = QLatin1String(" {") + i18n("Day names") + QLatin1Char('}'); break; case AbstractColumn::DateTime: type = QLatin1String(" {") + i18n("Date and time") + QLatin1Char('}'); break; } QString designation; switch(col->plotDesignation()) { case AbstractColumn::NoDesignation: break; case AbstractColumn::X: designation = QLatin1String(" [X]"); break; case AbstractColumn::Y: designation = QLatin1String(" [Y]"); break; case AbstractColumn::Z: designation = QLatin1String(" [Z]"); break; case AbstractColumn::XError: designation = QLatin1String(" [") + i18n("X-error") + QLatin1Char(']'); break; case AbstractColumn::XErrorPlus: designation = QLatin1String(" [") + i18n("X-error +") + QLatin1Char(']'); break; case AbstractColumn::XErrorMinus: designation = QLatin1String(" [") + i18n("X-error -") + QLatin1Char(']'); break; case AbstractColumn::YError: designation = QLatin1String(" [") + i18n("Y-error") + QLatin1Char(']'); break; case AbstractColumn::YErrorPlus: designation = QLatin1String(" [") + i18n("Y-error +") + QLatin1Char(']'); break; case AbstractColumn::YErrorMinus: designation = QLatin1String(" [") + i18n("Y-error -") + QLatin1Char(']'); break; } m_horizontal_header_data.replace(i, col->name() + type + designation); } } Column* SpreadsheetModel::column(int index) { return m_spreadsheet->column(index); } void SpreadsheetModel::activateFormulaMode(bool on) { if (m_formula_mode == on) return; m_formula_mode = on; int rows = m_spreadsheet->rowCount(); int cols = m_spreadsheet->columnCount(); if (rows > 0 && cols > 0) emit dataChanged(index(0,0), index(rows-1,cols-1)); } bool SpreadsheetModel::formulaModeActive() const { return m_formula_mode; } diff --git a/src/backend/worksheet/TextLabel.cpp b/src/backend/worksheet/TextLabel.cpp index 7a580c2d4..3edde74a0 100644 --- a/src/backend/worksheet/TextLabel.cpp +++ b/src/backend/worksheet/TextLabel.cpp @@ -1,872 +1,872 @@ /*************************************************************************** File : TextLabel.cpp Project : LabPlot Description : Text label supporting reach text and latex formatting -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-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 "TextLabel.h" #include "Worksheet.h" #include "TextLabelPrivate.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include /** * \class TextLabel * \brief A label supporting rendering of html- and tex-formated textes. * * The label is aligned relative to the specified position. * The position can be either specified by providing the x- and y- coordinates * in parent's coordinate system, or by specifying one of the predefined position * flags (\ca HorizontalPosition, \ca VerticalPosition). */ TextLabel::TextLabel(const QString& name, Type type):WorksheetElement(name), d_ptr(new TextLabelPrivate(this)), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::TextLabel(const QString &name, TextLabelPrivate *dd, Type type):WorksheetElement(name), d_ptr(dd), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::Type TextLabel::type() const { return m_type; } void TextLabel::init() { Q_D(TextLabel); KConfig config; KConfigGroup group; if (m_type == AxisTitle) group = config.group("AxisTitle"); else if (m_type == PlotTitle) group = config.group("PlotTitle"); else if (m_type == PlotLegendTitle) group = config.group("PlotLegendTitle"); else group = config.group("TextLabel"); //properties common to all types d->textWrapper.teXUsed = group.readEntry("TeXUsed", false); d->teXFont.setFamily(group.readEntry("TeXFontFamily", "Computer Modern")); d->teXFont.setPointSize(group.readEntry("TeXFontSize", 12)); d->teXFontColor = group.readEntry("TeXFontColor", QColor(Qt::black)); d->teXBackgroundColor = group.readEntry("TeXBackgroundColor", QColor(Qt::white)); d->rotationAngle = group.readEntry("Rotation", 0.0); d->staticText.setTextFormat(Qt::RichText); // explicitly set no wrap mode for text label to avoid unnecessary line breaks QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); d->staticText.setTextOption(textOption); //position and alignment relevant properties, dependent on the actual type if (m_type == PlotTitle || m_type == PlotLegendTitle) { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCenter); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionTop); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignBottom); } else { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCustom); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionCustom); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignCenter); } //scaling: //we need to scale from the font size specified in points to scene units. //furhermore, we create the tex-image in a higher resolution then usual desktop resolution // -> take this into account d->scaleFactor = Worksheet::convertToSceneUnits(1, Worksheet::Point); d->teXImageResolution = QApplication::desktop()->physicalDpiX(); d->teXImageScaleFactor = Worksheet::convertToSceneUnits(2.54/QApplication::desktop()->physicalDpiX(), Worksheet::Centimeter); connect(&d->teXImageFutureWatcher, &QFutureWatcher::finished, this, &TextLabel::updateTeXImage); } TextLabel::~TextLabel() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } QGraphicsItem* TextLabel::graphicsItem() const { return d_ptr; } void TextLabel::setParentGraphicsItem(QGraphicsItem* item) { Q_D(TextLabel); d->setParentItem(item); d->updatePosition(); } void TextLabel::retransform() { Q_D(TextLabel); d->retransform(); } void TextLabel::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("TextLabel::handleResize()"); Q_UNUSED(pageResize); Q_D(TextLabel); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); d->teXFont.setPointSizeF(d->teXFont.pointSizeF() * ratio); d->updateText(); //TODO: doesn't seem to work QTextDocument doc; doc.setHtml(d->textWrapper.text); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); QTextCharFormat fmt = cursor.charFormat(); QFont font = fmt.font(); font.setPointSizeF(font.pointSizeF() * ratio); fmt.setFont(font); cursor.setCharFormat(fmt); } /*! Returns an icon to be used in the project explorer. */ QIcon TextLabel::icon() const{ return QIcon::fromTheme("draw-text"); } QMenu* TextLabel::createContextMenu() { QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" if (!visibilityAction) { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &TextLabel::visibilityChanged); } visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); menu->insertSeparator(firstAction); return menu; } /* ============================ getter methods ================= */ CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::TextWrapper, text, textWrapper) CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXFontColor, teXFontColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXBackgroundColor, teXBackgroundColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QFont, teXFont, teXFont); CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::PositionWrapper, position, position); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::HorizontalAlignment, horizontalAlignment, horizontalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::VerticalAlignment, verticalAlignment, verticalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, rotationAngle, rotationAngle); /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(TextLabel, SetText, TextLabel::TextWrapper, textWrapper, updateText); void TextLabel::setText(const TextWrapper &textWrapper) { Q_D(TextLabel); if ( (textWrapper.text != d->textWrapper.text) || (textWrapper.teXUsed != d->textWrapper.teXUsed) ) - exec(new TextLabelSetTextCmd(d, textWrapper, i18n("%1: set label text"))); + exec(new TextLabelSetTextCmd(d, textWrapper, ki18n("%1: set label text"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFont, QFont, teXFont, updateText); void TextLabel::setTeXFont(const QFont& font) { Q_D(TextLabel); if (font != d->teXFont) - exec(new TextLabelSetTeXFontCmd(d, font, i18n("%1: set TeX main font"))); + exec(new TextLabelSetTeXFontCmd(d, font, ki18n("%1: set TeX main font"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFontColor, QColor, teXFontColor, updateText); void TextLabel::setTeXFontColor(const QColor color) { Q_D(TextLabel); if (color != d->teXFontColor) - exec(new TextLabelSetTeXFontColorCmd(d, color, i18n("%1: set TeX font color"))); + exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set TeX font color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, teXBackgroundColor, updateText); void TextLabel::setTeXBackgroundColor(const QColor color) { Q_D(TextLabel); if (color != d->teXBackgroundColor) - exec(new TextLabelSetTeXBackgroundColorCmd(d, color, i18n("%1: set TeX background color"))); + exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set TeX background color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetPosition, TextLabel::PositionWrapper, position, retransform); void TextLabel::setPosition(const PositionWrapper& pos) { Q_D(TextLabel); if (pos.point!=d->position.point || pos.horizontalPosition!=d->position.horizontalPosition || pos.verticalPosition!=d->position.verticalPosition) - exec(new TextLabelSetPositionCmd(d, pos, i18n("%1: set position"))); + exec(new TextLabelSetPositionCmd(d, pos, ki18n("%1: set position"))); } /*! sets the position without undo/redo-stuff */ void TextLabel::setPosition(QPointF point) { Q_D(TextLabel); if (point != d->position.point) { d->position.point = point; retransform(); } } /*! * position is set to invalid if the parent item is not drawn on the scene * (e.g. axis is not drawn because it's outside plot ranges -> don't draw axis' title label) */ void TextLabel::setPositionInvalid(bool invalid) { Q_D(TextLabel); if (invalid != d->positionInvalid) { d->positionInvalid = invalid; } } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect); void TextLabel::setRotationAngle(qreal angle) { Q_D(TextLabel); if (angle != d->rotationAngle) - exec(new TextLabelSetRotationAngleCmd(d, angle, i18n("%1: set rotation angle"))); + exec(new TextLabelSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetHorizontalAlignment, TextLabel::HorizontalAlignment, horizontalAlignment, retransform); void TextLabel::setHorizontalAlignment(const TextLabel::HorizontalAlignment hAlign) { Q_D(TextLabel); if (hAlign != d->horizontalAlignment) - exec(new TextLabelSetHorizontalAlignmentCmd(d, hAlign, i18n("%1: set horizontal alignment"))); + exec(new TextLabelSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetVerticalAlignment, TextLabel::VerticalAlignment, verticalAlignment, retransform); void TextLabel::setVerticalAlignment(const TextLabel::VerticalAlignment vAlign) { Q_D(TextLabel); if (vAlign != d->verticalAlignment) - exec(new TextLabelSetVerticalAlignmentCmd(d, vAlign, i18n("%1: set vertical alignment"))); + exec(new TextLabelSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment"))); } STD_SWAP_METHOD_SETTER_CMD_IMPL_F(TextLabel, SetVisible, bool, swapVisible, retransform); void TextLabel::setVisible(bool on) { Q_D(TextLabel); - exec(new TextLabelSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new TextLabelSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool TextLabel::isVisible() const { Q_D(const TextLabel); return d->isVisible(); } void TextLabel::setPrinting(bool on) { Q_D(TextLabel); d->m_printing = on; } void TextLabel::updateTeXImage() { Q_D(TextLabel); d->updateTeXImage(); } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void TextLabel::visibilityChanged() { Q_D(const TextLabel); this->setVisible(!d->isVisible()); } //############################################################################## //####################### Private implementation ############################### //############################################################################## TextLabelPrivate::TextLabelPrivate(TextLabel* owner) : teXRenderSuccessful(false), positionInvalid(false), suppressItemChangeEvent(false), suppressRetransform(false), m_printing(false), m_hovered(false), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); } QString TextLabelPrivate::name() const { return q->name(); } /*! calculates the position and the bounding box of the label. Called on geometry or text changes. */ void TextLabelPrivate::retransform() { if (suppressRetransform) return; if (position.horizontalPosition != TextLabel::hPositionCustom || position.verticalPosition != TextLabel::vPositionCustom) updatePosition(); float x = position.point.x(); float y = position.point.y(); //determine the size of the label in scene units. float w, h; if (textWrapper.teXUsed) { //image size is in pixel, convert to scene units w = teXImage.width()*teXImageScaleFactor; h = teXImage.height()*teXImageScaleFactor; } else { //size is in points, convert to scene units w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system QPointF itemPos; switch (horizontalAlignment) { case TextLabel::hAlignLeft: itemPos.setX(x - w/2); break; case TextLabel::hAlignCenter: itemPos.setX(x); break; case TextLabel::hAlignRight: itemPos.setX(x + w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: itemPos.setY(y - h/2); break; case TextLabel::vAlignCenter: itemPos.setY(y); break; case TextLabel::vAlignBottom: itemPos.setY(y + h/2); break; } suppressItemChangeEvent = true; setPos(itemPos); suppressItemChangeEvent = false; boundingRectangle.setX(-w/2); boundingRectangle.setY(-h/2); boundingRectangle.setWidth(w); boundingRectangle.setHeight(h); recalcShapeAndBoundingRect(); } /*! calculates the position of the label, when the position relative to the parent was specified (left, right, etc.) */ void TextLabelPrivate::updatePosition() { //determine the parent item QRectF parentRect; QGraphicsItem* parent = parentItem(); if (parent) { parentRect = parent->boundingRect(); } else { if (!scene()) return; parentRect = scene()->sceneRect(); } if (position.horizontalPosition != TextLabel::hPositionCustom) { if (position.horizontalPosition == TextLabel::hPositionLeft) position.point.setX( parentRect.x() ); else if (position.horizontalPosition == TextLabel::hPositionCenter) position.point.setX( parentRect.x() + parentRect.width()/2 ); else if (position.horizontalPosition == TextLabel::hPositionRight) position.point.setX( parentRect.x() + parentRect.width() ); } if (position.verticalPosition != TextLabel::vPositionCustom) { if (position.verticalPosition == TextLabel::vPositionTop) position.point.setY( parentRect.y() ); else if (position.verticalPosition == TextLabel::vPositionCenter) position.point.setY( parentRect.y() + parentRect.height()/2 ); else if (position.verticalPosition == TextLabel::vPositionBottom) position.point.setY( parentRect.y() + parentRect.height() ); } emit q->positionChanged(position); } /*! updates the static text. */ void TextLabelPrivate::updateText() { if (suppressRetransform) return; if (textWrapper.teXUsed) { TeXRenderer::Formatting format; format.fontColor = teXFontColor; format.backgroundColor = teXBackgroundColor; format.fontSize = teXFont.pointSize(); format.fontFamily = teXFont.family(); format.dpi = teXImageResolution; QFuture future = QtConcurrent::run(TeXRenderer::renderImageLaTeX, textWrapper.text, &teXRenderSuccessful, format); teXImageFutureWatcher.setFuture(future); //don't need to call retransorm() here since it is done in updateTeXImage //when the asynchronous rendering of the image is finished. } else { staticText.setText(textWrapper.text); //the size of the label was most probably changed. //call retransform() to recalculate the position and the bounding box of the label retransform(); } } void TextLabelPrivate::updateTeXImage() { teXImage = teXImageFutureWatcher.result(); retransform(); DEBUG("teXRenderSuccessful =" << teXRenderSuccessful); emit q->teXImageUpdated(teXRenderSuccessful); } bool TextLabelPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->changed(); emit q->visibleChanged(on); return oldValue; } /*! Returns the outer bounds of the item as a rectangle. */ QRectF TextLabelPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath TextLabelPrivate::shape() const { return labelShape; } /*! recalculates the outer bounds and the shape of the label. */ void TextLabelPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); QMatrix matrix; matrix.rotate(-rotationAngle); transformedBoundingRectangle = matrix.mapRect(boundingRectangle); labelShape = QPainterPath(); labelShape.addRect(boundingRectangle); labelShape = matrix.map(labelShape); emit q->changed(); } void TextLabelPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (positionInvalid) return; if (textWrapper.text.isEmpty()) return; painter->save(); painter->rotate(-rotationAngle); if (textWrapper.teXUsed) { if (boundingRect().width() != 0.0 && boundingRect().height() != 0.0) painter->drawImage(boundingRect(), teXImage); } else { painter->scale(scaleFactor, scaleFactor); float w = staticText.size().width(); float h = staticText.size().height(); painter->drawStaticText(QPoint(-w/2,-h/2), staticText); } painter->restore(); if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(labelShape); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(labelShape); } } QVariant TextLabelPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (suppressItemChangeEvent) return value; if (change == QGraphicsItem::ItemPositionChange) { //convert item's center point in parent's coordinates TextLabel::PositionWrapper tempPosition; tempPosition.point = positionFromItemPosition(value.toPointF()); tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; //emit the signals in order to notify the UI. //we don't set the position related member variables during the mouse movements. //this is done on mouse release events only. emit q->positionChanged(tempPosition); } return QGraphicsItem::itemChange(change, value); } void TextLabelPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { //convert position of the item in parent coordinates to label's position QPointF point = positionFromItemPosition(pos()); if (qAbs(point.x()-position.point.x())>20 && qAbs(point.y()-position.point.y())>20 ) { //position was changed -> set the position related member variables suppressRetransform = true; TextLabel::PositionWrapper tempPosition; tempPosition.point = point; tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; q->setPosition(tempPosition); suppressRetransform = false; } QGraphicsItem::mouseReleaseEvent(event); } /*! * converts label's position to GraphicsItem's position. */ QPointF TextLabelPrivate::positionFromItemPosition(QPointF itemPos) { float x = itemPos.x(); float y = itemPos.y(); float w, h; QPointF tmpPosition; if (textWrapper.teXUsed) { w = teXImage.width()*scaleFactor; h = teXImage.height()*scaleFactor; } else { w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new position switch (horizontalAlignment) { case TextLabel::hAlignLeft: tmpPosition.setX(x + w/2); break; case TextLabel::hAlignCenter: tmpPosition.setX(x); break; case TextLabel::hAlignRight: tmpPosition.setX(x - w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: tmpPosition.setY(y + h/2); break; case TextLabel::vAlignCenter: tmpPosition.setY(y); break; case TextLabel::vAlignBottom: tmpPosition.setY(y - h/2); break; } return tmpPosition; } void TextLabelPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void TextLabelPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void TextLabelPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void TextLabel::save(QXmlStreamWriter* writer) const { Q_D(const TextLabel); writer->writeStartElement( "textLabel" ); writeBasicAttributes(writer); writeCommentElement(writer); //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->position.point.x()) ); writer->writeAttribute( "y", QString::number(d->position.point.y()) ); writer->writeAttribute( "horizontalPosition", QString::number(d->position.horizontalPosition) ); writer->writeAttribute( "verticalPosition", QString::number(d->position.verticalPosition) ); writer->writeAttribute( "horizontalAlignment", QString::number(d->horizontalAlignment) ); writer->writeAttribute( "verticalAlignment", QString::number(d->verticalAlignment) ); writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); writer->writeStartElement( "text" ); writer->writeCharacters( d->textWrapper.text ); writer->writeEndElement(); writer->writeStartElement( "format" ); writer->writeAttribute( "teXUsed", QString::number(d->textWrapper.teXUsed) ); WRITE_QFONT(d->teXFont); writer->writeAttribute( "teXFontColor_r", QString::number(d->teXFontColor.red()) ); writer->writeAttribute( "teXFontColor_g", QString::number(d->teXFontColor.green()) ); writer->writeAttribute( "teXFontColor_b", QString::number(d->teXFontColor.blue()) ); writer->writeEndElement(); if (d->textWrapper.teXUsed) { writer->writeStartElement("teXImage"); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); d->teXImage.save(&buffer, "PNG"); writer->writeCharacters(ba.toBase64()); writer->writeEndElement(); } writer->writeEndElement(); // close "textLabel" section } //! Load from XML bool TextLabel::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "textLabel") { reader->raiseError(i18n("no textLabel element found")); return false; } if (!readBasicAttributes(reader)) return false; Q_D(TextLabel); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; bool teXImageFound = false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "textLabel") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.point.setX(str.toDouble()); str = attribs.value("y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.point.setY(str.toDouble()); str = attribs.value("horizontalPosition").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'horizontalPosition'")); + reader->raiseWarning(attributeWarning.subs("horizontalPosition").toString()); else d->position.horizontalPosition = (TextLabel::HorizontalPosition)str.toInt(); str = attribs.value("verticalPosition").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'verticalPosition'")); + reader->raiseWarning(attributeWarning.subs("verticalPosition").toString()); else d->position.verticalPosition = (TextLabel::VerticalPosition)str.toInt(); str = attribs.value("horizontalAlignment").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'horizontalAlignment'")); + reader->raiseWarning(attributeWarning.subs("horizontalAlignment").toString()); else d->horizontalAlignment = (TextLabel::HorizontalAlignment)str.toInt(); str = attribs.value("verticalAlignment").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'verticalAlignment'")); + reader->raiseWarning(attributeWarning.subs("verticalAlignment").toString()); else d->verticalAlignment = (TextLabel::VerticalAlignment)str.toInt(); str = attribs.value("rotationAngle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'rotationAngle'")); + reader->raiseWarning(attributeWarning.subs("rotationAngle").toString()); else d->rotationAngle = str.toInt(); str = attribs.value("visible").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "text") { d->textWrapper.text = reader->readElementText(); } else if (!preview && reader->name() == "format") { attribs = reader->attributes(); str = attribs.value("teXUsed").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'teXUsed'")); + reader->raiseWarning(attributeWarning.subs("teXUsed").toString()); else d->textWrapper.teXUsed = str.toInt(); READ_QFONT(d->teXFont); str = attribs.value("teXFontColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'teXFontColor_r'")); + reader->raiseWarning(attributeWarning.subs("teXFontColor_r").toString()); else d->teXFontColor.setRed( str.toInt() ); str = attribs.value("teXFontColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'teXFontColor_g'")); + reader->raiseWarning(attributeWarning.subs("teXFontColor_g").toString()); else d->teXFontColor.setGreen( str.toInt() ); str = attribs.value("teXFontColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'teXFontColor_b'")); + reader->raiseWarning(attributeWarning.subs("teXFontColor_b").toString()); else d->teXFontColor.setBlue( str.toInt() ); } else if (!preview && reader->name() == "teXImage") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray ba = QByteArray::fromBase64(content.toAscii()); teXImageFound = d->teXImage.loadFromData(ba); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (preview) return true; //in case we use latex and the image was stored (older versions of LabPlot didn't save the image)and loaded, //we just need to retransform. //otherwise, we set the static text and retransform in updateText() if ( !(d->textWrapper.teXUsed && teXImageFound) ) d->updateText(); else retransform(); return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void TextLabel::loadThemeConfig(const KConfig& config) { Q_D(TextLabel); KConfigGroup group = config.group("Label"); const QColor fontColor = group.readEntry("FontColor", QColor(Qt::white)); const QColor backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::black)); d->suppressRetransform = true; if (!d->textWrapper.teXUsed && !d->textWrapper.text.isEmpty()) { //replace colors in the html-formatted string QTextDocument doc; doc.setHtml(d->textWrapper.text); QTextCharFormat fmt; fmt.setForeground(QBrush(fontColor)); fmt.setBackground(QBrush(backgroundColor)); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); cursor.setCharFormat(fmt); TextLabel::TextWrapper wrapper(doc.toHtml(), d->textWrapper.teXUsed); this->setText(wrapper); } else { //replace colors in the TeX-string this->setTeXFontColor(fontColor); this->setTeXBackgroundColor(backgroundColor); } d->suppressRetransform = false; d->updateText(); } void TextLabel::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Label"); //TODO // group.writeEntry("TeXFontColor", (QColor) this->teXFontColor()); } diff --git a/src/backend/worksheet/Worksheet.cpp b/src/backend/worksheet/Worksheet.cpp index 73e4c6022..56d6c0290 100644 --- a/src/backend/worksheet/Worksheet.cpp +++ b/src/backend/worksheet/Worksheet.cpp @@ -1,1048 +1,1049 @@ /*************************************************************************** File : Worksheet.cpp Project : LabPlot Description : Worksheet -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2011-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "Worksheet.h" #include "WorksheetPrivate.h" #include "WorksheetElement.h" #include "commonfrontend/worksheet/WorksheetView.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/TextLabel.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "kdefrontend/worksheet/ExportWorksheetDialog.h" #include "kdefrontend/ThemeHandler.h" #include #include #include #include #include #include #include #include #include +#include #include /** * \class Worksheet * \brief Top-level container for worksheet elements like plot, labels, etc. * * The worksheet is, besides the data containers \c Spreadsheet and \c Matrix, another central part of the application * and provides an area for showing and grouping together different kinds of worksheet objects - plots, labels &etc; * * * \ingroup worksheet */ Worksheet::Worksheet(AbstractScriptingEngine* engine, const QString& name, bool loading) : AbstractPart(name), scripted(engine), d(new WorksheetPrivate(this)), m_view(nullptr) { connect(this, &Worksheet::aspectAdded, this, &Worksheet::handleAspectAdded); connect(this, &Worksheet::aspectAboutToBeRemoved, this, &Worksheet::handleAspectAboutToBeRemoved); connect(this, &Worksheet::aspectRemoved, this, &Worksheet::handleAspectRemoved); if (!loading) init(); } Worksheet::~Worksheet() { delete d; } void Worksheet::init() { KConfig config; KConfigGroup group = config.group( "Worksheet" ); //size d->scaleContent = group.readEntry("ScaleContent", false); d->useViewSize = group.readEntry("UseViewSize", false); d->pageRect.setX(0); d->pageRect.setY(0); d->pageRect.setWidth(group.readEntry("Width", 1500)); d->pageRect.setHeight(group.readEntry("Height",1500)); d->m_scene->setSceneRect(d->pageRect); //background d->backgroundType = (PlotArea::BackgroundType) group.readEntry("BackgroundType", (int) PlotArea::Color); d->backgroundColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("BackgroundColorStyle", (int) PlotArea::SingleColor); d->backgroundImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("BackgroundImageStyle", (int) PlotArea::Scaled); d->backgroundBrushStyle = (Qt::BrushStyle) group.readEntry("BackgroundBrushStyle", (int) Qt::SolidPattern); d->backgroundFileName = group.readEntry("BackgroundFileName", QString()); d->backgroundFirstColor = group.readEntry("BackgroundFirstColor", QColor(Qt::white)); d->backgroundSecondColor = group.readEntry("BackgroundSecondColor", QColor(Qt::black)); d->backgroundOpacity = group.readEntry("BackgroundOpacity", 1.0); //layout d->layout = (Worksheet::Layout) group.readEntry("Layout", (int) Worksheet::VerticalLayout); d->layoutTopMargin = group.readEntry("LayoutTopMargin", convertToSceneUnits(1, Centimeter)); d->layoutBottomMargin = group.readEntry("LayoutBottomMargin", convertToSceneUnits(1, Centimeter)); d->layoutLeftMargin = group.readEntry("LayoutLeftMargin", convertToSceneUnits(1, Centimeter)); d->layoutRightMargin = group.readEntry("LayoutRightMargin", convertToSceneUnits(1, Centimeter)); d->layoutVerticalSpacing = group.readEntry("LayoutVerticalSpacing", convertToSceneUnits(1, Centimeter)); d->layoutHorizontalSpacing = group.readEntry("LayoutHorizontalSpacing", convertToSceneUnits(1, Centimeter)); d->layoutRowCount = group.readEntry("LayoutRowCount", 2); d->layoutColumnCount = group.readEntry("LayoutColumnCount", 2); //default theme KConfigGroup settings = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); d->theme = settings.readEntry(QLatin1String("Theme"), ""); if (!d->theme.isEmpty()) loadTheme(d->theme); } /*! converts from \c unit to the scene units. At the moment, 1 scene unit corresponds to 1/10 mm. */ float Worksheet::convertToSceneUnits(const float value, const Worksheet::Unit unit) { switch (unit) { case Worksheet::Millimeter: return value*10.0; case Worksheet::Centimeter: return value*100.0; case Worksheet::Inch: return value*25.4*10.; case Worksheet::Point: return value*25.4/72.*10.; } return 0; } /*! converts from the scene units to \c unit . At the moment, 1 scene unit corresponds to 1/10 mm. */ float Worksheet::convertFromSceneUnits(const float value, const Worksheet::Unit unit) { switch (unit) { case Worksheet::Millimeter: return value/10.0; case Worksheet::Centimeter: return value/100.0; case Worksheet::Inch: return value/25.4/10.; case Worksheet::Point: return value/25.4/10.*72.; } return 0; } QIcon Worksheet::icon() const { return QIcon::fromTheme("labplot-worksheet"); } /** * Return a new context menu. The caller takes ownership of the menu. */ QMenu* Worksheet::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } //! Construct 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* Worksheet::view() const { if (!m_partView) { m_view = new WorksheetView(const_cast(this)); m_partView = m_view; connect(m_view, &WorksheetView::statusInfo, this, &Worksheet::statusInfo); } return m_partView; } /*! * returns the list of all parent aspects (folders and sub-folders) * together with all the data containers required to plot the data in the worksheet */ QVector Worksheet::dependsOn() const { //add all parent aspects (folders and sub-folders) QVector aspects = AbstractAspect::dependsOn(); //traverse all plots and add all data containers they depend on for (const auto* plot : children()) aspects << plot->dependsOn(); return aspects; } bool Worksheet::exportView() const { ExportWorksheetDialog* dlg = new ExportWorksheetDialog(m_view); dlg->setFileName(name()); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { QString path = dlg->path(); const WorksheetView::ExportFormat format = dlg->exportFormat(); const WorksheetView::ExportArea area = dlg->exportArea(); const bool background = dlg->exportBackground(); const int resolution = dlg->exportResolution(); WAIT_CURSOR; m_view->exportToFile(path, format, area, background, resolution); RESET_CURSOR; } delete dlg; return ret; } bool Worksheet::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, m_view); - dlg->setWindowTitle(i18n("Print Worksheet")); + dlg->setWindowTitle(i18nc("@title:window", "Print Worksheet")); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) m_view->print(&printer); delete dlg; return ret; } bool Worksheet::printPreview() const { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(m_view); connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &WorksheetView::print); return dlg->exec(); } void Worksheet::handleAspectAdded(const AbstractAspect* aspect) { const WorksheetElement* addedElement = qobject_cast(aspect); if (!addedElement) return; if (aspect->parentAspect() != this) return; //add the GraphicsItem of the added child to the scene QGraphicsItem* item = addedElement->graphicsItem(); d->m_scene->addItem(item); qreal zVal = 0; for (auto* child : children(IncludeHidden)) child->graphicsItem()->setZValue(zVal++); //if a theme was selected in the worksheet, apply this theme for newly added children if (!d->theme.isEmpty() && !isLoading()) { KConfig config(ThemeHandler::themeFilePath(d->theme), KConfig::SimpleConfig); const_cast(addedElement)->loadThemeConfig(config); } //recalculated the layout if (!isLoading()) { if (d->layout != Worksheet::NoLayout) d->updateLayout(false); } } void Worksheet::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const WorksheetElement* removedElement = qobject_cast(aspect); if (removedElement) { QGraphicsItem* item = removedElement->graphicsItem(); d->m_scene->removeItem(item); } } void Worksheet::handleAspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child) { Q_UNUSED(parent); Q_UNUSED(before); Q_UNUSED(child); if (d->layout != Worksheet::NoLayout) d->updateLayout(false); } QGraphicsScene* Worksheet::scene() const { return d->m_scene; } QRectF Worksheet::pageRect() const { return d->m_scene->sceneRect(); } /*! this slot is called when a worksheet element is selected in the project explorer. emits \c itemSelected() which forwards this event to the \c WorksheetView in order to select the corresponding \c QGraphicsItem. */ void Worksheet::childSelected(const AbstractAspect* aspect) { WorksheetElement* element=qobject_cast(const_cast(aspect)); if (element) emit itemSelected(element->graphicsItem()); } /*! this slot is called when a worksheet element is deselected in the project explorer. emits \c itemDeselected() which forwards this event to \c WorksheetView in order to deselect the corresponding \c QGraphicsItem. */ void Worksheet::childDeselected(const AbstractAspect* aspect) { WorksheetElement* element=qobject_cast(const_cast(aspect)); if (element) emit itemDeselected(element->graphicsItem()); } /*! * Emits the signal to select or to deselect the aspect corresponding to \c QGraphicsItem \c item in the project explorer, * if \c selected=true or \c selected=false, respectively. * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. * This function is called in \c WorksheetView upon selection changes. */ void Worksheet::setItemSelectedInView(const QGraphicsItem* item, const bool b) { //determine the corresponding aspect const AbstractAspect* aspect(nullptr); for (const auto* child : children(IncludeHidden) ) { aspect = this->aspectFromGraphicsItem(child, item); if (aspect) break; } if (!aspect) return; //forward selection/deselection to AbstractTreeModel if (b) emit childAspectSelectedInView(aspect); else emit childAspectDeselectedInView(aspect); } /*! * helper function: checks whether \c aspect or one of its children has the \c GraphicsItem \c item * Returns a pointer to \c WorksheetElement having this item. */ WorksheetElement* Worksheet::aspectFromGraphicsItem(const WorksheetElement* aspect, const QGraphicsItem* item) const { if ( aspect->graphicsItem() == item ) return const_cast(aspect); else { for (const auto* child : aspect->children(AbstractAspect::IncludeHidden) ) { WorksheetElement* a = this->aspectFromGraphicsItem(child, item); if (a) return a; } return nullptr; } } /*! Selects or deselects the worksheet in the project explorer. This function is called in \c WorksheetView. The worksheet gets deselected if there are selected items in the view, and selected if there are no selected items in the view. */ void Worksheet::setSelectedInView(const bool b) { if (b) emit childAspectSelectedInView(this); else emit childAspectDeselectedInView(this); } void Worksheet::deleteAspectFromGraphicsItem(const QGraphicsItem* item) { Q_ASSERT(item); //determine the corresponding aspect AbstractAspect* aspect(nullptr); for (const auto* child : children(IncludeHidden) ) { aspect = this->aspectFromGraphicsItem(child, item); if (aspect) break; } if (!aspect) return; if (aspect->parentAspect()) aspect->parentAspect()->removeChild(aspect); else this->removeChild(aspect); } void Worksheet::setIsClosing() { if (m_view) m_view->setIsClosing(); } void Worksheet::update() { emit requestUpdate(); } void Worksheet::setSuppressLayoutUpdate(bool value) { d->suppressLayoutUpdate = value; } void Worksheet::updateLayout() { d->updateLayout(); } /* =============================== getter methods for general options ==================================== */ BASIC_D_READER_IMPL(Worksheet, bool, scaleContent, scaleContent) BASIC_D_READER_IMPL(Worksheet, bool, useViewSize, useViewSize) /* =============================== getter methods for background options ================================= */ BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundType, backgroundType, backgroundType) BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundColorStyle, backgroundColorStyle, backgroundColorStyle) BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundImageStyle, backgroundImageStyle, backgroundImageStyle) BASIC_D_READER_IMPL(Worksheet, Qt::BrushStyle, backgroundBrushStyle, backgroundBrushStyle) CLASS_D_READER_IMPL(Worksheet, QColor, backgroundFirstColor, backgroundFirstColor) CLASS_D_READER_IMPL(Worksheet, QColor, backgroundSecondColor, backgroundSecondColor) CLASS_D_READER_IMPL(Worksheet, QString, backgroundFileName, backgroundFileName) BASIC_D_READER_IMPL(Worksheet, float, backgroundOpacity, backgroundOpacity) /* =============================== getter methods for layout options ====================================== */ BASIC_D_READER_IMPL(Worksheet, Worksheet::Layout, layout, layout) BASIC_D_READER_IMPL(Worksheet, float, layoutTopMargin, layoutTopMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutBottomMargin, layoutBottomMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutLeftMargin, layoutLeftMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutRightMargin, layoutRightMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutHorizontalSpacing, layoutHorizontalSpacing) BASIC_D_READER_IMPL(Worksheet, float, layoutVerticalSpacing, layoutVerticalSpacing) BASIC_D_READER_IMPL(Worksheet, int, layoutRowCount, layoutRowCount) BASIC_D_READER_IMPL(Worksheet, int, layoutColumnCount, layoutColumnCount) CLASS_D_READER_IMPL(Worksheet, QString, theme, theme) /* ============================ setter methods and undo commands for general options ===================== */ void Worksheet::setUseViewSize(bool useViewSize) { if (useViewSize != d->useViewSize) { d->useViewSize = useViewSize; emit useViewSizeRequested(); } } STD_SETTER_CMD_IMPL_S(Worksheet, SetScaleContent, bool, scaleContent) void Worksheet::setScaleContent(bool scaleContent) { if (scaleContent != d->scaleContent) - exec(new WorksheetSetScaleContentCmd(d, scaleContent, i18n("%1: change \"rescale the content\" property"))); + exec(new WorksheetSetScaleContentCmd(d, scaleContent, ki18n("%1: change \"rescale the content\" property"))); } /* ============================ setter methods and undo commands for background options ================= */ STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundType, PlotArea::BackgroundType, backgroundType, update) void Worksheet::setBackgroundType(PlotArea::BackgroundType type) { if (type != d->backgroundType) - exec(new WorksheetSetBackgroundTypeCmd(d, type, i18n("%1: background type changed"))); + exec(new WorksheetSetBackgroundTypeCmd(d, type, ki18n("%1: background type changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundColorStyle, PlotArea::BackgroundColorStyle, backgroundColorStyle, update) void Worksheet::setBackgroundColorStyle(PlotArea::BackgroundColorStyle style) { if (style != d->backgroundColorStyle) - exec(new WorksheetSetBackgroundColorStyleCmd(d, style, i18n("%1: background color style changed"))); + exec(new WorksheetSetBackgroundColorStyleCmd(d, style, ki18n("%1: background color style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundImageStyle, PlotArea::BackgroundImageStyle, backgroundImageStyle, update) void Worksheet::setBackgroundImageStyle(PlotArea::BackgroundImageStyle style) { if (style != d->backgroundImageStyle) - exec(new WorksheetSetBackgroundImageStyleCmd(d, style, i18n("%1: background image style changed"))); + exec(new WorksheetSetBackgroundImageStyleCmd(d, style, ki18n("%1: background image style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundBrushStyle, Qt::BrushStyle, backgroundBrushStyle, update) void Worksheet::setBackgroundBrushStyle(Qt::BrushStyle style) { if (style != d->backgroundBrushStyle) - exec(new WorksheetSetBackgroundBrushStyleCmd(d, style, i18n("%1: background brush style changed"))); + exec(new WorksheetSetBackgroundBrushStyleCmd(d, style, ki18n("%1: background brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundFirstColor, QColor, backgroundFirstColor, update) void Worksheet::setBackgroundFirstColor(const QColor &color) { if (color!= d->backgroundFirstColor) - exec(new WorksheetSetBackgroundFirstColorCmd(d, color, i18n("%1: set background first color"))); + exec(new WorksheetSetBackgroundFirstColorCmd(d, color, ki18n("%1: set background first color"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundSecondColor, QColor, backgroundSecondColor, update) void Worksheet::setBackgroundSecondColor(const QColor &color) { if (color!= d->backgroundSecondColor) - exec(new WorksheetSetBackgroundSecondColorCmd(d, color, i18n("%1: set background second color"))); + exec(new WorksheetSetBackgroundSecondColorCmd(d, color, ki18n("%1: set background second color"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundFileName, QString, backgroundFileName, update) void Worksheet::setBackgroundFileName(const QString& fileName) { if (fileName!= d->backgroundFileName) - exec(new WorksheetSetBackgroundFileNameCmd(d, fileName, i18n("%1: set background image"))); + exec(new WorksheetSetBackgroundFileNameCmd(d, fileName, ki18n("%1: set background image"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundOpacity, float, backgroundOpacity, update) void Worksheet::setBackgroundOpacity(float opacity) { if (opacity != d->backgroundOpacity) - exec(new WorksheetSetBackgroundOpacityCmd(d, opacity, i18n("%1: set opacity"))); + exec(new WorksheetSetBackgroundOpacityCmd(d, opacity, ki18n("%1: set opacity"))); } /* ============================ setter methods and undo commands for layout options ================= */ STD_SETTER_CMD_IMPL_F_S(Worksheet, SetLayout, Worksheet::Layout, layout, updateLayout) void Worksheet::setLayout(Worksheet::Layout layout) { if (layout != d->layout) { beginMacro(i18n("%1: set layout", name())); - exec(new WorksheetSetLayoutCmd(d, layout, i18n("%1: set layout"))); + exec(new WorksheetSetLayoutCmd(d, layout, ki18n("%1: set layout"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutTopMargin, float, layoutTopMargin, updateLayout) void Worksheet::setLayoutTopMargin(float margin) { if (margin != d->layoutTopMargin) { beginMacro(i18n("%1: set layout top margin", name())); - exec(new WorksheetSetLayoutTopMarginCmd(d, margin, i18n("%1: set layout top margin"))); + exec(new WorksheetSetLayoutTopMarginCmd(d, margin, ki18n("%1: set layout top margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutBottomMargin, float, layoutBottomMargin, updateLayout) void Worksheet::setLayoutBottomMargin(float margin) { if (margin != d->layoutBottomMargin) { beginMacro(i18n("%1: set layout bottom margin", name())); - exec(new WorksheetSetLayoutBottomMarginCmd(d, margin, i18n("%1: set layout bottom margin"))); + exec(new WorksheetSetLayoutBottomMarginCmd(d, margin, ki18n("%1: set layout bottom margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutLeftMargin, float, layoutLeftMargin, updateLayout) void Worksheet::setLayoutLeftMargin(float margin) { if (margin != d->layoutLeftMargin) { beginMacro(i18n("%1: set layout left margin", name())); - exec(new WorksheetSetLayoutLeftMarginCmd(d, margin, i18n("%1: set layout left margin"))); + exec(new WorksheetSetLayoutLeftMarginCmd(d, margin, ki18n("%1: set layout left margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRightMargin, float, layoutRightMargin, updateLayout) void Worksheet::setLayoutRightMargin(float margin) { if (margin != d->layoutRightMargin) { beginMacro(i18n("%1: set layout right margin", name())); - exec(new WorksheetSetLayoutRightMarginCmd(d, margin, i18n("%1: set layout right margin"))); + exec(new WorksheetSetLayoutRightMarginCmd(d, margin, ki18n("%1: set layout right margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutVerticalSpacing, float, layoutVerticalSpacing, updateLayout) void Worksheet::setLayoutVerticalSpacing(float spacing) { if (spacing != d->layoutVerticalSpacing) { beginMacro(i18n("%1: set layout vertical spacing", name())); - exec(new WorksheetSetLayoutVerticalSpacingCmd(d, spacing, i18n("%1: set layout vertical spacing"))); + exec(new WorksheetSetLayoutVerticalSpacingCmd(d, spacing, ki18n("%1: set layout vertical spacing"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutHorizontalSpacing, float, layoutHorizontalSpacing, updateLayout) void Worksheet::setLayoutHorizontalSpacing(float spacing) { if (spacing != d->layoutHorizontalSpacing) { beginMacro(i18n("%1: set layout horizontal spacing", name())); - exec(new WorksheetSetLayoutHorizontalSpacingCmd(d, spacing, i18n("%1: set layout horizontal spacing"))); + exec(new WorksheetSetLayoutHorizontalSpacingCmd(d, spacing, ki18n("%1: set layout horizontal spacing"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRowCount, int, layoutRowCount, updateLayout) void Worksheet::setLayoutRowCount(int count) { if (count != d->layoutRowCount) { beginMacro(i18n("%1: set layout row count", name())); - exec(new WorksheetSetLayoutRowCountCmd(d, count, i18n("%1: set layout row count"))); + exec(new WorksheetSetLayoutRowCountCmd(d, count, ki18n("%1: set layout row count"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutColumnCount, int, layoutColumnCount, updateLayout) void Worksheet::setLayoutColumnCount(int count) { if (count != d->layoutColumnCount) { beginMacro(i18n("%1: set layout column count", name())); - exec(new WorksheetSetLayoutColumnCountCmd(d, count, i18n("%1: set layout column count"))); + exec(new WorksheetSetLayoutColumnCountCmd(d, count, ki18n("%1: set layout column count"))); endMacro(); } } class WorksheetSetPageRectCmd : public StandardMacroSetterCmd { public: - WorksheetSetPageRectCmd(Worksheet::Private* target, QRectF newValue, const QString& description) + WorksheetSetPageRectCmd(Worksheet::Private* target, QRectF newValue, const KLocalizedString& description) : StandardMacroSetterCmd(target, &Worksheet::Private::pageRect, newValue, description) {} void finalize() override { m_target->updatePageRect(); emit m_target->q->pageRectChanged(m_target->*m_field); } void finalizeUndo() override { m_target->m_scene->setSceneRect(m_target->*m_field); emit m_target->q->pageRectChanged(m_target->*m_field); } }; void Worksheet::setPageRect(const QRectF& rect) { //don't allow any rectangulars of width/height equal to zero if (qFuzzyCompare(rect.width(), 0.) || qFuzzyCompare(rect.height(), 0.)) { emit pageRectChanged(d->pageRect); return; } if (rect != d->pageRect) { if (!d->useViewSize) { beginMacro(i18n("%1: set page size", name())); - exec(new WorksheetSetPageRectCmd(d, rect, i18n("%1: set page size"))); + exec(new WorksheetSetPageRectCmd(d, rect, ki18n("%1: set page size"))); endMacro(); } else { d->pageRect = rect; d->updatePageRect(); emit pageRectChanged(d->pageRect); } } } void Worksheet::setPrinting(bool on) const { QVector childElements = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* child : childElements) child->setPrinting(on); } STD_SETTER_CMD_IMPL_S(Worksheet, SetTheme, QString, theme) void Worksheet::setTheme(const QString& theme) { if (theme != d->theme) { if (!theme.isEmpty()) { beginMacro( i18n("%1: load theme %2", name(), theme) ); - exec(new WorksheetSetThemeCmd(d, theme, i18n("%1: set theme"))); + exec(new WorksheetSetThemeCmd(d, theme, ki18n("%1: set theme"))); loadTheme(theme); endMacro(); } else { - exec(new WorksheetSetThemeCmd(d, theme, i18n("%1: disable theming"))); + exec(new WorksheetSetThemeCmd(d, theme, ki18n("%1: disable theming"))); } } } //############################################################################## //###################### Private implementation ############################### //############################################################################## WorksheetPrivate::WorksheetPrivate(Worksheet* owner):q(owner), m_scene(new QGraphicsScene()), scaleContent(false), suppressLayoutUpdate(false) { } QString WorksheetPrivate::name() const { return q->name(); } void WorksheetPrivate::updatePageRect() { QRectF oldRect = m_scene->sceneRect(); m_scene->setSceneRect(pageRect); if (layout != Worksheet::NoLayout) updateLayout(); else { if (scaleContent) { qreal horizontalRatio = pageRect.width() / oldRect.width(); qreal verticalRatio = pageRect.height() / oldRect.height(); QVector childElements = q->children(AbstractAspect::IncludeHidden); if (useViewSize) { //don't make the change of the geometry undoable/redoable if the view size is used. for (auto* elem : childElements) { elem->setUndoAware(false); elem->handleResize(horizontalRatio, verticalRatio, true); elem->setUndoAware(true); } } else { for (auto* child : childElements) child->handleResize(horizontalRatio, verticalRatio, true); } } } } void WorksheetPrivate::update() { q->update(); } WorksheetPrivate::~WorksheetPrivate() { delete m_scene; } void WorksheetPrivate::updateLayout(bool undoable) { if (suppressLayoutUpdate) return; QVector list = q->children(); if (layout==Worksheet::NoLayout) { for(auto* elem : list) elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); return; } float x=layoutLeftMargin; float y=layoutTopMargin; float w, h; int count=list.count(); if (layout == Worksheet::VerticalLayout) { w= m_scene->sceneRect().width() - layoutLeftMargin - layoutRightMargin; h=(m_scene->sceneRect().height()-layoutTopMargin-layoutBottomMargin- (count-1)*layoutVerticalSpacing)/count; for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); y+=h + layoutVerticalSpacing; } } else if (layout == Worksheet::HorizontalLayout) { w=(m_scene->sceneRect().width()-layoutLeftMargin-layoutRightMargin- (count-1)*layoutHorizontalSpacing)/count; h= m_scene->sceneRect().height() - layoutTopMargin-layoutBottomMargin; for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); x+=w + layoutHorizontalSpacing; } } else { //GridLayout //add new rows, if not sufficient if (count>layoutRowCount*layoutColumnCount) { layoutRowCount = floor( (float)count/layoutColumnCount + 0.5); emit q->layoutRowCountChanged(layoutRowCount); } w=(m_scene->sceneRect().width()-layoutLeftMargin-layoutRightMargin- (layoutColumnCount-1)*layoutHorizontalSpacing)/layoutColumnCount; h=(m_scene->sceneRect().height()-layoutTopMargin-layoutBottomMargin- (layoutRowCount-1)*layoutVerticalSpacing)/layoutRowCount; int columnIndex=0; //counts the columns in a row for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); x+=w + layoutHorizontalSpacing; columnIndex++; if (columnIndex==layoutColumnCount) { columnIndex=0; x=layoutLeftMargin; y+=h + layoutVerticalSpacing; } } } } void WorksheetPrivate::setContainerRect(WorksheetElementContainer* elem, float x, float y, float h, float w, bool undoable) { if (useViewSize) { //when using the view size, no need to put rect changes onto the undo-stack elem->setUndoAware(false); elem->setRect(QRectF(x,y,w,h)); elem->setUndoAware(true); } else { //don't put rect changed onto the undo-stack if undoable-flag is set to true, //e.g. when new child is added or removed (the layout and the childrend rects will be updated anyway) if (!undoable) { elem->setUndoAware(false); elem->setRect(QRectF(x,y,w,h)); elem->setUndoAware(true); } else elem->setRect(QRectF(x,y,w,h)); } elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Worksheet::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "worksheet" ); writeBasicAttributes(writer); writeCommentElement(writer); //applied theme if (!d->theme.isEmpty()){ writer->writeStartElement( "theme" ); writer->writeAttribute("name", d->theme); writer->writeEndElement(); } //geometry writer->writeStartElement( "geometry" ); QRectF rect = d->m_scene->sceneRect(); writer->writeAttribute( "x", QString::number(rect.x()) ); writer->writeAttribute( "y", QString::number(rect.y()) ); writer->writeAttribute( "width", QString::number(rect.width()) ); writer->writeAttribute( "height", QString::number(rect.height()) ); writer->writeAttribute( "useViewSize", QString::number(d->useViewSize) ); writer->writeEndElement(); //layout writer->writeStartElement( "layout" ); writer->writeAttribute( "layout", QString::number(d->layout) ); writer->writeAttribute( "topMargin", QString::number(d->layoutTopMargin) ); writer->writeAttribute( "bottomMargin", QString::number(d->layoutBottomMargin) ); writer->writeAttribute( "leftMargin", QString::number(d->layoutLeftMargin) ); writer->writeAttribute( "rightMargin", QString::number(d->layoutRightMargin) ); writer->writeAttribute( "verticalSpacing", QString::number(d->layoutVerticalSpacing) ); writer->writeAttribute( "horizontalSpacing", QString::number(d->layoutHorizontalSpacing) ); writer->writeAttribute( "columnCount", QString::number(d->layoutColumnCount) ); writer->writeAttribute( "rowCount", QString::number(d->layoutRowCount) ); writer->writeEndElement(); //background properties writer->writeStartElement( "background" ); writer->writeAttribute( "type", QString::number(d->backgroundType) ); writer->writeAttribute( "colorStyle", QString::number(d->backgroundColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->backgroundImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->backgroundBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->backgroundFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->backgroundFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->backgroundFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->backgroundSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->backgroundSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->backgroundSecondColor.blue()) ); writer->writeAttribute( "fileName", d->backgroundFileName ); writer->writeAttribute( "opacity", QString::number(d->backgroundOpacity) ); writer->writeEndElement(); //serialize all children for (auto* child : children(IncludeHidden)) child->save(writer); writer->writeEndElement(); // close "worksheet" section } //! Load from XML bool Worksheet::load(XmlStreamReader* reader, bool preview) { if(!reader->isStartElement() || reader->name() != "worksheet") { reader->raiseError(i18n("no worksheet element found")); return false; } if (!readBasicAttributes(reader)) return false; //clear the theme that was potentially set in init() in order to correctly load here the worksheets without any theme used d->theme = ""; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; QRectF rect; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "worksheet") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "theme") { attribs = reader->attributes(); d->theme = attribs.value("name").toString(); } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else rect.setX(str.toDouble()); str = attribs.value("y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else rect.setY(str.toDouble()); str = attribs.value("width").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'width'")); + reader->raiseWarning(attributeWarning.subs("width").toString()); else rect.setWidth(str.toDouble()); str = attribs.value("height").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'height'")); + reader->raiseWarning(attributeWarning.subs("height").toString()); else rect.setHeight(str.toDouble()); str = attribs.value("useViewSize").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'useViewSize'")); + reader->raiseWarning(attributeWarning.subs("useViewSize").toString()); else d->useViewSize = str.toInt(); } else if (!preview && reader->name() == "layout") { attribs = reader->attributes(); str = attribs.value("layout").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("layout")); + reader->raiseWarning(attributeWarning.subs("layout").toString()); else d->layout = Worksheet::Layout(str.toInt()); str = attribs.value("topMargin").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("topMargin")); + reader->raiseWarning(attributeWarning.subs("topMargin").toString()); else d->layoutTopMargin = str.toDouble(); str = attribs.value("bottomMargin").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("bottomMargin")); + reader->raiseWarning(attributeWarning.subs("bottomMargin").toString()); else d->layoutBottomMargin = str.toDouble(); str = attribs.value("leftMargin").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("leftMargin")); + reader->raiseWarning(attributeWarning.subs("leftMargin").toString()); else d->layoutLeftMargin = str.toDouble(); str = attribs.value("rightMargin").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("rightMargin")); + reader->raiseWarning(attributeWarning.subs("rightMargin").toString()); else d->layoutRightMargin = str.toDouble(); str = attribs.value("verticalSpacing").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("verticalSpacing")); + reader->raiseWarning(attributeWarning.subs("verticalSpacing").toString()); else d->layoutVerticalSpacing = str.toDouble(); str = attribs.value("horizontalSpacing").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("horizontalSpacing")); + reader->raiseWarning(attributeWarning.subs("horizontalSpacing").toString()); else d->layoutHorizontalSpacing = str.toDouble(); str = attribs.value("columnCount").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("columnCount")); + reader->raiseWarning(attributeWarning.subs("columnCount").toString()); else d->layoutColumnCount = str.toInt(); str = attribs.value("rowCount").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("rowCount")); + reader->raiseWarning(attributeWarning.subs("rowCount").toString()); else d->layoutRowCount = str.toInt(); } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); str = attribs.value("type").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("type")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->backgroundType = PlotArea::BackgroundType(str.toInt()); str = attribs.value("colorStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("colorStyle")); + reader->raiseWarning(attributeWarning.subs("colorStyle").toString()); else d->backgroundColorStyle = PlotArea::BackgroundColorStyle(str.toInt()); str = attribs.value("imageStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("imageStyle")); + reader->raiseWarning(attributeWarning.subs("imageStyle").toString()); else d->backgroundImageStyle = PlotArea::BackgroundImageStyle(str.toInt()); str = attribs.value("brushStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("brushStyle")); + reader->raiseWarning(attributeWarning.subs("brushStyle").toString()); else d->backgroundBrushStyle = Qt::BrushStyle(str.toInt()); str = attribs.value("firstColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_r")); + reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->backgroundFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_g")); + reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->backgroundFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_b")); + reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->backgroundFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_r")); + reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->backgroundSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_g")); + reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->backgroundSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_b")); + reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->backgroundSecondColor.setBlue(str.toInt()); str = attribs.value("fileName").toString(); d->backgroundFileName = str; str = attribs.value("opacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("opacity")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->backgroundOpacity = str.toDouble(); } else if(reader->name() == "cartesianPlot") { CartesianPlot* plot = new CartesianPlot(""); plot->setIsLoading(true); if (!plot->load(reader, preview)) { delete plot; return false; } else addChildFast(plot); } else if(reader->name() == "textLabel") { TextLabel* label = new TextLabel(""); if (!label->load(reader, preview)) { delete label; return false; } else addChildFast(label); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (!preview) { d->m_scene->setSceneRect(rect); d->updateLayout(); } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void Worksheet::loadTheme(const QString& theme) { KConfig config(ThemeHandler::themeFilePath(theme), KConfig::SimpleConfig); //apply the same background color for Worksheet as for the CartesianPlot const KConfigGroup group = config.group("CartesianPlot"); this->setBackgroundBrushStyle((Qt::BrushStyle)group.readEntry("BackgroundBrushStyle",(int) this->backgroundBrushStyle())); this->setBackgroundColorStyle((PlotArea::BackgroundColorStyle)(group.readEntry("BackgroundColorStyle",(int) this->backgroundColorStyle()))); this->setBackgroundFirstColor(group.readEntry("BackgroundFirstColor",(QColor) this->backgroundFirstColor())); this->setBackgroundImageStyle((PlotArea::BackgroundImageStyle)group.readEntry("BackgroundImageStyle",(int) this->backgroundImageStyle())); this->setBackgroundOpacity(group.readEntry("BackgroundOpacity", this->backgroundOpacity())); this->setBackgroundSecondColor(group.readEntry("BackgroundSecondColor",(QColor) this->backgroundSecondColor())); this->setBackgroundType((PlotArea::BackgroundType)(group.readEntry("BackgroundType",(int) this->backgroundType()))); //load the theme for all the children const QVector& childElements = children(AbstractAspect::IncludeHidden); for (auto* child : childElements) child->loadThemeConfig(config); } diff --git a/src/backend/worksheet/WorksheetElement.cpp b/src/backend/worksheet/WorksheetElement.cpp index bd3efd05e..f99e8ddfe 100644 --- a/src/backend/worksheet/WorksheetElement.cpp +++ b/src/backend/worksheet/WorksheetElement.cpp @@ -1,237 +1,237 @@ /*************************************************************************** File : WorksheetElement.cpp Project : LabPlot Description : Base class for all Worksheet children. -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/WorksheetElement.h" #include "backend/worksheet/plots/AbstractPlot.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include #include #include -#include +#include /** * \class WorksheetElement * \brief Base class for all Worksheet children. * */ WorksheetElement::WorksheetElement(const QString &name) : AbstractAspect(name) { m_drawingOrderMenu = new QMenu(i18n("Drawing &order")); m_moveBehindMenu = new QMenu(i18n("Move &behind")); m_moveInFrontOfMenu = new QMenu(i18n("Move in &front of")); m_drawingOrderMenu->addMenu(m_moveBehindMenu); m_drawingOrderMenu->addMenu(m_moveInFrontOfMenu); connect(m_moveBehindMenu, &QMenu::aboutToShow, this, &WorksheetElement::prepareMoveBehindMenu); connect(m_moveInFrontOfMenu, &QMenu::aboutToShow, this, &WorksheetElement::prepareMoveInFrontOfMenu); connect(m_moveBehindMenu, &QMenu::triggered, this, &WorksheetElement::execMoveBehind); connect(m_moveInFrontOfMenu, &QMenu::triggered, this, &WorksheetElement::execMoveInFrontOf); } WorksheetElement::~WorksheetElement() { delete m_moveBehindMenu; delete m_moveInFrontOfMenu; delete m_drawingOrderMenu; } /** * \fn QGraphicsItem *WorksheetElement::graphicsItem() const * \brief Return the graphics item representing this element. * */ /** * \fn void WorksheetElement::setVisible(bool on) * \brief Show/hide the element. * */ /** * \fn bool WorksheetElement::isVisible() const * \brief Return whether the element is (at least) partially visible. * */ /** * \brief Return whether the element is fully visible (i.e., including all child elements). * * The standard implementation returns isVisible(). */ bool WorksheetElement::isFullyVisible() const { return isVisible(); } /** * \fn void WorksheetElement::setPrinting(bool on) * \brief Switches the printing mode on/off * */ /** * \fn void WorksheetElement::retransform() * \brief Tell the element to newly transform its graphics item into its coordinate system. * * This method must not change the undo-aware data of the element, only * the graphics item which represents the item is to be updated. */ void WorksheetElement::setZValue(qreal value) { graphicsItem()->setZValue(value); } /** This does exactly what Qt internally does to creates a shape from a painter path. */ QPainterPath WorksheetElement::shapeFromPath(const QPainterPath &path, const QPen &pen) { if (path == QPainterPath()) return path; // PERFTRACE("WorksheetElement::shapeFromPath()"); // TODO: We unfortunately need this hack as QPainterPathStroker will set a width of 1.0 // if we pass a value of 0.0 to QPainterPathStroker::setWidth() const qreal penWidthZero = qreal(0.00000001); QPainterPathStroker ps; ps.setCapStyle(pen.capStyle()); if (pen.widthF() <= 0.0) ps.setWidth(penWidthZero); else ps.setWidth(pen.widthF()); ps.setJoinStyle(pen.joinStyle()); ps.setMiterLimit(pen.miterLimit()); QPainterPath p = ps.createStroke(path); p.addPath(path); return p; } QMenu* WorksheetElement::createContextMenu() { QMenu* menu = AbstractAspect::createContextMenu(); //add the sub-menu for the drawing order //don't add the drawing order menu for axes, they're always drawn on top of each other elements if (dynamic_cast(this)) return menu; //don't add the drawing order menu for plots that are placed in a worksheet with an active layout if (dynamic_cast(this) ) { const Worksheet* w = dynamic_cast(this->parentAspect()); if (w && w->layout()!=Worksheet::NoLayout) return menu; } //don't add the drawing order menu if the parent element has no other children int children = 0; for (auto* child : parentAspect()->children()) { if( !dynamic_cast(child) ) children++; } if (children > 1) { menu->addSeparator(); menu->addMenu(m_drawingOrderMenu); } return menu; } void WorksheetElement::prepareMoveBehindMenu() { m_moveBehindMenu->clear(); AbstractAspect* parent = parentAspect(); int index = parent->indexOfChild(this); const QVector& children = parent->children(); for (int i=0; i(elem)) { QAction* action = m_moveBehindMenu->addAction(elem->name()); action->setData(i); } } //TODO: doesn't alway work properly //hide the "move behind" menu if it doesn't have any entries, show if not shown yet otherwise //m_moveBehindMenu->menuAction()->setVisible(!m_moveBehindMenu->isEmpty()); } void WorksheetElement::prepareMoveInFrontOfMenu() { m_moveInFrontOfMenu->clear(); AbstractAspect* parent = parentAspect(); int index = parent->indexOfChild(this); const QVector& children = parent->children(); for (int i = index + 1; i < children.size(); ++i) { const WorksheetElement* elem = children.at(i); //axes are always drawn on top of other elements, don't add them to the menu if (!dynamic_cast(elem)) { QAction* action = m_moveInFrontOfMenu->addAction(elem->name()); action->setData(i); } } //TODO: doesn't alway work properly //hide the "move in front" menu if it doesn't have any entries, show if not shown yet otherwise //m_moveInFrontOfMenu->menuAction()->setVisible(!m_moveInFrontOfMenu->isEmpty()); } void WorksheetElement::execMoveInFrontOf(QAction* action) { AbstractAspect* parent = parentAspect(); int index = action->data().toInt(); AbstractAspect* sibling1 = parent->child(index); AbstractAspect* sibling2 = parent->child(index + 1); beginMacro(i18n("%1: move behind %2.", name(), sibling1->name())); remove(); parent->insertChildBefore(this, sibling2); endMacro(); } void WorksheetElement::execMoveBehind(QAction* action) { AbstractAspect* parent = parentAspect(); int index = action->data().toInt(); AbstractAspect* sibling = parent->child(index); beginMacro(i18n("%1: move in front of %2.", name(), sibling->name())); remove(); parent->insertChildBefore(this, sibling); endMacro(); } void WorksheetElement::loadThemeConfig(const KConfig &) { } void WorksheetElement::saveThemeConfig(const KConfig &) { } diff --git a/src/backend/worksheet/WorksheetElementContainer.cpp b/src/backend/worksheet/WorksheetElementContainer.cpp index 17633b041..40388fb6c 100644 --- a/src/backend/worksheet/WorksheetElementContainer.cpp +++ b/src/backend/worksheet/WorksheetElementContainer.cpp @@ -1,281 +1,281 @@ /*************************************************************************** File : WorksheetElementContainer.cpp Project : LabPlot Description : Worksheet element container - parent of multiple elements -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/worksheet/WorksheetElementContainer.h" #include "backend/worksheet/WorksheetElementContainerPrivate.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include #include #include #include -#include +#include /** * \class WorksheetElementContainer * \brief Worksheet element container - parent of multiple elements * \ingroup worksheet * This class provides the functionality for a containers of multiple * worksheet elements. Such a container can be a plot or group of elements. */ WorksheetElementContainer::WorksheetElementContainer(const QString& name) : WorksheetElement(name), d_ptr(new WorksheetElementContainerPrivate(this)) { connect(this, &WorksheetElementContainer::aspectAdded, this, &WorksheetElementContainer::handleAspectAdded); } WorksheetElementContainer::WorksheetElementContainer(const QString& name, WorksheetElementContainerPrivate* dd) : WorksheetElement(name), d_ptr(dd) { connect(this, &WorksheetElementContainer::aspectAdded, this, &WorksheetElementContainer::handleAspectAdded); } WorksheetElementContainer::~WorksheetElementContainer() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } QGraphicsItem* WorksheetElementContainer::graphicsItem() const { return const_cast(static_cast(d_ptr)); } QRectF WorksheetElementContainer::rect() const { Q_D(const WorksheetElementContainer); return d->rect; } STD_SWAP_METHOD_SETTER_CMD_IMPL(WorksheetElementContainer, SetVisible, bool, swapVisible) void WorksheetElementContainer::setVisible(bool on) { Q_D(WorksheetElementContainer); //take care of proper ordering on the undo-stack, //when making the container and all its children visible/invisible. //if visible is set true, change the visibility of the container first if (on) { beginMacro( i18n("%1: set visible", name()) ); - exec( new WorksheetElementContainerSetVisibleCmd(d, on, i18n("%1: set visible")) ); + exec( new WorksheetElementContainerSetVisibleCmd(d, on, ki18n("%1: set visible")) ); } else { beginMacro( i18n("%1: set invisible", name()) ); } //change the visibility of all children QVector childList = children(AbstractAspect::IncludeHidden | AbstractAspect::Compress); for (auto* elem : childList) elem->setVisible(on); //if visible is set false, change the visibility of the container last if (!on) - exec(new WorksheetElementContainerSetVisibleCmd(d, false, i18n("%1: set invisible"))); + exec(new WorksheetElementContainerSetVisibleCmd(d, false, ki18n("%1: set invisible"))); endMacro(); } bool WorksheetElementContainer::isVisible() const { Q_D(const WorksheetElementContainer); return d->isVisible(); } bool WorksheetElementContainer::isFullyVisible() const { QVector childList = children(AbstractAspect::IncludeHidden | AbstractAspect::Compress); for (const auto* elem : childList) { if (!elem->isVisible()) return false; } return true; } void WorksheetElementContainer::setPrinting(bool on) { Q_D(WorksheetElementContainer); d->m_printing = on; } void WorksheetElementContainer::retransform() { // if (isLoading()) // return; PERFTRACE("WorksheetElementContainer::retransform()"); Q_D(WorksheetElementContainer); QVector childList = children(AbstractAspect::IncludeHidden | AbstractAspect::Compress); for (auto* child : childList) child->retransform(); d->recalcShapeAndBoundingRect(); } void WorksheetElementContainer::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("WorksheetElementContainer::handleResize()"); Q_D(const WorksheetElementContainer); if (pageResize) { QRectF rect(d->rect); rect.setWidth(d->rect.width()*horizontalRatio); rect.setHeight(d->rect.height()*verticalRatio); setRect(rect); } else { //TODO // for (auto* elem : children(IncludeHidden)) // elem->handleResize(horizontalRatio, verticalRatio); // retransform(); } } void WorksheetElementContainer::handleAspectAdded(const AbstractAspect* aspect) { Q_D(WorksheetElementContainer); const WorksheetElement* element = qobject_cast(aspect); if (element && (aspect->parentAspect() == this)) { connect(element, &WorksheetElement::hovered, this, &WorksheetElementContainer::childHovered); connect(element, &WorksheetElement::unhovered, this, &WorksheetElementContainer::childUnhovered); element->graphicsItem()->setParentItem(d); qreal zVal = 0; for (auto* child : children(IncludeHidden)) child->setZValue(zVal++); } if (!isLoading()) d->recalcShapeAndBoundingRect(); } void WorksheetElementContainer::childHovered() { Q_D(WorksheetElementContainer); if (!d->isSelected()) { if (d->m_hovered) d->m_hovered = false; d->update(); } } void WorksheetElementContainer::childUnhovered() { Q_D(WorksheetElementContainer); if (!d->isSelected()) { d->m_hovered = true; d->update(); } } void WorksheetElementContainer::prepareGeometryChange() { Q_D(WorksheetElementContainer); d->prepareGeometryChangeRequested(); } //################################################################ //################### Private implementation ########################## //################################################################ WorksheetElementContainerPrivate::WorksheetElementContainerPrivate(WorksheetElementContainer *owner) : q(owner), m_hovered(false), m_printing(false) { setAcceptHoverEvents(true); } QString WorksheetElementContainerPrivate::name() const { return q->name(); } void WorksheetElementContainerPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { scene()->clearSelection(); setSelected(true); QMenu* menu = q->createContextMenu(); menu->exec(event->screenPos()); } void WorksheetElementContainerPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; update(); } } void WorksheetElementContainerPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; update(); } } bool WorksheetElementContainerPrivate::swapVisible(bool on){ bool oldValue = isVisible(); setVisible(on); emit q->visibleChanged(on); return oldValue; } void WorksheetElementContainerPrivate::prepareGeometryChangeRequested() { prepareGeometryChange(); recalcShapeAndBoundingRect(); } void WorksheetElementContainerPrivate::recalcShapeAndBoundingRect() { // if (q->isLoading()) // return; boundingRectangle = QRectF(); containerShape = QPainterPath(); QVector childList = q->children(AbstractAspect::IncludeHidden | AbstractAspect::Compress); foreach (const WorksheetElement* elem, childList) boundingRectangle |= elem->graphicsItem()->mapRectToParent(elem->graphicsItem()->boundingRect()); float penWidth = 2.; boundingRectangle = QRectF(-boundingRectangle.width()/2 - penWidth / 2, -boundingRectangle.height()/2 - penWidth / 2, boundingRectangle.width() + penWidth, boundingRectangle.height() + penWidth); QPainterPath path; path.addRect(boundingRectangle); //make the shape somewhat thicker then the hoveredPen to make the selection/hovering box more visible containerShape.addPath(WorksheetElement::shapeFromPath(path, QPen(QBrush(), penWidth))); } // Inherited from QGraphicsItem QRectF WorksheetElementContainerPrivate::boundingRect() const { return boundingRectangle; } // Inherited from QGraphicsItem void WorksheetElementContainerPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (!isVisible()) return; if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(containerShape); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(containerShape); } } diff --git a/src/backend/worksheet/plots/AbstractPlot.cpp b/src/backend/worksheet/plots/AbstractPlot.cpp index d7e88cab9..13f90e468 100644 --- a/src/backend/worksheet/plots/AbstractPlot.cpp +++ b/src/backend/worksheet/plots/AbstractPlot.cpp @@ -1,121 +1,121 @@ /*************************************************************************** File : AbstractPlot.cpp Project : LabPlot Description : Base class for plots of different types -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2011-2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/worksheet/plots/AbstractPlot.h" #include "backend/worksheet/plots/AbstractPlotPrivate.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/plots/AbstractCoordinateSystem.h" #include "backend/worksheet/WorksheetElementContainerPrivate.h" #include "backend/lib/commandtemplates.h" -#include +#include /** * \class AbstractPlot * \brief Second level container in a Worksheet for logical grouping * * TODO: decide the exact role of AbstractPlot * */ AbstractPlot::AbstractPlot(const QString &name):WorksheetElementContainer(name, new AbstractPlotPrivate(this)), m_coordinateSystem(0), m_plotArea(0), m_title(0){ init(); } AbstractPlot::AbstractPlot(const QString &name, AbstractPlotPrivate *dd) : WorksheetElementContainer(name, dd), m_coordinateSystem(0), m_plotArea(0), m_title(0){ init(); } void AbstractPlot::init(){ graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); graphicsItem()->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); graphicsItem()->setFlag(QGraphicsItem::ItemIsSelectable, true); graphicsItem()->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); graphicsItem()->setFlag(QGraphicsItem::ItemIsFocusable, true); } PlotArea* AbstractPlot::plotArea(){ return m_plotArea; } AbstractCoordinateSystem* AbstractPlot::coordinateSystem() const{ return m_coordinateSystem; } TextLabel* AbstractPlot::title(){ return m_title; } void AbstractPlot::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("AbstractPlot::handleResize()"); Q_D(AbstractPlot); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); d->horizontalPadding *= ratio; d->verticalPadding *= ratio; WorksheetElementContainer::handleResize(horizontalRatio, verticalRatio, pageResize); } BASIC_SHARED_D_READER_IMPL(AbstractPlot, float, horizontalPadding, horizontalPadding) BASIC_SHARED_D_READER_IMPL(AbstractPlot, float, verticalPadding, verticalPadding) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(AbstractPlot, SetHorizontalPadding, float, horizontalPadding, retransform) void AbstractPlot::setHorizontalPadding(float padding) { Q_D(AbstractPlot); if (padding != d->horizontalPadding) - exec(new AbstractPlotSetHorizontalPaddingCmd(d, padding, i18n("%1: set horizontal padding"))); + exec(new AbstractPlotSetHorizontalPaddingCmd(d, padding, ki18n("%1: set horizontal padding"))); } STD_SETTER_CMD_IMPL_F_S(AbstractPlot, SetVerticalPadding, float, verticalPadding, retransform) void AbstractPlot::setVerticalPadding(float padding) { Q_D(AbstractPlot); if (padding != d->verticalPadding) - exec(new AbstractPlotSetVerticalPaddingCmd(d, padding, i18n("%1: set vertical padding"))); + exec(new AbstractPlotSetVerticalPaddingCmd(d, padding, ki18n("%1: set vertical padding"))); } //################################################################ //################### Private implementation ##################### //################################################################ AbstractPlotPrivate::AbstractPlotPrivate(AbstractPlot *owner) :WorksheetElementContainerPrivate(owner){ } QString AbstractPlotPrivate::name() const{ return q->name(); } diff --git a/src/backend/worksheet/plots/PlotArea.cpp b/src/backend/worksheet/plots/PlotArea.cpp index 62045f480..4d94862ee 100644 --- a/src/backend/worksheet/plots/PlotArea.cpp +++ b/src/backend/worksheet/plots/PlotArea.cpp @@ -1,587 +1,587 @@ /*************************************************************************** File : PlotArea.cpp Project : LabPlot Description : Plot area (for background filling and clipping). -------------------------------------------------------------------- Copyright : (C) 2011-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/plots/PlotAreaPrivate.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" #include #include #include -#include +#include /** * \class PlotArea * \brief Plot area (for background filling and clipping). * * \ingroup worksheet */ PlotArea::PlotArea(const QString &name):WorksheetElement(name), d_ptr(new PlotAreaPrivate(this)) { init(); } PlotArea::PlotArea(const QString &name, PlotAreaPrivate *dd) : WorksheetElement(name), d_ptr(dd) { init(); } PlotArea::~PlotArea() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void PlotArea::init() { Q_D(PlotArea); setHidden(true);//we don't show PlotArea aspect in the model view. d->rect = QRectF(0, 0, 1, 1); d->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); KConfig config; KConfigGroup group = config.group("PlotArea"); //Background d->backgroundType = (PlotArea::BackgroundType) group.readEntry("BackgroundType", (int)PlotArea::Color); d->backgroundColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("BackgroundColorStyle", (int) PlotArea::SingleColor); d->backgroundImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("BackgroundImageStyle", (int) PlotArea::Scaled); d->backgroundBrushStyle = (Qt::BrushStyle) group.readEntry("BackgroundBrushStyle", (int) Qt::SolidPattern); d->backgroundFileName = group.readEntry("BackgroundFileName", QString()); d->backgroundFirstColor = group.readEntry("BackgroundFirstColor", QColor(Qt::white)); d->backgroundSecondColor = group.readEntry("BackgroundSecondColor", QColor(Qt::black)); d->backgroundOpacity = group.readEntry("BackgroundOpacity", 1.0); //Border d->borderPen = QPen(group.readEntry("BorderColor", QColor(Qt::black)), group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)), (Qt::PenStyle) group.readEntry("BorderStyle", (int)Qt::SolidLine)); d->borderCornerRadius = group.readEntry("BorderCornerRadius", 0.0); d->borderOpacity = group.readEntry("BorderOpacity", 1.0); } QGraphicsItem *PlotArea::graphicsItem() const { return d_ptr; } STD_SWAP_METHOD_SETTER_CMD_IMPL(PlotArea, SetVisible, bool, swapVisible) void PlotArea::setVisible(bool on) { Q_D(PlotArea); - exec(new PlotAreaSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new PlotAreaSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool PlotArea::isVisible() const { Q_D(const PlotArea); return d->isVisible(); } void PlotArea::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("PlotArea::handleResize()"); Q_D(PlotArea); Q_UNUSED(pageResize); d->rect.setWidth(d->rect.width()*horizontalRatio); d->rect.setHeight(d->rect.height()*verticalRatio); // TODO: scale line width } void PlotArea::retransform() { } /* ============================ getter methods ================= */ BASIC_SHARED_D_READER_IMPL(PlotArea, bool, clippingEnabled, clippingEnabled()) CLASS_SHARED_D_READER_IMPL(PlotArea, QRectF, rect, rect) BASIC_SHARED_D_READER_IMPL(PlotArea, PlotArea::BackgroundType, backgroundType, backgroundType) BASIC_SHARED_D_READER_IMPL(PlotArea, PlotArea::BackgroundColorStyle, backgroundColorStyle, backgroundColorStyle) BASIC_SHARED_D_READER_IMPL(PlotArea, PlotArea::BackgroundImageStyle, backgroundImageStyle, backgroundImageStyle) CLASS_SHARED_D_READER_IMPL(PlotArea, Qt::BrushStyle, backgroundBrushStyle, backgroundBrushStyle) CLASS_SHARED_D_READER_IMPL(PlotArea, QColor, backgroundFirstColor, backgroundFirstColor) CLASS_SHARED_D_READER_IMPL(PlotArea, QColor, backgroundSecondColor, backgroundSecondColor) CLASS_SHARED_D_READER_IMPL(PlotArea, QString, backgroundFileName, backgroundFileName) BASIC_SHARED_D_READER_IMPL(PlotArea, qreal, backgroundOpacity, backgroundOpacity) CLASS_SHARED_D_READER_IMPL(PlotArea, QPen, borderPen, borderPen) BASIC_SHARED_D_READER_IMPL(PlotArea, qreal, borderCornerRadius, borderCornerRadius) BASIC_SHARED_D_READER_IMPL(PlotArea, qreal, borderOpacity, borderOpacity) /* ============================ setter methods and undo commands ================= */ STD_SWAP_METHOD_SETTER_CMD_IMPL(PlotArea, SetClippingEnabled, bool, toggleClipping); void PlotArea::setClippingEnabled(bool on) { Q_D(PlotArea); if (d->clippingEnabled() != on) - exec(new PlotAreaSetClippingEnabledCmd(d, on, i18n("%1: toggle clipping"))); + exec(new PlotAreaSetClippingEnabledCmd(d, on, ki18n("%1: toggle clipping"))); } /*! * sets plot area rect in scene coordinates. */ void PlotArea::setRect(const QRectF &newRect) { Q_D(PlotArea); d->setRect(newRect); } //Background STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundType, PlotArea::BackgroundType, backgroundType, update) void PlotArea::setBackgroundType(BackgroundType type) { Q_D(PlotArea); if (type != d->backgroundType) - exec(new PlotAreaSetBackgroundTypeCmd(d, type, i18n("%1: background type changed"))); + exec(new PlotAreaSetBackgroundTypeCmd(d, type, ki18n("%1: background type changed"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundColorStyle, PlotArea::BackgroundColorStyle, backgroundColorStyle, update) void PlotArea::setBackgroundColorStyle(BackgroundColorStyle style) { Q_D(PlotArea); if (style != d->backgroundColorStyle) - exec(new PlotAreaSetBackgroundColorStyleCmd(d, style, i18n("%1: background color style changed"))); + exec(new PlotAreaSetBackgroundColorStyleCmd(d, style, ki18n("%1: background color style changed"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundImageStyle, PlotArea::BackgroundImageStyle, backgroundImageStyle, update) void PlotArea::setBackgroundImageStyle(PlotArea::BackgroundImageStyle style) { Q_D(PlotArea); if (style != d->backgroundImageStyle) - exec(new PlotAreaSetBackgroundImageStyleCmd(d, style, i18n("%1: background image style changed"))); + exec(new PlotAreaSetBackgroundImageStyleCmd(d, style, ki18n("%1: background image style changed"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundBrushStyle, Qt::BrushStyle, backgroundBrushStyle, update) void PlotArea::setBackgroundBrushStyle(Qt::BrushStyle style) { Q_D(PlotArea); if (style != d->backgroundBrushStyle) - exec(new PlotAreaSetBackgroundBrushStyleCmd(d, style, i18n("%1: background brush style changed"))); + exec(new PlotAreaSetBackgroundBrushStyleCmd(d, style, ki18n("%1: background brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundFirstColor, QColor, backgroundFirstColor, update) void PlotArea::setBackgroundFirstColor(const QColor &color) { Q_D(PlotArea); if (color != d->backgroundFirstColor) - exec(new PlotAreaSetBackgroundFirstColorCmd(d, color, i18n("%1: set background first color"))); + exec(new PlotAreaSetBackgroundFirstColorCmd(d, color, ki18n("%1: set background first color"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundSecondColor, QColor, backgroundSecondColor, update) void PlotArea::setBackgroundSecondColor(const QColor &color) { Q_D(PlotArea); if (color != d->backgroundSecondColor) - exec(new PlotAreaSetBackgroundSecondColorCmd(d, color, i18n("%1: set background second color"))); + exec(new PlotAreaSetBackgroundSecondColorCmd(d, color, ki18n("%1: set background second color"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundFileName, QString, backgroundFileName, update) void PlotArea::setBackgroundFileName(const QString& fileName) { Q_D(PlotArea); if (fileName != d->backgroundFileName) - exec(new PlotAreaSetBackgroundFileNameCmd(d, fileName, i18n("%1: set background image"))); + exec(new PlotAreaSetBackgroundFileNameCmd(d, fileName, ki18n("%1: set background image"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBackgroundOpacity, qreal, backgroundOpacity, update) void PlotArea::setBackgroundOpacity(qreal opacity) { Q_D(PlotArea); if (opacity != d->backgroundOpacity) - exec(new PlotAreaSetBackgroundOpacityCmd(d, opacity, i18n("%1: set plot area opacity"))); + exec(new PlotAreaSetBackgroundOpacityCmd(d, opacity, ki18n("%1: set plot area opacity"))); } //Border STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBorderPen, QPen, borderPen, update) void PlotArea::setBorderPen(const QPen &pen) { Q_D(PlotArea); if (pen != d->borderPen) - exec(new PlotAreaSetBorderPenCmd(d, pen, i18n("%1: set plot area border"))); + exec(new PlotAreaSetBorderPenCmd(d, pen, ki18n("%1: set plot area border"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBorderCornerRadius, qreal, borderCornerRadius, update) void PlotArea::setBorderCornerRadius(qreal radius) { Q_D(PlotArea); if (radius != d->borderCornerRadius) - exec(new PlotAreaSetBorderCornerRadiusCmd(d, radius, i18n("%1: set plot area corner radius"))); + exec(new PlotAreaSetBorderCornerRadiusCmd(d, radius, ki18n("%1: set plot area corner radius"))); } STD_SETTER_CMD_IMPL_F_S(PlotArea, SetBorderOpacity, qreal, borderOpacity, update) void PlotArea::setBorderOpacity(qreal opacity) { Q_D(PlotArea); if (opacity != d->borderOpacity) - exec(new PlotAreaSetBorderOpacityCmd(d, opacity, i18n("%1: set plot area border opacity"))); + exec(new PlotAreaSetBorderOpacityCmd(d, opacity, ki18n("%1: set plot area border opacity"))); } //##################################################################### //################### Private implementation ########################## //##################################################################### PlotAreaPrivate::PlotAreaPrivate(PlotArea *owner):q(owner) { } QString PlotAreaPrivate::name() const { return q->name(); } bool PlotAreaPrivate::clippingEnabled() const { return (flags() & QGraphicsItem::ItemClipsChildrenToShape); } bool PlotAreaPrivate::toggleClipping(bool on) { bool oldValue = clippingEnabled(); setFlag(QGraphicsItem::ItemClipsChildrenToShape, on); return oldValue; } bool PlotAreaPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); return oldValue; } void PlotAreaPrivate::setRect(const QRectF& r) { prepareGeometryChange(); rect = mapRectFromScene(r); } QRectF PlotAreaPrivate::boundingRect () const { float width = rect.width(); float height = rect.height(); float penWidth = borderPen.width(); return QRectF(-width/2 - penWidth/2, -height/2 - penWidth/2, width + penWidth, height + penWidth); } QPainterPath PlotAreaPrivate::shape() const { QPainterPath path; if (qFuzzyIsNull(borderCornerRadius)) path.addRect(rect); else path.addRoundedRect(rect, borderCornerRadius, borderCornerRadius); return path; } void PlotAreaPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { // DEBUG("PlotAreaPrivate::paint()"); Q_UNUSED(option) Q_UNUSED(widget) if (!isVisible()) return; //draw the area painter->setOpacity(backgroundOpacity); painter->setPen(Qt::NoPen); if (backgroundType == PlotArea::Color) { switch (backgroundColorStyle) { case PlotArea::SingleColor: { painter->setBrush(QBrush(backgroundFirstColor)); break; } case PlotArea::HorizontalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.topRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient: { QRadialGradient radialGrad(rect.center(), rect.width()/2); radialGrad.setColorAt(0, backgroundFirstColor); radialGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(radialGrad)); break; } } } else if (backgroundType == PlotArea::Image) { if ( !backgroundFileName.trimmed().isEmpty() ) { QPixmap pix(backgroundFileName); switch (backgroundImageStyle) { case PlotArea::ScaledCropped: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatioByExpanding,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); break; case PlotArea::Scaled: pix = pix.scaled(rect.size().toSize(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatio,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); break; case PlotArea::Centered: painter->drawPixmap(QPointF(rect.center().x()-pix.size().width()/2,rect.center().y()-pix.size().height()/2),pix); break; case PlotArea::Tiled: painter->setBrush(QBrush(pix)); painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); break; case PlotArea::CenterTiled: painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); } } } else if (backgroundType == PlotArea::Pattern) { painter->setBrush(QBrush(backgroundFirstColor,backgroundBrushStyle)); } if ( qFuzzyIsNull(borderCornerRadius) ) painter->drawRect(rect); else painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); //draw the border if (borderPen.style() != Qt::NoPen) { painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); painter->setOpacity(borderOpacity); if ( qFuzzyIsNull(borderCornerRadius) ) painter->drawRect(rect); else painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); } // DEBUG("PlotAreaPrivate::paint() DONE"); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void PlotArea::save(QXmlStreamWriter* writer) const { Q_D(const PlotArea); writer->writeStartElement( "plotArea" ); writeBasicAttributes(writer); writeCommentElement(writer); writer->writeStartElement( "background" ); writer->writeAttribute( "type", QString::number(d->backgroundType) ); writer->writeAttribute( "colorStyle", QString::number(d->backgroundColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->backgroundImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->backgroundBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->backgroundFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->backgroundFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->backgroundFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->backgroundSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->backgroundSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->backgroundSecondColor.blue()) ); writer->writeAttribute( "fileName", d->backgroundFileName ); writer->writeAttribute( "opacity", QString::number(d->backgroundOpacity) ); writer->writeEndElement(); //border writer->writeStartElement( "border" ); WRITE_QPEN(d->borderPen); writer->writeAttribute( "borderOpacity", QString::number(d->borderOpacity) ); writer->writeAttribute( "borderCornerRadius", QString::number(d->borderCornerRadius) ); writer->writeEndElement(); writer->writeEndElement(); } //! Load from XML bool PlotArea::load(XmlStreamReader* reader, bool preview) { Q_D(PlotArea); if(!reader->isStartElement() || reader->name() != "plotArea") { reader->raiseError(i18n("no plot area element found")); return false; } if ( !readBasicAttributes(reader) ) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while ( !reader->atEnd() ) { reader->readNext(); if (reader->isEndElement() && reader->name() == "plotArea") break; if ( !reader->isStartElement() ) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); str = attribs.value("type").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("type")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->backgroundType = PlotArea::BackgroundType(str.toInt()); str = attribs.value("colorStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("colorStyle")); + reader->raiseWarning(attributeWarning.subs("colorStyle").toString()); else d->backgroundColorStyle = PlotArea::BackgroundColorStyle(str.toInt()); str = attribs.value("imageStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("imageStyle")); + reader->raiseWarning(attributeWarning.subs("imageStyle").toString()); else d->backgroundImageStyle = PlotArea::BackgroundImageStyle(str.toInt()); str = attribs.value("brushStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("brushStyle")); + reader->raiseWarning(attributeWarning.subs("brushStyle").toString()); else d->backgroundBrushStyle = Qt::BrushStyle(str.toInt()); str = attribs.value("firstColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_r")); + reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->backgroundFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_g")); + reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->backgroundFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_b")); + reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->backgroundFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_r")); + reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->backgroundSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_g")); + reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->backgroundSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_b")); + reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->backgroundSecondColor.setBlue(str.toInt()); str = attribs.value("fileName").toString(); d->backgroundFileName = str; str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("opacity")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->backgroundOpacity = str.toDouble(); } else if (!preview && reader->name() == "border") { attribs = reader->attributes(); READ_QPEN(d->borderPen); str = attribs.value("borderOpacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("borderOpacity")); + reader->raiseWarning(attributeWarning.subs("borderOpacity").toString()); else d->borderOpacity = str.toDouble(); str = attribs.value("borderCornerRadius").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("borderCornerRadius")); + reader->raiseWarning(attributeWarning.subs("borderCornerRadius").toString()); else d->borderCornerRadius = str.toDouble(); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } void PlotArea::loadThemeConfig(const KConfig& config) { const KConfigGroup group = config.group("CartesianPlot"); this->setBackgroundBrushStyle((Qt::BrushStyle)group.readEntry("BackgroundBrushStyle",(int) this->backgroundBrushStyle())); this->setBackgroundColorStyle((PlotArea::BackgroundColorStyle)(group.readEntry("BackgroundColorStyle",(int) this->backgroundColorStyle()))); this->setBackgroundFirstColor(group.readEntry("BackgroundFirstColor",(QColor) this->backgroundFirstColor())); this->setBackgroundImageStyle((PlotArea::BackgroundImageStyle)group.readEntry("BackgroundImageStyle",(int) this->backgroundImageStyle())); this->setBackgroundOpacity(group.readEntry("BackgroundOpacity", this->backgroundOpacity())); this->setBackgroundSecondColor(group.readEntry("BackgroundSecondColor",(QColor) this->backgroundSecondColor())); this->setBackgroundType((PlotArea::BackgroundType)(group.readEntry("BackgroundType",(int) this->backgroundType()))); this->borderPen().setColor(group.readEntry("BorderColor",(QColor) this->borderPen().color())); this->setBorderCornerRadius(group.readEntry("BorderCornerRadius", this->borderCornerRadius())); this->setBorderOpacity(group.readEntry("BorderOpacity", this->borderOpacity())); this->borderPen().setStyle((Qt::PenStyle)(group.readEntry("BorderStyle", (int) this->borderPen().style()))); this->borderPen().setWidthF(group.readEntry("BorderWidth", this->borderPen().widthF())); } void PlotArea::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("CartesianPlot"); group.writeEntry("BackgroundBrushStyle",(int) this->backgroundBrushStyle()); group.writeEntry("BackgroundColorStyle",(int) this->backgroundColorStyle()); group.writeEntry("BackgroundFirstColor",(QColor) this->backgroundFirstColor()); group.writeEntry("BackgroundImageStyle",(int) this->backgroundImageStyle()); group.writeEntry("BackgroundOpacity", this->backgroundOpacity()); group.writeEntry("BackgroundSecondColor",(QColor) this->backgroundSecondColor()); group.writeEntry("BackgroundType",(int) this->backgroundType()); group.writeEntry("BorderColor",(QColor) this->borderPen().color()); group.writeEntry("BorderCornerRadius", this->borderCornerRadius()); group.writeEntry("BorderOpacity", this->borderOpacity()); group.writeEntry("BorderStyle", (int) this->borderPen().style()); group.writeEntry("BorderWidth", this->borderPen().widthF()); } diff --git a/src/backend/worksheet/plots/cartesian/Axis.cpp b/src/backend/worksheet/plots/cartesian/Axis.cpp index 190f7bff4..2cf9314f5 100644 --- a/src/backend/worksheet/plots/cartesian/Axis.cpp +++ b/src/backend/worksheet/plots/cartesian/Axis.cpp @@ -1,2372 +1,2373 @@ /*************************************************************************** File : Axis.cpp Project : LabPlot Description : Axis for cartesian coordinate systems. -------------------------------------------------------------------- Copyright : (C) 2011-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2013-2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/AxisPrivate.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/TextLabel.h" #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/core/AbstractColumn.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" // #include "backend/lib/trace.h" #include "kdefrontend/GuiTools.h" #include #include #include #include +#include #include -#include +#include extern "C" { #include "backend/nsl/nsl_math.h" } /** * \class AxisGrid * \brief Helper class to get the axis grid drawn with the z-Value=0. * * The painting of the grid lines is separated from the painting of the axis itself. * This allows to use a different z-values for the grid lines (z=0, drawn below all other objects ) * and for the axis (z=FLT_MAX, drawn on top of all other objects) * * \ingroup worksheet */ class AxisGrid : public QGraphicsItem { public: AxisGrid(AxisPrivate* a) { axis = a; setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsFocusable, false); setAcceptHoverEvents(false); } QRectF boundingRect() const override { QPainterPath gridShape; gridShape.addPath(WorksheetElement::shapeFromPath(axis->majorGridPath, axis->majorGridPen)); gridShape.addPath(WorksheetElement::shapeFromPath(axis->minorGridPath, axis->minorGridPen)); QRectF boundingRectangle = gridShape.boundingRect(); return boundingRectangle; } void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override { Q_UNUSED(option) Q_UNUSED(widget) if (!axis->isVisible()) return; if (axis->linePath.isEmpty()) return; //draw major grid if (axis->majorGridPen.style() != Qt::NoPen) { painter->setOpacity(axis->majorGridOpacity); painter->setPen(axis->majorGridPen); painter->setBrush(Qt::NoBrush); painter->drawPath(axis->majorGridPath); } //draw minor grid if (axis->minorGridPen.style() != Qt::NoPen) { painter->setOpacity(axis->minorGridOpacity); painter->setPen(axis->minorGridPen); painter->setBrush(Qt::NoBrush); painter->drawPath(axis->minorGridPath); } } private: AxisPrivate* axis; }; /** * \class Axis * \brief Axis for cartesian coordinate systems. * * \ingroup worksheet */ Axis::Axis(const QString& name, AxisOrientation orientation) : WorksheetElement(name), d_ptr(new AxisPrivate(this)), m_menusInitialized(false) { d_ptr->orientation = orientation; init(); } Axis::Axis(const QString& name, AxisOrientation orientation, AxisPrivate* dd) : WorksheetElement(name), d_ptr(dd), m_menusInitialized(false) { d_ptr->orientation = orientation; init(); } void Axis::finalizeAdd() { Q_D(Axis); d->plot = dynamic_cast(parentAspect()); Q_ASSERT(d->plot); d->cSystem = dynamic_cast(d->plot->coordinateSystem()); } void Axis::init() { Q_D(Axis); KConfig config; KConfigGroup group = config.group( "Axis" ); d->autoScale = true; d->position = Axis::AxisCustom; d->offset = group.readEntry("PositionOffset", 0); d->scale = (Axis::AxisScale) group.readEntry("Scale", (int) Axis::ScaleLinear); d->autoScale = group.readEntry("AutoScale", true); d->start = group.readEntry("Start", 0); d->end = group.readEntry("End", 10); d->zeroOffset = group.readEntry("ZeroOffset", 0); d->scalingFactor = group.readEntry("ScalingFactor", 1.0); d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int) Qt::SolidLine) ); d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->lineOpacity = group.readEntry("LineOpacity", 1.0); d->arrowType = (Axis::ArrowType) group.readEntry("ArrowType", (int)Axis::NoArrow); d->arrowPosition = (Axis::ArrowPosition) group.readEntry("ArrowPosition", (int)Axis::ArrowRight); d->arrowSize = group.readEntry("ArrowSize", Worksheet::convertToSceneUnits(10, Worksheet::Point)); // axis title d->title = new TextLabel(this->name(), TextLabel::AxisTitle); connect( d->title, &TextLabel::changed, this, &Axis::labelChanged); addChild(d->title); d->title->setHidden(true); d->title->graphicsItem()->setParentItem(graphicsItem()); d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); d->title->graphicsItem()->setAcceptHoverEvents(false); d->title->setText(this->name()); if (d->orientation == AxisVertical) d->title->setRotationAngle(90); d->titleOffsetX = Worksheet::convertToSceneUnits(2, Worksheet::Point); //distance to the axis tick labels d->titleOffsetY = Worksheet::convertToSceneUnits(2, Worksheet::Point); //distance to the axis tick labels d->majorTicksDirection = (Axis::TicksDirection) group.readEntry("MajorTicksDirection", (int) Axis::ticksOut); d->majorTicksType = (Axis::TicksType) group.readEntry("MajorTicksType", (int) Axis::TicksTotalNumber); d->majorTicksNumber = group.readEntry("MajorTicksNumber", 11); d->majorTicksIncrement = group.readEntry("MajorTicksIncrement", 1.0); d->majorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MajorTicksLineStyle", (int)Qt::SolidLine) ); d->majorTicksPen.setColor( group.readEntry("MajorTicksColor", QColor(Qt::black) ) ); d->majorTicksPen.setWidthF( group.readEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point) ) ); d->majorTicksLength = group.readEntry("MajorTicksLength", Worksheet::convertToSceneUnits(6.0, Worksheet::Point)); d->majorTicksOpacity = group.readEntry("MajorTicksOpacity", 1.0); d->minorTicksDirection = (Axis::TicksDirection) group.readEntry("MinorTicksDirection", (int) Axis::ticksOut); d->minorTicksType = (Axis::TicksType) group.readEntry("MinorTicksType", (int) Axis::TicksTotalNumber); d->minorTicksNumber = group.readEntry("MinorTicksNumber", 1); d->minorTicksIncrement = group.readEntry("MinorTicksIncrement", 0.5); d->minorTicksPen.setStyle((Qt::PenStyle) group.readEntry("MinorTicksLineStyle", (int)Qt::SolidLine) ); d->minorTicksPen.setColor( group.readEntry("MinorTicksColor", QColor(Qt::black) ) ); d->minorTicksPen.setWidthF( group.readEntry("MinorTicksWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point) ) ); d->minorTicksLength = group.readEntry("MinorTicksLength", Worksheet::convertToSceneUnits(3.0, Worksheet::Point)); d->minorTicksOpacity = group.readEntry("MinorTicksOpacity", 1.0); //Labels d->labelsFormat = (Axis::LabelsFormat) group.readEntry("LabelsFormat", (int)Axis::FormatDecimal); d->labelsAutoPrecision = group.readEntry("LabelsAutoPrecision", true); d->labelsPrecision = group.readEntry("LabelsPrecision", 1); d->labelsPosition = (Axis::LabelsPosition) group.readEntry("LabelsPosition", (int) Axis::LabelsOut); d->labelsOffset= group.readEntry("LabelsOffset", Worksheet::convertToSceneUnits( 5.0, Worksheet::Point )); d->labelsRotationAngle = group.readEntry("LabelsRotation", 0); d->labelsFont = group.readEntry("LabelsFont", QFont()); d->labelsFont.setPixelSize( Worksheet::convertToSceneUnits( 10.0, Worksheet::Point ) ); d->labelsColor = group.readEntry("LabelsFontColor", QColor(Qt::black)); d->labelsPrefix = group.readEntry("LabelsPrefix", "" ); d->labelsSuffix = group.readEntry("LabelsSuffix", "" ); d->labelsOpacity = group.readEntry("LabelsOpacity", 1.0); //major grid d->majorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MajorGridStyle", (int) Qt::NoPen) ); d->majorGridPen.setColor(group.readEntry("MajorGridColor", QColor(Qt::gray)) ); d->majorGridPen.setWidthF( group.readEntry("MajorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->majorGridOpacity = group.readEntry("MajorGridOpacity", 1.0); //minor grid d->minorGridPen.setStyle( (Qt::PenStyle) group.readEntry("MinorGridStyle", (int) Qt::NoPen) ); d->minorGridPen.setColor(group.readEntry("MajorGridColor", QColor(Qt::gray)) ); d->minorGridPen.setWidthF( group.readEntry("MinorGridWidth", Worksheet::convertToSceneUnits( 1.0, Worksheet::Point ) ) ); d->minorGridOpacity = group.readEntry("MinorGridOpacity", 1.0); } /*! * For the most frequently edited properties, create Actions and ActionGroups for the context menu. * For some ActionGroups the actual actions are created in \c GuiTool, */ void Axis::initActions() { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &Axis::visibilityChangedSlot); //Orientation orientationActionGroup = new QActionGroup(this); orientationActionGroup->setExclusive(true); connect(orientationActionGroup, &QActionGroup::triggered, this, &Axis::orientationChangedSlot); - orientationHorizontalAction = new QAction(i18n("horizontal"), orientationActionGroup); + orientationHorizontalAction = new QAction(i18n("Horizontal"), orientationActionGroup); orientationHorizontalAction->setCheckable(true); - orientationVerticalAction = new QAction(i18n("vertical"), orientationActionGroup); + orientationVerticalAction = new QAction(i18n("Vertical"), orientationActionGroup); orientationVerticalAction->setCheckable(true); //Line lineStyleActionGroup = new QActionGroup(this); lineStyleActionGroup->setExclusive(true); connect(lineStyleActionGroup, &QActionGroup::triggered, this, &Axis::lineStyleChanged); lineColorActionGroup = new QActionGroup(this); lineColorActionGroup->setExclusive(true); connect(lineColorActionGroup, &QActionGroup::triggered, this, &Axis::lineColorChanged); //Ticks //TODO } void Axis::initMenus() { this->initActions(); //Orientation orientationMenu = new QMenu(i18n("Orientation")); orientationMenu->addAction(orientationHorizontalAction); orientationMenu->addAction(orientationVerticalAction); //Line lineMenu = new QMenu(i18n("Line")); - lineStyleMenu = new QMenu(i18n("style"), lineMenu); + lineStyleMenu = new QMenu(i18n("Style"), lineMenu); lineMenu->addMenu( lineStyleMenu ); - lineColorMenu = new QMenu(i18n("color"), lineMenu); + lineColorMenu = new QMenu(i18n("Color"), lineMenu); GuiTools::fillColorMenu( lineColorMenu, lineColorActionGroup ); lineMenu->addMenu( lineColorMenu ); m_menusInitialized = true; } QMenu* Axis::createContextMenu() { if (!m_menusInitialized) initMenus(); Q_D(const Axis); QMenu* menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); //Orientation if ( d->orientation == AxisHorizontal ) orientationHorizontalAction->setChecked(true); else orientationVerticalAction->setChecked(true); menu->insertMenu(firstAction, orientationMenu); //Line styles GuiTools::updatePenStyles( lineStyleMenu, lineStyleActionGroup, d->linePen.color() ); GuiTools::selectPenStyleAction(lineStyleActionGroup, d->linePen.style() ); GuiTools::selectColorAction(lineColorActionGroup, d->linePen.color() ); menu->insertMenu(firstAction, lineMenu); menu->insertSeparator(firstAction); return menu; } /*! Returns an icon to be used in the project explorer. */ QIcon Axis::icon() const{ Q_D(const Axis); QIcon ico; if (d->orientation == Axis::AxisHorizontal) ico = QIcon::fromTheme("labplot-axis-horizontal"); else ico = QIcon::fromTheme("labplot-axis-vertical"); return ico; } Axis::~Axis() { if (m_menusInitialized) { delete orientationMenu; delete lineMenu; } //no need to delete d->title, since it was added with addChild in init(); //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } QGraphicsItem *Axis::graphicsItem() const { return d_ptr; } /*! * overrides the implementation in WorksheetElement and sets the z-value to the maximal possible, * axes are drawn on top of all other object in the plot. */ void Axis::setZValue(qreal) { Q_D(Axis); d->setZValue(std::numeric_limits::max()); d->gridItem->setParentItem(d->parentItem()); d->gridItem->setZValue(0); } void Axis::retransform() { Q_D(Axis); d->retransform(); } void Axis::setSuppressRetransform(bool value) { Q_D(Axis); d->suppressRetransform = value; } void Axis::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("Axis::handleResize()"); Q_D(Axis); Q_UNUSED(pageResize); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); QPen pen = d->linePen; pen.setWidthF(pen.widthF() * ratio); d->linePen = pen; d->majorTicksLength *= ratio; // ticks are perpendicular to axis line -> verticalRatio relevant d->minorTicksLength *= ratio; d->labelsFont.setPixelSize( d->labelsFont.pixelSize() * ratio ); //TODO: take into account rotated labels d->labelsOffset *= ratio; d->title->handleResize(horizontalRatio, verticalRatio, pageResize); } /* ============================ getter methods ================= */ BASIC_SHARED_D_READER_IMPL(Axis, bool, autoScale, autoScale) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisOrientation, orientation, orientation) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisPosition, position, position) BASIC_SHARED_D_READER_IMPL(Axis, Axis::AxisScale, scale, scale) BASIC_SHARED_D_READER_IMPL(Axis, double, offset, offset) BASIC_SHARED_D_READER_IMPL(Axis, double, start, start) BASIC_SHARED_D_READER_IMPL(Axis, double, end, end) BASIC_SHARED_D_READER_IMPL(Axis, qreal, scalingFactor, scalingFactor) BASIC_SHARED_D_READER_IMPL(Axis, qreal, zeroOffset, zeroOffset) BASIC_SHARED_D_READER_IMPL(Axis, TextLabel*, title, title) BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetX, titleOffsetX) BASIC_SHARED_D_READER_IMPL(Axis, qreal, titleOffsetY, titleOffsetY) CLASS_SHARED_D_READER_IMPL(Axis, QPen, linePen, linePen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, lineOpacity, lineOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowType, arrowType, arrowType) BASIC_SHARED_D_READER_IMPL(Axis, Axis::ArrowPosition, arrowPosition, arrowPosition) BASIC_SHARED_D_READER_IMPL(Axis, qreal, arrowSize, arrowSize) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, majorTicksDirection, majorTicksDirection) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, majorTicksType, majorTicksType) BASIC_SHARED_D_READER_IMPL(Axis, int, majorTicksNumber, majorTicksNumber) BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksIncrement, majorTicksIncrement) BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, majorTicksColumn, majorTicksColumn) QString& Axis::majorTicksColumnPath() const { return d_ptr->majorTicksColumnPath; } BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksLength, majorTicksLength) CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorTicksPen, majorTicksPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorTicksOpacity, majorTicksOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksDirection, minorTicksDirection, minorTicksDirection) BASIC_SHARED_D_READER_IMPL(Axis, Axis::TicksType, minorTicksType, minorTicksType) BASIC_SHARED_D_READER_IMPL(Axis, int, minorTicksNumber, minorTicksNumber) BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksIncrement, minorTicksIncrement) BASIC_SHARED_D_READER_IMPL(Axis, const AbstractColumn*, minorTicksColumn, minorTicksColumn) QString& Axis::minorTicksColumnPath() const { return d_ptr->minorTicksColumnPath; } BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksLength, minorTicksLength) CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorTicksPen, minorTicksPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorTicksOpacity, minorTicksOpacity) BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsFormat, labelsFormat, labelsFormat); BASIC_SHARED_D_READER_IMPL(Axis, bool, labelsAutoPrecision, labelsAutoPrecision); BASIC_SHARED_D_READER_IMPL(Axis, int, labelsPrecision, labelsPrecision); BASIC_SHARED_D_READER_IMPL(Axis, Axis::LabelsPosition, labelsPosition, labelsPosition); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOffset, labelsOffset); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsRotationAngle, labelsRotationAngle); CLASS_SHARED_D_READER_IMPL(Axis, QColor, labelsColor, labelsColor); CLASS_SHARED_D_READER_IMPL(Axis, QFont, labelsFont, labelsFont); CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsPrefix, labelsPrefix); CLASS_SHARED_D_READER_IMPL(Axis, QString, labelsSuffix, labelsSuffix); BASIC_SHARED_D_READER_IMPL(Axis, qreal, labelsOpacity, labelsOpacity); CLASS_SHARED_D_READER_IMPL(Axis, QPen, majorGridPen, majorGridPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, majorGridOpacity, majorGridOpacity) CLASS_SHARED_D_READER_IMPL(Axis, QPen, minorGridPen, minorGridPen) BASIC_SHARED_D_READER_IMPL(Axis, qreal, minorGridOpacity, minorGridOpacity) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(Axis, SetAutoScale, bool, autoScale, retransform); void Axis::setAutoScale(bool autoScale) { Q_D(Axis); if (autoScale != d->autoScale) { - exec(new AxisSetAutoScaleCmd(d, autoScale, i18n("%1: set axis auto scaling"))); + exec(new AxisSetAutoScaleCmd(d, autoScale, ki18n("%1: set axis auto scaling"))); if (autoScale) { CartesianPlot *plot = qobject_cast(parentAspect()); if (!plot) return; if (d->orientation == Axis::AxisHorizontal) { d->end = plot->xMax(); d->start = plot->xMin(); } else { d->end = plot->yMax(); d->start = plot->yMin(); } retransform(); emit endChanged(d->end); emit startChanged(d->start); } } } STD_SWAP_METHOD_SETTER_CMD_IMPL(Axis, SetVisible, bool, swapVisible); void Axis::setVisible(bool on) { Q_D(Axis); - exec(new AxisSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new AxisSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool Axis::isVisible() const { Q_D(const Axis); return d->isVisible(); } void Axis::setPrinting(bool on) { Q_D(Axis); d->setPrinting(on); } STD_SETTER_CMD_IMPL_F_S(Axis, SetOrientation, Axis::AxisOrientation, orientation, retransform); void Axis::setOrientation( AxisOrientation orientation) { Q_D(Axis); if (orientation != d->orientation) - exec(new AxisSetOrientationCmd(d, orientation, i18n("%1: set axis orientation"))); + exec(new AxisSetOrientationCmd(d, orientation, ki18n("%1: set axis orientation"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetPosition, Axis::AxisPosition, position, retransform); void Axis::setPosition(AxisPosition position) { Q_D(Axis); if (position != d->position) - exec(new AxisSetPositionCmd(d, position, i18n("%1: set axis position"))); + exec(new AxisSetPositionCmd(d, position, ki18n("%1: set axis position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetScaling, Axis::AxisScale, scale, retransformTicks); void Axis::setScale(AxisScale scale) { Q_D(Axis); if (scale != d->scale) - exec(new AxisSetScalingCmd(d, scale, i18n("%1: set axis scale"))); + exec(new AxisSetScalingCmd(d, scale, ki18n("%1: set axis scale"))); } STD_SETTER_CMD_IMPL_F(Axis, SetOffset, double, offset, retransform); void Axis::setOffset(double offset, bool undo) { Q_D(Axis); if (offset != d->offset) { if (undo) { - exec(new AxisSetOffsetCmd(d, offset, i18n("%1: set axis offset"))); + exec(new AxisSetOffsetCmd(d, offset, ki18n("%1: set axis offset"))); } else { d->offset = offset; //don't need to call retransform() afterward //since the only usage of this call is in CartesianPlot, where retransform is called for all children anyway. } emit positionChanged(offset); } } STD_SETTER_CMD_IMPL_F_S(Axis, SetStart, double, start, retransform); void Axis::setStart(double start) { Q_D(Axis); if (start != d->start) - exec(new AxisSetStartCmd(d, start, i18n("%1: set axis start"))); + exec(new AxisSetStartCmd(d, start, ki18n("%1: set axis start"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetEnd, double, end, retransform); void Axis::setEnd(double end) { Q_D(Axis); if (end != d->end) - exec(new AxisSetEndCmd(d, end, i18n("%1: set axis end"))); + exec(new AxisSetEndCmd(d, end, ki18n("%1: set axis end"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetZeroOffset, qreal, zeroOffset, retransform); void Axis::setZeroOffset(qreal zeroOffset) { Q_D(Axis); if (zeroOffset != d->zeroOffset) - exec(new AxisSetZeroOffsetCmd(d, zeroOffset, i18n("%1: set axis zero offset"))); + exec(new AxisSetZeroOffsetCmd(d, zeroOffset, ki18n("%1: set axis zero offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetScalingFactor, qreal, scalingFactor, retransform); void Axis::setScalingFactor(qreal scalingFactor) { Q_D(Axis); if (scalingFactor != d->scalingFactor) - exec(new AxisSetScalingFactorCmd(d, scalingFactor, i18n("%1: set axis scaling factor"))); + exec(new AxisSetScalingFactorCmd(d, scalingFactor, ki18n("%1: set axis scaling factor"))); } //Title STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetX, qreal, titleOffsetX, retransform); void Axis::setTitleOffsetX(qreal offset) { Q_D(Axis); if (offset != d->titleOffsetX) - exec(new AxisSetTitleOffsetXCmd(d, offset, i18n("%1: set title offset"))); + exec(new AxisSetTitleOffsetXCmd(d, offset, ki18n("%1: set title offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetTitleOffsetY, qreal, titleOffsetY, retransform); void Axis::setTitleOffsetY(qreal offset) { Q_D(Axis); if (offset != d->titleOffsetY) - exec(new AxisSetTitleOffsetYCmd(d, offset, i18n("%1: set title offset"))); + exec(new AxisSetTitleOffsetYCmd(d, offset, ki18n("%1: set title offset"))); } //Line STD_SETTER_CMD_IMPL_F_S(Axis, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect); void Axis::setLinePen(const QPen &pen) { Q_D(Axis); if (pen != d->linePen) - exec(new AxisSetLinePenCmd(d, pen, i18n("%1: set line style"))); + exec(new AxisSetLinePenCmd(d, pen, ki18n("%1: set line style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLineOpacity, qreal, lineOpacity, update); void Axis::setLineOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->lineOpacity) - exec(new AxisSetLineOpacityCmd(d, opacity, i18n("%1: set line opacity"))); + exec(new AxisSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowType, Axis::ArrowType, arrowType, retransformArrow); void Axis::setArrowType(ArrowType type) { Q_D(Axis); if (type != d->arrowType) - exec(new AxisSetArrowTypeCmd(d, type, i18n("%1: set arrow type"))); + exec(new AxisSetArrowTypeCmd(d, type, ki18n("%1: set arrow type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowPosition, Axis::ArrowPosition, arrowPosition, retransformArrow); void Axis::setArrowPosition(ArrowPosition position) { Q_D(Axis); if (position != d->arrowPosition) - exec(new AxisSetArrowPositionCmd(d, position, i18n("%1: set arrow position"))); + exec(new AxisSetArrowPositionCmd(d, position, ki18n("%1: set arrow position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetArrowSize, qreal, arrowSize, retransformArrow); void Axis::setArrowSize(qreal arrowSize) { Q_D(Axis); if (arrowSize != d->arrowSize) - exec(new AxisSetArrowSizeCmd(d, arrowSize, i18n("%1: set arrow size"))); + exec(new AxisSetArrowSizeCmd(d, arrowSize, ki18n("%1: set arrow size"))); } //Major ticks STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksDirection, Axis::TicksDirection, majorTicksDirection, retransformTicks); void Axis::setMajorTicksDirection(const TicksDirection majorTicksDirection) { Q_D(Axis); if (majorTicksDirection != d->majorTicksDirection) - exec(new AxisSetMajorTicksDirectionCmd(d, majorTicksDirection, i18n("%1: set major ticks direction"))); + exec(new AxisSetMajorTicksDirectionCmd(d, majorTicksDirection, ki18n("%1: set major ticks direction"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksType, Axis::TicksType, majorTicksType, retransformTicks); void Axis::setMajorTicksType(const TicksType majorTicksType) { Q_D(Axis); if (majorTicksType!= d->majorTicksType) - exec(new AxisSetMajorTicksTypeCmd(d, majorTicksType, i18n("%1: set major ticks type"))); + exec(new AxisSetMajorTicksTypeCmd(d, majorTicksType, ki18n("%1: set major ticks type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksNumber, int, majorTicksNumber, retransformTicks); void Axis::setMajorTicksNumber(int majorTicksNumber) { Q_D(Axis); if (majorTicksNumber != d->majorTicksNumber) - exec(new AxisSetMajorTicksNumberCmd(d, majorTicksNumber, i18n("%1: set the total number of the major ticks"))); + exec(new AxisSetMajorTicksNumberCmd(d, majorTicksNumber, ki18n("%1: set the total number of the major ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksIncrement, qreal, majorTicksIncrement, retransformTicks); void Axis::setMajorTicksIncrement(qreal majorTicksIncrement) { Q_D(Axis); if (majorTicksIncrement != d->majorTicksIncrement) - exec(new AxisSetMajorTicksIncrementCmd(d, majorTicksIncrement, i18n("%1: set the increment for the major ticks"))); + exec(new AxisSetMajorTicksIncrementCmd(d, majorTicksIncrement, ki18n("%1: set the increment for the major ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksColumn, const AbstractColumn*, majorTicksColumn, retransformTicks) void Axis::setMajorTicksColumn(const AbstractColumn* column) { Q_D(Axis); if (column != d->majorTicksColumn) { - exec(new AxisSetMajorTicksColumnCmd(d, column, i18n("%1: assign major ticks' values"))); + exec(new AxisSetMajorTicksColumnCmd(d, column, ki18n("%1: assign major ticks' values"))); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Axis::majorTicksColumnAboutToBeRemoved); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksPen, QPen, majorTicksPen, recalcShapeAndBoundingRect); void Axis::setMajorTicksPen(const QPen &pen) { Q_D(Axis); if (pen != d->majorTicksPen) - exec(new AxisSetMajorTicksPenCmd(d, pen, i18n("%1: set major ticks style"))); + exec(new AxisSetMajorTicksPenCmd(d, pen, ki18n("%1: set major ticks style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksLength, qreal, majorTicksLength, retransformTicks); void Axis::setMajorTicksLength(qreal majorTicksLength) { Q_D(Axis); if (majorTicksLength != d->majorTicksLength) - exec(new AxisSetMajorTicksLengthCmd(d, majorTicksLength, i18n("%1: set major ticks length"))); + exec(new AxisSetMajorTicksLengthCmd(d, majorTicksLength, ki18n("%1: set major ticks length"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorTicksOpacity, qreal, majorTicksOpacity, update); void Axis::setMajorTicksOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->majorTicksOpacity) - exec(new AxisSetMajorTicksOpacityCmd(d, opacity, i18n("%1: set major ticks opacity"))); + exec(new AxisSetMajorTicksOpacityCmd(d, opacity, ki18n("%1: set major ticks opacity"))); } //Minor ticks STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksDirection, Axis::TicksDirection, minorTicksDirection, retransformTicks); void Axis::setMinorTicksDirection(const TicksDirection minorTicksDirection) { Q_D(Axis); if (minorTicksDirection != d->minorTicksDirection) - exec(new AxisSetMinorTicksDirectionCmd(d, minorTicksDirection, i18n("%1: set minor ticks direction"))); + exec(new AxisSetMinorTicksDirectionCmd(d, minorTicksDirection, ki18n("%1: set minor ticks direction"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksType, Axis::TicksType, minorTicksType, retransformTicks); void Axis::setMinorTicksType(const TicksType minorTicksType) { Q_D(Axis); if (minorTicksType!= d->minorTicksType) - exec(new AxisSetMinorTicksTypeCmd(d, minorTicksType, i18n("%1: set minor ticks type"))); + exec(new AxisSetMinorTicksTypeCmd(d, minorTicksType, ki18n("%1: set minor ticks type"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksNumber, int, minorTicksNumber, retransformTicks); void Axis::setMinorTicksNumber(int minorTicksNumber) { Q_D(Axis); if (minorTicksNumber != d->minorTicksNumber) - exec(new AxisSetMinorTicksNumberCmd(d, minorTicksNumber, i18n("%1: set the total number of the minor ticks"))); + exec(new AxisSetMinorTicksNumberCmd(d, minorTicksNumber, ki18n("%1: set the total number of the minor ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksIncrement, qreal, minorTicksIncrement, retransformTicks); void Axis::setMinorTicksIncrement(qreal minorTicksIncrement) { Q_D(Axis); if (minorTicksIncrement != d->minorTicksIncrement) - exec(new AxisSetMinorTicksIncrementCmd(d, minorTicksIncrement, i18n("%1: set the increment for the minor ticks"))); + exec(new AxisSetMinorTicksIncrementCmd(d, minorTicksIncrement, ki18n("%1: set the increment for the minor ticks"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksColumn, const AbstractColumn*, minorTicksColumn, retransformTicks) void Axis::setMinorTicksColumn(const AbstractColumn* column) { Q_D(Axis); if (column != d->minorTicksColumn) { - exec(new AxisSetMinorTicksColumnCmd(d, column, i18n("%1: assign minor ticks' values"))); + exec(new AxisSetMinorTicksColumnCmd(d, column, ki18n("%1: assign minor ticks' values"))); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Axis::retransformTicks); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Axis::minorTicksColumnAboutToBeRemoved); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksPen, QPen, minorTicksPen, recalcShapeAndBoundingRect); void Axis::setMinorTicksPen(const QPen &pen) { Q_D(Axis); if (pen != d->minorTicksPen) - exec(new AxisSetMinorTicksPenCmd(d, pen, i18n("%1: set minor ticks style"))); + exec(new AxisSetMinorTicksPenCmd(d, pen, ki18n("%1: set minor ticks style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksLength, qreal, minorTicksLength, retransformTicks); void Axis::setMinorTicksLength(qreal minorTicksLength) { Q_D(Axis); if (minorTicksLength != d->minorTicksLength) - exec(new AxisSetMinorTicksLengthCmd(d, minorTicksLength, i18n("%1: set minor ticks length"))); + exec(new AxisSetMinorTicksLengthCmd(d, minorTicksLength, ki18n("%1: set minor ticks length"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorTicksOpacity, qreal, minorTicksOpacity, update); void Axis::setMinorTicksOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->minorTicksOpacity) - exec(new AxisSetMinorTicksOpacityCmd(d, opacity, i18n("%1: set minor ticks opacity"))); + exec(new AxisSetMinorTicksOpacityCmd(d, opacity, ki18n("%1: set minor ticks opacity"))); } //Labels STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFormat, Axis::LabelsFormat, labelsFormat, retransformTicks); void Axis::setLabelsFormat(const LabelsFormat labelsFormat) { Q_D(Axis); if (labelsFormat != d->labelsFormat) { - exec(new AxisSetLabelsFormatCmd(d, labelsFormat, i18n("%1: set labels format"))); + exec(new AxisSetLabelsFormatCmd(d, labelsFormat, ki18n("%1: set labels format"))); //TODO: this part is not undo/redo-aware if (d->labelsFormatAutoChanged && labelsFormat == Axis::FormatDecimal) d->labelsFormatDecimalOverruled = true; else d->labelsFormatDecimalOverruled = false; } } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsAutoPrecision, bool, labelsAutoPrecision, retransformTickLabelStrings); void Axis::setLabelsAutoPrecision(const bool labelsAutoPrecision) { Q_D(Axis); if (labelsAutoPrecision != d->labelsAutoPrecision) - exec(new AxisSetLabelsAutoPrecisionCmd(d, labelsAutoPrecision, i18n("%1: set labels precision"))); + exec(new AxisSetLabelsAutoPrecisionCmd(d, labelsAutoPrecision, ki18n("%1: set labels precision"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrecision, int, labelsPrecision, retransformTickLabelStrings); void Axis::setLabelsPrecision(const int labelsPrecision) { Q_D(Axis); if (labelsPrecision != d->labelsPrecision) - exec(new AxisSetLabelsPrecisionCmd(d, labelsPrecision, i18n("%1: set labels precision"))); + exec(new AxisSetLabelsPrecisionCmd(d, labelsPrecision, ki18n("%1: set labels precision"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPosition, Axis::LabelsPosition, labelsPosition, retransformTickLabelPositions); void Axis::setLabelsPosition(const LabelsPosition labelsPosition) { Q_D(Axis); if (labelsPosition != d->labelsPosition) - exec(new AxisSetLabelsPositionCmd(d, labelsPosition, i18n("%1: set labels position"))); + exec(new AxisSetLabelsPositionCmd(d, labelsPosition, ki18n("%1: set labels position"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOffset, double, labelsOffset, retransformTickLabelPositions); void Axis::setLabelsOffset(double offset) { Q_D(Axis); if (offset != d->labelsOffset) - exec(new AxisSetLabelsOffsetCmd(d, offset, i18n("%1: set label offset"))); + exec(new AxisSetLabelsOffsetCmd(d, offset, ki18n("%1: set label offset"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsRotationAngle, qreal, labelsRotationAngle, recalcShapeAndBoundingRect); void Axis::setLabelsRotationAngle(qreal angle) { Q_D(Axis); if (angle != d->labelsRotationAngle) - exec(new AxisSetLabelsRotationAngleCmd(d, angle, i18n("%1: set label rotation angle"))); + exec(new AxisSetLabelsRotationAngleCmd(d, angle, ki18n("%1: set label rotation angle"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsColor, QColor, labelsColor, update); void Axis::setLabelsColor(const QColor &color) { Q_D(Axis); if (color != d->labelsColor) - exec(new AxisSetLabelsColorCmd(d, color, i18n("%1: set label color"))); + exec(new AxisSetLabelsColorCmd(d, color, ki18n("%1: set label color"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsFont, QFont, labelsFont, retransformTickLabelStrings); void Axis::setLabelsFont(const QFont &font) { Q_D(Axis); if (font != d->labelsFont) - exec(new AxisSetLabelsFontCmd(d, font, i18n("%1: set label font"))); + exec(new AxisSetLabelsFontCmd(d, font, ki18n("%1: set label font"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsPrefix, QString, labelsPrefix, retransformTickLabelStrings); void Axis::setLabelsPrefix(const QString& prefix) { Q_D(Axis); if (prefix != d->labelsPrefix) - exec(new AxisSetLabelsPrefixCmd(d, prefix, i18n("%1: set label prefix"))); + exec(new AxisSetLabelsPrefixCmd(d, prefix, ki18n("%1: set label prefix"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsSuffix, QString, labelsSuffix, retransformTickLabelStrings); void Axis::setLabelsSuffix(const QString& suffix) { Q_D(Axis); if (suffix != d->labelsSuffix) - exec(new AxisSetLabelsSuffixCmd(d, suffix, i18n("%1: set label suffix"))); + exec(new AxisSetLabelsSuffixCmd(d, suffix, ki18n("%1: set label suffix"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetLabelsOpacity, qreal, labelsOpacity, update); void Axis::setLabelsOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->labelsOpacity) - exec(new AxisSetLabelsOpacityCmd(d, opacity, i18n("%1: set labels opacity"))); + exec(new AxisSetLabelsOpacityCmd(d, opacity, ki18n("%1: set labels opacity"))); } //Major grid STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridPen, QPen, majorGridPen, retransformMajorGrid); void Axis::setMajorGridPen(const QPen &pen) { Q_D(Axis); if (pen != d->majorGridPen) - exec(new AxisSetMajorGridPenCmd(d, pen, i18n("%1: set major grid style"))); + exec(new AxisSetMajorGridPenCmd(d, pen, ki18n("%1: set major grid style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMajorGridOpacity, qreal, majorGridOpacity, update); void Axis::setMajorGridOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->majorGridOpacity) - exec(new AxisSetMajorGridOpacityCmd(d, opacity, i18n("%1: set major grid opacity"))); + exec(new AxisSetMajorGridOpacityCmd(d, opacity, ki18n("%1: set major grid opacity"))); } //Minor grid STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridPen, QPen, minorGridPen, retransformMinorGrid); void Axis::setMinorGridPen(const QPen &pen) { Q_D(Axis); if (pen != d->minorGridPen) - exec(new AxisSetMinorGridPenCmd(d, pen, i18n("%1: set minor grid style"))); + exec(new AxisSetMinorGridPenCmd(d, pen, ki18n("%1: set minor grid style"))); } STD_SETTER_CMD_IMPL_F_S(Axis, SetMinorGridOpacity, qreal, minorGridOpacity, update); void Axis::setMinorGridOpacity(qreal opacity) { Q_D(Axis); if (opacity != d->minorGridOpacity) - exec(new AxisSetMinorGridOpacityCmd(d, opacity, i18n("%1: set minor grid opacity"))); + exec(new AxisSetMinorGridOpacityCmd(d, opacity, ki18n("%1: set minor grid opacity"))); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## void Axis::labelChanged() { Q_D(Axis); d->recalcShapeAndBoundingRect(); } void Axis::retransformTicks() { Q_D(Axis); d->retransformTicks(); } void Axis::majorTicksColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Axis); if (aspect == d->majorTicksColumn) { d->majorTicksColumn = 0; d->retransformTicks(); } } void Axis::minorTicksColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Axis); if (aspect == d->minorTicksColumn) { d->minorTicksColumn = 0; d->retransformTicks(); } } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void Axis::orientationChangedSlot(QAction* action) { if (action == orientationHorizontalAction) this->setOrientation(AxisHorizontal); else this->setOrientation(AxisVertical); } void Axis::lineStyleChanged(QAction* action) { Q_D(const Axis); QPen pen = d->linePen; pen.setStyle(GuiTools::penStyleFromAction(lineStyleActionGroup, action)); this->setLinePen(pen); } void Axis::lineColorChanged(QAction* action) { Q_D(const Axis); QPen pen = d->linePen; pen.setColor(GuiTools::colorFromAction(lineColorActionGroup, action)); this->setLinePen(pen); } void Axis::visibilityChangedSlot() { Q_D(const Axis); this->setVisible(!d->isVisible()); } //##################################################################### //################### Private implementation ########################## //##################################################################### AxisPrivate::AxisPrivate(Axis* owner) : majorTicksColumn(0), minorTicksColumn(0), gridItem(new AxisGrid(this)), q(owner), suppressRetransform(false), labelsFormatDecimalOverruled(false), labelsFormatAutoChanged(false), plot(nullptr), cSystem(nullptr), m_hovered(false), m_suppressRecalc(false), m_printing(false) { setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setAcceptHoverEvents(true); } QString AxisPrivate::name() const{ return q->name(); } bool AxisPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); return oldValue; } QRectF AxisPrivate::boundingRect() const{ return boundingRectangle; } /*! Returns the shape of the XYCurve as a QPainterPath in local coordinates */ QPainterPath AxisPrivate::shape() const{ return axisShape; } /*! recalculates the position of the axis on the worksheet */ void AxisPrivate::retransform() { if (suppressRetransform || !plot) return; // PERFTRACE(name().toLatin1() + ", AxisPrivate::retransform()"); m_suppressRecalc = true; retransformLine(); m_suppressRecalc = false; recalcShapeAndBoundingRect(); } void AxisPrivate::retransformLine() { if (suppressRetransform) return; linePath = QPainterPath(); lines.clear(); QPointF startPoint; QPointF endPoint; if (orientation == Axis::AxisHorizontal) { if (position == Axis::AxisTop) offset = plot->yMax(); else if (position == Axis::AxisBottom) offset = plot->yMin(); else if (position == Axis::AxisCentered) offset = plot->yMin() + (plot->yMax()-plot->yMin())/2; startPoint.setX(start); startPoint.setY(offset); endPoint.setX(end); endPoint.setY(offset); } else { // vertical if (position == Axis::AxisLeft) offset = plot->xMin(); else if (position == Axis::AxisRight) offset = plot->xMax(); else if (position == Axis::AxisCentered) offset = plot->xMin() + (plot->xMax()-plot->xMin())/2; startPoint.setX(offset); startPoint.setY(start); endPoint.setY(end); endPoint.setX(offset); } lines.append(QLineF(startPoint, endPoint)); lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::MarkGaps); for (const auto& line : lines) { linePath.moveTo(line.p1()); linePath.lineTo(line.p2()); } if (linePath.isEmpty()) { recalcShapeAndBoundingRect(); return; } else { retransformArrow(); retransformTicks(); } } void AxisPrivate::retransformArrow() { if (suppressRetransform) return; arrowPath = QPainterPath(); if (arrowType == Axis::NoArrow || lines.isEmpty()) { recalcShapeAndBoundingRect(); return; } if (arrowPosition == Axis::ArrowRight || arrowPosition == Axis::ArrowBoth) { const QPointF& endPoint = lines.at(lines.size()-1).p2(); this->addArrow(endPoint, 1); } if (arrowPosition == Axis::ArrowLeft || arrowPosition == Axis::ArrowBoth) { const QPointF& endPoint = lines.at(0).p1(); this->addArrow(endPoint, -1); } recalcShapeAndBoundingRect(); } void AxisPrivate::addArrow(QPointF startPoint, int direction) { static const double cos_phi = cos(M_PI/6.); if (orientation == Axis::AxisHorizontal) { QPointF endPoint = QPointF(startPoint.x() + direction*arrowSize, startPoint.y()); arrowPath.moveTo(startPoint); arrowPath.lineTo(endPoint); switch (arrowType) { case Axis::NoArrow: break; case Axis::SimpleArrowSmall: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); break; case Axis::SimpleArrowBig: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); break; case Axis::FilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::FilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()-arrowSize/4*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/8, endPoint.y())); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y()+arrowSize/4*cos_phi)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()-arrowSize/2*cos_phi)); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/4, endPoint.y())); arrowPath.lineTo(QPointF(endPoint.x()-direction*arrowSize/2, endPoint.y()+arrowSize/2*cos_phi)); arrowPath.lineTo(endPoint); break; } } else { //vertical orientation QPointF endPoint = QPointF(startPoint.x(), startPoint.y()-direction*arrowSize); arrowPath.moveTo(startPoint); arrowPath.lineTo(endPoint); switch (arrowType) { case Axis::NoArrow: break; case Axis::SimpleArrowSmall: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); break; case Axis::SimpleArrowBig: arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.moveTo(endPoint); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); break; case Axis::FilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(endPoint); break; case Axis::FilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowSmall: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/8)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/4*cos_phi, endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(endPoint); break; case Axis::SemiFilledArrowBig: arrowPath.lineTo(QPointF(endPoint.x()-arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(QPointF(endPoint.x(), endPoint.y()+direction*arrowSize/4)); arrowPath.lineTo(QPointF(endPoint.x()+arrowSize/2*cos_phi, endPoint.y()+direction*arrowSize/2)); arrowPath.lineTo(endPoint); break; } } } //! helper function for retransformTicks() bool AxisPrivate::transformAnchor(QPointF* anchorPoint) { QVector points; points.append(*anchorPoint); points = cSystem->mapLogicalToScene(points); if (points.count() != 1) { // point is not mappable or in a coordinate gap return false; } else { *anchorPoint = points.at(0); return true; } } /*! recalculates the position of the axis ticks. */ void AxisPrivate::retransformTicks() { if (suppressRetransform) return; //TODO: check that start and end are > 0 for log and >=0 for sqrt, etc. majorTicksPath = QPainterPath(); minorTicksPath = QPainterPath(); majorTickPoints.clear(); minorTickPoints.clear(); tickLabelValues.clear(); if ( majorTicksNumber < 1 || (majorTicksDirection == Axis::noTicks && minorTicksDirection == Axis::noTicks) ) { retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect() return; } //determine the spacing for the major ticks double majorTicksSpacing = 0; int tmpMajorTicksNumber = 0; if (majorTicksType == Axis::TicksTotalNumber) { //the total number of the major ticks is given - > determine the spacing tmpMajorTicksNumber = majorTicksNumber; switch (scale) { case Axis::ScaleLinear: majorTicksSpacing = (end-start)/(majorTicksNumber-1); break; case Axis::ScaleLog10: majorTicksSpacing = (log10(end)-log10(start))/(majorTicksNumber-1); break; case Axis::ScaleLog2: majorTicksSpacing = (log(end)-log(start))/log(2)/(majorTicksNumber-1); break; case Axis::ScaleLn: majorTicksSpacing = (log(end)-log(start))/(majorTicksNumber-1); break; case Axis::ScaleSqrt: majorTicksSpacing = (sqrt(end)-sqrt(start))/(majorTicksNumber-1); break; case Axis::ScaleX2: majorTicksSpacing = (pow(end,2)-pow(start,2))/(majorTicksNumber-1); } } else if (majorTicksType == Axis::TicksIncrement) { //the spacing (increment) of the major ticks is given - > determine the number majorTicksSpacing = majorTicksIncrement; switch (scale) { case Axis::ScaleLinear: tmpMajorTicksNumber = qRound((end-start)/majorTicksSpacing + 1); break; case Axis::ScaleLog10: tmpMajorTicksNumber = qRound((log10(end)-log10(start))/majorTicksSpacing + 1); break; case Axis::ScaleLog2: tmpMajorTicksNumber = qRound((log(end)-log(start))/log(2)/majorTicksSpacing + 1); break; case Axis::ScaleLn: tmpMajorTicksNumber = qRound((log(end)-log(start))/majorTicksSpacing + 1); break; case Axis::ScaleSqrt: tmpMajorTicksNumber = qRound((sqrt(end)-sqrt(start))/majorTicksSpacing + 1); break; case Axis::ScaleX2: tmpMajorTicksNumber = qRound((pow(end,2)-pow(start,2))/majorTicksSpacing + 1); } } else { //custom column was provided if (majorTicksColumn) { tmpMajorTicksNumber = majorTicksColumn->rowCount(); } else { retransformTickLabelPositions(); //this calls recalcShapeAndBoundingRect() return; } } int tmpMinorTicksNumber; if (minorTicksType == Axis::TicksTotalNumber) tmpMinorTicksNumber = minorTicksNumber; else if (minorTicksType == Axis::TicksIncrement) tmpMinorTicksNumber = (end - start)/ (majorTicksNumber - 1)/minorTicksIncrement - 1; else (minorTicksColumn) ? tmpMinorTicksNumber = minorTicksColumn->rowCount() : tmpMinorTicksNumber = 0; QPointF anchorPoint; QPointF startPoint; QPointF endPoint; qreal majorTickPos=0.0; qreal minorTickPos; qreal nextMajorTickPos = 0.0; const int xDirection = cSystem->xDirection(); const int yDirection = cSystem->yDirection(); const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2; const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2; bool valid; for (int iMajor = 0; iMajor < tmpMajorTicksNumber; iMajor++) { //calculate major tick's position if (majorTicksType != Axis::TicksCustomColumn) { switch (scale) { case Axis::ScaleLinear: majorTickPos = start + majorTicksSpacing*iMajor; nextMajorTickPos = start + majorTicksSpacing*(iMajor+1); break; case Axis::ScaleLog10: majorTickPos = pow(10, log10(start) + majorTicksSpacing*iMajor); nextMajorTickPos = pow(10, log10(start) + majorTicksSpacing*(iMajor+1)); break; case Axis::ScaleLog2: majorTickPos = pow(2, log(start)/log(2) + majorTicksSpacing*iMajor); nextMajorTickPos = pow(2, log(start)/log(2) + majorTicksSpacing*(iMajor+1)); break; case Axis::ScaleLn: majorTickPos = exp(log(start) + majorTicksSpacing*iMajor); nextMajorTickPos = exp(log(start) + majorTicksSpacing*(iMajor+1)); break; case Axis::ScaleSqrt: majorTickPos = pow(sqrt(start) + majorTicksSpacing*iMajor, 2); nextMajorTickPos = pow(sqrt(start) + majorTicksSpacing*(iMajor+1), 2); break; case Axis::ScaleX2: majorTickPos = sqrt(sqrt(start) + majorTicksSpacing*iMajor); nextMajorTickPos = sqrt(sqrt(start) + majorTicksSpacing*(iMajor+1)); break; } } else { majorTickPos = majorTicksColumn->valueAt(iMajor); if (std::isnan(majorTickPos)) break; //stop iterating after the first non numerical value in the column } //calculate start and end points for major tick's line if (majorTicksDirection != Axis::noTicks ) { if (orientation == Axis::AxisHorizontal) { anchorPoint.setX(majorTickPos); anchorPoint.setY(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0); } } } else { // vertical anchorPoint.setY(majorTickPos); anchorPoint.setX(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleX) { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0); } } } //add major tick's line to the painter path if (valid) { majorTicksPath.moveTo(startPoint); majorTicksPath.lineTo(endPoint); majorTickPoints << anchorPoint; tickLabelValues<< scalingFactor*majorTickPos+zeroOffset; } } //minor ticks if ((Axis::noTicks != minorTicksDirection) && (tmpMajorTicksNumber > 1) && (tmpMinorTicksNumber > 0) && (iMajorvalueAt(iMinor); if (std::isnan(minorTickPos)) break; //stop iterating after the first non numerical value in the column //in the case a custom column is used for the minor ticks, we draw them _once_ for the whole range of the axis. //execute the minor ticks loop only once. if (iMajor > 0) break; } //calculate start and end points for minor tick's line if (orientation == Axis::AxisHorizontal) { anchorPoint.setX(minorTickPos); anchorPoint.setY(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? yDirection * minorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? -yDirection * minorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksOut) ? yDirection * minorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (minorTicksDirection & Axis::ticksIn) ? -yDirection * minorTicksLength : 0); } } } else { // vertical anchorPoint.setY(minorTickPos); anchorPoint.setX(offset); valid = transformAnchor(&anchorPoint); if (valid) { if (offset < middleX) { startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? xDirection * minorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? -xDirection * minorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksOut) ? xDirection * minorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((minorTicksDirection & Axis::ticksIn) ? -xDirection * minorTicksLength : 0, 0); } } } //add minor tick's line to the painter path if (valid) { minorTicksPath.moveTo(startPoint); minorTicksPath.lineTo(endPoint); minorTickPoints << anchorPoint; } } } } //tick positions where changed -> update the position of the tick labels and grid lines retransformTickLabelStrings(); retransformMajorGrid(); retransformMinorGrid(); } /*! creates the tick label strings starting with the most optimal (=the smallest possible number of float digits) precision for the floats */ void AxisPrivate::retransformTickLabelStrings() { if (suppressRetransform) return; // DEBUG("AxisPrivate::retransformTickLabelStrings()"); if (labelsAutoPrecision) { //check, whether we need to increase the current precision int newPrecision = upperLabelsPrecision(labelsPrecision); if (newPrecision!= labelsPrecision) { labelsPrecision = newPrecision; emit q->labelsPrecisionChanged(labelsPrecision); } else { //check, whether we can reduce the current precision newPrecision = lowerLabelsPrecision(labelsPrecision); if (newPrecision!= labelsPrecision) { labelsPrecision = newPrecision; emit q->labelsPrecisionChanged(labelsPrecision); } } } // DEBUG("labelsPrecision =" << labelsPrecision); //automatically switch from 'decimal' to 'scientific' format for big numbers (>10^4) //and back to decimal when the numbers get smaller after the auto-switch again if (labelsFormat == Axis::FormatDecimal && !labelsFormatDecimalOverruled) { for (auto value : tickLabelValues) { if (std::abs(value) > 1e4) { labelsFormat = Axis::FormatScientificE; emit q->labelsFormatChanged(labelsFormat); labelsFormatAutoChanged = true; break; } } } else if (labelsFormatAutoChanged ) { //check whether we still have big numbers bool changeBack = true; for (auto value : tickLabelValues) { if (std::abs(value) > 1e4) { changeBack = false; break; } } if (changeBack) { labelsFormatAutoChanged = false; labelsFormat = Axis::FormatDecimal; emit q->labelsFormatChanged(labelsFormat); } } tickLabelStrings.clear(); QString str; if (labelsFormat == Axis::FormatDecimal) { QString nullStr = QString::number(0, 'f', labelsPrecision); for (auto value : tickLabelValues) { str = QString::number(value, 'f', labelsPrecision); if (str == "-" + nullStr) str = nullStr; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatScientificE) { QString nullStr = QString::number(0, 'e', labelsPrecision); for (auto value : tickLabelValues) { str = QString::number(value, 'e', labelsPrecision); if (str == "-" + nullStr) str = nullStr; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowers10) { for (auto value : tickLabelValues) { str = "10" + QString::number(log10(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowers2) { for (auto value : tickLabelValues) { str = "2" + QString::number(log2(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatPowersE) { for (auto value : tickLabelValues) { str = "e" + QString::number(log(value), 'f', labelsPrecision) + ""; str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } else if (labelsFormat == Axis::FormatMultipliesPi) { for (auto value : tickLabelValues) { str = "" + QString::number(value / M_PI, 'f', labelsPrecision) + "" + QChar(0x03C0); str = labelsPrefix + str + labelsSuffix; tickLabelStrings << str; } } //recalculate the position of the tick labels retransformTickLabelPositions(); } /*! returns the smallest upper limit for the precision where no duplicates for the tick label float occur. */ int AxisPrivate::upperLabelsPrecision(int precision) { // DEBUG("AxisPrivate::upperLabelsPrecision() precision =" << precision); //round float to the current precision and look for duplicates. //if there are duplicates, increase the precision. QVector tempValues; for (int i = 0; i < tickLabelValues.size(); ++i) tempValues.append( nsl_math_round_places(tickLabelValues[i], precision) ); for (int i = 0; i < tempValues.size(); ++i) { for (int j = 0; j < tempValues.size(); ++j) { if (i == j) continue; if (tempValues.at(i) == tempValues.at(j)) { //duplicate for the current precision found, increase the precision and check again return upperLabelsPrecision(precision + 1); } } } //no duplicates for the current precision found: return the current value DEBUG(" upper precision = " << precision); return precision; } /*! returns highest lower limit for the precision where no duplicates for the tick label float occur. */ int AxisPrivate::lowerLabelsPrecision(int precision) { // DEBUG("AxisPrivate::lowerLabelsPrecision() precision =" << precision); //round float to the current precision and look for duplicates. //if there are duplicates, decrease the precision. QVector tempValues; for (int i = 0; i < tickLabelValues.size(); ++i) tempValues.append( nsl_math_round_places(tickLabelValues[i], precision-1) ); for (int i = 0; i < tempValues.size(); ++i) { for (int j = 0; j < tempValues.size(); ++j) { if (i == j) continue; if (tempValues.at(i) == tempValues.at(j)) { //duplicate found for the reduced precision //-> current precision cannot be reduced, return the current value DEBUG(" lower precision = " << precision); return precision; } } } //no duplicates found, reduce further, and check again if (precision == 0) return 0; else return lowerLabelsPrecision(precision - 1); } /*! recalculates the position of the tick labels. Called when the geometry related properties (position, offset, font size, suffix, prefix) of the labels are changed. */ void AxisPrivate::retransformTickLabelPositions() { tickLabelPoints.clear(); if (majorTicksDirection == Axis::noTicks || labelsPosition == Axis::NoLabels) { recalcShapeAndBoundingRect(); return; } QFontMetrics fm(labelsFont); float width = 0; float height = fm.ascent(); QPointF pos; const double middleX = plot->xMin() + (plot->xMax() - plot->xMin())/2; const double middleY = plot->yMin() + (plot->yMax() - plot->yMin())/2; const int xDirection = cSystem->xDirection(); const int yDirection = cSystem->yDirection(); QPointF startPoint, endPoint, anchorPoint; QTextDocument td; td.setDefaultFont(labelsFont); for ( int i = 0; i < majorTickPoints.size(); i++ ) { if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { width = fm.width(tickLabelStrings.at(i)); } else { td.setHtml(tickLabelStrings.at(i)); width = td.size().width(); height = td.size().height(); } anchorPoint = majorTickPoints.at(i); //center align all labels with respect to the end point of the tick line if (orientation == Axis::AxisHorizontal) { if (offset < middleY) { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? -yDirection * majorTicksLength : 0); } else { startPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksOut) ? yDirection * majorTicksLength : 0); endPoint = anchorPoint + QPointF(0, (majorTicksDirection & Axis::ticksIn) ? -yDirection * majorTicksLength : 0); } if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - width/2); pos.setY( endPoint.y() + height + labelsOffset ); } else { pos.setX( startPoint.x() - width/2); pos.setY( startPoint.y() - labelsOffset ); } } else {// vertical if (offset < middleX) { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? -xDirection * majorTicksLength : 0, 0); } else { startPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksOut) ? xDirection * majorTicksLength : 0, 0); endPoint = anchorPoint + QPointF((majorTicksDirection & Axis::ticksIn) ? -xDirection * majorTicksLength : 0, 0); } if (labelsPosition == Axis::LabelsOut) { pos.setX( endPoint.x() - width - labelsOffset ); pos.setY( endPoint.y() + height/2 ); } else { pos.setX( startPoint.x() + labelsOffset ); pos.setY( startPoint.y() + height/2 ); } } tickLabelPoints << pos; } recalcShapeAndBoundingRect(); } void AxisPrivate::retransformMajorGrid() { if (suppressRetransform) return; majorGridPath = QPainterPath(); if (majorGridPen.style() == Qt::NoPen || majorTickPoints.size() == 0) { recalcShapeAndBoundingRect(); return; } //major tick points are already in scene coordinates, convert them back to logical... //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function. //Currently, grid lines disappear somtimes without this flag QVector logicalMajorTickPoints = cSystem->mapSceneToLogical(majorTickPoints, AbstractCoordinateSystem::SuppressPageClipping); if (logicalMajorTickPoints.isEmpty()) return; //TODO: //when iterating over all grid lines, skip the first and the last points for auto scaled axes, //since we don't want to paint any grid lines at the plot boundaries bool skipLowestTick, skipUpperTick; if (orientation == Axis::AxisHorizontal) { //horizontal axis skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).x(), plot->xMin()); skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).x(), plot->xMax()); } else { skipLowestTick = qFuzzyCompare(logicalMajorTickPoints.at(0).y(), plot->yMin()); skipUpperTick = qFuzzyCompare(logicalMajorTickPoints.at(logicalMajorTickPoints.size()-1).y(), plot->yMax()); } int start, end; if (skipLowestTick) { if (logicalMajorTickPoints.size() > 1) start = 1; else start = 0; } else { start = 0; } if (skipUpperTick) { if (logicalMajorTickPoints.size() > 1) end = logicalMajorTickPoints.size() - 1; else end = 0; } else { end = logicalMajorTickPoints.size(); } QVector lines; if (orientation == Axis::AxisHorizontal) { //horizontal axis double yMin = plot->yMin(); double yMax = plot->yMax(); for (int i=start; ixMin(); double xMax = plot->xMax(); //skip the first and the last points, since we don't want to paint any grid lines at the plot boundaries for (int i = start; i < end; ++i) { const QPointF& point = logicalMajorTickPoints.at(i); lines.append( QLineF(xMin, point.y(), xMax, point.y()) ); } } lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::SuppressPageClipping); for (const auto& line : lines) { majorGridPath.moveTo(line.p1()); majorGridPath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } void AxisPrivate::retransformMinorGrid() { if (suppressRetransform) return; minorGridPath = QPainterPath(); if (minorGridPen.style() == Qt::NoPen) { recalcShapeAndBoundingRect(); return; } //minor tick points are already in scene coordinates, convert them back to logical... //TODO: mapping should work without SuppressPageClipping-flag, check float comparisons in the map-function. //Currently, grid lines disappear somtimes without this flag QVector logicalMinorTickPoints = cSystem->mapSceneToLogical(minorTickPoints, AbstractCoordinateSystem::SuppressPageClipping); QVector lines; if (orientation == Axis::AxisHorizontal) { //horizontal axis double yMin = plot->yMin(); double yMax = plot->yMax(); for (int i = 0; i < logicalMinorTickPoints.size(); ++i) { const QPointF& point = logicalMinorTickPoints.at(i); lines.append( QLineF(point.x(), yMin, point.x(), yMax) ); } } else { //vertical axis double xMin = plot->xMin(); double xMax = plot->xMax(); for (int i = 0; i < logicalMinorTickPoints.size(); ++i) { const QPointF& point = logicalMinorTickPoints.at(i); lines.append( QLineF(xMin, point.y(), xMax, point.y()) ); } } lines = cSystem->mapLogicalToScene(lines, AbstractCoordinateSystem::SuppressPageClipping); for (const auto& line : lines) { minorGridPath.moveTo(line.p1()); minorGridPath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } void AxisPrivate::recalcShapeAndBoundingRect() { if (m_suppressRecalc) return; prepareGeometryChange(); if (linePath.isEmpty()) { axisShape = QPainterPath(); boundingRectangle = QRectF(); title->setPositionInvalid(true); if (plot) plot->prepareGeometryChange(); return; } else { title->setPositionInvalid(false); } axisShape = WorksheetElement::shapeFromPath(linePath, linePen); axisShape.addPath(WorksheetElement::shapeFromPath(arrowPath, linePen)); axisShape.addPath(WorksheetElement::shapeFromPath(majorTicksPath, majorTicksPen)); axisShape.addPath(WorksheetElement::shapeFromPath(minorTicksPath, minorTicksPen)); QPainterPath tickLabelsPath = QPainterPath(); if (labelsPosition != Axis::NoLabels) { QTransform trafo; QPainterPath tempPath; QFontMetrics fm(labelsFont); QTextDocument td; td.setDefaultFont(labelsFont); for (int i = 0; i < tickLabelPoints.size(); i++) { tempPath = QPainterPath(); if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { tempPath.addRect( fm.boundingRect(tickLabelStrings.at(i)) ); } else { td.setHtml(tickLabelStrings.at(i)); tempPath.addRect( QRectF(0, -td.size().height(), td.size().width(), td.size().height()) ); } trafo.reset(); trafo.translate( tickLabelPoints.at(i).x(), tickLabelPoints.at(i).y() ); trafo.rotate( -labelsRotationAngle ); tempPath = trafo.map(tempPath); tickLabelsPath.addPath(WorksheetElement::shapeFromPath(tempPath, linePen)); } axisShape.addPath(WorksheetElement::shapeFromPath(tickLabelsPath, QPen())); } //add title label, if available if ( title->isVisible() && !title->text().text.isEmpty() ) { //determine the new position of the title label: //we calculate the new position here and not in retransform(), //since it depends on the size and position of the tick labels, tickLabelsPath, available here. QRectF rect=linePath.boundingRect(); qreal offsetX = titleOffsetX - labelsOffset; //the distance to the axis line qreal offsetY = titleOffsetY - labelsOffset; //the distance to the axis line if (orientation == Axis::AxisHorizontal) { offsetY -= title->graphicsItem()->boundingRect().height()/2 + tickLabelsPath.boundingRect().height(); title->setPosition( QPointF( (rect.topLeft().x() + rect.topRight().x())/2 + offsetX, rect.bottomLeft().y() - offsetY ) ); } else { offsetX -= title->graphicsItem()->boundingRect().width()/2 + tickLabelsPath.boundingRect().width(); title->setPosition( QPointF( rect.topLeft().x() + offsetX, (rect.topLeft().y() + rect.bottomLeft().y())/2 - offsetY) ); } axisShape.addPath(WorksheetElement::shapeFromPath(title->graphicsItem()->mapToParent(title->graphicsItem()->shape()), linePen)); } boundingRectangle = axisShape.boundingRect(); //if the axis goes beyond the current bounding box of the plot (too high offset is used, too long labels etc.) //request a prepareGeometryChange() for the plot in order to properly keep track of geometry changes if (plot) plot->prepareGeometryChange(); } /*! paints the content of the axis. Reimplemented from \c QGraphicsItem. \sa QGraphicsItem::paint() */ void AxisPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (!isVisible()) return; if (linePath.isEmpty()) return; //draw the line if (linePen.style() != Qt::NoPen) { painter->setOpacity(lineOpacity); painter->setPen(linePen); painter->setBrush(Qt::SolidPattern); painter->drawPath(linePath); //draw the arrow if (arrowType != Axis::NoArrow) painter->drawPath(arrowPath); } //draw the major ticks if (majorTicksDirection != Axis::noTicks) { painter->setOpacity(majorTicksOpacity); painter->setPen(majorTicksPen); painter->setBrush(Qt::NoBrush); painter->drawPath(majorTicksPath); } //draw the minor ticks if (minorTicksDirection != Axis::noTicks) { painter->setOpacity(minorTicksOpacity); painter->setPen(minorTicksPen); painter->setBrush(Qt::NoBrush); painter->drawPath(minorTicksPath); } // draw tick labels if (labelsPosition != Axis::NoLabels) { painter->setOpacity(labelsOpacity); painter->setPen(QPen(labelsColor)); painter->setFont(labelsFont); QTextDocument td; td.setDefaultFont(labelsFont); for (int i = 0; i < tickLabelPoints.size(); i++) { painter->translate(tickLabelPoints.at(i)); painter->save(); painter->rotate(-labelsRotationAngle); if (labelsFormat == Axis::FormatDecimal || labelsFormat == Axis::FormatScientificE) { painter->drawText(QPoint(0,0), tickLabelStrings.at(i)); } else { td.setHtml(tickLabelStrings.at(i)); painter->translate(0, -td.size().height()); td.drawContents(painter); } painter->restore(); painter->translate(-tickLabelPoints.at(i)); } } if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(axisShape); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(axisShape); } } void AxisPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void AxisPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(axisShape.boundingRect()); } } void AxisPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(axisShape.boundingRect()); } } void AxisPrivate::setPrinting(bool on) { m_printing = on; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Axis::save(QXmlStreamWriter* writer) const{ Q_D(const Axis); writer->writeStartElement( "axis" ); writeBasicAttributes( writer ); writeCommentElement( writer ); //general writer->writeStartElement( "general" ); writer->writeAttribute( "autoScale", QString::number(d->autoScale) ); writer->writeAttribute( "orientation", QString::number(d->orientation) ); writer->writeAttribute( "position", QString::number(d->position) ); writer->writeAttribute( "scale", QString::number(d->scale) ); writer->writeAttribute( "offset", QString::number(d->offset) ); writer->writeAttribute( "start", QString::number(d->start) ); writer->writeAttribute( "end", QString::number(d->end) ); writer->writeAttribute( "scalingFactor", QString::number(d->scalingFactor) ); writer->writeAttribute( "zeroOffset", QString::number(d->zeroOffset) ); writer->writeAttribute( "titleOffsetX", QString::number(d->titleOffsetX) ); writer->writeAttribute( "titleOffsetY", QString::number(d->titleOffsetY) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //label d->title->save( writer ); //line writer->writeStartElement( "line" ); WRITE_QPEN(d->linePen); writer->writeAttribute( "opacity", QString::number(d->lineOpacity) ); writer->writeAttribute( "arrowType", QString::number(d->arrowType) ); writer->writeAttribute( "arrowPosition", QString::number(d->arrowPosition) ); writer->writeAttribute( "arrowSize", QString::number(d->arrowSize) ); writer->writeEndElement(); //major ticks writer->writeStartElement( "majorTicks" ); writer->writeAttribute( "direction", QString::number(d->majorTicksDirection) ); writer->writeAttribute( "type", QString::number(d->majorTicksType) ); writer->writeAttribute( "number", QString::number(d->majorTicksNumber) ); writer->writeAttribute( "increment", QString::number(d->majorTicksIncrement) ); WRITE_COLUMN(d->majorTicksColumn, majorTicksColumn); writer->writeAttribute( "length", QString::number(d->majorTicksLength) ); WRITE_QPEN(d->majorTicksPen); writer->writeAttribute( "opacity", QString::number(d->majorTicksOpacity) ); writer->writeEndElement(); //minor ticks writer->writeStartElement( "minorTicks" ); writer->writeAttribute( "direction", QString::number(d->minorTicksDirection) ); writer->writeAttribute( "type", QString::number(d->minorTicksType) ); writer->writeAttribute( "number", QString::number(d->minorTicksNumber) ); writer->writeAttribute( "increment", QString::number(d->minorTicksIncrement) ); WRITE_COLUMN(d->minorTicksColumn, minorTicksColumn); writer->writeAttribute( "length", QString::number(d->minorTicksLength) ); WRITE_QPEN(d->minorTicksPen); writer->writeAttribute( "opacity", QString::number(d->minorTicksOpacity) ); writer->writeEndElement(); //extra ticks //labels writer->writeStartElement( "labels" ); writer->writeAttribute( "position", QString::number(d->labelsPosition) ); writer->writeAttribute( "offset", QString::number(d->labelsOffset) ); writer->writeAttribute( "rotation", QString::number(d->labelsRotationAngle) ); writer->writeAttribute( "format", QString::number(d->labelsFormat) ); writer->writeAttribute( "precision", QString::number(d->labelsPrecision) ); writer->writeAttribute( "autoPrecision", QString::number(d->labelsAutoPrecision) ); WRITE_QCOLOR(d->labelsColor); WRITE_QFONT(d->labelsFont); writer->writeAttribute( "prefix", d->labelsPrefix ); writer->writeAttribute( "suffix", d->labelsSuffix ); writer->writeAttribute( "opacity", QString::number(d->labelsOpacity) ); writer->writeEndElement(); //grid writer->writeStartElement( "majorGrid" ); WRITE_QPEN(d->majorGridPen); writer->writeAttribute( "opacity", QString::number(d->majorGridOpacity) ); writer->writeEndElement(); writer->writeStartElement( "minorGrid" ); WRITE_QPEN(d->minorGridPen); writer->writeAttribute( "opacity", QString::number(d->minorGridOpacity) ); writer->writeEndElement(); writer->writeEndElement(); // close "axis" section } //! Load from XML bool Axis::load(XmlStreamReader* reader, bool preview) { Q_D(Axis); if (!reader->isStartElement() || reader->name() != "axis") { reader->raiseError(i18n("no axis element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "axis") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("autoScale").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoScale'")); + reader->raiseWarning(attributeWarning.subs("autoScale").toString()); else d->autoScale = (bool)str.toInt(); str = attribs.value("orientation").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'orientation'")); + reader->raiseWarning(attributeWarning.subs("orientation").toString()); else d->orientation = (Axis::AxisOrientation)str.toInt(); str = attribs.value("position").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'position'")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else d->position = (Axis::AxisPosition)str.toInt(); str = attribs.value("scale").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'scale'")); + reader->raiseWarning(attributeWarning.subs("scale").toString()); else d->scale = (Axis::AxisScale)str.toInt(); str = attribs.value("offset").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'offset'")); + reader->raiseWarning(attributeWarning.subs("offset").toString()); else d->offset = str.toDouble(); str = attribs.value("start").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'start'")); + reader->raiseWarning(attributeWarning.subs("start").toString()); else d->start = str.toDouble(); str = attribs.value("end").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'end'")); + reader->raiseWarning(attributeWarning.subs("end").toString()); else d->end = str.toDouble(); str = attribs.value("scalingFactor").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'scalingFactor'")); + reader->raiseWarning(attributeWarning.subs("scalingFactor").toString()); else d->scalingFactor = str.toDouble(); str = attribs.value("zeroOffset").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'zeroOffset'")); + reader->raiseWarning(attributeWarning.subs("zeroOffset").toString()); else d->zeroOffset = str.toDouble(); str = attribs.value("titleOffsetX").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'titleOffsetX'")); + reader->raiseWarning(attributeWarning.subs("titleOffsetX").toString()); else d->titleOffsetX = str.toDouble(); str = attribs.value("titleOffsetY").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'titleOffsetY'")); + reader->raiseWarning(attributeWarning.subs("titleOffsetY").toString()); else d->titleOffsetY = str.toDouble(); str = attribs.value("visible").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (reader->name() == "textLabel") { d->title->load(reader, preview); } else if (!preview && reader->name() == "line") { attribs = reader->attributes(); READ_QPEN(d->linePen); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->lineOpacity = str.toDouble(); str = attribs.value("arrowType").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'arrowType'")); + reader->raiseWarning(attributeWarning.subs("arrowType").toString()); else d->arrowType = (Axis::ArrowType)str.toInt(); str = attribs.value("arrowPosition").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'arrowPosition'")); + reader->raiseWarning(attributeWarning.subs("arrowPosition").toString()); else d->arrowPosition = (Axis::ArrowPosition)str.toInt(); str = attribs.value("arrowSize").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'arrowSize'")); + reader->raiseWarning(attributeWarning.subs("arrowSize").toString()); else d->arrowSize = str.toDouble(); } else if (!preview && reader->name() == "majorTicks") { attribs = reader->attributes(); str = attribs.value("direction").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'direction'")); + reader->raiseWarning(attributeWarning.subs("direction").toString()); else d->majorTicksDirection = (Axis::TicksDirection)str.toInt(); str = attribs.value("type").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'type'")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->majorTicksType = (Axis::TicksType)str.toInt(); str = attribs.value("number").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'number'")); + reader->raiseWarning(attributeWarning.subs("number").toString()); else d->majorTicksNumber = str.toInt(); str = attribs.value("increment").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'increment'")); + reader->raiseWarning(attributeWarning.subs("increment").toString()); else d->majorTicksIncrement = str.toDouble(); READ_COLUMN(majorTicksColumn); str = attribs.value("length").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'length'")); + reader->raiseWarning(attributeWarning.subs("length").toString()); else d->majorTicksLength = str.toDouble(); READ_QPEN(d->majorTicksPen); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->majorTicksOpacity = str.toDouble(); } else if (!preview && reader->name() == "minorTicks") { attribs = reader->attributes(); str = attribs.value("direction").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'direction'")); + reader->raiseWarning(attributeWarning.subs("direction").toString()); else d->minorTicksDirection = (Axis::TicksDirection)str.toInt(); str = attribs.value("type").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'type'")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->minorTicksType = (Axis::TicksType)str.toInt(); str = attribs.value("number").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'number'")); + reader->raiseWarning(attributeWarning.subs("number").toString()); else d->minorTicksNumber = str.toInt(); str = attribs.value("increment").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'increment'")); + reader->raiseWarning(attributeWarning.subs("increment").toString()); else d->minorTicksIncrement = str.toDouble(); READ_COLUMN(minorTicksColumn); str = attribs.value("length").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'length'")); + reader->raiseWarning(attributeWarning.subs("length").toString()); else d->minorTicksLength = str.toDouble(); READ_QPEN(d->minorTicksPen); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->minorTicksOpacity = str.toDouble(); } else if (!preview && reader->name() == "labels") { attribs = reader->attributes(); str = attribs.value("position").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'position'")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else d->labelsPosition = (Axis::LabelsPosition)str.toInt(); str = attribs.value("offset").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'offset'")); + reader->raiseWarning(attributeWarning.subs("offset").toString()); else d->labelsOffset = str.toDouble(); str = attribs.value("rotation").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'rotation'")); + reader->raiseWarning(attributeWarning.subs("rotation").toString()); else d->labelsRotationAngle = str.toDouble(); str = attribs.value("format").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'format'")); + reader->raiseWarning(attributeWarning.subs("format").toString()); else d->labelsFormat = (Axis::LabelsFormat)str.toInt(); str = attribs.value("precision").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'precision'")); + reader->raiseWarning(attributeWarning.subs("precision").toString()); else d->labelsPrecision = str.toInt(); str = attribs.value("autoPrecision").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoPrecision'")); + reader->raiseWarning(attributeWarning.subs("autoPrecision").toString()); else d->labelsAutoPrecision = str.toInt(); READ_QCOLOR(d->labelsColor); READ_QFONT(d->labelsFont); //don't produce any warning if no prefix or suffix is set (empty string is allowd here in xml) d->labelsPrefix = attribs.value("prefix").toString(); d->labelsSuffix = attribs.value("suffix").toString(); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->labelsOpacity = str.toDouble(); } else if (!preview && reader->name() == "majorGrid") { attribs = reader->attributes(); READ_QPEN(d->majorGridPen); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->majorGridOpacity = str.toDouble(); } else if (!preview && reader->name() == "minorGrid") { attribs = reader->attributes(); READ_QPEN(d->minorGridPen); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->minorGridOpacity = str.toDouble(); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void Axis::loadThemeConfig(const KConfig& config) { const KConfigGroup group = config.group("Axis"); QPen p; // Tick label this->setLabelsColor(group.readEntry("LabelsFontColor",(QColor) this->labelsColor())); this->setLabelsOpacity(group.readEntry("LabelsOpacity",this->labelsOpacity())); //Line this->setLineOpacity(group.readEntry("LineOpacity",this->lineOpacity())); p.setColor(group.readEntry("LineColor", (QColor) this->linePen().color())); p.setStyle((Qt::PenStyle)group.readEntry("LineStyle",(int) this->linePen().style())); p.setWidthF(group.readEntry("LineWidth", this->linePen().widthF())); this->setLinePen(p); //Major ticks this->setMajorGridOpacity(group.readEntry("MajorGridOpacity", this->majorGridOpacity())); p.setColor(group.readEntry("MajorGridColor",(QColor) this->majorGridPen().color())); p.setStyle((Qt::PenStyle)group.readEntry("MajorGridStyle",(int) this->majorGridPen().style())); p.setWidthF(group.readEntry("MajorGridWidth", this->majorGridPen().widthF())); this->setMajorGridPen(p); p.setColor(group.readEntry("MajorTicksColor",(QColor)this->majorTicksPen().color())); p.setStyle((Qt::PenStyle)group.readEntry("MajorTicksLineStyle",(int) this->majorTicksPen().style())); p.setWidthF(group.readEntry("MajorTicksWidth", this->majorTicksPen().widthF())); this->setMajorTicksPen(p); this->setMajorTicksOpacity(group.readEntry("MajorTicksOpacity",this->majorTicksOpacity())); //Minor ticks this->setMinorGridOpacity(group.readEntry("MinorGridOpacity", this->minorGridOpacity())); p.setColor(group.readEntry("MinorGridColor",(QColor) this->minorGridPen().color())); p.setStyle((Qt::PenStyle)group.readEntry("MinorGridStyle",(int) this->minorGridPen().style())); p.setWidthF(group.readEntry("MinorGridWidth", this->minorGridPen().widthF())); this->setMinorGridPen(p); p.setColor(group.readEntry("MinorTicksColor",(QColor) this->minorTicksPen().color())); p.setStyle((Qt::PenStyle)group.readEntry("MinorTicksLineStyle",(int) this->minorTicksPen().style())); p.setWidthF(group.readEntry("MinorTicksWidth", this->minorTicksPen().widthF())); this->setMinorTicksPen(p); this->setMinorTicksOpacity(group.readEntry("MinorTicksOpacity",this->minorTicksOpacity())); const QVector& childElements = children(AbstractAspect::IncludeHidden); for (auto* child : childElements) child->loadThemeConfig(config); } void Axis::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Axis"); // Tick label group.writeEntry("LabelsFontColor", (QColor) this->labelsColor()); group.writeEntry("LabelsOpacity", this->labelsOpacity()); //Line group.writeEntry("LineOpacity", this->lineOpacity()); group.writeEntry("LineColor", (QColor) this->linePen().color()); group.writeEntry("LineStyle", (int) this->linePen().style()); group.writeEntry("LineWidth", this->linePen().widthF()); //Major ticks group.writeEntry("MajorGridOpacity", this->majorGridOpacity()); group.writeEntry("MajorGridColor", (QColor) this->majorGridPen().color()); group.writeEntry("MajorGridStyle", (int) this->majorGridPen().style()); group.writeEntry("MajorGridWidth", this->majorGridPen().widthF()); group.writeEntry("MajorTicksColor", (QColor)this->majorTicksPen().color()); group.writeEntry("MajorTicksLineStyle", (int) this->majorTicksPen().style()); group.writeEntry("MajorTicksWidth", this->majorTicksPen().widthF()); group.writeEntry("MajorTicksOpacity", this->majorTicksOpacity()); group.writeEntry("MajorTicksType", (int)this->majorTicksType()); //Minor ticks group.writeEntry("MinorGridOpacity", this->minorGridOpacity()); group.writeEntry("MinorGridColor",(QColor) this->minorGridPen().color()); group.writeEntry("MinorGridStyle", (int) this->minorGridPen().style()); group.writeEntry("MinorGridWidth", this->minorGridPen().widthF()); group.writeEntry("MinorTicksColor", (QColor) this->minorTicksPen().color()); group.writeEntry("MinorTicksLineStyle",( int) this->minorTicksPen().style()); group.writeEntry("MinorTicksWidth", this->minorTicksPen().widthF()); group.writeEntry("MinorTicksOpacity", this->minorTicksOpacity()); group.writeEntry("MinorTicksType", (int)this->minorTicksType()); const QVector& childElements = children(AbstractAspect::IncludeHidden); childElements.at(0)->saveThemeConfig(config); } diff --git a/src/backend/worksheet/plots/cartesian/CartesianPlot.cpp b/src/backend/worksheet/plots/cartesian/CartesianPlot.cpp index 61d421921..973e2ed4b 100644 --- a/src/backend/worksheet/plots/cartesian/CartesianPlot.cpp +++ b/src/backend/worksheet/plots/cartesian/CartesianPlot.cpp @@ -1,3038 +1,3047 @@ /*************************************************************************** File : CartesianPlot.cpp Project : LabPlot Description : Cartesian plot -------------------------------------------------------------------- Copyright : (C) 2011-2018 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "CartesianPlot.h" #include "CartesianPlotPrivate.h" #include "Axis.h" #include "XYCurve.h" #include "Histogram.h" #include "XYEquationCurve.h" #include "XYDataReductionCurve.h" #include "XYDifferentiationCurve.h" #include "XYIntegrationCurve.h" #include "XYInterpolationCurve.h" #include "XYSmoothCurve.h" #include "XYFitCurve.h" #include "XYFourierFilterCurve.h" #include "XYFourierTransformCurve.h" #include "backend/core/Project.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h" #include "backend/worksheet/plots/cartesian/CustomPoint.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/plots/AbstractPlotPrivate.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/TextLabel.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" //for PlotDataDialog::AnalysisAction. TODO: find a better place for this enum. #include "kdefrontend/ThemeHandler.h" #include "kdefrontend/widgets/ThemesWidget.h" #include #include #include #include #include #include #include #include +#include #include -#include +#include /** * \class CartesianPlot * \brief A xy-plot. * * */ CartesianPlot::CartesianPlot(const QString &name):AbstractPlot(name, new CartesianPlotPrivate(this)), - m_legend(0), m_zoomFactor(1.2), m_menusInitialized(false), + m_legend(nullptr), m_zoomFactor(1.2), m_menusInitialized(false), addNewMenu(nullptr), zoomMenu(nullptr), dataAnalysisMenu(nullptr), themeMenu(nullptr) { init(); } CartesianPlot::CartesianPlot(const QString &name, CartesianPlotPrivate *dd):AbstractPlot(name, dd), - m_legend(0), m_zoomFactor(1.2), + m_legend(nullptr), m_zoomFactor(1.2), addNewMenu(nullptr), zoomMenu(nullptr), dataAnalysisMenu(nullptr), themeMenu(nullptr) { init(); } CartesianPlot::~CartesianPlot() { if (m_menusInitialized) { delete addNewMenu; delete zoomMenu; delete themeMenu; } delete m_coordinateSystem; //don't need to delete objects added with addChild() //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } /*! initializes all member variables of \c CartesianPlot */ void CartesianPlot::init() { Q_D(CartesianPlot); d->cSystem = new CartesianCoordinateSystem(this); m_coordinateSystem = d->cSystem; d->rangeType = CartesianPlot::RangeFree; d->rangeLastValues = 1000; d->rangeFirstValues = 1000; d->autoScaleX = true; d->autoScaleY = true; d->xScale = ScaleLinear; d->yScale = ScaleLinear; d->xRangeBreakingEnabled = false; d->yRangeBreakingEnabled = false; //the following factor determines the size of the offset between the min/max points of the curves //and the coordinate system ranges, when doing auto scaling //Factor 0 corresponds to the exact match - min/max values of the curves correspond to the start/end values of the ranges. //TODO: make this factor optional. //Provide in the UI the possibility to choose between "exact" or 0% offset, 2%, 5% and 10% for the auto fit option d->autoScaleOffsetFactor = 0.0f; m_plotArea = new PlotArea(name() + " plot area"); addChildFast(m_plotArea); //Plot title m_title = new TextLabel(this->name(), TextLabel::PlotTitle); addChild(m_title); m_title->setHidden(true); m_title->setParentGraphicsItem(m_plotArea->graphicsItem()); //offset between the plot area and the area defining the coordinate system, in scene units. d->horizontalPadding = Worksheet::convertToSceneUnits(1.5, Worksheet::Centimeter); d->verticalPadding = Worksheet::convertToSceneUnits(1.5, Worksheet::Centimeter); connect(this, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(childAdded(const AbstractAspect*))); connect(this, SIGNAL(aspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*)), this, SLOT(childRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*))); graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); graphicsItem()->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); graphicsItem()->setFlag(QGraphicsItem::ItemIsSelectable, true); graphicsItem()->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); graphicsItem()->setFlag(QGraphicsItem::ItemIsFocusable, true); } /*! initializes all children of \c CartesianPlot and setups a default plot of type \c type with a plot title. */ void CartesianPlot::initDefault(Type type) { Q_D(CartesianPlot); switch (type) { case FourAxes: { d->xMin = 0; d->xMax = 1; d->yMin = 0; d->yMax = 1; //Axes Axis* axis = new Axis("x axis 1", Axis::AxisHorizontal); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisBottom); axis->setStart(0); axis->setEnd(1); axis->setMajorTicksDirection(Axis::ticksIn); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksIn); axis->setMinorTicksNumber(1); QPen pen = axis->majorGridPen(); pen.setStyle(Qt::SolidLine); axis->setMajorGridPen(pen); pen = axis->minorGridPen(); pen.setStyle(Qt::DotLine); axis->setMinorGridPen(pen); axis->setSuppressRetransform(false); axis = new Axis("x axis 2", Axis::AxisHorizontal); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisTop); axis->setStart(0); axis->setEnd(1); axis->setMajorTicksDirection(Axis::ticksIn); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksIn); axis->setMinorTicksNumber(1); axis->setLabelsPosition(Axis::NoLabels); axis->title()->setText(QString()); axis->setSuppressRetransform(false); axis = new Axis("y axis 1", Axis::AxisVertical); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisLeft); axis->setStart(0); axis->setEnd(1); axis->setMajorTicksDirection(Axis::ticksIn); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksIn); axis->setMinorTicksNumber(1); pen = axis->majorGridPen(); pen.setStyle(Qt::SolidLine); axis->setMajorGridPen(pen); pen = axis->minorGridPen(); pen.setStyle(Qt::DotLine); axis->setMinorGridPen(pen); axis->setSuppressRetransform(false); axis = new Axis("y axis 2", Axis::AxisVertical); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisRight); axis->setStart(0); axis->setEnd(1); axis->setOffset(1); axis->setMajorTicksDirection(Axis::ticksIn); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksIn); axis->setMinorTicksNumber(1); axis->setLabelsPosition(Axis::NoLabels); axis->title()->setText(QString()); axis->setSuppressRetransform(false); break; } case TwoAxes: { d->xMin = 0; d->xMax = 1; d->yMin = 0; d->yMax = 1; Axis* axis = new Axis("x axis 1", Axis::AxisHorizontal); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisBottom); axis->setStart(0); axis->setEnd(1); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->setSuppressRetransform(false); axis = new Axis("y axis 1", Axis::AxisVertical); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisLeft); axis->setStart(0); axis->setEnd(1); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->setSuppressRetransform(false); break; } case TwoAxesCentered: { d->xMin = -0.5; d->xMax = 0.5; d->yMin = -0.5; d->yMax = 0.5; d->horizontalPadding = Worksheet::convertToSceneUnits(1.0, Worksheet::Centimeter); d->verticalPadding = Worksheet::convertToSceneUnits(1.0, Worksheet::Centimeter); QPen pen = m_plotArea->borderPen(); pen.setStyle(Qt::NoPen); m_plotArea->setBorderPen(pen); Axis* axis = new Axis("x axis 1", Axis::AxisHorizontal); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisCentered); axis->setStart(-0.5); axis->setEnd(0.5); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->title()->setText(QString()); axis->setSuppressRetransform(false); axis = new Axis("y axis 1", Axis::AxisVertical); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisCentered); axis->setStart(-0.5); axis->setEnd(0.5); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->title()->setText(QString()); axis->setSuppressRetransform(false); break; } case TwoAxesCenteredZero: { d->xMin = -0.5; d->xMax = 0.5; d->yMin = -0.5; d->yMax = 0.5; d->horizontalPadding = Worksheet::convertToSceneUnits(1.0, Worksheet::Centimeter); d->verticalPadding = Worksheet::convertToSceneUnits(1.0, Worksheet::Centimeter); QPen pen = m_plotArea->borderPen(); pen.setStyle(Qt::NoPen); m_plotArea->setBorderPen(pen); Axis* axis = new Axis("x axis 1", Axis::AxisHorizontal); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisCustom); axis->setOffset(0); axis->setStart(-0.5); axis->setEnd(0.5); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->title()->setText(QString()); axis->setSuppressRetransform(false); axis = new Axis("y axis 1", Axis::AxisVertical); axis->setSuppressRetransform(true); addChild(axis); axis->setPosition(Axis::AxisCustom); axis->setOffset(0); axis->setStart(-0.5); axis->setEnd(0.5); axis->setMajorTicksDirection(Axis::ticksBoth); axis->setMajorTicksNumber(6); axis->setMinorTicksDirection(Axis::ticksBoth); axis->setMinorTicksNumber(1); axis->setArrowType(Axis::FilledArrowSmall); axis->title()->setText(QString()); axis->setSuppressRetransform(false); break; } } d->xMinPrev = d->xMin; d->xMaxPrev = d->xMax; d->yMinPrev = d->yMin; d->yMaxPrev = d->yMax; //Geometry, specify the plot rect in scene coordinates. //TODO: Use default settings for left, top, width, height and for min/max for the coordinate system float x = Worksheet::convertToSceneUnits(2, Worksheet::Centimeter); float y = Worksheet::convertToSceneUnits(2, Worksheet::Centimeter); float w = Worksheet::convertToSceneUnits(10, Worksheet::Centimeter); float h = Worksheet::convertToSceneUnits(10, Worksheet::Centimeter); //all plot children are initialized -> set the geometry of the plot in scene coordinates. d->rect = QRectF(x,y,w,h); } void CartesianPlot::initActions() { //"add new" actions addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve"), this); addHistogramPlot = new QAction(QIcon::fromTheme("labplot-xy-fourier_filter-curve"), i18n("Histogram"), this); addEquationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-equation-curve"), i18n("xy-curve from a mathematical equation"), this); // no icons yet addDataReductionCurveAction = new QAction(i18n("xy-curve from a data reduction"), this); addDifferentiationCurveAction = new QAction(i18n("xy-curve from a differentiation"), this); addIntegrationCurveAction = new QAction(i18n("xy-curve from an integration"), this); addInterpolationCurveAction = new QAction(i18n("xy-curve from an interpolation"), this); addSmoothCurveAction = new QAction(i18n("xy-curve from a smooth"), this); addFitCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("xy-curve from a fit to data"), this); addFourierFilterCurveAction = new QAction(i18n("xy-curve from a Fourier filter"), this); addFourierTransformCurveAction = new QAction(i18n("xy-curve from a Fourier transform"), this); // addInterpolationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("xy-curve from an interpolation"), this); // addSmoothCurveAction = new QAction(QIcon::fromTheme("labplot-xy-smooth-curve"), i18n("xy-curve from a smooth"), this); // addFourierFilterCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier_filter-curve"), i18n("xy-curve from a Fourier filter"), this); // addFourierTransformCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier_transform-curve"), i18n("xy-curve from a Fourier transform"), this); - addLegendAction = new QAction(QIcon::fromTheme("text-field"), i18n("legend"), this); + addLegendAction = new QAction(QIcon::fromTheme("text-field"), i18n("Legend"), this); if (children().size()>0) addLegendAction->setEnabled(false); //only one legend is allowed -> disable the action - addHorizontalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("horizontal axis"), this); - addVerticalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("vertical axis"), this); - addTextLabelAction = new QAction(QIcon::fromTheme("labplot-text-label"), i18n("text label"), this); - addCustomPointAction = new QAction(QIcon::fromTheme("draw-cross"), i18n("custom point"), this); + addHorizontalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("Horizontal Axis"), this); + addVerticalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("Vertical Axis"), this); + addTextLabelAction = new QAction(QIcon::fromTheme("labplot-text-label"), i18n("Text Label"), this); + addCustomPointAction = new QAction(QIcon::fromTheme("draw-cross"), i18n("Custom Point"), this); connect(addCurveAction, SIGNAL(triggered()), SLOT(addCurve())); connect(addHistogramPlot,SIGNAL(triggered()), SLOT(addHistogram())); connect(addEquationCurveAction, SIGNAL(triggered()), SLOT(addEquationCurve())); connect(addDataReductionCurveAction, SIGNAL(triggered()), SLOT(addDataReductionCurve())); connect(addDifferentiationCurveAction, SIGNAL(triggered()), SLOT(addDifferentiationCurve())); connect(addIntegrationCurveAction, SIGNAL(triggered()), SLOT(addIntegrationCurve())); connect(addInterpolationCurveAction, SIGNAL(triggered()), SLOT(addInterpolationCurve())); connect(addSmoothCurveAction, SIGNAL(triggered()), SLOT(addSmoothCurve())); connect(addFitCurveAction, SIGNAL(triggered()), SLOT(addFitCurve())); connect(addFourierFilterCurveAction, SIGNAL(triggered()), SLOT(addFourierFilterCurve())); connect(addFourierTransformCurveAction, SIGNAL(triggered()), SLOT(addFourierTransformCurve())); connect(addLegendAction, SIGNAL(triggered()), SLOT(addLegend())); connect(addHorizontalAxisAction, SIGNAL(triggered()), SLOT(addHorizontalAxis())); connect(addVerticalAxisAction, SIGNAL(triggered()), SLOT(addVerticalAxis())); connect(addTextLabelAction, SIGNAL(triggered()), SLOT(addTextLabel())); connect(addCustomPointAction, SIGNAL(triggered()), SLOT(addCustomPoint())); //Analysis menu actions - addDataOperationAction = new QAction(i18n("Data operation"), this); - addDataReductionAction = new QAction(i18n("Reduce data"), this); + addDataOperationAction = new QAction(i18n("Data Operation"), this); + addDataReductionAction = new QAction(i18n("Reduce Data"), this); addDifferentiationAction = new QAction(i18n("Differentiate"), this); addIntegrationAction = new QAction(i18n("Integrate"), this); addInterpolationAction = new QAction(i18n("Interpolate"), this); addSmoothAction = new QAction(i18n("Smooth"), this); QAction* fitAction = new QAction(i18n("Linear"), this); fitAction->setData(PlotDataDialog::FitLinear); addFitAction.append(fitAction); fitAction = new QAction(i18n("Power"), this); fitAction->setData(PlotDataDialog::FitPower); addFitAction.append(fitAction); fitAction = new QAction(i18n("Exponential (degree 1)"), this); fitAction->setData(PlotDataDialog::FitExp1); addFitAction.append(fitAction); fitAction = new QAction(i18n("Exponential (degree 2)"), this); fitAction->setData(PlotDataDialog::FitExp2); addFitAction.append(fitAction); fitAction = new QAction(i18n("Inverse exponential"), this); fitAction->setData(PlotDataDialog::FitInvExp); addFitAction.append(fitAction); fitAction = new QAction(i18n("Gauss"), this); fitAction->setData(PlotDataDialog::FitGauss); addFitAction.append(fitAction); fitAction = new QAction(i18n("Cauchy-Lorentz"), this); fitAction->setData(PlotDataDialog::FitCauchyLorentz); addFitAction.append(fitAction); fitAction = new QAction(i18n("Arc Tangent"), this); fitAction->setData(PlotDataDialog::FitTan); addFitAction.append(fitAction); - fitAction = new QAction(i18n("Hyperbolic tangent"), this); + fitAction = new QAction(i18n("Hyperbolic Tangent"), this); fitAction->setData(PlotDataDialog::FitTanh); addFitAction.append(fitAction); - fitAction = new QAction(i18n("Error function"), this); + fitAction = new QAction(i18n("Error Function"), this); fitAction->setData(PlotDataDialog::FitErrFunc); addFitAction.append(fitAction); fitAction = new QAction(i18n("Custom"), this); fitAction->setData(PlotDataDialog::FitCustom); addFitAction.append(fitAction); - addFourierFilterAction = new QAction(i18n("Fourier filter"), this); + addFourierFilterAction = new QAction(i18n("Fourier Filter"), this); connect(addDataReductionAction, SIGNAL(triggered()), SLOT(addDataReductionCurve())); connect(addDifferentiationAction, SIGNAL(triggered()), SLOT(addDifferentiationCurve())); connect(addIntegrationAction, SIGNAL(triggered()), SLOT(addIntegrationCurve())); connect(addInterpolationAction, SIGNAL(triggered()), SLOT(addInterpolationCurve())); connect(addSmoothAction, SIGNAL(triggered()), SLOT(addSmoothCurve())); for (const auto& action: addFitAction) connect(action, SIGNAL(triggered()), SLOT(addFitCurve())); connect(addFourierFilterAction, SIGNAL(triggered()), SLOT(addFourierFilterCurve())); //zoom/navigate actions - scaleAutoAction = new QAction(QIcon::fromTheme("labplot-auto-scale-all"), i18n("auto scale"), this); - scaleAutoXAction = new QAction(QIcon::fromTheme("labplot-auto-scale-x"), i18n("auto scale X"), this); - scaleAutoYAction = new QAction(QIcon::fromTheme("labplot-auto-scale-y"), i18n("auto scale Y"), this); - zoomInAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("zoom in"), this); - zoomOutAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("zoom out"), this); - zoomInXAction = new QAction(QIcon::fromTheme("labplot-zoom-in-x"), i18n("zoom in X"), this); - zoomOutXAction = new QAction(QIcon::fromTheme("labplot-zoom-out-x"), i18n("zoom out X"), this); - zoomInYAction = new QAction(QIcon::fromTheme("labplot-zoom-in-y"), i18n("zoom in Y"), this); - zoomOutYAction = new QAction(QIcon::fromTheme("labplot-zoom-out-y"), i18n("zoom out Y"), this); - shiftLeftXAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("shift left X"), this); - shiftRightXAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("shift right X"), this); - shiftUpYAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("shift up Y"), this); - shiftDownYAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("shift down Y"), this); + scaleAutoAction = new QAction(QIcon::fromTheme("labplot-auto-scale-all"), i18n("Auto Scale"), this); + scaleAutoXAction = new QAction(QIcon::fromTheme("labplot-auto-scale-x"), i18n("Auto Scale X"), this); + scaleAutoYAction = new QAction(QIcon::fromTheme("labplot-auto-scale-y"), i18n("Auto Scale Y"), this); + zoomInAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), this); + zoomOutAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), this); + zoomInXAction = new QAction(QIcon::fromTheme("labplot-zoom-in-x"), i18n("Zoom In X"), this); + zoomOutXAction = new QAction(QIcon::fromTheme("labplot-zoom-out-x"), i18n("Zoom Out X"), this); + zoomInYAction = new QAction(QIcon::fromTheme("labplot-zoom-in-y"), i18n("Zoom In Y"), this); + zoomOutYAction = new QAction(QIcon::fromTheme("labplot-zoom-out-y"), i18n("Zoom Out Y"), this); + shiftLeftXAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left X"), this); + shiftRightXAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right X"), this); + shiftUpYAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Up Y"), this); + shiftDownYAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Down Y"), this); connect(scaleAutoAction, SIGNAL(triggered()), SLOT(scaleAuto())); connect(scaleAutoXAction, SIGNAL(triggered()), SLOT(scaleAutoX())); connect(scaleAutoYAction, SIGNAL(triggered()), SLOT(scaleAutoY())); connect(zoomInAction, SIGNAL(triggered()), SLOT(zoomIn())); connect(zoomOutAction, SIGNAL(triggered()), SLOT(zoomOut())); connect(zoomInXAction, SIGNAL(triggered()), SLOT(zoomInX())); connect(zoomOutXAction, SIGNAL(triggered()), SLOT(zoomOutX())); connect(zoomInYAction, SIGNAL(triggered()), SLOT(zoomInY())); connect(zoomOutYAction, SIGNAL(triggered()), SLOT(zoomOutY())); connect(shiftLeftXAction, SIGNAL(triggered()), SLOT(shiftLeftX())); connect(shiftRightXAction, SIGNAL(triggered()), SLOT(shiftRightX())); connect(shiftUpYAction, SIGNAL(triggered()), SLOT(shiftUpY())); connect(shiftDownYAction, SIGNAL(triggered()), SLOT(shiftDownY())); //visibility action visibilityAction = new QAction(i18n("visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, SIGNAL(triggered()), this, SLOT(visibilityChanged())); } void CartesianPlot::initMenus() { initActions(); - addNewMenu = new QMenu(i18n("Add new")); + addNewMenu = new QMenu(i18n("Add New")); addNewMenu->addAction(addCurveAction); // addNewMenu->addAction(addHistogramPlot); addNewMenu->addAction(addEquationCurveAction); addNewMenu->addSeparator(); addNewMenu->addAction(addDataReductionCurveAction); addNewMenu->addAction(addDifferentiationCurveAction); addNewMenu->addAction(addIntegrationCurveAction); addNewMenu->addAction(addInterpolationCurveAction); addNewMenu->addAction(addSmoothCurveAction); addNewMenu->addAction(addFitCurveAction); addNewMenu->addAction(addFourierFilterCurveAction); addNewMenu->addAction(addFourierTransformCurveAction); addNewMenu->addSeparator(); addNewMenu->addAction(addLegendAction); addNewMenu->addSeparator(); addNewMenu->addAction(addHorizontalAxisAction); addNewMenu->addAction(addVerticalAxisAction); addNewMenu->addSeparator(); addNewMenu->addAction(addTextLabelAction); addNewMenu->addSeparator(); addNewMenu->addAction(addCustomPointAction); zoomMenu = new QMenu(i18n("Zoom")); zoomMenu->addAction(scaleAutoAction); zoomMenu->addAction(scaleAutoXAction); zoomMenu->addAction(scaleAutoYAction); zoomMenu->addSeparator(); zoomMenu->addAction(zoomInAction); zoomMenu->addAction(zoomOutAction); zoomMenu->addSeparator(); zoomMenu->addAction(zoomInXAction); zoomMenu->addAction(zoomOutXAction); zoomMenu->addSeparator(); zoomMenu->addAction(zoomInYAction); zoomMenu->addAction(zoomOutYAction); zoomMenu->addSeparator(); zoomMenu->addAction(shiftLeftXAction); zoomMenu->addAction(shiftRightXAction); zoomMenu->addSeparator(); zoomMenu->addAction(shiftUpYAction); zoomMenu->addAction(shiftDownYAction); // Data manipulation menu QMenu* dataManipulationMenu = new QMenu(i18n("Data Manipulation")); dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw")); dataManipulationMenu->addAction(addDataOperationAction); dataManipulationMenu->addAction(addDataReductionAction); // Data fit menu QMenu* dataFitMenu = new QMenu(i18n("Fit")); 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)); //analysis menu dataAnalysisMenu = new QMenu(i18n("Analysis")); dataAnalysisMenu->insertMenu(0, dataManipulationMenu); dataAnalysisMenu->addSeparator(); dataAnalysisMenu->addAction(addDifferentiationAction); dataAnalysisMenu->addAction(addIntegrationAction); dataAnalysisMenu->addSeparator(); dataAnalysisMenu->addAction(addInterpolationAction); dataAnalysisMenu->addAction(addSmoothAction); dataAnalysisMenu->addAction(addFourierFilterAction); dataAnalysisMenu->addSeparator(); dataAnalysisMenu->addMenu(dataFitMenu); //themes menu themeMenu = new QMenu(i18n("Apply Theme")); ThemesWidget* themeWidget = new ThemesWidget(0); connect(themeWidget, SIGNAL(themeSelected(QString)), this, SLOT(loadTheme(QString))); connect(themeWidget, SIGNAL(themeSelected(QString)), themeMenu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(themeWidget); themeMenu->addAction(widgetAction); m_menusInitialized = true; } QMenu* CartesianPlot::createContextMenu() { if (!m_menusInitialized) initMenus(); QMenu* menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); menu->insertMenu(firstAction, addNewMenu); menu->insertMenu(firstAction, zoomMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, themeMenu); menu->insertSeparator(firstAction); return menu; } QMenu* CartesianPlot::analysisMenu() { if (!m_menusInitialized) initMenus(); return dataAnalysisMenu; } /*! Returns an icon to be used in the project explorer. */ QIcon CartesianPlot::icon() const { return QIcon::fromTheme("office-chart-line"); } QVector CartesianPlot::dependsOn() const { //aspects which the plotted data in the worksheet depends on (spreadsheets and later matrices) QVector aspects; for (const auto* curve : children()) { if (curve->xColumn() && dynamic_cast(curve->xColumn()->parentAspect()) ) aspects << curve->xColumn()->parentAspect(); if (curve->yColumn() && dynamic_cast(curve->yColumn()->parentAspect()) ) aspects << curve->yColumn()->parentAspect(); } return aspects; } void CartesianPlot::navigate(CartesianPlot::NavigationOperation op) { if (op == ScaleAuto) scaleAuto(); else if (op == ScaleAutoX) scaleAutoX(); else if (op == ScaleAutoY) scaleAutoY(); else if (op == ZoomIn) zoomIn(); else if (op == ZoomOut) zoomOut(); else if (op == ZoomInX) zoomInX(); else if (op == ZoomOutX) zoomOutX(); else if (op == ZoomInY) zoomInY(); else if (op == ZoomOutY) zoomOutY(); else if (op == ShiftLeftX) shiftLeftX(); else if (op == ShiftRightX) shiftRightX(); else if (op == ShiftUpY) shiftUpY(); else if (op == ShiftDownY) shiftDownY(); } void CartesianPlot::setSuppressDataChangedSignal(bool value) { Q_D(CartesianPlot); d->suppressRetransform = value; } void CartesianPlot::processDropEvent(QDropEvent* event) { PERFTRACE("CartesianPlot::processDropEvent"); const QMimeData* mimeData = event->mimeData(); if (!mimeData) return; //deserialize the mime data to the vector of aspect pointers QByteArray data = mimeData->data(QLatin1String("labplot-dnd")); QVector vec; QDataStream stream(&data, QIODevice::ReadOnly); stream >> vec; QVector columns; for (auto i : vec) { AbstractAspect* aspect = (AbstractAspect*)i; AbstractColumn* column = dynamic_cast(aspect); if (column) columns << column; } //return if there are no columns being dropped. //TODO: extend this later when we allow to drag&drop plots, etc. if (columns.isEmpty()) return; //determine the first column with "x plot designation" as the x-data column for all curves to be created const AbstractColumn* xColumn = nullptr; for (const auto* column : columns) { if (column->plotDesignation() == AbstractColumn::X) { xColumn = column; break; } } //if no column with "x plot designation" is available, use the x-data column of the first curve in the plot, if (xColumn == nullptr) { QVector curves = children(); if (!curves.isEmpty()) xColumn = curves.at(0)->xColumn(); } //use the first dropped column if no column with "x plot designation" nor curves are available if (xColumn == nullptr) xColumn = columns.at(0); //create curves bool curvesAdded = false; for (const auto* column : columns) { if (column == xColumn) continue; XYCurve* curve = new XYCurve(column->name()); curve->suppressRetransform(true); //suppress retransform, all curved will be recalculated at the end curve->setXColumn(xColumn); curve->setYColumn(column); addChild(curve); curve->suppressRetransform(false); curvesAdded = true; } if (curvesAdded) dataChanged(); } bool CartesianPlot::isPanningActive() const { Q_D(const CartesianPlot); return d->panningStarted; } //############################################################################## //################################ getter methods ############################ //############################################################################## BASIC_SHARED_D_READER_IMPL(CartesianPlot, CartesianPlot::RangeType, rangeType, rangeType) BASIC_SHARED_D_READER_IMPL(CartesianPlot, int, rangeLastValues, rangeLastValues) BASIC_SHARED_D_READER_IMPL(CartesianPlot, int, rangeFirstValues, rangeFirstValues) BASIC_SHARED_D_READER_IMPL(CartesianPlot, bool, autoScaleX, autoScaleX) BASIC_SHARED_D_READER_IMPL(CartesianPlot, double, xMin, xMin) BASIC_SHARED_D_READER_IMPL(CartesianPlot, double, xMax, xMax) BASIC_SHARED_D_READER_IMPL(CartesianPlot, CartesianPlot::Scale, xScale, xScale) BASIC_SHARED_D_READER_IMPL(CartesianPlot, bool, xRangeBreakingEnabled, xRangeBreakingEnabled) CLASS_SHARED_D_READER_IMPL(CartesianPlot, CartesianPlot::RangeBreaks, xRangeBreaks, xRangeBreaks) BASIC_SHARED_D_READER_IMPL(CartesianPlot, bool, autoScaleY, autoScaleY) BASIC_SHARED_D_READER_IMPL(CartesianPlot, double, yMin, yMin) BASIC_SHARED_D_READER_IMPL(CartesianPlot, double, yMax, yMax) BASIC_SHARED_D_READER_IMPL(CartesianPlot, CartesianPlot::Scale, yScale, yScale) BASIC_SHARED_D_READER_IMPL(CartesianPlot, bool, yRangeBreakingEnabled, yRangeBreakingEnabled) CLASS_SHARED_D_READER_IMPL(CartesianPlot, CartesianPlot::RangeBreaks, yRangeBreaks, yRangeBreaks) CLASS_SHARED_D_READER_IMPL(CartesianPlot, QString, theme, theme) /*! returns the actual bounding rectangular of the plot area showing data (plot's rectangular minus padding) in plot's coordinates */ QRectF CartesianPlot::dataRect() const { Q_D(const CartesianPlot); return d->dataRect; } CartesianPlot::MouseMode CartesianPlot::mouseMode() const { Q_D(const CartesianPlot); return d->mouseMode; } //############################################################################## //###################### setter methods and undo commands #################### //############################################################################## /*! set the rectangular, defined in scene coordinates */ class CartesianPlotSetRectCmd : public QUndoCommand { public: CartesianPlotSetRectCmd(CartesianPlotPrivate* private_obj, QRectF rect) : m_private(private_obj), m_rect(rect) { setText(i18n("%1: change geometry rect", m_private->name())); }; void redo() override { QRectF tmp = m_private->rect; //TODO: // const double horizontalRatio = m_rect.width() / m_private->rect.width(); // const double verticalRatio = m_rect.height() / m_private->rect.height(); // m_private->q->handleResize(horizontalRatio, verticalRatio, false); m_private->rect = m_rect; m_rect = tmp; m_private->retransform(); emit m_private->q->rectChanged(m_private->rect); }; void undo() override { redo(); } private: CartesianPlotPrivate* m_private; QRectF m_rect; }; void CartesianPlot::setRect(const QRectF& rect) { Q_D(CartesianPlot); if (rect != d->rect) exec(new CartesianPlotSetRectCmd(d, rect)); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetRangeType, CartesianPlot::RangeType, rangeType, rangeChanged); void CartesianPlot::setRangeType(RangeType type) { Q_D(CartesianPlot); if (type != d->rangeType) - exec(new CartesianPlotSetRangeTypeCmd(d, type, i18n("%1: set range type"))); + exec(new CartesianPlotSetRangeTypeCmd(d, type, ki18n("%1: set range type"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetRangeLastValues, int, rangeLastValues, rangeChanged); void CartesianPlot::setRangeLastValues(int values) { Q_D(CartesianPlot); if (values != d->rangeLastValues) - exec(new CartesianPlotSetRangeLastValuesCmd(d, values, i18n("%1: set range"))); + exec(new CartesianPlotSetRangeLastValuesCmd(d, values, ki18n("%1: set range"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetRangeFirstValues, int, rangeFirstValues, rangeChanged); void CartesianPlot::setRangeFirstValues(int values) { Q_D(CartesianPlot); if (values != d->rangeFirstValues) - exec(new CartesianPlotSetRangeFirstValuesCmd(d, values, i18n("%1: set range"))); + exec(new CartesianPlotSetRangeFirstValuesCmd(d, values, ki18n("%1: set range"))); } class CartesianPlotSetAutoScaleXCmd : public QUndoCommand { public: CartesianPlotSetAutoScaleXCmd(CartesianPlotPrivate* private_obj, bool autoScale) : m_private(private_obj), m_autoScale(autoScale), m_autoScaleOld(false), m_minOld(0.0), m_maxOld(0.0) { setText(i18n("%1: change x-range auto scaling", m_private->name())); }; void redo() override { m_autoScaleOld = m_private->autoScaleX; if (m_autoScale) { m_minOld = m_private->xMin; m_maxOld = m_private->xMax; m_private->q->scaleAutoX(); } m_private->autoScaleX = m_autoScale; emit m_private->q->xAutoScaleChanged(m_autoScale); }; void undo() override { if (!m_autoScaleOld) { m_private->xMin = m_minOld; m_private->xMax = m_maxOld; m_private->retransformScales(); } m_private->autoScaleX = m_autoScaleOld; emit m_private->q->xAutoScaleChanged(m_autoScaleOld); } private: CartesianPlotPrivate* m_private; bool m_autoScale; bool m_autoScaleOld; double m_minOld; double m_maxOld; }; void CartesianPlot::setAutoScaleX(bool autoScaleX) { Q_D(CartesianPlot); if (autoScaleX != d->autoScaleX) exec(new CartesianPlotSetAutoScaleXCmd(d, autoScaleX)); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetXMin, double, xMin, retransformScales) void CartesianPlot::setXMin(double xMin) { Q_D(CartesianPlot); if (xMin != d->xMin) - exec(new CartesianPlotSetXMinCmd(d, xMin, i18n("%1: set min x"))); + exec(new CartesianPlotSetXMinCmd(d, xMin, ki18n("%1: set min x"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetXMax, double, xMax, retransformScales) void CartesianPlot::setXMax(double xMax) { Q_D(CartesianPlot); if (xMax != d->xMax) - exec(new CartesianPlotSetXMaxCmd(d, xMax, i18n("%1: set max x"))); + exec(new CartesianPlotSetXMaxCmd(d, xMax, ki18n("%1: set max x"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetXScale, CartesianPlot::Scale, xScale, retransformScales) void CartesianPlot::setXScale(Scale scale) { Q_D(CartesianPlot); if (scale != d->xScale) - exec(new CartesianPlotSetXScaleCmd(d, scale, i18n("%1: set x scale"))); + exec(new CartesianPlotSetXScaleCmd(d, scale, ki18n("%1: set x scale"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetXRangeBreakingEnabled, bool, xRangeBreakingEnabled, retransformScales) void CartesianPlot::setXRangeBreakingEnabled(bool enabled) { Q_D(CartesianPlot); if (enabled != d->xRangeBreakingEnabled) - exec(new CartesianPlotSetXRangeBreakingEnabledCmd(d, enabled, i18n("%1: x-range breaking enabled"))); + exec(new CartesianPlotSetXRangeBreakingEnabledCmd(d, enabled, ki18n("%1: x-range breaking enabled"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetXRangeBreaks, CartesianPlot::RangeBreaks, xRangeBreaks, retransformScales) void CartesianPlot::setXRangeBreaks(const RangeBreaks& breakings) { Q_D(CartesianPlot); - exec(new CartesianPlotSetXRangeBreaksCmd(d, breakings, i18n("%1: x-range breaks changed"))); + exec(new CartesianPlotSetXRangeBreaksCmd(d, breakings, ki18n("%1: x-range breaks changed"))); } class CartesianPlotSetAutoScaleYCmd : public QUndoCommand { public: CartesianPlotSetAutoScaleYCmd(CartesianPlotPrivate* private_obj, bool autoScale) : m_private(private_obj), m_autoScale(autoScale), m_autoScaleOld(false), m_minOld(0.0), m_maxOld(0.0) { setText(i18n("%1: change y-range auto scaling", m_private->name())); }; void redo() override { m_autoScaleOld = m_private->autoScaleY; if (m_autoScale) { m_minOld = m_private->yMin; m_maxOld = m_private->yMax; m_private->q->scaleAutoY(); } m_private->autoScaleY = m_autoScale; emit m_private->q->yAutoScaleChanged(m_autoScale); }; void undo() override { if (!m_autoScaleOld) { m_private->yMin = m_minOld; m_private->yMax = m_maxOld; m_private->retransformScales(); } m_private->autoScaleY = m_autoScaleOld; emit m_private->q->yAutoScaleChanged(m_autoScaleOld); } private: CartesianPlotPrivate* m_private; bool m_autoScale; bool m_autoScaleOld; double m_minOld; double m_maxOld; }; void CartesianPlot::setAutoScaleY(bool autoScaleY) { Q_D(CartesianPlot); if (autoScaleY != d->autoScaleY) exec(new CartesianPlotSetAutoScaleYCmd(d, autoScaleY)); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetYMin, double, yMin, retransformScales) void CartesianPlot::setYMin(double yMin) { Q_D(CartesianPlot); if (yMin != d->yMin) - exec(new CartesianPlotSetYMinCmd(d, yMin, i18n("%1: set min y"))); + exec(new CartesianPlotSetYMinCmd(d, yMin, ki18n("%1: set min y"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetYMax, double, yMax, retransformScales) void CartesianPlot::setYMax(double yMax) { Q_D(CartesianPlot); if (yMax != d->yMax) - exec(new CartesianPlotSetYMaxCmd(d, yMax, i18n("%1: set max y"))); + exec(new CartesianPlotSetYMaxCmd(d, yMax, ki18n("%1: set max y"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetYScale, CartesianPlot::Scale, yScale, retransformScales) void CartesianPlot::setYScale(Scale scale) { Q_D(CartesianPlot); if (scale != d->yScale) - exec(new CartesianPlotSetYScaleCmd(d, scale, i18n("%1: set y scale"))); + exec(new CartesianPlotSetYScaleCmd(d, scale, ki18n("%1: set y scale"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetYRangeBreakingEnabled, bool, yRangeBreakingEnabled, retransformScales) void CartesianPlot::setYRangeBreakingEnabled(bool enabled) { Q_D(CartesianPlot); if (enabled != d->yRangeBreakingEnabled) - exec(new CartesianPlotSetYRangeBreakingEnabledCmd(d, enabled, i18n("%1: y-range breaking enabled"))); + exec(new CartesianPlotSetYRangeBreakingEnabledCmd(d, enabled, ki18n("%1: y-range breaking enabled"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlot, SetYRangeBreaks, CartesianPlot::RangeBreaks, yRangeBreaks, retransformScales) void CartesianPlot::setYRangeBreaks(const RangeBreaks& breaks) { Q_D(CartesianPlot); - exec(new CartesianPlotSetYRangeBreaksCmd(d, breaks, i18n("%1: y-range breaks changed"))); + exec(new CartesianPlotSetYRangeBreaksCmd(d, breaks, ki18n("%1: y-range breaks changed"))); } STD_SETTER_CMD_IMPL_S(CartesianPlot, SetTheme, QString, theme) void CartesianPlot::setTheme(const QString& theme) { Q_D(CartesianPlot); if (theme != d->theme) { if (!theme.isEmpty()) { beginMacro( i18n("%1: load theme %2", name(), theme) ); - exec(new CartesianPlotSetThemeCmd(d, theme, i18n("%1: set theme"))); + exec(new CartesianPlotSetThemeCmd(d, theme, ki18n("%1: set theme"))); loadTheme(theme); endMacro(); } else - exec(new CartesianPlotSetThemeCmd(d, theme, i18n("%1: disable theming"))); + exec(new CartesianPlotSetThemeCmd(d, theme, ki18n("%1: disable theming"))); } } //################################################################ //########################## Slots ############################### //################################################################ void CartesianPlot::addHorizontalAxis() { Axis* axis = new Axis("x-axis", Axis::AxisHorizontal); if (axis->autoScale()) { axis->setUndoAware(false); axis->setStart(xMin()); axis->setEnd(xMax()); axis->setUndoAware(true); } addChild(axis); } void CartesianPlot::addVerticalAxis() { Axis* axis = new Axis("y-axis", Axis::AxisVertical); if (axis->autoScale()) { axis->setUndoAware(false); axis->setStart(yMin()); axis->setEnd(yMax()); axis->setUndoAware(true); } addChild(axis); } void CartesianPlot::addCurve() { addChild(new XYCurve("xy-curve")); } void CartesianPlot::addEquationCurve() { addChild(new XYEquationCurve("f(x)")); } void CartesianPlot::addHistogram() { addChild(new Histogram("Histogram")); } /*! * returns the first selected XYCurve in the plot */ const XYCurve* CartesianPlot::currentCurve() const { for (const auto* curve: this->children()) { if (curve->graphicsItem()->isSelected()) return curve; } return 0; } void CartesianPlot::addDataReductionCurve() { XYDataReductionCurve* curve = new XYDataReductionCurve("Data reduction"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: reduce '%2'", name(), curCurve->name()) ); curve->setName( i18n("Reduction of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); this->addChild(curve); curve->recalculate(); emit curve->dataReductionDataChanged(curve->dataReductionData()); } else { beginMacro(i18n("%1: add data reduction curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addDifferentiationCurve() { XYDifferentiationCurve* curve = new XYDifferentiationCurve("Differentiation"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: differentiate '%2'", name(), curCurve->name()) ); curve->setName( i18n("Derivative of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); this->addChild(curve); curve->recalculate(); emit curve->differentiationDataChanged(curve->differentiationData()); } else { beginMacro(i18n("%1: add differentiation curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addIntegrationCurve() { XYIntegrationCurve* curve = new XYIntegrationCurve("Integration"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: integrate '%2'", name(), curCurve->name()) ); curve->setName( i18n("Integral of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); this->addChild(curve); curve->recalculate(); emit curve->integrationDataChanged(curve->integrationData()); } else { beginMacro(i18n("%1: add differentiation curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addInterpolationCurve() { XYInterpolationCurve* curve = new XYInterpolationCurve("Interpolation"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: interpolate '%2'", name(), curCurve->name()) ); curve->setName( i18n("Interpolation of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); curve->recalculate(); this->addChild(curve); emit curve->interpolationDataChanged(curve->interpolationData()); } else { beginMacro(i18n("%1: add interpolation curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addSmoothCurve() { XYSmoothCurve* curve = new XYSmoothCurve("Smooth"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: smooth '%2'", name(), curCurve->name()) ); curve->setName( i18n("Smoothing of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); this->addChild(curve); curve->recalculate(); emit curve->smoothDataChanged(curve->smoothData()); } else { beginMacro(i18n("%1: add smoothing curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addFitCurve() { DEBUG("CartesianPlot::addFitCurve()"); XYFitCurve* curve = new XYFitCurve("fit"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: fit to '%2'", name(), curCurve->name()) ); curve->setName( i18n("Fit to '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); //set the fit model category and type const QAction* action = qobject_cast(QObject::sender()); PlotDataDialog::AnalysisAction type = (PlotDataDialog::AnalysisAction)action->data().toInt(); curve->initFitData(type); curve->initStartValues(curCurve); //fit with weights for y if the curve has error bars for y if (curCurve->yErrorType() == XYCurve::SymmetricError && curCurve->yErrorPlusColumn()) { XYFitCurve::FitData fitData = curve->fitData(); fitData.yWeightsType = nsl_fit_weight_instrumental; curve->setFitData(fitData); curve->setYErrorColumn(curCurve->yErrorPlusColumn()); } this->addChild(curve); curve->recalculate(); emit curve->fitDataChanged(curve->fitData()); } else { beginMacro(i18n("%1: add fit curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addFourierFilterCurve() { XYFourierFilterCurve* curve = new XYFourierFilterCurve("Fourier filter"); const XYCurve* curCurve = currentCurve(); if (curCurve) { beginMacro( i18n("%1: Fourier filtering of '%2'", name(), curCurve->name()) ); curve->setName( i18n("Fourier filtering of '%1'", curCurve->name()) ); curve->setDataSourceType(XYAnalysisCurve::DataSourceCurve); curve->setDataSourceCurve(curCurve); this->addChild(curve); } else { beginMacro(i18n("%1: add Fourier filter curve", name())); this->addChild(curve); } endMacro(); } void CartesianPlot::addFourierTransformCurve() { XYFourierTransformCurve* curve = new XYFourierTransformCurve("Fourier transform"); this->addChild(curve); } +/*! + * public helper function to set a legend object created outside of CartesianPlot, e.g. in \c OriginProjectParser. + */ +void CartesianPlot::addLegend(CartesianPlotLegend* legend) { + m_legend = legend; + this->addChild(legend); +} + void CartesianPlot::addLegend() { //don't do anything if there's already a legend if (m_legend) return; m_legend = new CartesianPlotLegend(this, "legend"); this->addChild(m_legend); m_legend->retransform(); //only one legend is allowed -> disable the action if (m_menusInitialized) addLegendAction->setEnabled(false); } void CartesianPlot::addTextLabel() { TextLabel* label = new TextLabel("text label"); this->addChild(label); label->setParentGraphicsItem(graphicsItem()); } void CartesianPlot::addCustomPoint() { CustomPoint* point = new CustomPoint(this, "custom point"); this->addChild(point); } void CartesianPlot::childAdded(const AbstractAspect* child) { Q_D(CartesianPlot); const XYCurve* curve = qobject_cast(child); if (curve) { connect(curve, SIGNAL(dataChanged()), this, SLOT(dataChanged())); connect(curve, SIGNAL(xDataChanged()), this, SLOT(xDataChanged())); connect(curve, SIGNAL(yDataChanged()), this, SLOT(yDataChanged())); connect(curve, SIGNAL(visibilityChanged(bool)), this, SLOT(curveVisibilityChanged())); //update the legend on changes of the name, line and symbol styles connect(curve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(updateLegend())); connect(curve, SIGNAL(lineTypeChanged(XYCurve::LineType)), this, SLOT(updateLegend())); connect(curve, SIGNAL(linePenChanged(QPen)), this, SLOT(updateLegend())); connect(curve, SIGNAL(lineOpacityChanged(qreal)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsStyleChanged(Symbol::Style)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsSizeChanged(qreal)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsRotationAngleChanged(qreal)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsOpacityChanged(qreal)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsBrushChanged(QBrush)), this, SLOT(updateLegend())); connect(curve, SIGNAL(symbolsPenChanged(QPen)), this, SLOT(updateLegend())); updateLegend(); d->curvesXMinMaxIsDirty = true; d->curvesYMinMaxIsDirty = true; } else { const Histogram* histo = qobject_cast(child); if (histo) { connect(histo, SIGNAL(HistogramdataChanged()), this, SLOT(HistogramdataChanged())); connect(histo, SIGNAL(xHistogramDataChanged()), this, SLOT(xHistogramDataChanged())); connect(histo, SIGNAL(yHistogramDataChanged()), this, SLOT(yHistogramDataChanged())); connect(histo, SIGNAL(visibilityChanged(bool)), this, SLOT(curveVisibilityChanged())); } } if (!isLoading()) { //if a theme was selected, apply the theme settings for newly added children, too if (!d->theme.isEmpty()) { const WorksheetElement* el = dynamic_cast(child); if (el) { KConfig config(ThemeHandler::themeFilePath(d->theme), KConfig::SimpleConfig); const_cast(el)->loadThemeConfig(config); } } else { //no theme is available, apply the default colors for curves only, s.a. XYCurve::loadThemeConfig() const XYCurve* curve = dynamic_cast(child); if (curve) { int index = indexOfChild(curve); QColor themeColor; if (index < m_themeColorPalette.size()) themeColor = m_themeColorPalette.at(index); else { if (m_themeColorPalette.size()) themeColor = m_themeColorPalette.last(); } XYCurve* c = const_cast(curve); //Line QPen p = curve->linePen(); p.setColor(themeColor); c->setLinePen(p); //Drop line p = curve->dropLinePen(); p.setColor(themeColor); c->setDropLinePen(p); //Symbol QBrush brush = c->symbolsBrush(); brush.setColor(themeColor); c->setSymbolsBrush(brush); p = c->symbolsPen(); p.setColor(themeColor); c->setSymbolsPen(p); //Filling c->setFillingFirstColor(themeColor); //Error bars p.setColor(themeColor); c->setErrorBarsPen(p); } } } } void CartesianPlot::childRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child) { Q_UNUSED(parent); Q_UNUSED(before); if (m_legend == child) { if (m_menusInitialized) addLegendAction->setEnabled(true); m_legend = nullptr; } else { const XYCurve* curve = qobject_cast(child); if (curve) updateLegend(); } } void CartesianPlot::updateLegend() { if (m_legend) m_legend->retransform(); } /*! called when in one of the curves the data was changed. Autoscales the coordinate system and the x-axes, when "auto-scale" is active. */ void CartesianPlot::dataChanged() { Q_D(CartesianPlot); d->curvesXMinMaxIsDirty = true; d->curvesYMinMaxIsDirty = true; if (d->autoScaleX && d->autoScaleY) this->scaleAuto(); else if (d->autoScaleX) this->scaleAutoX(); else if (d->autoScaleY) this->scaleAutoY(); else { //free ranges -> rentransform the curve that sent XYCurve* curve = dynamic_cast(QObject::sender()); if (curve) curve->retransform(); else { //no sender available, the function was called in CartesianPlot::dataChanged() (live data source got new data) //-> retransform all available curves since we don't know which curves are affected. //TODO: this logic can be very expensive for (auto c : children()) c->retransform(); } } } void CartesianPlot::HistogramdataChanged() { Q_D(CartesianPlot); d->curvesXMinMaxIsDirty = true; d->curvesYMinMaxIsDirty = true; if (d->autoScaleX && d->autoScaleY) this->scaleAuto(); else if (d->autoScaleX) this->scaleAutoY(); else if (d->autoScaleY) this->scaleAutoY(); else { Histogram* curve = dynamic_cast(QObject::sender()); if (curve) curve->retransform(); else { //no sender available, the function was called in CartesianPlot::dataChanged() (live data source got new data) //-> retransform all available curves since we don't know which curves are affected. //TODO: this logic can be very expensive for (auto c : children()) c->retransform(); } } } /*! called when in one of the curves the x-data was changed. Autoscales the coordinate system and the x-axes, when "auto-scale" is active. */ void CartesianPlot::xDataChanged() { if (project()->isLoading()) return; Q_D(CartesianPlot); if (d->suppressRetransform) return; d->curvesXMinMaxIsDirty = true; if (d->autoScaleX) this->scaleAutoX(); else { XYCurve* curve = dynamic_cast(QObject::sender()); curve->retransform(); } } void CartesianPlot::xHistogramDataChanged() { if (project()->isLoading()) return; Q_D(CartesianPlot); if (d->suppressRetransform) return; d->curvesXMinMaxIsDirty = true; if (d->autoScaleX) this->scaleAutoX(); else { Histogram* curve = dynamic_cast(QObject::sender()); curve->retransform(); } } /*! called when in one of the curves the x-data was changed. Autoscales the coordinate system and the x-axes, when "auto-scale" is active. */ void CartesianPlot::yDataChanged() { if (project()->isLoading()) return; Q_D(CartesianPlot); if (d->suppressRetransform) return; d->curvesYMinMaxIsDirty = true; if (d->autoScaleY) this->scaleAutoY(); else { XYCurve* curve = dynamic_cast(QObject::sender()); curve->retransform(); } } void CartesianPlot::yHistogramDataChanged() { if (project()->isLoading()) return; Q_D(CartesianPlot); if (d->suppressRetransform) return; d->curvesYMinMaxIsDirty = true; if (d->autoScaleY) this->scaleAutoY(); else { Histogram* curve = dynamic_cast(QObject::sender()); curve->retransform(); } } void CartesianPlot::curveVisibilityChanged() { Q_D(CartesianPlot); d->curvesXMinMaxIsDirty = true; d->curvesYMinMaxIsDirty = true; updateLegend(); if (d->autoScaleX && d->autoScaleY) this->scaleAuto(); else if (d->autoScaleX) this->scaleAutoX(); else if (d->autoScaleY) this->scaleAutoY(); } void CartesianPlot::setMouseMode(const MouseMode mouseMode) { Q_D(CartesianPlot); d->mouseMode = mouseMode; d->setHandlesChildEvents(mouseMode != CartesianPlot::SelectionMode); QList items = d->childItems(); if (d->mouseMode == CartesianPlot::SelectionMode) { for (auto* item: items) item->setFlag(QGraphicsItem::ItemStacksBehindParent, false); } else { for (auto* item: items) item->setFlag(QGraphicsItem::ItemStacksBehindParent, true); } //when doing zoom selection, prevent the graphics item from being movable //if it's currently movable (no worksheet layout available) const Worksheet* worksheet = dynamic_cast(parentAspect()); if (worksheet) { if (mouseMode == CartesianPlot::SelectionMode) { if (worksheet->layout() != Worksheet::NoLayout) graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); else graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); } else //zoom m_selection graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); } } void CartesianPlot::scaleAutoX() { Q_D(CartesianPlot); if (d->curvesXMinMaxIsDirty) { int count = 0; switch (d->rangeType) { case CartesianPlot::RangeFree: count = 0; break; case CartesianPlot::RangeLast: count = -d->rangeLastValues; break; case CartesianPlot::RangeFirst: count = d->rangeFirstValues; break; } d->curvesXMin = INFINITY; d->curvesXMax = -INFINITY; //loop over all xy-curves and determine the maximum and minimum x-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->xColumn()) continue; const double min = curve->xColumn()->minimum(count); if (min < d->curvesXMin) d->curvesXMin = min; const double max = curve->xColumn()->maximum(count); if (max > d->curvesXMax) d->curvesXMax = max; } //loop over all histograms and determine the maximum and minimum x-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->xColumn()) continue; const double min = curve->xColumn()->minimum(count); if (min < d->curvesXMin) d->curvesXMin = min; const double max = curve->xColumn()->maximum(count); if (max > d->curvesXMax) d->curvesXMax = max; } d->curvesXMinMaxIsDirty = false; } bool update = false; if (d->curvesXMin != d->xMin && d->curvesXMin != INFINITY) { d->xMin = d->curvesXMin; update = true; } if (d->curvesXMax != d->xMax && d->curvesXMax != -INFINITY) { d->xMax = d->curvesXMax; update = true; } if (update) { if (d->xMax == d->xMin) { //in case min and max are equal (e.g. if we plot a single point), subtract/add 10% of the value if (d->xMax != 0) { d->xMax = d->xMax*1.1; d->xMin = d->xMin*0.9; } else { d->xMax = 0.1; d->xMin = -0.1; } } else { double offset = (d->xMax - d->xMin)*d->autoScaleOffsetFactor; d->xMin -= offset; d->xMax += offset; } d->retransformScales(); } } void CartesianPlot::scaleAutoY() { Q_D(CartesianPlot); if (d->curvesYMinMaxIsDirty) { int count = 0; switch (d->rangeType) { case CartesianPlot::RangeFree: count = 0; break; case CartesianPlot::RangeLast: count = -d->rangeLastValues; break; case CartesianPlot::RangeFirst: count = d->rangeFirstValues; break; } d->curvesYMin = INFINITY; d->curvesYMax = -INFINITY; //loop over all xy-curves and determine the maximum and minimum y-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->yColumn()) continue; const double min = curve->yColumn()->minimum(count); if (min < d->curvesYMin) d->curvesYMin = min; const double max = curve->yColumn()->maximum(count); if (max > d->curvesYMax) d->curvesYMax = max; } //loop over all histograms and determine the maximum y-value for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (d->curvesYMin > 0.0) d->curvesYMin = 0.0; if ( curve->getYMaximum() > d->curvesYMax) d->curvesYMax = curve->getYMaximum(); } d->curvesYMinMaxIsDirty = false; } bool update = false; if (d->curvesYMin != d->yMin && d->curvesYMin != INFINITY) { d->yMin = d->curvesYMin; update = true; } if (d->curvesYMax != d->yMax && d->curvesYMax != -INFINITY) { d->yMax = d->curvesYMax; update = true; } if (update) { if (d->yMax == d->yMin) { //in case min and max are equal (e.g. if we plot a single point), subtract/add 10% of the value if (d->yMax != 0) { d->yMax = d->yMax*1.1; d->yMin = d->yMin*0.9; } else { d->yMax = 0.1; d->yMin = -0.1; } } else { double offset = (d->yMax - d->yMin)*d->autoScaleOffsetFactor; d->yMin -= offset; d->yMax += offset; } d->retransformScales(); } } void CartesianPlot::scaleAuto() { DEBUG("CartesianPlot::scaleAuto()"); Q_D(CartesianPlot); int count = 0; switch (d->rangeType) { case CartesianPlot::RangeFree: count = 0; break; case CartesianPlot::RangeLast: count = -d->rangeLastValues; break; case CartesianPlot::RangeFirst: count = d->rangeFirstValues; break; } if (d->curvesXMinMaxIsDirty) { d->curvesXMin = INFINITY; d->curvesXMax = -INFINITY; //loop over all xy-curves and determine the maximum and minimum x-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->xColumn()) continue; const double min = curve->xColumn()->minimum(count); if (min < d->curvesXMin) d->curvesXMin = min; double max = curve->xColumn()->maximum(count); if (max > d->curvesXMax) d->curvesXMax = max; } //loop over all histograms and determine the maximum and minimum x-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->xColumn()) continue; const double min = curve->xColumn()->minimum(count); if (min < d->curvesXMin) d->curvesXMin = min; const double max = curve->xColumn()->maximum(count); if (max > d->curvesXMax) d->curvesXMax = max; } d->curvesXMinMaxIsDirty = false; } if (d->curvesYMinMaxIsDirty) { d->curvesYMin = INFINITY; d->curvesYMax = -INFINITY; //loop over all xy-curves and determine the maximum and minimum y-values for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (!curve->yColumn()) continue; const double min = curve->yColumn()->minimum(count); if (min < d->curvesYMin) d->curvesYMin = min; const double max = curve->yColumn()->maximum(count); if (max > d->curvesYMax) d->curvesYMax = max; } //loop over all histograms and determine the maximum y-value for (const auto* curve: this->children()) { if (!curve->isVisible()) continue; if (d->curvesYMin > 0.0) d->curvesYMin = 0.0; const double max = curve->getYMaximum(); if (max > d->curvesYMax) d->curvesYMax = max; } } bool updateX = false; bool updateY = false; if (d->curvesXMin != d->xMin && d->curvesXMin != INFINITY) { d->xMin = d->curvesXMin; updateX = true; } if (d->curvesXMax != d->xMax && d->curvesXMax != -INFINITY) { d->xMax = d->curvesXMax; updateX = true; } if (d->curvesYMin != d->yMin && d->curvesYMin != INFINITY) { d->yMin = d->curvesYMin; updateY = true; } if (d->curvesYMax != d->yMax && d->curvesYMax != -INFINITY) { d->yMax = d->curvesYMax; updateY = true; } DEBUG(" xmin/xmax = " << d->xMin << '/' << d->xMax << ", ymin/ymax = " << d->yMin << '/' << d->yMax); if (updateX || updateY) { if (updateX) { if (d->xMax == d->xMin) { //in case min and max are equal (e.g. if we plot a single point), subtract/add 10% of the value if (d->xMax != 0) { d->xMax = d->xMax*1.1; d->xMin = d->xMin*0.9; } else { d->xMax = 0.1; d->xMin = -0.1; } } else { double offset = (d->xMax - d->xMin)*d->autoScaleOffsetFactor; d->xMin -= offset; d->xMax += offset; } } if (updateY) { if (d->yMax == d->yMin) { //in case min and max are equal (e.g. if we plot a single point), subtract/add 10% of the value if (d->yMax != 0) { d->yMax = d->yMax*1.1; d->yMin = d->yMin*0.9; } else { d->yMax = 0.1; d->yMin = -0.1; } } else { double offset = (d->yMax - d->yMin)*d->autoScaleOffsetFactor; d->yMin -= offset; d->yMax += offset; } } d->retransformScales(); } } void CartesianPlot::zoomIn() { DEBUG("CartesianPlot::zoomIn()"); Q_D(CartesianPlot); double oldRange = (d->xMax - d->xMin); double newRange = (d->xMax - d->xMin) / m_zoomFactor; d->xMax = d->xMax + (newRange - oldRange) / 2; d->xMin = d->xMin - (newRange - oldRange) / 2; oldRange = (d->yMax - d->yMin); newRange = (d->yMax - d->yMin) / m_zoomFactor; d->yMax = d->yMax + (newRange - oldRange) / 2; d->yMin = d->yMin - (newRange - oldRange) / 2; d->retransformScales(); } void CartesianPlot::zoomOut() { Q_D(CartesianPlot); double oldRange = (d->xMax-d->xMin); double newRange = (d->xMax-d->xMin)*m_zoomFactor; d->xMax = d->xMax + (newRange-oldRange)/2; d->xMin = d->xMin - (newRange-oldRange)/2; oldRange = (d->yMax-d->yMin); newRange = (d->yMax-d->yMin)*m_zoomFactor; d->yMax = d->yMax + (newRange-oldRange)/2; d->yMin = d->yMin - (newRange-oldRange)/2; d->retransformScales(); } void CartesianPlot::zoomInX() { Q_D(CartesianPlot); double oldRange = (d->xMax-d->xMin); double newRange = (d->xMax-d->xMin)/m_zoomFactor; d->xMax = d->xMax + (newRange-oldRange)/2; d->xMin = d->xMin - (newRange-oldRange)/2; d->retransformScales(); } void CartesianPlot::zoomOutX() { Q_D(CartesianPlot); double oldRange = (d->xMax-d->xMin); double newRange = (d->xMax-d->xMin)*m_zoomFactor; d->xMax = d->xMax + (newRange-oldRange)/2; d->xMin = d->xMin - (newRange-oldRange)/2; d->retransformScales(); } void CartesianPlot::zoomInY() { Q_D(CartesianPlot); double oldRange = (d->yMax-d->yMin); double newRange = (d->yMax-d->yMin)/m_zoomFactor; d->yMax = d->yMax + (newRange-oldRange)/2; d->yMin = d->yMin - (newRange-oldRange)/2; d->retransformScales(); } void CartesianPlot::zoomOutY() { Q_D(CartesianPlot); double oldRange = (d->yMax-d->yMin); double newRange = (d->yMax-d->yMin)*m_zoomFactor; d->yMax = d->yMax + (newRange-oldRange)/2; d->yMin = d->yMin - (newRange-oldRange)/2; d->retransformScales(); } void CartesianPlot::shiftLeftX() { Q_D(CartesianPlot); double offsetX = (d->xMax-d->xMin)*0.1; d->xMax -= offsetX; d->xMin -= offsetX; d->retransformScales(); } void CartesianPlot::shiftRightX() { Q_D(CartesianPlot); double offsetX = (d->xMax-d->xMin)*0.1; d->xMax += offsetX; d->xMin += offsetX; d->retransformScales(); } void CartesianPlot::shiftUpY() { Q_D(CartesianPlot); double offsetY = (d->yMax-d->yMin)*0.1; d->yMax += offsetY; d->yMin += offsetY; d->retransformScales(); } void CartesianPlot::shiftDownY() { Q_D(CartesianPlot); double offsetY = (d->yMax-d->yMin)*0.1; d->yMax -= offsetY; d->yMin -= offsetY; d->retransformScales(); } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void CartesianPlot::visibilityChanged() { Q_D(CartesianPlot); this->setVisible(!d->isVisible()); } //##################################################################### //################### Private implementation ########################## //##################################################################### CartesianPlotPrivate::CartesianPlotPrivate(CartesianPlot* plot) : AbstractPlotPrivate(plot), curvesXMinMaxIsDirty(false), curvesYMinMaxIsDirty(false), curvesXMin(INFINITY), curvesXMax(-INFINITY), curvesYMin(INFINITY), curvesYMax(-INFINITY), q(plot), mouseMode(CartesianPlot::SelectionMode), cSystem(nullptr), suppressRetransform(false), panningStarted(false), m_selectionBandIsShown(false) { setData(0, WorksheetElement::NameCartesianPlot); } /*! updates the position of plot rectangular in scene coordinates to \c r and recalculates the scales. The size of the plot corresponds to the size of the plot area, the area which is filled with the background color etc. and which can pose the parent item for several sub-items (like TextLabel). Note, the size of the area used to define the coordinate system doesn't need to be equal to this plot area. Also, the size (=bounding box) of CartesianPlot can be greater than the size of the plot area. */ void CartesianPlotPrivate::retransform() { if (suppressRetransform) return; PERFTRACE("CartesianPlotPrivate::retransform()"); prepareGeometryChange(); setPos( rect.x()+rect.width()/2, rect.y()+rect.height()/2); updateDataRect(); retransformScales(); //plotArea position is always (0, 0) in parent's coordinates, don't need to update here q->plotArea()->setRect(rect); //call retransform() for the title and the legend (if available) //when a predefined position relative to the (Left, Centered etc.) is used, //the actual position needs to be updated on plot's geometry changes. if (q->title()) q->title()->retransform(); if (q->m_legend) q->m_legend->retransform(); WorksheetElementContainerPrivate::recalcShapeAndBoundingRect(); } void CartesianPlotPrivate::retransformScales() { PERFTRACE("CartesianPlotPrivate::retransformScales()"); DEBUG(" xmin/xmax = " << xMin << '/'<< xMax << ", ymin/ymax = " << yMin << '/' << yMax); CartesianPlot* plot = dynamic_cast(q); QVector scales; //perform the mapping from the scene coordinates to the plot's coordinates here. QRectF itemRect = mapRectFromScene(rect); //check ranges for log-scales if (xScale != CartesianPlot::ScaleLinear) checkXRange(); //check whether we have x-range breaks - the first break, if available, should be valid bool hasValidBreak = (xRangeBreakingEnabled && !xRangeBreaks.list.isEmpty() && xRangeBreaks.list.first().isValid()); static const int breakGap = 20; double sceneStart, sceneEnd, logicalStart, logicalEnd; //create x-scales int plotSceneStart = itemRect.x() + horizontalPadding; int plotSceneEnd = itemRect.x() + itemRect.width() - horizontalPadding; if (!hasValidBreak) { //no breaks available -> range goes from the plot beginning to the end of the plot sceneStart = plotSceneStart; sceneEnd = plotSceneEnd; logicalStart = xMin; logicalEnd = xMax; //TODO: how should we handle the case sceneStart == sceneEnd? //(to reproduce, create plots and adjust the spacing/pading to get zero size for the plots) if (sceneStart != sceneEnd) scales << this->createScale(xScale, sceneStart, sceneEnd, logicalStart, logicalEnd); } else { int sceneEndLast = plotSceneStart; int logicalEndLast = xMin; for (const auto& rb: xRangeBreaks.list) { if (!rb.isValid()) break; //current range goes from the end of the previous one (or from the plot beginning) to curBreak.start sceneStart = sceneEndLast; if (&rb == &xRangeBreaks.list.first()) sceneStart += breakGap; sceneEnd = plotSceneStart + (plotSceneEnd-plotSceneStart) * rb.position; logicalStart = logicalEndLast; logicalEnd = rb.start; if (sceneStart != sceneEnd) scales << this->createScale(xScale, sceneStart, sceneEnd, logicalStart, logicalEnd); sceneEndLast = sceneEnd; logicalEndLast = rb.end; } //add the remaining range going from the last available range break to the end of the plot (=end of the x-data range) sceneStart = sceneEndLast+breakGap; sceneEnd = plotSceneEnd; logicalStart = logicalEndLast; logicalEnd = xMax; if (sceneStart != sceneEnd) scales << this->createScale(xScale, sceneStart, sceneEnd, logicalStart, logicalEnd); } cSystem->setXScales(scales); //check ranges for log-scales if (yScale != CartesianPlot::ScaleLinear) checkYRange(); //check whether we have y-range breaks - the first break, if available, should be valid hasValidBreak = (yRangeBreakingEnabled && !yRangeBreaks.list.isEmpty() && yRangeBreaks.list.first().isValid()); //create y-scales scales.clear(); plotSceneStart = itemRect.y()+itemRect.height()-verticalPadding; plotSceneEnd = itemRect.y()+verticalPadding; if (!hasValidBreak) { //no breaks available -> range goes from the plot beginning to the end of the plot sceneStart = plotSceneStart; sceneEnd = plotSceneEnd; logicalStart = yMin; logicalEnd = yMax; if (sceneStart != sceneEnd) scales << this->createScale(yScale, sceneStart, sceneEnd, logicalStart, logicalEnd); } else { int sceneEndLast = plotSceneStart; int logicalEndLast = yMin; for (const auto& rb: yRangeBreaks.list) { if (!rb.isValid()) break; //current range goes from the end of the previous one (or from the plot beginning) to curBreak.start sceneStart = sceneEndLast; if (&rb == &yRangeBreaks.list.first()) sceneStart -= breakGap; sceneEnd = plotSceneStart + (plotSceneEnd-plotSceneStart) * rb.position; logicalStart = logicalEndLast; logicalEnd = rb.start; if (sceneStart != sceneEnd) scales << this->createScale(yScale, sceneStart, sceneEnd, logicalStart, logicalEnd); sceneEndLast = sceneEnd; logicalEndLast = rb.end; } //add the remaining range going from the last available range break to the end of the plot (=end of the y-data range) sceneStart = sceneEndLast-breakGap; sceneEnd = plotSceneEnd; logicalStart = logicalEndLast; logicalEnd = yMax; if (sceneStart != sceneEnd) scales << this->createScale(yScale, sceneStart, sceneEnd, logicalStart, logicalEnd); } cSystem->setYScales(scales); //calculate the changes in x and y and save the current values for xMin, xMax, yMin, yMax double deltaXMin = 0; double deltaXMax = 0; double deltaYMin = 0; double deltaYMax = 0; if (xMin != xMinPrev) { deltaXMin = xMin - xMinPrev; emit plot->xMinChanged(xMin); } if (xMax != xMaxPrev) { deltaXMax = xMax - xMaxPrev; emit plot->xMaxChanged(xMax); } if (yMin != yMinPrev) { deltaYMin = yMin - yMinPrev; emit plot->yMinChanged(yMin); } if (yMax!=yMaxPrev) { deltaYMax = yMax - yMaxPrev; emit plot->yMaxChanged(yMax); } xMinPrev = xMin; xMaxPrev = xMax; yMinPrev = yMin; yMaxPrev = yMax; //adjust auto-scale axes for (auto* axis: q->children()) { if (!axis->autoScale()) continue; if (axis->orientation() == Axis::AxisHorizontal) { if (deltaXMax != 0) { axis->setUndoAware(false); axis->setSuppressRetransform(true); axis->setEnd(xMax); axis->setUndoAware(true); axis->setSuppressRetransform(false); } if (deltaXMin != 0) { axis->setUndoAware(false); axis->setSuppressRetransform(true); axis->setStart(xMin); axis->setUndoAware(true); axis->setSuppressRetransform(false); } //TODO; // if (axis->position() == Axis::AxisCustom && deltaYMin != 0) { // axis->setOffset(axis->offset() + deltaYMin, false); // } } else { if (deltaYMax != 0) { axis->setUndoAware(false); axis->setSuppressRetransform(true); axis->setEnd(yMax); axis->setUndoAware(true); axis->setSuppressRetransform(false); } if (deltaYMin != 0) { axis->setUndoAware(false); axis->setSuppressRetransform(true); axis->setStart(yMin); axis->setUndoAware(true); axis->setSuppressRetransform(false); } //TODO; // if (axis->position() == Axis::AxisCustom && deltaXMin != 0) { // axis->setOffset(axis->offset() + deltaXMin, false); // } } } // call retransform() on the parent to trigger the update of all axes and curvesю //no need to do this on load since all plots are retransformed again after the project is loaded. if (!q->isLoading()) q->retransform(); } /* * calculates the rectangular of the are showing the actual data (plot's rect minus padding), * in plot's coordinates. */ void CartesianPlotPrivate::updateDataRect() { dataRect = mapRectFromScene(rect); dataRect.setX(dataRect.x() + horizontalPadding); dataRect.setY(dataRect.y() + verticalPadding); dataRect.setWidth(dataRect.width() - horizontalPadding); dataRect.setHeight(dataRect.height() - verticalPadding); } void CartesianPlotPrivate::rangeChanged() { curvesXMinMaxIsDirty = true; curvesYMinMaxIsDirty = true; if (autoScaleX && autoScaleY) q->scaleAuto(); else if (autoScaleX) q->scaleAutoX(); else if (autoScaleY) q->scaleAutoY(); } /*! * don't allow any negative values for the x range when log or sqrt scalings are used */ void CartesianPlotPrivate::checkXRange() { double min = 0.01; if (xMin <= 0.0) { (min < xMax*min) ? xMin = min : xMin = xMax*min; emit q->xMinChanged(xMin); } else if (xMax <= 0.0) { (-min > xMin*min) ? xMax = -min : xMax = xMin*min; emit q->xMaxChanged(xMax); } } /*! * don't allow any negative values for the y range when log or sqrt scalings are used */ void CartesianPlotPrivate::checkYRange() { double min = 0.01; if (yMin <= 0.0) { (min < yMax*min) ? yMin = min : yMin = yMax*min; emit q->yMinChanged(yMin); } else if (yMax <= 0.0) { (-min > yMin*min) ? yMax = -min : yMax = yMin*min; emit q->yMaxChanged(yMax); } } CartesianScale* CartesianPlotPrivate::createScale(CartesianPlot::Scale type, double sceneStart, double sceneEnd, double logicalStart, double logicalEnd) { DEBUG("CartesianPlotPrivate::createScale() scence start/end = " << sceneStart << '/' << sceneEnd << ", logical start/end = " << logicalStart << '/' << logicalEnd); // Interval interval (logicalStart-0.01, logicalEnd+0.01); //TODO: move this to CartesianScale Interval interval (std::numeric_limits::lowest(), std::numeric_limits::max()); // Interval interval (logicalStart, logicalEnd); if (type == CartesianPlot::ScaleLinear) return CartesianScale::createLinearScale(interval, sceneStart, sceneEnd, logicalStart, logicalEnd); else { double base; if (type == CartesianPlot::ScaleLog10) base = 10.0; else if (type == CartesianPlot::ScaleLog2) base = 2.0; else base = M_E; return CartesianScale::createLogScale(interval, sceneStart, sceneEnd, logicalStart, logicalEnd, base); } } /*! * Reimplemented from QGraphicsItem. */ QVariant CartesianPlotPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemPositionChange) { const QPointF& itemPos = value.toPointF();//item's center point in parent's coordinates; const qreal x = itemPos.x(); const qreal y = itemPos.y(); //calculate the new rect and forward the changes to the frontend QRectF newRect; const qreal w = rect.width(); const qreal h = rect.height(); newRect.setX(x-w/2); newRect.setY(y-h/2); newRect.setWidth(w); newRect.setHeight(h); emit q->rectChanged(newRect); } return QGraphicsItem::itemChange(change, value); } //############################################################################## //################################## Events ################################## //############################################################################## void CartesianPlotPrivate::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (mouseMode == CartesianPlot::ZoomSelectionMode || mouseMode == CartesianPlot::ZoomXSelectionMode || mouseMode == CartesianPlot::ZoomYSelectionMode) { if (mouseMode == CartesianPlot::ZoomSelectionMode) m_selectionStart = event->pos(); else if (mouseMode == CartesianPlot::ZoomXSelectionMode) { m_selectionStart.setX(event->pos().x()); m_selectionStart.setY(dataRect.height()/2); } else if (mouseMode == CartesianPlot::ZoomYSelectionMode) { m_selectionStart.setX(-dataRect.width()/2); m_selectionStart.setY(event->pos().y()); } m_selectionEnd = m_selectionStart; m_selectionBandIsShown = true; } else { if ( dataRect.contains(event->pos()) ){ panningStarted = true; m_panningStart = event->pos(); setCursor(Qt::ClosedHandCursor); } QGraphicsItem::mousePressEvent(event); } } void CartesianPlotPrivate::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { if (mouseMode == CartesianPlot::SelectionMode) { if (panningStarted && dataRect.contains(event->pos()) ) { //don't retransform on small mouse movement deltas const int deltaXScene = (m_panningStart.x() - event->pos().x()); const int deltaYScene = (m_panningStart.y() - event->pos().y()); if (abs(deltaXScene) < 5 && abs(deltaYScene) < 5) return; const QPointF logicalEnd = cSystem->mapSceneToLogical(event->pos()); const QPointF logicalStart = cSystem->mapSceneToLogical(m_panningStart); const float deltaX = (logicalStart.x() - logicalEnd.x()); const float deltaY = (logicalStart.y() - logicalEnd.y()); xMax += deltaX; xMin += deltaX; yMax += deltaY; yMin += deltaY; retransformScales(); m_panningStart = event->pos(); } else QGraphicsItem::mouseMoveEvent(event); } else if (mouseMode == CartesianPlot::ZoomSelectionMode || mouseMode == CartesianPlot::ZoomXSelectionMode || mouseMode == CartesianPlot::ZoomYSelectionMode) { QGraphicsItem::mouseMoveEvent(event); if ( !boundingRect().contains(event->pos()) ) { q->info(""); return; } QString info; QPointF logicalStart = cSystem->mapSceneToLogical(m_selectionStart); if (mouseMode == CartesianPlot::ZoomSelectionMode) { m_selectionEnd = event->pos(); QPointF logicalEnd = cSystem->mapSceneToLogical(m_selectionEnd); info = QString::fromUtf8("Δx=") + QString::number(logicalEnd.x()-logicalStart.x()) + QString::fromUtf8(", Δy=") + QString::number(logicalEnd.y()-logicalStart.y()); } else if (mouseMode == CartesianPlot::ZoomXSelectionMode) { m_selectionEnd.setX(event->pos().x()); m_selectionEnd.setY(-dataRect.height()/2); QPointF logicalEnd = cSystem->mapSceneToLogical(m_selectionEnd); info = QString::fromUtf8("Δx=") + QString::number(logicalEnd.x()-logicalStart.x()); } else if (mouseMode == CartesianPlot::ZoomYSelectionMode) { m_selectionEnd.setX(dataRect.width()/2); m_selectionEnd.setY(event->pos().y()); QPointF logicalEnd = cSystem->mapSceneToLogical(m_selectionEnd); info = QString::fromUtf8("Δy=") + QString::number(logicalEnd.y()-logicalStart.y()); } q->info(info); update(); } } void CartesianPlotPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { setCursor(Qt::ArrowCursor); if (mouseMode == CartesianPlot::SelectionMode) { panningStarted = false; //TODO: why do we do this all the time?!?! const QPointF& itemPos = pos();//item's center point in parent's coordinates; const qreal x = itemPos.x(); const qreal y = itemPos.y(); //calculate the new rect and set it QRectF newRect; const qreal w = rect.width(); const qreal h = rect.height(); newRect.setX(x-w/2); newRect.setY(y-h/2); newRect.setWidth(w); newRect.setHeight(h); suppressRetransform = true; q->setRect(newRect); suppressRetransform = false; QGraphicsItem::mouseReleaseEvent(event); } else if (mouseMode == CartesianPlot::ZoomSelectionMode || mouseMode == CartesianPlot::ZoomXSelectionMode || mouseMode == CartesianPlot::ZoomYSelectionMode) { //don't zoom if very small region was selected, avoid occasional/unwanted zooming if ( qAbs(m_selectionEnd.x()-m_selectionStart.x()) < 20 || qAbs(m_selectionEnd.y()-m_selectionStart.y()) < 20 ) { m_selectionBandIsShown = false; return; } //determine the new plot ranges QPointF logicalZoomStart = cSystem->mapSceneToLogical(m_selectionStart, AbstractCoordinateSystem::SuppressPageClipping); QPointF logicalZoomEnd = cSystem->mapSceneToLogical(m_selectionEnd, AbstractCoordinateSystem::SuppressPageClipping); if (m_selectionEnd.x() > m_selectionStart.x()) { xMin = logicalZoomStart.x(); xMax = logicalZoomEnd.x(); } else { xMin = logicalZoomEnd.x(); xMax = logicalZoomStart.x(); } if (m_selectionEnd.y() > m_selectionStart.y()) { yMin = logicalZoomEnd.y(); yMax = logicalZoomStart.y(); } else { yMin = logicalZoomStart.y(); yMax = logicalZoomEnd.y(); } m_selectionBandIsShown = false; retransformScales(); } } void CartesianPlotPrivate::wheelEvent(QGraphicsSceneWheelEvent* event) { //determine first, which axes are selected and zoom only in the corresponding direction. //zoom the entire plot if no axes selected. bool zoomX = false; bool zoomY = false; for (auto* axis: q->children()) { if (!axis->graphicsItem()->isSelected()) continue; if (axis->orientation() == Axis::AxisHorizontal) zoomX = true; else zoomY = true; } if (event->delta() > 0) { if (!zoomX && !zoomY) { //no special axis selected -> zoom in everything q->zoomIn(); } else { if (zoomX) q->zoomInX(); if (zoomY) q->zoomInY(); } } else { if (!zoomX && !zoomY) { //no special axis selected -> zoom in everything q->zoomOut(); } else { if (zoomX) q->zoomOutX(); if (zoomY) q->zoomOutY(); } } } void CartesianPlotPrivate::hoverMoveEvent(QGraphicsSceneHoverEvent* event) { QPointF point = event->pos(); QString info; if (dataRect.contains(point)) { QPointF logicalPoint = cSystem->mapSceneToLogical(point); if (mouseMode == CartesianPlot::ZoomSelectionMode && !m_selectionBandIsShown) info = "x=" + QString::number(logicalPoint.x()) + ", y=" + QString::number(logicalPoint.y()); else if (mouseMode == CartesianPlot::ZoomXSelectionMode && !m_selectionBandIsShown) { QPointF p1(logicalPoint.x(), yMin); QPointF p2(logicalPoint.x(), yMax); m_selectionStartLine.setP1(cSystem->mapLogicalToScene(p1)); m_selectionStartLine.setP2(cSystem->mapLogicalToScene(p2)); info = "x=" + QString::number(logicalPoint.x()); update(); } else if (mouseMode == CartesianPlot::ZoomYSelectionMode && !m_selectionBandIsShown) { QPointF p1(xMin, logicalPoint.y()); QPointF p2(xMax, logicalPoint.y()); m_selectionStartLine.setP1(cSystem->mapLogicalToScene(p1)); m_selectionStartLine.setP2(cSystem->mapLogicalToScene(p2)); info = "y=" + QString::number(logicalPoint.y()); update(); } } q->info(info); QGraphicsItem::hoverMoveEvent(event); } void CartesianPlotPrivate::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * widget) { if (!isVisible()) return; painter->setPen(QPen(Qt::black, 3)); if ((mouseMode == CartesianPlot::ZoomXSelectionMode || mouseMode == CartesianPlot::ZoomYSelectionMode) && (!m_selectionBandIsShown)) painter->drawLine(m_selectionStartLine); if (m_selectionBandIsShown) { painter->save(); painter->setPen(QPen(Qt::black, 5)); painter->drawRect(QRectF(m_selectionStart, m_selectionEnd)); painter->setBrush(Qt::blue); painter->setOpacity(0.2); painter->drawRect(QRectF(m_selectionStart, m_selectionEnd)); painter->restore(); } WorksheetElementContainerPrivate::paint(painter, option, widget); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void CartesianPlot::save(QXmlStreamWriter* writer) const { Q_D(const CartesianPlot); writer->writeStartElement( "cartesianPlot" ); writeBasicAttributes(writer); writeCommentElement(writer); //applied theme if (!d->theme.isEmpty()) { writer->writeStartElement( "theme" ); writer->writeAttribute("name", d->theme); writer->writeEndElement(); } //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->rect.x()) ); writer->writeAttribute( "y", QString::number(d->rect.y()) ); writer->writeAttribute( "width", QString::number(d->rect.width()) ); writer->writeAttribute( "height", QString::number(d->rect.height()) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //coordinate system and padding writer->writeStartElement( "coordinateSystem" ); writer->writeAttribute( "autoScaleX", QString::number(d->autoScaleX) ); writer->writeAttribute( "autoScaleY", QString::number(d->autoScaleY) ); writer->writeAttribute( "xMin", QString::number(d->xMin) ); writer->writeAttribute( "xMax", QString::number(d->xMax) ); writer->writeAttribute( "yMin", QString::number(d->yMin) ); writer->writeAttribute( "yMax", QString::number(d->yMax) ); writer->writeAttribute( "xScale", QString::number(d->xScale) ); writer->writeAttribute( "yScale", QString::number(d->yScale) ); writer->writeAttribute( "horizontalPadding", QString::number(d->horizontalPadding) ); writer->writeAttribute( "verticalPadding", QString::number(d->verticalPadding) ); writer->writeEndElement(); //x-scale breaks if (d->xRangeBreakingEnabled || !d->xRangeBreaks.list.isEmpty()) { writer->writeStartElement("xRangeBreaks"); writer->writeAttribute( "enabled", QString::number(d->xRangeBreakingEnabled) ); for (const auto& rb: d->xRangeBreaks.list) { writer->writeStartElement("xRangeBreak"); writer->writeAttribute("start", QString::number(rb.start)); writer->writeAttribute("end", QString::number(rb.end)); writer->writeAttribute("position", QString::number(rb.position)); writer->writeAttribute("style", QString::number(rb.style)); writer->writeEndElement(); } writer->writeEndElement(); } //y-scale breaks if (d->yRangeBreakingEnabled || !d->yRangeBreaks.list.isEmpty()) { writer->writeStartElement("yRangeBreaks"); writer->writeAttribute( "enabled", QString::number(d->yRangeBreakingEnabled) ); for (const auto& rb: d->yRangeBreaks.list) { writer->writeStartElement("yRangeBreak"); writer->writeAttribute("start", QString::number(rb.start)); writer->writeAttribute("end", QString::number(rb.end)); writer->writeAttribute("position", QString::number(rb.position)); writer->writeAttribute("style", QString::number(rb.style)); writer->writeEndElement(); } writer->writeEndElement(); } //serialize all children (plot area, title text label, axes and curves) for (auto *elem: children(IncludeHidden)) elem->save(writer); writer->writeEndElement(); // close "cartesianPlot" section } //! Load from XML bool CartesianPlot::load(XmlStreamReader* reader, bool preview) { Q_D(CartesianPlot); if (!reader->isStartElement() || reader->name() != "cartesianPlot") { reader->raiseError(i18n("no cartesianPlot element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; bool titleLabelRead = false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "cartesianPlot") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "theme") { attribs = reader->attributes(); d->theme = attribs.value("name").toString(); } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else d->rect.setX( str.toDouble() ); str = attribs.value("y").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else d->rect.setY( str.toDouble() ); str = attribs.value("width").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'width'")); + reader->raiseWarning(attributeWarning.subs("width").toString()); else d->rect.setWidth( str.toDouble() ); str = attribs.value("height").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'height'")); + reader->raiseWarning(attributeWarning.subs("height").toString()); else d->rect.setHeight( str.toDouble() ); str = attribs.value("visible").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "coordinateSystem") { attribs = reader->attributes(); str = attribs.value("autoScaleX").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoScaleX'")); + reader->raiseWarning(attributeWarning.subs("autoScaleX").toString()); else d->autoScaleX = bool(str.toInt()); str = attribs.value("autoScaleY").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'autoScaleY'")); + reader->raiseWarning(attributeWarning.subs("autoScaleY").toString()); else d->autoScaleY = bool(str.toInt()); str = attribs.value("xMin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'xMin'")); + reader->raiseWarning(attributeWarning.subs("xMin").toString()); else { d->xMin = str.toDouble(); d->xMinPrev = d->xMin; } str = attribs.value("xMax").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'xMax'")); + reader->raiseWarning(attributeWarning.subs("xMax").toString()); else { d->xMax = str.toDouble(); d->xMaxPrev = d->xMax; } str = attribs.value("yMin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'yMin'")); + reader->raiseWarning(attributeWarning.subs("yMin").toString()); else { d->yMin = str.toDouble(); d->yMinPrev = d->yMin; } str = attribs.value("yMax").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'yMax'")); + reader->raiseWarning(attributeWarning.subs("yMax").toString()); else { d->yMax = str.toDouble(); d->yMaxPrev = d->yMax; } str = attribs.value("xScale").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'xScale'")); + reader->raiseWarning(attributeWarning.subs("xScale").toString()); else d->xScale = CartesianPlot::Scale(str.toInt()); str = attribs.value("yScale").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'yScale'")); + reader->raiseWarning(attributeWarning.subs("yScale").toString()); else d->yScale = CartesianPlot::Scale(str.toInt()); str = attribs.value("horizontalPadding").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'horizontalPadding'")); + reader->raiseWarning(attributeWarning.subs("horizontalPadding").toString()); else d->horizontalPadding = str.toDouble(); str = attribs.value("verticalPadding").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'verticalPadding'")); + reader->raiseWarning(attributeWarning.subs("verticalPadding").toString()); else d->verticalPadding = str.toDouble(); } else if (!preview && reader->name() == "xRangeBreaks") { //delete default rang break d->xRangeBreaks.list.clear(); attribs = reader->attributes(); str = attribs.value("enabled").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'enabled'")); + reader->raiseWarning(attributeWarning.subs("enabled").toString()); else d->xRangeBreakingEnabled = str.toInt(); } else if (!preview && reader->name() == "xRangeBreak") { attribs = reader->attributes(); RangeBreak b; str = attribs.value("start").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'start'")); + reader->raiseWarning(attributeWarning.subs("start").toString()); else b.start = str.toDouble(); str = attribs.value("end").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'end'")); + reader->raiseWarning(attributeWarning.subs("end").toString()); else b.end = str.toDouble(); str = attribs.value("position").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'position'")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else b.position = str.toDouble(); str = attribs.value("style").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'style'")); + reader->raiseWarning(attributeWarning.subs("style").toString()); else b.style = CartesianPlot::RangeBreakStyle(str.toInt()); d->xRangeBreaks.list << b; } else if (!preview && reader->name() == "yRangeBreaks") { //delete default rang break d->yRangeBreaks.list.clear(); attribs = reader->attributes(); str = attribs.value("enabled").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'enabled'")); + reader->raiseWarning(attributeWarning.subs("enabled").toString()); else d->yRangeBreakingEnabled = str.toInt(); } else if (!preview && reader->name() == "yRangeBreak") { attribs = reader->attributes(); RangeBreak b; str = attribs.value("start").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'start'")); + reader->raiseWarning(attributeWarning.subs("start").toString()); else b.start = str.toDouble(); str = attribs.value("end").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'end'")); + reader->raiseWarning(attributeWarning.subs("end").toString()); else b.end = str.toDouble(); str = attribs.value("position").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'position'")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else b.position = str.toDouble(); str = attribs.value("style").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'style'")); + reader->raiseWarning(attributeWarning.subs("style").toString()); else b.style = CartesianPlot::RangeBreakStyle(str.toInt()); d->yRangeBreaks.list << b; } else if (reader->name() == "textLabel") { if (!titleLabelRead) { //the first text label is always the title label m_title->load(reader, preview); titleLabelRead = true; } else { TextLabel* label = new TextLabel("text label"); if (label->load(reader, preview)) { addChildFast(label); label->setParentGraphicsItem(graphicsItem()); } else { delete label; return false; } } } else if (reader->name() == "plotArea") m_plotArea->load(reader, preview); else if (reader->name() == "axis") { Axis* axis = new Axis(""); if (axis->load(reader, preview)) addChildFast(axis); else { delete axis; return false; } } else if (reader->name() == "xyCurve") { XYCurve* curve = new XYCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyEquationCurve") { XYEquationCurve* curve = new XYEquationCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyDataReductionCurve") { XYDataReductionCurve* curve = new XYDataReductionCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyDifferentiationCurve") { XYDifferentiationCurve* curve = new XYDifferentiationCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyIntegrationCurve") { XYIntegrationCurve* curve = new XYIntegrationCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyInterpolationCurve") { XYInterpolationCurve* curve = new XYInterpolationCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyFitCurve") { XYFitCurve* curve = new XYFitCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyFourierFilterCurve") { XYFourierFilterCurve* curve = new XYFourierFilterCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xyFourierTransformCurve") { XYFourierTransformCurve* curve = new XYFourierTransformCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "xySmoothCurve") { XYSmoothCurve* curve = new XYSmoothCurve(""); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else if (reader->name() == "cartesianPlotLegend") { m_legend = new CartesianPlotLegend(this, ""); if (m_legend->load(reader, preview)) addChildFast(m_legend); else { delete m_legend; return false; } } else if (reader->name() == "customPoint") { CustomPoint* point = new CustomPoint(this, ""); if (point->load(reader, preview)) addChildFast(point); else { delete point; return false; } } else if(reader->name() == "Histogram") { Histogram* curve = new Histogram("Histogram"); if (curve->load(reader, preview)) addChildFast(curve); else { removeChild(curve); return false; } } else { // unknown element reader->raiseWarning(i18n("unknown cartesianPlot element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (preview) return true; d->retransform(); //if a theme was used, initialize the color palette if (!d->theme.isEmpty()) { //TODO: check whether the theme config really exists KConfig config( ThemeHandler::themeFilePath(d->theme), KConfig::SimpleConfig ); this->setColorPalette(config); } else { //initialize the color palette with default colors this->setColorPalette(KConfig()); } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void CartesianPlot::loadTheme(const QString& theme) { KConfig config(ThemeHandler::themeFilePath(theme), KConfig::SimpleConfig); loadThemeConfig(config); } void CartesianPlot::loadThemeConfig(const KConfig& config) { QString str = config.name(); str = str.right(str.length() - str.lastIndexOf(QDir::separator()) - 1); this->setTheme(str); //load the color palettes for the curves this->setColorPalette(config); //load the theme for all the children for (auto* child: children(AbstractAspect::IncludeHidden)) child->loadThemeConfig(config); Q_D(CartesianPlot); d->update(this->rect()); } void CartesianPlot::saveTheme(KConfig &config) { const QVector& axisElements = children(AbstractAspect::IncludeHidden); const QVector& plotAreaElements = children(AbstractAspect::IncludeHidden); const QVector& textLabelElements = children(AbstractAspect::IncludeHidden); axisElements.at(0)->saveThemeConfig(config); plotAreaElements.at(0)->saveThemeConfig(config); textLabelElements.at(0)->saveThemeConfig(config); for (auto *child: children(AbstractAspect::IncludeHidden)) child->saveThemeConfig(config); } //Generating colors from 5-color theme palette void CartesianPlot::setColorPalette(const KConfig& config) { if (config.hasGroup(QLatin1String("Theme"))) { KConfigGroup group = config.group(QLatin1String("Theme")); //read the five colors defining the palette m_themeColorPalette.clear(); m_themeColorPalette.append(group.readEntry("ThemePaletteColor1", QColor())); m_themeColorPalette.append(group.readEntry("ThemePaletteColor2", QColor())); m_themeColorPalette.append(group.readEntry("ThemePaletteColor3", QColor())); m_themeColorPalette.append(group.readEntry("ThemePaletteColor4", QColor())); m_themeColorPalette.append(group.readEntry("ThemePaletteColor5", QColor())); } else { //no theme is available, provide 5 "default colors" m_themeColorPalette.clear(); m_themeColorPalette.append(QColor(25, 25, 25)); m_themeColorPalette.append(QColor(0, 0, 127)); m_themeColorPalette.append(QColor(127 ,0, 0)); m_themeColorPalette.append(QColor(0, 127, 0)); m_themeColorPalette.append(QColor(85, 0, 127)); } //generate 30 additional shades if the color palette contains more than one color if (m_themeColorPalette.at(0) != m_themeColorPalette.at(1)) { QColor c; //3 factors to create shades from theme's palette float fac[3] = {0.25f,0.45f,0.65f}; //Generate 15 lighter shades for (int i = 0; i < 5; i++) { for (int j = 1; j < 4; j++) { c.setRed( m_themeColorPalette.at(i).red()*(1-fac[j-1]) ); c.setGreen( m_themeColorPalette.at(i).green()*(1-fac[j-1]) ); c.setBlue( m_themeColorPalette.at(i).blue()*(1-fac[j-1]) ); m_themeColorPalette.append(c); } } //Generate 15 darker shades for (int i = 0; i < 5; i++) { for (int j = 4; j < 7; j++) { c.setRed( m_themeColorPalette.at(i).red()+((255-m_themeColorPalette.at(i).red())*fac[j-4]) ); c.setGreen( m_themeColorPalette.at(i).green()+((255-m_themeColorPalette.at(i).green())*fac[j-4]) ); c.setBlue( m_themeColorPalette.at(i).blue()+((255-m_themeColorPalette.at(i).blue())*fac[j-4]) ); m_themeColorPalette.append(c); } } } } const QList& CartesianPlot::themeColorPalette() const { return m_themeColorPalette; } diff --git a/src/backend/worksheet/plots/cartesian/CartesianPlot.h b/src/backend/worksheet/plots/cartesian/CartesianPlot.h index 127ed6b8c..f64f4fae5 100644 --- a/src/backend/worksheet/plots/cartesian/CartesianPlot.h +++ b/src/backend/worksheet/plots/cartesian/CartesianPlot.h @@ -1,287 +1,288 @@ /*************************************************************************** File : CartesianPlot.h Project : LabPlot Description : Cartesian plot -------------------------------------------------------------------- Copyright : (C) 2011-2018 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 CARTESIANPLOT_H #define CARTESIANPLOT_H #include "backend/worksheet/plots/AbstractPlot.h" #include "backend/worksheet/plots/cartesian/Histogram.h" #include class QDropEvent; class QToolBar; class CartesianPlotPrivate; class CartesianPlotLegend; class AbstractColumn; class XYCurve; class XYEquationCurve; class XYDataReductionCurve; class XYDifferentiationCurve; class XYIntegrationCurve; class XYInterpolationCurve; class XYSmoothCurve; class XYFitCurve; class XYFourierFilterCurve; class KConfig; class XYFourierTransformCurve; class CartesianPlot:public AbstractPlot { Q_OBJECT public: explicit CartesianPlot(const QString &name); ~CartesianPlot() override; enum Scale {ScaleLinear, ScaleLog10, ScaleLog2, ScaleLn, ScaleSqrt, ScaleX2}; enum Type {FourAxes, TwoAxes, TwoAxesCentered, TwoAxesCenteredZero}; enum RangeType {RangeFree, RangeLast, RangeFirst}; enum RangeBreakStyle {RangeBreakSimple, RangeBreakVertical, RangeBreakSloped}; enum MouseMode {SelectionMode, ZoomSelectionMode, ZoomXSelectionMode, ZoomYSelectionMode}; enum NavigationOperation {ScaleAuto, ScaleAutoX, ScaleAutoY, ZoomIn, ZoomOut, ZoomInX, ZoomOutX, ZoomInY, ZoomOutY, ShiftLeftX, ShiftRightX, ShiftUpY, ShiftDownY }; struct RangeBreak { RangeBreak() : start(NAN), end(NAN), position(0.5), style(RangeBreakSloped) {} bool isValid() const { return (!std::isnan(start) && !std::isnan(end)); } double start; double end; double position; RangeBreakStyle style; }; //simple wrapper for QList in order to get our macros working struct RangeBreaks { RangeBreaks() : lastChanged(-1) { RangeBreak b; list << b; }; QList list; int lastChanged; }; void initDefault(Type = FourAxes); QIcon icon() const override; QMenu* createContextMenu() override; QMenu* analysisMenu(); QVector dependsOn() const override; void setRect(const QRectF&) override; QRectF dataRect() const; void setMouseMode(const MouseMode); MouseMode mouseMode() const; void navigate(NavigationOperation); void setSuppressDataChangedSignal(bool); const QList& themeColorPalette() const; void processDropEvent(QDropEvent*); bool isPanningActive() const; + void addLegend(CartesianPlotLegend*); void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; void loadThemeConfig(const KConfig&) override; void saveTheme(KConfig& config); BASIC_D_ACCESSOR_DECL(CartesianPlot::RangeType, rangeType, RangeType) BASIC_D_ACCESSOR_DECL(int, rangeLastValues, RangeLastValues) BASIC_D_ACCESSOR_DECL(int, rangeFirstValues, RangeFirstValues) BASIC_D_ACCESSOR_DECL(bool, autoScaleX, AutoScaleX) BASIC_D_ACCESSOR_DECL(bool, autoScaleY, AutoScaleY) BASIC_D_ACCESSOR_DECL(double, xMin, XMin) BASIC_D_ACCESSOR_DECL(double, xMax, XMax) BASIC_D_ACCESSOR_DECL(double, yMin, YMin) BASIC_D_ACCESSOR_DECL(double, yMax, YMax) BASIC_D_ACCESSOR_DECL(CartesianPlot::Scale, xScale, XScale) BASIC_D_ACCESSOR_DECL(CartesianPlot::Scale, yScale, YScale) BASIC_D_ACCESSOR_DECL(bool, xRangeBreakingEnabled, XRangeBreakingEnabled) BASIC_D_ACCESSOR_DECL(bool, yRangeBreakingEnabled, YRangeBreakingEnabled) CLASS_D_ACCESSOR_DECL(RangeBreaks, xRangeBreaks, XRangeBreaks) CLASS_D_ACCESSOR_DECL(RangeBreaks, yRangeBreaks, YRangeBreaks) QString theme() const; typedef CartesianPlotPrivate Private; public slots: void setTheme(const QString&); private: void init(); void initActions(); void initMenus(); void setColorPalette(const KConfig&); const XYCurve* currentCurve() const; CartesianPlotLegend* m_legend; double m_zoomFactor; QList m_themeColorPalette; bool m_menusInitialized; QAction* visibilityAction; //"add new" actions QAction* addCurveAction; QAction* addEquationCurveAction; QAction* addHistogramPlot; QAction* addDataReductionCurveAction; QAction* addDifferentiationCurveAction; QAction* addIntegrationCurveAction; QAction* addInterpolationCurveAction; QAction* addSmoothCurveAction; QAction* addFitCurveAction; QAction* addFourierFilterCurveAction; QAction* addFourierTransformCurveAction; QAction* addHorizontalAxisAction; QAction* addVerticalAxisAction; QAction* addLegendAction; QAction* addTextLabelAction; QAction* addCustomPointAction; //scaling, zooming, navigation actions QAction* scaleAutoXAction; QAction* scaleAutoYAction; QAction* scaleAutoAction; QAction* zoomInAction; QAction* zoomOutAction; QAction* zoomInXAction; QAction* zoomOutXAction; QAction* zoomInYAction; QAction* zoomOutYAction; QAction* shiftLeftXAction; QAction* shiftRightXAction; QAction* shiftUpYAction; QAction* shiftDownYAction; //analysis menu actions QAction* addDataOperationAction; QAction* addDataReductionAction; QAction* addDifferentiationAction; QAction* addIntegrationAction; QAction* addInterpolationAction; QAction* addSmoothAction; QVector addFitAction; QAction* addFourierFilterAction; QMenu* addNewMenu; QMenu* zoomMenu; QMenu* dataAnalysisMenu; QMenu* themeMenu; Q_DECLARE_PRIVATE(CartesianPlot) public slots: void addHorizontalAxis(); void addVerticalAxis(); void addCurve(); void addHistogram(); void addEquationCurve(); void addDataReductionCurve(); void addDifferentiationCurve(); void addIntegrationCurve(); void addInterpolationCurve(); void addSmoothCurve(); void addFitCurve(); void addFourierFilterCurve(); void addFourierTransformCurve(); void addLegend(); void addTextLabel(); void addCustomPoint(); void scaleAuto(); void scaleAutoX(); void scaleAutoY(); void zoomIn(); void zoomOut(); void zoomInX(); void zoomOutX(); void zoomInY(); void zoomOutY(); void shiftLeftX(); void shiftRightX(); void shiftUpY(); void shiftDownY(); void dataChanged(); private slots: void updateLegend(); void childAdded(const AbstractAspect*); void childRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child); void xDataChanged(); void yDataChanged(); void HistogramdataChanged(); void xHistogramDataChanged(); void yHistogramDataChanged(); void curveVisibilityChanged(); //SLOTs for changes triggered via QActions in the context menu void visibilityChanged(); void loadTheme(const QString&); protected: CartesianPlot(const QString &name, CartesianPlotPrivate *dd); signals: friend class CartesianPlotSetCRangeTypeCmd; friend class CartesianPlotSetCRangeLastValuesCmd; friend class CartesianPlotSetCRangeFirstValuesCmd; friend class CartesianPlotSetRectCmd; friend class CartesianPlotSetAutoScaleXCmd; friend class CartesianPlotSetXMinCmd; friend class CartesianPlotSetXMaxCmd; friend class CartesianPlotSetXScaleCmd; friend class CartesianPlotSetAutoScaleYCmd; friend class CartesianPlotSetYMinCmd; friend class CartesianPlotSetYMaxCmd; friend class CartesianPlotSetYScaleCmd; friend class CartesianPlotSetXRangeBreakingEnabledCmd; friend class CartesianPlotSetYRangeBreakingEnabledCmd; friend class CartesianPlotSetXRangeBreaksCmd; friend class CartesianPlotSetYRangeBreaksCmd; friend class CartesianPlotSetThemeCmd; void rangeTypeChanged(CartesianPlot::RangeType); void rangeLastValuesChanged(int); void rangeFirstValuesChanged(int); void rectChanged(QRectF&); void xAutoScaleChanged(bool); void xMinChanged(double); void xMaxChanged(double); void xScaleChanged(int); void yAutoScaleChanged(bool); void yMinChanged(double); void yMaxChanged(double); void yScaleChanged(int); void xRangeBreakingEnabledChanged(bool); void xRangeBreaksChanged(const CartesianPlot::RangeBreaks&); void yRangeBreakingEnabledChanged(bool); void yRangeBreaksChanged(const CartesianPlot::RangeBreaks&); void themeChanged(const QString&); }; #endif diff --git a/src/backend/worksheet/plots/cartesian/CartesianPlotLegend.cpp b/src/backend/worksheet/plots/cartesian/CartesianPlotLegend.cpp index 1f895abd4..539f30840 100644 --- a/src/backend/worksheet/plots/cartesian/CartesianPlotLegend.cpp +++ b/src/backend/worksheet/plots/cartesian/CartesianPlotLegend.cpp @@ -1,1148 +1,1148 @@ /*************************************************************************** File : CartesianPlotLegend.cpp Project : LabPlot Description : Legend for the cartesian plot -------------------------------------------------------------------- Copyright : (C) 2013-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 * * * ***************************************************************************/ /*! \class CartesianPlotLegend \brief Legend for the cartesian plot. \ingroup kdefrontend */ #include "CartesianPlotLegend.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegendPrivate.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/Worksheet.h" #include "backend/lib/XmlStreamReader.h" #include "backend/worksheet/TextLabel.h" #include "backend/lib/commandtemplates.h" #include #include #include #include #include #include -#include +#include CartesianPlotLegend::CartesianPlotLegend(CartesianPlot* plot, const QString &name) : WorksheetElement(name), d_ptr(new CartesianPlotLegendPrivate(this)), m_plot(plot) { init(); } CartesianPlotLegend::CartesianPlotLegend(CartesianPlot* plot, const QString &name, CartesianPlotLegendPrivate *dd) : WorksheetElement(name), d_ptr(dd), m_plot(plot) { init(); } CartesianPlotLegend::~CartesianPlotLegend() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void CartesianPlotLegend::init() { Q_D(CartesianPlotLegend); KConfig config; KConfigGroup group = config.group( "CartesianPlotLegend" ); d->labelFont = group.readEntry("LabelsFont", QFont()); d->labelFont.setPixelSize( Worksheet::convertToSceneUnits( 10, Worksheet::Point ) ); d->labelColor = Qt::black; d->labelColumnMajor = true; d->lineSymbolWidth = group.readEntry("LineSymbolWidth", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)); d->rowCount = 0; d->columnCount = 0; d->position.horizontalPosition = CartesianPlotLegend::hPositionRight; d->position.verticalPosition = CartesianPlotLegend::vPositionBottom; //Title d->title = new TextLabel(this->name(), TextLabel::PlotLegendTitle); addChild(d->title); d->title->setHidden(true); d->title->setParentGraphicsItem(graphicsItem()); d->title->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); connect(d->title, &TextLabel::changed, this, &CartesianPlotLegend::retransform); //Background d->backgroundType = (PlotArea::BackgroundType) group.readEntry("BackgroundType", (int) PlotArea::Color); d->backgroundColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("BackgroundColorStyle", (int) PlotArea::SingleColor); d->backgroundImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("BackgroundImageStyle", (int) PlotArea::Scaled); d->backgroundBrushStyle = (Qt::BrushStyle) group.readEntry("BackgroundBrushStyle", (int) Qt::SolidPattern); d->backgroundFileName = group.readEntry("BackgroundFileName", QString()); d->backgroundFirstColor = group.readEntry("BackgroundFirstColor", QColor(Qt::white)); d->backgroundSecondColor = group.readEntry("BackgroundSecondColor", QColor(Qt::black)); d->backgroundOpacity = group.readEntry("BackgroundOpacity", 1.0); //Border d->borderPen = QPen(group.readEntry("BorderColor", QColor(Qt::black)), group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)), (Qt::PenStyle) group.readEntry("BorderStyle", (int)Qt::SolidLine)); d->borderCornerRadius = group.readEntry("BorderCornerRadius", 0.0); d->borderOpacity = group.readEntry("BorderOpacity", 1.0); //Layout d->layoutTopMargin = group.readEntry("LayoutTopMargin", Worksheet::convertToSceneUnits(0.2f, Worksheet::Centimeter)); d->layoutBottomMargin = group.readEntry("LayoutBottomMargin", Worksheet::convertToSceneUnits(0.2f, Worksheet::Centimeter)); d->layoutLeftMargin = group.readEntry("LayoutLeftMargin", Worksheet::convertToSceneUnits(0.2f, Worksheet::Centimeter)); d->layoutRightMargin = group.readEntry("LayoutRightMargin", Worksheet::convertToSceneUnits(0.2f, Worksheet::Centimeter)); d->layoutVerticalSpacing = group.readEntry("LayoutVerticalSpacing", Worksheet::convertToSceneUnits(0.1f, Worksheet::Centimeter)); d->layoutHorizontalSpacing = group.readEntry("LayoutHorizontalSpacing", Worksheet::convertToSceneUnits(0.1f, Worksheet::Centimeter)); d->layoutColumnCount = group.readEntry("LayoutColumnCount", 1); graphicsItem()->setFlag(QGraphicsItem::ItemIsSelectable, true); graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable); graphicsItem()->setFlag(QGraphicsItem::ItemSendsGeometryChanges); this->initActions(); } void CartesianPlotLegend::initActions() { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &CartesianPlotLegend::visibilityChanged); } QMenu* CartesianPlotLegend::createContextMenu() { QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); return menu; } /*! Returns an icon to be used in the project explorer. */ QIcon CartesianPlotLegend::icon() const{ return QIcon::fromTheme("text-field"); } STD_SWAP_METHOD_SETTER_CMD_IMPL(CartesianPlotLegend, SetVisible, bool, swapVisible) void CartesianPlotLegend::setVisible(bool on) { Q_D(CartesianPlotLegend); - exec(new CartesianPlotLegendSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new CartesianPlotLegendSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool CartesianPlotLegend::isVisible() const{ Q_D(const CartesianPlotLegend); return d->isVisible(); } void CartesianPlotLegend::setPrinting(bool on) { Q_D(CartesianPlotLegend); d->m_printing = on; } QGraphicsItem *CartesianPlotLegend::graphicsItem() const{ return d_ptr; } void CartesianPlotLegend::retransform() { d_ptr->retransform(); } void CartesianPlotLegend::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { Q_UNUSED(horizontalRatio); Q_UNUSED(verticalRatio); Q_UNUSED(pageResize); //TODO // Q_D(const CartesianPlotLegend); } //############################################################################## //################################ getter methods ############################ //############################################################################## CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QFont, labelFont, labelFont) CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QColor, labelColor, labelColor) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, bool, labelColumnMajor, labelColumnMajor) CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, CartesianPlotLegend::PositionWrapper, position, position) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, lineSymbolWidth, lineSymbolWidth) //Title TextLabel* CartesianPlotLegend::title() { return d_ptr->title; } //Background BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, PlotArea::BackgroundType, backgroundType, backgroundType) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, PlotArea::BackgroundColorStyle, backgroundColorStyle, backgroundColorStyle) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, PlotArea::BackgroundImageStyle, backgroundImageStyle, backgroundImageStyle) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, Qt::BrushStyle, backgroundBrushStyle, backgroundBrushStyle) CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QColor, backgroundFirstColor, backgroundFirstColor) CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QColor, backgroundSecondColor, backgroundSecondColor) CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QString, backgroundFileName, backgroundFileName) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, backgroundOpacity, backgroundOpacity) //Border CLASS_SHARED_D_READER_IMPL(CartesianPlotLegend, QPen, borderPen, borderPen) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, borderCornerRadius, borderCornerRadius) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, borderOpacity, borderOpacity) //Layout BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutTopMargin, layoutTopMargin) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutBottomMargin, layoutBottomMargin) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutLeftMargin, layoutLeftMargin) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutRightMargin, layoutRightMargin) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutHorizontalSpacing, layoutHorizontalSpacing) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, float, layoutVerticalSpacing, layoutVerticalSpacing) BASIC_SHARED_D_READER_IMPL(CartesianPlotLegend, int, layoutColumnCount, layoutColumnCount) //############################################################################## //###################### setter methods and undo commands #################### //############################################################################## STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelFont, QFont, labelFont, retransform) void CartesianPlotLegend::setLabelFont(const QFont& font) { Q_D(CartesianPlotLegend); if (font!= d->labelFont) - exec(new CartesianPlotLegendSetLabelFontCmd(d, font, i18n("%1: set font"))); + exec(new CartesianPlotLegendSetLabelFontCmd(d, font, ki18n("%1: set font"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelColor, QColor, labelColor, update) void CartesianPlotLegend::setLabelColor(const QColor& color) { Q_D(CartesianPlotLegend); if (color!= d->labelColor) - exec(new CartesianPlotLegendSetLabelColorCmd(d, color, i18n("%1: set font color"))); + exec(new CartesianPlotLegendSetLabelColorCmd(d, color, ki18n("%1: set font color"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLabelColumnMajor, bool, labelColumnMajor, retransform) void CartesianPlotLegend::setLabelColumnMajor(bool columnMajor) { Q_D(CartesianPlotLegend); if (columnMajor != d->labelColumnMajor) - exec(new CartesianPlotLegendSetLabelColumnMajorCmd(d, columnMajor, i18n("%1: change column order"))); + exec(new CartesianPlotLegendSetLabelColumnMajorCmd(d, columnMajor, ki18n("%1: change column order"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLineSymbolWidth, float, lineSymbolWidth, retransform) void CartesianPlotLegend::setLineSymbolWidth(float width) { Q_D(CartesianPlotLegend); if (width != d->lineSymbolWidth) - exec(new CartesianPlotLegendSetLineSymbolWidthCmd(d, width, i18n("%1: change line+symbol width"))); + exec(new CartesianPlotLegendSetLineSymbolWidthCmd(d, width, ki18n("%1: change line+symbol width"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetPosition, CartesianPlotLegend::PositionWrapper, position, updatePosition); void CartesianPlotLegend::setPosition(const PositionWrapper& pos) { Q_D(CartesianPlotLegend); if (pos.point!=d->position.point || pos.horizontalPosition!=d->position.horizontalPosition || pos.verticalPosition!=d->position.verticalPosition) - exec(new CartesianPlotLegendSetPositionCmd(d, pos, i18n("%1: set position"))); + exec(new CartesianPlotLegendSetPositionCmd(d, pos, ki18n("%1: set position"))); } //Background STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundType, PlotArea::BackgroundType, backgroundType, update) void CartesianPlotLegend::setBackgroundType(PlotArea::BackgroundType type) { Q_D(CartesianPlotLegend); if (type != d->backgroundType) - exec(new CartesianPlotLegendSetBackgroundTypeCmd(d, type, i18n("%1: background type changed"))); + exec(new CartesianPlotLegendSetBackgroundTypeCmd(d, type, ki18n("%1: background type changed"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundColorStyle, PlotArea::BackgroundColorStyle, backgroundColorStyle, update) void CartesianPlotLegend::setBackgroundColorStyle(PlotArea::BackgroundColorStyle style) { Q_D(CartesianPlotLegend); if (style != d->backgroundColorStyle) - exec(new CartesianPlotLegendSetBackgroundColorStyleCmd(d, style, i18n("%1: background color style changed"))); + exec(new CartesianPlotLegendSetBackgroundColorStyleCmd(d, style, ki18n("%1: background color style changed"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundImageStyle, PlotArea::BackgroundImageStyle, backgroundImageStyle, update) void CartesianPlotLegend::setBackgroundImageStyle(PlotArea::BackgroundImageStyle style) { Q_D(CartesianPlotLegend); if (style != d->backgroundImageStyle) - exec(new CartesianPlotLegendSetBackgroundImageStyleCmd(d, style, i18n("%1: background image style changed"))); + exec(new CartesianPlotLegendSetBackgroundImageStyleCmd(d, style, ki18n("%1: background image style changed"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundBrushStyle, Qt::BrushStyle, backgroundBrushStyle, update) void CartesianPlotLegend::setBackgroundBrushStyle(Qt::BrushStyle style) { Q_D(CartesianPlotLegend); if (style != d->backgroundBrushStyle) - exec(new CartesianPlotLegendSetBackgroundBrushStyleCmd(d, style, i18n("%1: background brush style changed"))); + exec(new CartesianPlotLegendSetBackgroundBrushStyleCmd(d, style, ki18n("%1: background brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundFirstColor, QColor, backgroundFirstColor, update) void CartesianPlotLegend::setBackgroundFirstColor(const QColor &color) { Q_D(CartesianPlotLegend); if (color!= d->backgroundFirstColor) - exec(new CartesianPlotLegendSetBackgroundFirstColorCmd(d, color, i18n("%1: set background first color"))); + exec(new CartesianPlotLegendSetBackgroundFirstColorCmd(d, color, ki18n("%1: set background first color"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundSecondColor, QColor, backgroundSecondColor, update) void CartesianPlotLegend::setBackgroundSecondColor(const QColor &color) { Q_D(CartesianPlotLegend); if (color!= d->backgroundSecondColor) - exec(new CartesianPlotLegendSetBackgroundSecondColorCmd(d, color, i18n("%1: set background second color"))); + exec(new CartesianPlotLegendSetBackgroundSecondColorCmd(d, color, ki18n("%1: set background second color"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundFileName, QString, backgroundFileName, update) void CartesianPlotLegend::setBackgroundFileName(const QString& fileName) { Q_D(CartesianPlotLegend); if (fileName!= d->backgroundFileName) - exec(new CartesianPlotLegendSetBackgroundFileNameCmd(d, fileName, i18n("%1: set background image"))); + exec(new CartesianPlotLegendSetBackgroundFileNameCmd(d, fileName, ki18n("%1: set background image"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBackgroundOpacity, float, backgroundOpacity, update) void CartesianPlotLegend::setBackgroundOpacity(float opacity) { Q_D(CartesianPlotLegend); if (opacity != d->backgroundOpacity) - exec(new CartesianPlotLegendSetBackgroundOpacityCmd(d, opacity, i18n("%1: set opacity"))); + exec(new CartesianPlotLegendSetBackgroundOpacityCmd(d, opacity, ki18n("%1: set opacity"))); } //Border STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBorderPen, QPen, borderPen, update) void CartesianPlotLegend::setBorderPen(const QPen &pen) { Q_D(CartesianPlotLegend); if (pen != d->borderPen) - exec(new CartesianPlotLegendSetBorderPenCmd(d, pen, i18n("%1: set border style"))); + exec(new CartesianPlotLegendSetBorderPenCmd(d, pen, ki18n("%1: set border style"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBorderCornerRadius, qreal, borderCornerRadius, update) void CartesianPlotLegend::setBorderCornerRadius(float radius) { Q_D(CartesianPlotLegend); if (radius != d->borderCornerRadius) - exec(new CartesianPlotLegendSetBorderCornerRadiusCmd(d, radius, i18n("%1: set border corner radius"))); + exec(new CartesianPlotLegendSetBorderCornerRadiusCmd(d, radius, ki18n("%1: set border corner radius"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetBorderOpacity, qreal, borderOpacity, update) void CartesianPlotLegend::setBorderOpacity(float opacity) { Q_D(CartesianPlotLegend); if (opacity != d->borderOpacity) - exec(new CartesianPlotLegendSetBorderOpacityCmd(d, opacity, i18n("%1: set border opacity"))); + exec(new CartesianPlotLegendSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity"))); } //Layout STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutTopMargin, float, layoutTopMargin, retransform) void CartesianPlotLegend::setLayoutTopMargin(float margin) { Q_D(CartesianPlotLegend); if (margin != d->layoutTopMargin) - exec(new CartesianPlotLegendSetLayoutTopMarginCmd(d, margin, i18n("%1: set layout top margin"))); + exec(new CartesianPlotLegendSetLayoutTopMarginCmd(d, margin, ki18n("%1: set layout top margin"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutBottomMargin, float, layoutBottomMargin, retransform) void CartesianPlotLegend::setLayoutBottomMargin(float margin) { Q_D(CartesianPlotLegend); if (margin != d->layoutBottomMargin) - exec(new CartesianPlotLegendSetLayoutBottomMarginCmd(d, margin, i18n("%1: set layout bottom margin"))); + exec(new CartesianPlotLegendSetLayoutBottomMarginCmd(d, margin, ki18n("%1: set layout bottom margin"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutLeftMargin, float, layoutLeftMargin, retransform) void CartesianPlotLegend::setLayoutLeftMargin(float margin) { Q_D(CartesianPlotLegend); if (margin != d->layoutLeftMargin) - exec(new CartesianPlotLegendSetLayoutLeftMarginCmd(d, margin, i18n("%1: set layout left margin"))); + exec(new CartesianPlotLegendSetLayoutLeftMarginCmd(d, margin, ki18n("%1: set layout left margin"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutRightMargin, float, layoutRightMargin, retransform) void CartesianPlotLegend::setLayoutRightMargin(float margin) { Q_D(CartesianPlotLegend); if (margin != d->layoutRightMargin) - exec(new CartesianPlotLegendSetLayoutRightMarginCmd(d, margin, i18n("%1: set layout right margin"))); + exec(new CartesianPlotLegendSetLayoutRightMarginCmd(d, margin, ki18n("%1: set layout right margin"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutVerticalSpacing, float, layoutVerticalSpacing, retransform) void CartesianPlotLegend::setLayoutVerticalSpacing(float spacing) { Q_D(CartesianPlotLegend); if (spacing != d->layoutVerticalSpacing) - exec(new CartesianPlotLegendSetLayoutVerticalSpacingCmd(d, spacing, i18n("%1: set layout vertical spacing"))); + exec(new CartesianPlotLegendSetLayoutVerticalSpacingCmd(d, spacing, ki18n("%1: set layout vertical spacing"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutHorizontalSpacing, float, layoutHorizontalSpacing, retransform) void CartesianPlotLegend::setLayoutHorizontalSpacing(float spacing) { Q_D(CartesianPlotLegend); if (spacing != d->layoutHorizontalSpacing) - exec(new CartesianPlotLegendSetLayoutHorizontalSpacingCmd(d, spacing, i18n("%1: set layout horizontal spacing"))); + exec(new CartesianPlotLegendSetLayoutHorizontalSpacingCmd(d, spacing, ki18n("%1: set layout horizontal spacing"))); } STD_SETTER_CMD_IMPL_F_S(CartesianPlotLegend, SetLayoutColumnCount, int, layoutColumnCount, retransform) void CartesianPlotLegend::setLayoutColumnCount(int count) { Q_D(CartesianPlotLegend); if (count != d->layoutColumnCount) - exec(new CartesianPlotLegendSetLayoutColumnCountCmd(d, count, i18n("%1: set layout column count"))); + exec(new CartesianPlotLegendSetLayoutColumnCountCmd(d, count, ki18n("%1: set layout column count"))); } //############################################################################## //################################# SLOTS #################################### //############################################################################## //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void CartesianPlotLegend::visibilityChangedSlot() { Q_D(const CartesianPlotLegend); this->setVisible(!d->isVisible()); } //############################################################################## //######################### Private implementation ############################# //############################################################################## CartesianPlotLegendPrivate::CartesianPlotLegendPrivate(CartesianPlotLegend *owner):q(owner), suppressItemChangeEvent(false), suppressRetransform(false), m_printing(false), m_hovered(false) { setAcceptHoverEvents(true); } QString CartesianPlotLegendPrivate::name() const { return q->name(); } QRectF CartesianPlotLegendPrivate::boundingRect() const { return rect; } void CartesianPlotLegendPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } /*! Returns the shape of the CartesianPlotLegend as a QPainterPath in local coordinates */ QPainterPath CartesianPlotLegendPrivate::shape() const { QPainterPath path; if ( qFuzzyIsNull(borderCornerRadius) ) path.addRect(rect); else path.addRoundedRect(rect, borderCornerRadius, borderCornerRadius); return path; } bool CartesianPlotLegendPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); return oldValue; } /*! recalculates the rectangular of the legend. */ void CartesianPlotLegendPrivate::retransform() { if (suppressRetransform) return; prepareGeometryChange(); curvesList.clear(); for (auto* curve : q->m_plot->children()) { if (curve && curve->isVisible()) curvesList.push_back(curve); } int curveCount = curvesList.size(); columnCount = (curveCount= curveCount ) break; curve = curvesList.at(index); if (curve) { if (!curve->isVisible()) continue; w = fm.width(curve->name()); if (w>maxTextWidth) maxTextWidth = w; } } maxColumnTextWidths.append(maxTextWidth); legendWidth += maxTextWidth; } legendWidth += layoutLeftMargin + layoutRightMargin; //margins legendWidth += columnCount*lineSymbolWidth + layoutHorizontalSpacing; //width of the columns without the text legendWidth += (columnCount-1)*2*layoutHorizontalSpacing; //spacings between the columns if (title->isVisible() && !title->text().text.isEmpty()) { float titleWidth = title->graphicsItem()->boundingRect().width(); if (titleWidth > legendWidth) legendWidth = titleWidth; } //determine the height of the legend float legendHeight = layoutTopMargin + layoutBottomMargin; //margins legendHeight += rowCount*h; //height of the rows legendHeight += (rowCount-1)*layoutVerticalSpacing; //spacing between the rows if (title->isVisible() && !title->text().text.isEmpty()) legendHeight += title->graphicsItem()->boundingRect().height(); //legend title rect.setX(-legendWidth/2); rect.setY(-legendHeight/2); rect.setWidth(legendWidth); rect.setHeight(legendHeight); updatePosition(); } /*! calculates the position of the legend, when the position relative to the parent was specified (left, right, etc.) */ void CartesianPlotLegendPrivate::updatePosition() { //position the legend relative to the actual plot size minus small offset //TODO: make the offset dependent on the size of axis ticks. const QRectF parentRect = q->m_plot->dataRect(); float hOffset = Worksheet::convertToSceneUnits(10, Worksheet::Point); float vOffset = Worksheet::convertToSceneUnits(10, Worksheet::Point); if (position.horizontalPosition != CartesianPlotLegend::hPositionCustom) { if (position.horizontalPosition == CartesianPlotLegend::hPositionLeft) position.point.setX(parentRect.x() + rect.width()/2 + hOffset); else if (position.horizontalPosition == CartesianPlotLegend::hPositionCenter) position.point.setX(parentRect.x() + parentRect.width()/2); else if (position.horizontalPosition == CartesianPlotLegend::hPositionRight) position.point.setX(parentRect.x() + parentRect.width() - rect.width()/2 - hOffset); } if (position.verticalPosition != CartesianPlotLegend::vPositionCustom) { if (position.verticalPosition == CartesianPlotLegend::vPositionTop) position.point.setY(parentRect.y() + rect.height()/2 + vOffset); else if (position.verticalPosition == CartesianPlotLegend::vPositionCenter) position.point.setY(parentRect.y() + parentRect.height()/2); else if (position.verticalPosition == CartesianPlotLegend::vPositionBottom) position.point.setY(parentRect.y() + parentRect.height() - rect.height()/2 -vOffset); } suppressItemChangeEvent=true; setPos(position.point); suppressItemChangeEvent=false; emit q->positionChanged(position); suppressRetransform = true; title->retransform(); suppressRetransform = false; } /*! Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the legend. \sa QGraphicsItem::paint(). */ void CartesianPlotLegendPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option); Q_UNUSED(widget); if (!isVisible()) return; painter->save(); //draw the area painter->setOpacity(backgroundOpacity); painter->setPen(Qt::NoPen); if (backgroundType == PlotArea::Color) { switch (backgroundColorStyle) { case PlotArea::SingleColor:{ painter->setBrush(QBrush(backgroundFirstColor)); break; } case PlotArea::HorizontalLinearGradient:{ QLinearGradient linearGrad(rect.topLeft(), rect.topRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient:{ QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient:{ QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient:{ QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight()); linearGrad.setColorAt(0, backgroundFirstColor); linearGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient:{ QRadialGradient radialGrad(rect.center(), rect.width()/2); radialGrad.setColorAt(0, backgroundFirstColor); radialGrad.setColorAt(1, backgroundSecondColor); painter->setBrush(QBrush(radialGrad)); break; } } } else if (backgroundType == PlotArea::Image) { if ( !backgroundFileName.trimmed().isEmpty() ) { QPixmap pix(backgroundFileName); switch (backgroundImageStyle) { case PlotArea::ScaledCropped: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatioByExpanding,Qt::SmoothTransformation); painter->drawPixmap(rect.topLeft(),pix); break; case PlotArea::Scaled: pix = pix.scaled(rect.size().toSize(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(rect.topLeft(),pix); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(rect.topLeft(),pix); break; case PlotArea::Centered: painter->drawPixmap(QPointF(rect.center().x()-pix.size().width()/2,rect.center().y()-pix.size().height()/2),pix); break; case PlotArea::Tiled: painter->drawTiledPixmap(rect,pix); break; case PlotArea::CenterTiled: painter->drawTiledPixmap(rect,pix,QPoint(rect.size().width()/2,rect.size().height()/2)); } } } else if (backgroundType == PlotArea::Pattern) { painter->setBrush(QBrush(backgroundFirstColor,backgroundBrushStyle)); } if ( qFuzzyIsNull(borderCornerRadius) ) painter->drawRect(rect); else painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); //draw the border if (borderPen.style() != Qt::NoPen) { painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); painter->setOpacity(borderOpacity); if ( qFuzzyIsNull(borderCornerRadius) ) painter->drawRect(rect); else painter->drawRoundedRect(rect, borderCornerRadius, borderCornerRadius); } //draw curve's line+symbol and the names int curveCount = curvesList.size(); QFontMetrics fm(labelFont); float h=fm.ascent(); XYCurve* curve; painter->setFont(labelFont); //translate to left upper conner of the bounding rect plus the layout offset and the height of the title painter->translate(-rect.width()/2+layoutLeftMargin, -rect.height()/2+layoutTopMargin); if (title->isVisible() && !title->text().text.isEmpty()) painter->translate(0, title->graphicsItem()->boundingRect().height()); painter->save(); int index; for (int c=0; c= curveCount ) break; curve = curvesList.at(index); //curve's line (painted at the half of the ascent size) if (curve->lineType() != XYCurve::NoLine) { painter->setPen(curve->linePen()); painter->setOpacity(curve->lineOpacity()); painter->drawLine(0, h/2, lineSymbolWidth, h/2); } //error bars if ( (curve->xErrorType() != XYCurve::NoError && curve->xErrorPlusColumn()) || (curve->yErrorType() != XYCurve::NoError && curve->yErrorPlusColumn()) ) { painter->setOpacity(curve->errorBarsOpacity()); painter->setPen(curve->errorBarsPen()); //curve's error bars for x float errorBarsSize = Worksheet::convertToSceneUnits(10, Worksheet::Point); if (curve->symbolsStyle()!=Symbol::NoSymbols && errorBarsSizesymbolsSize()*1.4) errorBarsSize = curve->symbolsSize()*1.4; switch (curve->errorBarsType()) { case XYCurve::ErrorBarsSimple: //horiz. line if (curve->xErrorType() != XYCurve::NoError) painter->drawLine(lineSymbolWidth/2-errorBarsSize/2, h/2, lineSymbolWidth/2+errorBarsSize/2, h/2); //vert. line if (curve->yErrorType() != XYCurve::NoError) painter->drawLine(lineSymbolWidth/2, h/2-errorBarsSize/2, lineSymbolWidth/2, h/2+errorBarsSize/2); break; case XYCurve::ErrorBarsWithEnds: //horiz. line if (curve->xErrorType() != XYCurve::NoError) { painter->drawLine(lineSymbolWidth/2-errorBarsSize/2, h/2, lineSymbolWidth/2+errorBarsSize/2, h/2); //caps for the horiz. line painter->drawLine(lineSymbolWidth/2-errorBarsSize/2, h/2-errorBarsSize/4, lineSymbolWidth/2-errorBarsSize/2, h/2+errorBarsSize/4); painter->drawLine(lineSymbolWidth/2+errorBarsSize/2, h/2-errorBarsSize/4, lineSymbolWidth/2+errorBarsSize/2, h/2+errorBarsSize/4); } //vert. line if (curve->yErrorType() != XYCurve::NoError) { painter->drawLine(lineSymbolWidth/2, h/2-errorBarsSize/2, lineSymbolWidth/2, h/2+errorBarsSize/2); //caps for the vert. line painter->drawLine(lineSymbolWidth/2-errorBarsSize/4, h/2-errorBarsSize/2, lineSymbolWidth/2+errorBarsSize/4, h/2-errorBarsSize/2); painter->drawLine(lineSymbolWidth/2-errorBarsSize/4, h/2+errorBarsSize/2, lineSymbolWidth/2+errorBarsSize/4, h/2+errorBarsSize/2); } break; } } //curve's symbol if (curve->symbolsStyle()!=Symbol::NoSymbols) { painter->setOpacity(curve->symbolsOpacity()); painter->setBrush(curve->symbolsBrush()); painter->setPen(curve->symbolsPen()); QPainterPath path = Symbol::pathFromStyle(curve->symbolsStyle()); QTransform trafo; trafo.scale(curve->symbolsSize(), curve->symbolsSize()); path = trafo.map(path); if (curve->symbolsRotationAngle() != 0) { trafo.reset(); trafo.rotate(curve->symbolsRotationAngle()); path = trafo.map(path); } painter->translate(QPointF(lineSymbolWidth/2, h/2)); painter->drawPath(path); painter->translate(-QPointF(lineSymbolWidth/2, h/2)); } //curve's name painter->setPen(QPen(labelColor)); painter->setOpacity(1.0); //TODO: support HTML text? painter->drawText(QPoint(lineSymbolWidth+layoutHorizontalSpacing, h), curve->name()); painter->translate(0,layoutVerticalSpacing+h); } //translate to the beginning of the next column painter->restore(); int deltaX = lineSymbolWidth+layoutHorizontalSpacing+maxColumnTextWidths.at(c); //the width of the current columns deltaX += 2*layoutHorizontalSpacing; //spacing between two columns painter->translate(deltaX,0); painter->save(); } painter->restore(); painter->restore(); if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(shape()); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(shape()); } } QVariant CartesianPlotLegendPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (suppressItemChangeEvent) return value; if (change == QGraphicsItem::ItemPositionChange) { //convert item's center point in parent's coordinates CartesianPlotLegend::PositionWrapper tempPosition; tempPosition.point = value.toPointF(); tempPosition.horizontalPosition = CartesianPlotLegend::hPositionCustom; tempPosition.verticalPosition = CartesianPlotLegend::vPositionCustom; //emit the signals in order to notify the UI. //we don't set the position related member variables during the mouse movements. //this is done on mouse release events only. emit q->positionChanged(tempPosition); } return QGraphicsItem::itemChange(change, value); } void CartesianPlotLegendPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { //convert position of the item in parent coordinates to label's position QPointF point = pos(); if (point!=position.point) { //position was changed -> set the position related member variables suppressRetransform = true; CartesianPlotLegend::PositionWrapper tempPosition; tempPosition.point = point; tempPosition.horizontalPosition = CartesianPlotLegend::hPositionCustom; tempPosition.verticalPosition = CartesianPlotLegend::vPositionCustom; q->setPosition(tempPosition); suppressRetransform = false; } QGraphicsItem::mouseReleaseEvent(event); } void CartesianPlotLegendPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void CartesianPlotLegendPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void CartesianPlotLegend::save(QXmlStreamWriter* writer) const { Q_D(const CartesianPlotLegend); writer->writeStartElement( "cartesianPlotLegend" ); writeBasicAttributes( writer ); writeCommentElement( writer ); //general writer->writeStartElement( "general" ); WRITE_QCOLOR(d->labelColor); WRITE_QFONT(d->labelFont); writer->writeAttribute( "columnMajor", QString::number(d->labelColumnMajor) ); writer->writeAttribute( "lineSymbolWidth", QString::number(d->lineSymbolWidth) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->position.point.x()) ); writer->writeAttribute( "y", QString::number(d->position.point.y()) ); writer->writeAttribute( "horizontalPosition", QString::number(d->position.horizontalPosition) ); writer->writeAttribute( "verticalPosition", QString::number(d->position.verticalPosition) ); writer->writeEndElement(); //title d->title->save(writer); //background writer->writeStartElement( "background" ); writer->writeAttribute( "type", QString::number(d->backgroundType) ); writer->writeAttribute( "colorStyle", QString::number(d->backgroundColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->backgroundImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->backgroundBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->backgroundFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->backgroundFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->backgroundFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->backgroundSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->backgroundSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->backgroundSecondColor.blue()) ); writer->writeAttribute( "fileName", d->backgroundFileName ); writer->writeAttribute( "opacity", QString::number(d->backgroundOpacity) ); writer->writeEndElement(); //border writer->writeStartElement( "border" ); WRITE_QPEN(d->borderPen); writer->writeAttribute( "borderOpacity", QString::number(d->borderOpacity) ); writer->writeEndElement(); //layout writer->writeStartElement( "layout" ); writer->writeAttribute( "topMargin", QString::number(d->layoutTopMargin) ); writer->writeAttribute( "bottomMargin", QString::number(d->layoutBottomMargin) ); writer->writeAttribute( "leftMargin", QString::number(d->layoutLeftMargin) ); writer->writeAttribute( "rightMargin", QString::number(d->layoutRightMargin) ); writer->writeAttribute( "verticalSpacing", QString::number(d->layoutVerticalSpacing) ); writer->writeAttribute( "horizontalSpacing", QString::number(d->layoutHorizontalSpacing) ); writer->writeAttribute( "columnCount", QString::number(d->layoutColumnCount) ); writer->writeEndElement(); writer->writeEndElement(); // close "cartesianPlotLegend" section } //! Load from XML bool CartesianPlotLegend::load(XmlStreamReader* reader, bool preview) { Q_D(CartesianPlotLegend); if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "cartesianPlotLegend") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); READ_QCOLOR(d->labelColor); READ_QFONT(d->labelFont); str = attribs.value("columnMajor").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'columnMajor'")); + reader->raiseWarning(attributeWarning.subs("columnMajor").toString()); else d->labelColumnMajor = str.toInt(); str = attribs.value("lineSymbolWidth").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'lineSymbolWidth'")); + reader->raiseWarning(attributeWarning.subs("lineSymbolWidth").toString()); else d->lineSymbolWidth = str.toDouble(); str = attribs.value("visible").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.point.setX(str.toDouble()); str = attribs.value("y").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.point.setY(str.toDouble()); str = attribs.value("horizontalPosition").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'horizontalPosition'")); + reader->raiseWarning(attributeWarning.subs("horizontalPosition").toString()); else d->position.horizontalPosition = (CartesianPlotLegend::HorizontalPosition)str.toInt(); str = attribs.value("verticalPosition").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'verticalPosition'")); + reader->raiseWarning(attributeWarning.subs("verticalPosition").toString()); else d->position.verticalPosition = (CartesianPlotLegend::VerticalPosition)str.toInt(); } else if (reader->name() == "textLabel") { if (!d->title->load(reader, preview)) { delete d->title; d->title=0; return false; } } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); str = attribs.value("type").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("type")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->backgroundType = PlotArea::BackgroundType(str.toInt()); str = attribs.value("colorStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("colorStyle")); + reader->raiseWarning(attributeWarning.subs("colorStyle").toString()); else d->backgroundColorStyle = PlotArea::BackgroundColorStyle(str.toInt()); str = attribs.value("imageStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("imageStyle")); + reader->raiseWarning(attributeWarning.subs("imageStyle").toString()); else d->backgroundImageStyle = PlotArea::BackgroundImageStyle(str.toInt()); str = attribs.value("brushStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("brushStyle")); + reader->raiseWarning(attributeWarning.subs("brushStyle").toString()); else d->backgroundBrushStyle = Qt::BrushStyle(str.toInt()); str = attribs.value("firstColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_r")); + reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->backgroundFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_g")); + reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->backgroundFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_b")); + reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->backgroundFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_r")); + reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->backgroundSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_g")); + reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->backgroundSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_b")); + reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->backgroundSecondColor.setBlue(str.toInt()); str = attribs.value("fileName").toString(); d->backgroundFileName = str; str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("opacity")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->backgroundOpacity = str.toDouble(); } else if (!preview && reader->name() == "border") { attribs = reader->attributes(); READ_QPEN(d->borderPen); str = attribs.value("borderOpacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("borderOpacity")); + reader->raiseWarning(attributeWarning.subs("borderOpacity").toString()); else d->borderOpacity = str.toDouble(); } else if (!preview && reader->name() == "layout") { attribs = reader->attributes(); str = attribs.value("topMargin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("topMargin")); + reader->raiseWarning(attributeWarning.subs("topMargin").toString()); else d->layoutTopMargin = str.toDouble(); str = attribs.value("bottomMargin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("bottomMargin")); + reader->raiseWarning(attributeWarning.subs("bottomMargin").toString()); else d->layoutBottomMargin = str.toDouble(); str = attribs.value("leftMargin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("leftMargin")); + reader->raiseWarning(attributeWarning.subs("leftMargin").toString()); else d->layoutLeftMargin = str.toDouble(); str = attribs.value("rightMargin").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("rightMargin")); + reader->raiseWarning(attributeWarning.subs("rightMargin").toString()); else d->layoutRightMargin = str.toDouble(); str = attribs.value("verticalSpacing").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("verticalSpacing")); + reader->raiseWarning(attributeWarning.subs("verticalSpacing").toString()); else d->layoutVerticalSpacing = str.toDouble(); str = attribs.value("horizontalSpacing").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("horizontalSpacing")); + reader->raiseWarning(attributeWarning.subs("horizontalSpacing").toString()); else d->layoutHorizontalSpacing = str.toDouble(); str = attribs.value("columnCount").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("columnCount")); + reader->raiseWarning(attributeWarning.subs("columnCount").toString()); else d->layoutColumnCount = str.toInt(); } } return true; } void CartesianPlotLegend::loadThemeConfig(const KConfig& config) { KConfigGroup groupLabel = config.group("Label"); this->setLabelColor(groupLabel.readEntry("FontColor", QColor(Qt::white))); const KConfigGroup group = config.group("CartesianPlot"); this->setBackgroundBrushStyle((Qt::BrushStyle)group.readEntry("BackgroundBrushStyle",(int) this->backgroundBrushStyle())); this->setBackgroundColorStyle((PlotArea::BackgroundColorStyle)(group.readEntry("BackgroundColorStyle",(int) this->backgroundColorStyle()))); this->setBackgroundFirstColor(group.readEntry("BackgroundFirstColor",(QColor) this->backgroundFirstColor())); this->setBackgroundImageStyle((PlotArea::BackgroundImageStyle)group.readEntry("BackgroundImageStyle",(int) this->backgroundImageStyle())); this->setBackgroundOpacity(group.readEntry("BackgroundOpacity", this->backgroundOpacity())); this->setBackgroundSecondColor(group.readEntry("BackgroundSecondColor",(QColor) this->backgroundSecondColor())); this->setBackgroundType((PlotArea::BackgroundType)(group.readEntry("BackgroundType",(int) this->backgroundType()))); this->borderPen().setColor(group.readEntry("BorderColor",(QColor) this->borderPen().color())); this->setBorderCornerRadius(group.readEntry("BorderCornerRadius", this->borderCornerRadius())); this->setBorderOpacity(group.readEntry("BorderOpacity", this->borderOpacity())); this->borderPen().setStyle((Qt::PenStyle)(group.readEntry("BorderStyle", (int) this->borderPen().style()))); this->borderPen().setWidthF(group.readEntry("BorderWidth", this->borderPen().widthF())); title()->loadThemeConfig(config); } diff --git a/src/backend/worksheet/plots/cartesian/CustomPoint.cpp b/src/backend/worksheet/plots/cartesian/CustomPoint.cpp index bbcf7f4ad..a0d788d16 100644 --- a/src/backend/worksheet/plots/cartesian/CustomPoint.cpp +++ b/src/backend/worksheet/plots/cartesian/CustomPoint.cpp @@ -1,493 +1,493 @@ /*************************************************************************** File : CustomPoint.cpp Project : LabPlot Description : Custom user-defined point on the plot -------------------------------------------------------------------- Copyright : (C) 2015 Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015 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 "CustomPoint.h" #include "CustomPointPrivate.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include #include #include #include #include -#include +#include /** * \class CustomPoint * \brief A customizable point. * * The position can be either specified by mouse events or by providing the * x- and y- coordinates in parent's coordinate system */ CustomPoint::CustomPoint(const CartesianPlot* plot, const QString& name):WorksheetElement(name), d_ptr(new CustomPointPrivate(this,plot)) { init(); } CustomPoint::CustomPoint(const QString& name, CustomPointPrivate* dd):WorksheetElement(name), d_ptr(dd) { init(); } CustomPoint::~CustomPoint() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void CustomPoint::init() { Q_D(CustomPoint); KConfig config; KConfigGroup group; group = config.group("CustomPoint"); d->position.setX( group.readEntry("PositionXValue", d->plot->xMin() + (d->plot->xMax()-d->plot->xMin())/2) ); d->position.setY( group.readEntry("PositionYValue", d->plot->yMin() + (d->plot->yMax()-d->plot->yMin())/2) ); d->symbolStyle = (Symbol::Style)group.readEntry("SymbolStyle", (int)Symbol::Circle); d->symbolSize = group.readEntry("SymbolSize", Worksheet::convertToSceneUnits(5, Worksheet::Point)); d->symbolRotationAngle = group.readEntry("SymbolRotation", 0.0); d->symbolOpacity = group.readEntry("SymbolOpacity", 1.0); d->symbolBrush.setStyle( (Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern) ); d->symbolBrush.setColor( group.readEntry("SymbolFillingColor", QColor(Qt::red)) ); d->symbolPen.setStyle( (Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine) ); d->symbolPen.setColor( group.readEntry("SymbolBorderColor", QColor(Qt::black)) ); d->symbolPen.setWidthF( group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Point)) ); this->initActions(); retransform(); } void CustomPoint::initActions() { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &CustomPoint::visibilityChanged); } /*! Returns an icon to be used in the project explorer. */ QIcon CustomPoint::icon() const { return QIcon::fromTheme("draw-cross"); } QMenu* CustomPoint::createContextMenu() { QMenu* menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); return menu; } QGraphicsItem* CustomPoint::graphicsItem() const { return d_ptr; } void CustomPoint::retransform() { Q_D(CustomPoint); d->retransform(); } void CustomPoint::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { Q_UNUSED(horizontalRatio); Q_UNUSED(verticalRatio); Q_UNUSED(pageResize); } /* ============================ getter methods ================= */ CLASS_SHARED_D_READER_IMPL(CustomPoint, QPointF, position, position) //symbols BASIC_SHARED_D_READER_IMPL(CustomPoint, Symbol::Style, symbolStyle, symbolStyle) BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolOpacity, symbolOpacity) BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolRotationAngle, symbolRotationAngle) BASIC_SHARED_D_READER_IMPL(CustomPoint, qreal, symbolSize, symbolSize) CLASS_SHARED_D_READER_IMPL(CustomPoint, QBrush, symbolBrush, symbolBrush) CLASS_SHARED_D_READER_IMPL(CustomPoint, QPen, symbolPen, symbolPen) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetPosition, QPointF, position, retransform) void CustomPoint::setPosition(const QPointF& position) { Q_D(CustomPoint); if (position != d->position) - exec(new CustomPointSetPositionCmd(d, position, i18n("%1: set position"))); + exec(new CustomPointSetPositionCmd(d, position, ki18n("%1: set position"))); } //Symbol STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolStyle, Symbol::Style, symbolStyle, retransform) void CustomPoint::setSymbolStyle(Symbol::Style style) { Q_D(CustomPoint); if (style != d->symbolStyle) - exec(new CustomPointSetSymbolStyleCmd(d, style, i18n("%1: set symbol style"))); + exec(new CustomPointSetSymbolStyleCmd(d, style, ki18n("%1: set symbol style"))); } STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolSize, qreal, symbolSize, retransform) void CustomPoint::setSymbolSize(qreal size) { Q_D(CustomPoint); if (!qFuzzyCompare(1 + size, 1 + d->symbolSize)) - exec(new CustomPointSetSymbolSizeCmd(d, size, i18n("%1: set symbol size"))); + exec(new CustomPointSetSymbolSizeCmd(d, size, ki18n("%1: set symbol size"))); } STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolRotationAngle, qreal, symbolRotationAngle, retransform) void CustomPoint::setSymbolRotationAngle(qreal angle) { Q_D(CustomPoint); if (!qFuzzyCompare(1 + angle, 1 + d->symbolRotationAngle)) - exec(new CustomPointSetSymbolRotationAngleCmd(d, angle, i18n("%1: rotate symbols"))); + exec(new CustomPointSetSymbolRotationAngleCmd(d, angle, ki18n("%1: rotate symbols"))); } STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolBrush, QBrush, symbolBrush, update) void CustomPoint::setSymbolBrush(const QBrush &brush) { Q_D(CustomPoint); if (brush != d->symbolBrush) - exec(new CustomPointSetSymbolBrushCmd(d, brush, i18n("%1: set symbol filling"))); + exec(new CustomPointSetSymbolBrushCmd(d, brush, ki18n("%1: set symbol filling"))); } STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolPen, QPen, symbolPen, update) void CustomPoint::setSymbolPen(const QPen &pen) { Q_D(CustomPoint); if (pen != d->symbolPen) - exec(new CustomPointSetSymbolPenCmd(d, pen, i18n("%1: set symbol outline style"))); + exec(new CustomPointSetSymbolPenCmd(d, pen, ki18n("%1: set symbol outline style"))); } STD_SETTER_CMD_IMPL_F_S(CustomPoint, SetSymbolOpacity, qreal, symbolOpacity, update) void CustomPoint::setSymbolOpacity(qreal opacity) { Q_D(CustomPoint); if (opacity != d->symbolOpacity) - exec(new CustomPointSetSymbolOpacityCmd(d, opacity, i18n("%1: set symbol opacity"))); + exec(new CustomPointSetSymbolOpacityCmd(d, opacity, ki18n("%1: set symbol opacity"))); } STD_SWAP_METHOD_SETTER_CMD_IMPL_F(CustomPoint, SetVisible, bool, swapVisible, retransform); void CustomPoint::setVisible(bool on) { Q_D(CustomPoint); - exec(new CustomPointSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new CustomPointSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool CustomPoint::isVisible() const { Q_D(const CustomPoint); return d->isVisible(); } void CustomPoint::setPrinting(bool on) { Q_D(CustomPoint); d->m_printing = on; } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void CustomPoint::visibilityChanged() { Q_D(const CustomPoint); this->setVisible(!d->isVisible()); } //############################################################################## //####################### Private implementation ############################### //############################################################################## CustomPointPrivate::CustomPointPrivate(CustomPoint* owner, const CartesianPlot* p) : plot(p), suppressItemChangeEvent(false), suppressRetransform(false), m_printing(false), m_hovered(false), m_visible(true), q(owner) { setFlag(QGraphicsItem::ItemSendsGeometryChanges); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setAcceptHoverEvents(true); } QString CustomPointPrivate::name() const { return q->name(); } /*! calculates the position and the bounding box of the item/point. Called on geometry or properties changes. */ void CustomPointPrivate::retransform() { if (suppressRetransform) return; //calculate the point in the scene coordinates const CartesianCoordinateSystem* cSystem = dynamic_cast(plot->coordinateSystem()); QVector list, listScene; list<mapLogicalToScene(list, CartesianCoordinateSystem::DefaultMapping); if (!listScene.isEmpty()) { m_visible = true; positionScene = listScene.at(0); suppressItemChangeEvent=true; setPos(positionScene); suppressItemChangeEvent=false; } else { m_visible = false; } recalcShapeAndBoundingRect(); } bool CustomPointPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->changed(); emit q->visibleChanged(on); return oldValue; } /*! Returns the outer bounds of the item as a rectangle. */ QRectF CustomPointPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath CustomPointPrivate::shape() const { return pointShape; } /*! recalculates the outer bounds and the shape of the item. */ void CustomPointPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); pointShape = QPainterPath(); if (m_visible && symbolStyle != Symbol::NoSymbols) { QPainterPath path = Symbol::pathFromStyle(symbolStyle); QTransform trafo; trafo.scale(symbolSize, symbolSize); path = trafo.map(path); trafo.reset(); if (symbolRotationAngle != 0) { trafo.rotate(symbolRotationAngle); path = trafo.map(path); } pointShape = trafo.map(path); transformedBoundingRectangle = pointShape.boundingRect(); } } void CustomPointPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (!m_visible) return; if (symbolStyle != Symbol::NoSymbols) { painter->setOpacity(symbolOpacity); painter->setPen(symbolPen); painter->setBrush(symbolBrush); painter->drawPath(pointShape); } if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(pointShape); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(pointShape); } } QVariant CustomPointPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (suppressItemChangeEvent) return value; if (change == QGraphicsItem::ItemPositionChange) { //emit the signals in order to notify the UI. //we don't set the position related member variables during the mouse movements. //this is done on mouse release events only. const CartesianCoordinateSystem* cSystem = dynamic_cast(plot->coordinateSystem()); emit q->positionChanged(cSystem->mapSceneToLogical(value.toPointF())); } return QGraphicsItem::itemChange(change, value); } void CustomPointPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { //position was changed -> set the position member variables suppressRetransform = true; const CartesianCoordinateSystem* cSystem = dynamic_cast(plot->coordinateSystem()); q->setPosition(cSystem->mapSceneToLogical(pos())); suppressRetransform = false; QGraphicsItem::mouseReleaseEvent(event); } void CustomPointPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void CustomPointPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void CustomPointPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void CustomPoint::save(QXmlStreamWriter* writer) const { Q_D(const CustomPoint); writer->writeStartElement("customPoint"); writeBasicAttributes(writer); writeCommentElement(writer); //geometry writer->writeStartElement("geometry"); writer->writeAttribute( "x", QString::number(d->position.x()) ); writer->writeAttribute( "y", QString::number(d->position.y()) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //Symbols writer->writeStartElement("symbol"); writer->writeAttribute( "symbolStyle", QString::number(d->symbolStyle) ); writer->writeAttribute( "opacity", QString::number(d->symbolOpacity) ); writer->writeAttribute( "rotation", QString::number(d->symbolRotationAngle) ); writer->writeAttribute( "size", QString::number(d->symbolSize) ); WRITE_QBRUSH(d->symbolBrush); WRITE_QPEN(d->symbolPen); writer->writeEndElement(); writer->writeEndElement(); // close "CustomPoint" section } //! Load from XML bool CustomPoint::load(XmlStreamReader* reader, bool preview) { Q_D(CustomPoint); if (!reader->isStartElement() || reader->name() != "customPoint") { reader->raiseError(i18n("no custom point element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "customPoint") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.setX(str.toDouble()); str = attribs.value("y").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.setY(str.toDouble()); str = attribs.value("visible").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "symbol") { attribs = reader->attributes(); str = attribs.value("symbolStyle").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'symbolStyle'")); + reader->raiseWarning(attributeWarning.subs("symbolStyle").toString()); else d->symbolStyle = (Symbol::Style)str.toInt(); str = attribs.value("opacity").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->symbolOpacity = str.toDouble(); str = attribs.value("rotation").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'rotation'")); + reader->raiseWarning(attributeWarning.subs("rotation").toString()); else d->symbolRotationAngle = str.toDouble(); str = attribs.value("size").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'size'")); + reader->raiseWarning(attributeWarning.subs("size").toString()); else d->symbolSize = str.toDouble(); READ_QBRUSH(d->symbolBrush); READ_QPEN(d->symbolPen); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } retransform(); return true; } diff --git a/src/backend/worksheet/plots/cartesian/Histogram.cpp b/src/backend/worksheet/plots/cartesian/Histogram.cpp index 59a3789b6..b52f960ee 100644 --- a/src/backend/worksheet/plots/cartesian/Histogram.cpp +++ b/src/backend/worksheet/plots/cartesian/Histogram.cpp @@ -1,1593 +1,1594 @@ /*************************************************************************** File : Histogram.cpp Project : LabPlot Description : Histogram -------------------------------------------------------------------- Copyright : (C) 2016 Anu Mittal (anu22mittal@gmail.com) Copyright : (C) 2016-2017 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 * * * ***************************************************************************/ /*! \class Histogram \brief A 2D-curve, provides an interface for editing many properties of the curve. \ingroup worksheet */ #include "Histogram.h" #include "HistogramPrivate.h" #include "backend/core/column/Column.h" #include "backend/worksheet/plots/AbstractCoordinateSystem.h" #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/lib/commandtemplates.h" #include "backend/worksheet/Worksheet.h" #include "backend/lib/XmlStreamReader.h" #include "tools/ImageTools.h" #include #include #include +#include #include -#include +#include extern "C" { #include #include #include } Histogram::Histogram(const QString &name) : WorksheetElement(name), d_ptr(new HistogramPrivate(this)) { init(); } Histogram::Histogram(const QString &name, HistogramPrivate *dd) : WorksheetElement(name), d_ptr(dd) { init(); } void Histogram::init() { Q_D(Histogram); KConfig config; KConfigGroup group = config.group("Histogram"); d->xColumn = NULL; d->histogramType = (Histogram::HistogramType) group.readEntry("histogramType", (int)Histogram::Ordinary); d->binsOption = (Histogram::BinsOption) group.readEntry("binOption", (int)Histogram::Number); d->lineSkipGaps = group.readEntry("SkipLineGaps", false); d->lineInterpolationPointsCount = group.readEntry("LineInterpolationPointsCount", 1); d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int)Qt::SolidLine) ); d->linePen.setColor( group.readEntry("LineColor", QColor(Qt::black)) ); d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)) ); d->lineOpacity = group.readEntry("LineOpacity", 1.0); d->valuesType = (Histogram::ValuesType) group.readEntry("ValuesType", (int)Histogram::NoValues); d->valuesColumn = NULL; d->valuesPosition = (Histogram::ValuesPosition) group.readEntry("ValuesPosition", (int)Histogram::ValuesAbove); d->valuesDistance = group.readEntry("ValuesDistance", Worksheet::convertToSceneUnits(5, Worksheet::Point)); d->valuesRotationAngle = group.readEntry("ValuesRotation", 0.0); d->valuesOpacity = group.readEntry("ValuesOpacity", 1.0); d->valuesPrefix = group.readEntry("ValuesPrefix", ""); d->valuesSuffix = group.readEntry("ValuesSuffix", ""); d->valuesFont = group.readEntry("ValuesFont", QFont()); d->valuesFont.setPixelSize( Worksheet::convertToSceneUnits( 8, Worksheet::Point ) ); d->valuesColor = group.readEntry("ValuesColor", QColor(Qt::black)); d->fillingPosition = (Histogram::FillingPosition) group.readEntry("FillingPosition", (int)Histogram::NoFilling); d->fillingType = (PlotArea::BackgroundType) group.readEntry("FillingType", (int)PlotArea::Color); d->fillingColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("FillingColorStyle", (int) PlotArea::SingleColor); d->fillingImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("FillingImageStyle", (int) PlotArea::Scaled); d->fillingBrushStyle = (Qt::BrushStyle) group.readEntry("FillingBrushStyle", (int) Qt::SolidPattern); d->fillingFileName = group.readEntry("FillingFileName", QString()); d->fillingFirstColor = group.readEntry("FillingFirstColor", QColor(Qt::white)); d->fillingSecondColor = group.readEntry("FillingSecondColor", QColor(Qt::black)); d->fillingOpacity = group.readEntry("FillingOpacity", 1.0); this->initActions(); } void Histogram::initActions() { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &Histogram::visibilityChangedSlot); } QMenu* Histogram::createContextMenu() { QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); return menu; } /*! Returns an icon to be used in the project explorer. */ QIcon Histogram::icon() const { return QIcon::fromTheme("labplot-xy-curve"); } QGraphicsItem* Histogram::graphicsItem() const { return d_ptr; } STD_SWAP_METHOD_SETTER_CMD_IMPL(Histogram, SetVisible, bool, swapVisible) void Histogram::setVisible(bool on) { Q_D(Histogram); - exec(new HistogramSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new HistogramSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool Histogram::isVisible() const { Q_D(const Histogram); return d->isVisible(); } void Histogram::setPrinting(bool on) { Q_D(Histogram); d->m_printing = on; } void Histogram::setHistrogramType(Histogram::HistogramType histogramType) { d_ptr->histogramType = histogramType; DEBUG(histogramType); } Histogram::HistogramType Histogram::getHistrogramType() { return d_ptr->histogramType; } void Histogram::setbinsOption(Histogram::BinsOption binsOption) { d_ptr->histogramData.binsOption = binsOption; } void Histogram::setBinValue(int binValue) { d_ptr->histogramData.binValue= binValue; } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(Histogram, const AbstractColumn*, xColumn, xColumn) QString& Histogram::xColumnPath() const { return d_ptr->xColumnPath; } CLASS_SHARED_D_READER_IMPL(Histogram, QPen, linePen, linePen) BASIC_SHARED_D_READER_IMPL(Histogram, Histogram::HistogramData, histogramData, histogramData) //values BASIC_SHARED_D_READER_IMPL(Histogram, Histogram::ValuesType, valuesType, valuesType) BASIC_SHARED_D_READER_IMPL(Histogram, const AbstractColumn *, valuesColumn, valuesColumn) QString& Histogram::valuesColumnPath() const { return d_ptr->valuesColumnPath; } BASIC_SHARED_D_READER_IMPL(Histogram, Histogram::ValuesPosition, valuesPosition, valuesPosition) BASIC_SHARED_D_READER_IMPL(Histogram, qreal, valuesDistance, valuesDistance) BASIC_SHARED_D_READER_IMPL(Histogram, qreal, valuesRotationAngle, valuesRotationAngle) BASIC_SHARED_D_READER_IMPL(Histogram, qreal, valuesOpacity, valuesOpacity) CLASS_SHARED_D_READER_IMPL(Histogram, QString, valuesPrefix, valuesPrefix) CLASS_SHARED_D_READER_IMPL(Histogram, QString, valuesSuffix, valuesSuffix) CLASS_SHARED_D_READER_IMPL(Histogram, QColor, valuesColor, valuesColor) CLASS_SHARED_D_READER_IMPL(Histogram, QFont, valuesFont, valuesFont) //filling BASIC_SHARED_D_READER_IMPL(Histogram, Histogram::FillingPosition, fillingPosition, fillingPosition) BASIC_SHARED_D_READER_IMPL(Histogram, PlotArea::BackgroundType, fillingType, fillingType) BASIC_SHARED_D_READER_IMPL(Histogram, PlotArea::BackgroundColorStyle, fillingColorStyle, fillingColorStyle) BASIC_SHARED_D_READER_IMPL(Histogram, PlotArea::BackgroundImageStyle, fillingImageStyle, fillingImageStyle) CLASS_SHARED_D_READER_IMPL(Histogram, Qt::BrushStyle, fillingBrushStyle, fillingBrushStyle) CLASS_SHARED_D_READER_IMPL(Histogram, QColor, fillingFirstColor, fillingFirstColor) CLASS_SHARED_D_READER_IMPL(Histogram, QColor, fillingSecondColor, fillingSecondColor) CLASS_SHARED_D_READER_IMPL(Histogram, QString, fillingFileName, fillingFileName) BASIC_SHARED_D_READER_IMPL(Histogram, qreal, fillingOpacity, fillingOpacity) double Histogram::getYMaximum() const { return d_ptr->getYMaximum(); } bool Histogram::isSourceDataChangedSinceLastPlot() const { Q_D(const Histogram); return d->sourceDataChangedSinceLastPlot; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(Histogram, SetHistogramData, Histogram::HistogramData, histogramData, recalculate); void Histogram::setHistogramData(const Histogram::HistogramData& histogramData) { Q_D(Histogram); if ((histogramData.binValue != d->histogramData.binValue) || (histogramData.binsOption != d->histogramData.binsOption) ) - exec(new HistogramSetHistogramDataCmd(d, histogramData, i18n("%1: set equation"))); + exec(new HistogramSetHistogramDataCmd(d, histogramData, ki18n("%1: set equation"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetXColumn, const AbstractColumn*, xColumn, retransform) void Histogram::setXColumn(const AbstractColumn* column) { Q_D(Histogram); if (column != d->xColumn) { - exec(new HistogramSetXColumnCmd(d, column, i18n("%1: assign x values"))); + exec(new HistogramSetXColumnCmd(d, column, ki18n("%1: assign x values"))); emit sourceDataChangedSinceLastPlot(); //emit xHistogramDataChanged() in order to notify the plot about the changes emit xHistogramDataChanged(); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Histogram::xHistogramDataChanged); connect(column, &AbstractColumn::dataChanged, this, &Histogram::handleSourceDataChanged); //update the curve itself on changes connect(column, &AbstractColumn::dataChanged, this, &Histogram::retransform); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Histogram::xColumnAboutToBeRemoved); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(Histogram, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect) void Histogram::setLinePen(const QPen &pen) { Q_D(Histogram); if (pen != d->linePen) - exec(new HistogramSetLinePenCmd(d, pen, i18n("%1: set line style"))); + exec(new HistogramSetLinePenCmd(d, pen, ki18n("%1: set line style"))); } //Values-Tab STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesType, Histogram::ValuesType, valuesType, updateValues) void Histogram::setValuesType(Histogram::ValuesType type) { Q_D(Histogram); if (type != d->valuesType) - exec(new HistogramSetValuesTypeCmd(d, type, i18n("%1: set values type"))); + exec(new HistogramSetValuesTypeCmd(d, type, ki18n("%1: set values type"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesColumn, const AbstractColumn*, valuesColumn, updateValues) void Histogram::setValuesColumn(const AbstractColumn* column) { Q_D(Histogram); if (column != d->valuesColumn) { - exec(new HistogramSetValuesColumnCmd(d, column, i18n("%1: set values column"))); + exec(new HistogramSetValuesColumnCmd(d, column, ki18n("%1: set values column"))); if (column) { connect(column, &AbstractColumn::dataChanged, this, &Histogram::updateValues); connect(column->parentAspect(), &AbstractAspect::aspectAboutToBeRemoved, this, &Histogram::valuesColumnAboutToBeRemoved); } } } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesPosition, Histogram::ValuesPosition, valuesPosition, updateValues) void Histogram::setValuesPosition(ValuesPosition position) { Q_D(Histogram); if (position != d->valuesPosition) - exec(new HistogramSetValuesPositionCmd(d, position, i18n("%1: set values position"))); + exec(new HistogramSetValuesPositionCmd(d, position, ki18n("%1: set values position"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesDistance, qreal, valuesDistance, updateValues) void Histogram::setValuesDistance(qreal distance) { Q_D(Histogram); if (distance != d->valuesDistance) - exec(new HistogramSetValuesDistanceCmd(d, distance, i18n("%1: set values distance"))); + exec(new HistogramSetValuesDistanceCmd(d, distance, ki18n("%1: set values distance"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesRotationAngle, qreal, valuesRotationAngle, updateValues) void Histogram::setValuesRotationAngle(qreal angle) { Q_D(Histogram); if (!qFuzzyCompare(1 + angle, 1 + d->valuesRotationAngle)) - exec(new HistogramSetValuesRotationAngleCmd(d, angle, i18n("%1: rotate values"))); + exec(new HistogramSetValuesRotationAngleCmd(d, angle, ki18n("%1: rotate values"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesOpacity, qreal, valuesOpacity, updatePixmap) void Histogram::setValuesOpacity(qreal opacity) { Q_D(Histogram); if (opacity != d->valuesOpacity) - exec(new HistogramSetValuesOpacityCmd(d, opacity, i18n("%1: set values opacity"))); + exec(new HistogramSetValuesOpacityCmd(d, opacity, ki18n("%1: set values opacity"))); } //TODO: Format, Precision STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesPrefix, QString, valuesPrefix, updateValues) void Histogram::setValuesPrefix(const QString& prefix) { Q_D(Histogram); if (prefix!= d->valuesPrefix) - exec(new HistogramSetValuesPrefixCmd(d, prefix, i18n("%1: set values prefix"))); + exec(new HistogramSetValuesPrefixCmd(d, prefix, ki18n("%1: set values prefix"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesSuffix, QString, valuesSuffix, updateValues) void Histogram::setValuesSuffix(const QString& suffix) { Q_D(Histogram); if (suffix!= d->valuesSuffix) - exec(new HistogramSetValuesSuffixCmd(d, suffix, i18n("%1: set values suffix"))); + exec(new HistogramSetValuesSuffixCmd(d, suffix, ki18n("%1: set values suffix"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesFont, QFont, valuesFont, updateValues) void Histogram::setValuesFont(const QFont& font) { Q_D(Histogram); if (font!= d->valuesFont) - exec(new HistogramSetValuesFontCmd(d, font, i18n("%1: set values font"))); + exec(new HistogramSetValuesFontCmd(d, font, ki18n("%1: set values font"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetValuesColor, QColor, valuesColor, updatePixmap) void Histogram::setValuesColor(const QColor& color) { Q_D(Histogram); if (color != d->valuesColor) - exec(new HistogramSetValuesColorCmd(d, color, i18n("%1: set values color"))); + exec(new HistogramSetValuesColorCmd(d, color, ki18n("%1: set values color"))); } //Filling STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingPosition, Histogram::FillingPosition, fillingPosition, updateFilling) void Histogram::setFillingPosition(FillingPosition position) { Q_D(Histogram); if (position != d->fillingPosition) - exec(new HistogramSetFillingPositionCmd(d, position, i18n("%1: filling position changed"))); + exec(new HistogramSetFillingPositionCmd(d, position, ki18n("%1: filling position changed"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingType, PlotArea::BackgroundType, fillingType, updatePixmap) void Histogram::setFillingType(PlotArea::BackgroundType type) { Q_D(Histogram); if (type != d->fillingType) - exec(new HistogramSetFillingTypeCmd(d, type, i18n("%1: filling type changed"))); + exec(new HistogramSetFillingTypeCmd(d, type, ki18n("%1: filling type changed"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingColorStyle, PlotArea::BackgroundColorStyle, fillingColorStyle, updatePixmap) void Histogram::setFillingColorStyle(PlotArea::BackgroundColorStyle style) { Q_D(Histogram); if (style != d->fillingColorStyle) - exec(new HistogramSetFillingColorStyleCmd(d, style, i18n("%1: filling color style changed"))); + exec(new HistogramSetFillingColorStyleCmd(d, style, ki18n("%1: filling color style changed"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingImageStyle, PlotArea::BackgroundImageStyle, fillingImageStyle, updatePixmap) void Histogram::setFillingImageStyle(PlotArea::BackgroundImageStyle style) { Q_D(Histogram); if (style != d->fillingImageStyle) - exec(new HistogramSetFillingImageStyleCmd(d, style, i18n("%1: filling image style changed"))); + exec(new HistogramSetFillingImageStyleCmd(d, style, ki18n("%1: filling image style changed"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingBrushStyle, Qt::BrushStyle, fillingBrushStyle, updatePixmap) void Histogram::setFillingBrushStyle(Qt::BrushStyle style) { Q_D(Histogram); if (style != d->fillingBrushStyle) - exec(new HistogramSetFillingBrushStyleCmd(d, style, i18n("%1: filling brush style changed"))); + exec(new HistogramSetFillingBrushStyleCmd(d, style, ki18n("%1: filling brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingFirstColor, QColor, fillingFirstColor, updatePixmap) void Histogram::setFillingFirstColor(const QColor& color) { Q_D(Histogram); if (color!= d->fillingFirstColor) - exec(new HistogramSetFillingFirstColorCmd(d, color, i18n("%1: set filling first color"))); + exec(new HistogramSetFillingFirstColorCmd(d, color, ki18n("%1: set filling first color"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingSecondColor, QColor, fillingSecondColor, updatePixmap) void Histogram::setFillingSecondColor(const QColor& color) { Q_D(Histogram); if (color!= d->fillingSecondColor) - exec(new HistogramSetFillingSecondColorCmd(d, color, i18n("%1: set filling second color"))); + exec(new HistogramSetFillingSecondColorCmd(d, color, ki18n("%1: set filling second color"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingFileName, QString, fillingFileName, updatePixmap) void Histogram::setFillingFileName(const QString& fileName) { Q_D(Histogram); if (fileName!= d->fillingFileName) - exec(new HistogramSetFillingFileNameCmd(d, fileName, i18n("%1: set filling image"))); + exec(new HistogramSetFillingFileNameCmd(d, fileName, ki18n("%1: set filling image"))); } STD_SETTER_CMD_IMPL_F_S(Histogram, SetFillingOpacity, qreal, fillingOpacity, updatePixmap) void Histogram::setFillingOpacity(qreal opacity) { Q_D(Histogram); if (opacity != d->fillingOpacity) - exec(new HistogramSetFillingOpacityCmd(d, opacity, i18n("%1: set filling opacity"))); + exec(new HistogramSetFillingOpacityCmd(d, opacity, ki18n("%1: set filling opacity"))); } //############################################################################## //################################# SLOTS #################################### //############################################################################## void Histogram::retransform() { d_ptr->retransform(); } void Histogram::handleSourceDataChanged() { Q_D(Histogram); d->sourceDataChangedSinceLastPlot = true; emit sourceDataChangedSinceLastPlot(); } //TODO void Histogram::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { Q_UNUSED(pageResize); Q_UNUSED(verticalRatio); Q_D(const Histogram); //setValuesDistance(d->distance*); QFont font=d->valuesFont; font.setPointSizeF(font.pointSizeF()*horizontalRatio); setValuesFont(font); retransform(); } void Histogram::updateValues() { d_ptr->updateValues(); } void Histogram::xColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Histogram); if (aspect == d->xColumn) { d->xColumn = 0; d->retransform(); } } void Histogram::valuesColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(Histogram); if (aspect == d->valuesColumn) { d->valuesColumn = 0; d->updateValues(); } } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void Histogram::visibilityChangedSlot() { Q_D(const Histogram); this->setVisible(!d->isVisible()); } //############################################################################## //######################### Private implementation ############################# //############################################################################## HistogramPrivate::HistogramPrivate(Histogram *owner) : m_printing(false), m_hovered(false), m_suppressRecalc(false), m_suppressRetransform(false), m_hoverEffectImageIsDirty(false), m_selectionEffectImageIsDirty(false), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable, true); setAcceptHoverEvents(true); } QString HistogramPrivate::name() const { return q->name(); } QRectF HistogramPrivate::boundingRect() const { return boundingRectangle; } double HistogramPrivate::getYMaximum() { if (histogram) { double yMaxRange = 0.0; switch(histogramType) { case Histogram::Ordinary: { size_t maxYAddes = gsl_histogram_max_bin(histogram); yMaxRange = gsl_histogram_get(histogram, maxYAddes); break; } case Histogram::Cumulative: { yMaxRange = xColumn->rowCount(); break; } case Histogram::AvgShift: { //TODO } } return yMaxRange; } return -INFINITY; } /*! Returns the shape of the Histogram as a QPainterPath in local coordinates */ QPainterPath HistogramPrivate::shape() const { return curveShape; } void HistogramPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } bool HistogramPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); return oldValue; } /*! recalculates the position of the points to be drawn. Called when the data was changed. Triggers the update of lines, drop lines, symbols etc. */ void HistogramPrivate::retransform() { if (m_suppressRetransform) return; //qDebug()<<"HistogramPrivate::retransform() " << q->name(); symbolPointsLogical.clear(); symbolPointsScene.clear(); connectedPointsLogical.clear(); if (NULL == xColumn) { linePath = QPainterPath(); valuesPath = QPainterPath(); // dropLinePath = QPainterPath(); recalcShapeAndBoundingRect(); return; } int startRow = 0; int endRow = xColumn->rowCount() - 1; QPointF tempPoint; AbstractColumn::ColumnMode xColMode = xColumn->columnMode(); //take over only valid and non masked points. for (int row = startRow; row <= endRow; row++ ) { if (xColumn->isValid(row) && !xColumn->isMasked(row)) { switch(xColMode) { case AbstractColumn::Numeric: tempPoint.setX(xColumn->valueAt(row)); break; case AbstractColumn::Integer: //TODO case AbstractColumn::Text: //TODO case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: //TODO break; } symbolPointsLogical.append(tempPoint); connectedPointsLogical.push_back(true); } else { if (connectedPointsLogical.size()) connectedPointsLogical[connectedPointsLogical.size()-1] = false; } } //calculate the scene coordinates const AbstractPlot* plot = dynamic_cast(q->parentAspect()); if (!plot) return; const CartesianCoordinateSystem* cSystem = dynamic_cast(plot->coordinateSystem()); Q_ASSERT(cSystem); visiblePoints = std::vector(symbolPointsLogical.count(), false); cSystem->mapLogicalToScene(symbolPointsLogical, symbolPointsScene, visiblePoints); m_suppressRecalc = true; updateLines(); updateValues(); m_suppressRecalc = false; } /*! recalculates the painter path for the lines connecting the data points. Called each time when the type of this connection is changed. */ void HistogramPrivate::updateLines() { linePath = QPainterPath(); lines.clear(); const int count=symbolPointsLogical.count(); //nothing to do, if no data points available if (count<=1) { recalcShapeAndBoundingRect(); return; } int startRow = 0; int endRow = xColumn->rowCount() - 1; QPointF tempPoint,tempPoint1; double xAxisMin = xColumn->minimum(); double xAxisMax = xColumn->maximum(); switch (histogramData.binsOption) { case Histogram::Number: bins = (size_t)histogramData.binValue; break; case Histogram::SquareRoot: bins = (size_t)sqrt(histogramData.binValue); break; case Histogram::RiceRule: bins = (size_t)2*cbrt(histogramData.binValue); break; case Histogram::Width: bins = (size_t) (xAxisMax-xAxisMin)/histogramData.binValue; break; case Histogram::SturgisRule: bins =(size_t) 1 + 3.33*log(histogramData.binValue); break; } double width = (xAxisMax-xAxisMin)/bins; histogram = gsl_histogram_alloc (bins); // demo- number of bins gsl_histogram_set_ranges_uniform (histogram, xAxisMin,xAxisMax+1); //checking height of each column /*for(int i=0;i < bins; ++i) { qDebug() <isValid(row) && !xColumn->isMasked(row) ) gsl_histogram_increment(histogram, xColumn->valueAt(row)); } for(size_t i=0; i < bins; ++i) { tempPoint.setX(xAxisMin); tempPoint.setY(0.0); tempPoint1.setX(xAxisMin); tempPoint1.setY(gsl_histogram_get(histogram,i)); lines.append(QLineF(tempPoint, tempPoint1)); tempPoint.setX(xAxisMin); tempPoint.setY(gsl_histogram_get(histogram,i)); tempPoint1.setX(xAxisMin+width); tempPoint1.setY(gsl_histogram_get(histogram,i)); lines.append(QLineF(tempPoint,tempPoint1)); tempPoint.setX(xAxisMin+width); tempPoint.setY(gsl_histogram_get(histogram,i)); tempPoint1.setX(xAxisMin+width); tempPoint1.setY(0.0); lines.append(QLineF(tempPoint, tempPoint1)); tempPoint.setX(xAxisMin+width); tempPoint.setY(0.0); tempPoint1.setX(xAxisMin); tempPoint1.setY(0.0); lines.append(QLineF(tempPoint, tempPoint1)); xAxisMin+= width; } break; } case Histogram::Cumulative: { double point =0.0; for (int row = startRow; row <= endRow; row++ ) { if ( xColumn->isValid(row) && !xColumn->isMasked(row)) gsl_histogram_increment(histogram, xColumn->valueAt(row)); } for(size_t i=0; i < bins; ++i) { point+= gsl_histogram_get(histogram,i); tempPoint.setX(xAxisMin); tempPoint.setY(0.0); tempPoint1.setX(xAxisMin); tempPoint1.setY(point); lines.append(QLineF(tempPoint, tempPoint1)); tempPoint.setX(xAxisMin); tempPoint.setY(point); tempPoint1.setX(xAxisMin+width); tempPoint1.setY(point); lines.append(QLineF(tempPoint,tempPoint1)); tempPoint.setX(xAxisMin+width); tempPoint.setY(point); tempPoint1.setX(xAxisMin+width); tempPoint1.setY(0.0); lines.append(QLineF(tempPoint, tempPoint1)); tempPoint.setX(xAxisMin+width); tempPoint.setY(0.0); tempPoint1.setX(xAxisMin); tempPoint1.setY(0.0); lines.append(QLineF(tempPoint, tempPoint1)); xAxisMin+= width; } break; } case Histogram::AvgShift: { //TODO break; } } //calculate the lines connecting the data points for (int i = 0; i(q->parentAspect()); const AbstractCoordinateSystem* cSystem = plot->coordinateSystem(); lines = cSystem->mapLogicalToScene(lines); //new line path for (const auto& line: lines) { linePath.moveTo(line.p1()); linePath.lineTo(line.p2()); } updateFilling(); recalcShapeAndBoundingRect(); } /*! recreates the value strings to be shown and recalculates their draw position. */ void HistogramPrivate::updateValues() { valuesPath = QPainterPath(); valuesPoints.clear(); valuesStrings.clear(); if (valuesType == Histogram::NoValues) { recalcShapeAndBoundingRect(); return; } //determine the value string for all points that are currently visible in the plot if (valuesType == Histogram::ValuesY || valuesType == Histogram::ValuesYBracketed) { switch(histogramType) { case Histogram::Ordinary: for(size_t i=0; irowCount()); const AbstractColumn::ColumnMode xColMode = valuesColumn->columnMode(); for (int i = 0; i < endRow; ++i) { if (!visiblePoints[i]) continue; if ( !valuesColumn->isValid(i) || valuesColumn->isMasked(i) ) continue; switch (xColMode) { case AbstractColumn::Numeric: valuesStrings << valuesPrefix + QString::number(valuesColumn->valueAt(i)) + valuesSuffix; break; case AbstractColumn::Text: valuesStrings << valuesPrefix + valuesColumn->textAt(i) + valuesSuffix; case AbstractColumn::Integer: case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: //TODO break; } } } //Calculate the coordinates where to paint the value strings. //The coordinates depend on the actual size of the string. QPointF tempPoint; QFontMetrics fm(valuesFont); qreal w; qreal h = fm.ascent(); double xAxisMin = xColumn->minimum(); double xAxisMax = xColumn->maximum(); double width = (xAxisMax-xAxisMin)/bins; switch(valuesPosition) { case Histogram::ValuesAbove: for (int i = 0; i < valuesStrings.size(); i++) { w=fm.width(valuesStrings.at(i)); tempPoint.setX( symbolPointsScene.at(i).x() -w/2 +xAxisMin); tempPoint.setY( symbolPointsScene.at(i).y() - valuesDistance ); valuesPoints.append(tempPoint); xAxisMin+= 9*width; } break; case Histogram::ValuesUnder: for (int i = 0; i < valuesStrings.size(); i++) { w=fm.width(valuesStrings.at(i)); tempPoint.setX( symbolPointsScene.at(i).x() -w/2+xAxisMin ); tempPoint.setY( symbolPointsScene.at(i).y() + valuesDistance + h/2); valuesPoints.append(tempPoint); xAxisMin+= 9*width; } break; case Histogram::ValuesLeft: for (int i = 0; i < valuesStrings.size(); i++) { w=fm.width(valuesStrings.at(i)); tempPoint.setX( symbolPointsScene.at(i).x() - valuesDistance - w - 1 +xAxisMin); tempPoint.setY( symbolPointsScene.at(i).y()); valuesPoints.append(tempPoint); xAxisMin+= 9*width; } break; case Histogram::ValuesRight: for (int i = 0; i < valuesStrings.size(); i++) { tempPoint.setX( symbolPointsScene.at(i).x() + valuesDistance - 1 +xAxisMin); tempPoint.setY( symbolPointsScene.at(i).y() ); valuesPoints.append(tempPoint); xAxisMin+= 9*width; } break; } QTransform trafo; QPainterPath path; for (int i = 0; i < valuesPoints.size(); i++) { path = QPainterPath(); path.addText( QPoint(0,0), valuesFont, valuesStrings.at(i) ); trafo.reset(); trafo.translate( valuesPoints.at(i).x(), valuesPoints.at(i).y() ); if (valuesRotationAngle!=0) trafo.rotate( -valuesRotationAngle ); valuesPath.addPath(trafo.map(path)); } recalcShapeAndBoundingRect(); } void HistogramPrivate::updateFilling() { fillPolygons.clear(); if (fillingPosition==Histogram::NoFilling) { recalcShapeAndBoundingRect(); return; } QVector fillLines; const CartesianPlot* plot = dynamic_cast(q->parentAspect()); const AbstractCoordinateSystem* cSystem = plot->coordinateSystem(); //if there're no interpolation lines available (Histogram::NoLine selected), create line-interpolation, //use already available lines otherwise. if (lines.size()) fillLines = lines; else { for (int i=0; imapLogicalToScene(fillLines); } //no lines available (no points), nothing to do if (!fillLines.size()) return; //create polygon(s): //1. Depending on the current zoom-level, only a subset of the curve may be visible in the plot //and more of the filling area should be shown than the area defined by the start and end points of the currently visible points. //We check first whether the curve crosses the boundaries of the plot and determine new start and end points and put them to the boundaries. //2. Furthermore, depending on the current filling type we determine the end point (x- or y-coordinate) where all polygons are closed at the end. QPolygonF pol; QPointF start = fillLines.at(0).p1(); //starting point of the current polygon, initialize with the first visible point QPointF end = fillLines.at(fillLines.size()-1).p2(); //starting point of the current polygon, initialize with the last visible point const QPointF& first = symbolPointsLogical.at(0); //first point of the curve, may not be visible currently const QPointF& last = symbolPointsLogical.at(symbolPointsLogical.size()-1);//first point of the curve, may not be visible currently QPointF edge; float xEnd=0, yEnd=0; if (fillingPosition == Histogram::FillingAbove) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin())); } //coordinate at which to close all polygons yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())).y(); } else if (fillingPosition == Histogram::FillingBelow) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax())); } //coordinate at which to close all polygons yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).y(); } else if (fillingPosition == Histogram::FillingZeroBaseline) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (plot->yMax()>0) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax())); } else { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin())); } } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (plot->yMax()>0) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax())); } else { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin())); } } yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()>0 ? plot->yMin() : 0)).y(); } else if (fillingPosition == Histogram::FillingLeft) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.x(), edge.x())) { if (first.y() < plot->yMin()) start = edge; else if (first.y() > plot->yMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), first.y())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.x(), edge.x())) { if (last.y() < plot->yMin()) end = edge; else if (last.y() > plot->yMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), last.y())); } //coordinate at which to close all polygons xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).x(); } else { //FillingRight edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.x(), edge.x())) { if (first.y() < plot->yMin()) start = edge; else if (first.y() > plot->yMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), first.y())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.x(), edge.x())) { if (last.y() < plot->yMin()) end = edge; else if (last.y() > plot->yMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), last.y())); } //coordinate at which to close all polygons xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())).x(); } if (start != fillLines.at(0).p1()) pol << start; QPointF p1, p2; for (int i=0; icheck whether we have a break in between. const bool gap = false; //TODO if (!gap) { //-> we have no break in the curve -> connect the points by a horizontal/vertical line pol << fillLines.at(i-1).p2() << p1; } else { //-> we have a break in the curve -> close the polygon add it to the polygon list and start a new polygon if (fillingPosition==Histogram::FillingAbove || fillingPosition==Histogram::FillingBelow || fillingPosition==Histogram::FillingZeroBaseline) { pol << QPointF(fillLines.at(i-1).p2().x(), yEnd); pol << QPointF(start.x(), yEnd); } else { pol << QPointF(xEnd, fillLines.at(i-1).p2().y()); pol << QPointF(xEnd, start.y()); } fillPolygons << pol; pol.clear(); start = p1; } } pol << p1 << p2; } if (p2!=end) pol << end; //close the last polygon if (fillingPosition==Histogram::FillingAbove || fillingPosition==Histogram::FillingBelow || fillingPosition==Histogram::FillingZeroBaseline) { pol << QPointF(end.x(), yEnd); pol << QPointF(start.x(), yEnd); } else { pol << QPointF(xEnd, end.y()); pol << QPointF(xEnd, start.y()); } fillPolygons << pol; recalcShapeAndBoundingRect(); } /*! recalculates the outer bounds and the shape of the curve. */ void HistogramPrivate::recalcShapeAndBoundingRect() { //if (m_suppressRecalc) // return; prepareGeometryChange(); curveShape = QPainterPath(); curveShape.addPath(WorksheetElement::shapeFromPath(linePath, linePen)); if (valuesType != Histogram::NoValues) curveShape.addPath(valuesPath); boundingRectangle = curveShape.boundingRect(); for (const auto& pol : fillPolygons) boundingRectangle = boundingRectangle.united(pol.boundingRect()); //TODO: when the selection is painted, line intersections are visible. //simplified() removes those artifacts but is horrible slow for curves with large number of points. //search for an alternative. //curveShape = curveShape.simplified(); updatePixmap(); } void HistogramPrivate::draw(QPainter *painter) { //drawing line painter->setOpacity(lineOpacity); painter->setPen(linePen); painter->setBrush(Qt::NoBrush); painter->drawPath(linePath); //draw filling if (fillingPosition != Histogram::NoFilling) { painter->setOpacity(fillingOpacity); painter->setPen(Qt::SolidLine); drawFilling(painter); } //draw values if (valuesType != Histogram::NoValues) { painter->setOpacity(valuesOpacity); painter->setPen(valuesColor); painter->setBrush(Qt::SolidPattern); drawValues(painter); } } void HistogramPrivate::updatePixmap() { QPixmap pixmap(boundingRectangle.width(), boundingRectangle.height()); if (boundingRectangle.width()==0 || boundingRectangle.width()==0) { m_pixmap = pixmap; m_hoverEffectImageIsDirty = true; m_selectionEffectImageIsDirty = true; return; } pixmap.fill(Qt::transparent); QPainter painter(&pixmap); painter.setRenderHint(QPainter::Antialiasing, true); painter.translate(-boundingRectangle.topLeft()); draw(&painter); painter.end(); m_pixmap = pixmap; m_hoverEffectImageIsDirty = true; m_selectionEffectImageIsDirty = true; } /*! Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the curve. \sa QGraphicsItem::paint(). */ void HistogramPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option); Q_UNUSED(widget); if (!isVisible()) return; painter->setPen(Qt::NoPen); painter->setBrush(Qt::NoBrush); painter->setRenderHint(QPainter::SmoothPixmapTransform, true); draw(painter); if (m_hovered && !isSelected() && !m_printing) { if (m_hoverEffectImageIsDirty) { QPixmap pix = m_pixmap; pix.fill(QApplication::palette().color(QPalette::Shadow)); pix.setAlphaChannel(m_pixmap.alphaChannel()); m_hoverEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5); m_hoverEffectImageIsDirty = false; } painter->drawImage(boundingRectangle.topLeft(), m_hoverEffectImage, m_pixmap.rect()); return; } if (isSelected() && !m_printing) { if (m_selectionEffectImageIsDirty) { QPixmap pix = m_pixmap; pix.fill(QApplication::palette().color(QPalette::Highlight)); pix.setAlphaChannel(m_pixmap.alphaChannel()); m_selectionEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5); m_selectionEffectImageIsDirty = false; } painter->drawImage(boundingRectangle.topLeft(), m_selectionEffectImage, m_pixmap.rect()); return; } } /*! Drawing of symbolsPath is very slow, so we draw every symbol in the loop which us much faster (factor 10) */ void HistogramPrivate::drawValues(QPainter* painter) { QTransform trafo; QPainterPath path; for (int i=0; idrawPath(trafo.map(path)); } } void HistogramPrivate::drawFilling(QPainter* painter) { for (const auto& pol : fillPolygons) { QRectF rect = pol.boundingRect(); if (fillingType == PlotArea::Color) { switch (fillingColorStyle) { case PlotArea::SingleColor: { painter->setBrush(QBrush(fillingFirstColor)); break; } case PlotArea::HorizontalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.topRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient: { QRadialGradient radialGrad(rect.center(), rect.width()/2); radialGrad.setColorAt(0, fillingFirstColor); radialGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(radialGrad)); break; } } } else if (fillingType == PlotArea::Image) { if ( !fillingFileName.trimmed().isEmpty() ) { QPixmap pix(fillingFileName); switch (fillingImageStyle) { case PlotArea::ScaledCropped: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatioByExpanding,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); break; case PlotArea::Scaled: pix = pix.scaled(rect.size().toSize(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(rect.size().toSize(),Qt::KeepAspectRatio,Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); break; case PlotArea::Centered: { QPixmap backpix(rect.size().toSize()); backpix.fill(); QPainter p(&backpix); p.drawPixmap(QPointF(0,0),pix); p.end(); painter->setBrush(QBrush(backpix)); painter->setBrushOrigin(-pix.size().width()/2,-pix.size().height()/2); break; } case PlotArea::Tiled: painter->setBrush(QBrush(pix)); break; case PlotArea::CenterTiled: painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2,pix.size().height()/2); } } } else if (fillingType == PlotArea::Pattern) painter->setBrush(QBrush(fillingFirstColor,fillingBrushStyle)); painter->drawPolygon(pol); } } void HistogramPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { const CartesianPlot* plot = dynamic_cast(q->parentAspect()); if (plot->mouseMode() == CartesianPlot::SelectionMode && !isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void HistogramPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { const CartesianPlot* plot = dynamic_cast(q->parentAspect()); if (plot->mouseMode() == CartesianPlot::SelectionMode && m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } void HistogramPrivate::recalculate() { emit q->HistogramdataChanged(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Histogram::save(QXmlStreamWriter* writer) const { Q_D(const Histogram); writer->writeStartElement( "Histogram" ); writeBasicAttributes( writer ); writeCommentElement( writer ); //general writer->writeStartElement( "general" ); WRITE_COLUMN(d->xColumn, xColumn); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //Line writer->writeStartElement( "lines" ); WRITE_QPEN(d->linePen); writer->writeEndElement(); //Values writer->writeStartElement( "values" ); writer->writeAttribute( "type", QString::number(d->valuesType) ); WRITE_COLUMN(d->valuesColumn, valuesColumn); writer->writeAttribute( "position", QString::number(d->valuesPosition) ); writer->writeAttribute( "distance", QString::number(d->valuesDistance) ); writer->writeAttribute( "rotation", QString::number(d->valuesRotationAngle) ); writer->writeAttribute( "opacity", QString::number(d->valuesOpacity) ); //TODO values format and precision writer->writeAttribute( "prefix", d->valuesPrefix ); writer->writeAttribute( "suffix", d->valuesSuffix ); WRITE_QCOLOR(d->valuesColor); WRITE_QFONT(d->valuesFont); writer->writeEndElement(); //Filling writer->writeStartElement( "filling" ); writer->writeAttribute( "position", QString::number(d->fillingPosition) ); writer->writeAttribute( "type", QString::number(d->fillingType) ); writer->writeAttribute( "colorStyle", QString::number(d->fillingColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->fillingImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->fillingBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->fillingFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->fillingFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->fillingFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->fillingSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->fillingSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->fillingSecondColor.blue()) ); writer->writeAttribute( "fileName", d->fillingFileName ); writer->writeAttribute( "opacity", QString::number(d->fillingOpacity) ); writer->writeEndElement(); //write Histogram specific information writer->writeStartElement( "typeChanged" ); writer->writeAttribute( "Histogramtype", QString::number(d->histogramData.type) ); writer->writeAttribute( "BinsOption", QString::number(d->histogramData.binsOption) ); writer->writeAttribute( "binValue", QString::number(d->histogramData.binValue)); writer->writeEndElement(); if (d->xColumn) d->xColumn->save(writer); writer->writeEndElement(); //close "Histogram" section } //! Load from XML bool Histogram::load(XmlStreamReader* reader, bool preview) { Q_D(Histogram); if(!reader->isStartElement() || reader->name() != "Histogram") { reader->raiseError(i18n("no histogram element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "Histogram") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "general") { attribs = reader->attributes(); READ_COLUMN(xColumn); str = attribs.value("visible").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "typeChanged") { attribs = reader->attributes(); str = attribs.value("type").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'type'")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->histogramType = (Histogram::HistogramType)str.toInt(); str = attribs.value("BinsOption").toString(); d->binsOption = (Histogram::BinsOption)str.toInt(); str = attribs.value("binValue").toString(); d->histogramData.binValue = str.toInt(); } else if (!preview && reader->name() == "values") { attribs = reader->attributes(); str = attribs.value("type").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'type'")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->valuesType = (Histogram::ValuesType)str.toInt(); READ_COLUMN(valuesColumn); str = attribs.value("position").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'position'")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else d->valuesPosition = (Histogram::ValuesPosition)str.toInt(); str = attribs.value("distance").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'distance'")); + reader->raiseWarning(attributeWarning.subs("distance").toString()); else d->valuesDistance = str.toDouble(); str = attribs.value("rotation").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'rotation'")); + reader->raiseWarning(attributeWarning.subs("rotation").toString()); else d->valuesRotationAngle = str.toDouble(); str = attribs.value("opacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->valuesOpacity = str.toDouble(); //don't produce any warning if no prefix or suffix is set (empty string is allowd here in xml) d->valuesPrefix = attribs.value("prefix").toString(); d->valuesSuffix = attribs.value("suffix").toString(); READ_QCOLOR(d->valuesColor); READ_QFONT(d->valuesFont); str = attribs.value("opacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'opacity'")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->valuesOpacity = str.toDouble(); } else if (!preview && reader->name() == "filling") { attribs = reader->attributes(); str = attribs.value("position").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("position")); + reader->raiseWarning(attributeWarning.subs("position").toString()); else d->fillingPosition = Histogram::FillingPosition(str.toInt()); str = attribs.value("type").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("type")); + reader->raiseWarning(attributeWarning.subs("type").toString()); else d->fillingType = PlotArea::BackgroundType(str.toInt()); str = attribs.value("colorStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("colorStyle")); + reader->raiseWarning(attributeWarning.subs("colorStyle").toString()); else d->fillingColorStyle = PlotArea::BackgroundColorStyle(str.toInt()); str = attribs.value("imageStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("imageStyle")); + reader->raiseWarning(attributeWarning.subs("imageStyle").toString()); else d->fillingImageStyle = PlotArea::BackgroundImageStyle(str.toInt()); str = attribs.value("brushStyle").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("brushStyle")); + reader->raiseWarning(attributeWarning.subs("brushStyle").toString()); else d->fillingBrushStyle = Qt::BrushStyle(str.toInt()); str = attribs.value("firstColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_r")); + reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->fillingFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_g")); + reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->fillingFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_b")); + reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->fillingFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_r")); + reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->fillingSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_g")); + reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->fillingSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_b")); + reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->fillingSecondColor.setBlue(str.toInt()); str = attribs.value("fileName").toString(); d->fillingFileName = str; str = attribs.value("opacity").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("opacity")); + reader->raiseWarning(attributeWarning.subs("opacity").toString()); else d->fillingOpacity = str.toDouble(); } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } d->xColumn = column; } } return true; } diff --git a/src/backend/worksheet/plots/cartesian/Symbol.cpp b/src/backend/worksheet/plots/cartesian/Symbol.cpp index 861af9f9f..30970b5ad 100644 --- a/src/backend/worksheet/plots/cartesian/Symbol.cpp +++ b/src/backend/worksheet/plots/cartesian/Symbol.cpp @@ -1,147 +1,147 @@ /*************************************************************************** File : Symbol.cpp Project : LabPlot Description : Symbol -------------------------------------------------------------------- Copyright : (C) 2015 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 * * * ***************************************************************************/ /*! \class Symbol \brief \ingroup worksheet */ #include "Symbol.h" -#include +#include QPainterPath Symbol::pathFromStyle(Symbol::Style style) { QPainterPath path; QPolygonF polygon; if (style == Symbol::Circle) { path.addEllipse(QPoint(0,0), 0.5, 0.5); } else if (style == Symbol::Square) { path.addRect(QRectF(- 0.5, -0.5, 1.0, 1.0)); } else if (style == Symbol::EquilateralTriangle) { polygon< +#include XYAnalysisCurve::XYAnalysisCurve(const QString& name) : XYCurve(name, new XYAnalysisCurvePrivate(this)) { init(); } XYAnalysisCurve::XYAnalysisCurve(const QString& name, XYAnalysisCurvePrivate* dd) : XYCurve(name, dd) { init(); } // XYCurve::XYCurve(const QString &name) : WorksheetElement(name), d_ptr(new XYCurvePrivate(this)) { // init(); // } // // XYCurve::XYCurve(const QString& name, XYCurvePrivate* dd) : WorksheetElement(name), d_ptr(dd) { // init(); // } XYAnalysisCurve::~XYAnalysisCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYAnalysisCurve::init() { Q_D(XYAnalysisCurve); d->dataSourceType = XYAnalysisCurve::DataSourceSpreadsheet; d->dataSourceCurve = nullptr; d->lineType = XYCurve::Line; d->symbolsStyle = Symbol::NoSymbols; } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, XYAnalysisCurve::DataSourceType, dataSourceType, dataSourceType) BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const XYCurve*, dataSourceCurve, dataSourceCurve) const QString& XYAnalysisCurve::dataSourceCurvePath() const { return d_ptr->dataSourceCurvePath; } BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const AbstractColumn*, xDataColumn, xDataColumn) BASIC_SHARED_D_READER_IMPL(XYAnalysisCurve, const AbstractColumn*, yDataColumn, yDataColumn) const QString& XYAnalysisCurve::xDataColumnPath() const { Q_D(const XYAnalysisCurve); return d->xDataColumnPath; } const QString& XYAnalysisCurve::yDataColumnPath() const { Q_D(const XYAnalysisCurve); return d->yDataColumnPath; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_S(XYAnalysisCurve, SetDataSourceType, XYAnalysisCurve::DataSourceType, dataSourceType) void XYAnalysisCurve::setDataSourceType(DataSourceType type) { Q_D(XYAnalysisCurve); if (type != d->dataSourceType) - exec(new XYAnalysisCurveSetDataSourceTypeCmd(d, type, i18n("%1: data source type changed"))); + exec(new XYAnalysisCurveSetDataSourceTypeCmd(d, type, ki18n("%1: data source type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYAnalysisCurve, SetDataSourceCurve, const XYCurve*, dataSourceCurve, retransform) void XYAnalysisCurve::setDataSourceCurve(const XYCurve* curve) { Q_D(XYAnalysisCurve); if (curve != d->dataSourceCurve) { - exec(new XYAnalysisCurveSetDataSourceCurveCmd(d, curve, i18n("%1: data source curve changed"))); + exec(new XYAnalysisCurveSetDataSourceCurveCmd(d, curve, ki18n("%1: data source curve changed"))); handleSourceDataChanged(); //handle the changes when different columns were provided for the source curve connect(curve, SIGNAL(xColumnChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); connect(curve, SIGNAL(yColumnChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); //handle the changes when the data inside of the source curve columns connect(curve, SIGNAL(xDataChanged()), this, SLOT(handleSourceDataChanged())); connect(curve, SIGNAL(yDataChanged()), this, SLOT(handleSourceDataChanged())); //TODO: add disconnect in the undo-function } } STD_SETTER_CMD_IMPL_S(XYAnalysisCurve, SetXDataColumn, const AbstractColumn*, xDataColumn) void XYAnalysisCurve::setXDataColumn(const AbstractColumn* column) { Q_D(XYAnalysisCurve); if (column != d->xDataColumn) { - exec(new XYAnalysisCurveSetXDataColumnCmd(d, column, i18n("%1: assign x-data"))); + exec(new XYAnalysisCurveSetXDataColumnCmd(d, column, ki18n("%1: assign x-data"))); handleSourceDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); //TODO disconnect on undo } } } STD_SETTER_CMD_IMPL_S(XYAnalysisCurve, SetYDataColumn, const AbstractColumn*, yDataColumn) void XYAnalysisCurve::setYDataColumn(const AbstractColumn* column) { Q_D(XYAnalysisCurve); if (column != d->yDataColumn) { - exec(new XYAnalysisCurveSetYDataColumnCmd(d, column, i18n("%1: assign y-data"))); + exec(new XYAnalysisCurveSetYDataColumnCmd(d, column, ki18n("%1: assign y-data"))); handleSourceDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); //TODO disconnect on undo } } } //############################################################################## //################################# SLOTS #################################### //############################################################################## void XYAnalysisCurve::handleSourceDataChanged() { Q_D(XYAnalysisCurve); d->sourceDataChangedSinceLastRecalc = true; emit sourceDataChanged(); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYAnalysisCurvePrivate::XYAnalysisCurvePrivate(XYAnalysisCurve* owner) : XYCurvePrivate(owner), xDataColumn(nullptr), yDataColumn(nullptr), xColumn(nullptr), yColumn(nullptr), xVector(nullptr), yVector(nullptr), q(owner) { } XYAnalysisCurvePrivate::~XYAnalysisCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYAnalysisCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYAnalysisCurve); writer->writeStartElement("xyAnalysisCurve"); //write xy-curve information XYCurve::save(writer); //write data source specific information writer->writeStartElement("dataSource"); writer->writeAttribute( "type", QString::number(d->dataSourceType) ); WRITE_PATH(d->dataSourceCurve, dataSourceCurve); WRITE_COLUMN(d->xDataColumn, xDataColumn); WRITE_COLUMN(d->yDataColumn, yDataColumn); writer->writeEndElement(); writer->writeEndElement(); //"xyAnalysiCurve" } //! Load from XML bool XYAnalysisCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYAnalysisCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyAnalysisCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyCurve") { if ( !XYCurve::load(reader, preview) ) return false; } else if (reader->name() == "dataSource") { attribs = reader->attributes(); READ_INT_VALUE("type", dataSourceType, XYAnalysisCurve::DataSourceType); READ_PATH(dataSourceCurve); READ_COLUMN(xDataColumn); READ_COLUMN(yDataColumn); } } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYCurve.cpp b/src/backend/worksheet/plots/cartesian/XYCurve.cpp index 38abb4008..bd1f31ae5 100644 --- a/src/backend/worksheet/plots/cartesian/XYCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYCurve.cpp @@ -1,2466 +1,2468 @@ /*************************************************************************** File : XYCurve.cpp Project : LabPlot Description : A xy-curve -------------------------------------------------------------------- Copyright : (C) 2010-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2013 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 * * * ***************************************************************************/ /*! \class XYCurve \brief A 2D-curve, provides an interface for editing many properties of the curve. \ingroup worksheet */ #include "XYCurve.h" #include "XYCurvePrivate.h" #include "backend/core/column/Column.h" #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/lib/commandtemplates.h" #include "backend/core/Project.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include "backend/gsl/errors.h" #include "tools/ImageTools.h" #include #include #include +#include #include -#include +#include +#include extern "C" { #include #include } XYCurve::XYCurve(const QString &name) : WorksheetElement(name), d_ptr(new XYCurvePrivate(this)), m_menusInitialized(false) { init(); } XYCurve::XYCurve(const QString& name, XYCurvePrivate* dd) : WorksheetElement(name), d_ptr(dd), m_menusInitialized(false) { init(); } XYCurve::~XYCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYCurve::finalizeAdd() { Q_D(XYCurve); d->plot = dynamic_cast(parentAspect()); Q_ASSERT(d->plot); d->cSystem = dynamic_cast(d->plot->coordinateSystem()); } void XYCurve::init() { Q_D(XYCurve); KConfig config; KConfigGroup group = config.group("XYCurve"); d->xColumn = nullptr; d->yColumn = nullptr; d->lineType = (XYCurve::LineType) group.readEntry("LineType", (int)XYCurve::Line); d->lineSkipGaps = group.readEntry("SkipLineGaps", false); d->lineInterpolationPointsCount = group.readEntry("LineInterpolationPointsCount", 1); d->linePen.setStyle( (Qt::PenStyle) group.readEntry("LineStyle", (int)Qt::SolidLine) ); d->linePen.setColor( group.readEntry("LineColor", QColor(Qt::black)) ); d->linePen.setWidthF( group.readEntry("LineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)) ); d->lineOpacity = group.readEntry("LineOpacity", 1.0); d->dropLineType = (XYCurve::DropLineType) group.readEntry("DropLineType", (int)XYCurve::NoLine); d->dropLinePen.setStyle( (Qt::PenStyle) group.readEntry("DropLineStyle", (int)Qt::SolidLine) ); d->dropLinePen.setColor( group.readEntry("DropLineColor", QColor(Qt::black))); d->dropLinePen.setWidthF( group.readEntry("DropLineWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)) ); d->dropLineOpacity = group.readEntry("DropLineOpacity", 1.0); d->symbolsStyle = (Symbol::Style)group.readEntry("SymbolStyle", (int)Symbol::NoSymbols); d->symbolsSize = group.readEntry("SymbolSize", Worksheet::convertToSceneUnits(5, Worksheet::Point)); d->symbolsRotationAngle = group.readEntry("SymbolRotation", 0.0); d->symbolsOpacity = group.readEntry("SymbolOpacity", 1.0); d->symbolsBrush.setStyle( (Qt::BrushStyle)group.readEntry("SymbolFillingStyle", (int)Qt::SolidPattern) ); d->symbolsBrush.setColor( group.readEntry("SymbolFillingColor", QColor(Qt::black)) ); d->symbolsPen.setStyle( (Qt::PenStyle)group.readEntry("SymbolBorderStyle", (int)Qt::SolidLine) ); d->symbolsPen.setColor( group.readEntry("SymbolBorderColor", QColor(Qt::black)) ); d->symbolsPen.setWidthF( group.readEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(0.0, Worksheet::Point)) ); d->valuesType = (XYCurve::ValuesType) group.readEntry("ValuesType", (int)XYCurve::NoValues); d->valuesColumn = nullptr; d->valuesPosition = (XYCurve::ValuesPosition) group.readEntry("ValuesPosition", (int)XYCurve::ValuesAbove); d->valuesDistance = group.readEntry("ValuesDistance", Worksheet::convertToSceneUnits(5, Worksheet::Point)); d->valuesRotationAngle = group.readEntry("ValuesRotation", 0.0); d->valuesOpacity = group.readEntry("ValuesOpacity", 1.0); d->valuesPrefix = group.readEntry("ValuesPrefix", ""); d->valuesSuffix = group.readEntry("ValuesSuffix", ""); d->valuesFont = group.readEntry("ValuesFont", QFont()); d->valuesFont.setPixelSize( Worksheet::convertToSceneUnits( 8, Worksheet::Point ) ); d->valuesColor = group.readEntry("ValuesColor", QColor(Qt::black)); d->fillingPosition = (XYCurve::FillingPosition) group.readEntry("FillingPosition", (int)XYCurve::NoFilling); d->fillingType = (PlotArea::BackgroundType) group.readEntry("FillingType", (int)PlotArea::Color); d->fillingColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("FillingColorStyle", (int) PlotArea::SingleColor); d->fillingImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("FillingImageStyle", (int) PlotArea::Scaled); d->fillingBrushStyle = (Qt::BrushStyle) group.readEntry("FillingBrushStyle", (int) Qt::SolidPattern); d->fillingFileName = group.readEntry("FillingFileName", QString()); d->fillingFirstColor = group.readEntry("FillingFirstColor", QColor(Qt::white)); d->fillingSecondColor = group.readEntry("FillingSecondColor", QColor(Qt::black)); d->fillingOpacity = group.readEntry("FillingOpacity", 1.0); d->xErrorType = (XYCurve::ErrorType) group.readEntry("XErrorType", (int)XYCurve::NoError); d->xErrorPlusColumn = nullptr; d->xErrorMinusColumn = nullptr; d->yErrorType = (XYCurve::ErrorType) group.readEntry("YErrorType", (int)XYCurve::NoError); d->yErrorPlusColumn = nullptr; d->yErrorMinusColumn = nullptr; d->errorBarsType = (XYCurve::ErrorBarsType) group.readEntry("ErrorBarsType", (int)XYCurve::ErrorBarsSimple); d->errorBarsCapSize = group.readEntry( "ErrorBarsCapSize", Worksheet::convertToSceneUnits(10, Worksheet::Point) ); d->errorBarsPen.setStyle( (Qt::PenStyle)group.readEntry("ErrorBarsStyle", (int)Qt::SolidLine) ); d->errorBarsPen.setColor( group.readEntry("ErrorBarsColor", QColor(Qt::black)) ); d->errorBarsPen.setWidthF( group.readEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)) ); d->errorBarsOpacity = group.readEntry("ErrorBarsOpacity", 1.0); } void XYCurve::initActions() { - visibilityAction = new QAction(i18n("visible"), this); + visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, SIGNAL(triggered()), this, SLOT(visibilityChanged())); navigateToAction = new QAction(QIcon::fromTheme("go-next-view"), "", this); connect(navigateToAction, SIGNAL(triggered()), this, SLOT(navigateTo())); m_menusInitialized = true; } QMenu* XYCurve::createContextMenu() { if (!m_menusInitialized) initActions(); QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); //"data analysis" menu CartesianPlot* plot = dynamic_cast(parentAspect()); menu->insertMenu(visibilityAction, plot->analysisMenu()); menu->insertSeparator(visibilityAction); //"Navigate to spreadsheet"-action, show only if x- or y-columns have data from a spreadsheet AbstractAspect* parentSpreadsheet = 0; if (xColumn() && dynamic_cast(xColumn()->parentAspect()) ) parentSpreadsheet = xColumn()->parentAspect(); else if (yColumn() && dynamic_cast(yColumn()->parentAspect()) ) parentSpreadsheet = yColumn()->parentAspect(); if (parentSpreadsheet) { navigateToAction->setText(i18n("Navigate to \"%1\"", parentSpreadsheet->name())); navigateToAction->setData(parentSpreadsheet->path()); menu->insertAction(visibilityAction, navigateToAction); menu->insertSeparator(visibilityAction); } //if the context menu is called on an item that is not selected yet, select it if (!graphicsItem()->isSelected()) graphicsItem()->setSelected(true); return menu; } /*! Returns an icon to be used in the project explorer. */ QIcon XYCurve::icon() const { return QIcon::fromTheme("labplot-xy-curve"); } QGraphicsItem* XYCurve::graphicsItem() const { return d_ptr; } STD_SWAP_METHOD_SETTER_CMD_IMPL(XYCurve, SetVisible, bool, swapVisible) void XYCurve::setVisible(bool on) { Q_D(XYCurve); - exec(new XYCurveSetVisibleCmd(d, on, on ? i18n("%1: set visible") : i18n("%1: set invisible"))); + exec(new XYCurveSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool XYCurve::isVisible() const { Q_D(const XYCurve); return d->isVisible(); } void XYCurve::setPrinting(bool on) { Q_D(XYCurve); d->setPrinting(on); } //############################################################################## //########################## getter methods ################################## //############################################################################## //data source BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xColumn, xColumn) BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yColumn, yColumn) CLASS_SHARED_D_READER_IMPL(XYCurve, QString, xColumnPath, xColumnPath) CLASS_SHARED_D_READER_IMPL(XYCurve, QString, yColumnPath, yColumnPath) //line BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::LineType, lineType, lineType) BASIC_SHARED_D_READER_IMPL(XYCurve, bool, lineSkipGaps, lineSkipGaps) BASIC_SHARED_D_READER_IMPL(XYCurve, int, lineInterpolationPointsCount, lineInterpolationPointsCount) CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, linePen, linePen) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, lineOpacity, lineOpacity) //droplines BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::DropLineType, dropLineType, dropLineType) CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, dropLinePen, dropLinePen) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, dropLineOpacity, dropLineOpacity) //symbols BASIC_SHARED_D_READER_IMPL(XYCurve, Symbol::Style, symbolsStyle, symbolsStyle) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsOpacity, symbolsOpacity) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsRotationAngle, symbolsRotationAngle) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, symbolsSize, symbolsSize) CLASS_SHARED_D_READER_IMPL(XYCurve, QBrush, symbolsBrush, symbolsBrush) CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, symbolsPen, symbolsPen) //values BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ValuesType, valuesType, valuesType) BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn *, valuesColumn, valuesColumn) const QString& XYCurve::valuesColumnPath() const { return d_ptr->valuesColumnPath; } BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ValuesPosition, valuesPosition, valuesPosition) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesDistance, valuesDistance) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesRotationAngle, valuesRotationAngle) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, valuesOpacity, valuesOpacity) CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesPrefix, valuesPrefix) CLASS_SHARED_D_READER_IMPL(XYCurve, QString, valuesSuffix, valuesSuffix) CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, valuesColor, valuesColor) CLASS_SHARED_D_READER_IMPL(XYCurve, QFont, valuesFont, valuesFont) //filling BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::FillingPosition, fillingPosition, fillingPosition) BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundType, fillingType, fillingType) BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundColorStyle, fillingColorStyle, fillingColorStyle) BASIC_SHARED_D_READER_IMPL(XYCurve, PlotArea::BackgroundImageStyle, fillingImageStyle, fillingImageStyle) CLASS_SHARED_D_READER_IMPL(XYCurve, Qt::BrushStyle, fillingBrushStyle, fillingBrushStyle) CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingFirstColor, fillingFirstColor) CLASS_SHARED_D_READER_IMPL(XYCurve, QColor, fillingSecondColor, fillingSecondColor) CLASS_SHARED_D_READER_IMPL(XYCurve, QString, fillingFileName, fillingFileName) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, fillingOpacity, fillingOpacity) //error bars BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, xErrorType, xErrorType) BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorPlusColumn, xErrorPlusColumn) const QString& XYCurve::xErrorPlusColumnPath() const { return d_ptr->xErrorPlusColumnPath; } BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, xErrorMinusColumn, xErrorMinusColumn) const QString& XYCurve::xErrorMinusColumnPath() const { return d_ptr->xErrorMinusColumnPath; } BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorType, yErrorType, yErrorType) BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorPlusColumn, yErrorPlusColumn) const QString& XYCurve::yErrorPlusColumnPath() const { return d_ptr->yErrorPlusColumnPath; } BASIC_SHARED_D_READER_IMPL(XYCurve, const AbstractColumn*, yErrorMinusColumn, yErrorMinusColumn) const QString& XYCurve::yErrorMinusColumnPath() const { return d_ptr->yErrorMinusColumnPath; } BASIC_SHARED_D_READER_IMPL(XYCurve, XYCurve::ErrorBarsType, errorBarsType, errorBarsType) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsCapSize, errorBarsCapSize) CLASS_SHARED_D_READER_IMPL(XYCurve, QPen, errorBarsPen, errorBarsPen) BASIC_SHARED_D_READER_IMPL(XYCurve, qreal, errorBarsOpacity, errorBarsOpacity) /*! * return \c true if the data in the source columns (x, y) used in the analysis curves, \c false otherwise */ bool XYCurve::isSourceDataChangedSinceLastRecalc() const { Q_D(const XYCurve); return d->sourceDataChangedSinceLastRecalc; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXColumn, const AbstractColumn*, xColumn, retransform) void XYCurve::setXColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->xColumn) { - exec(new XYCurveSetXColumnCmd(d, column, i18n("%1: x-data source changed"))); + exec(new XYCurveSetXColumnCmd(d, column, ki18n("%1: x-data source changed"))); //emit xDataChanged() in order to notify the plot about the changes emit xDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SIGNAL(xDataChanged())); //update the curve itself on changes connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(retransform())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(xColumnAboutToBeRemoved(const AbstractAspect*))); //TODO: add disconnect in the undo-function } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYColumn, const AbstractColumn*, yColumn, retransform) void XYCurve::setYColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->yColumn) { - exec(new XYCurveSetYColumnCmd(d, column, i18n("%1: y-data source changed"))); + exec(new XYCurveSetYColumnCmd(d, column, ki18n("%1: y-data source changed"))); //emit yDataChanged() in order to notify the plot about the changes emit yDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SIGNAL(yDataChanged())); //update the curve itself on changes connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(retransform())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(yColumnAboutToBeRemoved(const AbstractAspect*))); //TODO: add disconnect in the undo-function } } } void XYCurve::setXColumnPath(const QString& path) { Q_D(XYCurve); d->xColumnPath = path; } void XYCurve::setYColumnPath(const QString& path) { Q_D(XYCurve); d->yColumnPath = path; } //Line STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineType, XYCurve::LineType, lineType, updateLines) void XYCurve::setLineType(LineType type) { Q_D(XYCurve); if (type != d->lineType) - exec(new XYCurveSetLineTypeCmd(d, type, i18n("%1: line type changed"))); + exec(new XYCurveSetLineTypeCmd(d, type, ki18n("%1: line type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineSkipGaps, bool, lineSkipGaps, updateLines) void XYCurve::setLineSkipGaps(bool skip) { Q_D(XYCurve); if (skip != d->lineSkipGaps) - exec(new XYCurveSetLineSkipGapsCmd(d, skip, i18n("%1: set skip line gaps"))); + exec(new XYCurveSetLineSkipGapsCmd(d, skip, ki18n("%1: set skip line gaps"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineInterpolationPointsCount, int, lineInterpolationPointsCount, updateLines) void XYCurve::setLineInterpolationPointsCount(int count) { Q_D(XYCurve); if (count != d->lineInterpolationPointsCount) - exec(new XYCurveSetLineInterpolationPointsCountCmd(d, count, i18n("%1: set the number of interpolation points"))); + exec(new XYCurveSetLineInterpolationPointsCountCmd(d, count, ki18n("%1: set the number of interpolation points"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLinePen, QPen, linePen, recalcShapeAndBoundingRect) void XYCurve::setLinePen(const QPen &pen) { Q_D(XYCurve); if (pen != d->linePen) - exec(new XYCurveSetLinePenCmd(d, pen, i18n("%1: set line style"))); + exec(new XYCurveSetLinePenCmd(d, pen, ki18n("%1: set line style"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetLineOpacity, qreal, lineOpacity, updatePixmap); void XYCurve::setLineOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->lineOpacity) - exec(new XYCurveSetLineOpacityCmd(d, opacity, i18n("%1: set line opacity"))); + exec(new XYCurveSetLineOpacityCmd(d, opacity, ki18n("%1: set line opacity"))); } //Drop lines STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineType, XYCurve::DropLineType, dropLineType, updateDropLines) void XYCurve::setDropLineType(DropLineType type) { Q_D(XYCurve); if (type != d->dropLineType) - exec(new XYCurveSetDropLineTypeCmd(d, type, i18n("%1: drop line type changed"))); + exec(new XYCurveSetDropLineTypeCmd(d, type, ki18n("%1: drop line type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLinePen, QPen, dropLinePen, recalcShapeAndBoundingRect) void XYCurve::setDropLinePen(const QPen &pen) { Q_D(XYCurve); if (pen != d->dropLinePen) - exec(new XYCurveSetDropLinePenCmd(d, pen, i18n("%1: set drop line style"))); + exec(new XYCurveSetDropLinePenCmd(d, pen, ki18n("%1: set drop line style"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetDropLineOpacity, qreal, dropLineOpacity, updatePixmap) void XYCurve::setDropLineOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->dropLineOpacity) - exec(new XYCurveSetDropLineOpacityCmd(d, opacity, i18n("%1: set drop line opacity"))); + exec(new XYCurveSetDropLineOpacityCmd(d, opacity, ki18n("%1: set drop line opacity"))); } // Symbols-Tab STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsStyle, Symbol::Style, symbolsStyle, updateSymbols) void XYCurve::setSymbolsStyle(Symbol::Style style) { Q_D(XYCurve); if (style != d->symbolsStyle) - exec(new XYCurveSetSymbolsStyleCmd(d, style, i18n("%1: set symbol style"))); + exec(new XYCurveSetSymbolsStyleCmd(d, style, ki18n("%1: set symbol style"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsSize, qreal, symbolsSize, updateSymbols) void XYCurve::setSymbolsSize(qreal size) { Q_D(XYCurve); if (!qFuzzyCompare(1 + size, 1 + d->symbolsSize)) - exec(new XYCurveSetSymbolsSizeCmd(d, size, i18n("%1: set symbol size"))); + exec(new XYCurveSetSymbolsSizeCmd(d, size, ki18n("%1: set symbol size"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsRotationAngle, qreal, symbolsRotationAngle, updateSymbols) void XYCurve::setSymbolsRotationAngle(qreal angle) { Q_D(XYCurve); if (!qFuzzyCompare(1 + angle, 1 + d->symbolsRotationAngle)) - exec(new XYCurveSetSymbolsRotationAngleCmd(d, angle, i18n("%1: rotate symbols"))); + exec(new XYCurveSetSymbolsRotationAngleCmd(d, angle, ki18n("%1: rotate symbols"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsBrush, QBrush, symbolsBrush, updatePixmap) void XYCurve::setSymbolsBrush(const QBrush &brush) { Q_D(XYCurve); if (brush != d->symbolsBrush) - exec(new XYCurveSetSymbolsBrushCmd(d, brush, i18n("%1: set symbol filling"))); + exec(new XYCurveSetSymbolsBrushCmd(d, brush, ki18n("%1: set symbol filling"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsPen, QPen, symbolsPen, updateSymbols) void XYCurve::setSymbolsPen(const QPen &pen) { Q_D(XYCurve); if (pen != d->symbolsPen) - exec(new XYCurveSetSymbolsPenCmd(d, pen, i18n("%1: set symbol outline style"))); + exec(new XYCurveSetSymbolsPenCmd(d, pen, ki18n("%1: set symbol outline style"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetSymbolsOpacity, qreal, symbolsOpacity, updatePixmap) void XYCurve::setSymbolsOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->symbolsOpacity) - exec(new XYCurveSetSymbolsOpacityCmd(d, opacity, i18n("%1: set symbols opacity"))); + exec(new XYCurveSetSymbolsOpacityCmd(d, opacity, ki18n("%1: set symbols opacity"))); } //Values-Tab STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesType, XYCurve::ValuesType, valuesType, updateValues) void XYCurve::setValuesType(XYCurve::ValuesType type) { Q_D(XYCurve); if (type != d->valuesType) - exec(new XYCurveSetValuesTypeCmd(d, type, i18n("%1: set values type"))); + exec(new XYCurveSetValuesTypeCmd(d, type, ki18n("%1: set values type"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesColumn, const AbstractColumn*, valuesColumn, updateValues) void XYCurve::setValuesColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->valuesColumn) { - exec(new XYCurveSetValuesColumnCmd(d, column, i18n("%1: set values column"))); + exec(new XYCurveSetValuesColumnCmd(d, column, ki18n("%1: set values column"))); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateValues())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(valuesColumnAboutToBeRemoved(const AbstractAspect*))); } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPosition, XYCurve::ValuesPosition, valuesPosition, updateValues) void XYCurve::setValuesPosition(ValuesPosition position) { Q_D(XYCurve); if (position != d->valuesPosition) - exec(new XYCurveSetValuesPositionCmd(d, position, i18n("%1: set values position"))); + exec(new XYCurveSetValuesPositionCmd(d, position, ki18n("%1: set values position"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesDistance, qreal, valuesDistance, updateValues) void XYCurve::setValuesDistance(qreal distance) { Q_D(XYCurve); if (distance != d->valuesDistance) - exec(new XYCurveSetValuesDistanceCmd(d, distance, i18n("%1: set values distance"))); + exec(new XYCurveSetValuesDistanceCmd(d, distance, ki18n("%1: set values distance"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesRotationAngle, qreal, valuesRotationAngle, updateValues) void XYCurve::setValuesRotationAngle(qreal angle) { Q_D(XYCurve); if (!qFuzzyCompare(1 + angle, 1 + d->valuesRotationAngle)) - exec(new XYCurveSetValuesRotationAngleCmd(d, angle, i18n("%1: rotate values"))); + exec(new XYCurveSetValuesRotationAngleCmd(d, angle, ki18n("%1: rotate values"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesOpacity, qreal, valuesOpacity, updatePixmap) void XYCurve::setValuesOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->valuesOpacity) - exec(new XYCurveSetValuesOpacityCmd(d, opacity, i18n("%1: set values opacity"))); + exec(new XYCurveSetValuesOpacityCmd(d, opacity, ki18n("%1: set values opacity"))); } //TODO: Format, Precision STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesPrefix, QString, valuesPrefix, updateValues) void XYCurve::setValuesPrefix(const QString& prefix) { Q_D(XYCurve); if (prefix != d->valuesPrefix) - exec(new XYCurveSetValuesPrefixCmd(d, prefix, i18n("%1: set values prefix"))); + exec(new XYCurveSetValuesPrefixCmd(d, prefix, ki18n("%1: set values prefix"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesSuffix, QString, valuesSuffix, updateValues) void XYCurve::setValuesSuffix(const QString& suffix) { Q_D(XYCurve); if (suffix != d->valuesSuffix) - exec(new XYCurveSetValuesSuffixCmd(d, suffix, i18n("%1: set values suffix"))); + exec(new XYCurveSetValuesSuffixCmd(d, suffix, ki18n("%1: set values suffix"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesFont, QFont, valuesFont, updateValues) void XYCurve::setValuesFont(const QFont& font) { Q_D(XYCurve); if (font != d->valuesFont) - exec(new XYCurveSetValuesFontCmd(d, font, i18n("%1: set values font"))); + exec(new XYCurveSetValuesFontCmd(d, font, ki18n("%1: set values font"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetValuesColor, QColor, valuesColor, updatePixmap) void XYCurve::setValuesColor(const QColor& color) { Q_D(XYCurve); if (color != d->valuesColor) - exec(new XYCurveSetValuesColorCmd(d, color, i18n("%1: set values color"))); + exec(new XYCurveSetValuesColorCmd(d, color, ki18n("%1: set values color"))); } //Filling STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingPosition, XYCurve::FillingPosition, fillingPosition, updateFilling) void XYCurve::setFillingPosition(FillingPosition position) { Q_D(XYCurve); if (position != d->fillingPosition) - exec(new XYCurveSetFillingPositionCmd(d, position, i18n("%1: filling position changed"))); + exec(new XYCurveSetFillingPositionCmd(d, position, ki18n("%1: filling position changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingType, PlotArea::BackgroundType, fillingType, updatePixmap) void XYCurve::setFillingType(PlotArea::BackgroundType type) { Q_D(XYCurve); if (type != d->fillingType) - exec(new XYCurveSetFillingTypeCmd(d, type, i18n("%1: filling type changed"))); + exec(new XYCurveSetFillingTypeCmd(d, type, ki18n("%1: filling type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingColorStyle, PlotArea::BackgroundColorStyle, fillingColorStyle, updatePixmap) void XYCurve::setFillingColorStyle(PlotArea::BackgroundColorStyle style) { Q_D(XYCurve); if (style != d->fillingColorStyle) - exec(new XYCurveSetFillingColorStyleCmd(d, style, i18n("%1: filling color style changed"))); + exec(new XYCurveSetFillingColorStyleCmd(d, style, ki18n("%1: filling color style changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingImageStyle, PlotArea::BackgroundImageStyle, fillingImageStyle, updatePixmap) void XYCurve::setFillingImageStyle(PlotArea::BackgroundImageStyle style) { Q_D(XYCurve); if (style != d->fillingImageStyle) - exec(new XYCurveSetFillingImageStyleCmd(d, style, i18n("%1: filling image style changed"))); + exec(new XYCurveSetFillingImageStyleCmd(d, style, ki18n("%1: filling image style changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingBrushStyle, Qt::BrushStyle, fillingBrushStyle, updatePixmap) void XYCurve::setFillingBrushStyle(Qt::BrushStyle style) { Q_D(XYCurve); if (style != d->fillingBrushStyle) - exec(new XYCurveSetFillingBrushStyleCmd(d, style, i18n("%1: filling brush style changed"))); + exec(new XYCurveSetFillingBrushStyleCmd(d, style, ki18n("%1: filling brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFirstColor, QColor, fillingFirstColor, updatePixmap) void XYCurve::setFillingFirstColor(const QColor& color) { Q_D(XYCurve); if (color != d->fillingFirstColor) - exec(new XYCurveSetFillingFirstColorCmd(d, color, i18n("%1: set filling first color"))); + exec(new XYCurveSetFillingFirstColorCmd(d, color, ki18n("%1: set filling first color"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingSecondColor, QColor, fillingSecondColor, updatePixmap) void XYCurve::setFillingSecondColor(const QColor& color) { Q_D(XYCurve); if (color != d->fillingSecondColor) - exec(new XYCurveSetFillingSecondColorCmd(d, color, i18n("%1: set filling second color"))); + exec(new XYCurveSetFillingSecondColorCmd(d, color, ki18n("%1: set filling second color"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingFileName, QString, fillingFileName, updatePixmap) void XYCurve::setFillingFileName(const QString& fileName) { Q_D(XYCurve); if (fileName != d->fillingFileName) - exec(new XYCurveSetFillingFileNameCmd(d, fileName, i18n("%1: set filling image"))); + exec(new XYCurveSetFillingFileNameCmd(d, fileName, ki18n("%1: set filling image"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetFillingOpacity, qreal, fillingOpacity, updatePixmap) void XYCurve::setFillingOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->fillingOpacity) - exec(new XYCurveSetFillingOpacityCmd(d, opacity, i18n("%1: set filling opacity"))); + exec(new XYCurveSetFillingOpacityCmd(d, opacity, ki18n("%1: set filling opacity"))); } //Error bars STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXErrorType, XYCurve::ErrorType, xErrorType, updateErrorBars) void XYCurve::setXErrorType(ErrorType type) { Q_D(XYCurve); if (type != d->xErrorType) - exec(new XYCurveSetXErrorTypeCmd(d, type, i18n("%1: x-error type changed"))); + exec(new XYCurveSetXErrorTypeCmd(d, type, ki18n("%1: x-error type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXErrorPlusColumn, const AbstractColumn*, xErrorPlusColumn, updateErrorBars) void XYCurve::setXErrorPlusColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->xErrorPlusColumn) { - exec(new XYCurveSetXErrorPlusColumnCmd(d, column, i18n("%1: set x-error column"))); + exec(new XYCurveSetXErrorPlusColumnCmd(d, column, ki18n("%1: set x-error column"))); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateErrorBars())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(xErrorPlusColumnAboutToBeRemoved(const AbstractAspect*))); } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetXErrorMinusColumn, const AbstractColumn*, xErrorMinusColumn, updateErrorBars) void XYCurve::setXErrorMinusColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->xErrorMinusColumn) { - exec(new XYCurveSetXErrorMinusColumnCmd(d, column, i18n("%1: set x-error column"))); + exec(new XYCurveSetXErrorMinusColumnCmd(d, column, ki18n("%1: set x-error column"))); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateErrorBars())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(xErrorMinusColumnAboutToBeRemoved(const AbstractAspect*))); } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYErrorType, XYCurve::ErrorType, yErrorType, updateErrorBars) void XYCurve::setYErrorType(ErrorType type) { Q_D(XYCurve); if (type != d->yErrorType) - exec(new XYCurveSetYErrorTypeCmd(d, type, i18n("%1: y-error type changed"))); + exec(new XYCurveSetYErrorTypeCmd(d, type, ki18n("%1: y-error type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYErrorPlusColumn, const AbstractColumn*, yErrorPlusColumn, updateErrorBars) void XYCurve::setYErrorPlusColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->yErrorPlusColumn) { - exec(new XYCurveSetYErrorPlusColumnCmd(d, column, i18n("%1: set y-error column"))); + exec(new XYCurveSetYErrorPlusColumnCmd(d, column, ki18n("%1: set y-error column"))); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateErrorBars())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(yErrorPlusColumnAboutToBeRemoved(const AbstractAspect*))); } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetYErrorMinusColumn, const AbstractColumn*, yErrorMinusColumn, updateErrorBars) void XYCurve::setYErrorMinusColumn(const AbstractColumn* column) { Q_D(XYCurve); if (column != d->yErrorMinusColumn) { - exec(new XYCurveSetYErrorMinusColumnCmd(d, column, i18n("%1: set y-error column"))); + exec(new XYCurveSetYErrorMinusColumnCmd(d, column, ki18n("%1: set y-error column"))); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(updateErrorBars())); connect(column->parentAspect(), SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(yErrorMinusColumnAboutToBeRemoved(const AbstractAspect*))); } } } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsCapSize, qreal, errorBarsCapSize, updateErrorBars) void XYCurve::setErrorBarsCapSize(qreal size) { Q_D(XYCurve); if (size != d->errorBarsCapSize) - exec(new XYCurveSetErrorBarsCapSizeCmd(d, size, i18n("%1: set error bar cap size"))); + exec(new XYCurveSetErrorBarsCapSizeCmd(d, size, ki18n("%1: set error bar cap size"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsType, XYCurve::ErrorBarsType, errorBarsType, updateErrorBars) void XYCurve::setErrorBarsType(ErrorBarsType type) { Q_D(XYCurve); if (type != d->errorBarsType) - exec(new XYCurveSetErrorBarsTypeCmd(d, type, i18n("%1: error bar type changed"))); + exec(new XYCurveSetErrorBarsTypeCmd(d, type, ki18n("%1: error bar type changed"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsPen, QPen, errorBarsPen, recalcShapeAndBoundingRect) void XYCurve::setErrorBarsPen(const QPen& pen) { Q_D(XYCurve); if (pen != d->errorBarsPen) - exec(new XYCurveSetErrorBarsPenCmd(d, pen, i18n("%1: set error bar style"))); + exec(new XYCurveSetErrorBarsPenCmd(d, pen, ki18n("%1: set error bar style"))); } STD_SETTER_CMD_IMPL_F_S(XYCurve, SetErrorBarsOpacity, qreal, errorBarsOpacity, updatePixmap) void XYCurve::setErrorBarsOpacity(qreal opacity) { Q_D(XYCurve); if (opacity != d->errorBarsOpacity) - exec(new XYCurveSetErrorBarsOpacityCmd(d, opacity, i18n("%1: set error bar opacity"))); + exec(new XYCurveSetErrorBarsOpacityCmd(d, opacity, ki18n("%1: set error bar opacity"))); } void XYCurve::suppressRetransform(bool b) { Q_D(XYCurve); d->suppressRetransform(b); } //############################################################################## //################################# SLOTS #################################### //############################################################################## void XYCurve::retransform() { Q_D(XYCurve); d->retransform(); } void XYCurve::updateValues() { Q_D(XYCurve); d->updateValues(); } void XYCurve::updateErrorBars() { Q_D(XYCurve); d->updateErrorBars(); } //TODO void XYCurve::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { Q_UNUSED(pageResize); Q_D(const XYCurve); setSymbolsSize(d->symbolsSize * horizontalRatio); QPen pen = d->symbolsPen; pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.0); setSymbolsPen(pen); pen = d->linePen; pen.setWidthF(pen.widthF() * (horizontalRatio + verticalRatio) / 2.0); setLinePen(pen); //setValuesDistance(d->distance*); QFont font=d->valuesFont; font.setPointSizeF(font.pointSizeF()*horizontalRatio); setValuesFont(font); } void XYCurve::xColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->xColumn) { d->xColumn = 0; d->retransform(); } } void XYCurve::yColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->yColumn) { d->yColumn = 0; d->retransform(); } } void XYCurve::valuesColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->valuesColumn) { d->valuesColumn = 0; d->updateValues(); } } void XYCurve::xErrorPlusColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->xErrorPlusColumn) { d->xErrorPlusColumn = 0; d->updateErrorBars(); } } void XYCurve::xErrorMinusColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->xErrorMinusColumn) { d->xErrorMinusColumn = 0; d->updateErrorBars(); } } void XYCurve::yErrorPlusColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->yErrorPlusColumn) { d->yErrorPlusColumn = 0; d->updateErrorBars(); } } void XYCurve::yErrorMinusColumnAboutToBeRemoved(const AbstractAspect* aspect) { Q_D(XYCurve); if (aspect == d->yErrorMinusColumn) { d->yErrorMinusColumn = 0; d->updateErrorBars(); } } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void XYCurve::visibilityChanged() { Q_D(const XYCurve); this->setVisible(!d->isVisible()); } void XYCurve::navigateTo() { project()->navigateTo(navigateToAction->data().toString()); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYCurvePrivate::XYCurvePrivate(XYCurve *owner) : sourceDataChangedSinceLastRecalc(false), q(owner), plot(nullptr), cSystem(nullptr), m_hoverEffectImageIsDirty(false), m_selectionEffectImageIsDirty(false), m_hovered(false), m_suppressRecalc(false), m_suppressRetransform(false), m_printing(false) { setFlag(QGraphicsItem::ItemIsSelectable, true); setAcceptHoverEvents(true); } QString XYCurvePrivate::name() const { return q->name(); } QRectF XYCurvePrivate::boundingRect() const { return boundingRectangle; } /*! Returns the shape of the XYCurve as a QPainterPath in local coordinates */ QPainterPath XYCurvePrivate::shape() const { return curveShape; } void XYCurvePrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } bool XYCurvePrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->visibilityChanged(on); return oldValue; } /*! recalculates the position of the points to be drawn. Called when the data was changed. Triggers the update of lines, drop lines, symbols etc. */ void XYCurvePrivate::retransform() { DEBUG("\nXYCurvePrivate::retransform() name = " << name().toStdString() << ", m_suppressRetransform = " << m_suppressRetransform); DEBUG(" plot = " << plot); if (m_suppressRetransform || !plot) return; #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform()"); #endif symbolPointsLogical.clear(); symbolPointsScene.clear(); connectedPointsLogical.clear(); if ( (NULL == xColumn) || (NULL == yColumn) ) { DEBUG(" xColumn or yColumn == NULL"); linePath = QPainterPath(); dropLinePath = QPainterPath(); symbolsPath = QPainterPath(); valuesPath = QPainterPath(); errorBarsPath = QPainterPath(); recalcShapeAndBoundingRect(); return; } if (!plot->isPanningActive()) { WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 0); } QPointF tempPoint; AbstractColumn::ColumnMode xColMode = xColumn->columnMode(); AbstractColumn::ColumnMode yColMode = yColumn->columnMode(); //take over only valid and non masked points. for (int row = 0; row < xColumn->rowCount(); row++) { if ( xColumn->isValid(row) && yColumn->isValid(row) && (!xColumn->isMasked(row)) && (!yColumn->isMasked(row)) ) { switch (xColMode) { case AbstractColumn::Numeric: case AbstractColumn::Integer: tempPoint.setX(xColumn->valueAt(row)); break; case AbstractColumn::Text: //TODO case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } switch (yColMode) { case AbstractColumn::Numeric: case AbstractColumn::Integer: tempPoint.setY(yColumn->valueAt(row)); break; case AbstractColumn::Text: //TODO case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } symbolPointsLogical.append(tempPoint); connectedPointsLogical.push_back(true); } else { if (!connectedPointsLogical.empty()) connectedPointsLogical[connectedPointsLogical.size()-1] = false; } } //calculate the scene coordinates visiblePoints = std::vector(symbolPointsLogical.count(), false); { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::retransform(), map logical points to scene coordinates"); #endif cSystem->mapLogicalToScene(symbolPointsLogical, symbolPointsScene, visiblePoints); } m_suppressRecalc = true; updateLines(); updateDropLines(); updateSymbols(); updateValues(); m_suppressRecalc = false; updateErrorBars(); RESET_CURSOR; } /*! recalculates the painter path for the lines connecting the data points. Called each time when the type of this connection is changed. */ void XYCurvePrivate::updateLines() { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines()"); #endif linePath = QPainterPath(); lines.clear(); if (lineType == XYCurve::NoLine) { DEBUG(" nothing to do, since line type is XYCurve::NoLine"); updateFilling(); recalcShapeAndBoundingRect(); return; } unsigned int count = (unsigned int)symbolPointsLogical.count(); if (count <= 1) { DEBUG(" nothing to do, since no data points available"); recalcShapeAndBoundingRect(); return; } //calculate the lines connecting the data points { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate the lines connecting the data points"); #endif QPointF tempPoint1, tempPoint2; QPointF curPoint, nextPoint; switch (lineType) { case XYCurve::NoLine: break; case XYCurve::Line: for (unsigned int i = 0; i < count - 1; i++) { if (!lineSkipGaps && !connectedPointsLogical[i]) continue; lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); } break; case XYCurve::StartHorizontal: for (unsigned int i = 0; i < count - 1; i++) { if (!lineSkipGaps && !connectedPointsLogical[i]) continue; curPoint = symbolPointsLogical.at(i); nextPoint = symbolPointsLogical.at(i+1); tempPoint1 = QPointF(nextPoint.x(), curPoint.y()); lines.append(QLineF(curPoint, tempPoint1)); lines.append(QLineF(tempPoint1, nextPoint)); } break; case XYCurve::StartVertical: for (unsigned int i = 0; i < count - 1; i++) { if (!lineSkipGaps && !connectedPointsLogical[i]) continue; curPoint = symbolPointsLogical.at(i); nextPoint = symbolPointsLogical.at(i+1); tempPoint1 = QPointF(curPoint.x(), nextPoint.y()); lines.append(QLineF(curPoint, tempPoint1)); lines.append(QLineF(tempPoint1,nextPoint)); } break; case XYCurve::MidpointHorizontal: for (unsigned int i = 0; i < count - 1; i++) { if (!lineSkipGaps && !connectedPointsLogical[i]) continue; curPoint = symbolPointsLogical.at(i); nextPoint = symbolPointsLogical.at(i+1); tempPoint1 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, curPoint.y()); tempPoint2 = QPointF(curPoint.x() + (nextPoint.x()-curPoint.x())/2, nextPoint.y()); lines.append(QLineF(curPoint, tempPoint1)); lines.append(QLineF(tempPoint1, tempPoint2)); lines.append(QLineF(tempPoint2, nextPoint)); } break; case XYCurve::MidpointVertical: for (unsigned int i = 0; i < count - 1; i++) { if (!lineSkipGaps && !connectedPointsLogical[i]) continue; curPoint = symbolPointsLogical.at(i); nextPoint = symbolPointsLogical.at(i+1); tempPoint1 = QPointF(curPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); tempPoint2 = QPointF(nextPoint.x(), curPoint.y() + (nextPoint.y()-curPoint.y())/2); lines.append(QLineF(curPoint, tempPoint1)); lines.append(QLineF(tempPoint1, tempPoint2)); lines.append(QLineF(tempPoint2, nextPoint)); } break; case XYCurve::Segments2: { int skip=0; for (unsigned int i = 0; i < count - 1; i++) { if (skip != 1) { if (!lineSkipGaps && !connectedPointsLogical[i]) { skip = 0; continue; } lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); skip++; } else skip = 0; } break; } case XYCurve::Segments3: { int skip = 0; for (unsigned int i = 0; i < count - 1; i++) { if (skip != 2) { if (!lineSkipGaps && !connectedPointsLogical[i]) { skip = 0; continue; } lines.append(QLineF(symbolPointsLogical.at(i), symbolPointsLogical.at(i+1))); skip++; } else skip = 0; } break; } case XYCurve::SplineCubicNatural: case XYCurve::SplineCubicPeriodic: case XYCurve::SplineAkimaNatural: case XYCurve::SplineAkimaPeriodic: { gsl_interp_accel *acc = gsl_interp_accel_alloc(); gsl_spline *spline = 0; double* x = new double[count]; double* y = new double[count]; for (unsigned int i = 0; i < count; i++) { x[i] = symbolPointsLogical.at(i).x(); y[i] = symbolPointsLogical.at(i).y(); } gsl_set_error_handler_off(); if (lineType == XYCurve::SplineCubicNatural) spline = gsl_spline_alloc(gsl_interp_cspline, count); else if (lineType == XYCurve::SplineCubicPeriodic) spline = gsl_spline_alloc(gsl_interp_cspline_periodic, count); else if (lineType == XYCurve::SplineAkimaNatural) spline = gsl_spline_alloc(gsl_interp_akima, count); else if (lineType == XYCurve::SplineAkimaPeriodic) spline = gsl_spline_alloc(gsl_interp_akima_periodic, count); if (!spline) { QString msg; if ( (lineType == XYCurve::SplineAkimaNatural || lineType == XYCurve::SplineAkimaPeriodic) && count < 5) msg = i18n("Error: Akima spline interpolation requires a minimum of 5 points."); else - msg = i18n("Error: Couldn't initialize the spline function."); + msg = i18n("Error: Could not initialize the spline function."); emit q->info(msg); recalcShapeAndBoundingRect(); delete[] x; delete[] y; gsl_interp_accel_free (acc); return; } int status = gsl_spline_init (spline, x, y, count); if (status) { //TODO: check in gsl/interp.c when GSL_EINVAL is thrown QString gslError; if (status == GSL_EINVAL) gslError = i18n("x values must be monotonically increasing."); else gslError = gslErrorToString(status); - emit q->info( i18n("Error: %1").arg(gslError) ); + emit q->info( i18n("Error: %1", gslError) ); recalcShapeAndBoundingRect(); delete[] x; delete[] y; gsl_spline_free (spline); gsl_interp_accel_free (acc); return; } //create interpolating points std::vector xinterp, yinterp; double step; double xi, yi, x1, x2; for (unsigned int i = 0; i < count - 1; i++) { x1 = x[i]; x2 = x[i+1]; step=fabs(x2 - x1)/(lineInterpolationPointsCount + 1); for (xi = x1; xi < x2; xi += step) { yi = gsl_spline_eval(spline, xi, acc); xinterp.push_back(xi); yinterp.push_back(yi); } } for (unsigned int i = 0; i < xinterp.size() - 1; i++) lines.append(QLineF(xinterp[i], yinterp[i], xinterp[i+1], yinterp[i+1])); lines.append(QLineF(xinterp[xinterp.size()-1], yinterp[yinterp.size()-1], x[count-1], y[count-1])); delete[] x; delete[] y; gsl_spline_free (spline); gsl_interp_accel_free (acc); break; } } } //map the lines to scene coordinates { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), map lines to scene coordinates"); #endif lines = cSystem->mapLogicalToScene(lines); } { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::updateLines(), calculate new line path"); #endif //new line path for (const auto& line : lines) { linePath.moveTo(line.p1()); linePath.lineTo(line.p2()); } } updateFilling(); recalcShapeAndBoundingRect(); } /*! recalculates the painter path for the drop lines. Called each time when the type of the drop lines is changed. */ void XYCurvePrivate::updateDropLines() { dropLinePath = QPainterPath(); if (dropLineType == XYCurve::NoDropLine) { recalcShapeAndBoundingRect(); return; } //calculate drop lines QVector lines; float xMin = 0; float yMin = 0; xMin = plot->xMin(); yMin = plot->yMin(); switch (dropLineType) { case XYCurve::NoDropLine: break; case XYCurve::DropLineX: for(int i=0; i(yColumn)->minimum())) ); } break; case XYCurve::DropLineXMaxBaseline: for(int i=0; i(yColumn)->maximum())) ); } break; } //map the drop lines to scene coordinates lines = cSystem->mapLogicalToScene(lines); //new painter path for the drop lines for (const auto& line : lines) { dropLinePath.moveTo(line.p1()); dropLinePath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } void XYCurvePrivate::updateSymbols() { symbolsPath = QPainterPath(); if (symbolsStyle != Symbol::NoSymbols) { QPainterPath path = Symbol::pathFromStyle(symbolsStyle); QTransform trafo; trafo.scale(symbolsSize, symbolsSize); path = trafo.map(path); trafo.reset(); if (symbolsRotationAngle != 0) { trafo.rotate(symbolsRotationAngle); path = trafo.map(path); } for (const auto& point : symbolPointsScene) { trafo.reset(); trafo.translate(point.x(), point.y()); symbolsPath.addPath(trafo.map(path)); } } recalcShapeAndBoundingRect(); } /*! recreates the value strings to be shown and recalculates their draw position. */ void XYCurvePrivate::updateValues() { DEBUG("XYCurvePrivate::updateValues()"); valuesPath = QPainterPath(); valuesPoints.clear(); valuesStrings.clear(); if (valuesType == XYCurve::NoValues) { recalcShapeAndBoundingRect(); return; } //determine the value string for all points that are currently visible in the plot switch (valuesType) { case XYCurve::NoValues: case XYCurve::ValuesX: { for(int i=0; ivaluesColumn->rowCount()) endRow = valuesColumn->rowCount(); else endRow = symbolPointsLogical.size(); AbstractColumn::ColumnMode xColMode = valuesColumn->columnMode(); for (int i = 0; i < endRow; ++i) { if (!visiblePoints[i]) continue; if ( !valuesColumn->isValid(i) || valuesColumn->isMasked(i) ) continue; switch (xColMode) { case AbstractColumn::Numeric: case AbstractColumn::Integer: valuesStrings << valuesPrefix + QString::number(valuesColumn->valueAt(i)) + valuesSuffix; break; case AbstractColumn::Text: valuesStrings << valuesPrefix + valuesColumn->textAt(i) + valuesSuffix; case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: //TODO break; } } } } //Calculate the coordinates where to paint the value strings. //The coordinates depend on the actual size of the string. QPointF tempPoint; QFontMetrics fm(valuesFont); qreal w; qreal h=fm.ascent(); for (int i=0; i1000) { recalcShapeAndBoundingRect(); return; } QVector fillLines; //if there're no interpolation lines available (XYCurve::NoLine selected), create line-interpolation, //use already available lines otherwise. if (!lines.isEmpty()) fillLines = lines; else { for (int i=0; imapLogicalToScene(fillLines); //no lines available (no points), nothing to do if (fillLines.isEmpty()) return; } //create polygon(s): //1. Depending on the current zoom-level, only a subset of the curve may be visible in the plot //and more of the filling area should be shown than the area defined by the start and end points of the currently visible points. //We check first whether the curve crosses the boundaries of the plot and determine new start and end points and put them to the boundaries. //2. Furthermore, depending on the current filling type we determine the end point (x- or y-coordinate) where all polygons are closed at the end. QPolygonF pol; QPointF start = fillLines.at(0).p1(); //starting point of the current polygon, initialize with the first visible point QPointF end = fillLines.at(fillLines.size()-1).p2(); //starting point of the current polygon, initialize with the last visible point const QPointF& first = symbolPointsLogical.at(0); //first point of the curve, may not be visible currently const QPointF& last = symbolPointsLogical.at(symbolPointsLogical.size()-1);//first point of the curve, may not be visible currently QPointF edge; float xEnd=0, yEnd=0; if (fillingPosition == XYCurve::FillingAbove) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin())); } //coordinate at which to close all polygons yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())).y(); } else if (fillingPosition == XYCurve::FillingBelow) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax())); } //coordinate at which to close all polygons yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).y(); } else if (fillingPosition == XYCurve::FillingZeroBaseline) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.y(), edge.y())) { if (plot->yMax() > 0) { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMax())); } else { if (first.x() < plot->xMin()) start = edge; else if (first.x() > plot->xMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else start = cSystem->mapLogicalToScene(QPointF(first.x(), plot->yMin())); } } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.y(), edge.y())) { if (plot->yMax() > 0) { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMax())); } else { if (last.x() < plot->xMin()) end = edge; else if (last.x() > plot->xMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); else end = cSystem->mapLogicalToScene(QPointF(last.x(), plot->yMin())); } } yEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin()>0 ? plot->yMin() : 0)).y(); } else if (fillingPosition == XYCurve::FillingLeft) { edge = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.x(), edge.x())) { if (first.y() < plot->yMin()) start = edge; else if (first.y() > plot->yMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(plot->xMax(), first.y())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.x(), edge.x())) { if (last.y() < plot->yMin()) end = edge; else if (last.y() > plot->yMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(plot->xMax(), last.y())); } //coordinate at which to close all polygons xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())).x(); } else { //FillingRight edge = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMin())); //start point if (AbstractCoordinateSystem::essentiallyEqual(start.x(), edge.x())) { if (first.y() < plot->yMin()) start = edge; else if (first.y() > plot->yMax()) start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); else start = cSystem->mapLogicalToScene(QPointF(plot->xMin(), first.y())); } //end point if (AbstractCoordinateSystem::essentiallyEqual(end.x(), edge.x())) { if (last.y() < plot->yMin()) end = edge; else if (last.y() > plot->yMax()) end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), plot->yMax())); else end = cSystem->mapLogicalToScene(QPointF(plot->xMin(), last.y())); } //coordinate at which to close all polygons xEnd = cSystem->mapLogicalToScene(QPointF(plot->xMax(), plot->yMin())).x(); } if (start != fillLines.at(0).p1()) pol << start; QPointF p1, p2; for (int i=0; icheck whether we have a break in between. const bool gap = false; //TODO if (!gap) { //-> we have no break in the curve -> connect the points by a horizontal/vertical line pol << fillLines.at(i-1).p2() << p1; } else { //-> we have a break in the curve -> close the polygon add it to the polygon list and start a new polygon if (fillingPosition==XYCurve::FillingAbove || fillingPosition==XYCurve::FillingBelow || fillingPosition==XYCurve::FillingZeroBaseline) { pol << QPointF(fillLines.at(i-1).p2().x(), yEnd); pol << QPointF(start.x(), yEnd); } else { pol << QPointF(xEnd, fillLines.at(i-1).p2().y()); pol << QPointF(xEnd, start.y()); } fillPolygons << pol; pol.clear(); start = p1; } } pol << p1 << p2; } if (p2!=end) pol << end; //close the last polygon if (fillingPosition==XYCurve::FillingAbove || fillingPosition==XYCurve::FillingBelow || fillingPosition==XYCurve::FillingZeroBaseline) { pol << QPointF(end.x(), yEnd); pol << QPointF(start.x(), yEnd); } else { pol << QPointF(xEnd, end.y()); pol << QPointF(xEnd, start.y()); } fillPolygons << pol; recalcShapeAndBoundingRect(); } void XYCurvePrivate::updateErrorBars() { errorBarsPath = QPainterPath(); if (xErrorType==XYCurve::NoError && yErrorType==XYCurve::NoError) { recalcShapeAndBoundingRect(); return; } QVector lines; float errorPlus, errorMinus; //the cap size for the errorbars is given in scene units. //determine first the (half of the) cap size in logical units: // * take the first visible point in logical units // * convert it to scene units // * add to this point an offset corresponding to the cap size in scene units // * convert this point back to logical units // * subtract from this point the original coordinates (without the new offset) // to determine the cap size in logical units. float capSizeX = 0; float capSizeY = 0; if (errorBarsType != XYCurve::ErrorBarsSimple && !symbolPointsLogical.isEmpty()) { //determine the index of the first visible point size_t i = 0; while (i no error bars to draw //cap size for x-error bars QPointF pointScene = cSystem->mapLogicalToScene(symbolPointsLogical.at((int)i)); pointScene.setY(pointScene.y()-errorBarsCapSize); QPointF pointLogical = cSystem->mapSceneToLogical(pointScene); capSizeX = (pointLogical.y() - symbolPointsLogical.at((int)i).y())/2; //cap size for y-error bars pointScene = cSystem->mapLogicalToScene(symbolPointsLogical.at((int)i)); pointScene.setX(pointScene.x()+errorBarsCapSize); pointLogical = cSystem->mapSceneToLogical(pointScene); capSizeY = (pointLogical.x() - symbolPointsLogical.at((int)i).x())/2; } for (int i=0; i < symbolPointsLogical.size(); ++i) { if (!visiblePoints[i]) continue; const QPointF& point = symbolPointsLogical.at(i); //error bars for x if (xErrorType != XYCurve::NoError) { //determine the values for the errors if (xErrorPlusColumn && xErrorPlusColumn->isValid(i) && !xErrorPlusColumn->isMasked(i)) errorPlus = xErrorPlusColumn->valueAt(i); else errorPlus = 0; if (xErrorType==XYCurve::SymmetricError) errorMinus = errorPlus; else { if (xErrorMinusColumn && xErrorMinusColumn->isValid(i) && !xErrorMinusColumn->isMasked(i)) errorMinus = xErrorMinusColumn->valueAt(i); else errorMinus = 0; } //draw the error bars switch (errorBarsType) { case XYCurve::ErrorBarsSimple: lines.append(QLineF(QPointF(point.x()-errorMinus, point.y()), QPointF(point.x()+errorPlus, point.y()))); break; case XYCurve::ErrorBarsWithEnds: lines.append(QLineF(QPointF(point.x()-errorMinus, point.y()), QPointF(point.x()+errorPlus, point.y()))); if (errorMinus!=0) { lines.append(QLineF(QPointF(point.x()-errorMinus, point.y()-capSizeX), QPointF(point.x()-errorMinus, point.y()+capSizeX))); } if (errorPlus!=0) { lines.append(QLineF(QPointF(point.x()+errorPlus, point.y()-capSizeX), QPointF(point.x()+errorPlus, point.y()+capSizeX))); } break; } } //error bars for y if (yErrorType != XYCurve::NoError) { //determine the values for the errors if (yErrorPlusColumn && yErrorPlusColumn->isValid(i) && !yErrorPlusColumn->isMasked(i)) errorPlus = yErrorPlusColumn->valueAt(i); else errorPlus = 0; if (yErrorType == XYCurve::SymmetricError) errorMinus = errorPlus; else { if (yErrorMinusColumn && yErrorMinusColumn->isValid(i) && !yErrorMinusColumn->isMasked(i) ) errorMinus = yErrorMinusColumn->valueAt(i); else errorMinus = 0; } //draw the error bars switch (errorBarsType) { case XYCurve::ErrorBarsSimple: lines.append(QLineF(QPointF(point.x(), point.y()-errorMinus), QPointF(point.x(), point.y()+errorPlus))); break; case XYCurve::ErrorBarsWithEnds: lines.append(QLineF(QPointF(point.x(), point.y()-errorMinus), QPointF(point.x(), point.y()+errorPlus))); if (errorMinus != 0) lines.append(QLineF(QPointF(point.x()-capSizeY, point.y()-errorMinus), QPointF(point.x()+capSizeY, point.y()-errorMinus))); if (errorPlus != 0) lines.append(QLineF(QPointF(point.x()-capSizeY, point.y()+errorPlus), QPointF(point.x()+capSizeY, point.y()+errorPlus))); break; } } } //map the error bars to scene coordinates lines = cSystem->mapLogicalToScene(lines); //new painter path for the drop lines for (const auto& line : lines) { errorBarsPath.moveTo(line.p1()); errorBarsPath.lineTo(line.p2()); } recalcShapeAndBoundingRect(); } /*! recalculates the outer bounds and the shape of the curve. */ void XYCurvePrivate::recalcShapeAndBoundingRect() { DEBUG("XYCurvePrivate::recalcShapeAndBoundingRect() m_suppressRecalc = " << m_suppressRecalc); if (m_suppressRecalc) return; #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::recalcShapeAndBoundingRect()"); #endif prepareGeometryChange(); curveShape = QPainterPath(); if (lineType != XYCurve::NoLine) curveShape.addPath(WorksheetElement::shapeFromPath(linePath, linePen)); if (dropLineType != XYCurve::NoDropLine) curveShape.addPath(WorksheetElement::shapeFromPath(dropLinePath, dropLinePen)); if (symbolsStyle != Symbol::NoSymbols) curveShape.addPath(symbolsPath); if (valuesType != XYCurve::NoValues) curveShape.addPath(valuesPath); if (xErrorType != XYCurve::NoError || yErrorType != XYCurve::NoError) curveShape.addPath(WorksheetElement::shapeFromPath(errorBarsPath, errorBarsPen)); boundingRectangle = curveShape.boundingRect(); for (const auto& pol : fillPolygons) boundingRectangle = boundingRectangle.united(pol.boundingRect()); //TODO: when the selection is painted, line intersections are visible. //simplified() removes those artifacts but is horrible slow for curves with large number of points. //search for an alternative. //curveShape = curveShape.simplified(); DEBUG(" Calling updatePixmap()"); updatePixmap(); } void XYCurvePrivate::draw(QPainter* painter) { #ifdef PERFTRACE_CURVES PERFTRACE(name().toLatin1() + ", XYCurvePrivate::draw()"); #endif //draw filling if (fillingPosition != XYCurve::NoFilling) { painter->setOpacity(fillingOpacity); painter->setPen(Qt::SolidLine); drawFilling(painter); } //draw lines if (lineType != XYCurve::NoLine) { painter->setOpacity(lineOpacity); painter->setPen(linePen); painter->setBrush(Qt::NoBrush); painter->drawPath(linePath); } //draw drop lines if (dropLineType != XYCurve::NoDropLine) { painter->setOpacity(dropLineOpacity); painter->setPen(dropLinePen); painter->setBrush(Qt::NoBrush); painter->drawPath(dropLinePath); } //draw error bars if ( (xErrorType != XYCurve::NoError) || (yErrorType != XYCurve::NoError) ) { painter->setOpacity(errorBarsOpacity); painter->setPen(errorBarsPen); painter->setBrush(Qt::NoBrush); painter->drawPath(errorBarsPath); } //draw symbols if (symbolsStyle != Symbol::NoSymbols) { painter->setOpacity(symbolsOpacity); painter->setPen(symbolsPen); painter->setBrush(symbolsBrush); drawSymbols(painter); } //draw values if (valuesType != XYCurve::NoValues) { painter->setOpacity(valuesOpacity); //don't use any painter pen, since this will force QPainter to render the text outline which is expensive painter->setPen(Qt::NoPen); painter->setBrush(valuesColor); drawValues(painter); } } void XYCurvePrivate::updatePixmap() { DEBUG("XYCurvePrivate::updatePixmap() m_suppressRecalc = " << m_suppressRecalc); if (m_suppressRecalc) return; WAIT_CURSOR; m_hoverEffectImageIsDirty = true; m_selectionEffectImageIsDirty = true; if (boundingRectangle.width() == 0 || boundingRectangle.height() == 0) { DEBUG(" boundingRectangle.width() or boundingRectangle.height() == 0"); m_pixmap = QPixmap(); RESET_CURSOR; return; } QPixmap pixmap(ceil(boundingRectangle.width()), ceil(boundingRectangle.height())); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); painter.setRenderHint(QPainter::Antialiasing, true); painter.translate(-boundingRectangle.topLeft()); draw(&painter); painter.end(); m_pixmap = pixmap; update(); RESET_CURSOR; } /*! Reimplementation of QGraphicsItem::paint(). This function does the actual painting of the curve. \sa QGraphicsItem::paint(). */ void XYCurvePrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option); Q_UNUSED(widget); if (!isVisible()) return; painter->setPen(Qt::NoPen); painter->setBrush(Qt::NoBrush); painter->setRenderHint(QPainter::SmoothPixmapTransform, true); if ( KSharedConfig::openConfig()->group("Settings_Worksheet").readEntry("DoubleBuffering", true) ) painter->drawPixmap(boundingRectangle.topLeft(), m_pixmap); //draw the cached pixmap (fast) else draw(painter); //draw directly again (slow) if (m_hovered && !isSelected() && !m_printing) { if (m_hoverEffectImageIsDirty) { QPixmap pix = m_pixmap; pix.fill(QApplication::palette().color(QPalette::Shadow)); pix.setAlphaChannel(m_pixmap.alphaChannel()); m_hoverEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5); m_hoverEffectImageIsDirty = false; } painter->drawImage(boundingRectangle.topLeft(), m_hoverEffectImage, m_pixmap.rect()); return; } if (isSelected() && !m_printing) { if (m_selectionEffectImageIsDirty) { QPixmap pix = m_pixmap; pix.fill(QApplication::palette().color(QPalette::Highlight)); pix.setAlphaChannel(m_pixmap.alphaChannel()); m_selectionEffectImage = ImageTools::blurred(pix.toImage(), m_pixmap.rect(), 5); m_selectionEffectImageIsDirty = false; } painter->drawImage(boundingRectangle.topLeft(), m_selectionEffectImage, m_pixmap.rect()); return; } } /*! Drawing of symbolsPath is very slow, so we draw every symbol in the loop which is much faster (factor 10) */ void XYCurvePrivate::drawSymbols(QPainter* painter) { QPainterPath path = Symbol::pathFromStyle(symbolsStyle); QTransform trafo; trafo.scale(symbolsSize, symbolsSize); path = trafo.map(path); trafo.reset(); if (symbolsRotationAngle != 0) { trafo.rotate(symbolsRotationAngle); path = trafo.map(path); } for (const auto& point : symbolPointsScene) { trafo.reset(); trafo.translate(point.x(), point.y()); painter->drawPath(trafo.map(path)); } } void XYCurvePrivate::drawValues(QPainter* painter) { QTransform trafo; QPainterPath path; for (int i=0; idrawPath(trafo.map(path)); } } void XYCurvePrivate::drawFilling(QPainter* painter) { for (const auto& pol : fillPolygons) { QRectF rect = pol.boundingRect(); if (fillingType == PlotArea::Color) { switch (fillingColorStyle) { case PlotArea::SingleColor: { painter->setBrush(QBrush(fillingFirstColor)); break; } case PlotArea::HorizontalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.topRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomLeft()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.topLeft(), rect.bottomRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient: { QLinearGradient linearGrad(rect.bottomLeft(), rect.topRight()); linearGrad.setColorAt(0, fillingFirstColor); linearGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient: { QRadialGradient radialGrad(rect.center(), rect.width()/2); radialGrad.setColorAt(0, fillingFirstColor); radialGrad.setColorAt(1, fillingSecondColor); painter->setBrush(QBrush(radialGrad)); break; } } } else if (fillingType == PlotArea::Image) { if ( !fillingFileName.trimmed().isEmpty() ) { QPixmap pix(fillingFileName); switch (fillingImageStyle) { case PlotArea::ScaledCropped: pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2); break; case PlotArea::Scaled: pix = pix.scaled(rect.size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation); painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2); break; case PlotArea::Centered: { QPixmap backpix(rect.size().toSize()); backpix.fill(); QPainter p(&backpix); p.drawPixmap(QPointF(0, 0), pix); p.end(); painter->setBrush(QBrush(backpix)); painter->setBrushOrigin(-pix.size().width()/2, -pix.size().height()/2); break; } case PlotArea::Tiled: painter->setBrush(QBrush(pix)); break; case PlotArea::CenterTiled: painter->setBrush(QBrush(pix)); painter->setBrushOrigin(pix.size().width()/2, pix.size().height()/2); } } } else if (fillingType == PlotArea::Pattern) painter->setBrush(QBrush(fillingFirstColor, fillingBrushStyle)); painter->drawPolygon(pol); } } void XYCurvePrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { const CartesianPlot* plot = dynamic_cast(q->parentAspect()); if (plot->mouseMode() == CartesianPlot::SelectionMode && !isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void XYCurvePrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { const CartesianPlot* plot = dynamic_cast(q->parentAspect()); if (plot->mouseMode() == CartesianPlot::SelectionMode && m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } void XYCurvePrivate::setPrinting(bool on) { m_printing = on; } void XYCurvePrivate::suppressRetransform(bool on) { m_suppressRetransform = on; m_suppressRecalc = on; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYCurve); writer->writeStartElement( "xyCurve" ); writeBasicAttributes( writer ); writeCommentElement( writer ); //general writer->writeStartElement( "general" ); WRITE_COLUMN(d->xColumn, xColumn); WRITE_COLUMN(d->yColumn, yColumn); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); //Line writer->writeStartElement( "lines" ); writer->writeAttribute( "type", QString::number(d->lineType) ); writer->writeAttribute( "skipGaps", QString::number(d->lineSkipGaps) ); writer->writeAttribute( "interpolationPointsCount", QString::number(d->lineInterpolationPointsCount) ); WRITE_QPEN(d->linePen); writer->writeAttribute( "opacity", QString::number(d->lineOpacity) ); writer->writeEndElement(); //Drop lines writer->writeStartElement( "dropLines" ); writer->writeAttribute( "type", QString::number(d->dropLineType) ); WRITE_QPEN(d->dropLinePen); writer->writeAttribute( "opacity", QString::number(d->dropLineOpacity) ); writer->writeEndElement(); //Symbols writer->writeStartElement( "symbols" ); writer->writeAttribute( "symbolsStyle", QString::number(d->symbolsStyle) ); writer->writeAttribute( "opacity", QString::number(d->symbolsOpacity) ); writer->writeAttribute( "rotation", QString::number(d->symbolsRotationAngle) ); writer->writeAttribute( "size", QString::number(d->symbolsSize) ); WRITE_QBRUSH(d->symbolsBrush); WRITE_QPEN(d->symbolsPen); writer->writeEndElement(); //Values writer->writeStartElement( "values" ); writer->writeAttribute( "type", QString::number(d->valuesType) ); WRITE_COLUMN(d->valuesColumn, valuesColumn); writer->writeAttribute( "position", QString::number(d->valuesPosition) ); writer->writeAttribute( "distance", QString::number(d->valuesDistance) ); writer->writeAttribute( "rotation", QString::number(d->valuesRotationAngle) ); writer->writeAttribute( "opacity", QString::number(d->valuesOpacity) ); //TODO values format and precision writer->writeAttribute( "prefix", d->valuesPrefix ); writer->writeAttribute( "suffix", d->valuesSuffix ); WRITE_QCOLOR(d->valuesColor); WRITE_QFONT(d->valuesFont); writer->writeEndElement(); //Filling writer->writeStartElement( "filling" ); writer->writeAttribute( "position", QString::number(d->fillingPosition) ); writer->writeAttribute( "type", QString::number(d->fillingType) ); writer->writeAttribute( "colorStyle", QString::number(d->fillingColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->fillingImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->fillingBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->fillingFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->fillingFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->fillingFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->fillingSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->fillingSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->fillingSecondColor.blue()) ); writer->writeAttribute( "fileName", d->fillingFileName ); writer->writeAttribute( "opacity", QString::number(d->fillingOpacity) ); writer->writeEndElement(); //Error bars writer->writeStartElement( "errorBars" ); writer->writeAttribute( "xErrorType", QString::number(d->xErrorType) ); WRITE_COLUMN(d->xErrorPlusColumn, xErrorPlusColumn); WRITE_COLUMN(d->xErrorMinusColumn, xErrorMinusColumn); writer->writeAttribute( "yErrorType", QString::number(d->yErrorType) ); WRITE_COLUMN(d->yErrorPlusColumn, yErrorPlusColumn); WRITE_COLUMN(d->yErrorMinusColumn, yErrorMinusColumn); writer->writeAttribute( "type", QString::number(d->errorBarsType) ); writer->writeAttribute( "capSize", QString::number(d->errorBarsCapSize) ); WRITE_QPEN(d->errorBarsPen); writer->writeAttribute( "opacity", QString::number(d->errorBarsOpacity) ); writer->writeEndElement(); writer->writeEndElement(); //close "xyCurve" section } //! Load from XML bool XYCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYCurve); if (!reader->isStartElement() || reader->name() != "xyCurve") { reader->raiseError(i18n("no xy-curve element found")); return false; } if (!readBasicAttributes(reader)) return false; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); READ_COLUMN(xColumn); READ_COLUMN(yColumn); str = attribs.value("visible").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'visible'")); + reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "lines") { attribs = reader->attributes(); READ_INT_VALUE("type", lineType, XYCurve::LineType); READ_INT_VALUE("skipGaps", lineSkipGaps, int); READ_INT_VALUE("interpolationPointsCount", lineInterpolationPointsCount, int); READ_QPEN(d->linePen); READ_DOUBLE_VALUE("opacity", lineOpacity); } else if (!preview && reader->name() == "dropLines") { attribs = reader->attributes(); READ_INT_VALUE("type", dropLineType, XYCurve::DropLineType); READ_QPEN(d->dropLinePen); READ_DOUBLE_VALUE("opacity", dropLineOpacity); } else if (!preview && reader->name() == "symbols") { attribs = reader->attributes(); READ_INT_VALUE("symbolsStyle", symbolsStyle, Symbol::Style); READ_DOUBLE_VALUE("opacity", symbolsOpacity); READ_DOUBLE_VALUE("rotation", symbolsRotationAngle); READ_DOUBLE_VALUE("size", symbolsSize); READ_QBRUSH(d->symbolsBrush); READ_QPEN(d->symbolsPen); } else if (!preview && reader->name() == "values") { attribs = reader->attributes(); READ_INT_VALUE("type", valuesType, XYCurve::ValuesType); READ_COLUMN(valuesColumn); READ_INT_VALUE("position", valuesPosition, XYCurve::ValuesPosition); READ_DOUBLE_VALUE("distance", valuesDistance); READ_DOUBLE_VALUE("rotation", valuesRotationAngle); READ_DOUBLE_VALUE("opacity", valuesOpacity); //don't produce any warning if no prefix or suffix is set (empty string is allowd here in xml) d->valuesPrefix = attribs.value("prefix").toString(); d->valuesSuffix = attribs.value("suffix").toString(); READ_QCOLOR(d->valuesColor); READ_QFONT(d->valuesFont); } else if (!preview && reader->name() == "filling") { attribs = reader->attributes(); READ_INT_VALUE("position", fillingPosition, XYCurve::FillingPosition); READ_INT_VALUE("type", fillingType, PlotArea::BackgroundType); READ_INT_VALUE("colorStyle", fillingColorStyle, PlotArea::BackgroundColorStyle); READ_INT_VALUE("imageStyle", fillingImageStyle, PlotArea::BackgroundImageStyle ); READ_INT_VALUE("brushStyle", fillingBrushStyle, Qt::BrushStyle); str = attribs.value("firstColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_r")); + reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->fillingFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_g")); + reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->fillingFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("firstColor_b")); + reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->fillingFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_r")); + reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->fillingSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_g")); + reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->fillingSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if (str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("secondColor_b")); + reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->fillingSecondColor.setBlue(str.toInt()); READ_STRING_VALUE("fileName", fillingFileName); READ_DOUBLE_VALUE("opacity", fillingOpacity); } else if (!preview && reader->name() == "errorBars") { attribs = reader->attributes(); READ_INT_VALUE("xErrorType", xErrorType, XYCurve::ErrorType); READ_COLUMN(xErrorPlusColumn); READ_COLUMN(xErrorMinusColumn); READ_INT_VALUE("yErrorType", yErrorType, XYCurve::ErrorType); READ_COLUMN(yErrorPlusColumn); READ_COLUMN(yErrorMinusColumn); READ_INT_VALUE("type", errorBarsType, XYCurve::ErrorBarsType); READ_DOUBLE_VALUE("capSize", errorBarsCapSize); READ_QPEN(d->errorBarsPen); READ_DOUBLE_VALUE("opacity", errorBarsOpacity); } } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void XYCurve::loadThemeConfig(const KConfig& config) { KConfigGroup group = config.group("XYCurve"); int index = parentAspect()->indexOfChild(this); const CartesianPlot* plot = dynamic_cast(parentAspect()); QColor themeColor; if (indexthemeColorPalette().size()) themeColor = plot->themeColorPalette().at(index); else { if (plot->themeColorPalette().size()) themeColor = plot->themeColorPalette().last(); } QPen p; Q_D(XYCurve); d->m_suppressRecalc = true; //Line p.setStyle((Qt::PenStyle)group.readEntry("LineStyle", (int)this->linePen().style())); p.setWidthF(group.readEntry("LineWidth", this->linePen().widthF())); p.setColor(themeColor); this->setLinePen(p); this->setLineOpacity(group.readEntry("LineOpacity", this->lineOpacity())); //Drop line p.setStyle((Qt::PenStyle)group.readEntry("DropLineStyle",(int) this->dropLinePen().style())); p.setWidthF(group.readEntry("DropLineWidth", this->dropLinePen().widthF())); p.setColor(themeColor); this->setDropLinePen(p); this->setDropLineOpacity(group.readEntry("DropLineOpacity", this->dropLineOpacity())); //Symbol this->setSymbolsOpacity(group.readEntry("SymbolOpacity", this->symbolsOpacity())); QBrush brush = symbolsBrush(); brush.setColor(themeColor); this->setSymbolsBrush(brush); p = symbolsPen(); p.setColor(themeColor); this->setSymbolsPen(p); //Values this->setValuesOpacity(group.readEntry("ValuesOpacity", this->valuesOpacity())); this->setValuesColor(group.readEntry("ValuesColor", this->valuesColor())); //Filling this->setFillingBrushStyle((Qt::BrushStyle)group.readEntry("FillingBrushStyle",(int) this->fillingBrushStyle())); this->setFillingColorStyle((PlotArea::BackgroundColorStyle)group.readEntry("FillingColorStyle",(int) this->fillingColorStyle())); this->setFillingOpacity(group.readEntry("FillingOpacity", this->fillingOpacity())); this->setFillingPosition((XYCurve::FillingPosition)group.readEntry("FillingPosition",(int) this->fillingPosition())); this->setFillingSecondColor(group.readEntry("FillingSecondColor",(QColor) this->fillingSecondColor())); this->setFillingFirstColor(themeColor); this->setFillingType((PlotArea::BackgroundType)group.readEntry("FillingType",(int) this->fillingType())); //Error Bars p.setStyle((Qt::PenStyle)group.readEntry("ErrorBarsStyle",(int) this->errorBarsPen().style())); p.setWidthF(group.readEntry("ErrorBarsWidth", this->errorBarsPen().widthF())); p.setColor(themeColor); this->setErrorBarsPen(p); this->setErrorBarsOpacity(group.readEntry("ErrorBarsOpacity",this->errorBarsOpacity())); d->m_suppressRecalc = false; d->recalcShapeAndBoundingRect(); } void XYCurve::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("XYCurve"); //Drop line group.writeEntry("DropLineColor",(QColor) this->dropLinePen().color()); group.writeEntry("DropLineStyle",(int) this->dropLinePen().style()); group.writeEntry("DropLineWidth", this->dropLinePen().widthF()); group.writeEntry("DropLineOpacity",this->dropLineOpacity()); //Error Bars group.writeEntry("ErrorBarsCapSize",this->errorBarsCapSize()); group.writeEntry("ErrorBarsOpacity",this->errorBarsOpacity()); group.writeEntry("ErrorBarsColor",(QColor) this->errorBarsPen().color()); group.writeEntry("ErrorBarsStyle",(int) this->errorBarsPen().style()); group.writeEntry("ErrorBarsWidth", this->errorBarsPen().widthF()); //Filling group.writeEntry("FillingBrushStyle",(int) this->fillingBrushStyle()); group.writeEntry("FillingColorStyle",(int) this->fillingColorStyle()); group.writeEntry("FillingOpacity", this->fillingOpacity()); group.writeEntry("FillingPosition",(int) this->fillingPosition()); group.writeEntry("FillingSecondColor",(QColor) this->fillingSecondColor()); group.writeEntry("FillingType",(int) this->fillingType()); //Line group.writeEntry("LineOpacity", this->lineOpacity()); group.writeEntry("LineStyle",(int) this->linePen().style()); group.writeEntry("LineWidth", this->linePen().widthF()); //Symbol group.writeEntry("SymbolOpacity", this->symbolsOpacity()); //Values group.writeEntry("ValuesOpacity", this->valuesOpacity()); group.writeEntry("ValuesColor", (QColor) this->valuesColor()); group.writeEntry("ValuesFont", this->valuesFont()); int index = parentAspect()->indexOfChild(this); if(index<5) { KConfigGroup themeGroup = config.group("Theme"); for(int i = index; i<5; i++) { QString s = "ThemePaletteColor" + QString::number(i+1); themeGroup.writeEntry(s,(QColor) this->linePen().color()); } } } diff --git a/src/backend/worksheet/plots/cartesian/XYDataReductionCurve.cpp b/src/backend/worksheet/plots/cartesian/XYDataReductionCurve.cpp index c6dacf547..7a61a0ff4 100644 --- a/src/backend/worksheet/plots/cartesian/XYDataReductionCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYDataReductionCurve.cpp @@ -1,417 +1,417 @@ /*************************************************************************** File : XYDataReductionCurve.cpp Project : LabPlot Description : A xy-curve defined by a data reduction -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /*! \class XYDataReductionCurve \brief A xy-curve defined by a data reduction \ingroup worksheet */ #include "XYDataReductionCurve.h" #include "XYDataReductionCurvePrivate.h" #include "CartesianCoordinateSystem.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" -#include +#include #include #include #include XYDataReductionCurve::XYDataReductionCurve(const QString& name) : XYAnalysisCurve(name, new XYDataReductionCurvePrivate(this)) { } XYDataReductionCurve::XYDataReductionCurve(const QString& name, XYDataReductionCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYDataReductionCurve::~XYDataReductionCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYDataReductionCurve::recalculate() { Q_D(XYDataReductionCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYDataReductionCurve::icon() const { return QIcon::fromTheme("labplot-xy-data-reduction-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYDataReductionCurve, XYDataReductionCurve::DataReductionData, dataReductionData, dataReductionData) const XYDataReductionCurve::DataReductionResult& XYDataReductionCurve::dataReductionResult() const { Q_D(const XYDataReductionCurve); return d->dataReductionResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYDataReductionCurve, SetDataReductionData, XYDataReductionCurve::DataReductionData, dataReductionData, recalculate); void XYDataReductionCurve::setDataReductionData(const XYDataReductionCurve::DataReductionData& reductionData) { Q_D(XYDataReductionCurve); - exec(new XYDataReductionCurveSetDataReductionDataCmd(d, reductionData, i18n("%1: set options and perform the data reduction"))); + exec(new XYDataReductionCurveSetDataReductionDataCmd(d, reductionData, ki18n("%1: set options and perform the data reduction"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYDataReductionCurvePrivate::XYDataReductionCurvePrivate(XYDataReductionCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYDataReductionCurvePrivate::~XYDataReductionCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYDataReductionCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create dataReduction result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result dataReductionResult = XYDataReductionCurve::DataReductionResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { dataReductionResult.available = true; dataReductionResult.valid = false; dataReductionResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the data reduction to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (dataReductionData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = dataReductionData.xRange.first(); xmax = dataReductionData.xRange.last(); } for (int row = 0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } //number of data points to use const size_t n = (size_t)xdataVector.size(); if (n < 2) { dataReductionResult.available = true; dataReductionResult.valid = false; dataReductionResult.status = i18n("Not enough data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // dataReduction settings const nsl_geom_linesim_type type = dataReductionData.type; const double tol = dataReductionData.tolerance; const double tol2 = dataReductionData.tolerance2; DEBUG("n =" << n); DEBUG("type:" << nsl_geom_linesim_type_name[type]); DEBUG("tolerance/step:" << tol); DEBUG("tolerance2/repeat/maxtol/region:" << tol2); /////////////////////////////////////////////////////////// emit q->completed(10); size_t npoints = 0; double calcTolerance = 0; // calculated tolerance from Douglas-Peucker variant size_t *index = (size_t *) malloc(n*sizeof(size_t)); switch (type) { case nsl_geom_linesim_type_douglas_peucker_variant: // tol used as number of points npoints = tol; calcTolerance = nsl_geom_linesim_douglas_peucker_variant(xdata, ydata, n, npoints, index); break; case nsl_geom_linesim_type_douglas_peucker: npoints = nsl_geom_linesim_douglas_peucker(xdata, ydata, n, tol, index); break; case nsl_geom_linesim_type_nthpoint: // tol used as step npoints = nsl_geom_linesim_nthpoint(n, (int)tol, index); break; case nsl_geom_linesim_type_raddist: npoints = nsl_geom_linesim_raddist(xdata, ydata, n, tol, index); break; case nsl_geom_linesim_type_perpdist: // tol2 used as repeat npoints = nsl_geom_linesim_perpdist_repeat(xdata, ydata, n, tol, tol2, index); break; case nsl_geom_linesim_type_interp: npoints = nsl_geom_linesim_interp(xdata, ydata, n, tol, index); break; case nsl_geom_linesim_type_visvalingam_whyatt: npoints = nsl_geom_linesim_visvalingam_whyatt(xdata, ydata, n, tol, index); break; case nsl_geom_linesim_type_reumann_witkam: npoints = nsl_geom_linesim_reumann_witkam(xdata, ydata, n, tol, index); break; case nsl_geom_linesim_type_opheim: npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index); break; case nsl_geom_linesim_type_lang: // tol2 used as region npoints = nsl_geom_linesim_opheim(xdata, ydata, n, tol, tol2, index); break; } DEBUG("npoints =" << npoints); if (type == nsl_geom_linesim_type_douglas_peucker_variant) { DEBUG("calculated tolerance =" << calcTolerance); } else Q_UNUSED(calcTolerance); emit q->completed(80); xVector->resize((int)npoints); yVector->resize((int)npoints); for (int i = 0; i < (int)npoints; i++) { (*xVector)[i] = xdata[index[i]]; (*yVector)[i] = ydata[index[i]]; } emit q->completed(90); const double posError = nsl_geom_linesim_positional_squared_error(xdata, ydata, n, index); const double areaError = nsl_geom_linesim_area_error(xdata, ydata, n, index); free(index); /////////////////////////////////////////////////////////// //write the result dataReductionResult.available = true; dataReductionResult.valid = true; if (npoints > 0) dataReductionResult.status = QString("OK"); else dataReductionResult.status = QString("FAILURE"); dataReductionResult.elapsedTime = timer.elapsed(); dataReductionResult.npoints = npoints; dataReductionResult.posError = posError; dataReductionResult.areaError = areaError; //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; emit q->completed(100); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYDataReductionCurve::save(QXmlStreamWriter* writer) const{ Q_D(const XYDataReductionCurve); writer->writeStartElement("xyDataReductionCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-dataReduction-curve specific information // dataReduction data writer->writeStartElement("dataReductionData"); writer->writeAttribute( "autoRange", QString::number(d->dataReductionData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->dataReductionData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->dataReductionData.xRange.last()) ); writer->writeAttribute( "type", QString::number(d->dataReductionData.type) ); writer->writeAttribute( "autoTolerance", QString::number(d->dataReductionData.autoTolerance) ); writer->writeAttribute( "tolerance", QString::number(d->dataReductionData.tolerance) ); writer->writeAttribute( "autoTolerance2", QString::number(d->dataReductionData.autoTolerance2) ); writer->writeAttribute( "tolerance2", QString::number(d->dataReductionData.tolerance2) ); writer->writeEndElement();// dataReductionData // dataReduction results (generated columns) writer->writeStartElement("dataReductionResult"); writer->writeAttribute( "available", QString::number(d->dataReductionResult.available) ); writer->writeAttribute( "valid", QString::number(d->dataReductionResult.valid) ); writer->writeAttribute( "status", d->dataReductionResult.status ); writer->writeAttribute( "time", QString::number(d->dataReductionResult.elapsedTime) ); writer->writeAttribute( "npoints", QString::number(d->dataReductionResult.npoints) ); writer->writeAttribute( "posError", QString::number(d->dataReductionResult.posError) ); writer->writeAttribute( "areaError", QString::number(d->dataReductionResult.areaError) ); //save calculated columns if available if (d->xColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"dataReductionResult" writer->writeEndElement(); //"xyDataReductionCurve" } //! Load from XML bool XYDataReductionCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYDataReductionCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyDataReductionCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "dataReductionData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", dataReductionData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", dataReductionData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", dataReductionData.xRange.last()); READ_INT_VALUE("type", dataReductionData.type, nsl_geom_linesim_type); READ_INT_VALUE("autoTolerance", dataReductionData.autoTolerance, int); READ_DOUBLE_VALUE("tolerance", dataReductionData.tolerance); READ_INT_VALUE("autoTolerance2", dataReductionData.autoTolerance2, int); READ_DOUBLE_VALUE("tolerance2", dataReductionData.tolerance2); } else if (!preview && reader->name() == "dataReductionResult") { attribs = reader->attributes(); READ_INT_VALUE("available", dataReductionResult.available, int); READ_INT_VALUE("valid", dataReductionResult.valid, int); READ_STRING_VALUE("status", dataReductionResult.status); READ_INT_VALUE("time", dataReductionResult.elapsedTime, int); READ_INT_VALUE("npoints", dataReductionResult.npoints, size_t); READ_DOUBLE_VALUE("posError", dataReductionResult.posError); READ_DOUBLE_VALUE("areaError", dataReductionResult.areaError); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name()=="x") d->xColumn = column; else if (column->name()=="y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYDifferentiationCurve.cpp b/src/backend/worksheet/plots/cartesian/XYDifferentiationCurve.cpp index 5eb4c7a2a..fb1c39374 100644 --- a/src/backend/worksheet/plots/cartesian/XYDifferentiationCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYDifferentiationCurve.cpp @@ -1,365 +1,365 @@ /*************************************************************************** File : XYDifferentiationCurve.cpp Project : LabPlot Description : A xy-curve defined by an differentiation -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 * * * ***************************************************************************/ /*! \class XYDifferentiationCurve \brief A xy-curve defined by an differentiation \ingroup worksheet */ #include "XYDifferentiationCurve.h" #include "XYDifferentiationCurvePrivate.h" #include "CartesianCoordinateSystem.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" extern "C" { #include } -#include +#include #include #include #include XYDifferentiationCurve::XYDifferentiationCurve(const QString& name) : XYAnalysisCurve(name, new XYDifferentiationCurvePrivate(this)) { } XYDifferentiationCurve::XYDifferentiationCurve(const QString& name, XYDifferentiationCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYDifferentiationCurve::~XYDifferentiationCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYDifferentiationCurve::recalculate() { Q_D(XYDifferentiationCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYDifferentiationCurve::icon() const { return QIcon::fromTheme("labplot-xy-differentiation-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYDifferentiationCurve, XYDifferentiationCurve::DifferentiationData, differentiationData, differentiationData) const XYDifferentiationCurve::DifferentiationResult& XYDifferentiationCurve::differentiationResult() const { Q_D(const XYDifferentiationCurve); return d->differentiationResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYDifferentiationCurve, SetDifferentiationData, XYDifferentiationCurve::DifferentiationData, differentiationData, recalculate); void XYDifferentiationCurve::setDifferentiationData(const XYDifferentiationCurve::DifferentiationData& differentiationData) { Q_D(XYDifferentiationCurve); - exec(new XYDifferentiationCurveSetDifferentiationDataCmd(d, differentiationData, i18n("%1: set options and perform the differentiation"))); + exec(new XYDifferentiationCurveSetDifferentiationDataCmd(d, differentiationData, ki18n("%1: set options and perform the differentiation"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYDifferentiationCurvePrivate::XYDifferentiationCurvePrivate(XYDifferentiationCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYDifferentiationCurvePrivate::~XYDifferentiationCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } // ... // see XYFitCurvePrivate void XYDifferentiationCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create differentiation result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result differentiationResult = XYDifferentiationCurve::DifferentiationResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { differentiationResult.available = true; differentiationResult.valid = false; differentiationResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the differentiation to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (differentiationData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = differentiationData.xRange.first(); xmax = differentiationData.xRange.last(); } for (int row=0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } //number of data points to differentiate const size_t n = (size_t)xdataVector.size(); if (n < 3) { differentiationResult.available = true; differentiationResult.valid = false; differentiationResult.status = i18n("Not enough data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // differentiation settings const nsl_diff_deriv_order_type derivOrder = differentiationData.derivOrder; const int accOrder = differentiationData.accOrder; DEBUG(nsl_diff_deriv_order_name[derivOrder] << "derivative"); DEBUG("accuracy order:" << accOrder); /////////////////////////////////////////////////////////// int status = 0; switch (derivOrder) { case nsl_diff_deriv_order_first: status = nsl_diff_first_deriv(xdata, ydata, n, accOrder); break; case nsl_diff_deriv_order_second: status = nsl_diff_second_deriv(xdata, ydata, n, accOrder); break; case nsl_diff_deriv_order_third: status = nsl_diff_third_deriv(xdata, ydata, n, accOrder); break; case nsl_diff_deriv_order_fourth: status = nsl_diff_fourth_deriv(xdata, ydata, n, accOrder); break; case nsl_diff_deriv_order_fifth: status = nsl_diff_fifth_deriv(xdata, ydata, n, accOrder); break; case nsl_diff_deriv_order_sixth: status = nsl_diff_sixth_deriv(xdata, ydata, n, accOrder); break; } xVector->resize((int)n); yVector->resize((int)n); memcpy(xVector->data(), xdata, n * sizeof(double)); memcpy(yVector->data(), ydata, n * sizeof(double)); /////////////////////////////////////////////////////////// //write the result differentiationResult.available = true; differentiationResult.valid = true; differentiationResult.status = QString::number(status); differentiationResult.elapsedTime = timer.elapsed(); //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYDifferentiationCurve::save(QXmlStreamWriter* writer) const{ Q_D(const XYDifferentiationCurve); writer->writeStartElement("xyDifferentiationCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-differentiation-curve specific information // differentiation data writer->writeStartElement("differentiationData"); writer->writeAttribute( "derivOrder", QString::number(d->differentiationData.derivOrder) ); writer->writeAttribute( "accOrder", QString::number(d->differentiationData.accOrder) ); writer->writeAttribute( "autoRange", QString::number(d->differentiationData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->differentiationData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->differentiationData.xRange.last()) ); writer->writeEndElement();// differentiationData // differentiation results (generated columns) writer->writeStartElement("differentiationResult"); writer->writeAttribute( "available", QString::number(d->differentiationResult.available) ); writer->writeAttribute( "valid", QString::number(d->differentiationResult.valid) ); writer->writeAttribute( "status", d->differentiationResult.status ); writer->writeAttribute( "time", QString::number(d->differentiationResult.elapsedTime) ); //save calculated columns if available if (d->xColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"differentiationResult" writer->writeEndElement(); //"xyDifferentiationCurve" } //! Load from XML bool XYDifferentiationCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYDifferentiationCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyDifferentiationCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "differentiationData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", differentiationData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", differentiationData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", differentiationData.xRange.last()); READ_INT_VALUE("derivOrder", differentiationData.derivOrder, nsl_diff_deriv_order_type); READ_INT_VALUE("accOrder", differentiationData.accOrder, int); } else if (!preview && reader->name() == "differentiationResult") { attribs = reader->attributes(); READ_INT_VALUE("available", differentiationResult.available, int); READ_INT_VALUE("valid", differentiationResult.valid, int); READ_STRING_VALUE("status", differentiationResult.status); READ_INT_VALUE("time", differentiationResult.elapsedTime, int); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name()=="x") d->xColumn = column; else if (column->name()=="y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYEquationCurve.cpp b/src/backend/worksheet/plots/cartesian/XYEquationCurve.cpp index 6a24e34d2..deac5c26a 100644 --- a/src/backend/worksheet/plots/cartesian/XYEquationCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYEquationCurve.cpp @@ -1,231 +1,231 @@ /*************************************************************************** File : XYEquationCurve.cpp Project : LabPlot Description : A xy-curve defined by a mathematical equation -------------------------------------------------------------------- Copyright : (C) 2014-2017 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 * * * ***************************************************************************/ /*! \class XYEquationCurve \brief A xy-curve defined by a mathematical equation \ingroup worksheet */ #include "XYEquationCurve.h" #include "XYEquationCurvePrivate.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/gsl/ExpressionParser.h" #include -#include +#include XYEquationCurve::XYEquationCurve(const QString& name) : XYCurve(name, new XYEquationCurvePrivate(this)) { init(); } XYEquationCurve::XYEquationCurve(const QString& name, XYEquationCurvePrivate* dd) : XYCurve(name, dd) { init(); } XYEquationCurve::~XYEquationCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYEquationCurve::init() { Q_D(XYEquationCurve); d->xColumn->setHidden(true); addChildFast(d->xColumn); d->yColumn->setHidden(true); addChildFast(d->yColumn); //TODO: read from the saved settings for XYEquationCurve? d->lineType = XYCurve::Line; d->symbolsStyle = Symbol::NoSymbols; setUndoAware(false); suppressRetransform(true); setXColumn(d->xColumn); setYColumn(d->yColumn); suppressRetransform(false); setUndoAware(true); } void XYEquationCurve::recalculate() { Q_D(XYEquationCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYEquationCurve::icon() const { return QIcon::fromTheme("labplot-xy-equation-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYEquationCurve, XYEquationCurve::EquationData, equationData, equationData) //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYEquationCurve, SetEquationData, XYEquationCurve::EquationData, equationData, recalculate); void XYEquationCurve::setEquationData(const XYEquationCurve::EquationData& equationData) { Q_D(XYEquationCurve); if ( (equationData.expression1 != d->equationData.expression1) || (equationData.expression2 != d->equationData.expression2) || (equationData.min != d->equationData.min) || (equationData.max != d->equationData.max) || (equationData.count != d->equationData.count) ) - exec(new XYEquationCurveSetEquationDataCmd(d, equationData, i18n("%1: set equation"))); + exec(new XYEquationCurveSetEquationDataCmd(d, equationData, ki18n("%1: set equation"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYEquationCurvePrivate::XYEquationCurvePrivate(XYEquationCurve* owner) : XYCurvePrivate(owner), xColumn(new Column("x", AbstractColumn::Numeric)), yColumn(new Column("y", AbstractColumn::Numeric)), xVector(static_cast* >(xColumn->data())), yVector(static_cast* >(yColumn->data())), q(owner) { } XYEquationCurvePrivate::~XYEquationCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYEquationCurvePrivate::recalculate() { //resize the vector if a new number of point to calculate was provided if (equationData.count != xVector->size()) { if (equationData.count >= 1) { xVector->resize(equationData.count); yVector->resize(equationData.count); } else { //invalid number of points provided xVector->clear(); yVector->clear(); emit q->dataChanged(); return; } } else { if (equationData.count < 1) return; } ExpressionParser* parser = ExpressionParser::getInstance(); bool rc = false; if (equationData.type == XYEquationCurve::Cartesian) { rc = parser->evaluateCartesian( equationData.expression1, equationData.min, equationData.max, equationData.count, xVector, yVector ); } else if (equationData.type == XYEquationCurve::Polar) { rc = parser->evaluatePolar( equationData.expression1, equationData.min, equationData.max, equationData.count, xVector, yVector ); } else if (equationData.type == XYEquationCurve::Parametric) { rc = parser->evaluateParametric(equationData.expression1, equationData.expression2, equationData.min, equationData.max, equationData.count, xVector, yVector); } if (!rc) { xVector->clear(); yVector->clear(); } emit q->dataChanged(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYEquationCurve::save(QXmlStreamWriter* writer) const{ Q_D(const XYEquationCurve); writer->writeStartElement( "xyEquationCurve" ); //write xy-curve information XYCurve::save(writer); //write xy-equationCurve specific information writer->writeStartElement( "equationData" ); writer->writeAttribute( "type", QString::number(d->equationData.type) ); writer->writeAttribute( "expression1", d->equationData.expression1 ); writer->writeAttribute( "expression2", d->equationData.expression2 ); writer->writeAttribute( "min", d->equationData.min); writer->writeAttribute( "max", d->equationData.max ); writer->writeAttribute( "count", QString::number(d->equationData.count) ); writer->writeEndElement(); writer->writeEndElement(); } //! Load from XML bool XYEquationCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYEquationCurve); if (!reader->isStartElement() || reader->name() != "xyEquationCurve") { reader->raiseError(i18n("no xy equation curve element found")); return false; } - QString attributeWarning = i18n( "Attribute '%1' missing or empty, default value is used" ); + KLocalizedString attributeWarning = ki18n( "Attribute '%1' missing or empty, default value is used" ); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyEquationCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyCurve") { if ( !XYCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "equationData") { attribs = reader->attributes(); READ_INT_VALUE("type", equationData.type, XYEquationCurve::EquationType); READ_STRING_VALUE("expression1", equationData.expression1); READ_STRING_VALUE("expression2", equationData.expression2); READ_STRING_VALUE("min", equationData.min); READ_STRING_VALUE("max", equationData.max); READ_INT_VALUE("count", equationData.count, int); } } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYFitCurve.cpp b/src/backend/worksheet/plots/cartesian/XYFitCurve.cpp index fcbe66869..bb2e83b02 100644 --- a/src/backend/worksheet/plots/cartesian/XYFitCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYFitCurve.cpp @@ -1,2379 +1,2396 @@  /*************************************************************************** File : XYFitCurve.cpp Project : LabPlot Description : A xy-curve defined by a fit model -------------------------------------------------------------------- Copyright : (C) 2014-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 * * * ***************************************************************************/ /*! \class XYFitCurve \brief A xy-curve defined by a fit model \ingroup worksheet */ #include "XYFitCurve.h" #include "XYFitCurvePrivate.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/gsl/errors.h" #include "backend/gsl/ExpressionParser.h" extern "C" { #include #include #include #include #include #include #include "backend/gsl/parser.h" #include "backend/nsl/nsl_sf_stats.h" #include "backend/nsl/nsl_stats.h" } #include #include #include XYFitCurve::XYFitCurve(const QString& name) : XYAnalysisCurve(name, new XYFitCurvePrivate(this)) { } XYFitCurve::XYFitCurve(const QString& name, XYFitCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYFitCurve::~XYFitCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYFitCurve::recalculate() { Q_D(XYFitCurve); d->recalculate(); } void XYFitCurve::evaluate(bool preview) { Q_D(XYFitCurve); d->evaluate(preview); } void XYFitCurve::initStartValues(const XYCurve* curve) { Q_D(XYFitCurve); XYFitCurve::FitData& fitData = d->fitData; initStartValues(fitData, curve); } void XYFitCurve::initStartValues(XYFitCurve::FitData& fitData, const XYCurve* curve) { DEBUG("XYFitCurve::initStartValues()"); if (!curve) { DEBUG(" no curve given"); return; } const Column* tmpXDataColumn = dynamic_cast(curve->xColumn()); const Column* tmpYDataColumn = dynamic_cast(curve->yColumn()); if (!tmpXDataColumn || !tmpYDataColumn) { DEBUG(" data columns not available"); return; } DEBUG(" x data rows = " << tmpXDataColumn->rowCount()); nsl_fit_model_category modelCategory = fitData.modelCategory; int modelType = fitData.modelType; int degree = fitData.degree; DEBUG(" fit model type = " << modelType << ", degree = " << degree); QVector& paramStartValues = fitData.paramStartValues; //QVector* xVector = static_cast* >(tmpXDataColumn->data()); //double xmean = gsl_stats_mean(xVector->constData(), 1, tmpXDataColumn->rowCount()); double xmin = tmpXDataColumn->minimum(); double xmax = tmpXDataColumn->maximum(); //double ymin = tmpYDataColumn->minimum(); //double ymax = tmpYDataColumn->maximum(); double xrange = xmax-xmin; //double yrange = ymax-ymin; DEBUG(" x min/max = " << xmin << ' ' << xmax); //DEBUG(" y min/max = " << ymin << ' ' << ymax); switch (modelCategory) { case nsl_fit_model_basic: switch (modelType) { case nsl_fit_model_polynomial: // not needed (works anyway) break; //TODO: handle basic models case nsl_fit_model_power: case nsl_fit_model_exponential: case nsl_fit_model_inverse_exponential: case nsl_fit_model_fourier: break; } break; case nsl_fit_model_peak: // use equidistant mu's and (xmax-xmin)/(10*degree) as sigma(, gamma) - if (modelType == nsl_fit_model_voigt) { - for (int d = 0; d < 1; d++) { //TODO: degree - paramStartValues[3*d+1] = xmin + (d+1.)*xrange/(degree+1.); - paramStartValues[3*d+2] = xrange/(10.*degree); - paramStartValues[3*d+3] = xrange/(10.*degree); + switch (modelType) { + case nsl_fit_model_gaussian: + case nsl_fit_model_lorentz: + case nsl_fit_model_sech: + case nsl_fit_model_logistic: + for (int d = 0; d < degree; d++) { + paramStartValues[3*d+2] = xmin + (d+1.)*xrange/(degree+1.); // mu + paramStartValues[3*d+1] = xrange/(10.*degree); // sigma } - } else { + break; + case nsl_fit_model_voigt: + for (int d = 0; d < degree; d++) { + paramStartValues[4*d+1] = xmin + (d+1.)*xrange/(degree+1.); // mu + paramStartValues[4*d+2] = xrange/(10.*degree); // sigma + paramStartValues[4*d+3] = xrange/(10.*degree); // gamma + } + break; + case nsl_fit_model_pseudovoigt1: for (int d = 0; d < degree; d++) { - paramStartValues[3*d+2] = xmin + (d+1.)*xrange/(degree+1.); - paramStartValues[3*d+1] = xrange/(10.*degree); + paramStartValues[4*d+1] = 0.5; // eta + paramStartValues[4*d+2] = xrange/(10.*degree); // sigma + paramStartValues[4*d+3] = xmin + (d+1.)*xrange/(degree+1.); // mu } + break; } break; case nsl_fit_model_growth: switch (modelType) { case nsl_fit_model_atan: case nsl_fit_model_tanh: case nsl_fit_model_algebraic_sigmoid: case nsl_fit_model_erf: case nsl_fit_model_gudermann: case nsl_fit_model_sigmoid: // use (xmax+xmin)/2 as mu and (xmax-xmin)/10 as sigma paramStartValues[1] = (xmax+xmin)/2.; paramStartValues[2] = xrange/10.; break; case nsl_fit_model_hill: paramStartValues[2] = xrange/10.; break; case nsl_fit_model_gompertz: //TODO break; } break; case nsl_fit_model_distribution: switch (modelType) { case nsl_sf_stats_gaussian: case nsl_sf_stats_laplace: case nsl_sf_stats_rayleigh_tail: case nsl_sf_stats_lognormal: case nsl_sf_stats_logistic: case nsl_sf_stats_sech: case nsl_sf_stats_cauchy_lorentz: case nsl_sf_stats_levy: // use (xmax+xmin)/2 as mu and (xmax-xmin)/10 as sigma paramStartValues[2] = (xmin+xmax)/2.; paramStartValues[1] = xrange/10.; break; //TODO: other types default: break; } break; case nsl_fit_model_custom: // not possible break; } } /*! * sets the parameter names for given model category, model type and degree in \c fitData for given action */ void XYFitCurve::initFitData(PlotDataDialog::AnalysisAction action) { if (!action) return; Q_D(XYFitCurve); XYFitCurve::FitData& fitData = d->fitData; if (action == PlotDataDialog::FitLinear) { //Linear fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = (int)nsl_fit_model_polynomial; fitData.degree = 1; } else if (action == PlotDataDialog::FitPower) { //Power fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = (int)nsl_fit_model_power; fitData.degree = 1; } else if (action == PlotDataDialog::FitExp1) { //Exponential (degree 1) fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = (int)nsl_fit_model_exponential; fitData.degree = 1; } else if (action == PlotDataDialog::FitExp2) { //Exponential (degree 2) fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = (int)nsl_fit_model_exponential; fitData.degree = 2; } else if (action == PlotDataDialog::FitInvExp) { //Inverse exponential fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = (int)nsl_fit_model_inverse_exponential; } else if (action == PlotDataDialog::FitGauss) { //Gauss fitData.modelCategory = nsl_fit_model_peak; fitData.modelType = (int)nsl_fit_model_gaussian; fitData.degree = 1; } else if (action == PlotDataDialog::FitCauchyLorentz) { //Cauchy-Lorentz fitData.modelCategory = nsl_fit_model_peak; fitData.modelType = (int)nsl_fit_model_lorentz; fitData.degree = 1; } else if (action == PlotDataDialog::FitTan) { //Arc tangent fitData.modelCategory = nsl_fit_model_growth; fitData.modelType = (int)nsl_fit_model_atan; } else if (action == PlotDataDialog::FitTanh) { //Hyperbolic tangent fitData.modelCategory = nsl_fit_model_growth; fitData.modelType = (int)nsl_fit_model_tanh; } else if (action == PlotDataDialog::FitErrFunc) { //Error function fitData.modelCategory = nsl_fit_model_growth; fitData.modelType = (int)nsl_fit_model_erf; } else { //Custom fitData.modelCategory = nsl_fit_model_custom; fitData.modelType = 0; } XYFitCurve::initFitData(fitData); } /*! * sets the model expression and the parameter names for given model category, model type and degree in \c fitData */ void XYFitCurve::initFitData(XYFitCurve::FitData& fitData) { nsl_fit_model_category modelCategory = fitData.modelCategory; int modelType = fitData.modelType; QString& model = fitData.model; QStringList& paramNames = fitData.paramNames; QStringList& paramNamesUtf8 = fitData.paramNamesUtf8; int degree = fitData.degree; QVector& paramStartValues = fitData.paramStartValues; QVector& paramLowerLimits = fitData.paramLowerLimits; QVector& paramUpperLimits = fitData.paramUpperLimits; QVector& paramFixed = fitData.paramFixed; if (modelCategory != nsl_fit_model_custom) { DEBUG("XYFitCurve::initFitData() for model category = " << nsl_fit_model_category_name[modelCategory] << ", model type = " << modelType << ", degree = " << degree); paramNames.clear(); } else { DEBUG("XYFitCurve::initFitData() for model category = nsl_fit_model_custom, model type = " << modelType << ", degree = " << degree); } paramNamesUtf8.clear(); // 10 indices used in multi degree models QStringList indices = { UTF8_QSTRING("₁"), UTF8_QSTRING("₂"), UTF8_QSTRING("₃"), UTF8_QSTRING("₄"), UTF8_QSTRING("₅"), UTF8_QSTRING("₆"), UTF8_QSTRING("₇"), UTF8_QSTRING("₈"), UTF8_QSTRING("₉"), UTF8_QSTRING("₁₀")}; switch (modelCategory) { case nsl_fit_model_basic: model = nsl_fit_model_basic_equation[fitData.modelType]; switch (modelType) { case nsl_fit_model_polynomial: paramNames << "c0" << "c1"; paramNamesUtf8 << UTF8_QSTRING("c₀") << UTF8_QSTRING("c₁"); if (degree == 2) { model += " + c2*x^2"; paramNames << "c2"; paramNamesUtf8 << UTF8_QSTRING("c₂"); } else if (degree > 2) { for (int i = 2; i <= degree; ++i) { QString numStr = QString::number(i); model += "+c" + numStr + "*x^" + numStr; paramNames << 'c' + numStr; paramNamesUtf8 << 'c' + indices[i-1]; } } break; case nsl_fit_model_power: if (degree == 1) { paramNames << "a" << "b"; } else { paramNames << "a" << "b" << "c"; model = "a + b*x^c"; } break; case nsl_fit_model_exponential: if (degree == 1) { paramNames << "a" << "b"; } else { for (int i = 1; i <= degree; i++) { QString numStr = QString::number(i); if (i == 1) model = "a1*exp(b1*x)"; else model += " + a" + numStr + "*exp(b" + numStr + "*x)"; paramNames << 'a' + numStr << 'b' + numStr; paramNamesUtf8 << 'a' + indices[i-1] << 'b' + indices[i-1]; } } break; case nsl_fit_model_inverse_exponential: paramNames << "a" << "b" << "c"; break; case nsl_fit_model_fourier: paramNames << "w" << "a0" << "a1" << "b1"; paramNamesUtf8 << UTF8_QSTRING("ω") << UTF8_QSTRING("a₀") << UTF8_QSTRING("a₁") << UTF8_QSTRING("b₁"); if (degree > 1) { for (int i = 1; i <= degree; ++i) { QString numStr = QString::number(i); model += "+ (a" + numStr + "*cos(" + numStr + "*w*x) + b" + numStr + "*sin(" + numStr + "*w*x))"; paramNames << 'a' + numStr << 'b' + numStr; paramNamesUtf8 << 'a' + indices[i-1] << 'b' + indices[i-1]; } } break; } break; case nsl_fit_model_peak: model = nsl_fit_model_peak_equation[fitData.modelType]; switch (modelType) { case nsl_fit_model_gaussian: switch (degree) { case 1: paramNames << "a" << "s" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << UTF8_QSTRING("μ"); break; - case 2: - model = "1./sqrt(2*pi) * (a1/s1 * exp(-((x-mu1)/s1)^2/2) + a2/s2 * exp(-((x-mu2)/s2)^2/2))"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂"); - break; - case 3: - model = "1./sqrt(2*pi) * (a1/s1 * exp(-((x-mu1)/s1)^2/2) + a2/s2 * exp(-((x-mu2)/s2)^2/2) + a3/s3 * exp(-((x-mu3)/s3)^2/2))"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2" << "a3" << "s3" << "mu3"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂") - << UTF8_QSTRING("A₃") << UTF8_QSTRING("σ₃") << UTF8_QSTRING("μ₃"); - break; default: model = "1./sqrt(2*pi) * ("; for (int i = 1; i <= degree; ++i) { QString numStr = QString::number(i); if (i > 1) model += " + "; model += 'a' + numStr + "/s" + numStr + "* exp(-((x-mu" + numStr + ")/s" + numStr + ")^2/2)"; paramNames << 'a' + numStr << 's' + numStr << "mu" + numStr; paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("σ") + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1]; } model += ')'; } break; case nsl_fit_model_lorentz: switch (degree) { case 1: paramNames << "a" << "g" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("γ") << UTF8_QSTRING("μ"); break; - case 2: - model = "1./pi * (a1 * g1/(g1^2+(x-mu1)^2) + a2 * g2/(g2^2+(x-mu2)^2))"; - paramNames << "a1" << "g1" << "mu1" << "a2" << "g2" << "mu2"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("γ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("γ₂") << UTF8_QSTRING("μ₂"); - break; - case 3: - model = "1./pi * (a1 * g1/(g1^2+(x-mu1)^2) + a2 * g2/(g2^2+(x-mu2)^2) + a3 * g3/(g3^2+(x-mu3)^2))"; - paramNames << "a1" << "g1" << "mu1" << "a2" << "g2" << "mu2" << "a3" << "g3" << "mu3"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("γ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("γ₂") << UTF8_QSTRING("μ₂") - << UTF8_QSTRING("A₃") << UTF8_QSTRING("γ₃") << UTF8_QSTRING("μ₃"); - break; default: model = "1./pi * ("; for (int i = 1; i <= degree; ++i) { QString numStr = QString::number(i); if (i > 1) model += " + "; model += 'a' + numStr + " * g" + numStr + "/(g" + numStr + "^2+(x-mu" + numStr + ")^2)"; paramNames << 'a' + numStr << 'g' + numStr << "mu" + numStr; paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("γ") + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1]; } model += ')'; } break; case nsl_fit_model_sech: switch (degree) { case 1: paramNames << "a" << "s" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << UTF8_QSTRING("μ"); break; - case 2: - model = "1/pi * (a1/s1 * sech((x-mu1)/s1) + a2/s2 * sech((x-mu2)/s2))"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂"); - break; - case 3: - model = "1/pi * (a1/s1 * sech((x-mu1)/s1) + a2/s2 * sech((x-mu2)/s2) + a3/s3 * sech((x-mu3)/s3))"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2" << "a3" << "s3" << "mu3"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂") - << UTF8_QSTRING("A₃") << UTF8_QSTRING("σ₃") << UTF8_QSTRING("μ₃"); - break; default: model = "1/pi * ("; for (int i = 1; i <= degree; ++i) { QString numStr = QString::number(i); if (i > 1) model += " + "; model += 'a' + numStr + "/s" + numStr + "* sech((x-mu" + numStr + ")/s" + numStr + ')'; paramNames << 'a' + numStr << "s" + numStr << "mu" + numStr; paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("σ") + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1]; } model += ')'; } break; case nsl_fit_model_logistic: switch (degree) { case 1: paramNames << "a" << "s" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << UTF8_QSTRING("μ"); break; - case 2: - model = "1/4 * (a1/s1 * sech((x-mu1)/2/s1)**2 + a2/s2 * sech((x-mu2)/2/s2)**2)"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂"); - break; - case 3: - model = "1/4 * (a1/s1 * sech((x-mu1)/2/s1)**2 + a2/s2 * sech((x-mu2)/2/s2)**2 + a3/s3 * sech((x-mu3)/2/s3)**2)"; - paramNames << "a1" << "s1" << "mu1" << "a2" << "s2" << "mu2" << "a3" << "s3" << "mu3"; - paramNamesUtf8 << UTF8_QSTRING("A₁") << UTF8_QSTRING("σ₁") << UTF8_QSTRING("μ₁") - << UTF8_QSTRING("A₂") << UTF8_QSTRING("σ₂") << UTF8_QSTRING("μ₂") - << UTF8_QSTRING("A₃") << UTF8_QSTRING("σ₃") << UTF8_QSTRING("μ₃"); - break; default: model = "1/4 * ("; for (int i = 1; i <= degree; ++i) { QString numStr = QString::number(i); if (i > 1) model += " + "; model += 'a' + numStr + "/s" + numStr + "* sech((x-mu" + numStr + ")/2/s" + numStr + ")**2"; paramNames << 'a' + numStr << 's' + numStr << "mu" + numStr; paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("σ") + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1]; } model += ')'; } break; case nsl_fit_model_voigt: - //TODO: degree - paramNames << "a" << "mu" << "s" << "g"; - paramNamesUtf8 << "A" << UTF8_QSTRING("μ") << UTF8_QSTRING("σ") << UTF8_QSTRING("γ"); + switch(degree) { + case 1: + paramNames << "a" << "mu" << "s" << "g"; + paramNamesUtf8 << "A" << UTF8_QSTRING("μ") << UTF8_QSTRING("σ") << UTF8_QSTRING("γ"); + break; + default: + model = ""; + for (int i = 1; i <= degree; ++i) { + QString numStr = QString::number(i); + if (i > 1) + model += " + "; + model += 'a' + numStr + "*voigt(x-mu" + numStr + ",s" + numStr + ",g" + numStr + ')'; + paramNames << "a" + numStr << "mu" + numStr << "s" + numStr << "g" + numStr; + paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1] << UTF8_QSTRING("σ") + indices[i-1] << UTF8_QSTRING("γ") + indices[i-1]; + } + } + break; + case nsl_fit_model_pseudovoigt1: + switch(degree) { + case 1: + paramNames << "a" << "et" << "w" << "mu"; // eta function exists! + paramNamesUtf8 << "A" << UTF8_QSTRING("η") << "w" << UTF8_QSTRING("μ"); + break; + default: + model=""; + for (int i = 1; i <= degree; ++i) { + QString numStr = QString::number(i); + if (i > 1) + model += " + "; + model += 'a' + numStr + "*pseudovoigt1(x-mu" + numStr + ",eta" + numStr + ",w" + numStr + ')'; + paramNames << "a" + numStr << "eta" + numStr << "w" + numStr << "mu" + numStr; + paramNamesUtf8 << 'A' + indices[i-1] << UTF8_QSTRING("η") + indices[i-1] << 'w' + indices[i-1] << UTF8_QSTRING("μ") + indices[i-1]; + } + } break; } break; case nsl_fit_model_growth: model = nsl_fit_model_growth_equation[fitData.modelType]; switch (modelType) { case nsl_fit_model_atan: case nsl_fit_model_tanh: case nsl_fit_model_algebraic_sigmoid: case nsl_fit_model_erf: case nsl_fit_model_gudermann: paramNames << "a" << "mu" << "s"; paramNamesUtf8 << "A" << UTF8_QSTRING("μ") << UTF8_QSTRING("σ"); break; case nsl_fit_model_sigmoid: paramNames << "a" << "mu" << "k"; paramNamesUtf8 << "A" << UTF8_QSTRING("μ") << "k"; break; case nsl_fit_model_hill: paramNames << "a" << "n" << "a"; paramNamesUtf8 << "A" << "n" << UTF8_QSTRING("σ"); break; case nsl_fit_model_gompertz: paramNames << "a" << "b" << "c"; break; } break; case nsl_fit_model_distribution: model = nsl_sf_stats_distribution_equation[fitData.modelType]; switch (modelType) { case nsl_sf_stats_gaussian: case nsl_sf_stats_laplace: case nsl_sf_stats_rayleigh_tail: case nsl_sf_stats_lognormal: case nsl_sf_stats_logistic: case nsl_sf_stats_sech: paramNames << "a" << "s" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << UTF8_QSTRING("μ"); break; case nsl_sf_stats_gaussian_tail: paramNames << "A" << "s" << "a" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << "a" << UTF8_QSTRING("μ"); break; case nsl_sf_stats_exponential: paramNames << "a" << "l" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("λ") << UTF8_QSTRING("μ"); break; case nsl_sf_stats_exponential_power: paramNames << "a" << "s" << "b" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << "b" << UTF8_QSTRING("μ"); break; case nsl_sf_stats_cauchy_lorentz: case nsl_sf_stats_levy: paramNames << "a" << "g" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("γ") << UTF8_QSTRING("μ"); break; case nsl_sf_stats_rayleigh: paramNames << "a" << "s"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ"); break; case nsl_sf_stats_landau: paramNames << "a"; paramNamesUtf8 << "A"; break; case nsl_sf_stats_levy_alpha_stable: // unused distributions case nsl_sf_stats_levy_skew_alpha_stable: case nsl_sf_stats_bernoulli: break; case nsl_sf_stats_gamma: paramNames << "a" << "k" << "t"; paramNamesUtf8 << "A"<< "k" << UTF8_QSTRING("θ"); break; case nsl_sf_stats_flat: paramNames << "A" << "b" << "a"; break; case nsl_sf_stats_chi_squared: paramNames << "a" << "n"; paramNamesUtf8 << "A" << "n"; break; case nsl_sf_stats_fdist: paramNames << "a" << "n1" << "n2"; paramNamesUtf8 << "A" << UTF8_QSTRING("ν₁") << UTF8_QSTRING("ν₂"); break; case nsl_sf_stats_tdist: paramNames << "a" << "n"; paramNamesUtf8 << "A" << UTF8_QSTRING("ν"); break; case nsl_sf_stats_beta: case nsl_sf_stats_pareto: paramNames << "A" << "a" << "b"; break; case nsl_sf_stats_weibull: paramNames << "a" << "k" << "l" << "mu"; paramNamesUtf8 << "A" << "k" << UTF8_QSTRING("λ") << UTF8_QSTRING("μ"); break; case nsl_sf_stats_gumbel1: paramNames << "a" << "s" << "mu" << "b"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ") << UTF8_QSTRING("μ") << UTF8_QSTRING("β"); break; case nsl_sf_stats_gumbel2: paramNames << "A" << "a" << "b" << "mu"; paramNamesUtf8 << "A" << "a" << "b" << UTF8_QSTRING("μ"); break; case nsl_sf_stats_poisson: paramNames << "a" << "l"; paramNamesUtf8 << "A" << UTF8_QSTRING("λ"); break; case nsl_sf_stats_binomial: case nsl_sf_stats_negative_binomial: case nsl_sf_stats_pascal: paramNames << "a" << "p" << "n"; paramNamesUtf8 << "A" << "p" << "n"; break; case nsl_sf_stats_geometric: case nsl_sf_stats_logarithmic: paramNames << "a" << "p"; paramNamesUtf8 << "A" << "p"; break; case nsl_sf_stats_hypergeometric: paramNames << "a" << "n1" << "n2" << "t"; paramNamesUtf8 << "A" << UTF8_QSTRING("n₁") << UTF8_QSTRING("n₂") << "t"; break; case nsl_sf_stats_maxwell_boltzmann: paramNames << "a" << "s"; paramNamesUtf8 << "A" << UTF8_QSTRING("σ"); break; case nsl_sf_stats_frechet: paramNames << "a" << "g" << "s" << "mu"; paramNamesUtf8 << "A" << UTF8_QSTRING("γ") << UTF8_QSTRING("σ") << UTF8_QSTRING("μ"); break; } break; case nsl_fit_model_custom: break; } + DEBUG("model: " << model.toStdString()); if (paramNamesUtf8.isEmpty()) paramNamesUtf8 << paramNames; //resize the vector for the start values and set the elements to 1.0 //in case a custom model is used, do nothing, we take over the previous values if (modelCategory != nsl_fit_model_custom) { const int np = paramNames.size(); paramStartValues.resize(np); paramFixed.resize(np); paramLowerLimits.resize(np); paramUpperLimits.resize(np); for (int i = 0; i < np; ++i) { paramStartValues[i] = 1.0; paramFixed[i] = false; paramLowerLimits[i] = -std::numeric_limits::max(); paramUpperLimits[i] = std::numeric_limits::max(); } // set some model-dependent start values // TODO: see initStartValues() if (modelCategory == nsl_fit_model_distribution) { if (modelType == (int)nsl_sf_stats_flat) paramStartValues[2] = -1.0; else if (modelType == (int)nsl_sf_stats_levy) paramStartValues[2] = 0.0; else if (modelType == (int)nsl_sf_stats_exponential_power || modelType == (int)nsl_sf_stats_weibull || modelType == (int)nsl_sf_stats_gumbel2 || modelType == (int)nsl_sf_stats_frechet) paramStartValues[3] = 0.0; else if (modelType == (int)nsl_sf_stats_binomial || modelType == (int)nsl_sf_stats_negative_binomial || modelType == (int)nsl_sf_stats_pascal || modelType == (int)nsl_sf_stats_geometric || modelType == (int)nsl_sf_stats_logarithmic) paramStartValues[1] = 0.5; } } } /*! Returns an icon to be used in the project explorer. */ QIcon XYFitCurve::icon() const { return QIcon::fromTheme("labplot-xy-fit-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYFitCurve, const AbstractColumn*, xErrorColumn, xErrorColumn) BASIC_SHARED_D_READER_IMPL(XYFitCurve, const AbstractColumn*, yErrorColumn, yErrorColumn) const QString& XYFitCurve::xErrorColumnPath() const { Q_D(const XYFitCurve); return d->xErrorColumnPath; } const QString& XYFitCurve::yErrorColumnPath() const { Q_D(const XYFitCurve); return d->yErrorColumnPath; } BASIC_SHARED_D_READER_IMPL(XYFitCurve, XYFitCurve::FitData, fitData, fitData) const XYFitCurve::FitResult& XYFitCurve::fitResult() const { Q_D(const XYFitCurve); return d->fitResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_S(XYFitCurve, SetXErrorColumn, const AbstractColumn*, xErrorColumn) void XYFitCurve::setXErrorColumn(const AbstractColumn* column) { Q_D(XYFitCurve); if (column != d->xErrorColumn) { - exec(new XYFitCurveSetXErrorColumnCmd(d, column, i18n("%1: assign x-error"))); + exec(new XYFitCurveSetXErrorColumnCmd(d, column, ki18n("%1: assign x-error"))); handleSourceDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); //TODO disconnect on undo } } } STD_SETTER_CMD_IMPL_S(XYFitCurve, SetYErrorColumn, const AbstractColumn*, yErrorColumn) void XYFitCurve::setYErrorColumn(const AbstractColumn* column) { Q_D(XYFitCurve); if (column != d->yErrorColumn) { - exec(new XYFitCurveSetYErrorColumnCmd(d, column, i18n("%1: assign y-error"))); + exec(new XYFitCurveSetYErrorColumnCmd(d, column, ki18n("%1: assign y-error"))); handleSourceDataChanged(); if (column) { connect(column, SIGNAL(dataChanged(const AbstractColumn*)), this, SLOT(handleSourceDataChanged())); //TODO disconnect on undo } } } // TODO: do not recalculate STD_SETTER_CMD_IMPL_F_S(XYFitCurve, SetFitData, XYFitCurve::FitData, fitData, recalculate); void XYFitCurve::setFitData(const XYFitCurve::FitData& fitData) { Q_D(XYFitCurve); - exec(new XYFitCurveSetFitDataCmd(d, fitData, i18n("%1: set fit options and perform the fit"))); + exec(new XYFitCurveSetFitDataCmd(d, fitData, ki18n("%1: set fit options and perform the fit"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYFitCurvePrivate::XYFitCurvePrivate(XYFitCurve* owner) : XYAnalysisCurvePrivate(owner), xErrorColumn(nullptr), yErrorColumn(nullptr), residualsColumn(nullptr), residualsVector(nullptr), q(owner) { } XYFitCurvePrivate::~XYFitCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } // data structure to pass parameter to fit functions struct data { size_t n; //number of data points double* x; //pointer to the vector with x-data values double* y; //pointer to the vector with y-data values double* weight; //pointer to the vector with weight values nsl_fit_model_category modelCategory; int modelType; int degree; QString* func; // string containing the definition of the model/function QStringList* paramNames; double* paramMin; // lower parameter limits double* paramMax; // upper parameter limits bool* paramFixed; // parameter fixed? }; /*! * \param paramValues vector containing current values of the fit parameters * \param params * \param f vector with the weighted residuals weight[i]*(Yi - y[i]) */ int func_f(const gsl_vector* paramValues, void* params, gsl_vector* f) { //DEBUG("func_f"); size_t n = ((struct data*)params)->n; double* x = ((struct data*)params)->x; double* y = ((struct data*)params)->y; double* weight = ((struct data*)params)->weight; nsl_fit_model_category modelCategory = ((struct data*)params)->modelCategory; unsigned int modelType = ((struct data*)params)->modelType; QByteArray funcba = ((struct data*)params)->func->toLatin1(); // a local byte array is needed! const char *func = funcba.constData(); // function to evaluate QStringList* paramNames = ((struct data*)params)->paramNames; double *min = ((struct data*)params)->paramMin; double *max = ((struct data*)params)->paramMax; // set current values of the parameters for (int i = 0; i < paramNames->size(); i++) { double v = gsl_vector_get(paramValues, (size_t)i); // bound values if limits are set QByteArray paramnameba = paramNames->at(i).toLatin1(); assign_variable(paramnameba.constData(), nsl_fit_map_bound(v, min[i], max[i])); QDEBUG("Parameter"<n; double* xVector = ((struct data*)params)->x; double* weight = ((struct data*)params)->weight; nsl_fit_model_category modelCategory = ((struct data*)params)->modelCategory; unsigned int modelType = ((struct data*)params)->modelType; unsigned int degree = ((struct data*)params)->degree; QStringList* paramNames = ((struct data*)params)->paramNames; double *min = ((struct data*)params)->paramMin; double *max = ((struct data*)params)->paramMax; bool *fixed = ((struct data*)params)->paramFixed; // calculate the Jacobian matrix: // Jacobian matrix J(i,j) = df_i / dx_j // where f_i = w_i*(Y_i - y_i), // Y_i = model and the x_j are the parameters double x; switch (modelCategory) { case nsl_fit_model_basic: switch (modelType) { case nsl_fit_model_polynomial: // Y(x) = c0 + c1*x + ... + cn*x^n for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < (unsigned int)paramNames->size(); ++j) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_polynomial_param_deriv(x, j, weight[i])); } } break; case nsl_fit_model_power: // Y(x) = a*x^b or Y(x) = a + b*x^c. if (degree == 1) { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_power1_param_deriv(j, x, a, b, weight[i])); } } } else if (degree == 2) { const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double c = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_power2_param_deriv(j, x, b, c, weight[i])); } } } break; case nsl_fit_model_exponential: { // Y(x) = a*exp(b*x) + c*exp(d*x) + ... double *p = new double[2*degree]; for (unsigned int i = 0; i < 2*degree; i++) p[i] = nsl_fit_map_bound(gsl_vector_get(paramValues, i), min[i], max[i]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2*degree; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_exponentialn_param_deriv(j, x, p, weight[i])); } } delete[] p; break; } case nsl_fit_model_inverse_exponential: { // Y(x) = a*(1-exp(b*x))+c const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_inverse_exponential_param_deriv(j, x, a, b, weight[i])); } } break; } case nsl_fit_model_fourier: { // Y(x) = a0 + (a1*cos(w*x) + b1*sin(w*x)) + ... + (an*cos(n*w*x) + bn*sin(n*w*x) //parameters: w, a0, a1, b1, ... an, bn double* a = new double[degree]; double* b = new double[degree]; double w = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); a[0] = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); b[0] = 0; for (unsigned int i = 1; i < degree; ++i) { a[i] = nsl_fit_map_bound(gsl_vector_get(paramValues, 2*i), min[2*i], max[2*i]); b[i] = nsl_fit_map_bound(gsl_vector_get(paramValues, 2*i+1), min[2*i+1], max[2*i+1]); } for (size_t i = 0; i < n; i++) { x = xVector[i]; double wd = 0; //first derivative with respect to the w parameter for (unsigned int j = 1; j < degree; ++j) { wd += -a[j]*j*x*sin(j*w*x) + b[j]*j*x*cos(j*w*x); } gsl_matrix_set(J, i, 0, weight[i]*wd); gsl_matrix_set(J, i, 1, weight[i]); for (unsigned int j = 1; j <= degree; ++j) { gsl_matrix_set(J, (size_t)i, (size_t)(2*j), nsl_fit_model_fourier_param_deriv(0, j, x, w, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(2*j+1), nsl_fit_model_fourier_param_deriv(1, j, x, w, weight[i])); } for (unsigned int j = 0; j <= 2*degree+1; j++) if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); } delete[] a; delete[] b; break; } } break; case nsl_fit_model_peak: switch (modelType) { case nsl_fit_model_gaussian: case nsl_fit_model_lorentz: case nsl_fit_model_sech: case nsl_fit_model_logistic: for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < degree; ++j) { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 3*j), min[3*j], max[3*j]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 3*j+1), min[3*j+1], max[3*j+1]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3*j+2), min[3*j+2], max[3*j+2]); switch (modelType) { case nsl_fit_model_gaussian: gsl_matrix_set(J, (size_t)i, (size_t)(3*j), nsl_fit_model_gaussian_param_deriv(0, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+1), nsl_fit_model_gaussian_param_deriv(1, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+2), nsl_fit_model_gaussian_param_deriv(2, x, a, s, mu, weight[i])); break; case nsl_fit_model_lorentz: // a,s,t gsl_matrix_set(J, (size_t)i, (size_t)(3*j), nsl_fit_model_lorentz_param_deriv(0, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+1), nsl_fit_model_lorentz_param_deriv(1, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+2), nsl_fit_model_lorentz_param_deriv(2, x, a, s, mu, weight[i])); break; case nsl_fit_model_sech: gsl_matrix_set(J, (size_t)i, (size_t)(3*j), nsl_fit_model_sech_param_deriv(0, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+1), nsl_fit_model_sech_param_deriv(1, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+2), nsl_fit_model_sech_param_deriv(2, x, a, s, mu, weight[i])); break; case nsl_fit_model_logistic: gsl_matrix_set(J, (size_t)i, (size_t)(3*j), nsl_fit_model_logistic_param_deriv(0, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+1), nsl_fit_model_logistic_param_deriv(1, x, a, s, mu, weight[i])); gsl_matrix_set(J, (size_t)i, (size_t)(3*j+2), nsl_fit_model_logistic_param_deriv(2, x, a, s, mu, weight[i])); break; } } for (unsigned int j = 0; j < 3*degree; j++) if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); } break; case nsl_fit_model_voigt: for (size_t i = 0; i < n; i++) { x = xVector[i]; - //TODO: degree + for (unsigned int j = 0; j < degree; ++j) { + const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j), min[4*j], max[4*j]); + const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+1), min[4*j+1], max[4*j+1]); + const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+2), min[4*j+2], max[4*j+2]); + const double g = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+3), min[4*j+3], max[4*j+3]); + + gsl_matrix_set(J, (size_t)i, (size_t)(4*j), nsl_fit_model_voigt_param_deriv(0, x, a, mu, s, g, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+1), nsl_fit_model_voigt_param_deriv(1, x, a, mu, s, g, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+2), nsl_fit_model_voigt_param_deriv(2, x, a, mu, s, g, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+3), nsl_fit_model_voigt_param_deriv(3, x, a, mu, s, g, weight[i])); + } + for (unsigned int j = 0; j < 4*degree; j++) + if (fixed[j]) + gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); + } + break; + case nsl_fit_model_pseudovoigt1: + for (size_t i = 0; i < n; i++) { + x = xVector[i]; - const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); - const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); - const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); - const double g = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); - for (unsigned int j = 0; j < 4; j++) + for (unsigned int j = 0; j < degree; ++j) { + const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j), min[4*j], max[4*j]); + const double eta = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+1), min[4*j+1], max[4*j+1]); + const double w = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+2), min[4*j+2], max[4*j+2]); + const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 4*j+3), min[4*j+3], max[4*j+3]); + + gsl_matrix_set(J, (size_t)i, (size_t)(4*j), nsl_fit_model_voigt_param_deriv(0, x, a, eta, w, mu, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+1), nsl_fit_model_voigt_param_deriv(1, x, a, eta, w, mu, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+2), nsl_fit_model_voigt_param_deriv(2, x, a, eta, w, mu, weight[i])); + gsl_matrix_set(J, (size_t)i, (size_t)(4*j+3), nsl_fit_model_voigt_param_deriv(3, x, a, eta, w, mu, weight[i])); + } + for (unsigned int j = 0; j < 4*degree; j++) if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); - else - gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_voigt_param_deriv(j, x, a, mu, s, g, weight[i])); } + break; } break; case nsl_fit_model_growth: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) { gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); } else { switch (modelType) { case nsl_fit_model_atan: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_atan_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_tanh: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_tanh_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_algebraic_sigmoid: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_algebraic_sigmoid_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_sigmoid: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_sigmoid_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_erf: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_erf_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_hill: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_hill_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_gompertz: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gompertz_param_deriv(j, x, a, mu, s, weight[i])); break; case nsl_fit_model_gudermann: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gudermann_param_deriv(j, x, a, mu, s, weight[i])); break; } } } } } break; case nsl_fit_model_distribution: switch (modelType) { case nsl_sf_stats_gaussian: case nsl_sf_stats_exponential: case nsl_sf_stats_laplace: case nsl_sf_stats_cauchy_lorentz: case nsl_sf_stats_rayleigh_tail: case nsl_sf_stats_lognormal: case nsl_sf_stats_logistic: case nsl_sf_stats_sech: case nsl_sf_stats_levy: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else { switch (modelType) { case nsl_sf_stats_gaussian: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gaussian_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_exponential: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_exponential_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_laplace: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_laplace_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_cauchy_lorentz: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_lorentz_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_rayleigh_tail: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_rayleigh_tail_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_lognormal: if (x > 0) gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_lognormal_param_deriv(j, x, a, s, mu, weight[i])); else gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); break; case nsl_sf_stats_logistic: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_logistic_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_sech: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_sech_dist_param_deriv(j, x, a, s, mu, weight[i])); break; case nsl_sf_stats_levy: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_levy_param_deriv(j, x, a, s, mu, weight[i])); break; } } } } break; } case nsl_sf_stats_gaussian_tail: { const double A = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gaussian_tail_param_deriv(j, x, A, s, a, mu, weight[i])); } } break; } case nsl_sf_stats_exponential_power: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_exp_pow_param_deriv(j, x, a, s, b, mu, weight[i])); } } break; } case nsl_sf_stats_rayleigh: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_rayleigh_param_deriv(j, x, a, s, weight[i])); } } break; } case nsl_sf_stats_gamma: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double k = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double t = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gamma_param_deriv(j, x, a, k, t, weight[i])); } } break; } case nsl_sf_stats_flat: { const double A = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_flat_param_deriv(j, x, A, b, a, weight[i])); } } break; } case nsl_sf_stats_chi_squared: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double nu = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_chi_square_param_deriv(j, x, a, nu, weight[i])); } } break; } case nsl_sf_stats_tdist: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double nu = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_students_t_param_deriv(j, x, a, nu, weight[i])); } } break; } case nsl_sf_stats_fdist: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double n1 = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double n2 = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_fdist_param_deriv(j, x, a, n1, n2, weight[i])); } } break; } case nsl_sf_stats_beta: case nsl_sf_stats_pareto: { const double A = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else { switch (modelType) { case nsl_sf_stats_beta: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_beta_param_deriv(j, x, A, a, b, weight[i])); break; case nsl_sf_stats_pareto: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_pareto_param_deriv(j, x, A, a, b, weight[i])); break; } } } } break; } case nsl_sf_stats_weibull: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double k = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double l = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else { if (x > 0) gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_weibull_param_deriv(j, x, a, k, l, mu, weight[i])); else gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); } } } break; } case nsl_sf_stats_gumbel1: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gumbel1_param_deriv(j, x, a, s, mu, b, weight[i])); } } break; } case nsl_sf_stats_gumbel2: { const double A = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double b = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_gumbel2_param_deriv(j, x, A, a, b, mu, weight[i])); } } break; } case nsl_sf_stats_poisson: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double l = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_poisson_param_deriv(j, x, a, l, weight[i])); } } break; } case nsl_sf_stats_maxwell_boltzmann: { // Y(x) = a*sqrt(2/pi) * x^2/s^3 * exp(-(x/s)^2/2) const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_maxwell_param_deriv(j, x, a, s, weight[i])); } } break; } case nsl_sf_stats_frechet: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double g = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double s = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double mu = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_frechet_param_deriv(j, x, a, g, s, mu, weight[i])); } } break; } case nsl_sf_stats_landau: { // const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); for (size_t i = 0; i < n; i++) { x = xVector[i]; if (fixed[0]) gsl_matrix_set(J, (size_t)i, 0, 0.); else gsl_matrix_set(J, (size_t)i, 0, nsl_fit_model_landau_param_deriv(0, x, weight[i])); } break; } case nsl_sf_stats_binomial: case nsl_sf_stats_negative_binomial: case nsl_sf_stats_pascal: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double p = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double N = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 3; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else { switch (modelType) { case nsl_sf_stats_binomial: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_binomial_param_deriv(j, x, a, p, N, weight[i])); break; case nsl_sf_stats_negative_binomial: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_negative_binomial_param_deriv(j, x, a, p, N, weight[i])); break; case nsl_sf_stats_pascal: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_pascal_param_deriv(j, x, a, p, N, weight[i])); break; } } } } break; } case nsl_sf_stats_geometric: case nsl_sf_stats_logarithmic: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double p = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 2; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else { switch (modelType) { case nsl_sf_stats_geometric: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_geometric_param_deriv(j, x, a, p, weight[i])); break; case nsl_sf_stats_logarithmic: gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_logarithmic_param_deriv(j, x, a, p, weight[i])); break; } } } } break; } case nsl_sf_stats_hypergeometric: { const double a = nsl_fit_map_bound(gsl_vector_get(paramValues, 0), min[0], max[0]); const double n1 = nsl_fit_map_bound(gsl_vector_get(paramValues, 1), min[1], max[1]); const double n2 = nsl_fit_map_bound(gsl_vector_get(paramValues, 2), min[2], max[2]); const double t = nsl_fit_map_bound(gsl_vector_get(paramValues, 3), min[3], max[3]); for (size_t i = 0; i < n; i++) { x = xVector[i]; for (unsigned int j = 0; j < 4; j++) { if (fixed[j]) gsl_matrix_set(J, (size_t)i, (size_t)j, 0.); else gsl_matrix_set(J, (size_t)i, (size_t)j, nsl_fit_model_hypergeometric_param_deriv(j, x, a, n1, n2, t, weight[i])); } } break; } // unused distributions case nsl_sf_stats_levy_alpha_stable: case nsl_sf_stats_levy_skew_alpha_stable: case nsl_sf_stats_bernoulli: break; } break; case nsl_fit_model_custom: QByteArray funcba = ((struct data*)params)->func->toLatin1(); const char* func = funcba.data(); QByteArray nameba; double value; const unsigned int np = paramNames->size(); for (size_t i = 0; i < n; i++) { x = xVector[i]; assign_variable("x", x); for (unsigned int j = 0; j < np; j++) { for (unsigned int k = 0; k < np; k++) { if (k != j) { nameba = paramNames->at(k).toLatin1(); value = nsl_fit_map_bound(gsl_vector_get(paramValues, k), min[k], max[k]); assign_variable(nameba.data(), value); } } nameba = paramNames->at(j).toLatin1(); const char *name = nameba.data(); value = nsl_fit_map_bound(gsl_vector_get(paramValues, j), min[j], max[j]); assign_variable(name, value); const double f_p = parse(func); double eps = 1.e-9; if (std::abs(f_p) > 0) eps *= std::abs(f_p); // scale step size with function value value += eps; assign_variable(name, value); const double f_pdp = parse(func); // DEBUG("evaluate deriv"<* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); residualsVector = static_cast* >(residualsColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->addChild(residualsColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); residualsVector->clear(); } } void XYFitCurvePrivate::recalculate() { DEBUG("XYFitCurvePrivate::recalculate()"); QElapsedTimer timer; timer.start(); // prepare source data columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { DEBUG(" spreadsheet columns as data source"); tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { DEBUG(" curve columns as data source"); tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { DEBUG("ERROR: Preparing source data columns failed!"); return; } prepareResultColumns(); // clear the previous result fitResult = XYFitCurve::FitResult(); //fit settings const unsigned int maxIters = fitData.maxIterations; //maximal number of iterations const double delta = fitData.eps; //fit tolerance const unsigned int np = fitData.paramNames.size(); //number of fit parameters if (np == 0) { fitResult.available = true; fitResult.valid = false; fitResult.status = i18n("Model has no parameters."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //determine the data source columns if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { fitResult.available = true; fitResult.valid = false; fitResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } if (yErrorColumn) { if (yErrorColumn->rowCount() < tmpXDataColumn->rowCount()) { fitResult.available = true; fitResult.valid = false; fitResult.status = i18n("Not sufficient weight data points provided."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } } //copy all valid data point for the fit to temporary vectors QVector xdataVector; QVector ydataVector; QVector xerrorVector; QVector yerrorVector; double xmin, xmax; if (fitData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = fitData.fitRange.first(); xmax = fitData.fitRange.last(); } DEBUG("fit range = " << xmin << " .. " << xmax); for (int row = 0; row < tmpXDataColumn->rowCount(); ++row) { //only copy those data where _all_ values (for x and y and errors, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { if (dataSourceType == XYAnalysisCurve::DataSourceCurve || (!xErrorColumn && !yErrorColumn) || !fitData.useDataErrors) { // x-y xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } else if (!xErrorColumn) { // x-y-dy if (!std::isnan(yErrorColumn->valueAt(row))) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); yerrorVector.append(yErrorColumn->valueAt(row)); } } else { // x-y-dx-dy if (!std::isnan(xErrorColumn->valueAt(row)) && !std::isnan(yErrorColumn->valueAt(row))) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); xerrorVector.append(xErrorColumn->valueAt(row)); yerrorVector.append(yErrorColumn->valueAt(row)); } } } } } //number of data points to fit const size_t n = xdataVector.size(); DEBUG("number of data points: " << n); if (n == 0) { fitResult.available = true; fitResult.valid = false; fitResult.status = i18n("No data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } if (n < np) { fitResult.available = true; fitResult.valid = false; fitResult.status = i18n("The number of data points (%1) must be greater than or equal to the number of parameters (%2).", n, np); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); double* xerror = xerrorVector.data(); // size may be 0 double* yerror = yerrorVector.data(); // size may be 0 DEBUG("x errors: " << xerrorVector.size()); DEBUG("y errors: " << yerrorVector.size()); double* weight = new double[n]; for (size_t i = 0; i < n; i++) weight[i] = 1.; switch (fitData.yWeightsType) { case nsl_fit_weight_no: case nsl_fit_weight_statistical_fit: case nsl_fit_weight_relative_fit: break; case nsl_fit_weight_instrumental: // yerror are sigmas for(int i = 0; i < (int)n; i++) if (i < yerrorVector.size()) weight[i] = 1./gsl_pow_2(yerror[i]); break; case nsl_fit_weight_direct: // yerror are weights for(int i = 0; i < (int)n; i++) if (i < yerrorVector.size()) weight[i] = yerror[i]; break; case nsl_fit_weight_inverse: // yerror are inverse weights for(int i = 0; i < (int)n; i++) if (i < yerrorVector.size()) weight[i] = 1./yerror[i]; break; case nsl_fit_weight_statistical: for (int i = 0; i < (int)n; i++) weight[i] = 1./ydata[i]; break; case nsl_fit_weight_relative: for (int i = 0; i < (int)n; i++) weight[i] = 1./gsl_pow_2(ydata[i]); break; } /////////////////////// GSL >= 2 has a complete new interface! But the old one is still supported. /////////////////////////// // GSL >= 2 : "the 'fdf' field of gsl_multifit_function_fdf is now deprecated and does not need to be specified for nonlinear least squares problems" unsigned int nf = 0; // number of fixed parameter for (unsigned int i = 0; i < np; i++) { const bool fixed = fitData.paramFixed.data()[i]; if (fixed) nf++; DEBUG("parameter " << i << " fixed: " << fixed); } //function to fit gsl_multifit_function_fdf f; DEBUG("model = " << fitData.model.toStdString()); struct data params = {n, xdata, ydata, weight, fitData.modelCategory, fitData.modelType, fitData.degree, &fitData.model, &fitData.paramNames, fitData.paramLowerLimits.data(), fitData.paramUpperLimits.data(), fitData.paramFixed.data()}; f.f = &func_f; f.df = &func_df; f.fdf = &func_fdf; f.n = n; f.p = np; f.params = ¶ms; DEBUG("initialize the derivative solver (using Levenberg-Marquardt robust solver)"); const gsl_multifit_fdfsolver_type* T = gsl_multifit_fdfsolver_lmsder; gsl_multifit_fdfsolver* s = gsl_multifit_fdfsolver_alloc(T, n, np); DEBUG("set start values"); double* x_init = fitData.paramStartValues.data(); double* x_min = fitData.paramLowerLimits.data(); double* x_max = fitData.paramUpperLimits.data(); DEBUG("scale start values if limits are set"); for (unsigned int i = 0; i < np; i++) x_init[i] = nsl_fit_map_unbound(x_init[i], x_min[i], x_max[i]); DEBUG(" DONE"); gsl_vector_view x = gsl_vector_view_array(x_init, np); DEBUG("Turning off GSL error handler to avoid overflow/underflow"); gsl_set_error_handler_off(); DEBUG("Initialize solver with function f and initial guess x"); gsl_multifit_fdfsolver_set(s, &f, &x.vector); DEBUG("Iterate ..."); int status; unsigned int iter = 0; fitResult.solverOutput.clear(); writeSolverState(s); do { iter++; // update weights for Y-depending weights (using function values from residuals) if (fitData.yWeightsType == nsl_fit_weight_statistical_fit) { for (size_t i = 0; i < n; i++) weight[i] = 1./(gsl_vector_get(s->f, i)/sqrt(weight[i]) + ydata[i]); // 1/Y_i } else if (fitData.yWeightsType == nsl_fit_weight_relative_fit) { for (size_t i = 0; i < n; i++) weight[i] = 1./gsl_pow_2(gsl_vector_get(s->f, i)/sqrt(weight[i]) + ydata[i]); // 1/Y_i^2 } status = gsl_multifit_fdfsolver_iterate(s); writeSolverState(s); if (status) { DEBUG("iter " << iter << ", status = " << gsl_strerror(status)); break; } status = gsl_multifit_test_delta(s->dx, s->x, delta, delta); } while (status == GSL_CONTINUE && iter < maxIters); // second run for x-error fitting if (xerrorVector.size() > 0) { DEBUG("Rerun fit with x errors"); unsigned int iter2 = 0; double chi = 0, chiOld = 0; double *fun = new double[n]; do { iter2++; chiOld = chi; //printf("iter2 = %d\n", iter2); // calculate function from residuals for (size_t i = 0; i < n; i++) fun[i] = gsl_vector_get(s->f, i) * 1./sqrt(weight[i]) + ydata[i]; // calculate weight[i] for (size_t i = 0; i < n; i++) { // calculate df[i] size_t index = i-1; if (i == 0) index = i; if (i == n-1) index = i-2; double df = (fun[index+1] - fun[index])/(xdata[index+1] - xdata[index]); //printf("df = %g\n", df); double sigmasq = 1.; switch (fitData.xWeightsType) { // x-error type: f'(x)^2*s_x^2 = f'(x)/w_x case nsl_fit_weight_no: break; case nsl_fit_weight_direct: // xerror = w_x sigmasq = df*df/xerror[i]; break; case nsl_fit_weight_instrumental: // xerror = s_x sigmasq = df*df*xerror[i]*xerror[i]; break; case nsl_fit_weight_inverse: // xerror = 1/w_x = s_x^2 sigmasq = df*df*xerror[i]; break; case nsl_fit_weight_statistical: // s_x^2 = 1/w_x = x sigmasq = xdata[i]; break; case nsl_fit_weight_relative: // s_x^2 = 1/w_x = x^2 sigmasq = xdata[i]*xdata[i]; break; case nsl_fit_weight_statistical_fit: // unused case nsl_fit_weight_relative_fit: break; } if (yerrorVector.size() > 0) { switch (fitData.yWeightsType) { // y-error types: s_y^2 = 1/w_y case nsl_fit_weight_no: break; case nsl_fit_weight_direct: // yerror = w_y sigmasq += 1./yerror[i]; break; case nsl_fit_weight_instrumental: // yerror = s_y sigmasq += yerror[i]*yerror[i]; break; case nsl_fit_weight_inverse: // yerror = 1/w_y sigmasq += yerror[i]; break; case nsl_fit_weight_statistical: // unused case nsl_fit_weight_relative: break; case nsl_fit_weight_statistical_fit: // s_y^2 = 1/w_y = Y_i sigmasq += fun[i]; break; case nsl_fit_weight_relative_fit: // s_y^2 = 1/w_y = Y_i^2 sigmasq += fun[i]*fun[i]; break; } } //printf ("sigma[%d] = %g\n", i, sqrt(sigmasq)); weight[i] = 1./sigmasq; } // update weights gsl_multifit_fdfsolver_set(s, &f, &x.vector); do { // fit iter++; writeSolverState(s); status = gsl_multifit_fdfsolver_iterate (s); //printf ("status = %s\n", gsl_strerror (status)); if (status) { DEBUG("iter " << iter << ", status = " << gsl_strerror(status)); break; } status = gsl_multifit_test_delta(s->dx, s->x, delta, delta); } while (status == GSL_CONTINUE && iter < maxIters); chi = gsl_blas_dnrm2(s->f); //printf("chi = %.12g (dchi = %g)\n", chi, fabs(chi-chiOld)); } while (iter2 < maxIters && fabs(chi-chiOld) > fitData.eps); delete[] fun; } delete[] weight; // unscale start parameter for (unsigned int i = 0; i < np; i++) x_init[i] = nsl_fit_map_bound(x_init[i], x_min[i], x_max[i]); //get the covariance matrix //TODO: scale the Jacobian when limits are used before constructing the covar matrix? gsl_matrix* covar = gsl_matrix_alloc(np, np); #if GSL_MAJOR_VERSION >= 2 // the Jacobian is not part of the solver anymore gsl_matrix *J = gsl_matrix_alloc(s->fdf->n, s->fdf->p); gsl_multifit_fdfsolver_jac(s, J); gsl_multifit_covar(J, 0.0, covar); #else gsl_multifit_covar(s->J, 0.0, covar); #endif //write the result fitResult.available = true; fitResult.valid = true; fitResult.status = gslErrorToString(status); fitResult.iterations = iter; fitResult.dof = n - (np - nf); // samples - (parameter - fixed parameter) //gsl_blas_dnrm2() - computes the Euclidian norm (||r||_2 = \sqrt {\sum r_i^2}) of the vector with the elements weight[i]*(Yi - y[i]) //gsl_blas_dasum() - computes the absolute sum \sum |r_i| of the elements of the vector with the elements weight[i]*(Yi - y[i]) fitResult.sse = gsl_pow_2(gsl_blas_dnrm2(s->f)); if (fitResult.dof != 0) { fitResult.rms = fitResult.sse/fitResult.dof; fitResult.rsd = sqrt(fitResult.rms); } fitResult.mse = fitResult.sse/n; fitResult.rmse = sqrt(fitResult.mse); fitResult.mae = gsl_blas_dasum(s->f)/n; // SST needed for coefficient of determination, R-squared fitResult.sst = gsl_stats_tss(ydata, 1, n); // for a linear model without intercept R-squared is calculated differently // see https://cran.r-project.org/doc/FAQ/R-FAQ.html#Why-does-summary_0028_0029-report-strange-results-for-the-R_005e2-estimate-when-I-fit-a-linear-model-with-no-intercept_003f if (fitData.modelCategory == nsl_fit_model_basic && fitData.modelType == nsl_fit_model_polynomial && fitData.degree == 1 && x_init[0] == 0) { DEBUG("Using alternative R^2 for linear model without intercept"); fitResult.sst = gsl_stats_tss_m(ydata, 1, n, 0); } if (fitResult.sst < fitResult.sse) { DEBUG("Using alternative R^2 since R^2 would be negative (probably custom model without intercept)"); fitResult.sst = gsl_stats_tss_m(ydata, 1, n, 0); } fitResult.rsquare = nsl_stats_rsquare(fitResult.sse, fitResult.sst); fitResult.rsquareAdj = nsl_stats_rsquareAdj(fitResult.rsquare, np, fitResult.dof, 1); fitResult.chisq_p = nsl_stats_chisq_p(fitResult.sse, fitResult.dof); fitResult.fdist_F = nsl_stats_fdist_F(fitResult.sst, fitResult.rms, np, 1); fitResult.fdist_p = nsl_stats_fdist_p(fitResult.fdist_F, np, fitResult.dof); fitResult.logLik = nsl_stats_logLik(fitResult.sse, n); fitResult.aic = nsl_stats_aic(fitResult.sse, n, np, 1); fitResult.bic = nsl_stats_bic(fitResult.sse, n, np, 1); //parameter values // GSL: const double c = GSL_MAX_DBL(1., sqrt(fitResult.rms)); // increase error for poor fit // NIST: const double c = sqrt(fitResult.rms); // increase error for poor fit, decrease for good fit const double c = sqrt(fitResult.rms); fitResult.paramValues.resize(np); fitResult.errorValues.resize(np); fitResult.tdist_tValues.resize(np); fitResult.tdist_pValues.resize(np); fitResult.tdist_marginValues.resize(np); for (unsigned int i = 0; i < np; i++) { // scale resulting values if they are bounded fitResult.paramValues[i] = nsl_fit_map_bound(gsl_vector_get(s->x, i), x_min[i], x_max[i]); // use results as start values if desired if (fitData.useResults) { fitData.paramStartValues.data()[i] = fitResult.paramValues[i]; DEBUG("saving parameter " << i << ": " << fitResult.paramValues[i] << ' ' << fitData.paramStartValues.data()[i]); } fitResult.errorValues[i] = c*sqrt(gsl_matrix_get(covar, i, i)); fitResult.tdist_tValues[i] = nsl_stats_tdist_t(fitResult.paramValues.at(i), fitResult.errorValues.at(i)); fitResult.tdist_pValues[i] = nsl_stats_tdist_p(fitResult.tdist_tValues.at(i), fitResult.dof); fitResult.tdist_marginValues[i] = nsl_stats_tdist_margin(0.05, fitResult.dof, fitResult.errorValues.at(i)); } // fill residuals vector. To get residuals on the correct x values, fill the rest with zeros. residualsVector->resize(tmpXDataColumn->rowCount()); if (fitData.autoRange) { // evaluate full range of residuals xVector->resize(tmpXDataColumn->rowCount()); for (int i = 0; i < tmpXDataColumn->rowCount(); i++) (*xVector)[i] = tmpXDataColumn->valueAt(i); ExpressionParser* parser = ExpressionParser::getInstance(); bool rc = parser->evaluateCartesian(fitData.model, xVector, residualsVector, fitData.paramNames, fitResult.paramValues); for (int i = 0; i < tmpXDataColumn->rowCount(); i++) (*residualsVector)[i] = tmpYDataColumn->valueAt(i) - (*residualsVector)[i]; if (!rc) residualsVector->clear(); } else { // only selected range size_t j = 0; for (int i = 0; i < tmpXDataColumn->rowCount(); i++) { if (tmpXDataColumn->valueAt(i) >= xmin && tmpXDataColumn->valueAt(i) <= xmax) residualsVector->data()[i] = - gsl_vector_get(s->f, j++); else // outside range residualsVector->data()[i] = 0; } } residualsColumn->setChanged(); //free resources gsl_multifit_fdfsolver_free(s); gsl_matrix_free(covar); //calculate the fit function (vectors) evaluate(); fitResult.elapsedTime = timer.elapsed(); //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } /* evaluate fit function */ void XYFitCurvePrivate::evaluate(bool preview) { DEBUG("XYFitCurvePrivate::evaluate() preview = " << preview); // prepare source data columns const AbstractColumn* tmpXDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { DEBUG(" spreadsheet columns as data source"); tmpXDataColumn = xDataColumn; } else { DEBUG(" curve columns as data source"); tmpXDataColumn = dataSourceCurve->xColumn(); } if (!tmpXDataColumn) { DEBUG("ERROR: Preparing source data column failed!"); return; } prepareResultColumns(); if (!xVector || !yVector) { DEBUG(" xVector or yVector not defined!"); return; } ExpressionParser* parser = ExpressionParser::getInstance(); double xmin, xmax; if (fitData.autoEvalRange) { // evaluate fit on full data range xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { // use given range for evaluation xmin = fitData.evalRange.first(); xmax = fitData.evalRange.last(); } DEBUG(" eval range = " << xmin << " .. " << xmax); xVector->resize((int)fitData.evaluatedPoints); yVector->resize((int)fitData.evaluatedPoints); DEBUG(" vector size = " << xVector->size()); QVector paramValues = fitResult.paramValues; if (preview) // results not available yet paramValues = fitData.paramStartValues; // Debug /* if (paramValues.size() == 0) DEBUG(" ERROR: No parameter defined!"); for (auto value: paramValues) DEBUG(" param value = " << value); */ bool rc = parser->evaluateCartesian(fitData.model, QString::number(xmin), QString::number(xmax), (int)fitData.evaluatedPoints, xVector, yVector, fitData.paramNames, paramValues); if (!rc) { xVector->clear(); yVector->clear(); residualsVector->clear(); } // Debug /* DEBUG(" x | y"); for (int i = 0; i < qMin(10, xVector->size()); i++) DEBUG(" " << (*xVector)[i] << " | " << (*yVector)[i]); */ // TODO: do we weed to do something to make preview work? // this should be already done by dataChanged() q->retransform(); // PREVIEW redraw the curve (this breaks context menu fit!) if (preview) { // emit q->dataChanged(); // sourceDataChangedSinceLastRecalc = false; } } /*! * writes out the current state of the solver \c s */ void XYFitCurvePrivate::writeSolverState(gsl_multifit_fdfsolver* s) { QString state; //current parameter values, semicolon separated double* min = fitData.paramLowerLimits.data(); double* max = fitData.paramUpperLimits.data(); for (int i = 0; i < fitData.paramNames.size(); ++i) { const double x = gsl_vector_get(s->x, i); // map parameter if bounded state += QString::number(nsl_fit_map_bound(x, min[i], max[i])) + '\t'; } //current value of the chi2-function state += QString::number(gsl_pow_2(gsl_blas_dnrm2(s->f))); state += ';'; fitResult.solverOutput += state; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYFitCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYFitCurve); writer->writeStartElement("xyFitCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-fit-curve specific information //fit data - only save model expression and parameter names for custom model, otherwise they are set in XYFitCurve::initFitData() writer->writeStartElement("fitData"); WRITE_COLUMN(d->xErrorColumn, xErrorColumn); WRITE_COLUMN(d->yErrorColumn, yErrorColumn); writer->writeAttribute("autoRange", QString::number(d->fitData.autoRange)); writer->writeAttribute("fitRangeMin", QString::number(d->fitData.fitRange.first(), 'g', 15)); writer->writeAttribute("fitRangeMax", QString::number(d->fitData.fitRange.last(), 'g', 15)); writer->writeAttribute("modelCategory", QString::number(d->fitData.modelCategory)); writer->writeAttribute("modelType", QString::number(d->fitData.modelType)); writer->writeAttribute("xWeightsType", QString::number(d->fitData.xWeightsType)); writer->writeAttribute("weightsType", QString::number(d->fitData.yWeightsType)); writer->writeAttribute("degree", QString::number(d->fitData.degree)); if (d->fitData.modelCategory == nsl_fit_model_custom) writer->writeAttribute("model", d->fitData.model); writer->writeAttribute("maxIterations", QString::number(d->fitData.maxIterations)); writer->writeAttribute("eps", QString::number(d->fitData.eps, 'g', 15)); writer->writeAttribute("evaluatedPoints", QString::number(d->fitData.evaluatedPoints)); writer->writeAttribute("autoEvalRange", QString::number(d->fitData.autoEvalRange)); writer->writeAttribute("useDataErrors", QString::number(d->fitData.useDataErrors)); writer->writeAttribute("useResults", QString::number(d->fitData.useResults)); if (d->fitData.modelCategory == nsl_fit_model_custom) { writer->writeStartElement("paramNames"); foreach (const QString &name, d->fitData.paramNames) writer->writeTextElement("name", name); writer->writeEndElement(); } writer->writeStartElement("paramStartValues"); foreach (const double &value, d->fitData.paramStartValues) writer->writeTextElement("startValue", QString::number(value, 'g', 15)); writer->writeEndElement(); // use 16 digits to handle -DBL_MAX writer->writeStartElement("paramLowerLimits"); foreach (const double &limit, d->fitData.paramLowerLimits) writer->writeTextElement("lowerLimit", QString::number(limit, 'g', 16)); writer->writeEndElement(); // use 16 digits to handle DBL_MAX writer->writeStartElement("paramUpperLimits"); foreach (const double &limit, d->fitData.paramUpperLimits) writer->writeTextElement("upperLimit", QString::number(limit, 'g', 16)); writer->writeEndElement(); writer->writeStartElement("paramFixed"); foreach (const double &fixed, d->fitData.paramFixed) writer->writeTextElement("fixed", QString::number(fixed)); writer->writeEndElement(); writer->writeEndElement(); //"fitData" //fit results (generated columns and goodness of the fit) writer->writeStartElement("fitResult"); writer->writeAttribute("available", QString::number(d->fitResult.available)); writer->writeAttribute("valid", QString::number(d->fitResult.valid)); writer->writeAttribute("status", d->fitResult.status); writer->writeAttribute("iterations", QString::number(d->fitResult.iterations)); writer->writeAttribute("time", QString::number(d->fitResult.elapsedTime)); writer->writeAttribute("dof", QString::number(d->fitResult.dof)); writer->writeAttribute("sse", QString::number(d->fitResult.sse, 'g', 15)); writer->writeAttribute("sst", QString::number(d->fitResult.sst, 'g', 15)); writer->writeAttribute("rms", QString::number(d->fitResult.rms, 'g', 15)); writer->writeAttribute("rsd", QString::number(d->fitResult.rsd, 'g', 15)); writer->writeAttribute("mse", QString::number(d->fitResult.mse, 'g', 15)); writer->writeAttribute("rmse", QString::number(d->fitResult.rmse, 'g', 15)); writer->writeAttribute("mae", QString::number(d->fitResult.mae, 'g', 15)); writer->writeAttribute("rsquare", QString::number(d->fitResult.rsquare, 'g', 15)); writer->writeAttribute("rsquareAdj", QString::number(d->fitResult.rsquareAdj, 'g', 15)); writer->writeAttribute("chisq_p", QString::number(d->fitResult.chisq_p, 'g', 15)); writer->writeAttribute("fdist_F", QString::number(d->fitResult.fdist_F, 'g', 15)); writer->writeAttribute("fdist_p", QString::number(d->fitResult.fdist_p, 'g', 15)); writer->writeAttribute("aic", QString::number(d->fitResult.aic, 'g', 15)); writer->writeAttribute("bic", QString::number(d->fitResult.bic, 'g', 15)); writer->writeAttribute("solverOutput", d->fitResult.solverOutput); writer->writeStartElement("paramValues"); foreach (const double &value, d->fitResult.paramValues) writer->writeTextElement("value", QString::number(value, 'g', 15)); writer->writeEndElement(); writer->writeStartElement("errorValues"); foreach (const double &value, d->fitResult.errorValues) writer->writeTextElement("error", QString::number(value, 'g', 15)); writer->writeEndElement(); writer->writeStartElement("tdist_tValues"); foreach (const double &value, d->fitResult.tdist_tValues) writer->writeTextElement("tdist_t", QString::number(value, 'g', 15)); writer->writeEndElement(); writer->writeStartElement("tdist_pValues"); foreach (const double &value, d->fitResult.tdist_pValues) writer->writeTextElement("tdist_p", QString::number(value, 'g', 15)); writer->writeEndElement(); writer->writeStartElement("tdist_marginValues"); foreach (const double &value, d->fitResult.tdist_marginValues) writer->writeTextElement("tdist_margin", QString::number(value, 'g', 15)); writer->writeEndElement(); //save calculated columns if available if (d->xColumn && d->yColumn && d->residualsColumn) { d->xColumn->save(writer); d->yColumn->save(writer); d->residualsColumn->save(writer); } writer->writeEndElement(); //"fitResult" writer->writeEndElement(); //"xyFitCurve" } //! Load from XML bool XYFitCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYFitCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyFitCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "fitData") { attribs = reader->attributes(); READ_COLUMN(xErrorColumn); READ_COLUMN(yErrorColumn); READ_INT_VALUE("autoRange", fitData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", fitData.fitRange.first()); // old name READ_DOUBLE_VALUE("xRangeMax", fitData.fitRange.last()); // old name READ_DOUBLE_VALUE("fitRangeMin", fitData.fitRange.first()); READ_DOUBLE_VALUE("fitRangeMax", fitData.fitRange.last()); READ_INT_VALUE("modelCategory", fitData.modelCategory, nsl_fit_model_category); READ_INT_VALUE("modelType", fitData.modelType, unsigned int); READ_INT_VALUE("xWeightsType", fitData.xWeightsType, nsl_fit_weight_type); READ_INT_VALUE("weightsType", fitData.yWeightsType, nsl_fit_weight_type); READ_INT_VALUE("degree", fitData.degree, int); if (d->fitData.modelCategory == nsl_fit_model_custom) { READ_STRING_VALUE("model", fitData.model); DEBUG("read model = " << d->fitData.model.toStdString()); } READ_INT_VALUE("maxIterations", fitData.maxIterations, int); READ_DOUBLE_VALUE("eps", fitData.eps); READ_INT_VALUE("fittedPoints", fitData.evaluatedPoints, size_t); // old name READ_INT_VALUE("evaluatedPoints", fitData.evaluatedPoints, size_t); READ_INT_VALUE("evaluateFullRange", fitData.autoEvalRange, bool); // old name READ_INT_VALUE("autoEvalRange", fitData.autoEvalRange, bool); READ_INT_VALUE("useDataErrors", fitData.useDataErrors, bool); READ_INT_VALUE("useResults", fitData.useResults, bool); //set the model expression and the parameter names (can be derived from the saved values for category, type and degree) XYFitCurve::initFitData(d->fitData); } else if (!preview && reader->name() == "name") { // needed for custom model d->fitData.paramNames << reader->readElementText(); } else if (!preview && reader->name() == "startValue") { d->fitData.paramStartValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "fixed") { d->fitData.paramFixed << (bool)reader->readElementText().toInt(); } else if (!preview && reader->name() == "lowerLimit") { bool ok; double x = reader->readElementText().toDouble(&ok); if (ok) // -DBL_MAX results in conversion error d->fitData.paramLowerLimits << x; else d->fitData.paramLowerLimits << -std::numeric_limits::max(); } else if (!preview && reader->name() == "upperLimit") { bool ok; double x = reader->readElementText().toDouble(&ok); if (ok) // DBL_MAX results in conversion error d->fitData.paramUpperLimits << x; else d->fitData.paramUpperLimits << std::numeric_limits::max(); } else if (!preview && reader->name() == "value") { d->fitResult.paramValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "error") { d->fitResult.errorValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "tdist_t") { d->fitResult.tdist_tValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "tdist_p") { d->fitResult.tdist_pValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "tdist_margin") { d->fitResult.tdist_marginValues << reader->readElementText().toDouble(); } else if (!preview && reader->name() == "fitResult") { attribs = reader->attributes(); READ_INT_VALUE("available", fitResult.available, int); READ_INT_VALUE("valid", fitResult.valid, int); READ_STRING_VALUE("status", fitResult.status); READ_INT_VALUE("iterations", fitResult.iterations, int); READ_INT_VALUE("time", fitResult.elapsedTime, int); READ_DOUBLE_VALUE("dof", fitResult.dof); READ_DOUBLE_VALUE("sse", fitResult.sse); READ_DOUBLE_VALUE("sst", fitResult.sst); READ_DOUBLE_VALUE("rms", fitResult.rms); READ_DOUBLE_VALUE("rsd", fitResult.rsd); READ_DOUBLE_VALUE("mse", fitResult.mse); READ_DOUBLE_VALUE("rmse", fitResult.rmse); READ_DOUBLE_VALUE("mae", fitResult.mae); READ_DOUBLE_VALUE("rsquare", fitResult.rsquare); READ_DOUBLE_VALUE("rsquareAdj", fitResult.rsquareAdj); READ_DOUBLE_VALUE("chisq_p", fitResult.chisq_p); READ_DOUBLE_VALUE("fdist_F", fitResult.fdist_F); READ_DOUBLE_VALUE("fdist_p", fitResult.fdist_p); READ_DOUBLE_VALUE("aic", fitResult.aic); READ_DOUBLE_VALUE("bic", fitResult.bic); READ_STRING_VALUE("solverOutput", fitResult.solverOutput); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name() == "x") d->xColumn = column; else if (column->name() == "y") d->yColumn = column; else if (column->name() == "residuals") d->residualsColumn = column; } } if (preview) return true; // new fit model style (reset model type of old projects) if (d->fitData.modelCategory == nsl_fit_model_basic && d->fitData.modelType >= NSL_FIT_MODEL_BASIC_COUNT) { DEBUG("reset old fit model"); d->fitData.modelType = 0; d->fitData.degree = 1; // reset size of fields not touched by initFitData() d->fitData.paramStartValues.resize(2); d->fitData.paramFixed.resize(2); d->fitResult.paramValues.resize(2); d->fitResult.errorValues.resize(2); d->fitResult.tdist_tValues.resize(2); d->fitResult.tdist_pValues.resize(2); d->fitResult.tdist_marginValues.resize(2); } // not present in old projects if (d->fitResult.tdist_tValues.size() == 0) d->fitResult.tdist_tValues.resize(d->fitResult.paramValues.size()); if (d->fitResult.tdist_pValues.size() == 0) d->fitResult.tdist_pValues.resize(d->fitResult.paramValues.size()); if (d->fitResult.tdist_marginValues.size() == 0) d->fitResult.tdist_marginValues.resize(d->fitResult.paramValues.size()); // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn && d->residualsColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); addChild(d->residualsColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); d->residualsVector = static_cast* >(d->residualsColumn->data()); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp b/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp index 84a955310..778672621 100644 --- a/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYFourierFilterCurve.cpp @@ -1,398 +1,398 @@ /*************************************************************************** File : XYFourierFilterCurve.cpp Project : LabPlot Description : A xy-curve defined by a Fourier filter -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /*! \class XYFourierFilterCurve \brief A xy-curve defined by a Fourier filter \ingroup worksheet */ #include "XYFourierFilterCurve.h" #include "XYFourierFilterCurvePrivate.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/gsl/errors.h" extern "C" { #include #ifdef HAVE_FFTW3 #include #endif #include "backend/nsl/nsl_sf_poly.h" } #include #include #include #include // qWarning() XYFourierFilterCurve::XYFourierFilterCurve(const QString& name) : XYAnalysisCurve(name, new XYFourierFilterCurvePrivate(this)) { } XYFourierFilterCurve::XYFourierFilterCurve(const QString& name, XYFourierFilterCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYFourierFilterCurve::~XYFourierFilterCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYFourierFilterCurve::recalculate() { Q_D(XYFourierFilterCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYFourierFilterCurve::icon() const { return QIcon::fromTheme("labplot-xy-fourier_filter-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYFourierFilterCurve, XYFourierFilterCurve::FilterData, filterData, filterData) const XYFourierFilterCurve::FilterResult& XYFourierFilterCurve::filterResult() const { Q_D(const XYFourierFilterCurve); return d->filterResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYFourierFilterCurve, SetFilterData, XYFourierFilterCurve::FilterData, filterData, recalculate); void XYFourierFilterCurve::setFilterData(const XYFourierFilterCurve::FilterData& filterData) { Q_D(XYFourierFilterCurve); - exec(new XYFourierFilterCurveSetFilterDataCmd(d, filterData, i18n("%1: set filter options and perform the Fourier filter"))); + exec(new XYFourierFilterCurveSetFilterDataCmd(d, filterData, ki18n("%1: set filter options and perform the Fourier filter"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYFourierFilterCurvePrivate::XYFourierFilterCurvePrivate(XYFourierFilterCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYFourierFilterCurvePrivate::~XYFourierFilterCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYFourierFilterCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create filter result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result filterResult = XYFourierFilterCurve::FilterResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { filterResult.available = true; filterResult.valid = false; filterResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the differentiation to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (filterData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = filterData.xRange.first(); xmax = filterData.xRange.last(); } for (int row=0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } //number of data points to filter const size_t n = (size_t)xdataVector.size(); if (n == 0) { filterResult.available = true; filterResult.valid = false; filterResult.status = i18n("No data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // filter settings const nsl_filter_type type = filterData.type; const nsl_filter_form form = filterData.form; const int order = filterData.order; const double cutoff = filterData.cutoff, cutoff2 = filterData.cutoff2; const nsl_filter_cutoff_unit unit = filterData.unit, unit2 = filterData.unit2; DEBUG("n ="< 0. Giving up."; return; } DEBUG("cut off @" << cutindex << cutindex2); DEBUG("bandwidth =" << bandwidth); // run filter int status = nsl_filter_fourier(ydata, n, type, form, order, cutindex, bandwidth); xVector->resize((int)n); yVector->resize((int)n); memcpy(xVector->data(), xdataVector.data(), n*sizeof(double)); memcpy(yVector->data(), ydata, n*sizeof(double)); /////////////////////////////////////////////////////////// //write the result filterResult.available = true; filterResult.valid = true; filterResult.status = gslErrorToString(status); filterResult.elapsedTime = timer.elapsed(); //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYFourierFilterCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYFourierFilterCurve); writer->writeStartElement("xyFourierFilterCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-fourier_filter-curve specific information //filter data writer->writeStartElement("filterData"); writer->writeAttribute( "autoRange", QString::number(d->filterData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->filterData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->filterData.xRange.last()) ); writer->writeAttribute( "type", QString::number(d->filterData.type) ); writer->writeAttribute( "form", QString::number(d->filterData.form) ); writer->writeAttribute( "order", QString::number(d->filterData.order) ); writer->writeAttribute( "cutoff", QString::number(d->filterData.cutoff) ); writer->writeAttribute( "unit", QString::number(d->filterData.unit) ); writer->writeAttribute( "cutoff2", QString::number(d->filterData.cutoff2) ); writer->writeAttribute( "unit2", QString::number(d->filterData.unit2) ); writer->writeEndElement();// filterData //filter results (generated columns) writer->writeStartElement("filterResult"); writer->writeAttribute( "available", QString::number(d->filterResult.available) ); writer->writeAttribute( "valid", QString::number(d->filterResult.valid) ); writer->writeAttribute( "status", d->filterResult.status ); writer->writeAttribute( "time", QString::number(d->filterResult.elapsedTime) ); //save calculated columns if available if (d->xColumn && d->yColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"filterResult" writer->writeEndElement(); //"xyFourierFilterCurve" } //! Load from XML bool XYFourierFilterCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYFourierFilterCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyFourierFilterCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "filterData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", filterData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", filterData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", filterData.xRange.last()); READ_INT_VALUE("type", filterData.type, nsl_filter_type); READ_INT_VALUE("form", filterData.form, nsl_filter_form); READ_INT_VALUE("order", filterData.order, int); READ_DOUBLE_VALUE("cutoff", filterData.cutoff); READ_INT_VALUE("unit", filterData.unit, nsl_filter_cutoff_unit); READ_DOUBLE_VALUE("cutoff2", filterData.cutoff2); READ_INT_VALUE("unit2", filterData.unit2, nsl_filter_cutoff_unit); } else if (!preview && reader->name() == "filterResult") { attribs = reader->attributes(); READ_INT_VALUE("available", filterResult.available, int); READ_INT_VALUE("valid", filterResult.valid, int); READ_STRING_VALUE("status", filterResult.status); READ_INT_VALUE("time", filterResult.elapsedTime, int); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name() == "x") d->xColumn = column; else if (column->name() == "y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } else qWarning()<<" d->xColumn == NULL!"; return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYFourierTransformCurve.cpp b/src/backend/worksheet/plots/cartesian/XYFourierTransformCurve.cpp index 2bc840872..08467cede 100644 --- a/src/backend/worksheet/plots/cartesian/XYFourierTransformCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYFourierTransformCurve.cpp @@ -1,384 +1,384 @@ /*************************************************************************** File : XYFourierTransformCurve.cpp Project : LabPlot Description : A xy-curve defined by a Fourier transform -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /*! \class XYFourierTransformCurve \brief A xy-curve defined by a Fourier transform \ingroup worksheet */ #include "XYFourierTransformCurve.h" #include "XYFourierTransformCurvePrivate.h" #include "backend/core/AbstractColumn.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/gsl/errors.h" extern "C" { #include "backend/nsl/nsl_sf_poly.h" } -#include +#include #include #include #include #include // qWarning() XYFourierTransformCurve::XYFourierTransformCurve(const QString& name) : XYAnalysisCurve(name, new XYFourierTransformCurvePrivate(this)) { } XYFourierTransformCurve::XYFourierTransformCurve(const QString& name, XYFourierTransformCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYFourierTransformCurve::~XYFourierTransformCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYFourierTransformCurve::recalculate() { Q_D(XYFourierTransformCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYFourierTransformCurve::icon() const { return QIcon::fromTheme("labplot-xy-fourier_transform-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYFourierTransformCurve, XYFourierTransformCurve::TransformData, transformData, transformData) const XYFourierTransformCurve::TransformResult& XYFourierTransformCurve::transformResult() const { Q_D(const XYFourierTransformCurve); return d->transformResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYFourierTransformCurve, SetTransformData, XYFourierTransformCurve::TransformData, transformData, recalculate); void XYFourierTransformCurve::setTransformData(const XYFourierTransformCurve::TransformData& transformData) { Q_D(XYFourierTransformCurve); - exec(new XYFourierTransformCurveSetTransformDataCmd(d, transformData, i18n("%1: set transform options and perform the Fourier transform"))); + exec(new XYFourierTransformCurveSetTransformDataCmd(d, transformData, ki18n("%1: set transform options and perform the Fourier transform"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYFourierTransformCurvePrivate::XYFourierTransformCurvePrivate(XYFourierTransformCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYFourierTransformCurvePrivate::~XYFourierTransformCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYFourierTransformCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create transform result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result transformResult = XYFourierTransformCurve::TransformResult(); if (!xDataColumn || !yDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (xDataColumn->rowCount()!=yDataColumn->rowCount()) { transformResult.available = true; transformResult.valid = false; transformResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the transform to temporary vectors QVector xdataVector; QVector ydataVector; const double xmin = transformData.xRange.first(); const double xmax = transformData.xRange.last(); for (int row=0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(xDataColumn->valueAt(row)) && !std::isnan(yDataColumn->valueAt(row)) && !xDataColumn->isMasked(row) && !yDataColumn->isMasked(row)) { // only when inside given range if (xDataColumn->valueAt(row) >= xmin && xDataColumn->valueAt(row) <= xmax) { xdataVector.append(xDataColumn->valueAt(row)); ydataVector.append(yDataColumn->valueAt(row)); } } } //number of data points to transform unsigned int n = (unsigned int)ydataVector.size(); if (n == 0) { transformResult.available = true; transformResult.valid = false; transformResult.status = i18n("No data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // transform settings const nsl_sf_window_type windowType = transformData.windowType; const nsl_dft_result_type type = transformData.type; const bool twoSided = transformData.twoSided; const bool shifted = transformData.shifted; const nsl_dft_xscale xScale = transformData.xScale; DEBUG("n =" << n); DEBUG("window type:" << nsl_sf_window_type_name[windowType]); DEBUG("type:" << nsl_dft_result_type_name[type]); DEBUG("scale:" << nsl_dft_xscale_name[xScale]); DEBUG("two sided:" << twoSided); DEBUG("shifted:" << shifted); #ifndef NDEBUG QDebug out = qDebug(); for (unsigned int i=0; i < n; i++) out<= n/2 && shifted) xdata[i] = (n-1)/(xmax-xmin)*(i/(double)n-1.); else xdata[i] = (n-1)*i/(xmax-xmin)/n; } break; case nsl_dft_xscale_index: for (unsigned int i=0; i < N; i++) { if (i >= n/2 && shifted) xdata[i] = (int)i-(int) N; else xdata[i] = i; } break; case nsl_dft_xscale_period: { double f0 = (n-1)/(xmax-xmin)/n; for (unsigned int i=0; i < N; i++) { double f = (n-1)*i/(xmax-xmin)/n; xdata[i] = 1/(f+f0); } break; } } #ifndef NDEBUG out = qDebug(); for (unsigned int i=0; i < N; i++) out << ydata[i] << '(' << xdata[i] << ')'; #endif xVector->resize((int)N); yVector->resize((int)N); if(shifted) { memcpy(xVector->data(), &xdata[n/2], n/2*sizeof(double)); memcpy(&xVector->data()[n/2], xdata, n/2*sizeof(double)); memcpy(yVector->data(), &ydata[n/2], n/2*sizeof(double)); memcpy(&yVector->data()[n/2], ydata, n/2*sizeof(double)); } else { memcpy(xVector->data(), xdata, N*sizeof(double)); memcpy(yVector->data(), ydata, N*sizeof(double)); } /////////////////////////////////////////////////////////// //write the result transformResult.available = true; transformResult.valid = true; transformResult.status = gslErrorToString(status); transformResult.elapsedTime = timer.elapsed(); //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYFourierTransformCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYFourierTransformCurve); writer->writeStartElement("xyFourierTransformCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-fourier_transform-curve specific information //transform data writer->writeStartElement("transformData"); writer->writeAttribute( "autoRange", QString::number(d->transformData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->transformData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->transformData.xRange.last()) ); writer->writeAttribute( "type", QString::number(d->transformData.type) ); writer->writeAttribute( "twoSided", QString::number(d->transformData.twoSided) ); writer->writeAttribute( "shifted", QString::number(d->transformData.shifted) ); writer->writeAttribute( "xScale", QString::number(d->transformData.xScale) ); writer->writeAttribute( "windowType", QString::number(d->transformData.windowType) ); writer->writeEndElement();// transformData //transform results (generated columns) writer->writeStartElement("transformResult"); writer->writeAttribute( "available", QString::number(d->transformResult.available) ); writer->writeAttribute( "valid", QString::number(d->transformResult.valid) ); writer->writeAttribute( "status", d->transformResult.status ); writer->writeAttribute( "time", QString::number(d->transformResult.elapsedTime) ); //save calculated columns if available if (d->xColumn && d->yColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"transformResult" writer->writeEndElement(); //"xyFourierTransformCurve" } //! Load from XML bool XYFourierTransformCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYFourierTransformCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyFourierTransformCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "transformData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", transformData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", transformData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", transformData.xRange.last()); READ_INT_VALUE("type", transformData.type, nsl_dft_result_type); READ_INT_VALUE("twoSided", transformData.twoSided, bool); READ_INT_VALUE("shifted", transformData.shifted, bool); READ_INT_VALUE("xScale", transformData.xScale, nsl_dft_xscale); READ_INT_VALUE("windowType", transformData.windowType, nsl_sf_window_type); } else if (!preview && reader->name() == "transformResult") { attribs = reader->attributes(); READ_INT_VALUE("available", transformResult.available, int); READ_INT_VALUE("valid", transformResult.valid, int); READ_STRING_VALUE("status", transformResult.status); READ_INT_VALUE("time", transformResult.elapsedTime, int); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name() == "x") d->xColumn = column; else if (column->name() == "y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } else qWarning()<<" d->xColumn == NULL!"; return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYIntegrationCurve.cpp b/src/backend/worksheet/plots/cartesian/XYIntegrationCurve.cpp index 11092a825..5bd832a98 100644 --- a/src/backend/worksheet/plots/cartesian/XYIntegrationCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYIntegrationCurve.cpp @@ -1,365 +1,365 @@ /*************************************************************************** File : XYIntegrationCurve.cpp Project : LabPlot Description : A xy-curve defined by an integration -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /*! \class XYIntegrationCurve \brief A xy-curve defined by an integration \ingroup worksheet */ #include "XYIntegrationCurve.h" #include "XYIntegrationCurvePrivate.h" #include "CartesianCoordinateSystem.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" extern "C" { #include } -#include +#include #include #include #include XYIntegrationCurve::XYIntegrationCurve(const QString& name) : XYAnalysisCurve(name, new XYIntegrationCurvePrivate(this)) { } XYIntegrationCurve::XYIntegrationCurve(const QString& name, XYIntegrationCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYIntegrationCurve::~XYIntegrationCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYIntegrationCurve::recalculate() { Q_D(XYIntegrationCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYIntegrationCurve::icon() const { return QIcon::fromTheme("labplot-xy-integration-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYIntegrationCurve, XYIntegrationCurve::IntegrationData, integrationData, integrationData) const XYIntegrationCurve::IntegrationResult& XYIntegrationCurve::integrationResult() const { Q_D(const XYIntegrationCurve); return d->integrationResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYIntegrationCurve, SetIntegrationData, XYIntegrationCurve::IntegrationData, integrationData, recalculate); void XYIntegrationCurve::setIntegrationData(const XYIntegrationCurve::IntegrationData& integrationData) { Q_D(XYIntegrationCurve); - exec(new XYIntegrationCurveSetIntegrationDataCmd(d, integrationData, i18n("%1: set options and perform the integration"))); + exec(new XYIntegrationCurveSetIntegrationDataCmd(d, integrationData, ki18n("%1: set options and perform the integration"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYIntegrationCurvePrivate::XYIntegrationCurvePrivate(XYIntegrationCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYIntegrationCurvePrivate::~XYIntegrationCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYIntegrationCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create integration result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result integrationResult = XYIntegrationCurve::IntegrationResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { integrationResult.available = true; integrationResult.valid = false; integrationResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the integration to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (integrationData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = integrationData.xRange.first(); xmax = integrationData.xRange.last(); } for (int row = 0; row < tmpXDataColumn->rowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } const size_t n = (size_t)xdataVector.size(); // number of data points to integrate if (n < 2) { integrationResult.available = true; integrationResult.valid = false; integrationResult.status = i18n("Not enough data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // integration settings const nsl_int_method_type method = integrationData.method; const bool absolute = integrationData.absolute; DEBUG("method:"<resize((int)np); yVector->resize((int)np); memcpy(xVector->data(), xdata, np * sizeof(double)); memcpy(yVector->data(), ydata, np * sizeof(double)); /////////////////////////////////////////////////////////// //write the result integrationResult.available = true; integrationResult.valid = true; integrationResult.status = QString::number(status); integrationResult.elapsedTime = timer.elapsed(); integrationResult.value = ydata[np-1]; //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYIntegrationCurve::save(QXmlStreamWriter* writer) const{ Q_D(const XYIntegrationCurve); writer->writeStartElement("xyIntegrationCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-integration-curve specific information // integration data writer->writeStartElement("integrationData"); writer->writeAttribute( "autoRange", QString::number(d->integrationData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->integrationData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->integrationData.xRange.last()) ); writer->writeAttribute( "method", QString::number(d->integrationData.method) ); writer->writeAttribute( "absolute", QString::number(d->integrationData.absolute) ); writer->writeEndElement();// integrationData // integration results (generated columns) writer->writeStartElement("integrationResult"); writer->writeAttribute( "available", QString::number(d->integrationResult.available) ); writer->writeAttribute( "valid", QString::number(d->integrationResult.valid) ); writer->writeAttribute( "status", d->integrationResult.status ); writer->writeAttribute( "time", QString::number(d->integrationResult.elapsedTime) ); writer->writeAttribute( "value", QString::number(d->integrationResult.value) ); //save calculated columns if available if (d->xColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"integrationResult" writer->writeEndElement(); //"xyIntegrationCurve" } //! Load from XML bool XYIntegrationCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYIntegrationCurve); if (!reader->isStartElement() || reader->name() != "xyIntegrationCurve") { reader->raiseError(i18n("no xy integration curve element found")); return false; } - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyIntegrationCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "integrationData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", integrationData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", integrationData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", integrationData.xRange.last()); READ_INT_VALUE("method", integrationData.method, nsl_int_method_type); READ_INT_VALUE("absolute", integrationData.absolute, bool); } else if (!preview && reader->name() == "integrationResult") { attribs = reader->attributes(); READ_INT_VALUE("available", integrationResult.available, int); READ_INT_VALUE("valid", integrationResult.valid, int); READ_STRING_VALUE("status", integrationResult.status); READ_INT_VALUE("time", integrationResult.elapsedTime, int); READ_DOUBLE_VALUE("value", integrationResult.value); } else if (!preview && reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name()=="x") d->xColumn = column; else if (column->name()=="y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp index 75f375a05..377967e22 100644 --- a/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYInterpolationCurve.cpp @@ -1,554 +1,554 @@ /*************************************************************************** File : XYInterpolationCurve.cpp Project : LabPlot Description : A xy-curve defined by an interpolation -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2016-2017 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 * * * ***************************************************************************/ /*! \class XYInterpolationCurve \brief A xy-curve defined by an interpolation \ingroup worksheet */ #include "XYInterpolationCurve.h" #include "XYInterpolationCurvePrivate.h" #include "CartesianCoordinateSystem.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" #include "backend/gsl/errors.h" extern "C" { #include #include #include "backend/nsl/nsl_diff.h" #include "backend/nsl/nsl_int.h" } #include #include #include XYInterpolationCurve::XYInterpolationCurve(const QString& name) : XYAnalysisCurve(name, new XYInterpolationCurvePrivate(this)) { } XYInterpolationCurve::XYInterpolationCurve(const QString& name, XYInterpolationCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYInterpolationCurve::~XYInterpolationCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYInterpolationCurve::recalculate() { Q_D(XYInterpolationCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYInterpolationCurve::icon() const { return QIcon::fromTheme("labplot-xy-interpolation-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYInterpolationCurve, XYInterpolationCurve::InterpolationData, interpolationData, interpolationData) const XYInterpolationCurve::InterpolationResult& XYInterpolationCurve::interpolationResult() const { Q_D(const XYInterpolationCurve); return d->interpolationResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYInterpolationCurve, SetInterpolationData, XYInterpolationCurve::InterpolationData, interpolationData, recalculate); void XYInterpolationCurve::setInterpolationData(const XYInterpolationCurve::InterpolationData& interpolationData) { Q_D(XYInterpolationCurve); - exec(new XYInterpolationCurveSetInterpolationDataCmd(d, interpolationData, i18n("%1: set options and perform the interpolation"))); + exec(new XYInterpolationCurveSetInterpolationDataCmd(d, interpolationData, ki18n("%1: set options and perform the interpolation"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYInterpolationCurvePrivate::XYInterpolationCurvePrivate(XYInterpolationCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYInterpolationCurvePrivate::~XYInterpolationCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYInterpolationCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create interpolation result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result interpolationResult = XYInterpolationCurve::InterpolationResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { interpolationResult.available = true; interpolationResult.valid = false; interpolationResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the interpolation to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (interpolationData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = interpolationData.xRange.first(); xmax = interpolationData.xRange.last(); } for (int row=0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } //number of data points to interpolate const size_t n = (size_t)xdataVector.size(); if (n < 2) { interpolationResult.available = true; interpolationResult.valid = false; interpolationResult.status = i18n("Not enough data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // interpolation settings const nsl_interp_type type = interpolationData.type; const nsl_interp_pch_variant variant = interpolationData.variant; const double tension = interpolationData.tension; const double continuity = interpolationData.continuity; const double bias = interpolationData.bias; const nsl_interp_evaluate evaluate = interpolationData.evaluate; const size_t npoints = interpolationData.npoints; DEBUG("type:"<data(), yVector->data(), npoints); break; case nsl_interp_evaluate_second_derivative: nsl_diff_second_deriv_second_order(xVector->data(), yVector->data(), npoints); break; case nsl_interp_evaluate_integral: nsl_int_trapezoid(xVector->data(), yVector->data(), npoints, 0); break; } } // check values for (int i = 0; i < (int)npoints; i++) { if ((*yVector)[i] > std::numeric_limits::max()) (*yVector)[i] = std::numeric_limits::max(); else if ((*yVector)[i] < std::numeric_limits::lowest()) (*yVector)[i] = std::numeric_limits::lowest(); } gsl_spline_free(spline); gsl_interp_accel_free(acc); /////////////////////////////////////////////////////////// //write the result interpolationResult.available = true; interpolationResult.valid = true; interpolationResult.status = gslErrorToString(status); interpolationResult.elapsedTime = timer.elapsed(); //redraw the curve emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void XYInterpolationCurve::save(QXmlStreamWriter* writer) const { Q_D(const XYInterpolationCurve); writer->writeStartElement("xyInterpolationCurve"); //write the base class XYAnalysisCurve::save(writer); //write xy-interpolation-curve specific information // interpolation data writer->writeStartElement("interpolationData"); writer->writeAttribute( "autoRange", QString::number(d->interpolationData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->interpolationData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->interpolationData.xRange.last()) ); writer->writeAttribute( "type", QString::number(d->interpolationData.type) ); writer->writeAttribute( "variant", QString::number(d->interpolationData.variant) ); writer->writeAttribute( "tension", QString::number(d->interpolationData.tension) ); writer->writeAttribute( "continuity", QString::number(d->interpolationData.continuity) ); writer->writeAttribute( "bias", QString::number(d->interpolationData.bias) ); writer->writeAttribute( "npoints", QString::number(d->interpolationData.npoints) ); writer->writeAttribute( "pointsMode", QString::number(d->interpolationData.pointsMode) ); writer->writeAttribute( "evaluate", QString::number(d->interpolationData.evaluate) ); writer->writeEndElement();// interpolationData // interpolation results (generated columns) writer->writeStartElement("interpolationResult"); writer->writeAttribute( "available", QString::number(d->interpolationResult.available) ); writer->writeAttribute( "valid", QString::number(d->interpolationResult.valid) ); writer->writeAttribute( "status", d->interpolationResult.status ); writer->writeAttribute( "time", QString::number(d->interpolationResult.elapsedTime) ); //save calculated columns if available if (d->xColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"interpolationResult" writer->writeEndElement(); //"xyInterpolationCurve" } //! Load from XML bool XYInterpolationCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYInterpolationCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xyInterpolationCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "interpolationData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", interpolationData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", interpolationData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", interpolationData.xRange.last()); READ_INT_VALUE("type", interpolationData.type, nsl_interp_type); READ_INT_VALUE("variant", interpolationData.variant, nsl_interp_pch_variant); READ_DOUBLE_VALUE("tension", interpolationData.tension); READ_DOUBLE_VALUE("continuity", interpolationData.continuity); READ_DOUBLE_VALUE("bias", interpolationData.bias); READ_INT_VALUE("npoints", interpolationData.npoints, size_t); READ_INT_VALUE("pointsMode", interpolationData.pointsMode, XYInterpolationCurve::PointsMode); READ_INT_VALUE("evaluate", interpolationData.evaluate, nsl_interp_evaluate); } else if (!preview && reader->name() == "interpolationResult") { attribs = reader->attributes(); READ_INT_VALUE("available", interpolationResult.available, int); READ_INT_VALUE("valid", interpolationResult.valid, int); READ_STRING_VALUE("status", interpolationResult.status); READ_INT_VALUE("time", interpolationResult.elapsedTime, int); } else if (reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name()=="x") d->xColumn = column; else if (column->name()=="y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } return true; } diff --git a/src/backend/worksheet/plots/cartesian/XYSmoothCurve.cpp b/src/backend/worksheet/plots/cartesian/XYSmoothCurve.cpp index ec98b98a7..74113bc97 100644 --- a/src/backend/worksheet/plots/cartesian/XYSmoothCurve.cpp +++ b/src/backend/worksheet/plots/cartesian/XYSmoothCurve.cpp @@ -1,383 +1,383 @@ /*************************************************************************** File : XYSmoothCurve.cpp Project : LabPlot Description : A xy-curve defined by a smooth -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 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 * * * ***************************************************************************/ /*! \class XYSmoothCurve \brief A xy-curve defined by a smooth \ingroup worksheet */ #include "XYSmoothCurve.h" #include "XYSmoothCurvePrivate.h" #include "backend/core/column/Column.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/macros.h" -#include +#include #include #include #include extern "C" { #include // gsl_pow_* #include "backend/nsl/nsl_stats.h" #include "backend/nsl/nsl_sf_kernel.h" } XYSmoothCurve::XYSmoothCurve(const QString& name) : XYAnalysisCurve(name, new XYSmoothCurvePrivate(this)) { } XYSmoothCurve::XYSmoothCurve(const QString& name, XYSmoothCurvePrivate* dd) : XYAnalysisCurve(name, dd) { } XYSmoothCurve::~XYSmoothCurve() { //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene } void XYSmoothCurve::recalculate() { Q_D(XYSmoothCurve); d->recalculate(); } /*! Returns an icon to be used in the project explorer. */ QIcon XYSmoothCurve::icon() const { return QIcon::fromTheme("labplot-xy-smooth-curve"); } //############################################################################## //########################## getter methods ################################## //############################################################################## BASIC_SHARED_D_READER_IMPL(XYSmoothCurve, XYSmoothCurve::SmoothData, smoothData, smoothData) const XYSmoothCurve::SmoothResult& XYSmoothCurve::smoothResult() const { Q_D(const XYSmoothCurve); return d->smoothResult; } //############################################################################## //################# setter methods and undo commands ########################## //############################################################################## STD_SETTER_CMD_IMPL_F_S(XYSmoothCurve, SetSmoothData, XYSmoothCurve::SmoothData, smoothData, recalculate); void XYSmoothCurve::setSmoothData(const XYSmoothCurve::SmoothData& smoothData) { Q_D(XYSmoothCurve); - exec(new XYSmoothCurveSetSmoothDataCmd(d, smoothData, i18n("%1: set options and perform the smooth"))); + exec(new XYSmoothCurveSetSmoothDataCmd(d, smoothData, ki18n("%1: set options and perform the smooth"))); } //############################################################################## //######################### Private implementation ############################# //############################################################################## XYSmoothCurvePrivate::XYSmoothCurvePrivate(XYSmoothCurve* owner) : XYAnalysisCurvePrivate(owner), q(owner) { } XYSmoothCurvePrivate::~XYSmoothCurvePrivate() { //no need to delete xColumn and yColumn, they are deleted //when the parent aspect is removed } void XYSmoothCurvePrivate::recalculate() { QElapsedTimer timer; timer.start(); //create smooth result columns if not available yet, clear them otherwise if (!xColumn) { xColumn = new Column("x", AbstractColumn::Numeric); yColumn = new Column("y", AbstractColumn::Numeric); xVector = static_cast* >(xColumn->data()); yVector = static_cast* >(yColumn->data()); xColumn->setHidden(true); q->addChild(xColumn); yColumn->setHidden(true); q->addChild(yColumn); q->setUndoAware(false); q->setXColumn(xColumn); q->setYColumn(yColumn); q->setUndoAware(true); } else { xVector->clear(); yVector->clear(); } // clear the previous result smoothResult = XYSmoothCurve::SmoothResult(); //determine the data source columns const AbstractColumn* tmpXDataColumn = 0; const AbstractColumn* tmpYDataColumn = 0; if (dataSourceType == XYAnalysisCurve::DataSourceSpreadsheet) { //spreadsheet columns as data source tmpXDataColumn = xDataColumn; tmpYDataColumn = yDataColumn; } else { //curve columns as data source tmpXDataColumn = dataSourceCurve->xColumn(); tmpYDataColumn = dataSourceCurve->yColumn(); } if (!tmpXDataColumn || !tmpYDataColumn) { emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //check column sizes if (tmpXDataColumn->rowCount() != tmpYDataColumn->rowCount()) { smoothResult.available = true; smoothResult.valid = false; smoothResult.status = i18n("Number of x and y data points must be equal."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } //copy all valid data point for the smooth to temporary vectors QVector xdataVector; QVector ydataVector; double xmin; double xmax; if (smoothData.autoRange) { xmin = tmpXDataColumn->minimum(); xmax = tmpXDataColumn->maximum(); } else { xmin = smoothData.xRange.first(); xmax = smoothData.xRange.last(); } for (int row = 0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(tmpXDataColumn->valueAt(row)) && !std::isnan(tmpYDataColumn->valueAt(row)) && !tmpXDataColumn->isMasked(row) && !tmpYDataColumn->isMasked(row)) { // only when inside given range if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) { xdataVector.append(tmpXDataColumn->valueAt(row)); ydataVector.append(tmpYDataColumn->valueAt(row)); } } } //number of data points to smooth const size_t n = (size_t)xdataVector.size(); if (n < 2) { smoothResult.available = true; smoothResult.valid = false; smoothResult.status = i18n("Not enough data points available."); emit q->dataChanged(); sourceDataChangedSinceLastRecalc = false; return; } double* xdata = xdataVector.data(); double* ydata = ydataVector.data(); // smooth settings const nsl_smooth_type type = smoothData.type; const size_t points = smoothData.points; const nsl_smooth_weight_type weight = smoothData.weight; const double percentile = smoothData.percentile; const int order = smoothData.order; const nsl_smooth_pad_mode mode = smoothData.mode; const double lvalue = smoothData.lvalue; const double rvalue = smoothData.rvalue; DEBUG("type:"<writeStartElement("smoothData"); writer->writeAttribute( "autoRange", QString::number(d->smoothData.autoRange) ); writer->writeAttribute( "xRangeMin", QString::number(d->smoothData.xRange.first()) ); writer->writeAttribute( "xRangeMax", QString::number(d->smoothData.xRange.last()) ); writer->writeAttribute( "type", QString::number(d->smoothData.type) ); writer->writeAttribute( "points", QString::number(d->smoothData.points) ); writer->writeAttribute( "weight", QString::number(d->smoothData.weight) ); writer->writeAttribute( "percentile", QString::number(d->smoothData.percentile) ); writer->writeAttribute( "order", QString::number(d->smoothData.order) ); writer->writeAttribute( "mode", QString::number(d->smoothData.mode) ); writer->writeAttribute( "lvalue", QString::number(d->smoothData.lvalue) ); writer->writeAttribute( "rvalue", QString::number(d->smoothData.rvalue) ); writer->writeEndElement();// smoothData // smooth results (generated columns) writer->writeStartElement("smoothResult"); writer->writeAttribute( "available", QString::number(d->smoothResult.available) ); writer->writeAttribute( "valid", QString::number(d->smoothResult.valid) ); writer->writeAttribute( "status", d->smoothResult.status ); writer->writeAttribute( "time", QString::number(d->smoothResult.elapsedTime) ); //save calculated columns if available if (d->xColumn) { d->xColumn->save(writer); d->yColumn->save(writer); } writer->writeEndElement(); //"smoothResult" writer->writeEndElement(); //"xySmoothCurve" } //! Load from XML bool XYSmoothCurve::load(XmlStreamReader* reader, bool preview) { Q_D(XYSmoothCurve); - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "xySmoothCurve") break; if (!reader->isStartElement()) continue; if (reader->name() == "xyAnalysisCurve") { if ( !XYAnalysisCurve::load(reader, preview) ) return false; } else if (!preview && reader->name() == "smoothData") { attribs = reader->attributes(); READ_INT_VALUE("autoRange", smoothData.autoRange, bool); READ_DOUBLE_VALUE("xRangeMin", smoothData.xRange.first()); READ_DOUBLE_VALUE("xRangeMax", smoothData.xRange.last()); READ_INT_VALUE("type", smoothData.type, nsl_smooth_type); READ_INT_VALUE("points", smoothData.points, size_t); READ_INT_VALUE("weight", smoothData.weight, nsl_smooth_weight_type); READ_DOUBLE_VALUE("percentile", smoothData.percentile); READ_INT_VALUE("order", smoothData.order, int); READ_INT_VALUE("mode", smoothData.mode, nsl_smooth_pad_mode); READ_DOUBLE_VALUE("lvalue", smoothData.lvalue); READ_DOUBLE_VALUE("rvalue", smoothData.rvalue); } else if (!preview && reader->name() == "smoothResult") { attribs = reader->attributes(); READ_INT_VALUE("available", smoothResult.available, int); READ_INT_VALUE("valid", smoothResult.valid, int); READ_STRING_VALUE("status", smoothResult.status); READ_INT_VALUE("time", smoothResult.elapsedTime, int); } else if (!preview && reader->name() == "column") { Column* column = new Column("", AbstractColumn::Numeric); if (!column->load(reader, preview)) { delete column; return false; } if (column->name()=="x") d->xColumn = column; else if (column->name()=="y") d->yColumn = column; } } if (preview) return true; // wait for data to be read before using the pointers QThreadPool::globalInstance()->waitForDone(); if (d->xColumn && d->yColumn) { d->xColumn->setHidden(true); addChild(d->xColumn); d->yColumn->setHidden(true); addChild(d->yColumn); d->xVector = static_cast* >(d->xColumn->data()); d->yVector = static_cast* >(d->yColumn->data()); setUndoAware(false); XYCurve::d_ptr->xColumn = d->xColumn; XYCurve::d_ptr->yColumn = d->yColumn; setUndoAware(true); } return true; } diff --git a/src/commonfrontend/ProjectExplorer.cpp b/src/commonfrontend/ProjectExplorer.cpp index ed474febf..c734fef4a 100644 --- a/src/commonfrontend/ProjectExplorer.cpp +++ b/src/commonfrontend/ProjectExplorer.cpp @@ -1,851 +1,851 @@ /*************************************************************************** File : ProjectExplorer.cpp Project : LabPlot Description : A tree view for displaying and editing an AspectTreeModel. -------------------------------------------------------------------- Copyright : (C) 2007-2008 by Tilman Benkert (thzs@gmx.net) Copyright : (C) 2010-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 "ProjectExplorer.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/AbstractPart.h" #include "backend/core/Project.h" #include "backend/lib/XmlStreamReader.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "commonfrontend/core/PartMdiView.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include /*! \class ProjectExplorer \brief A tree view for displaying and editing an AspectTreeModel. In addition to the functionality of QTreeView, ProjectExplorer allows the usage of the context menus provided by AspectTreeModel and propagates the item selection in the view to the model. Furthermore, features for searching and filtering in the model are provided. \ingroup commonfrontend */ ProjectExplorer::ProjectExplorer(QWidget* parent) : m_columnToHide(0), m_treeView(new QTreeView(parent)), m_project(nullptr), m_dragStarted(false), m_frameFilter(new QFrame(this)) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); QHBoxLayout* layoutFilter= new QHBoxLayout(m_frameFilter); layoutFilter->setSpacing(0); layoutFilter->setContentsMargins(0, 0, 0, 0); QLabel* lFilter = new QLabel(i18n("Search/Filter:")); layoutFilter->addWidget(lFilter); m_leFilter = new QLineEdit(m_frameFilter); m_leFilter->setClearButtonEnabled(true); m_leFilter->setPlaceholderText(i18n("Search/Filter text")); layoutFilter->addWidget(m_leFilter); bFilterOptions = new QPushButton(m_frameFilter); bFilterOptions->setIcon(QIcon::fromTheme("configure")); bFilterOptions->setCheckable(true); layoutFilter->addWidget(bFilterOptions); layout->addWidget(m_frameFilter); m_treeView->setAnimated(true); m_treeView->setAlternatingRowColors(true); m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_treeView->setUniformRowHeights(true); m_treeView->viewport()->installEventFilter(this); m_treeView->header()->setStretchLastSection(true); m_treeView->header()->installEventFilter(this); m_treeView->setDragEnabled(true); m_treeView->setAcceptDrops(true); m_treeView->setDropIndicatorShown(true); m_treeView->setDragDropMode(QAbstractItemView::InternalMove); layout->addWidget(m_treeView); this->createActions(); connect(m_leFilter, SIGNAL(textChanged(QString)), this, SLOT(filterTextChanged(QString))); connect(bFilterOptions, SIGNAL(toggled(bool)), this, SLOT(toggleFilterOptionsMenu(bool))); } void ProjectExplorer::createActions() { - caseSensitiveAction = new QAction(i18n("case sensitive"), this); + caseSensitiveAction = new QAction(i18n("Case Sensitive"), this); caseSensitiveAction->setCheckable(true); caseSensitiveAction->setChecked(false); connect(caseSensitiveAction, SIGNAL(triggered()), this, SLOT(toggleFilterCaseSensitivity())); - matchCompleteWordAction = new QAction(i18n("match complete word"), this); + matchCompleteWordAction = new QAction(i18n("Match Complete Word"), this); matchCompleteWordAction->setCheckable(true); matchCompleteWordAction->setChecked(false); connect(matchCompleteWordAction, SIGNAL(triggered()), this, SLOT(toggleFilterMatchCompleteWord())); - expandTreeAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-tree")), i18n("expand all"), this); + expandTreeAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-tree")), i18n("Expand All"), this); connect(expandTreeAction, SIGNAL(triggered()), m_treeView, SLOT(expandAll())); - expandSelectedTreeAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-tree")), i18n("expand selected"), this); + expandSelectedTreeAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-tree")), i18n("Expand Selected"), this); connect(expandSelectedTreeAction, SIGNAL(triggered()), this, SLOT(expandSelected())); - collapseTreeAction = new QAction(i18n("collapse all"), this); + collapseTreeAction = new QAction(i18n("Collapse All"), this); connect(collapseTreeAction, SIGNAL(triggered()), m_treeView, SLOT(collapseAll())); - collapseSelectedTreeAction = new QAction(i18n("collapse selected"), this); + collapseSelectedTreeAction = new QAction(i18n("Collapse Selected"), this); connect(collapseSelectedTreeAction, SIGNAL(triggered()), this, SLOT(collapseSelected())); - deleteSelectedTreeAction = new QAction(QIcon::fromTheme("edit-delete"), i18n("delete selected"), this); + deleteSelectedTreeAction = new QAction(QIcon::fromTheme("edit-delete"), i18n("Delete Selected"), this); connect(deleteSelectedTreeAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); - toggleFilterAction = new QAction(QIcon::fromTheme(QLatin1String("view-filter")), i18n("hide search/filter options"), this); + toggleFilterAction = new QAction(QIcon::fromTheme(QLatin1String("view-filter")), i18n("Hide Search/Filter Options"), this); connect(toggleFilterAction, SIGNAL(triggered()), this, SLOT(toggleFilterWidgets())); - showAllColumnsAction = new QAction(i18n("show all"),this); + showAllColumnsAction = new QAction(i18n("Show All"),this); showAllColumnsAction->setCheckable(true); showAllColumnsAction->setChecked(true); showAllColumnsAction->setEnabled(false); connect(showAllColumnsAction, SIGNAL(triggered()), this, SLOT(showAllColumns())); } /*! shows the context menu in the tree. In addition to the context menu of the currently selected aspect, treeview specific options are added. */ void ProjectExplorer::contextMenuEvent(QContextMenuEvent *event) { if(!m_treeView->model()) return; const QModelIndex& index = m_treeView->indexAt(m_treeView->viewport()->mapFrom(this, event->pos())); if (!index.isValid()) m_treeView->clearSelection(); const QModelIndexList& items = m_treeView->selectionModel()->selectedIndexes(); QMenu* menu = nullptr; if (items.size()/4 == 1) { AbstractAspect* aspect = static_cast(index.internalPointer()); menu = aspect->createContextMenu(); } else { menu = new QMenu(); if (items.size()/4 > 1) { menu->addAction(expandSelectedTreeAction); menu->addAction(collapseSelectedTreeAction); menu->addSeparator(); menu->addAction(deleteSelectedTreeAction); menu->addSeparator(); } else { menu->addAction(expandTreeAction); menu->addAction(collapseTreeAction); menu->addSeparator(); menu->addAction(toggleFilterAction); //Menu for showing/hiding the columns in the tree view - QMenu* columnsMenu = menu->addMenu(i18n("show/hide columns")); + QMenu* columnsMenu = menu->addMenu(i18n("Show/Hide columns")); columnsMenu->addAction(showAllColumnsAction); columnsMenu->addSeparator(); for (int i=0; iaddAction(list_showColumnActions.at(i)); //TODO //Menu for showing/hiding the top-level aspects (Worksheet, Spreadhsheet, etc) in the tree view - // QMenu* objectsMenu = menu->addMenu(i18n("show/hide objects")); + // QMenu* objectsMenu = menu->addMenu(i18n("Show/Hide objects")); } } menu->exec(event->globalPos()); delete menu; } void ProjectExplorer::setCurrentAspect(const AbstractAspect* aspect) { const AspectTreeModel* tree_model = qobject_cast(m_treeView->model()); if(tree_model) m_treeView->setCurrentIndex(tree_model->modelIndexOfAspect(aspect)); } /*! Sets the \c model for the tree view to present. */ void ProjectExplorer::setModel(AspectTreeModel* treeModel) { m_treeView->setModel(treeModel); connect(treeModel, SIGNAL(renameRequested(QModelIndex)), m_treeView, SLOT(edit(QModelIndex))); connect(treeModel, SIGNAL(indexSelected(QModelIndex)), this, SLOT(selectIndex(QModelIndex))); connect(treeModel, SIGNAL(indexDeselected(QModelIndex)), this, SLOT(deselectIndex(QModelIndex))); connect(treeModel, SIGNAL(hiddenAspectSelected(const AbstractAspect*)), this, SIGNAL(hiddenAspectSelected(const AbstractAspect*))); connect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(currentChanged(QModelIndex,QModelIndex)) ); connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)) ); //create action for showing/hiding the columns in the tree. //this is done here since the number of columns is not available in createActions() yet. if (list_showColumnActions.size()==0) { showColumnsSignalMapper = new QSignalMapper(this); for (int i=0; imodel()->columnCount(); i++) { QAction* showColumnAction = new QAction(treeModel->headerData(i, Qt::Horizontal).toString(), this); showColumnAction->setCheckable(true); showColumnAction->setChecked(true); list_showColumnActions.append(showColumnAction); connect(showColumnAction, SIGNAL(triggered(bool)), showColumnsSignalMapper, SLOT(map())); showColumnsSignalMapper->setMapping(showColumnAction, i); } connect(showColumnsSignalMapper, SIGNAL(mapped(int)), this, SLOT(toggleColumn(int))); } else { for (int i=0; iisChecked()) m_treeView->hideColumn(i); } } } void ProjectExplorer::setProject(Project* project) { connect(project, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(aspectAdded(const AbstractAspect*))); connect(project, SIGNAL(requestSaveState(QXmlStreamWriter*)), this, SLOT(save(QXmlStreamWriter*))); connect(project, SIGNAL(requestLoadState(XmlStreamReader*)), this, SLOT(load(XmlStreamReader*))); connect(project, SIGNAL(requestNavigateTo(QString)), this, SLOT(navigateTo(QString))); connect(project, SIGNAL(loaded()), this, SLOT(resizeHeader())); m_project = project; //for newly created projects, resize the header to fit the size of the header section names. //for projects loaded from a file, this function will be called laterto fit the sizes //of the content once the project is loaded resizeHeader(); } QModelIndex ProjectExplorer::currentIndex() const { return m_treeView->currentIndex(); } /*! handles the contextmenu-event of the horizontal header in the tree view. Provides a menu for selective showing and hiding of columns. */ bool ProjectExplorer::eventFilter(QObject* obj, QEvent* event) { if (obj == m_treeView->header() && event->type() == QEvent::ContextMenu) { //Menu for showing/hiding the columns in the tree view QMenu* columnsMenu = new QMenu(m_treeView->header()); columnsMenu->addSection(i18n("Columns")); columnsMenu->addAction(showAllColumnsAction); columnsMenu->addSeparator(); for (int i=0; iaddAction(list_showColumnActions.at(i)); QContextMenuEvent* e = static_cast(event); columnsMenu->exec(e->globalPos()); delete columnsMenu; return true; } else if (obj == m_treeView->viewport()) { QMouseEvent* e = static_cast(event); if (event->type() == QEvent::MouseButtonPress) { if (e->button() == Qt::LeftButton) { m_dragStartPos = e->globalPos(); m_dragStarted = false; } } else if (event->type() == QEvent::MouseMove) { if ( !m_dragStarted && m_treeView->selectionModel()->selectedIndexes().size() > 0 && (e->globalPos() - m_dragStartPos).manhattanLength() >= QApplication::startDragDistance()) { m_dragStarted = true; QDrag* drag = new QDrag(this); QMimeData* mimeData = new QMimeData; //determine the selected objects and serialize the pointers to QMimeData QVector vec; QModelIndexList items = m_treeView->selectionModel()->selectedIndexes(); //there are four model indices in each row -> divide by 4 to obtain the number of selected rows (=aspects) for (int i = 0; i < items.size()/4; ++i) { const QModelIndex& index = items.at(i*4); AbstractAspect* aspect = static_cast(index.internalPointer()); vec << (quintptr)aspect; } QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << vec; mimeData->setData("labplot-dnd", data); drag->setMimeData(mimeData); drag->exec(); } } else if (event->type() == QEvent::DragEnter) { //ignore events not related to internal drags of columns etc., e.g. dropping of external files onto LabPlot QDragEnterEvent* dragEnterEvent = static_cast(event); const QMimeData* mimeData = dragEnterEvent->mimeData(); if (!mimeData) { event->ignore(); return false; } if (mimeData->formats().at(0) != QLatin1String("labplot-dnd")) { event->ignore(); return false; } event->setAccepted(true); } else if (event->type() == QEvent::DragMove) { // only accept drop events if we have a plot under the cursor where we can drop columns onto QDragMoveEvent* dragMoveEvent = static_cast(event); QModelIndex index = m_treeView->indexAt(dragMoveEvent->pos()); if (!index.isValid()) return false; AbstractAspect* aspect = static_cast(index.internalPointer()); const bool isPlot = (dynamic_cast(aspect) != nullptr); event->setAccepted(isPlot); } else if (event->type() == QEvent::Drop) { QDropEvent* dropEvent = static_cast(event); QModelIndex index = m_treeView->indexAt(dropEvent->pos()); if (!index.isValid()) return false; AbstractAspect* aspect = static_cast(index.internalPointer()); CartesianPlot* plot = dynamic_cast(aspect); if (plot != nullptr) plot->processDropEvent(dropEvent); } } return QObject::eventFilter(obj, event); } //############################################################################## //################################# SLOTS #################################### //############################################################################## /*! expand the aspect \c aspect (the tree index corresponding to it) in the tree view and makes it visible and selected. Called when a new aspect is added to the project. */ void ProjectExplorer::aspectAdded(const AbstractAspect* aspect) { if (m_project->isLoading()) return; //don't do anything if hidden aspects were added if (aspect->hidden()) return; //don't do anything for newly added data spreadsheets of data picker curves if (aspect->inherits("Spreadsheet") && aspect->parentAspect()->inherits("DatapickerCurve")) return; const AspectTreeModel* tree_model = qobject_cast(m_treeView->model()); const QModelIndex& index = tree_model->modelIndexOfAspect(aspect); //expand and make the aspect visible m_treeView->setExpanded(index, true); // newly added columns are only expanded but not selected, return here if ( aspect->inherits("Column") ) { m_treeView->setExpanded(tree_model->modelIndexOfAspect(aspect->parentAspect()), true); return; } m_treeView->scrollTo(index); m_treeView->setCurrentIndex(index); m_treeView->header()->resizeSections(QHeaderView::ResizeToContents); m_treeView->header()->resizeSection(0, m_treeView->header()->sectionSize(0)*1.2); } void ProjectExplorer::navigateTo(const QString& path) { const AspectTreeModel* tree_model = qobject_cast(m_treeView->model()); if(tree_model) m_treeView->setCurrentIndex(tree_model->modelIndexOfAspect(path)); } void ProjectExplorer::currentChanged(const QModelIndex & current, const QModelIndex & previous) { Q_UNUSED(previous); emit currentAspectChanged(static_cast(current.internalPointer())); } void ProjectExplorer::toggleColumn(int index) { //determine the total number of checked column actions int checked = 0; for (const auto* action : list_showColumnActions) { if (action->isChecked()) checked++; } if (list_showColumnActions.at(index)->isChecked()) { m_treeView->showColumn(index); m_treeView->header()->resizeSection(0,0 ); m_treeView->header()->resizeSections(QHeaderView::ResizeToContents); for (auto* action : list_showColumnActions) action->setEnabled(true); //deactivate the "show all column"-action, if all actions are checked if ( checked == list_showColumnActions.size() ) { showAllColumnsAction->setEnabled(false); showAllColumnsAction->setChecked(true); } } else { m_treeView->hideColumn(index); showAllColumnsAction->setEnabled(true); showAllColumnsAction->setChecked(false); //if there is only one checked column-action, deactivated it. //It should't be possible to hide all columns if ( checked == 1 ) { int i=0; while( !list_showColumnActions.at(i)->isChecked() ) i++; list_showColumnActions.at(i)->setEnabled(false); } } } void ProjectExplorer::showAllColumns() { for (int i=0; imodel()->columnCount(); i++) { m_treeView->showColumn(i); m_treeView->header()->resizeSection(0,0 ); m_treeView->header()->resizeSections(QHeaderView::ResizeToContents); } showAllColumnsAction->setEnabled(false); for (auto* action : list_showColumnActions) { action->setEnabled(true); action->setChecked(true); } } /*! shows/hides the frame with the search/filter widgets */ void ProjectExplorer::toggleFilterWidgets() { if (m_frameFilter->isVisible()) { m_frameFilter->hide(); - toggleFilterAction->setText(i18n("show search/filter options")); + toggleFilterAction->setText(i18n("Show Search/Filter Options")); } else { m_frameFilter->show(); - toggleFilterAction->setText(i18n("hide search/filter options")); + toggleFilterAction->setText(i18n("Hide Search/Filter Options")); } } /*! toggles the menu for the filter/search options */ void ProjectExplorer::toggleFilterOptionsMenu(bool checked) { if (checked) { QMenu menu; menu.addAction(caseSensitiveAction); menu.addAction(matchCompleteWordAction); connect(&menu, SIGNAL(aboutToHide()), bFilterOptions, SLOT(toggle())); menu.exec(bFilterOptions->mapToGlobal(QPoint(0,bFilterOptions->height()))); } } void ProjectExplorer::resizeHeader() { m_treeView->header()->resizeSections(QHeaderView::ResizeToContents); m_treeView->header()->resizeSection(0, m_treeView->header()->sectionSize(0)*1.2); //make the column "Name" somewhat bigger } /*! called when the filter/search text was changend. */ void ProjectExplorer::filterTextChanged(const QString& text) { QModelIndex root = m_treeView->model()->index(0,0); filter(root, text); } bool ProjectExplorer::filter(const QModelIndex& index, const QString& text) { Qt::CaseSensitivity sensitivity = caseSensitiveAction->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; bool matchCompleteWord = matchCompleteWordAction->isChecked(); bool childVisible = false; const int rows = index.model()->rowCount(index); for (int i=0; i(child.internalPointer()); bool visible; if(text.isEmpty()) visible = true; else if (matchCompleteWord) visible = aspect->name().startsWith(text, sensitivity); else visible = aspect->name().contains(text, sensitivity); if (visible) { //current item is visible -> make all its children visible without applying the filter for (int j=0; jrowCount(child); ++j) { m_treeView->setRowHidden(j, child, false); if(text.isEmpty()) filter(child, text); } childVisible = true; } else { //check children items. if one of the children is visible, make the parent (current) item visible too. visible = filter(child, text); if (visible) childVisible = true; } m_treeView->setRowHidden(i, index, !visible); } return childVisible; } void ProjectExplorer::toggleFilterCaseSensitivity() { filterTextChanged(m_leFilter->text()); } void ProjectExplorer::toggleFilterMatchCompleteWord() { filterTextChanged(m_leFilter->text()); } void ProjectExplorer::selectIndex(const QModelIndex& index) { if (m_project->isLoading()) return; if ( !m_treeView->selectionModel()->isSelected(index) ) { m_treeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); m_treeView->setExpanded(index, true); m_treeView->scrollTo(index); } } void ProjectExplorer::deselectIndex(const QModelIndex & index) { if (m_project->isLoading()) return; if ( m_treeView->selectionModel()->isSelected(index) ) m_treeView->selectionModel()->select(index, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); } void ProjectExplorer::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QModelIndex index; QModelIndexList items; AbstractAspect* aspect = 0; //there are four model indices in each row //-> divide by 4 to obtain the number of selected rows (=aspects) items = selected.indexes(); for (int i=0; i(index.internalPointer()); aspect->setSelected(true); } items = deselected.indexes(); for (int i = 0; i < items.size()/4; ++i) { index = items.at(i*4); aspect = static_cast(index.internalPointer()); aspect->setSelected(false); } items = m_treeView->selectionModel()->selectedRows(); QList selectedAspects; for (const QModelIndex& index : items) { aspect = static_cast(index.internalPointer()); selectedAspects<selectionModel()->selectedIndexes(); for (const auto& index : items) m_treeView->setExpanded(index, true); } void ProjectExplorer::collapseSelected() { const QModelIndexList items = m_treeView->selectionModel()->selectedIndexes(); for (const auto& index : items) m_treeView->setExpanded(index, false); } void ProjectExplorer::deleteSelected() { const QModelIndexList items = m_treeView->selectionModel()->selectedIndexes(); if (!items.size()) return; int rc = KMessageBox::warningYesNo( this, i18np("Do you really want to delete the selected object?", "Do you really want to delete the selected %1 objects?", items.size()/4), i18np("Delete selected object", "Delete selected objects", items.size()/4)); if (rc == KMessageBox::No) return; m_project->beginMacro(i18np("Project Explorer: delete %1 selected object", "Project Explorer: delete %1 selected objects", items.size()/4)); for (int i = 0; i < items.size()/4; ++i) { const QModelIndex& index = items.at(i*4); AbstractAspect* aspect = static_cast(index.internalPointer()); aspect->remove(); } m_project->endMacro(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## struct ViewState { Qt::WindowStates state; QRect geometry; }; /** * \brief Save the current state of the tree view * (expanded items and the currently selected item) as XML */ void ProjectExplorer::save(QXmlStreamWriter* writer) const { AspectTreeModel* model = qobject_cast(m_treeView->model()); QList selected; QList expanded; QList withView; QVector viewStates; int currentRow = -1; //row corresponding to the current index in the tree view, -1 for the root element (=project) QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows(); //check whether the project node itself is expanded if (m_treeView->isExpanded(m_treeView->model()->index(0,0))) expanded.push_back(-1); int row = 0; QVector aspects = const_cast(m_project)->children("AbstractAspect", AbstractAspect::Recursive); for (const auto* aspect : aspects) { const QModelIndex& index = model->modelIndexOfAspect(aspect); const AbstractPart* part = dynamic_cast(aspect); if (part && part->hasMdiSubWindow()) { withView.push_back(row); ViewState s = {part->view()->windowState(), part->view()->geometry()}; viewStates.push_back(s); } if (model->rowCount(index)>0 && m_treeView->isExpanded(index)) expanded.push_back(row); if (selectedRows.indexOf(index) != -1) selected.push_back(row); if (index == m_treeView->currentIndex()) currentRow = row; row++; } writer->writeStartElement("state"); writer->writeStartElement("expanded"); for (int i = 0; i < expanded.size(); ++i) writer->writeTextElement("row", QString::number(expanded.at(i))); writer->writeEndElement(); writer->writeStartElement("selected"); for (int i = 0; i < selected.size(); ++i) writer->writeTextElement("row", QString::number(selected.at(i))); writer->writeEndElement(); writer->writeStartElement("view"); for (int i = 0; i < withView.size(); ++i) { writer->writeStartElement("row"); const ViewState& s = viewStates.at(i); writer->writeAttribute( "state", QString::number(s.state) ); writer->writeAttribute( "x", QString::number(s.geometry.x()) ); writer->writeAttribute( "y", QString::number(s.geometry.y()) ); writer->writeAttribute( "width", QString::number(s.geometry.width()) ); writer->writeAttribute( "height", QString::number(s.geometry.height()) ); writer->writeCharacters(QString::number(withView.at(i))); writer->writeEndElement(); } writer->writeEndElement(); writer->writeStartElement("current"); writer->writeTextElement("row", QString::number(currentRow)); writer->writeEndElement(); writer->writeEndElement(); } /** * \brief Load from XML */ bool ProjectExplorer::load(XmlStreamReader* reader) { const AspectTreeModel* model = qobject_cast(m_treeView->model()); const QVector aspects = const_cast(m_project)->children("AbstractAspect", AbstractAspect::Recursive); bool expandedItem = false; bool selectedItem = false; bool viewItem = false; (void)viewItem; // because of a strange g++-warning about unused viewItem bool currentItem = false; QModelIndex currentIndex; QString str; int row; QVector selected; QList expanded; QXmlStreamAttributes attribs; - QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "state") break; if (!reader->isStartElement()) continue; if (reader->name() == "expanded") { expandedItem = true; selectedItem = false; viewItem = false; currentItem = false; } else if (reader->name() == "selected") { expandedItem = false; selectedItem = true; viewItem = false; currentItem = false; } else if (reader->name() == "view") { expandedItem = false; selectedItem = false; viewItem = true; currentItem = false; } else if (reader->name() == "current") { expandedItem = false; selectedItem = false; viewItem = false; currentItem = true; } else if (reader->name() == "row") { attribs = reader->attributes(); row = reader->readElementText().toInt(); QModelIndex index; if (row == -1) index = model->modelIndexOfAspect(m_project); //-1 corresponds tothe project-item (s.a. ProjectExplorer::save()) else if (row >= aspects.size()) continue; else index = model->modelIndexOfAspect(aspects.at(row)); if (expandedItem) expanded.push_back(index); else if (selectedItem) selected.push_back(index); else if (currentItem) currentIndex = index; else if (viewItem) { AbstractPart* part = dynamic_cast(aspects.at(row)); if (!part) continue; //TODO: add error/warning message here? emit currentAspectChanged(part); str = attribs.value("state").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'state'")); + reader->raiseWarning(attributeWarning.subs("state").toString()); else { part->view()->setWindowState(Qt::WindowStates(str.toInt())); part->mdiSubWindow()->setWindowState(Qt::WindowStates(str.toInt())); } if (str != "0") continue; //no geometry settings required for maximized/minimized windows QRect geometry; str = attribs.value("x").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'x'")); + reader->raiseWarning(attributeWarning.subs("x").toString()); else geometry.setX(str.toInt()); str = attribs.value("y").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'y'")); + reader->raiseWarning(attributeWarning.subs("y").toString()); else geometry.setY(str.toInt()); str = attribs.value("width").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'width'")); + reader->raiseWarning(attributeWarning.subs("width").toString()); else geometry.setWidth(str.toInt()); str = attribs.value("height").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.arg("'height'")); + reader->raiseWarning(attributeWarning.subs("height").toString()); else geometry.setHeight(str.toInt()); part->mdiSubWindow()->setGeometry(geometry); } } } for (const auto& index : expanded) { m_treeView->setExpanded(index, true); collapseParents(index, expanded);//collapse all parent indices if they are not expanded } for (const auto& index : selected) m_treeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); m_treeView->setCurrentIndex(currentIndex); m_treeView->scrollTo(currentIndex); //when setting the current index above it gets expanded, collapse all parent indices if they are were not expanded when saved collapseParents(currentIndex, expanded); return true; } void ProjectExplorer::collapseParents(const QModelIndex& index, const QList& expanded) { //root index doesn't have any parents - this case is not caught by the second if-statement below if (index.column()==0 && index.row()==0) return; const QModelIndex parent = index.parent(); if (parent == QModelIndex()) return; if (expanded.indexOf(parent) == -1) m_treeView->collapse(parent); } diff --git a/src/commonfrontend/cantorWorksheet/CantorWorksheetView.cpp b/src/commonfrontend/cantorWorksheet/CantorWorksheetView.cpp index a895b7c3a..be6ee331c 100644 --- a/src/commonfrontend/cantorWorksheet/CantorWorksheetView.cpp +++ b/src/commonfrontend/cantorWorksheet/CantorWorksheetView.cpp @@ -1,239 +1,239 @@ /*************************************************************************** File : CantorWorksheetView.cpp Project : LabPlot Description : View class for CantorWorksheet -------------------------------------------------------------------- Copyright : (C) 2015 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 "CantorWorksheetView.h" #include "backend/cantorWorksheet/CantorWorksheet.h" #include #include #include #include #include #include #include CantorWorksheetView::CantorWorksheetView(CantorWorksheet* worksheet) : QWidget(), m_worksheet(worksheet) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); part = worksheet->part(); if (part) { layout->addWidget(part->widget()); initActions(); initMenus(); connect(m_worksheet, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_worksheet, SIGNAL(statusChanged(Cantor::Session::Status)), this, SLOT(statusChanged(Cantor::Session::Status))); } else { - QLabel* label = new QLabel(i18n("Failed to initialize %1").arg(m_worksheet->backendName())); + QLabel* label = new QLabel(i18n("Failed to initialize %1", m_worksheet->backendName())); label->setAlignment(Qt::AlignHCenter); layout->addWidget(label); } } void CantorWorksheetView::initActions() { QActionGroup* cantorActionGroup = new QActionGroup(this); cantorActionGroup->setExclusive(false); m_restartBackendAction = new QAction(QIcon::fromTheme("system-reboot"), i18n("Restart Backend"), cantorActionGroup); m_restartBackendAction->setData("restart_backend"); m_evaluateWorsheetAction = new QAction(QIcon::fromTheme("system-run"), i18n("Evaluate Worksheet"), cantorActionGroup); m_evaluateWorsheetAction->setData("evaluate_worksheet"); m_evaluateEntryAction = new QAction(i18n("Evaluate Entry"), cantorActionGroup); m_evaluateEntryAction->setShortcut(Qt::SHIFT + Qt::Key_Return); m_evaluateEntryAction->setData("evaluate_current"); m_insertCommandEntryAction = new QAction(i18n("Insert Command Entry"), cantorActionGroup); m_insertCommandEntryAction->setData("insert_command_entry"); m_insertCommandEntryAction->setShortcut(Qt::CTRL + Qt::Key_Return); m_insertTextEntryAction = new QAction(i18n("Insert Text Entry"), cantorActionGroup); m_insertTextEntryAction->setData("insert_text_entry"); - m_insertLatexEntryAction = new QAction(i18n("Insert Latex Entry"), cantorActionGroup); + m_insertLatexEntryAction = new QAction(i18n("Insert LaTeX Entry"), cantorActionGroup); m_insertLatexEntryAction->setData("insert_latex_entry"); m_insertPageBreakAction = new QAction(i18n("Insert Page Break"), cantorActionGroup); m_insertPageBreakAction->setData("insert_page_break_entry"); m_removeCurrentEntryAction = new QAction(i18n("Remove Current Entry"), cantorActionGroup); m_removeCurrentEntryAction->setData("remove_current"); m_computeEigenvectorsAction = new QAction(i18n("Compute Eigenvectors"), cantorActionGroup); m_computeEigenvectorsAction->setData("eigenvectors_assistant"); m_createMattrixAction = new QAction(i18n("Create Matrix"), cantorActionGroup); m_createMattrixAction->setData("creatematrix_assistant"); m_computeEigenvaluesAction = new QAction(i18n("Compute Eigenvalues"), cantorActionGroup); m_computeEigenvaluesAction->setData("eigenvalues_assistant"); m_invertMattrixAction = new QAction(i18n("Invert Matrix"), cantorActionGroup); m_invertMattrixAction->setData("invertmatrix_assistant"); m_differentiationAction = new QAction(i18n("Differentiation"), cantorActionGroup); m_differentiationAction->setData("differentiate_assistant"); m_integrationAction = new QAction(i18n("Integration"), cantorActionGroup); m_integrationAction->setData("integrate_assistant"); m_solveEquationsAction = new QAction(i18n("Solve Equations"), cantorActionGroup); m_solveEquationsAction->setData("solve_assistant"); - m_zoomIn = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom in"), cantorActionGroup); + m_zoomIn = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), cantorActionGroup); m_zoomIn->setData("view_zoom_in"); m_zoomIn->setShortcut(Qt::CTRL+Qt::Key_Plus); - m_zoomOut = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom out"), cantorActionGroup); + m_zoomOut = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), cantorActionGroup); m_zoomOut->setData("view_zoom_out"); m_zoomOut->setShortcut(Qt::CTRL+Qt::Key_Minus); m_find = new QAction(QIcon::fromTheme("edit-find"), i18n("Find"), cantorActionGroup); m_find->setData("edit_find"); m_find->setShortcut(Qt::CTRL+Qt::Key_F); m_replace = new QAction(QIcon::fromTheme("edit-replace"), i18n("Replace"), cantorActionGroup); m_replace->setData("edit_replace"); m_replace->setShortcut(Qt::CTRL+Qt::Key_R); m_lineNumbers = new KToggleAction(i18n("Line Numbers"), cantorActionGroup); m_lineNumbers->setChecked(false); m_lineNumbers->setData("enable_expression_numbers"); m_animateWorksheet = new KToggleAction(i18n("Animate Worksheet"), cantorActionGroup); m_animateWorksheet->setChecked(true); m_animateWorksheet->setData("enable_animations"); - m_latexTypesetting = new KToggleAction(i18n("Latex Typesetting"), cantorActionGroup); + m_latexTypesetting = new KToggleAction(i18n("LaTeX Typesetting"), cantorActionGroup); m_latexTypesetting->setChecked(true); m_latexTypesetting->setData("enable_typesetting"); m_showCompletion = new QAction(i18n("Syntax Completion"), cantorActionGroup); m_showCompletion->setShortcut(Qt::CTRL + Qt::Key_Space); m_showCompletion->setData("show_completion"); connect(cantorActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(triggerCantorAction(QAction*))); } void CantorWorksheetView::initMenus() { m_worksheetMenu = new QMenu("Worksheet", part->widget()); m_worksheetMenu->addAction(m_evaluateWorsheetAction); m_worksheetMenu->addAction(m_evaluateEntryAction); m_worksheetMenu->addAction(m_insertCommandEntryAction); m_worksheetMenu->addAction(m_insertTextEntryAction); m_worksheetMenu->addAction(m_insertLatexEntryAction); m_worksheetMenu->addAction(m_insertPageBreakAction); m_worksheetMenu->addAction(m_removeCurrentEntryAction); m_worksheetMenu->addAction(m_showCompletion); m_linearAlgebraMenu = new QMenu("Linear Algebra", part->widget()); m_linearAlgebraMenu->addAction(m_invertMattrixAction); m_linearAlgebraMenu->addAction(m_createMattrixAction); m_linearAlgebraMenu->addAction(m_computeEigenvectorsAction); m_linearAlgebraMenu->addAction(m_computeEigenvaluesAction); m_calculateMenu = new QMenu("Calculate", part->widget()); m_calculateMenu->addAction(m_solveEquationsAction); m_calculateMenu->addAction(m_integrationAction); m_calculateMenu->addAction(m_differentiationAction); m_settingsMenu = new QMenu("Settings", part->widget()); m_settingsMenu->addAction(m_lineNumbers); m_settingsMenu->addAction(m_animateWorksheet); m_settingsMenu->addAction(m_latexTypesetting); } /*! * Populates the menu \c menu with the CantorWorksheet and CantorWorksheet view relevant actions. * The menu is used * - as the context menu in CantorWorksheetView * - as the "CantorWorksheet menu" in the main menu-bar (called form MainWin) * - as a part of the CantorWorksheet context menu in project explorer */ void CantorWorksheetView::createContextMenu(QMenu* menu) const{ Q_ASSERT(menu); if (!part) return; QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_worksheetMenu); menu->insertMenu(firstAction, m_linearAlgebraMenu); menu->insertMenu(firstAction, m_calculateMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, m_zoomIn); menu->insertAction(firstAction, m_zoomOut); menu->insertSeparator(firstAction); menu->insertAction(firstAction, m_find); menu->insertAction(firstAction, m_replace); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_settingsMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, m_restartBackendAction); menu->insertSeparator(firstAction); } void CantorWorksheetView::fillToolBar(QToolBar* toolbar) { if (!part) return; toolbar->addAction(m_restartBackendAction); toolbar->addAction(m_evaluateWorsheetAction); } /*! * Slot for actions triggered */ void CantorWorksheetView::triggerCantorAction(QAction* action) { QString actionName = action->data().toString(); if(!actionName.isEmpty()) part->action(actionName.toStdString().c_str())->trigger(); } CantorWorksheetView::~CantorWorksheetView() { if (part) part->widget()->setParent(0); } void CantorWorksheetView::statusChanged(Cantor::Session::Status status) { if(status==Cantor::Session::Running) { m_evaluateWorsheetAction->setText(i18n("Interrupt")); m_evaluateWorsheetAction->setIcon(QIcon::fromTheme(QLatin1String("dialog-close"))); emit m_worksheet->statusInfo(i18n("Calculating...")); } else { m_evaluateWorsheetAction->setText(i18n("Evaluate Worksheet")); m_evaluateWorsheetAction->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); emit m_worksheet->statusInfo(i18n("Ready")); } } diff --git a/src/commonfrontend/datapicker/DatapickerImageView.cpp b/src/commonfrontend/datapicker/DatapickerImageView.cpp index 2b199b264..29927be5d 100644 --- a/src/commonfrontend/datapicker/DatapickerImageView.cpp +++ b/src/commonfrontend/datapicker/DatapickerImageView.cpp @@ -1,807 +1,806 @@ /*************************************************************************** File : DatapickerImageView.cpp Project : LabPlot Description : DatapickerImage view for datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "commonfrontend/datapicker/DatapickerImageView.h" #include "backend/worksheet/Worksheet.h" #include "backend/datapicker/DatapickerPoint.h" #include "backend/datapicker/Datapicker.h" #include "backend/datapicker/Transform.h" #include "backend/datapicker/DatapickerCurve.h" #include "backend/datapicker/DatapickerImage.h" #include #include #include #include #include #include #include #include #include #include -#include +#include /** * \class DatapickerImageView * \brief Datapicker/DatapickerImage view */ /*! Constructur of the class. Creates a view for the DatapickerImage \c image and initializes the internal model. */ DatapickerImageView::DatapickerImageView(DatapickerImage* image) : QGraphicsView(), m_image(image), m_datapicker(dynamic_cast(m_image->parentAspect())), m_transform(new Transform()), m_mouseMode(SelectAndEditMode), m_selectionBandIsShown(false), magnificationFactor(0), m_rotationAngle(0), tbZoom(0) { setScene(m_image->scene()); setRenderHint(QPainter::Antialiasing); setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setResizeAnchor(QGraphicsView::AnchorViewCenter); setMinimumSize(16, 16); setFocusPolicy(Qt::StrongFocus); viewport()->setAttribute( Qt::WA_OpaquePaintEvent ); viewport()->setAttribute( Qt::WA_NoSystemBackground ); setCacheMode(QGraphicsView::CacheBackground); initActions(); initMenus(); selectAndEditModeAction->setChecked(true); m_image->setSegmentsHoverEvent(true); setInteractive(true); changeZoom(zoomOriginAction); currentZoomAction=zoomInViewAction; if (m_image->plotPointsType() == DatapickerImage::AxisPoints) setAxisPointsAction->setChecked(true); else if (m_image->plotPointsType() == DatapickerImage::CurvePoints) setCurvePointsAction->setChecked(true); else selectSegmentAction->setChecked(true); handleImageActions(); changeRotationAngle(); //signal/slot connections //for general actions connect( m_image, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*)) ); connect( m_image, SIGNAL(requestUpdate()), this, SLOT(updateBackground()) ); connect( m_image, SIGNAL(requestUpdateActions()), this, SLOT(handleImageActions()) ); connect( m_datapicker, SIGNAL(requestUpdateActions()), this, SLOT(handleImageActions()) ); connect( m_image, SIGNAL(rotationAngleChanged(float)), this, SLOT(changeRotationAngle()) ); } DatapickerImageView::~DatapickerImageView() { delete m_transform; } void DatapickerImageView::initActions() { QActionGroup* zoomActionGroup = new QActionGroup(this); QActionGroup* mouseModeActionGroup = new QActionGroup(this); QActionGroup* plotPointsTypeActionGroup = new QActionGroup(this); navigationActionGroup = new QActionGroup(this); magnificationActionGroup = new QActionGroup(this); //Zoom actions - zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom in"), zoomActionGroup); + zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), zoomActionGroup); zoomInViewAction->setShortcut(Qt::CTRL+Qt::Key_Plus); - zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom out"), zoomActionGroup); + zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), zoomActionGroup); zoomOutViewAction->setShortcut(Qt::CTRL+Qt::Key_Minus); - zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original size"), zoomActionGroup); + zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), zoomActionGroup); zoomOriginAction->setShortcut(Qt::CTRL+Qt::Key_1); - zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to height"), zoomActionGroup); - zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to width"), zoomActionGroup); + zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to Height"), zoomActionGroup); + zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to Width"), zoomActionGroup); // Mouse mode actions selectAndEditModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Edit"), mouseModeActionGroup); selectAndEditModeAction->setCheckable(true); navigationModeAction = new QAction(QIcon::fromTheme("input-mouse"), i18n("Navigate"), mouseModeActionGroup); navigationModeAction->setCheckable(true); zoomSelectionModeAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Select and Zoom"), mouseModeActionGroup); zoomSelectionModeAction->setCheckable(true); selectAndMoveModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Move"), mouseModeActionGroup); selectAndMoveModeAction->setCheckable(true); setAxisPointsAction = new QAction(QIcon::fromTheme("labplot-plot-axis-points"), i18n("Set Axis Points"), plotPointsTypeActionGroup); setAxisPointsAction->setCheckable(true); setCurvePointsAction = new QAction(QIcon::fromTheme("labplot-xy-curve-points"), i18n("Set Curve Points"), plotPointsTypeActionGroup); setCurvePointsAction->setCheckable(true); selectSegmentAction = new QAction(QIcon::fromTheme("labplot-xy-curve-segments"), i18n("Select Curve Segments"), plotPointsTypeActionGroup); selectSegmentAction->setCheckable(true); addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("New Curve"), this); shiftLeftAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left"), navigationActionGroup); shiftLeftAction->setShortcut(Qt::Key_Right); shiftRightAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right"), navigationActionGroup); shiftRightAction->setShortcut(Qt::Key_Left); shiftUpAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Up"), navigationActionGroup); shiftUpAction->setShortcut(Qt::Key_Up); shiftDownAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Down"), navigationActionGroup); shiftDownAction->setShortcut(Qt::Key_Down); noMagnificationAction = new QAction(QIcon::fromTheme("labplot-1x-zoom"), i18n("No Magnification"), magnificationActionGroup); noMagnificationAction->setCheckable(true); noMagnificationAction->setChecked(true); twoTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-2x-zoom"), i18n("2x Magnification"), magnificationActionGroup); twoTimesMagnificationAction->setCheckable(true); threeTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-3x-zoom"), i18n("3x Magnification"), magnificationActionGroup); threeTimesMagnificationAction->setCheckable(true); fourTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-4x-zoom"), i18n("4x Magnification"), magnificationActionGroup); fourTimesMagnificationAction->setCheckable(true); fiveTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-5x-zoom"), i18n("5x Magnification"), magnificationActionGroup); fiveTimesMagnificationAction->setCheckable(true); connect( mouseModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(mouseModeChanged(QAction*)) ); connect( zoomActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeZoom(QAction*)) ); connect( plotPointsTypeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changePointsType(QAction*)) ); connect( addCurveAction, SIGNAL(triggered()), this, SLOT(addCurve()) ); connect( navigationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeSelectedItemsPosition(QAction*)) ); connect( magnificationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(magnificationChanged(QAction*)) ); } void DatapickerImageView::initMenus() { m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); m_viewMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); m_viewMouseModeMenu->addAction(selectAndEditModeAction); m_viewMouseModeMenu->addAction(navigationModeAction); m_viewMouseModeMenu->addAction(zoomSelectionModeAction); m_viewMouseModeMenu->addAction(selectAndMoveModeAction); m_viewImageMenu = new QMenu(i18n("Data Entry Mode"), this); m_viewImageMenu->addAction(setAxisPointsAction); m_viewImageMenu->addAction(setCurvePointsAction); m_viewImageMenu->addAction(selectSegmentAction); m_zoomMenu = new QMenu(i18n("Zoom View"), this); m_zoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_zoomMenu->addAction(zoomInViewAction); m_zoomMenu->addAction(zoomOutViewAction); m_zoomMenu->addAction(zoomOriginAction); m_zoomMenu->addAction(zoomFitPageHeightAction); m_zoomMenu->addAction(zoomFitPageWidthAction); m_navigationMenu = new QMenu(i18n("Move Last Point"), this); m_navigationMenu->addAction(shiftLeftAction); m_navigationMenu->addAction(shiftRightAction); m_navigationMenu->addAction(shiftUpAction); m_navigationMenu->addAction(shiftDownAction); m_magnificationMenu = new QMenu(i18n("Magnification"), this); m_magnificationMenu->setIcon(QIcon::fromTheme("labplot-zoom")); m_magnificationMenu->addAction(noMagnificationAction); m_magnificationMenu->addAction(twoTimesMagnificationAction); m_magnificationMenu->addAction(threeTimesMagnificationAction); m_magnificationMenu->addAction(fourTimesMagnificationAction); m_magnificationMenu->addAction(fiveTimesMagnificationAction); } /*! * Populates the menu \c menu with the image and image-view relevant actions. * The menu is used * - as the context menu in DatapickerImageView * - as the "datapicker menu" in the main menu-bar (called form MainWin) * - as a part of the image context menu in project explorer */ void DatapickerImageView::createContextMenu(QMenu* menu) const { Q_ASSERT(menu); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_viewImageMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, addCurveAction); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_navigationMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_viewMouseModeMenu); menu->insertMenu(firstAction, m_zoomMenu); menu->insertMenu(firstAction, m_magnificationMenu); menu->insertSeparator(firstAction); } void DatapickerImageView::fillToolBar(QToolBar* toolBar) { toolBar->addSeparator(); toolBar->addAction(setAxisPointsAction); toolBar->addAction(setCurvePointsAction); toolBar->addAction(selectSegmentAction); toolBar->addSeparator(); toolBar->addAction(addCurveAction); toolBar->addSeparator(); toolBar->addAction(noMagnificationAction); toolBar->addAction(twoTimesMagnificationAction); toolBar->addAction(threeTimesMagnificationAction); toolBar->addAction(fourTimesMagnificationAction); toolBar->addAction(fiveTimesMagnificationAction); toolBar->addSeparator(); toolBar->addAction(shiftRightAction); toolBar->addAction(shiftLeftAction); toolBar->addAction(shiftUpAction); toolBar->addAction(shiftDownAction); toolBar->addSeparator(); toolBar->addAction(selectAndEditModeAction); toolBar->addAction(navigationModeAction); toolBar->addAction(zoomSelectionModeAction); toolBar->addAction(selectAndMoveModeAction); tbZoom = new QToolButton(toolBar); tbZoom->setPopupMode(QToolButton::MenuButtonPopup); tbZoom->setMenu(m_zoomMenu); tbZoom->setDefaultAction(currentZoomAction); toolBar->addWidget(tbZoom); } void DatapickerImageView::setScene(QGraphicsScene* scene) { QGraphicsView::setScene(scene); setTransform(QTransform()); } void DatapickerImageView::drawForeground(QPainter* painter, const QRectF& rect) { if (m_mouseMode==ZoomSelectionMode && m_selectionBandIsShown) { painter->save(); const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(); painter->setPen(QPen(Qt::black, 5/transform().m11())); painter->drawRect(selRect); painter->setBrush(Qt::blue); painter->setOpacity(0.2); painter->drawRect(selRect); painter->restore(); } QGraphicsView::drawForeground(painter, rect); } void DatapickerImageView::drawBackground(QPainter* painter, const QRectF& rect) { painter->save(); QRectF scene_rect = sceneRect(); if (!scene_rect.contains(rect)) painter->fillRect(rect, Qt::lightGray); //shadow int shadowSize = scene_rect.width()*0.02; QRectF rightShadowRect(scene_rect.right(), scene_rect.top() + shadowSize, shadowSize, scene_rect.height()); QRectF bottomShadowRect(scene_rect.left() + shadowSize, scene_rect.bottom(), scene_rect.width(), shadowSize); painter->fillRect(rightShadowRect.intersected(rect), Qt::darkGray); painter->fillRect(bottomShadowRect.intersected(rect), Qt::darkGray); // canvas if (m_image->isLoaded) { if (m_image->plotImageType() == DatapickerImage::OriginalImage) { QImage todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter->drawImage(scene_rect.topLeft(), todraw); } else if (m_image->plotImageType() == DatapickerImage::ProcessedImage) { QImage todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter->drawImage(scene_rect.topLeft(), todraw); } else { painter->fillRect(scene_rect, Qt::white); } } else { painter->setBrush(QBrush(Qt::gray)); painter->drawRect(scene_rect); } invalidateScene(rect, QGraphicsScene::BackgroundLayer); painter->restore(); } //############################################################################## //#################################### Events ############################### //############################################################################## void DatapickerImageView::wheelEvent(QWheelEvent *event) { if (m_mouseMode == ZoomSelectionMode) { if (event->delta() > 0) scale(1.2, 1.2); else if (event->delta() < 0) scale(1.0/1.2, 1.0/1.2); } else { QGraphicsView::wheelEvent(event); } } void DatapickerImageView::mousePressEvent(QMouseEvent* event) { //prevent the deselection of items when context menu event //was triggered (right button click) if (event->button() == Qt::RightButton) { event->accept(); return; } if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionStart = event->pos(); m_selectionBandIsShown = true; return; } QPointF eventPos = mapToScene(event->pos()); if ( m_mouseMode == SelectAndEditMode && m_image->isLoaded && sceneRect().contains(eventPos) ) { if ( m_image->plotPointsType() == DatapickerImage::AxisPoints ) { int childCount = m_image->childCount(AbstractAspect::IncludeHidden); if (childCount < 3) m_datapicker->addNewPoint(eventPos, m_image); } else if ( m_image->plotPointsType() == DatapickerImage::CurvePoints && m_datapicker->activeCurve() ) { m_datapicker->addNewPoint(eventPos, m_datapicker->activeCurve()); } } // make sure the datapicker (or its currently active curve) is selected in the project explorer if the view was clicked. // We need this for the case when we change from the project-node in the project explorer to the datapicker node by clicking the view. if (m_datapicker->activeCurve() && m_image->plotPointsType() != DatapickerImage::AxisPoints) { m_datapicker->setSelectedInView(false); m_datapicker->activeCurve()->setSelectedInView(true); } else { if (m_datapicker->activeCurve()) m_datapicker->activeCurve()->setSelectedInView(false); m_datapicker->setSelectedInView(true); } QGraphicsView::mousePressEvent(event); } void DatapickerImageView::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionBandIsShown = false; viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized()); //don't zoom if very small region was selected, avoid occasional/unwanted zooming m_selectionEnd = event->pos(); if ( abs(m_selectionEnd.x()-m_selectionStart.x())>20 && abs(m_selectionEnd.y()-m_selectionStart.y())>20 ) fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio); } QGraphicsView::mouseReleaseEvent(event); } void DatapickerImageView::mouseMoveEvent(QMouseEvent* event) { if ( m_mouseMode == SelectAndEditMode || m_mouseMode == ZoomSelectionMode ) { if (m_image->isLoaded) setCursor(Qt::CrossCursor); else setCursor(Qt::ArrowCursor); } else { setCursor(Qt::ArrowCursor); } //show the selection band if (m_selectionBandIsShown) { QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized(); m_selectionEnd = event->pos(); rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized()); int penWidth = 5/transform().m11(); rect.setX(rect.x()-penWidth); rect.setY(rect.y()-penWidth); rect.setHeight(rect.height()+2*penWidth); rect.setWidth(rect.width()+2*penWidth); viewport()->repaint(rect); return; } QPointF pos = mapToScene(event->pos()); //show the current coordinates under the mouse cursor in the status bar if (m_image->plotPointsType() == DatapickerImage::CurvePoints) { QVector3D logicalPos = m_transform->mapSceneToLogical(pos, m_image->axisPoints()); if (m_image->axisPoints().type == DatapickerImage::Ternary) { emit statusInfo( "a =" + QString::number(logicalPos.x()) + ", b =" + QString::number(logicalPos.y()) + ", c =" + QString::number(logicalPos.z())); } else { QString xLabel('x'); QString yLabel('y'); if (m_image->axisPoints().type == DatapickerImage::PolarInDegree) { xLabel = 'r'; yLabel = "y(deg)"; } else if (m_image->axisPoints().type == DatapickerImage::PolarInRadians) { xLabel = 'r'; yLabel = "y(rad)"; } if (m_datapicker->activeCurve()) { - QString statusText = m_datapicker->name() + ", " + i18n("active curve") + " \"" + m_datapicker->activeCurve()->name() + '"'; - statusText += ": " + xLabel + '=' + QString::number(logicalPos.x()) + ", " + yLabel + '=' + QString::number(logicalPos.y()); + QString statusText = i18n("%1, active curve \"%2\": %3=%4, %5=%6", m_datapicker->name(), m_datapicker->activeCurve()->name(), xLabel, QString::number(logicalPos.x()), yLabel, QString::number(logicalPos.y())); emit statusInfo(statusText); } } } //show the magnification window if ( magnificationFactor && m_mouseMode == SelectAndEditMode && m_image->isLoaded && sceneRect().contains(pos) && m_image->plotPointsType() != DatapickerImage::SegmentPoints ) { if (!m_image->m_magnificationWindow) { // m_image->m_magnificationWindow = new QGraphicsPixmapItem(0, scene()); m_image->m_magnificationWindow = new QGraphicsPixmapItem; scene()->addItem(m_image->m_magnificationWindow); m_image->m_magnificationWindow->setZValue(std::numeric_limits::max()); } m_image->m_magnificationWindow->setVisible(false); //copy the part of the view to be shown magnified const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Centimeter)/transform().m11(); const QRectF copyRect(pos.x() - size/(2*magnificationFactor), pos.y() - size/(2*magnificationFactor), size/magnificationFactor, size/magnificationFactor); QPixmap px = QPixmap::grabWidget(this, mapFromScene(copyRect).boundingRect()); px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); //draw the bounding rect QPainter painter(&px); const QPen pen = QPen(Qt::lightGray, 2/transform().m11()); painter.setPen(pen); QRect rect = px.rect(); rect.setWidth(rect.width()-pen.widthF()/2); rect.setHeight(rect.height()-pen.widthF()/2); painter.drawRect(rect); //set the pixmap m_image->m_magnificationWindow->setPixmap(px); m_image->m_magnificationWindow->setPos(pos.x()- px.width()/2, pos.y()- px.height()/2); m_image->m_magnificationWindow->setVisible(true); } else if (m_image->m_magnificationWindow) { m_image->m_magnificationWindow->setVisible(false); } QGraphicsView::mouseMoveEvent(event); } void DatapickerImageView::contextMenuEvent(QContextMenuEvent* e) { Q_UNUSED(e); //no need to propagate the event to the scene and graphics items QMenu *menu = new QMenu(this); this->createContextMenu(menu); menu->exec(QCursor::pos()); } //############################################################################## //#################################### SLOTs ############################### //############################################################################## void DatapickerImageView::changePointsType(QAction* action) { if (action==setAxisPointsAction) m_image->setPlotPointsType(DatapickerImage::AxisPoints); else if (action==setCurvePointsAction) m_image->setPlotPointsType(DatapickerImage::CurvePoints); else if (action==selectSegmentAction) m_image->setPlotPointsType(DatapickerImage::SegmentPoints); } void DatapickerImageView::changeZoom(QAction* action) { if (action==zoomInViewAction) { scale(1.2, 1.2); } else if (action==zoomOutViewAction) { scale(1.0/1.2, 1.0/1.2); } else if (action==zoomOriginAction) { static const float hscale = QApplication::desktop()->physicalDpiX()/(25.4*Worksheet::convertToSceneUnits(1,Worksheet::Millimeter)); static const float vscale = QApplication::desktop()->physicalDpiY()/(25.4*Worksheet::convertToSceneUnits(1,Worksheet::Millimeter)); setTransform(QTransform::fromScale(hscale, vscale)); m_rotationAngle = 0; } else if (action==zoomFitPageWidthAction) { float scaleFactor = viewport()->width()/scene()->sceneRect().width(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); m_rotationAngle = 0; } else if (action==zoomFitPageHeightAction) { float scaleFactor = viewport()->height()/scene()->sceneRect().height(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); m_rotationAngle = 0; } currentZoomAction=action; if (tbZoom) tbZoom->setDefaultAction(action); //change and set angle if tranform reset changeRotationAngle(); } void DatapickerImageView::changeSelectedItemsPosition(QAction* action) { if (scene()->selectedItems().isEmpty()) return; QPointF shift(0, 0); if (action == shiftLeftAction) shift.setX(1); else if (action == shiftRightAction) shift.setX(-1); else if (action == shiftUpAction) shift.setY(-1); else if (action == shiftDownAction) shift.setY(1); m_image->beginMacro(i18n("%1: change position of selected DatapickerPoints.", m_image->name())); const QVector axisPoints = m_image->children(AbstractAspect::IncludeHidden); for (auto* point : axisPoints) { if (!point->graphicsItem()->isSelected()) continue; QPointF newPos = point->position(); newPos = newPos + shift; point->setPosition(newPos); int pointIndex = m_image->indexOfChild(point, AbstractAspect::IncludeHidden); if (pointIndex == -1) continue; DatapickerImage::ReferencePoints points = m_image->axisPoints(); points.scenePos[pointIndex].setX(point->position().x()); points.scenePos[pointIndex].setY(point->position().y()); m_image->setUndoAware(false); m_image->setAxisPoints(points); m_image->setUndoAware(true); } for (auto* curve : m_image->parentAspect()->children()) { for (auto* point : curve->children(AbstractAspect::IncludeHidden)) { if (!point->graphicsItem()->isSelected()) continue; QPointF newPos = point->position(); newPos = newPos + shift; point->setPosition(newPos); } } m_image->endMacro(); } void DatapickerImageView::mouseModeChanged(QAction* action) { if (action==selectAndEditModeAction) { m_mouseMode = SelectAndEditMode; setInteractive(true); setDragMode(QGraphicsView::NoDrag); m_image->setSegmentsHoverEvent(true); } else if (action==navigationModeAction) { m_mouseMode = NavigationMode; setInteractive(false); setDragMode(QGraphicsView::ScrollHandDrag); m_image->setSegmentsHoverEvent(false); } else if (action==zoomSelectionModeAction) { m_mouseMode = ZoomSelectionMode; setInteractive(false); setDragMode(QGraphicsView::NoDrag); m_image->setSegmentsHoverEvent(false); } else { m_mouseMode = SelectAndMoveMode; setInteractive(true); setDragMode(QGraphicsView::NoDrag); m_image->setSegmentsHoverEvent(false); } } void DatapickerImageView::magnificationChanged(QAction* action) { if (action==noMagnificationAction) magnificationFactor = 0; else if (action==twoTimesMagnificationAction) magnificationFactor = 2; else if (action==threeTimesMagnificationAction) magnificationFactor = 3; else if (action==fourTimesMagnificationAction) magnificationFactor = 4; else if (action==fiveTimesMagnificationAction) magnificationFactor = 5; } void DatapickerImageView::addCurve() { m_datapicker->beginMacro(i18n("%1: add new curve.", m_datapicker->name())); DatapickerCurve* curve = new DatapickerCurve(i18n("Curve")); curve->addDatasheet(m_image->axisPoints().type); m_datapicker->addChild(curve); m_datapicker->endMacro(); } void DatapickerImageView::changeRotationAngle() { this->rotate(m_rotationAngle); this->rotate(-m_image->rotationAngle()); m_rotationAngle = m_image->rotationAngle(); updateBackground(); } void DatapickerImageView::handleImageActions() { if (m_image->isLoaded) { magnificationActionGroup->setEnabled(true); setAxisPointsAction->setEnabled(true); int pointsCount = m_image->childCount(AbstractAspect::IncludeHidden); if (pointsCount>0) navigationActionGroup->setEnabled(true); else navigationActionGroup->setEnabled(false); if (pointsCount > 2) { addCurveAction->setEnabled(true); if (m_datapicker->activeCurve()) { setCurvePointsAction->setEnabled(true); selectSegmentAction->setEnabled(true); } else { setCurvePointsAction->setEnabled(false); selectSegmentAction->setEnabled(false); } } else { addCurveAction->setEnabled(false); setCurvePointsAction->setEnabled(false); selectSegmentAction->setEnabled(false); if (m_image->plotPointsType() != DatapickerImage::AxisPoints) { m_image->setUndoAware(false); m_image->setPlotPointsType(DatapickerImage::AxisPoints); m_image->setUndoAware(true); } } } else { navigationActionGroup->setEnabled(false); magnificationActionGroup->setEnabled(false); setAxisPointsAction->setEnabled(false); addCurveAction->setEnabled(false); setCurvePointsAction->setEnabled(false); selectSegmentAction->setEnabled(false); } } void DatapickerImageView::exportToFile(const QString& path, const WorksheetView::ExportFormat format, const int resolution) { QRectF sourceRect; sourceRect = scene()->sceneRect(); //print if (format==WorksheetView::Pdf) { QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(path); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); printer.setPaperSize( QSizeF(w, h), QPrinter::Millimeter); printer.setPageMargins(0,0,0,0, QPrinter::Millimeter); printer.setPrintRange(QPrinter::PageRange); printer.setCreator( QLatin1String("LabPlot ") + LVERSION ); QPainter painter(&printer); painter.setRenderHint(QPainter::Antialiasing); QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); painter.begin(&printer); exportPaint(&painter, targetRect, sourceRect); painter.end(); } else if (format==WorksheetView::Svg) { QSvgGenerator generator; generator.setFileName(path); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*QApplication::desktop()->physicalDpiX()/25.4; h = h*QApplication::desktop()->physicalDpiY()/25.4; generator.setSize(QSize(w, h)); QRectF targetRect(0, 0, w, h); generator.setViewBox(targetRect); QPainter painter; painter.begin(&generator); exportPaint(&painter, targetRect, sourceRect); painter.end(); } else { //PNG //TODO add all formats supported by Qt in QImage int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*resolution/25.4; h = h*resolution/25.4; QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QRectF targetRect(0, 0, w, h); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect); painter.end(); image.save(path, "png"); } } void DatapickerImageView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect) { painter->save(); painter->scale(targetRect.width()/sourceRect.width(), targetRect.height()/sourceRect.height()); drawBackground(painter, sourceRect); painter->restore(); m_image->setPrinting(true); scene()->render(painter, QRectF(), sourceRect); m_image->setPrinting(false); } void DatapickerImageView::print(QPrinter* printer) { const QRectF scene_rect = sceneRect(); int w = Worksheet::convertFromSceneUnits(scene_rect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(scene_rect.height(), Worksheet::Millimeter); printer->setPaperSize( QSizeF(w, h), QPrinter::Millimeter); printer->setPageMargins(0,0,0,0, QPrinter::Millimeter); printer->setPrintRange(QPrinter::PageRange); printer->setCreator( QString("LabPlot ") + LVERSION ); QPainter painter(printer); QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); painter.setRenderHint(QPainter::Antialiasing); painter.begin(printer); painter.save(); painter.scale(targetRect.width()/scene_rect.width(), targetRect.height()/scene_rect.height()); // canvas if (m_image->isLoaded) { if (m_image->plotImageType() == DatapickerImage::OriginalImage) { QImage todraw = m_image->originalPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter.drawImage(scene_rect.topLeft(), todraw); } else if (m_image->plotImageType() == DatapickerImage::ProcessedImage) { QImage todraw = m_image->processedPlotImage.scaled(scene_rect.width(), scene_rect.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); painter.drawImage(scene_rect.topLeft(), todraw); } else { painter.fillRect(scene_rect, Qt::white); } } else { painter.setBrush(QBrush(Qt::gray)); painter.drawRect(scene_rect); } painter.restore(); m_image->setPrinting(true); scene()->render(&painter, QRectF(), scene_rect); m_image->setPrinting(false); painter.end(); } void DatapickerImageView::updateBackground() { invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer); } diff --git a/src/commonfrontend/datapicker/DatapickerView.cpp b/src/commonfrontend/datapicker/DatapickerView.cpp index c3b016a21..4d0bb8bb3 100644 --- a/src/commonfrontend/datapicker/DatapickerView.cpp +++ b/src/commonfrontend/datapicker/DatapickerView.cpp @@ -1,216 +1,216 @@ /*************************************************************************** File : DatapickerView.cpp Project : LabPlot Description : View class for Datapicker -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "DatapickerView.h" #include "backend/datapicker/Datapicker.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/datapicker/DatapickerImage.h" #include "commonfrontend/workbook/WorkbookView.h" #include "backend/datapicker/DatapickerCurve.h" #include "commonfrontend/datapicker/DatapickerImageView.h" #include #include #include -#include +#include /*! \class DatapickerView \brief View class for Datapicker \ingroup commonfrontend */ DatapickerView::DatapickerView(Datapicker* datapicker) : QWidget(), m_tabWidget(new TabWidget(this)), m_datapicker(datapicker), lastSelectedIndex(0) { m_tabWidget->setTabPosition(QTabWidget::South); m_tabWidget->setTabShape(QTabWidget::Rounded); // m_tabWidget->setMovable(true); m_tabWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_tabWidget->setMinimumSize(600, 600); QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_tabWidget); //add tab for each children view m_initializing = true; for (const auto* aspect : m_datapicker->children(AbstractAspect::IncludeHidden)) { handleAspectAdded(aspect); for (const auto* child : aspect->children()) { handleAspectAdded(child); } } m_initializing = false; //SIGNALs/SLOTs connect(m_datapicker, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(handleDescriptionChanged(const AbstractAspect*))); connect(m_datapicker, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(handleAspectAdded(const AbstractAspect*))); connect(m_datapicker, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(handleAspectAboutToBeRemoved(const AbstractAspect*))); connect(m_datapicker, SIGNAL(datapickerItemSelected(int)), this, SLOT(itemSelected(int))); connect(m_tabWidget, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); connect(m_tabWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTabContextMenu(QPoint))); connect(m_tabWidget, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); } DatapickerView::~DatapickerView() { //delete all children views here, its own view will be deleted in ~AbstractPart() for (const auto* aspect : m_datapicker->children(AbstractAspect::IncludeHidden)) { for (const auto* child : aspect->children()) { const AbstractPart* part = dynamic_cast(child); if (part) part->deleteView(); } const AbstractPart* part = dynamic_cast(aspect); if (part) part->deleteView(); } } void DatapickerView::fillToolBar(QToolBar* toolBar) { DatapickerImageView* view = dynamic_cast(m_datapicker->image()->view()); view->fillToolBar(toolBar); } void DatapickerView::createContextMenu(QMenu* menu) const { Q_ASSERT(menu); m_datapicker->image()->createContextMenu(menu); } int DatapickerView::currentIndex() const { return m_tabWidget->currentIndex(); } //############################################################################## //######################### Private slots #################################### //############################################################################## /*! called when the current tab was changed. Propagates the selection of \c Spreadsheet or of a \c DatapickerImage object to \c Datapicker. */ void DatapickerView::tabChanged(int index) { if (m_initializing) return; if (index==-1) return; m_datapicker->setChildSelectedInView(lastSelectedIndex, false); m_datapicker->setChildSelectedInView(index, true); lastSelectedIndex = index; } void DatapickerView::tabMoved(int from, int to) { Q_UNUSED(from); Q_UNUSED(to); //TODO: // AbstractAspect* aspect = m_datapicker->child(to); // if (aspect) { // m_tabMoving = true; // AbstractAspect* sibling = m_datapicker->child(from); // qDebug()<<"insert: " << to << " " << aspect->name() << ", " << from << " " << sibling->name(); // aspect->remove(); // m_datapicker->insertChildBefore(aspect, sibling); // qDebug()<<"inserted"; // m_tabMoving = false; // } } void DatapickerView::itemSelected(int index) { m_initializing = true; m_tabWidget->setCurrentIndex(index); m_initializing = false; } void DatapickerView::showTabContextMenu(QPoint point) { QMenu* menu = 0; AbstractAspect* aspect = m_datapicker->child(m_tabWidget->currentIndex(), AbstractAspect::IncludeHidden); Spreadsheet* spreadsheet = dynamic_cast(aspect); if (spreadsheet) { menu = spreadsheet->createContextMenu(); } else { DatapickerImage* image = dynamic_cast(aspect); if (image) menu = image->createContextMenu(); } if (menu) menu->exec(m_tabWidget->mapToGlobal(point)); } void DatapickerView::handleDescriptionChanged(const AbstractAspect* aspect) { int index = -1; QString name; if (aspect->parentAspect() == m_datapicker) { //datapicker curve was renamed index= m_datapicker->indexOfChild(aspect, AbstractAspect::IncludeHidden); name = aspect->name() + ": " + aspect->children().constFirst()->name(); } else { //data spreadsheet was renamed or one of its columns, which is not relevant here index = m_datapicker->indexOfChild(aspect->parentAspect(), AbstractAspect::IncludeHidden); name = aspect->parentAspect()->name() + ": " + aspect->name(); } if (index != -1) m_tabWidget->setTabText(index, name); } void DatapickerView::handleAspectAdded(const AbstractAspect* aspect) { int index; const AbstractPart* part; QString name; if (dynamic_cast(aspect)) { index = 0; part = dynamic_cast(aspect); name = aspect->name(); } else if (dynamic_cast(aspect)) { index = m_datapicker->indexOfChild(aspect, AbstractAspect::IncludeHidden); const Spreadsheet* spreadsheet = dynamic_cast(aspect->child(0)); Q_ASSERT(spreadsheet); part = dynamic_cast(spreadsheet); name = aspect->name() + ": " + spreadsheet->name(); } else { return; } m_tabWidget->insertTab(index, part->view(), name); m_tabWidget->setTabIcon(m_tabWidget->count(), aspect->icon()); } void DatapickerView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const DatapickerCurve* curve = dynamic_cast(aspect); if (curve) { int index = m_datapicker->indexOfChild(aspect, AbstractAspect::IncludeHidden); m_tabWidget->removeTab(index); } } diff --git a/src/commonfrontend/matrix/MatrixView.cpp b/src/commonfrontend/matrix/MatrixView.cpp index e1ba7858e..23f868abb 100644 --- a/src/commonfrontend/matrix/MatrixView.cpp +++ b/src/commonfrontend/matrix/MatrixView.cpp @@ -1,1481 +1,1481 @@ /*************************************************************************** File : MatrixView.cpp Project : LabPlot Description : View class for Matrix -------------------------------------------------------------------- Copyright : (C) 2008-2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "commonfrontend/matrix/MatrixView.h" #include "backend/matrix/Matrix.h" #include "backend/matrix/MatrixModel.h" #include "backend/matrix/matrixcommands.h" #include "backend/lib/macros.h" #include "backend/core/column/Column.h" #include "backend/core/column/ColumnPrivate.h" #include "kdefrontend/matrix/MatrixFunctionDialog.h" #include "kdefrontend/spreadsheet/StatisticsDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include MatrixView::MatrixView(Matrix* matrix) : QWidget(), m_stackedWidget(new QStackedWidget(this)), m_tableView(new QTableView(this)), m_imageLabel(new QLabel(this)), m_matrix(matrix), m_model(new MatrixModel(matrix)), m_imageIsDirty(true) { init(); //resize the view to show a 10x10 region of the matrix. //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_matrix->isLoading()) { int w = m_tableView->horizontalHeader()->sectionSize(0)*10 + m_tableView->verticalHeader()->width(); int h = m_tableView->verticalHeader()->sectionSize(0)*10 + m_tableView->horizontalHeader()->height(); resize(w+50, h+50); } } MatrixView::~MatrixView() { delete m_model; } MatrixModel* MatrixView::model() const { return m_model; } void MatrixView::init() { initActions(); connectActions(); initMenus(); QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding)); setFocusPolicy(Qt::StrongFocus); setFocus(); installEventFilter(this); layout->addWidget(m_stackedWidget); //table data view m_tableView->setModel(m_model); m_stackedWidget->addWidget(m_tableView); //horizontal header QHeaderView* h_header = m_tableView->horizontalHeader(); h_header->setMovable(false); h_header->installEventFilter(this); //vertical header QHeaderView* v_header = m_tableView->verticalHeader(); v_header->setMovable(false); v_header->installEventFilter(this); //set the header sizes to the (potentially user customized) sizes stored in Matrix adjustHeaders(); //image view QScrollArea* area = new QScrollArea(this); m_stackedWidget->addWidget(area); area->setWidget(m_imageLabel); //SLOTs connect(m_matrix, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_model, SIGNAL(changed()), this, SLOT(matrixDataChanged())); //keyboard shortcuts QShortcut* sel_all = new QShortcut(QKeySequence(tr("Ctrl+A", "Matrix: select all")), m_tableView); connect(sel_all, SIGNAL(activated()), m_tableView, SLOT(selectAll())); //TODO: add shortcuts for copy&paste, //for a single shortcut we need to descriminate between copy&paste for columns, rows or selected cells. } void MatrixView::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_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); // matrix related actions QActionGroup* viewActionGroup = new QActionGroup(this); viewActionGroup->setExclusive(true); action_data_view = new QAction(QIcon::fromTheme("labplot-matrix"), i18n("Data"), viewActionGroup); action_data_view->setCheckable(true); action_data_view->setChecked(true); action_image_view = new QAction(QIcon::fromTheme("image-x-generic"), i18n("Image"), viewActionGroup); action_image_view->setCheckable(true); connect(viewActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(switchView(QAction*))); action_fill_function = new QAction(QIcon::fromTheme(""), i18n("Function Values"), this); action_fill_const = new QAction(QIcon::fromTheme(""), i18n("Const Values"), this); action_clear_matrix = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Matrix"), this); action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this); action_transpose = new QAction(i18n("&Transpose"), this); action_mirror_horizontally = new QAction(QIcon::fromTheme("object-flip-horizontal"), i18n("Mirror &Horizontally"), this); action_mirror_vertically = new QAction(QIcon::fromTheme("object-flip-vertical"), i18n("Mirror &Vertically"), this); // action_duplicate = new QAction(i18nc("duplicate matrix", "&Duplicate"), this); //TODO //icon QActionGroup* headerFormatActionGroup = new QActionGroup(this); headerFormatActionGroup->setExclusive(true); action_header_format_1= new QAction(i18n("Rows and Columns"), headerFormatActionGroup); action_header_format_1->setCheckable(true); action_header_format_2= new QAction(i18n("xy-Values"), headerFormatActionGroup); action_header_format_2->setCheckable(true); action_header_format_3= new QAction(i18n("Rows, Columns and xy-Values"), headerFormatActionGroup); action_header_format_3->setCheckable(true); connect(headerFormatActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(headerFormatChanged(QAction*))); // column related actions action_add_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("&Add Columns"), this); action_insert_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("&Insert Empty Columns"), this); action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remo&ve Columns"), this); action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Columns"), this); action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); // row related actions action_add_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above"), i18n("&Add Rows"), this); action_insert_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("&Insert Empty Rows"), this); action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Rows"), this); action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Rows"), this); action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); } void MatrixView::connectActions() { // selection related actions connect(action_cut_selection, SIGNAL(triggered()), this, SLOT(cutSelection())); connect(action_copy_selection, SIGNAL(triggered()), this, SLOT(copySelection())); connect(action_paste_into_selection, SIGNAL(triggered()), this, SLOT(pasteIntoSelection())); connect(action_clear_selection, SIGNAL(triggered()), this, SLOT(clearSelectedCells())); connect(action_select_all, SIGNAL(triggered()), m_tableView, SLOT(selectAll())); // matrix related actions connect(action_fill_function, SIGNAL(triggered()), this, SLOT(fillWithFunctionValues())); connect(action_fill_const, SIGNAL(triggered()), this, SLOT(fillWithConstValues())); connect(action_go_to_cell, SIGNAL(triggered()), this, SLOT(goToCell())); //connect(action_duplicate, SIGNAL(triggered()), this, SLOT(duplicate())); connect(action_clear_matrix, SIGNAL(triggered()), m_matrix, SLOT(clear())); connect(action_transpose, SIGNAL(triggered()), m_matrix, SLOT(transpose())); connect(action_mirror_horizontally, SIGNAL(triggered()), m_matrix, SLOT(mirrorHorizontally())); connect(action_mirror_vertically, SIGNAL(triggered()), m_matrix, SLOT(mirrorVertically())); // column related actions connect(action_add_columns, SIGNAL(triggered()), this, SLOT(addColumns())); connect(action_insert_columns, SIGNAL(triggered()), this, SLOT(insertEmptyColumns())); connect(action_remove_columns, SIGNAL(triggered()), this, SLOT(removeSelectedColumns())); connect(action_clear_columns, SIGNAL(triggered()), this, SLOT(clearSelectedColumns())); connect(action_statistics_columns, SIGNAL(triggered()), this, SLOT(showColumnStatistics())); // row related actions connect(action_add_rows, SIGNAL(triggered()), this, SLOT(addRows())); connect(action_insert_rows, SIGNAL(triggered()), this, SLOT(insertEmptyRows())); connect(action_remove_rows, SIGNAL(triggered()), this, SLOT(removeSelectedRows())); connect(action_clear_rows, SIGNAL(triggered()), this, SLOT(clearSelectedRows())); connect(action_statistics_rows, SIGNAL(triggered()), this, SLOT(showRowStatistics())); } void MatrixView::initMenus() { //selection menu m_selectionMenu = new QMenu(i18n("Selection"), this); m_selectionMenu->addAction(action_cut_selection); m_selectionMenu->addAction(action_copy_selection); m_selectionMenu->addAction(action_paste_into_selection); m_selectionMenu->addAction(action_clear_selection); //column menu m_columnMenu = new QMenu(this); m_columnMenu->addAction(action_insert_columns); m_columnMenu->addAction(action_remove_columns); m_columnMenu->addAction(action_clear_columns); m_columnMenu->addAction(action_statistics_columns); //row menu m_rowMenu = new QMenu(this); m_rowMenu->addAction(action_insert_rows); m_rowMenu->addAction(action_remove_rows); m_rowMenu->addAction(action_clear_rows); m_rowMenu->addAction(action_statistics_rows); //matrix menu m_matrixMenu = new QMenu(this); m_matrixMenu->addMenu(m_selectionMenu); m_matrixMenu->addSeparator(); QMenu* submenu = new QMenu(i18n("Generate Data"), this); submenu->addAction(action_fill_const); submenu->addAction(action_fill_function); m_matrixMenu->addMenu(submenu); m_matrixMenu->addSeparator(); submenu = new QMenu(i18n("View"), this); submenu->addAction(action_data_view); submenu->addAction(action_image_view); m_matrixMenu->addMenu(submenu); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_select_all); m_matrixMenu->addAction(action_clear_matrix); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_transpose); m_matrixMenu->addAction(action_mirror_horizontally); m_matrixMenu->addAction(action_mirror_vertically); m_matrixMenu->addSeparator(); - m_headerFormatMenu = new QMenu(i18n("Header format"), this); + m_headerFormatMenu = new QMenu(i18n("Header Format"), this); m_headerFormatMenu->addAction(action_header_format_1); m_headerFormatMenu->addAction(action_header_format_2); m_headerFormatMenu->addAction(action_header_format_3); m_matrixMenu->addMenu(m_headerFormatMenu); m_matrixMenu->addSeparator(); m_matrixMenu->addAction(action_go_to_cell); } /*! * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. * The menu is used * - as the context menu in MatrixView * - as the "matrix menu" in the main menu-bar (called form MainWin) * - as a part of the matrix context menu in project explorer */ void MatrixView::createContextMenu(QMenu* menu) const { Q_ASSERT(menu); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_selectionMenu); menu->insertSeparator(firstAction); QMenu* submenu = new QMenu(i18n("Generate Data"), const_cast(this)); submenu->addAction(action_fill_const); submenu->addAction(action_fill_function); menu->insertMenu(firstAction, submenu); menu->insertSeparator(firstAction); submenu = new QMenu(i18n("View"), const_cast(this)); submenu->addAction(action_data_view); submenu->addAction(action_image_view); menu->insertMenu(firstAction, submenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_select_all); menu->insertAction(firstAction, action_clear_matrix); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_transpose); menu->insertAction(firstAction, action_mirror_horizontally); menu->insertAction(firstAction, action_mirror_vertically); menu->insertSeparator(firstAction); // menu->insertAction(firstAction, action_duplicate); menu->insertMenu(firstAction, m_headerFormatMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_go_to_cell); menu->insertSeparator(firstAction); } /*! set the row and column size to the saved sizes. */ void MatrixView::adjustHeaders() { QHeaderView* h_header = m_tableView->horizontalHeader(); QHeaderView* v_header = m_tableView->verticalHeader(); disconnect(v_header, &QHeaderView::sectionResized, this, &MatrixView::handleVerticalSectionResized); disconnect(h_header, &QHeaderView::sectionResized, this, &MatrixView::handleHorizontalSectionResized); //resize columns to the saved sizes or to fit the contents if the widht is 0 int cols = m_matrix->columnCount(); for (int i=0; icolumnWidth(i) == 0) m_tableView->resizeColumnToContents(i); else m_tableView->setColumnWidth(i, m_matrix->columnWidth(i)); } //resize rows to the saved sizes or to fit the contents if the height is 0 int rows = m_matrix->rowCount(); for (int i=0; irowHeight(i) == 0) m_tableView->resizeRowToContents(i); else m_tableView->setRowHeight(i, m_matrix->rowHeight(i)); } connect(v_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleVerticalSectionResized(int,int,int))); connect(h_header, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleHorizontalSectionResized(int,int,int))); } /*! Resizes the headers/columns to fit the new content. Called on changes of the header format in Matrix. */ void MatrixView::resizeHeaders() { m_tableView->resizeColumnsToContents(); m_tableView->resizeRowsToContents(); if (m_matrix->headerFormat() == Matrix::HeaderRowsColumns) action_header_format_1->setChecked(true); else if (m_matrix->headerFormat() == Matrix::HeaderValues) action_header_format_2->setChecked(true); else action_header_format_3->setChecked(true); } /*! Returns how many columns are selected. If full is true, this function only returns the number of fully selected columns. */ int MatrixView::selectedColumnCount(bool full) const { int count = 0; int cols = m_matrix->columnCount(); for (int i=0; iselectionModel()->isColumnSelected(col, QModelIndex()); else return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex()); } /*! Return how many rows are (at least partly) selected If full is true, this function only returns the number of fully selected rows. */ int MatrixView::selectedRowCount(bool full) const { int count = 0; int rows = m_matrix->rowCount(); for (int i=0; i < rows; i++) if (isRowSelected(i, full)) count++; return count; } /*! Returns true if row \c row is selected; otherwise false If full is true, this function only returns true if the whole row is selected. */ bool MatrixView::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 full is true, this function only looks for fully selected columns. */ int MatrixView::firstSelectedColumn(bool full) const { int cols = m_matrix->columnCount(); for (int i=0; icolumnCount(); 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 full is true, this function only looks for fully selected rows. */ int MatrixView::firstSelectedRow(bool full) const { int rows = m_matrix->rowCount(); for (int i=0; i < rows; i++) { if (isRowSelected(i, full)) return i; } return -1; } /*! Return the index of the last selected row If full is true, this function only looks for fully selected rows. */ int MatrixView::lastSelectedRow(bool full) const { int rows = m_matrix->rowCount(); for (int i=rows-1; i >= 0; i--) if (isRowSelected(i, full)) return i; return -2; } bool MatrixView::isCellSelected(int row, int col) const { if (row < 0 || col < 0 || row >= m_matrix->rowCount() || col >= m_matrix->columnCount()) return false; return m_tableView->selectionModel()->isSelected(m_model->index(row, col)); } void MatrixView::setCellSelected(int row, int col) { m_tableView->selectionModel()->select(m_model->index(row, col), QItemSelectionModel::Select); } void MatrixView::setCellsSelected(int first_row, int first_col, int last_row, int last_col) { 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), QItemSelectionModel::SelectCurrent); } /*! Determine the current cell (-1 if no cell is designated as the current) */ void MatrixView::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 MatrixView::eventFilter(QObject * watched, QEvent * event) { if (event->type() == QEvent::ContextMenu) { QContextMenuEvent* cm_event = static_cast(event); QPoint global_pos = cm_event->globalPos(); if (watched == m_tableView->verticalHeader()) m_rowMenu->exec(global_pos); else if (watched == m_tableView->horizontalHeader()) m_columnMenu->exec(global_pos); else if (watched == this) m_matrixMenu->exec(global_pos); else return QWidget::eventFilter(watched, event); return true; } else return QWidget::eventFilter(watched, event); } void MatrixView::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) advanceCell(); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## /*! Advance current cell after [Return] or [Enter] was pressed */ void MatrixView::advanceCell() { QModelIndex idx = m_tableView->currentIndex(); if (idx.row()+1 < m_matrix->rowCount()) m_tableView->setCurrentIndex(idx.sibling(idx.row()+1, idx.column())); } void MatrixView::goToCell() { bool ok; int col = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_matrix->columnCount(), 1, &ok); if (!ok) return; int row = QInputDialog::getInteger(0, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_matrix->rowCount(), 1, &ok); if (!ok) return; goToCell(row-1, col-1); } void MatrixView::goToCell(int row, int col) { QModelIndex index = m_model->index(row, col); m_tableView->scrollTo(index); m_tableView->setCurrentIndex(index); } void MatrixView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(oldSize) m_matrix->setColumnWidth(logicalIndex, newSize); } void MatrixView::handleVerticalSectionResized(int logicalIndex, int oldSize, int newSize) { Q_UNUSED(oldSize) m_matrix->setRowHeight(logicalIndex, newSize); } void MatrixView::fillWithFunctionValues() { MatrixFunctionDialog* dlg = new MatrixFunctionDialog(m_matrix); dlg->exec(); } void MatrixView::fillWithConstValues() { bool ok = false; double value = QInputDialog::getDouble(this, i18n("Fill the matrix with constant value"), i18n("Value"), 0, -2147483647, 2147483647, 6, &ok); if (ok) { WAIT_CURSOR; QVector>* newData = static_cast>*>(m_matrix->data()); for (int col = 0; col < m_matrix->columnCount(); ++col) { for (int row = 0; row < m_matrix->rowCount(); ++row) (newData->operator[](col))[row] = value; } m_matrix->setData(newData); RESET_CURSOR; } } //############################ selection related slots ######################### void MatrixView::cutSelection() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: cut selected cell(s)", m_matrix->name())); copySelection(); clearSelectedCells(); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::copySelection() { int first_col = firstSelectedColumn(false); if (first_col == -1) return; int last_col = lastSelectedColumn(false); if (last_col == -2) return; int first_row = firstSelectedRow(false); if (first_row == -1) return; int last_row = lastSelectedRow(false); if (last_row == -2) return; int cols = last_col - first_col +1; int rows = last_row - first_row +1; WAIT_CURSOR; QString output_str; for (int r=0; r < rows; r++) { for (int c=0; c < cols; c++) { //TODO: mode if (isCellSelected(first_row + r, first_col + c)) output_str += QLocale().toString(m_matrix->cell(first_row + r, first_col + c), m_matrix->numericFormat(), 16); // copy with max. precision if (c < cols-1) output_str += '\t'; } if (r < rows-1) output_str += '\n'; } QApplication::clipboard()->setText(output_str); RESET_CURSOR; } void MatrixView::pasteIntoSelection() { if (m_matrix->columnCount() < 1 || m_matrix->rowCount() < 1) return; const QMimeData* mime_data = QApplication::clipboard()->mimeData(); if (!mime_data->hasFormat("text/plain")) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: paste from clipboard", m_matrix->name())); int first_col = firstSelectedColumn(false); int last_col = lastSelectedColumn(false); int first_row = firstSelectedRow(false); int last_row = lastSelectedRow(false); int input_row_count = 0; int input_col_count = 0; int rows, cols; QString input_str = QString(mime_data->data("text/plain")); QList< QStringList > cell_texts; QStringList input_rows(input_str.split('\n')); input_row_count = input_rows.count(); input_col_count = 0; for (int i=0; i < input_row_count; i++) { cell_texts.append(input_rows.at(i).split('\t')); if (cell_texts.at(i).count() > input_col_count) input_col_count = cell_texts.at(i).count(); } // if the is no selection or only one cell selected, the // selection will be expanded to the needed size from the current cell if ( (first_col == -1 || first_row == -1) || (last_row == first_row && last_col == first_col) ) { 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; // resize the matrix if necessary if (last_col >= m_matrix->columnCount()) m_matrix->appendColumns(last_col+1-m_matrix->columnCount()); if (last_row >= m_matrix->rowCount()) m_matrix->appendRows(last_row+1-m_matrix->rowCount()); // select the rectangle to be pasted in setCellsSelected(first_row, first_col, last_row, last_col); } rows = last_row - first_row + 1; cols = last_col - first_col + 1; for (int r=0; rsetCell(first_row + r, first_col + c, cell_texts.at(r).at(c).toDouble()); } } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedCells() { int first_row = firstSelectedRow(); if (first_row<0) return; int first_col = firstSelectedColumn(); if (first_col<0) return; int last_row = lastSelectedRow(); int last_col = lastSelectedColumn(); WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected cell(s)", m_matrix->name())); for (int i=first_row; i <= last_row; i++) { for (int j=first_col; j <= last_col; j++) { if (isCellSelected(i, j)) m_matrix->clearCell(i, j); } } m_matrix->endMacro(); RESET_CURSOR; } class UpdateImageTask : public QRunnable { public: UpdateImageTask(int start, int end, QImage& image, const void* data, double scaleFactor, double min) : m_image(image), m_data(data) { m_start = start; m_end = end; m_scaleFactor = scaleFactor; m_min = min; }; void run() { for (int row = m_start; row < m_end; ++row) { m_mutex.lock(); QRgb* line = reinterpret_cast(m_image.scanLine(row)); m_mutex.unlock(); for (int col = 0; col < m_image.width(); ++col) { const int gray = (static_cast>*>(m_data)->at(col).at(row)-m_min)*m_scaleFactor; line[col] = qRgb(gray, gray, gray); } } } private: QMutex m_mutex; int m_start; int m_end; QImage& m_image; const void* m_data; double m_scaleFactor; double m_min; }; void MatrixView::updateImage() { WAIT_CURSOR; m_image = QImage(m_matrix->columnCount(), m_matrix->rowCount(), QImage::Format_ARGB32); //find min/max value double dmax = -DBL_MAX, dmin = DBL_MAX; const QVector>* data = static_cast>*>(m_matrix->data()); const int width = m_matrix->columnCount(); const int height = m_matrix->rowCount(); for (int col = 0; col < width; ++col) { for (int row = 0; row < height; ++row) { const double value = (data->operator[](col))[row]; if (dmax < value) dmax = value; if (dmin > value) dmin = value; } } //update the image const double scaleFactor = 255.0/(dmax-dmin); QThreadPool* pool = QThreadPool::globalInstance(); int range = ceil(double(m_image.height())/pool->maxThreadCount()); for (int i = 0; i < pool->maxThreadCount(); ++i) { const int start = i*range; int end = (i+1)*range; if (end>m_image.height()) end = m_image.height(); UpdateImageTask* task = new UpdateImageTask(start, end, m_image, data, scaleFactor, dmin); pool->start(task); } pool->waitForDone(); m_imageLabel->resize(width, height); m_imageLabel->setPixmap(QPixmap::fromImage(m_image)); m_imageIsDirty = false; RESET_CURSOR; } //############################# matrix related slots ########################### void MatrixView::switchView(QAction* action) { if (action == action_data_view) m_stackedWidget->setCurrentIndex(0); else { if (m_imageIsDirty) this->updateImage(); m_stackedWidget->setCurrentIndex(1); } } void MatrixView::matrixDataChanged() { m_imageIsDirty = true; if (m_stackedWidget->currentIndex() == 1) this->updateImage(); } void MatrixView::headerFormatChanged(QAction* action) { if (action == action_header_format_1) m_matrix->setHeaderFormat(Matrix::HeaderRowsColumns); else if (action == action_header_format_2) m_matrix->setHeaderFormat(Matrix::HeaderValues); else m_matrix->setHeaderFormat(Matrix::HeaderRowsColumnsValues); } //############################# column related slots ########################### /*! Append as many columns as are selected. */ void MatrixView::addColumns() { m_matrix->appendColumns(selectedColumnCount(false)); } void MatrixView::insertEmptyColumns() { int first = firstSelectedColumn(); int last = lastSelectedColumn(); if (first < 0) return; int count, current = first; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: insert empty column(s)", m_matrix->name())); while (current <= last) { current = first+1; while (current <= last && isColumnSelected(current)) current++; count = current-first; m_matrix->insertColumns(first, count); current += count; last += count; while (current <= last && isColumnSelected(current)) current++; first = current; } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::removeSelectedColumns() { int first = firstSelectedColumn(); int last = lastSelectedColumn(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: remove selected column(s)", m_matrix->name())); for (int i=last; i >= first; i--) if (isColumnSelected(i, false)) m_matrix->removeColumns(i, 1); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedColumns() { WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected column(s)", m_matrix->name())); for (int i=0; i < m_matrix->columnCount(); i++) { if (isColumnSelected(i, false)) m_matrix->clearColumn(i); } m_matrix->endMacro(); RESET_CURSOR; } //############################## rows related slots ############################ /*! Append as many rows as are selected. */ void MatrixView::addRows() { m_matrix->appendRows(selectedRowCount(false)); } void MatrixView::insertEmptyRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); int count, current = first; if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: insert empty rows(s)", m_matrix->name())); while (current <= last) { current = first+1; while (current <= last && isRowSelected(current)) current++; count = current-first; m_matrix->insertRows(first, count); current += count; last += count; while (current <= last && !isRowSelected(current)) current++; first = current; } m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::removeSelectedRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: remove selected rows(s)", m_matrix->name())); for (int i=last; i >= first; i--) if (isRowSelected(i, false)) m_matrix->removeRows(i, 1); m_matrix->endMacro(); RESET_CURSOR; } void MatrixView::clearSelectedRows() { int first = firstSelectedRow(); int last = lastSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_matrix->beginMacro(i18n("%1: clear selected rows(s)", m_matrix->name())); for (int i=first; i <= last; i++) { if (isRowSelected(i)) m_matrix->clearRow(i); } m_matrix->endMacro(); RESET_CURSOR; } /*! prints the complete matrix to \c printer. */ void MatrixView::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(); QVector>* data = static_cast>*>(m_matrix->data()); const int rows = m_matrix->rowCount(); const int cols = m_matrix->columnCount(); int height = margin; const int vertHeaderWidth = vHeader->width(); int right = margin + vertHeaderWidth; int columnsPerTable = 0; int headerStringWidth = 0; int firstRowStringWidth = vertHeaderWidth; bool tablesNeeded = false; QVector firstRowCeilSizes; firstRowCeilSizes.reserve(data[0].size()); firstRowCeilSizes.resize(data[0].size()); QRect br; for (int i = 0; i < data->size(); ++i) { br = painter.boundingRect(br, Qt::AlignCenter,QString::number(data->at(i)[0]) + '\t'); firstRowCeilSizes[i] = br.width() > m_tableView->columnWidth(i) ? br.width() : m_tableView->columnWidth(i); } for (int col = 0; col < cols; ++col) { headerStringWidth += m_tableView->columnWidth(col); br = painter.boundingRect(br, Qt::AlignCenter,QString::number(data->at(col)[0]) + '\t'); firstRowStringWidth += br.width(); 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++; for (int table = 0; table < tablesCount; ++table) { right = margin + vertHeaderWidth; //Paint the horizontal header first 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 w; 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(); w = /*m_tableView->columnWidth(i)*/ firstRowCeilSizes[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()); } //first horizontal line painter.drawLine(margin + vertHeaderWidth, height, right-1, height); height += tr.height(); painter.drawLine(margin, height, right-1, height); // print table values QString cellText; for (i=0; imodel()->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)*/ firstRowCeilSizes[j]; cellText = QString::number(data->at(j)[i]) + '\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 MatrixView::exportToFile(const QString& path, const QString& separator) const { QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; QTextStream out(&file); QString sep = separator; sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive); sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); //export values const int cols = m_matrix->columnCount(); const int rows = m_matrix->rowCount(); const QVector >* data = static_cast>*>(m_matrix->data()); for (int row = 0; row < rows; ++row) { for (int col=0; colat(col)[row]; if (col!=cols-1) out< > toExport; int firstSelectedCol = 0; int firstSelectedRowi = 0; int totalRowCount = 0; int cols = 0; if (entire) { cols = m_matrix->columnCount(); totalRowCount = m_matrix->rowCount(); toExport.reserve(totalRowCount); toExport.resize(totalRowCount); for (int row = 0; row < totalRowCount; ++row) { toExport[row].reserve(cols); toExport[row].resize(cols); //TODO: mode for (int col = 0; col < cols; ++col) toExport[row][col] = m_matrix->text(row,col); } firstSelectedCol = 0; firstSelectedRowi = 0; } else { cols = selectedColumnCount(); totalRowCount = selectedRowCount(); firstSelectedCol = firstSelectedColumn(); if (firstSelectedCol == -1) return; firstSelectedRowi = firstSelectedRow(); if (firstSelectedRowi == -1) return; const int lastSelectedCol = lastSelectedColumn(); const int lastSelectedRowi = lastSelectedRow(); toExport.reserve(lastSelectedRowi - firstSelectedRowi+1); toExport.resize(lastSelectedRowi - firstSelectedRowi+1); int r = 0; int c = 0; for (int row = firstSelectedRowi; row <= lastSelectedRowi; ++row, ++r) { toExport[r].reserve(lastSelectedCol - firstSelectedCol+1); toExport[r].resize(lastSelectedCol - firstSelectedCol+1); c = 0; //TODO: mode for (int col = firstSelectedCol; col <= lastSelectedCol; ++col,++c) toExport[r][c] = m_matrix->text(row, col); } } int columnsStringSize = 0; int headerStringSize = 0; int columnsPerTable = 0; const int firstHHeaderSectionLength = m_tableView->model()->headerData(0, Qt::Horizontal).toString().length(); const int firstSelectedVHeaderSectionLength = m_tableView->model()->headerData(firstSelectedRow(), Qt::Vertical).toString().length(); if (verticalHeaders) { if (entire) headerStringSize += firstHHeaderSectionLength; else headerStringSize += firstSelectedVHeaderSectionLength; } if (!horizontalHeaders && verticalHeaders) { if (entire) columnsStringSize += firstHHeaderSectionLength; else columnsStringSize += firstSelectedVHeaderSectionLength; } for (int col = 0; col < cols; ++col) { int maxSize = -1; for (int row = 0; row < toExport.size(); ++row) { if (toExport.at(row).at(col).size() > maxSize) maxSize = toExport.at(row).at(col).size(); } columnsStringSize += maxSize; if (horizontalHeaders) headerStringSize += m_tableView->model()->headerData(col, Qt::Horizontal).toString().length(); if ((columnsStringSize > 65) || (headerStringSize > 65)) break; ++columnsPerTable; } 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 Matrix Export to \\LaTeX{} } \n"); out << QLatin1String("\\author{LabPlot} \n"); out << QLatin1String("\\date{\\today} \n"); // out << "\\maketitle \n"; } const QString endTabularTable ("\\end{tabular} \n \\end{table} \n"); const QString tableCaption ("\\caption{"+ m_matrix->name() + "} \n"); const QString beginTable ("\\begin{table}[ht] \n"); const QString centeredColumn( gridLines ? QLatin1String(" c |") : QLatin1String(" c ")); int rowCount = 0; const int maxRows = 45; bool captionRemoved = false; if (columnsSeparating) { 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}{"); textable<< (gridLines ?QLatin1String("|") : QLatin1String("")); for (int i = 0; i < columnsPerTable; ++i) textable << centeredColumn; if (verticalHeaders) textable << centeredColumn; textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) textable << QLatin1String(" & "); for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) { textable << m_tableView->model()->headerData(col + firstSelectedCol, Qt::Horizontal).toString(); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } for (const auto& s : textable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor} "; out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) { out << toExport.at(row).at(col); if (col != ((table * columnsPerTable)+ columnsPerTable)-1) out << QLatin1String(" & "); } 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; } captionRemoved = false; QStringList remainingTable; remainingTable << beginTable; if (captions) remainingTable << tableCaption; remainingTable << QLatin1String("\\centering \n"); remainingTable << QLatin1String("\\begin{tabular}{") << (gridLines ? QLatin1String("|"):QLatin1String("")); for (int c = 0; c < remainingColumns; ++c) remainingTable << centeredColumn; if (verticalHeaders) remainingTable << centeredColumn; remainingTable << QLatin1String("} \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) remainingTable << QLatin1String(" & "); for (int col = 0; col < remainingColumns; ++col) { remainingTable << m_tableView->model()->headerData(firstSelectedCol+col + (tablesCount * columnsPerTable), Qt::Horizontal).toString(); if (col != remainingColumns-1) remainingTable << QLatin1String(" & "); } remainingTable << QLatin1String("\\\\ \n"); if (gridLines) remainingTable << QLatin1String("\\hline \n"); } for (const auto& s : remainingTable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor}"; out << m_tableView->model()->headerData(row+ firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = 0; col < remainingColumns; ++col ) { out << toExport.at(row).at(col + (tablesCount * columnsPerTable)); if (col != remainingColumns-1) out << QLatin1String(" & "); } 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 ? QLatin1String("|"):QLatin1String("")); for (int c = 0; c < cols; ++c) textable << centeredColumn; if (verticalHeaders) textable << centeredColumn; textable << QLatin1String("} \n"); if (gridLines) textable << QLatin1String("\\hline \n"); if (horizontalHeaders) { if (latexHeaders) textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); if (verticalHeaders) textable << QLatin1String(" & "); for (int col = 0; col < cols ; ++col) { textable << m_tableView->model()->headerData(col+firstSelectedCol, Qt::Horizontal).toString(); if (col != cols-1) textable << QLatin1String(" & "); } textable << QLatin1String("\\\\ \n"); if (gridLines) textable << QLatin1String("\\hline \n"); } for (const auto& s : textable) out << s; for (int row = 0; row < totalRowCount; ++row) { if (verticalHeaders) { out << "\\cellcolor{HeaderBgColor}"; out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); out << QLatin1String(" & "); } for (int col = 0; col < cols; ++col ) { out << toExport.at(row).at(col); if (col != cols-1) out << " & "; } 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; if (!captionRemoved) captionRemoved = true; rowCount = 0; if (!captionRemoved) captionRemoved = true; } } out << endTabularTable; } if (latexHeaders) out << QLatin1String("\\end{document} \n"); } void MatrixView::showColumnStatistics() { if (selectedColumnCount() > 0) { QString dlgTitle (m_matrix->name() + " column statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); QVector columns; for (int col = 0; col < m_matrix->columnCount(); ++col) { if (isColumnSelected(col, false)) { QString headerString = m_tableView->model()->headerData(col, Qt::Horizontal).toString(); columns << new Column(headerString, static_cast>*>(m_matrix->data())->at(col)); } } dlg->setColumns(columns); if (dlg->exec() == QDialog::Accepted) { qDeleteAll(columns); columns.clear(); } } } void MatrixView::showRowStatistics() { if (selectedRowCount() > 0) { QString dlgTitle (m_matrix->name() + " row statistics"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); QVector columns; for (int row = 0; row < m_matrix->rowCount(); ++row) { if (isRowSelected(row, false)) { QString headerString = m_tableView->model()->headerData(row, Qt::Vertical).toString(); //TODO: mode columns << new Column(headerString, m_matrix->rowCells(row, 0, m_matrix->columnCount()-1)); } } dlg->setColumns(columns); if (dlg->exec() == QDialog::Accepted) { qDeleteAll(columns); columns.clear(); } } } void MatrixView::exportToFits(const QString &fileName, const int exportTo) const { FITSFilter* filter = new FITSFilter; filter->setExportTo(exportTo); filter->write(fileName, m_matrix); delete filter; } diff --git a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp index e8a3d2ffb..2c637552a 100644 --- a/src/commonfrontend/spreadsheet/SpreadsheetView.cpp +++ b/src/commonfrontend/spreadsheet/SpreadsheetView.cpp @@ -1,2644 +1,2645 @@ /*************************************************************************** File : SpreadsheetView.cpp Project : LabPlot Description : View class for Spreadsheet -------------------------------------------------------------------- Copyright : (C) 2011-2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "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 #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/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_suppressSelectionChangedEvent(false), m_readOnly(readOnly), m_columnSetAsMenu(nullptr), m_columnGenerateDataMenu(nullptr), m_columnSortMenu(nullptr) { QHBoxLayout* 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->setClickable(true); m_horizontalHeader->setHighlightSections(true); m_tableView->setHorizontalHeader(m_horizontalHeader); m_horizontalHeader->setMovable(true); m_horizontalHeader->installEventFilter(this); resizeHeader(); connect(m_horizontalHeader, SIGNAL(sectionMoved(int,int,int)), this, SLOT(handleHorizontalSectionMoved(int,int,int))); connect(m_horizontalHeader, SIGNAL(sectionDoubleClicked(int)), this, SLOT(handleHorizontalHeaderDoubleClicked(int))); connect(m_horizontalHeader, SIGNAL(sectionResized(int,int,int)), this, SLOT(handleHorizontalSectionResized(int,int,int))); connect(m_horizontalHeader, SIGNAL(sectionClicked(int)), this, SLOT(columnClicked(int)) ); // vertical header QHeaderView* v_header = m_tableView->verticalHeader(); v_header->setResizeMode(QHeaderView::Fixed); v_header->setMovable(false); v_header->installEventFilter(this); setFocusPolicy(Qt::StrongFocus); setFocus(); installEventFilter(this); connectActions(); showComments(false); connect(m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(updateHeaderGeometry(Qt::Orientation,int,int)) ); connect(m_model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), this, SLOT(handleHeaderDataChanged(Qt::Orientation,int,int)) ); connect(m_spreadsheet, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(handleAspectAdded(const AbstractAspect*))); connect(m_spreadsheet, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(handleAspectAboutToBeRemoved(const AbstractAspect*))); connect(m_spreadsheet, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); for (auto* column: m_spreadsheet->children()) connect(column, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createColumnContextMenu(QMenu*))); //selection relevant connections QItemSelectionModel* sel_model = m_tableView->selectionModel(); connect(sel_model, SIGNAL(currentColumnChanged(QModelIndex,QModelIndex)), this, SLOT(currentColumnChanged(QModelIndex,QModelIndex))); connect(sel_model, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection))); connect(sel_model, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)) ); connect(m_spreadsheet, SIGNAL(columnSelected(int)), this, SLOT(selectColumn(int)) ); connect(m_spreadsheet, SIGNAL(columnDeselected(int)), this, SLOT(deselectColumn(int)) ); } /*! set the column sizes to the saved values or resize to content if no size was saved yet */ void 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(""), i18n("Assign &Formula"), this); // action_recalculate = new QAction(QIcon::fromTheme(""), i18n("Recalculate"), this); action_fill_sel_row_numbers = new QAction(QIcon::fromTheme(""), i18n("Row Numbers"), this); action_fill_row_numbers = new QAction(QIcon::fromTheme(""), i18n("Row Numbers"), this); action_fill_random = new QAction(QIcon::fromTheme(""), i18n("Uniform Random Values"), this); action_fill_random_nonuniform = new QAction(QIcon::fromTheme(""), i18n("Random Values"), this); action_fill_equidistant = new QAction(QIcon::fromTheme(""), i18n("Equidistant Values"), this); action_fill_function = new QAction(QIcon::fromTheme(""), i18n("Function Values"), this); action_fill_const = new QAction(QIcon::fromTheme(""), 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 ); // column related actions action_insert_column_left = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("Insert Column Left"), this); action_insert_column_right = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("Insert Column Right"), this); action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remove Selected Columns"), this); action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Selected Columns"), this); action_set_as_none = new QAction(i18n("None"), this); action_set_as_none->setData(AbstractColumn::NoDesignation); action_set_as_x = new QAction("X", this); action_set_as_x->setData(AbstractColumn::X); action_set_as_y = new QAction("Y", this); action_set_as_y->setData(AbstractColumn::Y); action_set_as_z = new QAction("Z", this); action_set_as_z->setData(AbstractColumn::Z); action_set_as_xerr = new QAction(i18n("X-error"), this); action_set_as_xerr->setData(AbstractColumn::XError); action_set_as_xerr_minus = new QAction(i18n("X-error minus"), this); action_set_as_xerr_minus->setData(AbstractColumn::XErrorMinus); action_set_as_xerr_plus = new QAction(i18n("X-error plus"), this); action_set_as_xerr_plus->setData(AbstractColumn::XErrorPlus); action_set_as_yerr = new QAction(i18n("Y-error"), this); action_set_as_yerr->setData(AbstractColumn::YError); action_set_as_yerr_minus = new QAction(i18n("Y-error minus"), this); action_set_as_yerr_minus->setData(AbstractColumn::YErrorMinus); action_set_as_yerr_plus = new QAction(i18n("Y-error plus"), this); action_set_as_yerr_plus->setData(AbstractColumn::YErrorPlus); action_reverse_columns = new QAction(QIcon::fromTheme(""), i18n("Reverse"), this); action_drop_values = new QAction(QIcon::fromTheme(""), i18n("Drop Values"), this); action_mask_values = new QAction(QIcon::fromTheme(""), i18n("Mask Values"), this); // action_join_columns = new QAction(QIcon::fromTheme(""), i18n("Join"), this); action_normalize_columns = new QAction(QIcon::fromTheme(""), i18n("&Normalize"), this); action_normalize_selection = new QAction(QIcon::fromTheme(""), i18n("&Normalize Selection"), this); action_sort_columns = new QAction(QIcon::fromTheme(""), i18n("&Selected Columns"), this); action_sort_asc_column = new QAction(QIcon::fromTheme("view-sort-ascending"), i18n("&Ascending"), this); action_sort_desc_column = new QAction(QIcon::fromTheme("view-sort-descending"), i18n("&Descending"), this); action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Column Statisti&cs"), this); // row related actions action_insert_row_above = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("Insert Row Above"), this); action_insert_row_below = new QAction(QIcon::fromTheme("edit-table-insert-row-below"), i18n("Insert Row Below"), this); action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Selected Rows"), this); action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selected Rows"), this); action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Row Statisti&cs"), this); //plot data action - action_plot_data = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); + action_plot_data = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot Data"), this); //Analyze and plot menu actions //TODO: no own icons yet - addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data operation"), this); -// addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data operation"), this); + addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Operation"), this); +// addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data Operation"), this); //TODO: enable when available addDataOperationAction->setEnabled(false); - 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 = 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 = 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 = 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 = 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 = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), this); addFourierFilterAction->setData(PlotDataDialog::FourierFilter); connect(addDataReductionAction, SIGNAL(triggered()), SLOT(plotData())); connect(addDifferentiationAction, SIGNAL(triggered()), SLOT(plotData())); connect(addIntegrationAction, SIGNAL(triggered()), SLOT(plotData())); connect(addInterpolationAction, SIGNAL(triggered()), SLOT(plotData())); connect(addSmoothAction, SIGNAL(triggered()), SLOT(plotData())); for (const auto& action: addFitAction) connect(action, SIGNAL(triggered()), SLOT(plotData())); connect(addFourierFilterAction, SIGNAL(triggered()), SLOT(plotData())); } void SpreadsheetView::initMenus() { //Selection menu m_selectionMenu = new QMenu(i18n("Selection"), this); QMenu* submenu = nullptr; if (!m_readOnly) { - submenu= new QMenu(i18n("Fi&ll Selection with"), this); + submenu= new QMenu(i18n("Fi&ll Selection With"), this); submenu->addAction(action_fill_sel_row_numbers); submenu->addAction(action_fill_const); m_selectionMenu->addMenu(submenu); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_cut_selection); } m_selectionMenu->addAction(action_copy_selection); if (!m_readOnly) { m_selectionMenu->addAction(action_paste_into_selection); m_selectionMenu->addAction(action_clear_selection); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_mask_selection); m_selectionMenu->addAction(action_unmask_selection); m_selectionMenu->addSeparator(); m_selectionMenu->addAction(action_normalize_selection); } // Column menu m_columnMenu = new QMenu(this); m_columnMenu->addAction(action_plot_data); // Data manipulation sub-menu QMenu* dataManipulationMenu = new QMenu(i18n("Data Manipulation")); dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw")); dataManipulationMenu->addAction(addDataOperationAction); dataManipulationMenu->addAction(addDataReductionAction); // Data fit sub-menu QMenu* dataFitMenu = new QMenu(i18n("Fit")); dataFitMenu->setIcon(QIcon::fromTheme("labplot-xy-fit-curve")); dataFitMenu->addAction(addFitAction.at(0)); dataFitMenu->addAction(addFitAction.at(1)); dataFitMenu->addAction(addFitAction.at(2)); dataFitMenu->addAction(addFitAction.at(3)); dataFitMenu->addAction(addFitAction.at(4)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(5)); dataFitMenu->addAction(addFitAction.at(6)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(7)); dataFitMenu->addAction(addFitAction.at(8)); dataFitMenu->addAction(addFitAction.at(9)); dataFitMenu->addSeparator(); dataFitMenu->addAction(addFitAction.at(10)); //analyze and plot data menu - m_analyzePlotMenu = new QMenu(i18n("Analyze and plot data")); + m_analyzePlotMenu = new QMenu(i18n("Analyze and Plot Data")); m_analyzePlotMenu->insertMenu(0, dataManipulationMenu); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addAction(addDifferentiationAction); m_analyzePlotMenu->addAction(addIntegrationAction); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addAction(addInterpolationAction); m_analyzePlotMenu->addAction(addSmoothAction); m_analyzePlotMenu->addAction(addFourierFilterAction); m_analyzePlotMenu->addSeparator(); m_analyzePlotMenu->addMenu(dataFitMenu); m_columnMenu->addMenu(m_analyzePlotMenu); m_columnSetAsMenu = new QMenu(i18n("Set Column As")); m_columnMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_x); m_columnSetAsMenu->addAction(action_set_as_y); m_columnSetAsMenu->addAction(action_set_as_z); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_xerr); m_columnSetAsMenu->addAction(action_set_as_xerr_minus); m_columnSetAsMenu->addAction(action_set_as_xerr_plus); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_yerr); m_columnSetAsMenu->addAction(action_set_as_yerr_minus); m_columnSetAsMenu->addAction(action_set_as_yerr_plus); m_columnSetAsMenu->addSeparator(); m_columnSetAsMenu->addAction(action_set_as_none); m_columnMenu->addMenu(m_columnSetAsMenu); if (!m_readOnly) { m_columnGenerateDataMenu = new QMenu(i18n("Generate Data"), this); m_columnGenerateDataMenu->addAction(action_fill_row_numbers); m_columnGenerateDataMenu->addAction(action_fill_const); m_columnGenerateDataMenu->addAction(action_fill_equidistant); m_columnGenerateDataMenu->addAction(action_fill_random_nonuniform); m_columnGenerateDataMenu->addAction(action_fill_function); m_columnMenu->addSeparator(); m_columnMenu->addMenu(m_columnGenerateDataMenu); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_reverse_columns); m_columnMenu->addAction(action_drop_values); m_columnMenu->addAction(action_mask_values); // m_columnMenu->addAction(action_join_columns); m_columnMenu->addAction(action_normalize_columns); m_columnSortMenu = new QMenu(i18n("Sort"), this); m_columnSortMenu->setIcon(QIcon::fromTheme("view-sort-ascending")); m_columnSortMenu->addAction(action_sort_asc_column); m_columnSortMenu->addAction(action_sort_desc_column); m_columnSortMenu->addAction(action_sort_columns); m_columnMenu->addSeparator(); m_columnMenu->addMenu(m_columnSortMenu); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_insert_column_left); m_columnMenu->addAction(action_insert_column_right); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_remove_columns); m_columnMenu->addAction(action_clear_columns); } m_columnMenu->addSeparator(); m_columnMenu->addAction(action_toggle_comments); m_columnMenu->addSeparator(); m_columnMenu->addAction(action_statistics_columns); action_statistics_columns->setVisible(false); //Spreadsheet menu m_spreadsheetMenu = new QMenu(this); m_spreadsheetMenu->addAction(action_plot_data); m_spreadsheetMenu->addMenu(m_analyzePlotMenu); 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); action_statistics_all_columns->setVisible(true); //Row menu m_rowMenu = new QMenu(this); if (!m_readOnly) { - submenu = new QMenu(i18n("Fi&ll Selection with"), this); + submenu = new QMenu(i18n("Fi&ll Selection With"), this); submenu->addAction(action_fill_sel_row_numbers); submenu->addAction(action_fill_const); m_rowMenu->addMenu(submenu); m_rowMenu->addSeparator(); m_rowMenu->addAction(action_insert_row_above); m_rowMenu->addAction(action_insert_row_below); m_rowMenu->addSeparator(); m_rowMenu->addAction(action_remove_rows); m_rowMenu->addAction(action_clear_rows); } m_rowMenu->addSeparator(); m_rowMenu->addAction(action_statistics_rows); action_statistics_rows->setVisible(false); } void SpreadsheetView::connectActions() { connect(action_cut_selection, SIGNAL(triggered()), this, SLOT(cutSelection())); connect(action_copy_selection, SIGNAL(triggered()), this, SLOT(copySelection())); connect(action_paste_into_selection, SIGNAL(triggered()), this, SLOT(pasteIntoSelection())); connect(action_mask_selection, SIGNAL(triggered()), this, SLOT(maskSelection())); connect(action_unmask_selection, SIGNAL(triggered()), this, SLOT(unmaskSelection())); connect(action_clear_selection, SIGNAL(triggered()), this, SLOT(clearSelectedCells())); // connect(action_recalculate, SIGNAL(triggered()), this, SLOT(recalculateSelectedCells())); connect(action_fill_row_numbers, SIGNAL(triggered()), this, SLOT(fillWithRowNumbers())); connect(action_fill_sel_row_numbers, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithRowNumbers())); // connect(action_fill_random, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithRandomNumbers())); connect(action_fill_random_nonuniform, SIGNAL(triggered()), this, SLOT(fillWithRandomValues())); connect(action_fill_equidistant, SIGNAL(triggered()), this, SLOT(fillWithEquidistantValues())); connect(action_fill_function, SIGNAL(triggered()), this, SLOT(fillWithFunctionValues())); connect(action_fill_const, SIGNAL(triggered()), this, SLOT(fillSelectedCellsWithConstValues())); connect(action_select_all, SIGNAL(triggered()), m_tableView, SLOT(selectAll())); connect(action_clear_spreadsheet, SIGNAL(triggered()), m_spreadsheet, SLOT(clear())); connect(action_clear_masks, SIGNAL(triggered()), m_spreadsheet, SLOT(clearMasks())); connect(action_sort_spreadsheet, SIGNAL(triggered()), this, SLOT(sortSpreadsheet())); connect(action_go_to_cell, SIGNAL(triggered()), this, SLOT(goToCell())); connect(action_insert_column_left, SIGNAL(triggered()), this, SLOT(insertColumnLeft())); connect(action_insert_column_right, SIGNAL(triggered()), this, SLOT(insertColumnRight())); connect(action_remove_columns, SIGNAL(triggered()), this, SLOT(removeSelectedColumns())); connect(action_clear_columns, SIGNAL(triggered()), this, SLOT(clearSelectedColumns())); connect(action_set_as_none, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_x, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_y, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_z, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr_minus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_xerr_plus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr_minus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_set_as_yerr_plus, SIGNAL(triggered()), this, SLOT(setSelectionAs())); connect(action_reverse_columns, SIGNAL(triggered()), this, SLOT(reverseColumns())); connect(action_drop_values, SIGNAL(triggered()), this, SLOT(dropColumnValues())); connect(action_mask_values, SIGNAL(triggered()), this, SLOT(maskColumnValues())); // connect(action_join_columns, SIGNAL(triggered()), this, SLOT(joinColumns())); connect(action_normalize_columns, SIGNAL(triggered()), this, SLOT(normalizeSelectedColumns())); connect(action_normalize_selection, SIGNAL(triggered()), this, SLOT(normalizeSelection())); connect(action_sort_columns, SIGNAL(triggered()), this, SLOT(sortSelectedColumns())); connect(action_sort_asc_column, SIGNAL(triggered()), this, SLOT(sortColumnAscending())); connect(action_sort_desc_column, SIGNAL(triggered()), this, SLOT(sortColumnDescending())); connect(action_statistics_columns, SIGNAL(triggered()), this, SLOT(showColumnStatistics())); connect(action_statistics_all_columns, SIGNAL(triggered()), this, SLOT(showAllColumnsStatistics())); connect(action_insert_row_above, SIGNAL(triggered()), this, SLOT(insertRowAbove())); connect(action_insert_row_below, SIGNAL(triggered()), this, SLOT(insertRowBelow())); connect(action_remove_rows, SIGNAL(triggered()), this, SLOT(removeSelectedRows())); connect(action_clear_rows, SIGNAL(triggered()), this, SLOT(clearSelectedRows())); connect(action_statistics_rows, SIGNAL(triggered()), this, SLOT(showRowStatistics())); connect(action_toggle_comments, SIGNAL(triggered()), this, SLOT(toggleComments())); connect(action_plot_data, SIGNAL(triggered()), this, SLOT(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 = 0; // 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->insertAction(firstAction, action_plot_data); menu->insertSeparator(firstAction); } menu->insertMenu(firstAction, m_selectionMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_select_all); if (!m_readOnly) { menu->insertAction(firstAction, action_clear_spreadsheet); menu->insertAction(firstAction, action_clear_masks); menu->insertAction(firstAction, action_sort_spreadsheet); menu->insertSeparator(firstAction); } menu->insertAction(firstAction, action_go_to_cell); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_toggle_comments); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_statistics_all_columns); menu->insertSeparator(firstAction); } /*! * adds column specific actions in SpreadsheetView to the context menu shown in the project explorer. */ void SpreadsheetView::createColumnContextMenu(QMenu* menu) { const Column* column = dynamic_cast(QObject::sender()); if (!column) return; //should never happen, since the sender is always a Column const bool numeric = (column->columnMode() == AbstractColumn::Numeric) || (column->columnMode() == AbstractColumn::Integer); if (numeric) { QAction* firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_columnSetAsMenu); const bool hasValues = column->hasValues(); if (!m_readOnly) { menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_columnGenerateDataMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, action_reverse_columns); menu->insertAction(firstAction, action_drop_values); menu->insertAction(firstAction, action_mask_values); menu->insertAction(firstAction, action_normalize_columns); 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, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createColumnContextMenu(QMenu*))); } void SpreadsheetView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const Column* col = dynamic_cast(aspect); if (!col || col->parentAspect() != m_spreadsheet) return; disconnect(col, 0, this, 0); } 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::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 show 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; icolumnCount(); for (int i=0; icolumn(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; icolumn(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; icolumnCount(); 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; iselectionModel()->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) { QContextMenuEvent* 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; for(const Column* col : selectedColumns()) { if ( !(col->columnMode() == AbstractColumn::Numeric || col->columnMode() == AbstractColumn::Integer) ) { numeric = false; break; } } action_plot_data->setEnabled(numeric); m_analyzePlotMenu->setEnabled(numeric); m_columnSetAsMenu->setEnabled(numeric); if (!m_readOnly) { m_columnGenerateDataMenu->setEnabled(numeric); action_reverse_columns->setEnabled(numeric); action_drop_values->setEnabled(numeric); action_mask_values->setEnabled(numeric); action_normalize_columns->setEnabled(numeric); 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 action_reverse_columns->setEnabled(hasValues); action_drop_values->setEnabled(hasValues); action_mask_values->setEnabled(hasValues); action_normalize_columns->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) { QKeyEvent* 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; action_plot_data->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('\n')); input_row_count = input_rows.count(); input_col_count = 0; for (int i=0; i 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 the 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 (int r = 0; rsetPlotDesignation(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()) { 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() { PlotDataDialog* dlg = new PlotDataDialog(m_spreadsheet); const QObject* sender = QObject::sender(); if (sender != action_plot_data) { PlotDataDialog::AnalysisAction action = (PlotDataDialog::AnalysisAction)dynamic_cast(sender)->data().toInt(); dlg->setAnalysisAction(action); } 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 double_data(rows); QVector int_data(rows); for (int i = 0; i < rows; ++i) double_data[i] = int_data[i] = i+1; for (auto* col: selectedColumns()) { switch (col->columnMode()) { case AbstractColumn::Numeric: col->replaceValues(0, double_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; RandomValuesDialog* dlg = new RandomValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithEquidistantValues() { if (selectedColumnCount() < 1) return; EquidistantValuesDialog* dlg = new EquidistantValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::fillWithFunctionValues() { if (selectedColumnCount() < 1) return; FunctionValuesDialog* 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, 0, &stringOk); if (stringOk && !stringValue.isEmpty()) { WAIT_CURSOR; QVector results; for (int row = first; row <= last; row++) { if (isCellSelected(row, col)) results << stringValue; else results << col_ptr->textAt(row); } col_ptr->replaceTexts(first, results); RESET_CURSOR; } break; //TODO: handle other modes case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: break; } col_ptr->setSuppressDataChangedSignal(false); col_ptr->setChanged(); } m_spreadsheet->endMacro(); } /*! Open the sort dialog for all columns. */ void SpreadsheetView::sortSpreadsheet() { sortDialog(m_spreadsheet->children()); } /*! Insert an empty column left to the firt selected column */ void SpreadsheetView::insertColumnLeft() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty column", m_spreadsheet->name())); Column* newCol = new Column("1", AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); const int first = firstSelectedColumn(); if (first >= 0) { //determine the first selected column Column* firstCol = m_spreadsheet->child(first); //resize the new column and insert it before the first selected column newCol->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->insertChildBefore(newCol, firstCol); } else { if (m_spreadsheet->columnCount()>0) { //columns available but no columns selected -> prepend the new column at the very beginning Column* firstCol = m_spreadsheet->child(0); newCol->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->insertChildBefore(newCol, firstCol); } else { //no columns available anymore -> resize the spreadsheet and the new column to the default size KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); const int rows = group.readEntry(QLatin1String("RowCount"), 100); m_spreadsheet->setRowCount(rows); newCol->insertRows(0, rows); //add/append a new column m_spreadsheet->addChild(newCol); } } m_spreadsheet->endMacro(); RESET_CURSOR; } /*! Insert an empty column right to the last selected column */ void SpreadsheetView::insertColumnRight() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty column", m_spreadsheet->name())); Column* newCol = new Column("1", AbstractColumn::Numeric); newCol->setPlotDesignation(AbstractColumn::Y); const int last = lastSelectedColumn(); if (last >= 0) { newCol->insertRows(0, m_spreadsheet->rowCount()); if (last < m_spreadsheet->columnCount() - 1) { //determine the column next to the last selected column Column* nextCol = m_spreadsheet->child(last + 1); //insert the new column before the column next to the last selected column m_spreadsheet->insertChildBefore(newCol, nextCol); } else { //last column selected, no next column available -> add/append a new column m_spreadsheet->addChild(newCol); } } else { if (m_spreadsheet->columnCount()>0) { //columns available but no columns selected -> append the new column at the very end newCol->insertRows(0, m_spreadsheet->rowCount()); m_spreadsheet->addChild(newCol); } else { //no columns available anymore -> resize the spreadsheet and the new column to the default size KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Spreadsheet")); const int rows = group.readEntry(QLatin1String("RowCount"), 100); newCol->insertRows(0, rows); m_spreadsheet->setRowCount(rows); //add/append a new column m_spreadsheet->addChild(newCol); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected columns", m_spreadsheet->name())); for (auto* column : selectedColumns()) m_spreadsheet->removeChild(column); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedColumns() { WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected columns", m_spreadsheet->name())); if (formulaModeActive()) { for (auto* col: selectedColumns()) { col->setSuppressDataChangedSignal(true); col->clearFormulas(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } else { for (auto* col: selectedColumns()) { col->setSuppressDataChangedSignal(true); col->clear(); col->setSuppressDataChangedSignal(false); col->setChanged(); } } m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::setSelectionAs() { QVector columns = selectedColumns(); if (!columns.size()) return; m_spreadsheet->beginMacro(i18n("%1: set plot designation", m_spreadsheet->name())); QAction* action = dynamic_cast(QObject::sender()); if (!action) return; AbstractColumn::PlotDesignation pd = (AbstractColumn::PlotDesignation)action->data().toInt(); for (auto* col: columns) col->setPlotDesignation(pd); m_spreadsheet->endMacro(); } 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; QVector* 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; DropValuesDialog* dlg = new DropValuesDialog(m_spreadsheet); dlg->setColumns(selectedColumns()); dlg->exec(); } void SpreadsheetView::maskColumnValues() { if (selectedColumnCount() < 1) return; DropValuesDialog* 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; rowrowCount(); 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"); StatisticsDialog* 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"); StatisticsDialog* dlg = new StatisticsDialog(dlgTitle); QVector columns; for (int i = 0; i < m_spreadsheet->rowCount(); ++i) { if (isRowSelected(i)) { QVector rowValues; for (int j = 0; j < m_spreadsheet->columnCount(); ++j) rowValues << m_spreadsheet->column(j)->valueAt(i); columns << new Column(QString::number(i+1), rowValues); } } dlg->setColumns(columns); if (dlg->exec() == QDialog::Accepted) { qDeleteAll(columns); columns.clear(); } } /*! Insert an empty row above(=before) the first selected row */ void SpreadsheetView::insertRowAbove() { int first = firstSelectedRow(); if (first < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty rows", m_spreadsheet->name())); m_spreadsheet->insertRows(first, 1); m_spreadsheet->endMacro(); RESET_CURSOR; } /*! Insert an empty row below the last selected row */ void SpreadsheetView::insertRowBelow() { int last = lastSelectedRow(); if (last < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: insert empty rows", m_spreadsheet->name())); if (last < m_spreadsheet->rowCount() -1) m_spreadsheet->insertRows(last + 1, 1); //insert before the next to the last selected row else m_spreadsheet->appendRow(); //append one row at the end m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::removeSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: remove selected rows", m_spreadsheet->name())); //TODO setSuppressDataChangedSignal for (const auto& i: selectedRows().intervals()) m_spreadsheet->removeRows(i.start(), i.size()); m_spreadsheet->endMacro(); RESET_CURSOR; } void SpreadsheetView::clearSelectedRows() { if (firstSelectedRow() < 0) return; WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: clear selected rows", m_spreadsheet->name())); for (auto* col: selectedColumns()) { col->setSuppressDataChangedSignal(true); if (formulaModeActive()) { for (const auto& i: selectedRows().intervals()) col->setFormula(i, ""); } 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, ""); } 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::getInteger(0, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_spreadsheet->columnCount(), 1, &ok); if (!ok) return; int row = QInputDialog::getInteger(0, 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); SortDialog* 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; icolumnCount(); i++) m_spreadsheet->setColumnSelectedInView(i, selModel->isColumnSelected(i, QModelIndex())); } bool SpreadsheetView::exportView() { ExportSpreadsheetDialog* 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; if (dlg->format() == 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); } else if (dlg->format() == ExportSpreadsheetDialog::FITS) { const int exportTo = dlg->exportToFits(); const bool commentsAsUnits = dlg->commentsAsUnitsFits(); exportToFits(path, exportTo, commentsAsUnits); } else { const QString separator = dlg->separator(); exportToFile(path, exportHeader, separator); } RESET_CURSOR; } delete dlg; return ret; } bool SpreadsheetView::printView() { QPrinter printer; QPrintDialog* dlg = new QPrintDialog(&printer, this); bool ret; - dlg->setWindowTitle(i18n("Print Spreadsheet")); + dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); if ((ret = dlg->exec()) == QDialog::Accepted) { print(&printer); } delete dlg; return ret; } bool SpreadsheetView::printPreview() { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); connect(dlg, SIGNAL(paintRequested(QPrinter*)), this, SLOT(print(QPrinter*))); 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; int i; 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 w; 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(); 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; imodel()->headerData(i, Qt::Vertical).toString()+'\t'; tr = painter.boundingRect(tr, Qt::AlignCenter, cellText); painter.drawLine(right, height, right, height+tr.height()); br.setTopLeft(QPoint(right,height)); br.setWidth(vertHeaderWidth); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += vertHeaderWidth; painter.drawLine(right, height, right, height+tr.height()); int j = table * columnsPerTable; int toJ = table * columnsPerTable + columnsPerTable; if ((remainingColumns > 0) && (table == tablesCount-1)) { j = (tablesCount-1)*columnsPerTable; toJ = (tablesCount-1)* columnsPerTable + remainingColumns; } for (; j< toJ; j++) { int w = m_tableView->columnWidth(j); cellText = m_spreadsheet->column(j)->isValid(i) ? m_spreadsheet->text(i,j)+'\t': QLatin1String("- \t"); tr = painter.boundingRect(tr,Qt::AlignCenter,cellText); br.setTopLeft(QPoint(right,height)); br.setWidth(w); br.setHeight(tr.height()); painter.drawText(br, Qt::AlignCenter, cellText); right += w; painter.drawLine(right, height, right, height+tr.height()); } height += br.height(); painter.drawLine(margin, height, right-1, height); if (height >= printer->height()-margin ) { printer->newPage(); height = margin; painter.drawLine(margin, height, right, height); } } } RESET_CURSOR; } void SpreadsheetView::exportToFile(const QString& path, const bool exportHeader, const QString& separator) 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; jcolumn(j)->name(); if (j!=cols-1) out<rowCount(); ++i) { for (int j=0; jcolumn(j)->asStringColumn()->textAt(i); if (j!=cols-1) out< 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 ?QLatin1String("|") : QLatin1String("")); 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 ? QLatin1String("|"):QLatin1String("")); 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 ? QLatin1String("|"):QLatin1String("")); 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 { FITSFilter* filter = new FITSFilter; filter->setExportTo(exportTo); filter->setCommentsAsUnits(commentsAsUnits); filter->write(fileName, m_spreadsheet); delete filter; } diff --git a/src/commonfrontend/workbook/WorkbookView.cpp b/src/commonfrontend/workbook/WorkbookView.cpp index 07f0c29a3..95252bac1 100644 --- a/src/commonfrontend/workbook/WorkbookView.cpp +++ b/src/commonfrontend/workbook/WorkbookView.cpp @@ -1,208 +1,208 @@ /*************************************************************************** File : WorkbookView.cpp Project : LabPlot Description : View class for Workbook -------------------------------------------------------------------- Copyright : (C) 2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "WorkbookView.h" #include "backend/core/AbstractAspect.h" #include "backend/core/AbstractPart.h" #include "backend/core/Workbook.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include #include -#include +#include /*! \class WorkbookView \brief View class for Workbook \ingroup commonfrontend */ WorkbookView::WorkbookView(Workbook* workbook) : QWidget(), m_tabWidget(new TabWidget(this)), m_workbook(workbook), lastSelectedIndex(0) { m_tabWidget->setTabPosition(QTabWidget::South); m_tabWidget->setTabShape(QTabWidget::Rounded); m_tabWidget->setMovable(true); m_tabWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_tabWidget->setMinimumSize(200, 200); QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_tabWidget); //add tab for each children view m_initializing = true; for (const auto* aspect : m_workbook->children()) handleAspectAdded(aspect); m_initializing = false; //Actions action_add_spreadsheet = new QAction(QIcon::fromTheme("labplot-spreadsheet"), i18n("Add new Spreadsheet"), this); action_add_matrix = new QAction(QIcon::fromTheme("labplot-matrix"), i18n("Add new Matrix"), this); connect(action_add_spreadsheet, SIGNAL(triggered()), this, SLOT(addSpreadsheet())); connect(action_add_matrix, SIGNAL(triggered()), this, SLOT(addMatrix())); //SIGNALs/SLOTs connect(m_workbook, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(handleDescriptionChanged(const AbstractAspect*))); connect(m_workbook, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(handleAspectAdded(const AbstractAspect*))); connect(m_workbook, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(handleAspectAboutToBeRemoved(const AbstractAspect*))); connect(m_workbook, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_workbook, SIGNAL(workbookItemSelected(int)), this, SLOT(itemSelected(int)) ); connect(m_tabWidget, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); connect(m_tabWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTabContextMenu(QPoint))); connect(m_tabWidget, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); } WorkbookView::~WorkbookView() { //no need to react on currentChanged() in TabWidget when views are deleted disconnect(m_tabWidget, 0, 0, 0); //delete all children views here, its own view will be deleted in ~AbstractPart() for (const auto* part : m_workbook->children()) part->deleteView(); } int WorkbookView::currentIndex() const { return m_tabWidget->currentIndex(); } //############################################################################## //######################### Private slots #################################### //############################################################################## /*! called when the current tab was changed. Propagates the selection of \c Spreadsheet or of a \c Matrix object to \c Workbook. */ void WorkbookView::tabChanged(int index) { if (m_initializing) return; if (index==-1) return; m_workbook->setChildSelectedInView(lastSelectedIndex, false); m_workbook->setChildSelectedInView(index, true); lastSelectedIndex = index; } void WorkbookView::tabMoved(int from, int to) { Q_UNUSED(from); Q_UNUSED(to); //TODO: // AbstractAspect* aspect = m_workbook->child(to); // if (aspect) { // m_tabMoving = true; // AbstractAspect* sibling = m_workbook->child(from); // qDebug()<<"insert: " << to << " " << aspect->name() << ", " << from << " " << sibling->name(); // aspect->remove(); // m_workbook->insertChildBefore(aspect, sibling); // qDebug()<<"inserted"; // m_tabMoving = false; // } } void WorkbookView::itemSelected(int index) { m_tabWidget->setCurrentIndex(index); } /*! * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. * The menu is used * - as the context menu in WorkbookView * - as the "spreadsheet menu" in the main menu-bar (called form MainWin) * - as a part of the spreadsheet context menu in project explorer */ void WorkbookView::createContextMenu(QMenu* menu) const { Q_ASSERT(menu); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, action_add_spreadsheet); menu->insertAction(firstAction, action_add_matrix); menu->insertSeparator(firstAction); } void WorkbookView::showTabContextMenu(QPoint point) { QMenu* menu = 0; AbstractAspect* aspect = m_workbook->child(m_tabWidget->currentIndex()); Spreadsheet* spreadsheet = dynamic_cast(aspect); if (spreadsheet) { menu = spreadsheet->createContextMenu(); } else { Matrix* matrix = dynamic_cast(aspect); if (matrix) menu = matrix->createContextMenu(); } if (menu) menu->exec(m_tabWidget->mapToGlobal(point)); } void WorkbookView::addMatrix() { Matrix* matrix = new Matrix(0, i18n("Matrix")); m_workbook->addChild(matrix); } void WorkbookView::addSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(0, i18n("Spreadsheet")); m_workbook->addChild(spreadsheet); } void WorkbookView::handleDescriptionChanged(const AbstractAspect* aspect) { int index = m_workbook->indexOfChild(aspect); if (index != -1 && indexcount()) m_tabWidget->setTabText(index, aspect->name()); } void WorkbookView::handleAspectAdded(const AbstractAspect* aspect) { const AbstractPart* part = dynamic_cast(aspect); if (!part) return; int index = m_workbook->indexOfChild(aspect); m_tabWidget->insertTab(index, part->view(), aspect->name()); m_tabWidget->setCurrentIndex(index); m_tabWidget->setTabIcon(m_tabWidget->count(), aspect->icon()); this->tabChanged(index); } void WorkbookView::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { int index = m_workbook->indexOfChild(aspect); m_tabWidget->removeTab(index); } diff --git a/src/commonfrontend/worksheet/WorksheetView.cpp b/src/commonfrontend/worksheet/WorksheetView.cpp index d08b08062..0e9291392 100644 --- a/src/commonfrontend/worksheet/WorksheetView.cpp +++ b/src/commonfrontend/worksheet/WorksheetView.cpp @@ -1,1891 +1,1891 @@ /*************************************************************************** File : WorksheetView.cpp Project : LabPlot Description : Worksheet view -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016 Stefan-Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "commonfrontend/worksheet/WorksheetView.h" #include "backend/core/AbstractColumn.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYCurvePrivate.h" #include "backend/worksheet/TextLabel.h" #include "commonfrontend/core/PartMdiView.h" #include "kdefrontend/widgets/ThemesWidget.h" #include "kdefrontend/worksheet/GridDialog.h" #include "kdefrontend/worksheet/PresenterWidget.h" #include "kdefrontend/worksheet/DynamicPresenterWidget.h" #include "backend/lib/trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include /** * \class WorksheetView * \brief Worksheet view */ /*! Constructur of the class. Creates a view for the Worksheet \c worksheet and initializes the internal model. */ WorksheetView::WorksheetView(Worksheet* worksheet) : QGraphicsView(), m_worksheet(worksheet), m_mouseMode(SelectionMode), m_cartesianPlotActionMode(ApplyActionToSelection), m_cartesianPlotMouseMode(CartesianPlot::SelectionMode), m_selectionBandIsShown(false), magnificationFactor(0), m_magnificationWindow(0), m_suppressSelectionChangedEvent(false), lastAddedWorksheetElement(0), m_fadeInTimeLine(0), m_fadeOutTimeLine(0), m_isClosing(false), m_menusInitialized(false), m_ctrlPressed(false), m_numScheduledScalings(0), m_addNewMenu(nullptr), m_addNewCartesianPlotMenu(nullptr), m_zoomMenu(nullptr), m_magnificationMenu(nullptr), m_layoutMenu(nullptr), m_gridMenu(nullptr), m_themeMenu(nullptr), m_viewMouseModeMenu(nullptr), m_cartesianPlotMenu(nullptr), m_cartesianPlotMouseModeMenu(nullptr), m_cartesianPlotAddNewMenu(nullptr), m_cartesianPlotZoomMenu(nullptr), m_cartesianPlotActionModeMenu(nullptr), m_dataManipulationMenu(nullptr), tbNewCartesianPlot(0), tbZoom(0), tbMagnification(0) { setScene(m_worksheet->scene()); setRenderHint(QPainter::Antialiasing); setRubberBandSelectionMode(Qt::ContainsItemBoundingRect); setTransformationAnchor(QGraphicsView::AnchorViewCenter); setResizeAnchor(QGraphicsView::AnchorViewCenter); setMinimumSize(16, 16); setFocusPolicy(Qt::StrongFocus); if (m_worksheet->useViewSize()) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } viewport()->setAttribute( Qt::WA_OpaquePaintEvent ); viewport()->setAttribute( Qt::WA_NoSystemBackground ); setAcceptDrops(true); setCacheMode(QGraphicsView::CacheBackground); m_gridSettings.style = WorksheetView::NoGrid; //signal/slot connections connect(m_worksheet, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_worksheet, SIGNAL(itemSelected(QGraphicsItem*)), this, SLOT(selectItem(QGraphicsItem*)) ); connect(m_worksheet, SIGNAL(itemDeselected(QGraphicsItem*)), this, SLOT(deselectItem(QGraphicsItem*)) ); connect(m_worksheet, SIGNAL(requestUpdate()), this, SLOT(updateBackground()) ); connect(m_worksheet, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(aspectAboutToBeRemoved(const AbstractAspect*))); connect(m_worksheet, SIGNAL(useViewSizeRequested()), this, SLOT(useViewSizeRequested()) ); connect(m_worksheet, SIGNAL(layoutChanged(Worksheet::Layout)), this, SLOT(layoutChanged(Worksheet::Layout)) ); connect(scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged()) ); //resize the view to make the complete scene visible. //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_worksheet->isLoading()) { float w = Worksheet::convertFromSceneUnits(sceneRect().width(), Worksheet::Inch); float h = Worksheet::convertFromSceneUnits(sceneRect().height(), Worksheet::Inch); w *= QApplication::desktop()->physicalDpiX(); h *= QApplication::desktop()->physicalDpiY(); resize(w*1.1, h*1.1); } //rescale to the original size static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); setTransform(QTransform::fromScale(hscale, vscale)); } void WorksheetView::initActions() { QActionGroup* addNewActionGroup = new QActionGroup(this); QActionGroup* zoomActionGroup = new QActionGroup(this); QActionGroup* mouseModeActionGroup = new QActionGroup(this); QActionGroup* layoutActionGroup = new QActionGroup(this); QActionGroup* gridActionGroup = new QActionGroup(this); gridActionGroup->setExclusive(true); QActionGroup* magnificationActionGroup = new QActionGroup(this); - selectAllAction = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select all"), this); + selectAllAction = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this); selectAllAction->setShortcut(Qt::CTRL+Qt::Key_A); this->addAction(selectAllAction); connect(selectAllAction, SIGNAL(triggered()), SLOT(selectAllElements())); deleteAction = new QAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), this); deleteAction->setShortcut(Qt::Key_Delete); this->addAction(deleteAction); connect(deleteAction, SIGNAL(triggered()), SLOT(deleteElement())); backspaceAction = new QAction(this); backspaceAction->setShortcut(Qt::Key_Backspace); this->addAction(backspaceAction); connect(backspaceAction, SIGNAL(triggered()), SLOT(deleteElement())); //Zoom actions - zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom in"), zoomActionGroup); + zoomInViewAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), zoomActionGroup); zoomInViewAction->setShortcut(Qt::CTRL+Qt::Key_Plus); - zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom out"), zoomActionGroup); + zoomOutViewAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), zoomActionGroup); zoomOutViewAction->setShortcut(Qt::CTRL+Qt::Key_Minus); - zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original size"), zoomActionGroup); + zoomOriginAction = new QAction(QIcon::fromTheme("zoom-original"), i18n("Original Size"), zoomActionGroup); zoomOriginAction->setShortcut(Qt::CTRL+Qt::Key_1); - zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to height"), zoomActionGroup); - zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to width"), zoomActionGroup); - zoomFitSelectionAction = new QAction(i18n("Fit to selection"), zoomActionGroup); + zoomFitPageHeightAction = new QAction(QIcon::fromTheme("zoom-fit-height"), i18n("Fit to Height"), zoomActionGroup); + zoomFitPageWidthAction = new QAction(QIcon::fromTheme("zoom-fit-width"), i18n("Fit to Width"), zoomActionGroup); + zoomFitSelectionAction = new QAction(i18n("Fit to Selection"), zoomActionGroup); // Mouse mode actions selectionModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Edit"), mouseModeActionGroup); selectionModeAction->setCheckable(true); navigationModeAction = new QAction(QIcon::fromTheme("input-mouse"), i18n("Navigate"), mouseModeActionGroup); navigationModeAction->setCheckable(true); zoomSelectionModeAction = new QAction(QIcon::fromTheme("page-zoom"), i18n("Select and Zoom"), mouseModeActionGroup); zoomSelectionModeAction->setCheckable(true); //Magnification actions noMagnificationAction = new QAction(QIcon::fromTheme("labplot-1x-zoom"), i18n("No Magnification"), magnificationActionGroup); noMagnificationAction->setCheckable(true); noMagnificationAction->setChecked(true); twoTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-2x-zoom"), i18n("2x Magnification"), magnificationActionGroup); twoTimesMagnificationAction->setCheckable(true); threeTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-3x-zoom"), i18n("3x Magnification"), magnificationActionGroup); threeTimesMagnificationAction->setCheckable(true); fourTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-4x-zoom"), i18n("4x Magnification"), magnificationActionGroup); fourTimesMagnificationAction->setCheckable(true); fiveTimesMagnificationAction = new QAction(QIcon::fromTheme("labplot-5x-zoom"), i18n("5x Magnification"), magnificationActionGroup); fiveTimesMagnificationAction->setCheckable(true); //TODO implement later "group selection action" where multiple objects can be selected by drawing a rectangular // selectionModeAction = new QAction(QIcon::fromTheme("select-rectangular"), i18n("Selection"), mouseModeActionGroup); // selectionModeAction->setCheckable(true); //"Add new" related actions - addCartesianPlot1Action = new QAction(QIcon::fromTheme("labplot-xy-plot-four-axes"), i18n("box plot, four axes"), addNewActionGroup); - addCartesianPlot2Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes"), i18n("box plot, two axes"), addNewActionGroup); - addCartesianPlot3Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered"), i18n("two axes, centered"), addNewActionGroup); - addCartesianPlot4Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered-origin"), i18n("two axes, crossing at origin"), addNewActionGroup); - addTextLabelAction = new QAction(QIcon::fromTheme("draw-text"), i18n("text label"), addNewActionGroup); - addBarChartPlot= new QAction(QIcon::fromTheme("office-chart-line"), i18n("bar chart"), addNewActionGroup); + addCartesianPlot1Action = new QAction(QIcon::fromTheme("labplot-xy-plot-four-axes"), i18n("Box Plot, Four Axes"), addNewActionGroup); + addCartesianPlot2Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes"), i18n("Box Plot, Two Axes"), addNewActionGroup); + addCartesianPlot3Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered"), i18n("Two Axes, Centered"), addNewActionGroup); + addCartesianPlot4Action = new QAction(QIcon::fromTheme("labplot-xy-plot-two-axes-centered-origin"), i18n("Two Axes, Crossing at Origin"), addNewActionGroup); + addTextLabelAction = new QAction(QIcon::fromTheme("draw-text"), i18n("Text Label"), addNewActionGroup); + addBarChartPlot= new QAction(QIcon::fromTheme("office-chart-line"), i18n("Bar Chart"), addNewActionGroup); //Layout actions - verticalLayoutAction = new QAction(QIcon::fromTheme("labplot-editvlayout"), i18n("Vertical layout"), layoutActionGroup); + verticalLayoutAction = new QAction(QIcon::fromTheme("labplot-editvlayout"), i18n("Vertical Layout"), layoutActionGroup); verticalLayoutAction->setCheckable(true); - horizontalLayoutAction = new QAction(QIcon::fromTheme("labplot-edithlayout"), i18n("Horizontal layout"), layoutActionGroup); + horizontalLayoutAction = new QAction(QIcon::fromTheme("labplot-edithlayout"), i18n("Horizontal Layout"), layoutActionGroup); horizontalLayoutAction->setCheckable(true); - gridLayoutAction = new QAction(QIcon::fromTheme("labplot-editgrid"), i18n("Grid layout"), layoutActionGroup); + gridLayoutAction = new QAction(QIcon::fromTheme("labplot-editgrid"), i18n("Grid Layout"), layoutActionGroup); gridLayoutAction->setCheckable(true); - breakLayoutAction = new QAction(QIcon::fromTheme("labplot-editbreaklayout"), i18n("Break layout"), layoutActionGroup); + breakLayoutAction = new QAction(QIcon::fromTheme("labplot-editbreaklayout"), i18n("Break Layout"), layoutActionGroup); breakLayoutAction->setEnabled(false); //Grid actions - noGridAction = new QAction(i18n("no grid"), gridActionGroup); + noGridAction = new QAction(i18n("No Grid"), gridActionGroup); noGridAction->setCheckable(true); noGridAction->setChecked(true); noGridAction->setData(WorksheetView::NoGrid); - denseLineGridAction = new QAction(i18n("dense line grid"), gridActionGroup); + denseLineGridAction = new QAction(i18n("Dense Line Grid"), gridActionGroup); denseLineGridAction->setCheckable(true); - sparseLineGridAction = new QAction(i18n("sparse line grid"), gridActionGroup); + sparseLineGridAction = new QAction(i18n("Sparse Line Grid"), gridActionGroup); sparseLineGridAction->setCheckable(true); - denseDotGridAction = new QAction(i18n("dense dot grid"), gridActionGroup); + denseDotGridAction = new QAction(i18n("Dense Dot Grid"), gridActionGroup); denseDotGridAction->setCheckable(true); - sparseDotGridAction = new QAction(i18n("sparse dot grid"), gridActionGroup); + sparseDotGridAction = new QAction(i18n("Sparse Dot Grid"), gridActionGroup); sparseDotGridAction->setCheckable(true); - customGridAction = new QAction(i18n("custom grid"), gridActionGroup); + customGridAction = new QAction(i18n("Custom Grid"), gridActionGroup); customGridAction->setCheckable(true); - snapToGridAction = new QAction(i18n("snap to grid"), this); + snapToGridAction = new QAction(i18n("Snap to Grid"), this); snapToGridAction->setCheckable(true); - showPresenterMode = new QAction(QIcon::fromTheme("view-fullscreen"), i18n("Show in presenter mode"), this); + showPresenterMode = new QAction(QIcon::fromTheme("view-fullscreen"), i18n("Show in Presenter Mode"), this); //check the action corresponding to the currently active layout in worksheet this->layoutChanged(m_worksheet->layout()); connect(addNewActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(addNew(QAction*))); connect(mouseModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(mouseModeChanged(QAction*))); connect(zoomActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeZoom(QAction*))); connect(magnificationActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(magnificationChanged(QAction*))); connect(layoutActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeLayout(QAction*))); connect(gridActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(changeGrid(QAction*))); connect(snapToGridAction, SIGNAL(triggered()), this, SLOT(changeSnapToGrid())); connect(showPresenterMode, SIGNAL(triggered()), this, SLOT(presenterMode())); //action for cartesian plots QActionGroup* cartesianPlotActionModeActionGroup = new QActionGroup(this); cartesianPlotActionModeActionGroup->setExclusive(true); - cartesianPlotApplyToSelectionAction = new QAction(i18n("selected plots"), cartesianPlotActionModeActionGroup); + cartesianPlotApplyToSelectionAction = new QAction(i18n("Selected Plots"), cartesianPlotActionModeActionGroup); cartesianPlotApplyToSelectionAction->setCheckable(true); cartesianPlotApplyToSelectionAction->setChecked(true); - cartesianPlotApplyToAllAction = new QAction(i18n("all plots"), cartesianPlotActionModeActionGroup); + cartesianPlotApplyToAllAction = new QAction(i18n("All Plots"), cartesianPlotActionModeActionGroup); cartesianPlotApplyToAllAction->setCheckable(true); connect(cartesianPlotActionModeActionGroup, SIGNAL(triggered(QAction*)), SLOT(cartesianPlotActionModeChanged(QAction*))); QActionGroup* cartesianPlotMouseModeActionGroup = new QActionGroup(this); cartesianPlotMouseModeActionGroup->setExclusive(true); - cartesianPlotSelectionModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and edit"), cartesianPlotMouseModeActionGroup); + cartesianPlotSelectionModeAction = new QAction(QIcon::fromTheme("labplot-cursor-arrow"), i18n("Select and Edit"), cartesianPlotMouseModeActionGroup); cartesianPlotSelectionModeAction->setCheckable(true); cartesianPlotSelectionModeAction->setChecked(true); - cartesianPlotZoomSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select"), i18n("Select region and zoom in"), cartesianPlotMouseModeActionGroup); + cartesianPlotZoomSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select"), i18n("Select Region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomSelectionModeAction->setCheckable(true); - cartesianPlotZoomXSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-x"), i18n("Select x-region and zoom in"), cartesianPlotMouseModeActionGroup); + cartesianPlotZoomXSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-x"), i18n("Select x-region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomXSelectionModeAction->setCheckable(true); - cartesianPlotZoomYSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-y"), i18n("Select y-region and zoom in"), cartesianPlotMouseModeActionGroup); + cartesianPlotZoomYSelectionModeAction = new QAction(QIcon::fromTheme("labplot-zoom-select-y"), i18n("Select y-region and Zoom In"), cartesianPlotMouseModeActionGroup); cartesianPlotZoomYSelectionModeAction->setCheckable(true); connect(cartesianPlotMouseModeActionGroup, SIGNAL(triggered(QAction*)), SLOT(cartesianPlotMouseModeChanged(QAction*))); QActionGroup* cartesianPlotAddNewActionGroup = new QActionGroup(this); addCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve"), cartesianPlotAddNewActionGroup); - addEquationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-equation-curve"), i18n("xy-curve from a mathematical equation"), cartesianPlotAddNewActionGroup); + addEquationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-equation-curve"), i18n("xy-curve From a Mathematical Equation"), cartesianPlotAddNewActionGroup); // TODO: no own icons yet - addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve from a data operation"), cartesianPlotAddNewActionGroup); -// addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("xy-curve from a data operation"), cartesianPlotAddNewActionGroup); - addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve from a data reduction"), cartesianPlotAddNewActionGroup); -// addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("xy-curve from a data reduction"), cartesianPlotAddNewActionGroup); - addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve from a differentiation"), cartesianPlotAddNewActionGroup); -// addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("xy-curve from a differentiation"), cartesianPlotAddNewActionGroup); - addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve from an integration"), cartesianPlotAddNewActionGroup); -// addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("xy-curve from an integration"), cartesianPlotAddNewActionGroup); - - addInterpolationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("xy-curve from an interpolation"), cartesianPlotAddNewActionGroup); - addSmoothCurveAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("xy-curve from a smooth"), cartesianPlotAddNewActionGroup); - addFitCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("xy-curve from a fit to data"), cartesianPlotAddNewActionGroup); - addFourierFilterCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("xy-curve from a Fourier filter"), cartesianPlotAddNewActionGroup); - addFourierTransformCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("xy-curve from a Fourier transform"), cartesianPlotAddNewActionGroup); - addLegendAction = new QAction(QIcon::fromTheme("text-field"), i18n("legend"), cartesianPlotAddNewActionGroup); - addHorizontalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("horizontal axis"), cartesianPlotAddNewActionGroup); - addVerticalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("vertical axis"), cartesianPlotAddNewActionGroup); - addCustomPointAction = new QAction(QIcon::fromTheme("draw-cross"), i18n("custom point"), cartesianPlotAddNewActionGroup); + addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve From a Data Operation"), cartesianPlotAddNewActionGroup); +// addDataOperationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("xy-curve From a Data Operation"), cartesianPlotAddNewActionGroup); + addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve From a Data Reduction"), cartesianPlotAddNewActionGroup); +// addDataReductionCurveAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("xy-curve From a Data Reduction"), cartesianPlotAddNewActionGroup); + addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve From a Differentiation"), cartesianPlotAddNewActionGroup); +// addDifferentiationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("xy-curve From a Differentiation"), cartesianPlotAddNewActionGroup); + addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("xy-curve From an Integration"), cartesianPlotAddNewActionGroup); +// addIntegrationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("xy-curve From an Integration"), cartesianPlotAddNewActionGroup); + + addInterpolationCurveAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("xy-curve From an Interpolation"), cartesianPlotAddNewActionGroup); + addSmoothCurveAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("xy-curve From a Smooth"), cartesianPlotAddNewActionGroup); + addFitCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("xy-curve From a Fit to Data"), cartesianPlotAddNewActionGroup); + addFourierFilterCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("xy-curve From a Fourier Filter"), cartesianPlotAddNewActionGroup); + addFourierTransformCurveAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("xy-curve From a Fourier Transform"), cartesianPlotAddNewActionGroup); + addLegendAction = new QAction(QIcon::fromTheme("text-field"), i18n("Legend"), cartesianPlotAddNewActionGroup); + addHorizontalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-horizontal"), i18n("Horizontal Axis"), cartesianPlotAddNewActionGroup); + addVerticalAxisAction = new QAction(QIcon::fromTheme("labplot-axis-vertical"), i18n("Vertical Axis"), cartesianPlotAddNewActionGroup); + addCustomPointAction = new QAction(QIcon::fromTheme("draw-cross"), i18n("Custom Point"), cartesianPlotAddNewActionGroup); // Analysis menu // TODO: no own icons yet - addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data operation"), cartesianPlotAddNewActionGroup); -// addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data operation"), cartesianPlotAddNewActionGroup); - addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data reduction"), cartesianPlotAddNewActionGroup); -// addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Data reduction"), cartesianPlotAddNewActionGroup); + addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); +// addDataOperationAction = new QAction(QIcon::fromTheme("labplot-xy-data-operation-curve"), i18n("Data Operation"), cartesianPlotAddNewActionGroup); + addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); +// addDataReductionAction = new QAction(QIcon::fromTheme("labplot-xy-data-reduction-curve"), i18n("Data Reduction"), cartesianPlotAddNewActionGroup); addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); // addDifferentiationAction = new QAction(QIcon::fromTheme("labplot-xy-differentiation-curve"), i18n("Differentiation"), cartesianPlotAddNewActionGroup); addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); // addIntegrationAction = new QAction(QIcon::fromTheme("labplot-xy-integration-curve"), i18n("Integration"), cartesianPlotAddNewActionGroup); addInterpolationAction = new QAction(QIcon::fromTheme("labplot-xy-interpolation-curve"), i18n("Interpolation"), cartesianPlotAddNewActionGroup); addSmoothAction = new QAction(QIcon::fromTheme("labplot-xy-smoothing-curve"), i18n("Smooth"), cartesianPlotAddNewActionGroup); - addFitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Data fitting"), cartesianPlotAddNewActionGroup); - addFourierFilterAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier filter"), cartesianPlotAddNewActionGroup); - addFourierTransformAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("Fourier transform"), cartesianPlotAddNewActionGroup); + addFitAction = new QAction(QIcon::fromTheme("labplot-xy-fit-curve"), i18n("Data Fitting"), cartesianPlotAddNewActionGroup); + addFourierFilterAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-filter-curve"), i18n("Fourier Filter"), cartesianPlotAddNewActionGroup); + addFourierTransformAction = new QAction(QIcon::fromTheme("labplot-xy-fourier-transform-curve"), i18n("Fourier Transform"), cartesianPlotAddNewActionGroup); connect(cartesianPlotAddNewActionGroup, SIGNAL(triggered(QAction*)), SLOT(cartesianPlotAddNew(QAction*))); QActionGroup* cartesianPlotNavigationGroup = new QActionGroup(this); - scaleAutoAction = new QAction(QIcon::fromTheme("labplot-auto-scale-all"), i18n("auto scale"), cartesianPlotNavigationGroup); + scaleAutoAction = new QAction(QIcon::fromTheme("labplot-auto-scale-all"), i18n("Auto Scale"), cartesianPlotNavigationGroup); scaleAutoAction->setData(CartesianPlot::ScaleAuto); - scaleAutoXAction = new QAction(QIcon::fromTheme("labplot-auto-scale-x"), i18n("auto scale X"), cartesianPlotNavigationGroup); + scaleAutoXAction = new QAction(QIcon::fromTheme("labplot-auto-scale-x"), i18n("Auto Scale X"), cartesianPlotNavigationGroup); scaleAutoXAction->setData(CartesianPlot::ScaleAutoX); - scaleAutoYAction = new QAction(QIcon::fromTheme("labplot-auto-scale-y"), i18n("auto scale Y"), cartesianPlotNavigationGroup); + scaleAutoYAction = new QAction(QIcon::fromTheme("labplot-auto-scale-y"), i18n("Auto Scale Y"), cartesianPlotNavigationGroup); scaleAutoYAction->setData(CartesianPlot::ScaleAutoY); - zoomInAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("zoom in"), cartesianPlotNavigationGroup); + zoomInAction = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"), cartesianPlotNavigationGroup); zoomInAction->setData(CartesianPlot::ZoomIn); - zoomOutAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("zoom out"), cartesianPlotNavigationGroup); + zoomOutAction = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"), cartesianPlotNavigationGroup); zoomOutAction->setData(CartesianPlot::ZoomOut); - zoomInXAction = new QAction(QIcon::fromTheme("labplot-zoom-in-x"), i18n("zoom in X"), cartesianPlotNavigationGroup); + zoomInXAction = new QAction(QIcon::fromTheme("labplot-zoom-in-x"), i18n("Zoom In X"), cartesianPlotNavigationGroup); zoomInXAction->setData(CartesianPlot::ZoomInX); - zoomOutXAction = new QAction(QIcon::fromTheme("labplot-zoom-out-x"), i18n("zoom out X"), cartesianPlotNavigationGroup); + zoomOutXAction = new QAction(QIcon::fromTheme("labplot-zoom-out-x"), i18n("Zoom Out X"), cartesianPlotNavigationGroup); zoomOutXAction->setData(CartesianPlot::ZoomOutX); - zoomInYAction = new QAction(QIcon::fromTheme("labplot-zoom-in-y"), i18n("zoom in Y"), cartesianPlotNavigationGroup); + zoomInYAction = new QAction(QIcon::fromTheme("labplot-zoom-in-y"), i18n("Zoom In Y"), cartesianPlotNavigationGroup); zoomInYAction->setData(CartesianPlot::ZoomInY); - zoomOutYAction = new QAction(QIcon::fromTheme("labplot-zoom-out-y"), i18n("zoom out Y"), cartesianPlotNavigationGroup); + zoomOutYAction = new QAction(QIcon::fromTheme("labplot-zoom-out-y"), i18n("Zoom Out Y"), cartesianPlotNavigationGroup); zoomOutYAction->setData(CartesianPlot::ZoomOutY); - shiftLeftXAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("shift left X"), cartesianPlotNavigationGroup); + shiftLeftXAction = new QAction(QIcon::fromTheme("labplot-shift-left-x"), i18n("Shift Left X"), cartesianPlotNavigationGroup); shiftLeftXAction->setData(CartesianPlot::ShiftLeftX); - shiftRightXAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("shift right X"), cartesianPlotNavigationGroup); + shiftRightXAction = new QAction(QIcon::fromTheme("labplot-shift-right-x"), i18n("Shift Right X"), cartesianPlotNavigationGroup); shiftRightXAction->setData(CartesianPlot::ShiftRightX); - shiftUpYAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("shift up Y"), cartesianPlotNavigationGroup); + shiftUpYAction = new QAction(QIcon::fromTheme("labplot-shift-up-y"), i18n("Shift Up Y"), cartesianPlotNavigationGroup); shiftUpYAction->setData(CartesianPlot::ShiftUpY); - shiftDownYAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("shift down Y"), cartesianPlotNavigationGroup); + shiftDownYAction = new QAction(QIcon::fromTheme("labplot-shift-down-y"), i18n("Shift Down Y"), cartesianPlotNavigationGroup); shiftDownYAction->setData(CartesianPlot::ShiftDownY); connect(cartesianPlotNavigationGroup, SIGNAL(triggered(QAction*)), SLOT(cartesianPlotNavigationChanged(QAction*))); //set some default values selectionModeAction->setChecked(true); handleCartesianPlotActions(); currentZoomAction = zoomInViewAction; currentMagnificationAction = noMagnificationAction; } void WorksheetView::initMenus() { initActions(); m_addNewCartesianPlotMenu = new QMenu(i18n("xy-plot"), this); m_addNewCartesianPlotMenu->addAction(addCartesianPlot1Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot2Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot3Action); m_addNewCartesianPlotMenu->addAction(addCartesianPlot4Action); - m_addNewMenu = new QMenu(i18n("Add new"), this); + m_addNewMenu = new QMenu(i18n("Add New"), this); m_addNewMenu->addMenu(m_addNewCartesianPlotMenu)->setIcon(QIcon::fromTheme("office-chart-line")); m_addNewMenu->addSeparator(); m_addNewMenu->addAction(addTextLabelAction); m_viewMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); m_viewMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); m_viewMouseModeMenu->addAction(selectionModeAction); m_viewMouseModeMenu->addAction(navigationModeAction); m_viewMouseModeMenu->addAction(zoomSelectionModeAction); m_zoomMenu = new QMenu(i18n("Zoom"), this); m_zoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_zoomMenu->addAction(zoomInViewAction); m_zoomMenu->addAction(zoomOutViewAction); m_zoomMenu->addAction(zoomOriginAction); m_zoomMenu->addAction(zoomFitPageHeightAction); m_zoomMenu->addAction(zoomFitPageWidthAction); m_zoomMenu->addAction(zoomFitSelectionAction); m_magnificationMenu = new QMenu(i18n("Magnification"), this); m_magnificationMenu->setIcon(QIcon::fromTheme("labplot-zoom")); m_magnificationMenu->addAction(noMagnificationAction); m_magnificationMenu->addAction(twoTimesMagnificationAction); m_magnificationMenu->addAction(threeTimesMagnificationAction); m_magnificationMenu->addAction(fourTimesMagnificationAction); m_magnificationMenu->addAction(fiveTimesMagnificationAction); m_layoutMenu = new QMenu(i18n("Layout"), this); m_layoutMenu->addAction(verticalLayoutAction); m_layoutMenu->addAction(horizontalLayoutAction); m_layoutMenu->addAction(gridLayoutAction); m_layoutMenu->addSeparator(); m_layoutMenu->addAction(breakLayoutAction); m_gridMenu = new QMenu(i18n("Grid"), this); m_gridMenu->setIcon(QIcon::fromTheme("view-grid")); m_gridMenu->addAction(noGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(sparseLineGridAction); m_gridMenu->addAction(denseLineGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(sparseDotGridAction); m_gridMenu->addAction(denseDotGridAction); m_gridMenu->addSeparator(); m_gridMenu->addAction(customGridAction); //TODO: implement "snap to grid" and activate this action // m_gridMenu->addSeparator(); // m_gridMenu->addAction(snapToGridAction); m_cartesianPlotMenu = new QMenu(i18n("Cartesian Plot"), this); m_cartesianPlotMouseModeMenu = new QMenu(i18n("Mouse Mode"), this); m_cartesianPlotMouseModeMenu->setIcon(QIcon::fromTheme("input-mouse")); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomXSelectionModeAction); m_cartesianPlotMouseModeMenu->addAction(cartesianPlotZoomYSelectionModeAction); m_cartesianPlotMouseModeMenu->addSeparator(); - m_cartesianPlotAddNewMenu = new QMenu(i18n("Add new"), this); + m_cartesianPlotAddNewMenu = new QMenu(i18n("Add New"), this); m_cartesianPlotAddNewMenu->addAction(addCurveAction); m_cartesianPlotAddNewMenu->addAction(addEquationCurveAction); m_cartesianPlotAddNewMenu->addAction(addDataOperationCurveAction); m_cartesianPlotAddNewMenu->addAction(addDataReductionCurveAction); m_cartesianPlotAddNewMenu->addAction(addDifferentiationCurveAction); m_cartesianPlotAddNewMenu->addAction(addIntegrationCurveAction); m_cartesianPlotAddNewMenu->addAction(addInterpolationCurveAction); m_cartesianPlotAddNewMenu->addAction(addSmoothCurveAction); m_cartesianPlotAddNewMenu->addAction(addFitCurveAction); m_cartesianPlotAddNewMenu->addAction(addFourierFilterCurveAction); m_cartesianPlotAddNewMenu->addAction(addFourierTransformCurveAction); m_cartesianPlotAddNewMenu->addAction(addLegendAction); m_cartesianPlotAddNewMenu->addSeparator(); m_cartesianPlotAddNewMenu->addAction(addHorizontalAxisAction); m_cartesianPlotAddNewMenu->addAction(addVerticalAxisAction); m_cartesianPlotZoomMenu = new QMenu(i18n("Zoom/Navigate"), this); m_cartesianPlotZoomMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_cartesianPlotZoomMenu->addAction(scaleAutoAction); m_cartesianPlotZoomMenu->addAction(scaleAutoXAction); m_cartesianPlotZoomMenu->addAction(scaleAutoYAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInAction); m_cartesianPlotZoomMenu->addAction(zoomOutAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInXAction); m_cartesianPlotZoomMenu->addAction(zoomOutXAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(zoomInYAction); m_cartesianPlotZoomMenu->addAction(zoomOutYAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(shiftLeftXAction); m_cartesianPlotZoomMenu->addAction(shiftRightXAction); m_cartesianPlotZoomMenu->addSeparator(); m_cartesianPlotZoomMenu->addAction(shiftUpYAction); m_cartesianPlotZoomMenu->addAction(shiftDownYAction); - m_cartesianPlotActionModeMenu = new QMenu(i18n("Apply actions to"), this); + m_cartesianPlotActionModeMenu = new QMenu(i18n("Apply Actions to"), this); m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToSelectionAction); m_cartesianPlotActionModeMenu->addAction(cartesianPlotApplyToAllAction); m_cartesianPlotMenu->addMenu(m_cartesianPlotMouseModeMenu); m_cartesianPlotMenu->addMenu(m_cartesianPlotAddNewMenu); m_cartesianPlotMenu->addMenu(m_cartesianPlotZoomMenu); m_cartesianPlotMenu->addSeparator(); m_cartesianPlotMenu->addMenu(m_cartesianPlotActionModeMenu); // Data manipulation menu m_dataManipulationMenu = new QMenu(i18n("Data Manipulation")); m_dataManipulationMenu->setIcon(QIcon::fromTheme("zoom-draw")); m_dataManipulationMenu->addAction(addDataOperationAction); m_dataManipulationMenu->addAction(addDataReductionAction); //themes menu m_themeMenu = new QMenu(i18n("Apply Theme")); ThemesWidget* themeWidget = new ThemesWidget(0); connect(themeWidget, SIGNAL(themeSelected(QString)), m_worksheet, SLOT(setTheme(QString))); connect(themeWidget, SIGNAL(themeSelected(QString)), m_themeMenu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(themeWidget); m_themeMenu->addAction(widgetAction); m_menusInitialized = true; } /*! * Populates the menu \c menu with the worksheet and worksheet view relevant actions. * The menu is used * - as the context menu in WorksheetView * - as the "worksheet menu" in the main menu-bar (called form MainWin) * - as a part of the worksheet context menu in project explorer */ void WorksheetView::createContextMenu(QMenu* menu) { Q_ASSERT(menu != nullptr); if (!m_menusInitialized) initMenus(); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size() > 1) firstAction = menu->actions().at(1); menu->insertMenu(firstAction, m_addNewMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_viewMouseModeMenu); menu->insertMenu(firstAction, m_zoomMenu); menu->insertMenu(firstAction, m_magnificationMenu); menu->insertMenu(firstAction, m_layoutMenu); menu->insertMenu(firstAction, m_gridMenu); menu->insertMenu(firstAction, m_themeMenu); menu->insertSeparator(firstAction); menu->insertMenu(firstAction, m_cartesianPlotMenu); menu->insertSeparator(firstAction); menu->insertAction(firstAction, showPresenterMode); menu->insertSeparator(firstAction); } void WorksheetView::createAnalysisMenu(QMenu* menu) { Q_ASSERT(menu != nullptr); if (!m_menusInitialized) initMenus(); // Data manipulation menu menu->insertMenu(0, m_dataManipulationMenu); menu->addAction(addDifferentiationAction); menu->addAction(addIntegrationAction); menu->addAction(addInterpolationAction); menu->addAction(addSmoothAction); menu->addAction(addFitAction); menu->addAction(addFourierFilterAction); menu->addAction(addFourierTransformAction); } void WorksheetView::fillToolBar(QToolBar* toolBar) { toolBar->addSeparator(); tbNewCartesianPlot = new QToolButton(toolBar); tbNewCartesianPlot->setPopupMode(QToolButton::MenuButtonPopup); tbNewCartesianPlot->setMenu(m_addNewCartesianPlotMenu); tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action); toolBar->addWidget(tbNewCartesianPlot); toolBar->addAction(addTextLabelAction); toolBar->addSeparator(); toolBar->addAction(verticalLayoutAction); toolBar->addAction(horizontalLayoutAction); toolBar->addAction(gridLayoutAction); toolBar->addAction(breakLayoutAction); toolBar->addSeparator(); toolBar->addAction(selectionModeAction); toolBar->addAction(navigationModeAction); toolBar->addAction(zoomSelectionModeAction); tbZoom = new QToolButton(toolBar); tbZoom->setPopupMode(QToolButton::MenuButtonPopup); tbZoom->setMenu(m_zoomMenu); tbZoom->setDefaultAction(currentZoomAction); toolBar->addWidget(tbZoom); tbMagnification = new QToolButton(toolBar); tbMagnification->setPopupMode(QToolButton::MenuButtonPopup); tbMagnification->setMenu(m_magnificationMenu); tbMagnification->setDefaultAction(currentMagnificationAction); toolBar->addWidget(tbMagnification); } void WorksheetView::fillCartesianPlotToolBar(QToolBar* toolBar) { toolBar->addAction(cartesianPlotSelectionModeAction); toolBar->addAction(cartesianPlotZoomSelectionModeAction); toolBar->addAction(cartesianPlotZoomXSelectionModeAction); toolBar->addAction(cartesianPlotZoomYSelectionModeAction); toolBar->addSeparator(); toolBar->addAction(addCurveAction); toolBar->addAction(addEquationCurveAction); // don't over populate the tool bar // toolBar->addAction(addDifferentiationCurveAction); // toolBar->addAction(addIntegrationCurveAction); // toolBar->addAction(addDataOperationCurveAction); // toolBar->addAction(addDataReductionCurveAction); // toolBar->addAction(addInterpolationCurveAction); // toolBar->addAction(addSmoothCurveAction); // toolBar->addAction(addFitCurveAction); // toolBar->addAction(addFourierFilterCurveAction); // toolBar->addAction(addFourierTransformCurveAction); toolBar->addAction(addLegendAction); toolBar->addSeparator(); toolBar->addAction(addHorizontalAxisAction); toolBar->addAction(addVerticalAxisAction); toolBar->addSeparator(); toolBar->addAction(scaleAutoAction); toolBar->addAction(scaleAutoXAction); toolBar->addAction(scaleAutoYAction); toolBar->addAction(zoomInAction); toolBar->addAction(zoomOutAction); toolBar->addAction(zoomInXAction); toolBar->addAction(zoomOutXAction); toolBar->addAction(zoomInYAction); toolBar->addAction(zoomOutYAction); toolBar->addAction(shiftLeftXAction); toolBar->addAction(shiftRightXAction); toolBar->addAction(shiftUpYAction); toolBar->addAction(shiftDownYAction); handleCartesianPlotActions(); } void WorksheetView::setScene(QGraphicsScene* scene) { QGraphicsView::setScene(scene); } void WorksheetView::setIsClosing() { m_isClosing = true; } void WorksheetView::drawForeground(QPainter* painter, const QRectF& rect) { if (m_mouseMode==ZoomSelectionMode && m_selectionBandIsShown) { painter->save(); const QRectF& selRect = mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(); //TODO: don't hardcode for black here, use a a different color depending on the theme of the worksheet/plot under the mouse cursor? painter->setPen(QPen(Qt::black, 5/transform().m11())); painter->drawRect(selRect); painter->setBrush(QApplication::palette().color(QPalette::Highlight)); painter->setOpacity(0.2); painter->drawRect(selRect); painter->restore(); } QGraphicsView::drawForeground(painter, rect); } void WorksheetView::drawBackgroundItems(QPainter* painter, const QRectF& scene_rect) { // canvas painter->setOpacity(m_worksheet->backgroundOpacity()); if (m_worksheet->backgroundType() == PlotArea::Color) { switch (m_worksheet->backgroundColorStyle()) { case PlotArea::SingleColor: { painter->setBrush(QBrush(m_worksheet->backgroundFirstColor())); break; } case PlotArea::HorizontalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.topRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::VerticalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomLeft()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::TopLeftDiagonalLinearGradient: { QLinearGradient linearGrad(scene_rect.topLeft(), scene_rect.bottomRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::BottomLeftDiagonalLinearGradient: { QLinearGradient linearGrad(scene_rect.bottomLeft(), scene_rect.topRight()); linearGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); linearGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(linearGrad)); break; } case PlotArea::RadialGradient: { QRadialGradient radialGrad(scene_rect.center(), scene_rect.width()/2); radialGrad.setColorAt(0, m_worksheet->backgroundFirstColor()); radialGrad.setColorAt(1, m_worksheet->backgroundSecondColor()); painter->setBrush(QBrush(radialGrad)); break; } //default: // painter->setBrush(QBrush(m_worksheet->backgroundFirstColor())); } painter->drawRect(scene_rect); } else if (m_worksheet->backgroundType() == PlotArea::Image) { // background image const QString& backgroundFileName = m_worksheet->backgroundFileName().trimmed(); if ( !backgroundFileName.isEmpty() ) { QPixmap pix(backgroundFileName); switch (m_worksheet->backgroundImageStyle()) { case PlotArea::ScaledCropped: pix = pix.scaled(scene_rect.size().toSize(),Qt::KeepAspectRatioByExpanding,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::Scaled: pix = pix.scaled(scene_rect.size().toSize(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::ScaledAspectRatio: pix = pix.scaled(scene_rect.size().toSize(),Qt::KeepAspectRatio,Qt::SmoothTransformation); painter->drawPixmap(scene_rect.topLeft(),pix); break; case PlotArea::Centered: painter->drawPixmap(QPointF(scene_rect.center().x()-pix.size().width()/2,scene_rect.center().y()-pix.size().height()/2),pix); break; case PlotArea::Tiled: painter->drawTiledPixmap(scene_rect,pix); break; case PlotArea::CenterTiled: painter->drawTiledPixmap(scene_rect,pix,QPoint(scene_rect.size().width()/2,scene_rect.size().height()/2)); break; //default: // painter->drawPixmap(scene_rect.topLeft(),pix); } } } else if (m_worksheet->backgroundType() == PlotArea::Pattern) { // background pattern painter->setBrush(QBrush(m_worksheet->backgroundFirstColor(),m_worksheet->backgroundBrushStyle())); painter->drawRect(scene_rect); } //grid if (m_gridSettings.style != WorksheetView::NoGrid) { QColor c=m_gridSettings.color; c.setAlphaF(m_gridSettings.opacity); painter->setPen(c); qreal x, y; qreal left = scene_rect.left(); qreal right = scene_rect.right(); qreal top = scene_rect.top(); qreal bottom = scene_rect.bottom(); if (m_gridSettings.style==WorksheetView::LineGrid) { QLineF line; //horizontal lines y = top + m_gridSettings.verticalSpacing; while (y < bottom) { line.setLine( left, y, right, y ); painter->drawLine(line); y += m_gridSettings.verticalSpacing; } //vertical lines x = left + m_gridSettings.horizontalSpacing; while (x < right) { line.setLine( x, top, x, bottom ); painter->drawLine(line); x += m_gridSettings.horizontalSpacing; } } else { //DotGrid y = top + m_gridSettings.verticalSpacing; while (y < bottom) { x = left;// + m_gridSettings.horizontalSpacing; while (x < right) { x += m_gridSettings.horizontalSpacing; painter->drawPoint(x, y); } y += m_gridSettings.verticalSpacing; } } } } void WorksheetView::drawBackground(QPainter* painter, const QRectF& rect) { painter->save(); //painter->setRenderHint(QPainter::Antialiasing); QRectF scene_rect = sceneRect(); if (!m_worksheet->useViewSize()) { // background KColorScheme scheme(QPalette::Active, KColorScheme::Window); const QColor& color = scheme.background().color(); if (!scene_rect.contains(rect)) painter->fillRect(rect, color); //shadow // int shadowSize = scene_rect.width()*0.02; // QRectF rightShadowRect(scene_rect.right(), scene_rect.top() + shadowSize, shadowSize, scene_rect.height()); // QRectF bottomShadowRect(scene_rect.left() + shadowSize, scene_rect.bottom(), scene_rect.width(), shadowSize); // // const QColor& shadeColor = scheme.shade(color, KColorScheme::MidShade); // painter->fillRect(rightShadowRect.intersected(rect), shadeColor); // painter->fillRect(bottomShadowRect.intersected(rect), shadeColor); } drawBackgroundItems(painter, scene_rect); invalidateScene(rect, QGraphicsScene::BackgroundLayer); painter->restore(); } bool WorksheetView::isPlotAtPos(QPoint pos) const { bool plot = false; QGraphicsItem* item = itemAt(pos); if (item) { plot = item->data(0).toInt() == WorksheetElement::NameCartesianPlot; if (!plot && item->parentItem()) plot = item->parentItem()->data(0).toInt() == WorksheetElement::NameCartesianPlot; } return plot; } CartesianPlot* WorksheetView::plotAt(QPoint pos) const { QGraphicsItem* item = itemAt(pos); if (!item) return nullptr; QGraphicsItem* plotItem = nullptr; if (item->data(0).toInt() == WorksheetElement::NameCartesianPlot) plotItem = item; else { if (item->parentItem() && item->parentItem()->data(0).toInt() == WorksheetElement::NameCartesianPlot) plotItem = item->parentItem(); } if (plotItem == nullptr) return nullptr; CartesianPlot* plot = nullptr; for (auto* p : m_worksheet->children()) { if (p->graphicsItem() == plotItem) { plot = p; break; } } return plot; } //############################################################################## //#################################### Events ############################### //############################################################################## void WorksheetView::resizeEvent(QResizeEvent *event) { if (m_worksheet->useViewSize()) this->processResize(); QGraphicsView::resizeEvent(event); } void WorksheetView::wheelEvent(QWheelEvent* event) { //https://wiki.qt.io/Smooth_Zoom_In_QGraphicsView if (m_mouseMode == ZoomSelectionMode || m_ctrlPressed) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 15; // see QWheelEvent documentation zoom(numSteps); } else QGraphicsView::wheelEvent(event); } void WorksheetView::zoom(int numSteps) { m_numScheduledScalings += numSteps; if (m_numScheduledScalings * numSteps < 0) // if user moved the wheel in another direction, we reset previously scheduled scalings m_numScheduledScalings = numSteps; QTimeLine* anim = new QTimeLine(350, this); anim->setUpdateInterval(20); connect(anim, SIGNAL (valueChanged(qreal)), SLOT (scalingTime())); connect(anim, SIGNAL (finished()), SLOT (animFinished())); anim->start(); } void WorksheetView::scalingTime(){ qreal factor = 1.0 + qreal(m_numScheduledScalings) / 300.0; scale(factor, factor); } void WorksheetView::animFinished() { if (m_numScheduledScalings > 0) m_numScheduledScalings--; else m_numScheduledScalings++; sender()->~QObject(); } void WorksheetView::mousePressEvent(QMouseEvent* event) { //prevent the deselection of items when context menu event //was triggered (right button click) if (event->button() == Qt::RightButton) { event->accept(); return; } if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionStart = event->pos(); m_selectionEnd = m_selectionStart; //select&zoom'g starts -> reset the end point to the start point m_selectionBandIsShown = true; QGraphicsView::mousePressEvent(event); return; } //to select curves having overlapping bounding boxes we need to check whether the cursor //is inside of item's shapes and not inside of the bounding boxes (Qt's default behaviour). for (auto* item : items(event->pos())) { if (!dynamic_cast(item)) continue; if ( item->shape().contains(item->mapFromScene(mapToScene(event->pos()))) ) { //deselect currently selected items for (auto* selectedItem : scene()->selectedItems()) selectedItem->setSelected(false); //select the item under the cursor and update the current selection item->setSelected(true); selectionChanged(); return; } } // select the worksheet in the project explorer if the view was clicked // and there is no selection currently. We need this for the case when // there is a single worksheet in the project and we change from the project-node // in the project explorer to the worksheet-node by clicking the view. if ( scene()->selectedItems().isEmpty() ) m_worksheet->setSelectedInView(true); QGraphicsView::mousePressEvent(event); } void WorksheetView::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && m_mouseMode == ZoomSelectionMode) { m_selectionBandIsShown = false; viewport()->repaint(QRect(m_selectionStart, m_selectionEnd).normalized()); //don't zoom if very small region was selected, avoid occasional/unwanted zooming m_selectionEnd = event->pos(); if ( abs(m_selectionEnd.x() - m_selectionStart.x()) > 20 && abs(m_selectionEnd.y() - m_selectionStart.y()) > 20 ) fitInView(mapToScene(QRect(m_selectionStart, m_selectionEnd).normalized()).boundingRect(), Qt::KeepAspectRatio); } QGraphicsView::mouseReleaseEvent(event); } void WorksheetView::mouseMoveEvent(QMouseEvent* event) { if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode != CartesianPlot::SelectionMode ) { //check whether there is a cartesian plot under the cursor //and set the cursor appearance according to the current mouse mode for the cartesian plots if ( isPlotAtPos(event->pos()) ) { if (m_cartesianPlotMouseMode == CartesianPlot::ZoomSelectionMode) setCursor(Qt::CrossCursor); else if (m_cartesianPlotMouseMode == CartesianPlot::ZoomXSelectionMode) setCursor(Qt::SizeHorCursor); else if (m_cartesianPlotMouseMode == CartesianPlot::ZoomYSelectionMode) setCursor(Qt::SizeVerCursor); } else setCursor(Qt::ArrowCursor); } else if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode == CartesianPlot::SelectionMode ) setCursor(Qt::ArrowCursor); else if (m_selectionBandIsShown) { QRect rect = QRect(m_selectionStart, m_selectionEnd).normalized(); m_selectionEnd = event->pos(); rect = rect.united(QRect(m_selectionStart, m_selectionEnd).normalized()); qreal penWidth = 5/transform().m11(); rect.setX(rect.x()-penWidth); rect.setY(rect.y()-penWidth); rect.setHeight(rect.height()+2*penWidth); rect.setWidth(rect.width()+2*penWidth); viewport()->repaint(rect); } //show the magnification window if (magnificationFactor /*&& m_mouseMode == SelectAndEditMode*/) { if (!m_magnificationWindow) { m_magnificationWindow = new QGraphicsPixmapItem(0); m_magnificationWindow->setZValue(std::numeric_limits::max()); scene()->addItem(m_magnificationWindow); } m_magnificationWindow->setVisible(false); //copy the part of the view to be shown magnified QPointF pos = mapToScene(event->pos()); const int size = Worksheet::convertToSceneUnits(2.0, Worksheet::Centimeter)/transform().m11(); const QRectF copyRect(pos.x() - size/(2*magnificationFactor), pos.y() - size/(2*magnificationFactor), size/magnificationFactor, size/magnificationFactor); QPixmap px = QPixmap::grabWidget(this, mapFromScene(copyRect).boundingRect()); px = px.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); //draw the bounding rect QPainter painter(&px); const QPen pen = QPen(Qt::lightGray, 2/transform().m11()); painter.setPen(pen); QRect rect = px.rect(); rect.setWidth(rect.width()-pen.widthF()/2); rect.setHeight(rect.height()-pen.widthF()/2); painter.drawRect(rect); //set the pixmap m_magnificationWindow->setPixmap(px); m_magnificationWindow->setPos(pos.x()- px.width()/2, pos.y()- px.height()/2); m_magnificationWindow->setVisible(true); } else if (m_magnificationWindow) m_magnificationWindow->setVisible(false); QGraphicsView::mouseMoveEvent(event); } void WorksheetView::contextMenuEvent(QContextMenuEvent* e) { if ( (m_magnificationWindow && m_magnificationWindow->isVisible() && items(e->pos()).size() == 1) || !itemAt(e->pos()) ) { //no item or only the magnification window under the cursor -> show the context menu for the worksheet QMenu *menu = new QMenu(this); this->createContextMenu(menu); menu->exec(QCursor::pos()); } else { //propagate the event to the scene and graphics items QGraphicsView::contextMenuEvent(e); } } void WorksheetView::keyPressEvent(QKeyEvent* event) { if (event->matches(QKeySequence::Copy)) { //add here copying of objects exportToClipboard(); } else { if (event->key() == Qt::Key_Control) m_ctrlPressed = true; } QGraphicsView::keyPressEvent(event); } void WorksheetView::keyReleaseEvent(QKeyEvent* event) { m_ctrlPressed = false; QGraphicsView::keyReleaseEvent(event); } void WorksheetView::dragEnterEvent(QDragEnterEvent* event) { //ignore events not related to internal drags of columns etc., e.g. dropping of external files onto LabPlot const QMimeData* mimeData = event->mimeData(); if (!mimeData) { event->ignore(); return; } if (mimeData->formats().at(0) != QLatin1String("labplot-dnd")) { event->ignore(); return; } //select the worksheet in the project explorer and bring the view to the foreground m_worksheet->setSelectedInView(true); m_worksheet->mdiSubWindow()->mdiArea()->setActiveSubWindow(m_worksheet->mdiSubWindow()); event->setAccepted(true); } void WorksheetView::dragMoveEvent(QDragMoveEvent* event) { // only accept drop events if we have a plot under the cursor where we can drop columns onto bool plot = isPlotAtPos(event->pos()); event->setAccepted(plot); } void WorksheetView::dropEvent(QDropEvent* event) { CartesianPlot* plot = plotAt(event->pos()); if (plot != nullptr) plot->processDropEvent(event); } //############################################################################## //#################################### SLOTs ################################ //############################################################################## void WorksheetView::useViewSizeRequested() { if (m_worksheet->useViewSize()) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); zoomFitPageHeightAction->setVisible(false); zoomFitPageWidthAction->setVisible(false); currentZoomAction = zoomInViewAction; if (tbZoom) tbZoom->setDefaultAction(zoomInViewAction); //determine and set the current view size this->processResize(); } else { setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); zoomFitPageHeightAction->setVisible(true); zoomFitPageWidthAction->setVisible(true); } } void WorksheetView::processResize() { if (size() != sceneRect().size()) { static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); m_worksheet->setUndoAware(false); m_worksheet->setPageRect(QRectF(0.0, 0.0, width()/hscale, height()/vscale)); m_worksheet->setUndoAware(true); } } void WorksheetView::changeZoom(QAction* action) { if (action == zoomInViewAction) zoom(1); else if (action == zoomOutViewAction) zoom(-1); else if (action == zoomOriginAction) { static const float hscale = QApplication::desktop()->physicalDpiX()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); static const float vscale = QApplication::desktop()->physicalDpiY()/(Worksheet::convertToSceneUnits(1,Worksheet::Inch)); setTransform(QTransform::fromScale(hscale, vscale)); } else if (action == zoomFitPageWidthAction) { float scaleFactor = viewport()->width()/scene()->sceneRect().width(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); } else if (action == zoomFitPageHeightAction) { float scaleFactor = viewport()->height()/scene()->sceneRect().height(); setTransform(QTransform::fromScale(scaleFactor, scaleFactor)); } else if (action == zoomFitSelectionAction) fitInView(scene()->selectionArea().boundingRect(),Qt::KeepAspectRatio); currentZoomAction = action; if (tbZoom) tbZoom->setDefaultAction(action); } void WorksheetView::magnificationChanged(QAction* action) { if (action == noMagnificationAction) magnificationFactor = 0; else if (action == twoTimesMagnificationAction) magnificationFactor = 2; else if (action == threeTimesMagnificationAction) magnificationFactor = 3; else if (action == fourTimesMagnificationAction) magnificationFactor = 4; else if (action == fiveTimesMagnificationAction) magnificationFactor = 5; currentMagnificationAction = action; if (tbMagnification) tbMagnification->setDefaultAction(action); } void WorksheetView::mouseModeChanged(QAction* action) { if (action == selectionModeAction) { m_mouseMode = SelectionMode; setInteractive(true); setDragMode(QGraphicsView::NoDrag); } else if (action == navigationModeAction) { m_mouseMode = NavigationMode; setInteractive(false); setDragMode(QGraphicsView::ScrollHandDrag); } else { m_mouseMode = ZoomSelectionMode; setInteractive(false); setDragMode(QGraphicsView::NoDrag); } } //"Add new" related slots void WorksheetView::addNew(QAction* action) { WorksheetElement* aspect = 0; if (action == addCartesianPlot1Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::FourAxes); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot1Action); } else if (action == addCartesianPlot2Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxes); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot2Action); } else if (action == addCartesianPlot3Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxesCentered); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot3Action); } else if (action == addCartesianPlot4Action) { CartesianPlot* plot = new CartesianPlot(i18n("xy-plot")); plot->initDefault(CartesianPlot::TwoAxesCenteredZero); plot->setMouseMode(m_cartesianPlotMouseMode); aspect = plot; if (tbNewCartesianPlot) tbNewCartesianPlot->setDefaultAction(addCartesianPlot4Action); } else if (action == addTextLabelAction) { - TextLabel* l = new TextLabel(i18n("text label")); - l->setText(i18n("text label")); + TextLabel* l = new TextLabel(i18n("Text Label")); + l->setText(i18n("Text Label")); aspect = l; } if (!aspect) return; m_worksheet->addChild(aspect); handleCartesianPlotActions(); if (!m_fadeInTimeLine) { m_fadeInTimeLine = new QTimeLine(1000, this); m_fadeInTimeLine->setFrameRange(0, 100); connect(m_fadeInTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(fadeIn(qreal))); } //if there is already an element fading in, stop the time line and show the element with the full opacity. if (m_fadeInTimeLine->state() == QTimeLine::Running) { m_fadeInTimeLine->stop(); QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(); effect->setOpacity(1); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } //fade-in the newly added element lastAddedWorksheetElement = aspect; QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(); effect->setOpacity(0); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); m_fadeInTimeLine->start(); } /*! * select all top-level items */ void WorksheetView::selectAllElements() { //deselect all previously selected items since there can be some non top-level items belong them m_suppressSelectionChangedEvent = true; for (auto* item : m_selectedItems) m_worksheet->setItemSelectedInView(item, false); //select top-level items for (auto* item : scene()->items()) { if (!item->parentItem()) item->setSelected(true); } m_suppressSelectionChangedEvent = false; this->selectionChanged(); } /*! * deletes selected worksheet elements */ void WorksheetView::deleteElement() { if (m_selectedItems.isEmpty()) return; int rc = KMessageBox::warningYesNo( this, i18np("Do you really want to delete the selected object?", "Do you really want to delete the selected %1 objects?", m_selectedItems.size()), i18np("Delete selected object", "Delete selected objects", m_selectedItems.size())); if (rc == KMessageBox::No) return; m_suppressSelectionChangedEvent = true; m_worksheet->beginMacro(i18n("%1: Remove selected worksheet elements.", m_worksheet->name())); for (auto* item : m_selectedItems) m_worksheet->deleteAspectFromGraphicsItem(item); m_worksheet->endMacro(); m_suppressSelectionChangedEvent = false; } void WorksheetView::aspectAboutToBeRemoved(const AbstractAspect* aspect) { lastAddedWorksheetElement = dynamic_cast(const_cast(aspect)); if (!lastAddedWorksheetElement) return; //FIXME: fading-out doesn't work //also, the following code collides with undo/redo of the deletion //of a worksheet element (after redoing the element is not shown with the full opacity /* if (!m_fadeOutTimeLine) { m_fadeOutTimeLine = new QTimeLine(1000, this); m_fadeOutTimeLine->setFrameRange(0, 100); connect(m_fadeOutTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(fadeOut(qreal))); } //if there is already an element fading out, stop the time line if (m_fadeOutTimeLine->state() == QTimeLine::Running) m_fadeOutTimeLine->stop(); m_fadeOutTimeLine->start(); */ } void WorksheetView::fadeIn(qreal value) { QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(); effect->setOpacity(value); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } void WorksheetView::fadeOut(qreal value) { QGraphicsOpacityEffect* effect = new QGraphicsOpacityEffect(); effect->setOpacity(1 - value); lastAddedWorksheetElement->graphicsItem()->setGraphicsEffect(effect); } /*! * called when one of the layout-actions in WorkseetView was triggered. * sets the layout in Worksheet and enables/disables the layout actions. */ void WorksheetView::changeLayout(QAction* action) { if (action == breakLayoutAction) { verticalLayoutAction->setEnabled(true); verticalLayoutAction->setChecked(false); horizontalLayoutAction->setEnabled(true); horizontalLayoutAction->setChecked(false); gridLayoutAction->setEnabled(true); gridLayoutAction->setChecked(false); breakLayoutAction->setEnabled(false); m_worksheet->setLayout(Worksheet::NoLayout); } else { verticalLayoutAction->setEnabled(false); horizontalLayoutAction->setEnabled(false); gridLayoutAction->setEnabled(false); breakLayoutAction->setEnabled(true); if (action == verticalLayoutAction) { verticalLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::VerticalLayout); } else if (action == horizontalLayoutAction) { horizontalLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::HorizontalLayout); } else { gridLayoutAction->setChecked(true); m_worksheet->setLayout(Worksheet::GridLayout); } } } void WorksheetView::changeGrid(QAction* action) { if (action == noGridAction) { m_gridSettings.style = WorksheetView::NoGrid; snapToGridAction->setEnabled(false); } else if (action == sparseLineGridAction) { m_gridSettings.style = WorksheetView::LineGrid; m_gridSettings.color = Qt::gray; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 15; m_gridSettings.verticalSpacing = 15; } else if (action == denseLineGridAction) { m_gridSettings.style = WorksheetView::LineGrid; m_gridSettings.color = Qt::gray; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 5; m_gridSettings.verticalSpacing = 5; } else if (action == denseDotGridAction) { m_gridSettings.style = WorksheetView::DotGrid; m_gridSettings.color = Qt::black; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 5; m_gridSettings.verticalSpacing = 5; } else if (action == sparseDotGridAction) { m_gridSettings.style = WorksheetView::DotGrid; m_gridSettings.color = Qt::black; m_gridSettings.opacity = 0.7; m_gridSettings.horizontalSpacing = 15; m_gridSettings.verticalSpacing = 15; } else if (action == customGridAction) { GridDialog* dlg = new GridDialog(this); if (dlg->exec() == QDialog::Accepted) dlg->save(m_gridSettings); else return; } if (m_gridSettings.style == WorksheetView::NoGrid) snapToGridAction->setEnabled(false); else snapToGridAction->setEnabled(true); invalidateScene(sceneRect(), QGraphicsScene::BackgroundLayer); } //TODO void WorksheetView::changeSnapToGrid() { } /*! * Selects the QGraphicsItem \c item in \c WorksheetView. * The selection in \c ProjectExplorer is forwarded to \c Worksheet * and is finally handled here. */ void WorksheetView::selectItem(QGraphicsItem* item) { m_suppressSelectionChangedEvent = true; item->setSelected(true); m_selectedItems<setSelected(false); m_selectedItems.removeOne(item); handleCartesianPlotActions(); m_suppressSelectionChangedEvent = false; } /*! * Called on selection changes in the view. * Determines which items were selected and deselected * and forwards these changes to \c Worksheet */ void WorksheetView::selectionChanged() { //if the project is being closed, the scene items are being removed and the selection can change. //don't react on these changes since this can lead crashes (worksheet object is already in the destructor). if (m_isClosing) return; if (m_suppressSelectionChangedEvent) return; QList items = scene()->selectedItems(); //When making a graphics item invisible, it gets deselected in the scene. //In this case we don't want to deselect the item in the project explorer. bool invisibleDeselected = false; //check, whether the previously selected items were deselected now. //Forward the deselection prior to the selection of new items //in order to avoid the unwanted multiple selection in project explorer for (auto* item : m_selectedItems ) { if ( items.indexOf(item) == -1 ) { if (item->isVisible()) m_worksheet->setItemSelectedInView(item, false); else invisibleDeselected = true; } } //select new items if (items.isEmpty() && invisibleDeselected == false) { //no items selected -> select the worksheet again. m_worksheet->setSelectedInView(true); //if one of the "zoom&select" plot mouse modes was selected before, activate the default "selection mode" again //since no plots are selected now. if (m_mouseMode == SelectionMode && m_cartesianPlotMouseMode!= CartesianPlot::SelectionMode) { cartesianPlotSelectionModeAction->setChecked(true); cartesianPlotMouseModeChanged(cartesianPlotSelectionModeAction); } } else { for (const auto* item : items) m_worksheet->setItemSelectedInView(item, true); //items selected -> deselect the worksheet in the project explorer //prevents unwanted multiple selection with worksheet (if it was selected before) m_worksheet->setSelectedInView(false); } m_selectedItems = items; handleCartesianPlotActions(); } //check whether we have cartesian plots selected and activate/deactivate void WorksheetView::handleCartesianPlotActions() { if (!m_menusInitialized) return; bool plot = false; if (m_cartesianPlotActionMode == ApplyActionToSelection) { //check whether we have cartesian plots selected for (auto* item : m_selectedItems) { //TODO: or if a children of a plot is selected if (item->data(0).toInt() == WorksheetElement::NameCartesianPlot) { plot = true; break; } } } else { //actions are applied to all available plots -> check whether we have plots plot = (m_worksheet->children().size() != 0); } cartesianPlotSelectionModeAction->setEnabled(plot); cartesianPlotZoomSelectionModeAction->setEnabled(plot); cartesianPlotZoomXSelectionModeAction->setEnabled(plot); cartesianPlotZoomYSelectionModeAction->setEnabled(plot); addCurveAction->setEnabled(plot); addEquationCurveAction->setEnabled(plot); addDataOperationCurveAction->setEnabled(false); addDataReductionCurveAction->setEnabled(plot); addDifferentiationCurveAction->setEnabled(plot); addIntegrationCurveAction->setEnabled(plot); addInterpolationCurveAction->setEnabled(plot); addSmoothCurveAction->setEnabled(plot); addFitCurveAction->setEnabled(plot); addFourierFilterCurveAction->setEnabled(plot); addFourierTransformCurveAction->setEnabled(plot); addHorizontalAxisAction->setEnabled(plot); addVerticalAxisAction->setEnabled(plot); addLegendAction->setEnabled(plot); scaleAutoXAction->setEnabled(plot); scaleAutoYAction->setEnabled(plot); scaleAutoAction->setEnabled(plot); zoomInAction->setEnabled(plot); zoomOutAction->setEnabled(plot); zoomInXAction->setEnabled(plot); zoomOutXAction->setEnabled(plot); zoomInYAction->setEnabled(plot); zoomOutYAction->setEnabled(plot); shiftLeftXAction->setEnabled(plot); shiftRightXAction->setEnabled(plot); shiftUpYAction->setEnabled(plot); shiftDownYAction->setEnabled(plot); // analysis menu //TODO: enable also if children of plots are selected addDataOperationAction->setEnabled(false); m_dataManipulationMenu->setEnabled(plot); addDifferentiationAction->setEnabled(plot); addIntegrationAction->setEnabled(plot); addInterpolationAction->setEnabled(plot); addSmoothAction->setEnabled(plot); addFitAction->setEnabled(plot); addFourierFilterAction->setEnabled(plot); addFourierTransformAction->setEnabled(plot); } void WorksheetView::exportToFile(const QString& path, const ExportFormat format, const ExportArea area, const bool background, const int resolution) { QRectF sourceRect; //determine the rectangular to print if (area == WorksheetView::ExportBoundingBox) sourceRect = scene()->itemsBoundingRect(); else if (area == WorksheetView::ExportSelection) { //TODO doesn't work: rect = scene()->selectionArea().boundingRect(); for (const auto* item : m_selectedItems) sourceRect = sourceRect.united( item->mapToScene(item->boundingRect()).boundingRect() ); } else sourceRect = scene()->sceneRect(); //print if (format == WorksheetView::Pdf) { QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(path); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); printer.setPaperSize( QSizeF(w, h), QPrinter::Millimeter); printer.setPageMargins(0,0,0,0, QPrinter::Millimeter); printer.setPrintRange(QPrinter::PageRange); printer.setCreator(QLatin1String("LabPlot ") + LVERSION); QPainter painter(&printer); painter.setRenderHint(QPainter::Antialiasing); QRectF targetRect(0, 0, painter.device()->width(),painter.device()->height()); painter.begin(&printer); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); } else if (format == WorksheetView::Svg) { QSvgGenerator generator; generator.setFileName(path); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*QApplication::desktop()->physicalDpiX()/25.4; h = h*QApplication::desktop()->physicalDpiY()/25.4; generator.setSize(QSize(w, h)); QRectF targetRect(0, 0, w, h); generator.setViewBox(targetRect); QPainter painter; painter.begin(&generator); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); } else { //PNG //TODO add all formats supported by Qt in QImage int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*resolution/25.4; h = h*resolution/25.4; QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QRectF targetRect(0, 0, w, h); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, background); painter.end(); image.save(path, "PNG"); } } void WorksheetView::exportToClipboard() { #ifndef QT_NO_CLIPBOARD QRectF sourceRect; if (m_selectedItems.size() == 0) sourceRect = scene()->itemsBoundingRect(); else { //export selection for (const auto* item : m_selectedItems) sourceRect = sourceRect.united( item->mapToScene(item->boundingRect()).boundingRect() ); } int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w = w*QApplication::desktop()->physicalDpiX()/25.4; h = h*QApplication::desktop()->physicalDpiY()/25.4; QImage image(QSize(w, h), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QRectF targetRect(0, 0, w, h); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, true); painter.end(); QClipboard* clipboard = QApplication::clipboard(); clipboard->setImage(image, QClipboard::Clipboard); #endif } void WorksheetView::exportPaint(QPainter* painter, const QRectF& targetRect, const QRectF& sourceRect, const bool background) { //draw the background if (background) { painter->save(); painter->scale(targetRect.width()/sourceRect.width(), targetRect.height()/sourceRect.height()); drawBackground(painter, sourceRect); painter->restore(); } //draw the scene items m_worksheet->setPrinting(true); scene()->render(painter, QRectF(), sourceRect); m_worksheet->setPrinting(false); } void WorksheetView::print(QPrinter* printer) { m_worksheet->setPrinting(true); QPainter painter(printer); painter.setRenderHint(QPainter::Antialiasing); // draw background QRectF page_rect = printer->pageRect(); QRectF scene_rect = scene()->sceneRect(); //qDebug()<<"source (scene):"<name(), selectedPlots)); for (auto* plot : plots) { //TODO: or if any children of a plot is selected if (m_selectedItems.indexOf(plot->graphicsItem()) != -1) this->cartesianPlotAdd(plot, action); } if (selectedPlots > 1) m_worksheet->endMacro(); } else { if (plots.size() > 1) m_worksheet->beginMacro(i18n("%1: Add curve to %2 plots", m_worksheet->name(), plots.size())); for (auto* plot : plots) this->cartesianPlotAdd(plot, action); if (plots.size() > 1) m_worksheet->endMacro(); } } void WorksheetView::cartesianPlotAdd(CartesianPlot* plot, QAction* action) { DEBUG("WorksheetView::cartesianPlotAdd()"); if (action == addCurveAction) plot->addCurve(); else if (action == addEquationCurveAction) plot->addEquationCurve(); else if (action == addDataReductionCurveAction) plot->addDataReductionCurve(); else if (action == addDifferentiationCurveAction) plot->addDifferentiationCurve(); else if (action == addIntegrationCurveAction) plot->addIntegrationCurve(); else if (action == addInterpolationCurveAction) plot->addInterpolationCurve(); else if (action == addFitCurveAction) plot->addFitCurve(); else if (action == addFourierFilterCurveAction) plot->addFourierFilterCurve(); else if (action == addFourierTransformCurveAction) plot->addFourierTransformCurve(); else if (action == addSmoothCurveAction) plot->addSmoothCurve(); else if (action == addLegendAction) plot->addLegend(); else if (action == addHorizontalAxisAction) plot->addHorizontalAxis(); else if (action == addVerticalAxisAction) plot->addVerticalAxis(); else if (action == addCustomPointAction) plot->addCustomPoint(); // analysis actions else if (action == addDataReductionAction) plot->addDataReductionCurve(); else if (action == addDifferentiationAction) plot->addDifferentiationCurve(); else if (action == addIntegrationAction) plot->addIntegrationCurve(); else if (action == addInterpolationAction) plot->addInterpolationCurve(); else if (action == addFitAction) plot->addFitCurve(); else if (action == addFourierFilterAction) plot->addFourierFilterCurve(); else if (action == addFourierTransformAction) plot->addFourierTransformCurve(); else if (action == addSmoothAction) plot->addSmoothCurve(); } void WorksheetView::cartesianPlotNavigationChanged(QAction* action) { CartesianPlot::NavigationOperation op = (CartesianPlot::NavigationOperation)action->data().toInt(); if (m_cartesianPlotActionMode == ApplyActionToSelection) { for (auto* plot : m_worksheet->children() ) { if (m_selectedItems.indexOf(plot->graphicsItem()) != -1) plot->navigate(op); } } else { for (auto* plot : m_worksheet->children() ) plot->navigate(op); } } void WorksheetView::presenterMode() { KConfigGroup group = KSharedConfig::openConfig()->group("Settings_Worksheet"); //show dynamic presenter widget, if enabled if (group.readEntry("PresenterModeInteractive", false)) { DynamicPresenterWidget* dynamicPresenterWidget = new DynamicPresenterWidget(m_worksheet); dynamicPresenterWidget->showFullScreen(); return; } //show static presenter widget (default) QRectF sourceRect(scene()->sceneRect()); int w = Worksheet::convertFromSceneUnits(sourceRect.width(), Worksheet::Millimeter); int h = Worksheet::convertFromSceneUnits(sourceRect.height(), Worksheet::Millimeter); w *= QApplication::desktop()->physicalDpiX()/25.4; h *= QApplication::desktop()->physicalDpiY()/25.4; QRectF targetRect(0, 0, w, h); QDesktopWidget* const dw = QApplication::desktop(); const int primaryScreenIdx = dw->primaryScreen(); const QRectF& screenSize = dw->availableGeometry(primaryScreenIdx); if (targetRect.width() > screenSize.width() || ((targetRect.height() > screenSize.height()))) { const double ratio = qMin(screenSize.width() / targetRect.width(), screenSize.height() / targetRect.height()); targetRect.setWidth(targetRect.width()* ratio); targetRect.setHeight(targetRect.height() * ratio); } QImage image(QSize(targetRect.width(), targetRect.height()), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); exportPaint(&painter, targetRect, sourceRect, true); painter.end(); PresenterWidget* presenterWidget = new PresenterWidget(QPixmap::fromImage(image), m_worksheet->name()); presenterWidget->showFullScreen(); } diff --git a/src/kdefrontend/GuiObserver.cpp b/src/kdefrontend/GuiObserver.cpp index 535e6d371..37ed7713b 100644 --- a/src/kdefrontend/GuiObserver.cpp +++ b/src/kdefrontend/GuiObserver.cpp @@ -1,574 +1,574 @@ /*************************************************************************** File : GuiObserver.cpp Project : LabPlot Description : GUI observer -------------------------------------------------------------------- Copyright : (C) 2010-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2017 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/matrix/Matrix.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/plots/cartesian/BarChartPlot.h" #include "backend/worksheet/TextLabel.h" #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #include "backend/core/Project.h" #include "backend/datapicker/Datapicker.h" #include "backend/datapicker/DatapickerImage.h" #include "backend/datapicker/DatapickerCurve.h" #include "commonfrontend/ProjectExplorer.h" #include "kdefrontend/MainWin.h" #include "kdefrontend/dockwidgets/AxisDock.h" #include "kdefrontend/dockwidgets/NoteDock.h" #include "kdefrontend/dockwidgets/CartesianPlotDock.h" #include "kdefrontend/dockwidgets/CartesianPlotLegendDock.h" #include "kdefrontend/dockwidgets/ColumnDock.h" #include "kdefrontend/dockwidgets/LiveDataDock.h" #include "kdefrontend/dockwidgets/MatrixDock.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/XYFitCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierFilterCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierTransformCurveDock.h" #include "kdefrontend/dockwidgets/XYSmoothCurveDock.h" #include "kdefrontend/dockwidgets/CustomPointDock.h" #include "kdefrontend/dockwidgets/WorksheetDock.h" #ifdef HAVE_CANTOR_LIBS #include "kdefrontend/dockwidgets/CantorWorksheetDock.h" #endif #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/widgets/DatapickerImageWidget.h" #include "kdefrontend/widgets/DatapickerCurveWidget.h" #include #include #include #include #include /*! \class GuiObserver \brief The GUI observer looks for the selection changes in the main window and shows/hides the correspondings dock widgets, toolbars etc. This class is intended to simplify (or not to overload) the code in MainWin. \ingroup kdefrontend */ GuiObserver::GuiObserver(MainWin* mainWin) { connect(mainWin->m_projectExplorer, SIGNAL(selectedAspectsChanged(QList&)), this, SLOT(selectedAspectsChanged(QList&)) ); connect(mainWin->m_projectExplorer, SIGNAL(hiddenAspectSelected(const AbstractAspect*)), this, SLOT(hiddenAspectSelected(const AbstractAspect*)) ); m_mainWindow = mainWin; } /*! called on selection changes in the project explorer. Determines the type of the currently selected objects (aspects) and activates the corresponding dockwidgets, toolbars etc. */ void GuiObserver::selectedAspectsChanged(QList& selectedAspects) const { if (selectedAspects.isEmpty()) { if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->hide(); - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Properties")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); return; } QString prevClassName, className; //check, whether objects of different types where selected //don't show any dock widgets in this case. for (auto* aspect: selectedAspects) { className = aspect->metaObject()->className(); if (className != prevClassName && !prevClassName.isEmpty()) { if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->hide(); - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Properties")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); return; } prevClassName = className; } if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->show(); if (className == "Spreadsheet") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Spreadsheet")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet")); if (!m_mainWindow->spreadsheetDock) { m_mainWindow->spreadsheetDock = new SpreadsheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->spreadsheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->spreadsheetDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->spreadsheetDock->setSpreadsheets(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->spreadsheetDock); } else if (className == "Column") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Column")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column")); if (!m_mainWindow->columnDock) { m_mainWindow->columnDock = new ColumnDock(m_mainWindow->stackedWidget); connect(m_mainWindow->columnDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->columnDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->columnDock->setColumns(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->columnDock); } else if (className == "Matrix") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Matrix")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix")); if (!m_mainWindow->matrixDock) { m_mainWindow->matrixDock = new MatrixDock(m_mainWindow->stackedWidget); connect(m_mainWindow->matrixDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->matrixDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->matrixDock->setMatrices(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->matrixDock); } else if (className == "Worksheet") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Worksheet")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Worksheet")); if (!m_mainWindow->worksheetDock) { m_mainWindow->worksheetDock = new WorksheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->worksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->worksheetDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->worksheetDock->setWorksheets(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->worksheetDock); } else if (className == "CartesianPlot") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Cartesian Plot")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot")); if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cartesianPlotDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->cartesianPlotDock->setPlots(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cartesianPlotDock); } else if (className == "CartesianPlotLegend") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Legend")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend")); if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cartesianPlotLegendDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->cartesianPlotLegendDock->setLegends(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cartesianPlotLegendDock); } else if (className == "Axis") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Axis")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis")); if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); connect(m_mainWindow->axisDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->axisDock->setAxes(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->axisDock); } else if (className == "XYCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("xy-Curve")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve")); if (!m_mainWindow->xyCurveDock) { m_mainWindow->xyCurveDock = new XYCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyCurveDock->setupGeneral(); connect(m_mainWindow->xyCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyCurveDock); } else if (className == "XYEquationCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("xy-Equation")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation")); if (!m_mainWindow->xyEquationCurveDock) { m_mainWindow->xyEquationCurveDock = new XYEquationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyEquationCurveDock->setupGeneral(); connect(m_mainWindow->xyEquationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyEquationCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyEquationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyEquationCurveDock); } else if (className == "XYDataReductionCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Data reduction")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Data Reduction")); if (!m_mainWindow->xyDataReductionCurveDock) { m_mainWindow->xyDataReductionCurveDock = new XYDataReductionCurveDock(m_mainWindow->stackedWidget, m_mainWindow->statusBar()); m_mainWindow->xyDataReductionCurveDock->setupGeneral(); connect(m_mainWindow->xyDataReductionCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyDataReductionCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock); } else if (className == "XYDifferentiationCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Differentiation")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation")); if (!m_mainWindow->xyDifferentiationCurveDock) { m_mainWindow->xyDifferentiationCurveDock = new XYDifferentiationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyDifferentiationCurveDock->setupGeneral(); connect(m_mainWindow->xyDifferentiationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDifferentiationCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyDifferentiationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDifferentiationCurveDock); } else if (className == "XYIntegrationCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Integration")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration")); if (!m_mainWindow->xyIntegrationCurveDock) { m_mainWindow->xyIntegrationCurveDock = new XYIntegrationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyIntegrationCurveDock->setupGeneral(); connect(m_mainWindow->xyIntegrationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyIntegrationCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyIntegrationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyIntegrationCurveDock); } else if (className == "XYInterpolationCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Interpolation")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation")); if (!m_mainWindow->xyInterpolationCurveDock) { m_mainWindow->xyInterpolationCurveDock = new XYInterpolationCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyInterpolationCurveDock->setupGeneral(); connect(m_mainWindow->xyInterpolationCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyInterpolationCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyInterpolationCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyInterpolationCurveDock); } else if (className == "XYFitCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Fit")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit")); if (!m_mainWindow->xyFitCurveDock) { m_mainWindow->xyFitCurveDock = new XYFitCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFitCurveDock->setupGeneral(); connect(m_mainWindow->xyFitCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFitCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFitCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFitCurveDock); } else if (className == "XYFourierTransformCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Fourier Transform")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform")); if (!m_mainWindow->xyFourierTransformCurveDock) { m_mainWindow->xyFourierTransformCurveDock = new XYFourierTransformCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFourierTransformCurveDock->setupGeneral(); connect(m_mainWindow->xyFourierTransformCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFourierTransformCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFourierTransformCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFourierTransformCurveDock); } else if (className == "XYFourierFilterCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Fourier Filter")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter")); if (!m_mainWindow->xyFourierFilterCurveDock) { m_mainWindow->xyFourierFilterCurveDock = new XYFourierFilterCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xyFourierFilterCurveDock->setupGeneral(); connect(m_mainWindow->xyFourierFilterCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyFourierFilterCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xyFourierFilterCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyFourierFilterCurveDock); } else if (className == "XYSmoothCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Smoothing")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing")); if (!m_mainWindow->xySmoothCurveDock) { m_mainWindow->xySmoothCurveDock = new XYSmoothCurveDock(m_mainWindow->stackedWidget); m_mainWindow->xySmoothCurveDock->setupGeneral(); connect(m_mainWindow->xySmoothCurveDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xySmoothCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->xySmoothCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xySmoothCurveDock); } else if (className=="Histogram") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Histogram properties")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties")); if (!m_mainWindow->histogramDock) { m_mainWindow->histogramDock = new HistogramDock(m_mainWindow->stackedWidget); m_mainWindow->histogramDock->setupGeneral(); connect(m_mainWindow->histogramDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->histogramDock); } QList list; for (auto* aspect: selectedAspects) list<(aspect); m_mainWindow->histogramDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->histogramDock); } else if (className == "TextLabel") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Text Label")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label")); if (!m_mainWindow->textLabelDock) { m_mainWindow->textLabelDock = new LabelWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->textLabelDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->textLabelDock->setLabels(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->textLabelDock); } else if (className == "CustomPoint") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Custom Point")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point")); if (!m_mainWindow->customPointDock) { m_mainWindow->customPointDock = new CustomPointDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->customPointDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->customPointDock->setPoints(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->customPointDock); } else if (className == "DatapickerCurve") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Datapicker Curve")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve")); if (!m_mainWindow->datapickerCurveDock) { m_mainWindow->datapickerCurveDock = new DatapickerCurveWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->datapickerCurveDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->datapickerCurveDock->setCurves(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->datapickerCurveDock); } else if (className == "Datapicker") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Datapicker")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker")); if (!m_mainWindow->datapickerImageDock) { m_mainWindow->datapickerImageDock = new DatapickerImageWidget(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->datapickerImageDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect)->image(); m_mainWindow->datapickerImageDock->setImages(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->datapickerImageDock); } else if (className == "Project") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Project")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project")); if (!m_mainWindow->projectDock) { m_mainWindow->projectDock = new ProjectDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->projectDock); } m_mainWindow->projectDock->setProject(m_mainWindow->m_project); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->projectDock); } else if (className=="CantorWorksheet") { #ifdef HAVE_CANTOR_LIBS if (!m_mainWindow->cantorWorksheetDock) { m_mainWindow->cantorWorksheetDock = new CantorWorksheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->cantorWorksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cantorWorksheetDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); if (list.size() == 1) m_mainWindow->m_propertiesDock->setWindowTitle(list.first()->backendName() + " Properties"); else m_mainWindow->m_propertiesDock->setWindowTitle("CAS Properties"); m_mainWindow->cantorWorksheetDock->setCantorWorksheets(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->cantorWorksheetDock); #endif } else if (className == "Notes") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Notes")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes")); if (!m_mainWindow->notesDock) { m_mainWindow->notesDock = new NoteDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->notesDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->notesDock->setNotesList(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->notesDock); } else if (className == "LiveDataSource") { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Live data source")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source")); if (!m_mainWindow->m_liveDataDock) { m_mainWindow->m_liveDataDock = new LiveDataDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->m_liveDataDock); } QList list; for (auto* aspect: selectedAspects) list << qobject_cast(aspect); m_mainWindow->m_liveDataDock->setLiveDataSources(list); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->m_liveDataDock); } else { - m_mainWindow->m_propertiesDock->setWindowTitle(i18n("Properties")); + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->hide(); } } /*! handles the selection of a hidden aspect \c aspect in the view (relevant for WorksheetView only at the moment). Currently, a hidden aspect can only be a plot title lable or an axis label. -> Activate the corresponding DockWidget and make the title tab current. */ void GuiObserver::hiddenAspectSelected(const AbstractAspect* aspect) const { const AbstractAspect* parent = aspect->parentAspect(); if (!parent) return; QString className = parent->metaObject()->className(); if (className == "Axis") { if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } m_mainWindow->axisDock->activateTitleTab(); } else if (className == "CartesianPlot") { if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } m_mainWindow->cartesianPlotDock->activateTitleTab(); } else if (className == "CartesianPlotLegend") { if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } m_mainWindow->cartesianPlotLegendDock->activateTitleTab(); } } diff --git a/src/kdefrontend/GuiTools.cpp b/src/kdefrontend/GuiTools.cpp index f040c393d..d5ba77138 100644 --- a/src/kdefrontend/GuiTools.cpp +++ b/src/kdefrontend/GuiTools.cpp @@ -1,229 +1,229 @@ /*************************************************************************** File : GuiTools.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2013 Alexander Semke (alexander.semke*web.de) (replace * with @ in the email addresses) Description : constains several static functions which are used on frequently throughout the kde frontend. ***************************************************************************/ /*************************************************************************** * * * 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 "GuiTools.h" #include #include #include #include #include #include static const int colorsCount = 26; static QColor colors[colorsCount] = {QColor(255,255,255), QColor(0,0,0), QColor(192,0,0), QColor(255,0,0), QColor(255,192,192), //red QColor(0,192,0), QColor(0,255,0), QColor(192,255,192), //green QColor(0,0,192), QColor(0,0,255), QColor(192,192,255), //blue QColor(192,192,0), QColor(255,255,0), QColor(255,255,192), //yellow QColor(0,192,192), QColor(0,255,255), QColor(192,255,255), //cyan QColor(192,0,192), QColor(255,0,255), QColor(255,192,255), //magenta QColor(192,88,0), QColor(255,128,0), QColor(255,168,88), //orange QColor(128,128,128), QColor(160,160,160), QColor(195,195,195) //grey }; /*! fills the ComboBox \c combobox with the six possible Qt::PenStyles, the color \c color is used. */ void GuiTools::updatePenStyles(QComboBox* comboBox, const QColor& color){ int index=comboBox->currentIndex(); comboBox->clear(); QPainter pa; int offset=2; int w=50; int h=10; QPixmap pm( w, h ); comboBox->setIconSize( QSize(w,h) ); //loop over six possible Qt-PenStyles, draw on the pixmap and insert it - static QString list[6] = { i18n("no line"), i18n("solid line"), i18n("dash line"), - i18n("dot line"), i18n("dash-dot line"), i18n("dash-dot-dot line") }; + static QString list[6] = { i18n("No Line"), i18n("Solid Line"), i18n("Dash Line"), + i18n("Dot Line"), i18n("Dash-dot Line"), i18n("Dash-dot-dot Line") }; for (int i=0;i<6;i++){ pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen( QPen( color, 1, (Qt::PenStyle)i ) ); pa.drawLine( offset, h/2, w-offset, h/2); pa.end(); comboBox->addItem( QIcon(pm), list[i] ); } comboBox->setCurrentIndex(index); } /*! fills the QMenu \c menu with the six possible Qt::PenStyles, the color \c color is used. QActions are created with \c actionGroup as the parent, if not available. If already available, onle the color in the QAction's icons is updated. */ void GuiTools::updatePenStyles(QMenu* menu, QActionGroup* actionGroup, const QColor& color){ QPainter pa; int offset=2; int w=50; int h=10; QPixmap pm( w, h ); //loop over six possible Qt-PenStyles, draw on the pixmap and insert it - static QString list[6] = { i18n("no line"), i18n("solid line"), i18n("dash line"), - i18n("dot line"), i18n("dash-dot line"), i18n("dash-dot-dot line") }; + static QString list[6] = { i18n("No Line"), i18n("Solid Line"), i18n("Dash Line"), + i18n("Dot Line"), i18n("Dash-dot Line"), i18n("Dash-dot-dot Line") }; QAction* action; if (actionGroup->actions().isEmpty()){ //TODO setting of the icon size doesn't work here menu->setStyleSheet( "QMenu::icon { width:50px; height:10px; }" ); for (int i=0;i<6;i++){ pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen( QPen( color, 1, (Qt::PenStyle)i ) ); pa.drawLine( offset, h/2, w-offset, h/2); pa.end(); action = new QAction( QIcon(pm), list[i], actionGroup ); action->setCheckable(true); menu->addAction( action ); } }else{ for (int i=0;i<6;i++){ pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen( QPen( color, 1, (Qt::PenStyle)i ) ); pa.drawLine( offset, h/2, w-offset, h/2); pa.end(); action = actionGroup->actions().at(i); action->setIcon( QIcon(pm) ); } } } void GuiTools::selectPenStyleAction(QActionGroup* actionGroup, Qt::PenStyle style) { int index = (int)style; Q_ASSERT(index < actionGroup->actions().size()); actionGroup->actions().at(index)->setChecked(true); } Qt::PenStyle GuiTools::penStyleFromAction(QActionGroup* actionGroup, QAction* action) { int index = actionGroup->actions().indexOf(action); return Qt::PenStyle(index); } /*! fills the ComboBox for the symbol filling patterns with the 14 possible Qt::BrushStyles. */ void GuiTools::updateBrushStyles(QComboBox* comboBox, const QColor& color) { int index=comboBox->currentIndex(); comboBox->clear(); QPainter pa; int offset=2; int w=50; int h=20; QPixmap pm( w, h ); comboBox->setIconSize( QSize(w,h) ); QPen pen(Qt::SolidPattern, 1); pa.setPen( pen ); - static QString list[15] = { i18n("none"), i18n("uniform"), i18n("extremely dense"), - i18n("very dense"), i18n("somewhat dense"), i18n("half dense"), - i18n("somewhat sparse"), i18n("very sparse"), i18n("extremely sparse"), - i18n("horiz. lines"), i18n("vert. lines"), i18n("crossing lines"), - i18n("backward diag. lines"), i18n("forward diag. lines"), i18n("crossing diag. lines") }; + static QString list[15] = { i18n("None"), i18n("Uniform"), i18n("Extremely Dense"), + i18n("Very Dense"), i18n("Somewhat Dense"), i18n("Half Dense"), + i18n("Somewhat Sparse"), i18n("Very Sparse"), i18n("Extremely Sparse"), + i18n("Horiz. Lines"), i18n("Vert. Lines"), i18n("Crossing Lines"), + i18n("Backward Diag. Lines"), i18n("Forward Diag. Lines"), i18n("Crossing Diag. Lines") }; const QColor& borderColor = (qApp->palette().color(QPalette::Base).lightness() < 128) ? Qt::white : Qt::black; for (int i=0;i<15;i++) { pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(borderColor); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush( QBrush(color, (Qt::BrushStyle)i) ); pa.drawRect( offset, offset, w - 2*offset, h - 2*offset); pa.end(); comboBox->addItem( QIcon(pm), list[i] ); } comboBox->setCurrentIndex(index); } void GuiTools::fillColorMenu(QMenu* menu, QActionGroup* actionGroup){ - static const QString colorNames[colorsCount] = {i18n("white"), i18n("black"), - i18n("dark red"), i18n("red"), i18n("light red"), - i18n("dark green"), i18n("green"), i18n("light green"), - i18n("dark blue"), i18n("blue"), i18n("light blue"), - i18n("dark yellow"), i18n("yellow"), i18n("light yellow"), - i18n("dark cyan"), i18n("cyan"), i18n("light cyan"), - i18n("dark magenta"), i18n("magenta"), i18n("light magenta"), - i18n("dark orange"), i18n("orange"), i18n("light orange"), - i18n("dark grey"), i18n("grey"), i18n("light grey") + static const QString colorNames[colorsCount] = {i18n("White"), i18n("Black"), + i18n("Dark Red"), i18n("Red"), i18n("Light Red"), + i18n("Dark Green"), i18n("Green"), i18n("Light Green"), + i18n("Dark Blue"), i18n("Blue"), i18n("Light Blue"), + i18n("Dark Yellow"), i18n("Yellow"), i18n("Light Yellow"), + i18n("Dark Cyan"), i18n("Cyan"), i18n("Light Cyan"), + i18n("Dark Magenta"), i18n("Magenta"), i18n("Light Magenta"), + i18n("Dark Orange"), i18n("Orange"), i18n("Light Orange"), + i18n("Dark Grey"), i18n("Grey"), i18n("Light Grey") }; QPixmap pix(16,16); QPainter p(&pix); for (int i=0; isetCheckable(true); menu->addAction(action); } } /*! * Selects (checks) the action in the group \c actionGroup hat corresponds to the color \c color. * Unchecks the previously checked action if the color * was not found in the list of predefined colors. */ void GuiTools::selectColorAction(QActionGroup* actionGroup, const QColor& color) { int index; for (index=0; indexactions().at(index)->setChecked(true); break; } } if (index==colorsCount) { //the color was not found in the list of predefined colors // -> uncheck the previously checked action QAction* checkedAction = actionGroup->checkedAction(); if (checkedAction) checkedAction->setChecked(false); } } QColor& GuiTools::colorFromAction(QActionGroup* actionGroup, QAction* action) { int index = actionGroup->actions().indexOf(action); if (index==-1 || index>=colorsCount) index = 0; return colors[index]; } // ComboBox with colors // QImage img(16,16,QImage::Format_RGB32); // QPainter p(&img); // QRect rect = img.rect().adjusted(1,1,-1,-1); // p.fillRect(rect, Qt::red); // comboBox->setItemData(0, QPixmap::fromImage(img), Qt::DecorationRole); diff --git a/src/kdefrontend/HistoryDialog.cpp b/src/kdefrontend/HistoryDialog.cpp index 7be37fa01..26f2f537a 100644 --- a/src/kdefrontend/HistoryDialog.cpp +++ b/src/kdefrontend/HistoryDialog.cpp @@ -1,102 +1,103 @@ /*************************************************************************** File : HistoryDialog.cpp Project : LabPlot Description : history dialog -------------------------------------------------------------------- Copyright : (C) 2012-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "HistoryDialog.h" #include -#include +#include #include #include #include #include #include #include +#include /*! \class HistoryDialog \brief Display the content of project's undo stack. \ingroup kdefrontend */ HistoryDialog::HistoryDialog(QWidget* parent, QUndoStack* stack, const QString& emptyLabel) : QDialog(parent), m_undoStack(stack), m_clearUndoStackButton(nullptr) { QUndoView* undoView = new QUndoView(stack, this); undoView->setCleanIcon( QIcon::fromTheme("edit-clear-history") ); undoView->setEmptyLabel(emptyLabel); undoView->setMinimumWidth(350); undoView->setWhatsThis(i18n("List of all performed steps/actions.\n" "Select an item in the list to navigate to the corresponding step.")); setWindowIcon( QIcon::fromTheme("view-history") ); - setWindowTitle(i18n("Undo/Redo History")); + setWindowTitle(i18nc("@title:window", "Undo/Redo History")); setAttribute(Qt::WA_DeleteOnClose); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &HistoryDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &HistoryDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &HistoryDialog::reject); if (stack->count()) { m_clearUndoStackButton = new QPushButton; btnBox->addButton(m_clearUndoStackButton, QDialogButtonBox::ActionRole); m_clearUndoStackButton->setText(i18n("&Clear")); m_clearUndoStackButton->setToolTip(i18n("Clears the undo history. Commands are not undone or redone; the state of the project remains unchanged.")); m_clearUndoStackButton->setIcon(QIcon::fromTheme("edit-clear")); connect(m_clearUndoStackButton, &QPushButton::clicked, this, &HistoryDialog::clearUndoStack); } QFrame* line = new QFrame; line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(undoView); layout->addWidget(line); layout->addWidget(btnBox); setLayout(layout); //restore saved dialog size if available KConfigGroup conf(KSharedConfig::openConfig(), "HistoryDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize( QSize(500, 300).expandedTo(minimumSize()) ); } HistoryDialog::~HistoryDialog() { //save dialog size KConfigGroup conf(KSharedConfig::openConfig(), "HistoryDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void HistoryDialog::clearUndoStack() { if (KMessageBox::questionYesNo( this, i18n("Do you really want to clear the undo history?"), - i18n("Clear history") + i18n("Clear History") ) == KMessageBox::Yes) m_undoStack->clear(); } diff --git a/src/kdefrontend/LabPlot.cpp b/src/kdefrontend/LabPlot.cpp index dde1a2481..3794b64fb 100644 --- a/src/kdefrontend/LabPlot.cpp +++ b/src/kdefrontend/LabPlot.cpp @@ -1,148 +1,148 @@ /*************************************************************************** File : LabPlot.cpp Project : LabPlot Description : main function -------------------------------------------------------------------- Copyright : (C) 2008 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2008-2016 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 #include #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include "MainWin.h" #include "backend/core/AbstractColumn.h" #include "backend/lib/macros.h" int main (int argc, char *argv[]) { QApplication app(argc, argv); KLocalizedString::setApplicationDomain("labplot2"); KAboutData aboutData( QStringLiteral("labplot2"), QString("labplot2"), LVERSION, i18n("LabPlot2 is a KDE-application for interactive graphing and analysis of scientific data."), KAboutLicense::GPL,i18n("(c) 2007-2018"), QString(), QStringLiteral("https://labplot.kde.org")); aboutData.addAuthor(i18n("Stefan Gerlach"), i18n("developer"), "stefan.gerlach@uni.kn", 0); aboutData.addAuthor(i18n("Alexander Semke"), i18n("developer"), "alexander.semke@web.de", 0); aboutData.addAuthor(i18n("Fábián Kristóf-Szabolcs"), i18n("developer"), "f-kristof@hotmail.com", 0); aboutData.addAuthor(i18n("Andreas Kainz"), i18n("icon designer"), "kainz.a@gmail.com", 0); aboutData.addCredit(i18n("Yuri Chornoivan"), i18n("Help on many questions about the KDE-infrastructure and translation related topics"), "yurchor@ukr.net", 0); aboutData.addCredit(i18n("Garvit Khatri"), i18n("Porting LabPlot2 to KF5 and Integration with Cantor"), "garvitdelhi@gmail.com", 0); aboutData.setOrganizationDomain(QByteArray("kde.org")); aboutData.setDesktopFileName(QStringLiteral("org.kde.labplot2")); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption nosplashOption("no-splash", i18n("disable splash screen")); parser.addOption(nosplashOption); QCommandLineOption presenterOption("presenter", i18n("start in the presenter mode")); parser.addOption(presenterOption); parser.addPositionalArgument("+[file]", i18n( "open a project file")); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); const QStringList args = parser.positionalArguments(); QString filename; if (args.count() > 0) filename = args[0]; if(!filename.isEmpty() ) { //determine the absolute file path in order to properly save it in MainWin in "Recent Files" QDir dir; filename = dir.absoluteFilePath(filename); if ( !QFile::exists(filename)) { if ( KMessageBox::warningContinueCancel( 0, i18n( "Could not open file \'%1\'. Click \'Continue\' to proceed starting or \'Cancel\' to exit the application.", filename), - i18n("Failed to open")) == KMessageBox::Cancel) { + i18n("Failed to Open")) == KMessageBox::Cancel) { exit(-1); //"Cancel" clicked -> exit the application } else { filename = ""; //Wrong file -> clear the file name and continue } } } QSplashScreen* splash = nullptr; if (!parser.isSet(nosplashOption)) { const QString& file = QStandardPaths::locate(QStandardPaths::DataLocation, "splash.png"); splash = new QSplashScreen(QPixmap(file)); splash->show(); } // debugging paths QStringList appdatapaths = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); QDEBUG("AppDataLocation paths = " << appdatapaths); QDEBUG("Icon theme search paths = " << QIcon::themeSearchPaths()); // needed in order to have the signals triggered by SignallingUndoCommand //TODO: redesign/remove this qRegisterMetaType("const AbstractAspect*"); qRegisterMetaType("const AbstractColumn*"); #ifdef _WIN32 // enable debugging on console if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); } #endif KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General"); QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze")); KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); QString schemeName = group.readEntry("ColorScheme", defaultSchemeName); KColorSchemeManager manager; manager.activateScheme(manager.indexForScheme(schemeName)); MainWin* window = new MainWin(0, filename); window->show(); if(splash) splash->finish(window); if (parser.isSet(presenterOption)) window->showPresenter(); return app.exec(); } diff --git a/src/kdefrontend/MainWin.cpp b/src/kdefrontend/MainWin.cpp index d64c72e2e..dfb1989bc 100644 --- a/src/kdefrontend/MainWin.cpp +++ b/src/kdefrontend/MainWin.cpp @@ -1,1846 +1,1846 @@ /*************************************************************************** File : MainWin.cc Project : LabPlot Description : Main window of the application -------------------------------------------------------------------- Copyright : (C) 2009-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2015 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/worksheet/Worksheet.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/projects/OriginProjectParser.h" #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 "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 "kdefrontend/datasources/ImportFileDialog.h" #include "kdefrontend/datasources/ImportProjectDialog.h" #include "kdefrontend/datasources/ImportSQLDatabaseDialog.h" #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/HistoryDialog.h" #include "kdefrontend/SettingsDialog.h" #include "kdefrontend/GuiObserver.h" #include "kdefrontend/widgets/FITSHeaderEditDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CANTOR_LIBS #include #endif /*! \class MainWin \brief Main application window. \ingroup kdefrontend */ MainWin::MainWin(QWidget *parent, const QString& filename) : KXmlGuiWindow(parent), m_currentSubWindow(0), m_project(0), m_aspectTreeModel(0), m_projectExplorer(0), m_projectExplorerDock(0), m_propertiesDock(0), m_currentAspect(0), m_currentFolder(0), m_suppressCurrentSubWindowChangedEvent(false), m_closing(false), m_autoSaveActive(false), m_visibilityMenu(0), m_newMenu(0), m_editMenu(0), axisDock(0), notesDock(0), cartesianPlotDock(0), cartesianPlotLegendDock(0), columnDock(0), m_liveDataDock(0), matrixDock(0), spreadsheetDock(0), projectDock(0), xyCurveDock(0), xyEquationCurveDock(0), xyDataReductionCurveDock(0), xyDifferentiationCurveDock(0), xyIntegrationCurveDock(0), xyInterpolationCurveDock(0), xySmoothCurveDock(0), xyFitCurveDock(0), xyFourierFilterCurveDock(0), xyFourierTransformCurveDock(0), histogramDock(0), worksheetDock(0), textLabelDock(0), customPointDock(0), datapickerImageDock(0), datapickerCurveDock(0), #ifdef HAVE_CANTOR_LIBS cantorWorksheetDock(0), #endif m_guiObserver(0) { 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 != 0) { m_mdiArea->closeAllSubWindows(); disconnect(m_project, 0, this, 0); delete m_project; } if (m_aspectTreeModel) delete m_aspectTreeModel; if (m_guiObserver) delete m_guiObserver; } void MainWin::showPresenter() { Worksheet* w = activeWorksheet(); if (w) { WorksheetView* 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) { WorksheetView* 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 won't be started.")); + i18n("No worksheets are available in the project. The presenter mode will not be started.")); } } } AspectTreeModel* MainWin::model() const { return m_aspectTreeModel; } Project* MainWin::project() const { return m_project; } void MainWin::initGUI(const QString& fileName) { m_mdiArea = new QMdiArea; setCentralWidget(m_mdiArea); connect(m_mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(handleCurrentSubWindowChanged(QMdiSubWindow*))); 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, QLatin1String("labplot2ui.rc")); #endif //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"))) { QToolBar* toolbar = dynamic_cast(container); if (toolbar) toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } } initMenus(); QToolBar* mainToolBar = qobject_cast(factory()->container("main_toolbar", this)); if (!mainToolBar) { - QMessageBox::critical(this, i18n(" GUI configuration file not found"), i18n("labplot2ui.rc file was not found. Please check your installation.")); + QMessageBox::critical(this, i18n("GUI configuration file not found"), i18n("labplot2ui.rc file was not found. Please check your installation.")); //TODO: the application is not really usable if the rc file was not found. We should quit the application. The following line crashes //the application because of the splash screen. We need to find another solution. // QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); //call close as soon as we enter the eventloop return; } QToolButton* tbImport = new QToolButton(mainToolBar); tbImport->setPopupMode(QToolButton::MenuButtonPopup); tbImport->setMenu(m_importMenu); tbImport->setDefaultAction(m_importFileAction); mainToolBar->addWidget(tbImport); qobject_cast(factory()->container("import", this))->setIcon(QIcon::fromTheme("document-import")); setWindowIcon(QIcon::fromTheme("LabPlot2", QGuiApplication::windowIcon())); setAttribute( Qt::WA_DeleteOnClose ); //make the status bar of a fixed size in order to avoid height changes when placing a ProgressBar there. QFont font; font.setFamily(font.defaultFamily()); QFontMetrics fm(font); statusBar()->setFixedHeight(fm.height()+5); //load recently used projects m_recentProjectsAction->loadEntries( KSharedConfig::openConfig()->group("Recent Files") ); //set the view mode of the mdi area KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); int viewMode = group.readEntry("ViewMode", 0); if (viewMode == 1) { m_mdiArea->setViewMode(QMdiArea::TabbedView); int tabPosition = group.readEntry("TabPosition", 0); m_mdiArea->setTabPosition(QTabWidget::TabPosition(tabPosition)); m_mdiArea->setTabsClosable(true); m_mdiArea->setTabsMovable(true); m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); } //auto-save m_autoSaveActive = group.readEntry("AutoSave", 0); int interval = group.readEntry("AutoSaveInterval", 1); interval = interval*60*1000; m_autoSaveTimer.setInterval(interval); connect(&m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveProject())); if (!fileName.isEmpty()) { if (Project::isLabPlotProject(fileName) || OriginProjectParser::isOriginProject(fileName)) { - openProject(fileName); + QTimer::singleShot(0, this, [=] () { openProject(fileName); }); } else { newProject(); QTimer::singleShot(0, this, [=] () { importFileDialog(fileName); }); } } else { //There is no file to open. Depending on the settings do nothing, //create a new project or open the last used project. int load = group.readEntry("LoadOnStart", 0); if (load == 1) //create new project newProject(); else if (load == 2) { //create new project with a worksheet newProject(); newWorksheet(); } else if (load == 3) { //open last used project if (!m_recentProjectsAction->urls().isEmpty()) { QDEBUG("TO OPEN m_recentProjectsAction->urls() =" << m_recentProjectsAction->urls().first()); openRecentProject( m_recentProjectsAction->urls().constFirst() ); } } } updateGUIOnProjectChanges(); } void MainWin::initActions() { // ******************** File-menu ******************************* //add some standard actions KStandardAction::openNew(this, SLOT(newProject()),actionCollection()); KStandardAction::open(this, SLOT(openProject()),actionCollection()); m_recentProjectsAction = KStandardAction::openRecent(this, SLOT(openRecentProject(QUrl)),actionCollection()); m_closeAction = KStandardAction::close(this, SLOT(closeProject()),actionCollection()); m_saveAction = KStandardAction::save(this, SLOT(saveProject()),actionCollection()); m_saveAsAction = KStandardAction::saveAs(this, SLOT(saveProjectAs()),actionCollection()); m_printAction = KStandardAction::print(this, SLOT(print()),actionCollection()); m_printPreviewAction = KStandardAction::printPreview(this, SLOT(printPreview()),actionCollection()); KStandardAction::fullScreen(this, SLOT(toggleFullScreen()), this, actionCollection()); //New Folder/Workbook/Spreadsheet/Matrix/Worksheet/Datasources m_newWorkbookAction = new QAction(QIcon::fromTheme("labplot-workbook-new"),i18n("Workbook"),this); actionCollection()->addAction("new_workbook", m_newWorkbookAction); connect(m_newWorkbookAction, SIGNAL(triggered()), SLOT(newWorkbook())); m_newDatapickerAction = new QAction(QIcon::fromTheme("color-picker-black"), i18n("Datapicker"), this); actionCollection()->addAction("new_datapicker", m_newDatapickerAction); connect(m_newDatapickerAction, SIGNAL(triggered()), SLOT(newDatapicker())); m_newSpreadsheetAction = new QAction(QIcon::fromTheme("labplot-spreadsheet-new"),i18n("Spreadsheet"),this); // m_newSpreadsheetAction->setShortcut(Qt::CTRL+Qt::Key_Equal); actionCollection()->addAction("new_spreadsheet", m_newSpreadsheetAction); connect(m_newSpreadsheetAction, SIGNAL(triggered()), SLOT(newSpreadsheet())); m_newMatrixAction = new QAction(QIcon::fromTheme("labplot-matrix-new"),i18n("Matrix"),this); // m_newMatrixAction->setShortcut(Qt::CTRL+Qt::Key_Equal); actionCollection()->addAction("new_matrix", m_newMatrixAction); connect(m_newMatrixAction, SIGNAL(triggered()), SLOT(newMatrix())); m_newWorksheetAction= new QAction(QIcon::fromTheme("labplot-worksheet-new"),i18n("Worksheet"),this); // m_newWorksheetAction->setShortcut(Qt::ALT+Qt::Key_X); actionCollection()->addAction("new_worksheet", m_newWorksheetAction); connect(m_newWorksheetAction, SIGNAL(triggered()), SLOT(newWorksheet())); m_newNotesAction= new QAction(QIcon::fromTheme("document-new"),i18n("Note"),this); actionCollection()->addAction("new_notes", m_newNotesAction); connect(m_newNotesAction, SIGNAL(triggered()), SLOT(newNotes())); // m_newScriptAction = new QAction(QIcon::fromTheme("insert-text"),i18n("Note/Script"),this); // actionCollection()->addAction("new_script", m_newScriptAction); // connect(m_newScriptAction, SIGNAL(triggered()),SLOT(newScript())); m_newFolderAction = new QAction(QIcon::fromTheme("folder-new"),i18n("Folder"),this); actionCollection()->addAction("new_folder", m_newFolderAction); connect(m_newFolderAction, SIGNAL(triggered()), SLOT(newFolder())); //"New file datasources" m_newLiveDataSourceAction = new QAction(QIcon::fromTheme("application-octet-stream"),i18n("Live Data Source"),this); actionCollection()->addAction("new_live_datasource", m_newLiveDataSourceAction); connect(m_newLiveDataSourceAction, SIGNAL(triggered()), this, SLOT(newLiveDataSourceActionTriggered())); //Import/Export m_importFileAction = new QAction(QIcon::fromTheme("document-import"), i18n("From File"), this); actionCollection()->setDefaultShortcut(m_importFileAction, Qt::CTRL+Qt::SHIFT+Qt::Key_I); actionCollection()->addAction("import_file", m_importFileAction); connect(m_importFileAction, SIGNAL(triggered()), SLOT(importFileDialog())); m_importSqlAction = new QAction(QIcon::fromTheme("document-import-database"), i18n("From SQL Database"), this); actionCollection()->addAction("import_sql", m_importSqlAction); connect(m_importSqlAction, SIGNAL(triggered()),SLOT(importSqlDialog())); m_importLabPlotAction = new QAction(QIcon::fromTheme("document-import"), i18n("LabPlot Project"), this); actionCollection()->addAction("import_labplot", m_importLabPlotAction); connect(m_importLabPlotAction, SIGNAL(triggered()),SLOT(importProjectDialog())); #ifdef HAVE_LIBORIGIN m_importOpjAction = new QAction(QIcon::fromTheme("document-import-database"), i18n("Origin Project (OPJ)"), this); actionCollection()->addAction("import_opj", m_importOpjAction); connect(m_importOpjAction, SIGNAL(triggered()),SLOT(importProjectDialog())); #endif m_exportAction = new QAction(QIcon::fromTheme("document-export"), i18n("Export"), this); actionCollection()->setDefaultShortcut(m_exportAction, Qt::CTRL+Qt::SHIFT+Qt::Key_E); actionCollection()->addAction("export", m_exportAction); connect(m_exportAction, SIGNAL(triggered()), SLOT(exportDialog())); m_editFitsFileAction = new QAction(i18n("FITS Metadata Editor"), this); actionCollection()->addAction("edit_fits", m_editFitsFileAction); connect(m_editFitsFileAction, SIGNAL(triggered()), SLOT(editFitsFileDialog())); // Edit //Undo/Redo-stuff m_undoAction = KStandardAction::undo(this, SLOT(undo()), actionCollection()); m_redoAction = KStandardAction::redo(this, SLOT(redo()), actionCollection()); m_historyAction = new QAction(QIcon::fromTheme("view-history"), i18n("Undo/Redo History"),this); actionCollection()->addAction("history", m_historyAction); connect(m_historyAction, SIGNAL(triggered()), SLOT(historyDialog())); // TODO: more menus // Appearance // Analysis: see WorksheetView.cpp // Drawing // Script //Windows QAction* action = new QAction(i18n("&Close"), this); actionCollection()->setDefaultShortcut(action, Qt::CTRL+Qt::Key_U); action->setStatusTip(i18n("Close the active window")); actionCollection()->addAction("close window", action); connect(action, SIGNAL(triggered()), m_mdiArea, SLOT(closeActiveSubWindow())); action = new QAction(i18n("Close &All"), this); action->setStatusTip(i18n("Close all the windows")); actionCollection()->addAction("close all windows", action); connect(action, SIGNAL(triggered()), m_mdiArea, SLOT(closeAllSubWindows())); m_tileWindows = new QAction(i18n("&Tile"), this); m_tileWindows->setStatusTip(i18n("Tile the windows")); actionCollection()->addAction("tile windows", m_tileWindows); connect(m_tileWindows, SIGNAL(triggered()), m_mdiArea, SLOT(tileSubWindows())); m_cascadeWindows = new QAction(i18n("&Cascade"), this); m_cascadeWindows->setStatusTip(i18n("Cascade the windows")); actionCollection()->addAction("cascade windows", m_cascadeWindows); connect(m_cascadeWindows, SIGNAL(triggered()), m_mdiArea, SLOT(cascadeSubWindows())); action = new QAction(QIcon::fromTheme("go-next-view"), i18n("Ne&xt"), this); action->setStatusTip(i18n("Move the focus to the next window")); actionCollection()->addAction("next window", action); connect(action, SIGNAL(triggered()), m_mdiArea, SLOT(activateNextSubWindow())); action = new QAction(QIcon::fromTheme("go-previous-view"), i18n("Pre&vious"), this); action->setStatusTip(i18n("Move the focus to the previous window")); actionCollection()->addAction("previous window", action); connect(action, SIGNAL(triggered()), m_mdiArea, SLOT(activatePreviousSubWindow())); //"Standard actions" KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection()); KStandardAction::quit(this, SLOT(close()), actionCollection()); //Actions for window visibility QActionGroup* windowVisibilityActions = new QActionGroup(this); windowVisibilityActions->setExclusive(true); m_visibilityFolderAction = new QAction(QIcon::fromTheme("folder"), i18n("Current &Folder Only"), windowVisibilityActions); m_visibilityFolderAction->setCheckable(true); m_visibilityFolderAction->setData(Project::folderOnly); m_visibilitySubfolderAction = new QAction(QIcon::fromTheme("folder-documents"), i18n("Current Folder and &Subfolders"), windowVisibilityActions); m_visibilitySubfolderAction->setCheckable(true); m_visibilitySubfolderAction->setData(Project::folderAndSubfolders); m_visibilityAllAction = new QAction(i18n("&All"), windowVisibilityActions); m_visibilityAllAction->setCheckable(true); m_visibilityAllAction->setData(Project::allMdiWindows); connect(windowVisibilityActions, SIGNAL(triggered(QAction*)), this, SLOT(setMdiWindowVisibility(QAction*))); //Actions for hiding/showing the dock widgets QActionGroup * docksActions = new QActionGroup(this); docksActions->setExclusive(false); - m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project explorer"), docksActions); + m_toggleProjectExplorerDockAction = new QAction(QIcon::fromTheme("view-list-tree"), i18n("Project Explorer"), docksActions); m_toggleProjectExplorerDockAction->setCheckable(true); m_toggleProjectExplorerDockAction->setChecked(true); actionCollection()->addAction("toggle_project_explorer_dock", m_toggleProjectExplorerDockAction); - m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties explorer"), docksActions); + m_togglePropertiesDockAction = new QAction(QIcon::fromTheme("view-list-details"), i18n("Properties Explorer"), docksActions); m_togglePropertiesDockAction->setCheckable(true); m_togglePropertiesDockAction->setChecked(true); actionCollection()->addAction("toggle_properties_explorer_dock", m_togglePropertiesDockAction); connect(docksActions, SIGNAL(triggered(QAction*)), this, SLOT(toggleDockWidget(QAction*))); } void MainWin::initMenus() { //menu for adding new aspects - m_newMenu = new QMenu(i18n("Add new"), this); + m_newMenu = new QMenu(i18n("Add New"), this); m_newMenu->setIcon(QIcon::fromTheme("document-new")); m_newMenu->addAction(m_newFolderAction); m_newMenu->addAction(m_newWorkbookAction); m_newMenu->addAction(m_newSpreadsheetAction); m_newMenu->addAction(m_newMatrixAction); m_newMenu->addAction(m_newWorksheetAction); m_newMenu->addAction(m_newNotesAction); m_newMenu->addAction(m_newDatapickerAction); m_newMenu->addSeparator(); m_newMenu->addAction(m_newLiveDataSourceAction); //import menu m_importMenu = new QMenu(this); m_importMenu->setIcon(QIcon::fromTheme("document-import")); m_importMenu ->addAction(m_importFileAction); m_importMenu ->addAction(m_importSqlAction); m_importMenu->addSeparator(); m_importMenu->addAction(m_importLabPlotAction); #ifdef HAVE_LIBORIGIN m_importMenu ->addAction(m_importOpjAction); #endif #ifdef HAVE_CANTOR_LIBS m_newMenu->addSeparator(); m_newCantorWorksheetMenu = new QMenu(i18n("CAS Worksheet")); m_newCantorWorksheetMenu->setIcon(QIcon::fromTheme("archive-insert")); //"Adding Cantor backends to menue and context menu" QStringList m_availableBackend = Cantor::Backend::listAvailableBackends(); if(m_availableBackend.count() > 0) { unplugActionList(QLatin1String("backends_list")); QList newBackendActions; for (Cantor::Backend* backend : Cantor::Backend::availableBackends()) { if (!backend->isEnabled()) continue; QAction* action = new QAction(QIcon::fromTheme(backend->icon()), backend->name(),this); action->setData(backend->name()); newBackendActions << action; m_newCantorWorksheetMenu->addAction(action); } connect(m_newCantorWorksheetMenu, SIGNAL(triggered(QAction*)), this, SLOT(newCantorWorksheet(QAction*))); plugActionList(QLatin1String("backends_list"), newBackendActions); } m_newMenu->addMenu(m_newCantorWorksheetMenu); #else delete this->guiFactory()->container("cas_worksheet", this); delete this->guiFactory()->container("new_cas_worksheet", this); delete this->guiFactory()->container("cas_worksheet_toolbar", this); #endif //menu subwindow visibility policy - m_visibilityMenu = new QMenu(i18n("Window visibility policy"), this); + m_visibilityMenu = new QMenu(i18n("Window Visibility Policy"), this); m_visibilityMenu->setIcon(QIcon::fromTheme("window-duplicate")); m_visibilityMenu ->addAction(m_visibilityFolderAction); m_visibilityMenu ->addAction(m_visibilitySubfolderAction); m_visibilityMenu ->addAction(m_visibilityAllAction); //menu for editing files m_editMenu = new QMenu(i18n("Edit"), this); m_editMenu->addAction(m_editFitsFileAction); KColorSchemeManager schemeManager; KActionMenu* schemesMenu = schemeManager.createSchemeSelectionMenu(i18n("Color Theme"), this); schemesMenu->menu()->setTitle(i18n("Color Theme")); schemesMenu->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-color"))); QMenu* settingsMenu = dynamic_cast(factory()->container("settings", this)); if (settingsMenu) settingsMenu->insertMenu(settingsMenu->actions().constFirst(), schemesMenu->menu()); //set the action for the current color scheme checked KConfigGroup generalGlobalsGroup = KSharedConfig::openConfig(QLatin1String("kdeglobals"))->group("General"); QString defaultSchemeName = generalGlobalsGroup.readEntry("ColorScheme", QStringLiteral("Breeze")); KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); QString schemeName = group.readEntry("ColorScheme", defaultSchemeName); for (auto* action : schemesMenu->menu()->actions()) { if (action->text() == schemeName) { action->setChecked(true); break; } } connect(schemesMenu->menu(), &QMenu::triggered, this, &MainWin::colorSchemeChanged); } void MainWin::colorSchemeChanged(QAction* action) { QString schemeName = KLocalizedString::removeAcceleratorMarker(action->text()); //background of the mdi area is not updated on theme changes, do it here. KColorSchemeManager schemeManager; QModelIndex index = schemeManager.indexForScheme(schemeName); const QPalette& palette = KColorScheme::createApplicationPalette( KSharedConfig::openConfig(index.data(Qt::UserRole).toString()) ); const QBrush& brush = palette.brush(QPalette::Dark); m_mdiArea->setBackground(brush); //save the selected color scheme KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_General")); group.writeEntry("ColorScheme", schemeName); group.sync(); } /*! Asks to save the project if it was modified. \return \c true if the project still needs to be saved ("cancel" clicked), \c false otherwise. */ bool MainWin::warnModified() { if (m_project->hasChanged()) { int want_save = KMessageBox::warningYesNoCancel( this, i18n("The current project %1 has been modified. Do you want to save it?", m_project->name()), i18n("Save Project")); switch (want_save) { case KMessageBox::Yes: return !saveProject(); case KMessageBox::No: break; case KMessageBox::Cancel: return true; } } return false; } /*! * updates the state of actions, menus and toolbars (enabled or disabled) * on project changes (project closes and opens) */ void MainWin::updateGUIOnProjectChanges() { if (m_closing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == NULL) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } //disable all menus if there is no project bool b = (m_project == 0); m_saveAction->setEnabled(!b); m_saveAsAction->setEnabled(!b); m_printAction->setEnabled(!b); m_printPreviewAction->setEnabled(!b); m_importFileAction->setEnabled(!b); m_importSqlAction->setEnabled(!b); #ifdef HAVE_LIBORIGIN m_importOpjAction->setEnabled(!b); #endif m_exportAction->setEnabled(!b); m_newWorkbookAction->setEnabled(!b); m_newSpreadsheetAction->setEnabled(!b); m_newMatrixAction->setEnabled(!b); m_newWorksheetAction->setEnabled(!b); m_newDatapickerAction->setEnabled(!b); m_closeAction->setEnabled(!b); m_toggleProjectExplorerDockAction->setEnabled(!b); m_togglePropertiesDockAction->setEnabled(!b); if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif } factory->container("new", this)->setEnabled(!b); factory->container("edit", this)->setEnabled(!b); factory->container("import", this)->setEnabled(!b); if (b) setCaption("LabPlot2"); else setCaption(m_project->name()); // undo/redo actions are disabled in both cases - when the project is closed or opened m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } /* * updates the state of actions, menus and toolbars (enabled or disabled) * depending on the currently active window (worksheet or spreadsheet). */ void MainWin::updateGUI() { if (m_project->isLoading()) return; if (m_closing) return; KXMLGUIFactory* factory = this->guiFactory(); if (factory->container("worksheet", this) == NULL) { //no worksheet menu found, most probably labplot2ui.rc //was not properly installed -> return here in order not to crash return; } if (!m_mdiArea->currentSubWindow()) { factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); factory->container("datapicker", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->hide(); factory->container("worksheet_toolbar", this)->hide(); // factory->container("histogram_toolbar",this)->hide(); // factory->container("barchart_toolbar",this)->hide(); factory->container("cartesian_plot_toolbar", this)->hide(); factory->container("datapicker_toolbar", this)->hide(); #ifdef HAVE_CANTOR_LIBS factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->hide(); #endif return; } //Handle the Worksheet-object Worksheet* w = this->activeWorksheet(); if (w != 0) { //enable worksheet related menus factory->container("worksheet", this)->setEnabled(true); factory->container("analysis", this)->setEnabled(true); //TODO factory->container("drawing", this)->setEnabled(true); //disable spreadsheet and matrix related menus factory->container("spreadsheet", this)->setEnabled(false); factory->container("matrix", this)->setEnabled(false); //populate worksheet menu WorksheetView* view=qobject_cast(w->view()); QMenu* menu = qobject_cast(factory->container("worksheet", this)); menu->clear(); view->createContextMenu(menu); //populate analysis menu menu = qobject_cast(factory->container("analysis", this)); menu->clear(); view->createAnalysisMenu(menu); //populate worksheet-toolbar QToolBar* toolbar = qobject_cast(factory->container("worksheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //populate the toolbar for cartesian plots toolbar=qobject_cast(factory->container("cartesian_plot_toolbar", this)); toolbar->clear(); view->fillCartesianPlotToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); //hide the spreadsheet toolbar factory->container("spreadsheet_toolbar", this)->setVisible(false); } else { factory->container("worksheet", this)->setEnabled(false); factory->container("analysis", this)->setEnabled(false); // factory->container("drawing", this)->setEnabled(false); factory->container("worksheet_toolbar", this)->setEnabled(false); factory->container("cartesian_plot_toolbar", this)->setEnabled(false); } //Handle the Spreadsheet-object const Spreadsheet* spreadsheet = this->activeSpreadsheet(); if (spreadsheet) { //enable spreadsheet related menus factory->container("spreadsheet", this)->setEnabled(true); //populate spreadsheet-menu SpreadsheetView* view = qobject_cast(spreadsheet->view()); QMenu* menu = qobject_cast(factory->container("spreadsheet", this)); menu->clear(); view->createContextMenu(menu); //populate spreadsheet-toolbar QToolBar* toolbar = qobject_cast(factory->container("spreadsheet_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); toolbar->setEnabled(true); } else { factory->container("spreadsheet", this)->setEnabled(false); factory->container("spreadsheet_toolbar", this)->setEnabled(false); } //Handle the Matrix-object const Matrix* matrix = this->activeMatrix(); if (matrix) { factory->container("matrix", this)->setEnabled(true); //populate matrix-menu MatrixView* view = qobject_cast(matrix->view()); QMenu* menu = qobject_cast(factory->container("matrix", this)); menu->clear(); view->createContextMenu(menu); } else factory->container("matrix", this)->setEnabled(false); #ifdef HAVE_CANTOR_LIBS CantorWorksheet* cantorworksheet = this->activeCantorWorksheet(); if(cantorworksheet) { // enable Cantor Worksheet related menues factory->container("cas_worksheet", this)->setEnabled(true); CantorWorksheetView* view=qobject_cast(cantorworksheet->view()); QMenu* menu=qobject_cast(factory->container("cas_worksheet", this)); menu->clear(); view->createContextMenu(menu); QToolBar* toolbar=qobject_cast(factory->container("cas_worksheet_toolbar", this)); toolbar->setVisible(true); toolbar->clear(); view->fillToolBar(toolbar); } else { //no Cantor worksheet selected -> deactivate Cantor worksheet related menu and toolbar factory->container("cas_worksheet", this)->setEnabled(false); factory->container("cas_worksheet_toolbar", this)->setVisible(false); } #endif const Datapicker* datapicker = this->activeDatapicker(); if (datapicker) { factory->container("datapicker", this)->setEnabled(true); //populate datapicker-menu DatapickerView* view = qobject_cast(datapicker->view()); QMenu* menu = qobject_cast(factory->container("datapicker", this)); menu->clear(); view->createContextMenu(menu); //populate spreadsheet-toolbar QToolBar* toolbar = qobject_cast(factory->container("datapicker_toolbar", this)); toolbar->clear(); view->fillToolBar(toolbar); toolbar->setVisible(true); } else { factory->container("datapicker", this)->setEnabled(false); factory->container("datapicker_toolbar", this)->setVisible(false); } } /*! creates a new empty project. Returns \c true, if a new project was created. */ bool MainWin::newProject() { //close the current project, if available if (!closeProject()) return false; QApplication::processEvents(QEventLoop::AllEvents, 100); if (m_project) delete m_project; if (m_aspectTreeModel) delete m_aspectTreeModel; m_project = new Project(); m_currentAspect = m_project; m_currentFolder = m_project; KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); Project::MdiWindowVisibility vis = Project::MdiWindowVisibility(group.readEntry("MdiWindowVisibility", 0)); m_project->setMdiWindowVisibility( vis ); if (vis == Project::folderOnly) m_visibilityFolderAction->setChecked(true); else if (vis == Project::folderAndSubfolders) m_visibilitySubfolderAction->setChecked(true); else m_visibilityAllAction->setChecked(true); m_aspectTreeModel = new AspectTreeModel(m_project, this); //newProject is called for the first time, there is no project explorer yet //-> initialize the project explorer, the GUI-observer and the dock widgets. if (m_projectExplorer == 0) { m_projectExplorerDock = new QDockWidget(this); m_projectExplorerDock->setObjectName("projectexplorer"); - m_projectExplorerDock->setWindowTitle(i18n("Project Explorer")); + m_projectExplorerDock->setWindowTitle(i18nc("@title:window", "Project Explorer")); addDockWidget(Qt::LeftDockWidgetArea, m_projectExplorerDock); m_projectExplorer = new ProjectExplorer(m_projectExplorerDock); m_projectExplorerDock->setWidget(m_projectExplorer); connect(m_projectExplorer, SIGNAL(currentAspectChanged(AbstractAspect*)), this, SLOT(handleCurrentAspectChanged(AbstractAspect*))); connect(m_projectExplorerDock, SIGNAL(visibilityChanged(bool)), SLOT(projectExplorerDockVisibilityChanged(bool))); //Properties dock m_propertiesDock = new QDockWidget(this); m_propertiesDock->setObjectName("aspect_properties_dock"); - m_propertiesDock->setWindowTitle(i18n("Properties")); + m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); addDockWidget(Qt::RightDockWidgetArea, m_propertiesDock); QScrollArea* sa = new QScrollArea(m_propertiesDock); stackedWidget = new QStackedWidget(sa); sa->setWidget(stackedWidget); sa->setWidgetResizable(true); m_propertiesDock->setWidget(sa); connect(m_propertiesDock, SIGNAL(visibilityChanged(bool)), SLOT(propertiesDockVisibilityChanged(bool))); //GUI-observer; m_guiObserver = new GuiObserver(this); } m_projectExplorer->setModel(m_aspectTreeModel); m_projectExplorer->setProject(m_project); m_projectExplorer->setCurrentAspect(m_project); m_projectExplorerDock->show(); m_propertiesDock->show(); updateGUIOnProjectChanges(); connect(m_project, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(handleAspectAdded(const AbstractAspect*))); connect(m_project, SIGNAL(aspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*)), this, SLOT(handleAspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*))); connect(m_project, SIGNAL(aspectAboutToBeRemoved(const AbstractAspect*)), this, SLOT(handleAspectAboutToBeRemoved(const AbstractAspect*))); connect(m_project, SIGNAL(statusInfo(QString)), statusBar(), SLOT(showMessage(QString))); connect(m_project, SIGNAL(changed()), this, SLOT(projectChanged())); connect(m_project, SIGNAL(requestProjectContextMenu(QMenu*)), this, SLOT(createContextMenu(QMenu*))); connect(m_project, SIGNAL(requestFolderContextMenu(const Folder*,QMenu*)), this, SLOT(createFolderContextMenu(const Folder*,QMenu*))); connect(m_project, SIGNAL(mdiWindowVisibilityChanged()), this, SLOT(updateMdiWindowVisibility())); m_undoViewEmptyLabel = i18n("%1: created", m_project->name()); setCaption(m_project->name()); return true; } void MainWin::openProject() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); - const QString& path = QFileDialog::getOpenFileName(this,i18n("Open project"), dir, + const QString& path = QFileDialog::getOpenFileName(this,i18n("Open Project"), dir, i18n("LabPlot Projects (%1);;Origin Projects (%2)", Project::supportedExtensions(), OriginProjectParser::supportedExtensions()) ); if (path.isEmpty())// "Cancel" was clicked return; this->openProject(path); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } } void MainWin::openProject(const QString& filename) { if (filename == m_currentFileName) { - KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open project")); + KMessageBox::information(this, i18n("The project file %1 is already opened.", filename), i18n("Open Project")); return; } if (!newProject()) return; WAIT_CURSOR; QElapsedTimer timer; timer.start(); bool rc = false; if (Project::isLabPlotProject(filename)) rc = m_project->load(filename); else if (OriginProjectParser::isOriginProject(filename)) { OriginProjectParser parser; parser.setProjectFileName(filename); parser.importTo(m_project, QStringList()); //TODO: add return code rc = true; } if (!rc) { closeProject(); RESET_CURSOR; return; } m_currentFileName = filename; m_project->setFileName(filename); m_project->undoStack()->clear(); m_undoViewEmptyLabel = i18n("%1: opened", m_project->name()); m_recentProjectsAction->addUrl( QUrl(filename) ); setCaption(m_project->name()); updateGUIOnProjectChanges(); updateGUI(); //there are most probably worksheets or spreadsheets in the open project -> update the GUI m_saveAction->setEnabled(false); statusBar()->showMessage( i18n("Project successfully opened (in %1 seconds).", (float)timer.elapsed()/1000) ); if (m_autoSaveActive) m_autoSaveTimer.start(); RESET_CURSOR; } void MainWin::openRecentProject(const QUrl& url) { if (url.isLocalFile()) // fix for Windows this->openProject(url.toLocalFile()); else this->openProject(url.path()); } /*! Closes the current project, if available. Return \c true, if the project was closed. */ bool MainWin::closeProject() { if (m_project == 0) return true; //nothing to close if (warnModified()) return false; delete m_aspectTreeModel; m_aspectTreeModel = 0; delete m_project; m_project = 0; m_currentFileName = ""; //update the UI if we're just closing a project //and not closing(quitting) the application if (!m_closing) { m_projectExplorerDock->hide(); m_propertiesDock->hide(); m_currentAspect = 0; m_currentFolder = 0; updateGUIOnProjectChanges(); if (m_autoSaveActive) m_autoSaveTimer.stop(); } return true; } bool MainWin::saveProject() { const QString& fileName = m_project->fileName(); if (fileName.isEmpty()) return saveProjectAs(); else return save(fileName); } bool MainWin::saveProjectAs() { KConfigGroup conf(KSharedConfig::openConfig(), "MainWin"); const QString& dir = conf.readEntry("LastOpenDir", ""); - QString path = QFileDialog::getSaveFileName(this, i18n("Save project as"), dir, + QString path = QFileDialog::getSaveFileName(this, i18n("Save Project As"), dir, i18n("LabPlot Projects (*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ)")); if (path.isEmpty())// "Cancel" was clicked return false; if (path.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false) path.append(QLatin1String(".lml")); //save new "last open directory" int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { const QString& newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastOpenDir", newDir); } return save(path); } /*! * auxillary function that does the actual saving of the project */ bool MainWin::save(const QString& fileName) { WAIT_CURSOR; // use file ending to find out how to compress file QIODevice* file; // if ending is .lml, do gzip compression anyway if (fileName.endsWith(QLatin1String(".lml"))) file = new KCompressionDevice(fileName, KCompressionDevice::GZip); else file = new KFilterDev(fileName); if (file == 0) file = new QFile(fileName); bool ok; if (file->open(QIODevice::WriteOnly)) { m_project->setFileName(fileName); QXmlStreamWriter writer(file); m_project->save(&writer); m_project->undoStack()->clear(); m_project->setChanged(false); file->close(); setCaption(m_project->name()); statusBar()->showMessage(i18n("Project saved")); m_saveAction->setEnabled(false); m_recentProjectsAction->addUrl( QUrl(fileName) ); ok = true; //if the project dock is visible, refresh the shown content //(version and modification time might have been changed) if (stackedWidget->currentWidget() == projectDock) projectDock->setProject(m_project); //we have a file name now // -> auto save can be activated now if not happened yet if (m_autoSaveActive && !m_autoSaveTimer.isActive()) m_autoSaveTimer.start(); } else { KMessageBox::error(this, i18n("Sorry. Could not open file for writing.")); ok = false; } delete file; RESET_CURSOR; return ok; } /*! * automatically saves the project in the specified time interval. */ void MainWin::autoSaveProject() { //don't auto save when there are no changes or the file name //was not provided yet (the project was never explicitly saved yet). if ( !m_project->hasChanged() || m_project->fileName().isEmpty()) return; this->saveProject(); } /*! prints the current sheet (worksheet, spreadsheet or matrix) */ void MainWin::print() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printView()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->showMessage(""); } void MainWin::printPreview() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); statusBar()->showMessage(i18n("Preparing printing of %1", part->name())); if (part->printPreview()) statusBar()->showMessage(i18n("%1 printed", part->name())); else statusBar()->showMessage(""); } /**************************************************************************************/ /*! adds a new Folder to the project. */ void MainWin::newFolder() { Folder* folder = new Folder(i18n("Folder")); this->addAspectToProject(folder); } /*! adds a new Workbook to the project. */ void MainWin::newWorkbook() { Workbook* workbook = new Workbook(0, i18n("Workbook")); this->addAspectToProject(workbook); } /*! adds a new Datapicker to the project. */ void MainWin::newDatapicker() { Datapicker* datapicker = new Datapicker(0, i18n("Datapicker")); this->addAspectToProject(datapicker); } /*! adds a new Spreadsheet to the project. */ void MainWin::newSpreadsheet() { Spreadsheet* spreadsheet = new Spreadsheet(0, i18n("Spreadsheet")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new spreadsheet to the workbook Workbook* workbook = activeWorkbook(); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); AbstractAspect* aspect = static_cast(index.internalPointer()); if (!aspect->inherits("Folder")) { workbook->addChild(spreadsheet); return; } } this->addAspectToProject(spreadsheet); } /*! adds a new Matrix to the project. */ void MainWin::newMatrix() { Matrix* matrix = new Matrix(0, i18n("Matrix")); //if the current active window is a workbook and no folder/project is selected in the project explorer, //add the new matrix to the workbook Workbook* workbook = activeWorkbook(); if (workbook) { QModelIndex index = m_projectExplorer->currentIndex(); AbstractAspect* aspect = static_cast(index.internalPointer()); if (!aspect->inherits("Folder")) { workbook->addChild(matrix); return; } } this->addAspectToProject(matrix); } /*! adds a new Worksheet to the project. */ void MainWin::newWorksheet() { Worksheet* worksheet = new Worksheet(0, i18n("Worksheet")); this->addAspectToProject(worksheet); } /*! adds a new Note to the project. */ void MainWin::newNotes() { Note* notes = new Note(i18n("Note")); this->addAspectToProject(notes); } /*! returns a pointer to a Workbook-object, if the currently active Mdi-Subwindow is \a WorkbookView. Otherwise returns \a 0. */ Workbook* MainWin::activeWorkbook() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! returns a pointer to a Datapicker-object, if the currently active Mdi-Subwindow is \a DatapickerView. Otherwise returns \a 0. */ Datapicker* MainWin::activeDatapicker() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } /*! returns a pointer to a \c Spreadsheet object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c SpreadsheetView Otherwise returns \c 0. */ Spreadsheet* MainWin::activeSpreadsheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Spreadsheet* spreadsheet = 0; const Workbook* workbook = dynamic_cast(part); if (workbook) { spreadsheet = workbook->currentSpreadsheet(); if (!spreadsheet) { //potentially, the spreadsheet was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a spreadsheet or one of its columns currently selected in the project explorer spreadsheet = dynamic_cast(m_currentAspect); if (!spreadsheet) { if (m_currentAspect->parentAspect()) spreadsheet = dynamic_cast(m_currentAspect->parentAspect()); } } } else spreadsheet = dynamic_cast(part); return spreadsheet; } /*! returns a pointer to a \c Matrix object, if the currently active Mdi-Subwindow or if the currently selected tab in a \c WorkbookView is a \c MatrixView Otherwise returns \c 0. */ Matrix* MainWin::activeMatrix() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); Matrix* matrix = 0; Workbook* workbook = dynamic_cast(part); if (workbook) { matrix = workbook->currentMatrix(); if (!matrix) { //potentially, the matrix was not selected in workbook yet since the selection in project explorer //arrives in workbook's slot later than in this function //->check whether we have a matrix currently selected in the project explorer matrix = dynamic_cast(m_currentAspect); } } else matrix = dynamic_cast(part); return matrix; } /*! returns a pointer to a Worksheet-object, if the currently active Mdi-Subwindow is \a WorksheetView Otherwise returns \a 0. */ Worksheet* MainWin::activeWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #ifdef HAVE_CANTOR_LIBS /* adds a new Cantor Spreadsheet to the project. */ void MainWin::newCantorWorksheet(QAction* action) { CantorWorksheet* cantorworksheet = new CantorWorksheet(0, action->data().toString()); this->addAspectToProject(cantorworksheet); } /********************************************************************************/ /*! returns a pointer to a CantorWorksheet-object, if the currently active Mdi-Subwindow is \a CantorWorksheetView Otherwise returns \a 0. */ CantorWorksheet* MainWin::activeCantorWorksheet() const { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return 0; AbstractPart* part = dynamic_cast(win)->part(); Q_ASSERT(part); return dynamic_cast(part); } #endif /*! called if there were changes in the project. Adds "changed" to the window caption and activates the save-Action. */ void MainWin::projectChanged() { setCaption(i18n("%1 [Changed]", m_project->name())); m_saveAction->setEnabled(true); m_undoAction->setEnabled(true); return; } void MainWin::handleCurrentSubWindowChanged(QMdiSubWindow* win) { if (!win) return; PartMdiView *view = qobject_cast(win); if (!view) { updateGUI(); return; } if (view == m_currentSubWindow) { //do nothing, if the current sub-window gets selected again. //This event happens, when labplot loses the focus (modal window is opened or the user switches to another application) //and gets it back (modal window is closed or the user switches back to labplot). return; } else m_currentSubWindow = view; updateGUI(); if (!m_suppressCurrentSubWindowChangedEvent) m_projectExplorer->setCurrentAspect(view->part()); } void MainWin::handleAspectAdded(const AbstractAspect* aspect) { const AbstractPart* part = dynamic_cast(aspect); if (part) { //TODO: export, print and print preview should be handled in the views and not in MainWin. connect(part, SIGNAL(exportRequested()), this, SLOT(exportDialog())); connect(part, SIGNAL(printRequested()), this, SLOT(print())); connect(part, SIGNAL(printPreviewRequested()), this, SLOT(printPreview())); connect(part, SIGNAL(showRequested()), this, SLOT(handleShowSubWindowRequested())); } } void MainWin::handleAspectRemoved(const AbstractAspect* parent,const AbstractAspect* before,const AbstractAspect* aspect) { Q_UNUSED(before); Q_UNUSED(aspect); m_projectExplorer->setCurrentAspect(parent); } void MainWin::handleAspectAboutToBeRemoved(const AbstractAspect *aspect) { const AbstractPart *part = qobject_cast(aspect); if (!part) return; const Workbook* workbook = dynamic_cast(aspect->parentAspect()); const Datapicker* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (!workbook && !datapicker) { PartMdiView* win = part->mdiSubWindow(); if (win) m_mdiArea->removeSubWindow(win); } } /*! called when the current aspect in the tree of the project explorer was changed. Selects the new aspect. */ void MainWin::handleCurrentAspectChanged(AbstractAspect *aspect) { if (!aspect) aspect = m_project; // should never happen, just in case m_suppressCurrentSubWindowChangedEvent = true; if (aspect->folder() != m_currentFolder) { m_currentFolder = aspect->folder(); updateMdiWindowVisibility(); } m_currentAspect = aspect; //activate the corresponding MDI sub window for the current aspect activateSubWindowForAspect(aspect); m_suppressCurrentSubWindowChangedEvent = false; updateGUI(); } void MainWin::activateSubWindowForAspect(const AbstractAspect* aspect) const { const AbstractPart* part = dynamic_cast(aspect); if (part) { //for LiveDataSource we currently don't show any view /*if (dynamic_cast(part)) return;*/ PartMdiView* win; //for aspects being children of a Workbook, we show workbook's window, otherwise the window of the selected part const Workbook* workbook = dynamic_cast(aspect->parentAspect()); const Datapicker* datapicker = dynamic_cast(aspect->parentAspect()); if (!datapicker) datapicker = dynamic_cast(aspect->parentAspect()->parentAspect()); if (workbook) win = workbook->mdiSubWindow(); else if (datapicker) win = datapicker->mdiSubWindow(); else win = part->mdiSubWindow(); if (m_mdiArea->subWindowList().indexOf(win) == -1) { if (dynamic_cast(part)) m_mdiArea->addSubWindow(win, Qt::Tool); else m_mdiArea->addSubWindow(win); win->show(); } m_mdiArea->setActiveSubWindow(win); } else { //activate the mdiView of the parent, if a child was selected const AbstractAspect* parent = aspect->parentAspect(); if (parent) { activateSubWindowForAspect(parent); //if the parent's parent is a Workbook (a column of a spreadsheet in workbook was selected), //we need to select the corresponding tab in WorkbookView too if (parent->parentAspect()) { Workbook* workbook = dynamic_cast(parent->parentAspect()); Datapicker* datapicker = dynamic_cast(parent->parentAspect()); if (!datapicker) datapicker = dynamic_cast(parent->parentAspect()->parentAspect()); if (workbook) workbook->childSelected(parent); else if (datapicker) datapicker->childSelected(parent); } } } return; } void MainWin::setMdiWindowVisibility(QAction * action) { m_project->setMdiWindowVisibility((Project::MdiWindowVisibility)(action->data().toInt())); } /*! shows the sub window of a worksheet, matrix or a spreadsheet. Used if the window was closed before and the user asks to show the window again via the context menu in the project explorer. */ void MainWin::handleShowSubWindowRequested() { activateSubWindowForAspect(m_currentAspect); } /*! this is called on a right click on the root folder in the project explorer */ void MainWin::createContextMenu(QMenu* menu) const { menu->addMenu(m_newMenu); //The tabbed view collides with the visibility policy for the subwindows. //Hide the menus for the visibility policy if the tabbed view is used. if (m_mdiArea->viewMode() != QMdiArea::TabbedView) menu->addMenu(m_visibilityMenu); } /*! this is called on a right click on a non-root folder in the project explorer */ void MainWin::createFolderContextMenu(const Folder* folder, QMenu* menu) const { Q_UNUSED(folder); //Folder provides it's own context menu. Add a separator before adding additional actions. menu->addSeparator(); this->createContextMenu(menu); } void MainWin::undo() { WAIT_CURSOR; m_project->undoStack()->undo(); if (m_project->undoStack()->index()==0) { setCaption(m_project->name()); m_saveAction->setEnabled(false); m_undoAction->setEnabled(false); m_project->setChanged(false); } m_redoAction->setEnabled(true); RESET_CURSOR; } void MainWin::redo() { WAIT_CURSOR; m_project->undoStack()->redo(); projectChanged(); if (m_project->undoStack()->index() == m_project->undoStack()->count()) m_redoAction->setEnabled(false); RESET_CURSOR; } /*! Shows/hides mdi sub-windows depending on the current visibility policy. */ void MainWin::updateMdiWindowVisibility() const { QList windows = m_mdiArea->subWindowList(); PartMdiView* part_view; switch (m_project->mdiWindowVisibility()) { case Project::allMdiWindows: for (auto* window : windows) window->show(); break; case Project::folderOnly: for (auto* window : windows) { part_view = qobject_cast(window); Q_ASSERT(part_view); if (part_view->part()->folder() == m_currentFolder) part_view->show(); else part_view->hide(); } break; case Project::folderAndSubfolders: for (auto* window : windows) { part_view = qobject_cast(window); if (part_view->part()->isDescendantOf(m_currentFolder)) part_view->show(); else part_view->hide(); } break; } } void MainWin::toggleDockWidget(QAction* action) const { if (action->objectName() == "toggle_project_explorer_dock") { if (m_projectExplorerDock->isVisible()) m_projectExplorerDock->hide(); else m_projectExplorerDock->show(); } else if (action->objectName() == "toggle_properties_explorer_dock") { if (m_propertiesDock->isVisible()) m_propertiesDock->hide(); else m_propertiesDock->show(); } } void MainWin::projectExplorerDockVisibilityChanged(bool visible) { m_toggleProjectExplorerDockAction->setChecked(visible); } void MainWin::propertiesDockVisibilityChanged(bool visible) { m_togglePropertiesDockAction->setChecked(visible); } void MainWin::toggleFullScreen() { if (this->windowState() == Qt::WindowFullScreen) this->setWindowState(m_lastWindowState); else { m_lastWindowState = this->windowState(); this->showFullScreen(); } } void MainWin::closeEvent(QCloseEvent* event) { m_closing = true; if (!this->closeProject()) { m_closing = false; event->ignore(); } } void MainWin::dragEnterEvent(QDragEnterEvent* event) { event->accept(); } void MainWin::dropEvent(QDropEvent* event) { if (event->mimeData() && !event->mimeData()->urls().isEmpty()) { QUrl url = event->mimeData()->urls().at(0); const QString& f = url.toLocalFile(); if (Project::isLabPlotProject(f) || OriginProjectParser::isOriginProject(f)) openProject(f); else { if (!m_project) newProject(); importFileDialog(f); } event->accept(); } else event->ignore(); } void MainWin::handleSettingsChanges() { const KConfigGroup group = KSharedConfig::openConfig()->group( "Settings_General" ); QMdiArea::ViewMode viewMode = QMdiArea::ViewMode(group.readEntry("ViewMode", 0)); if (m_mdiArea->viewMode() != viewMode) { m_mdiArea->setViewMode(viewMode); if (viewMode == QMdiArea::SubWindowView) this->updateMdiWindowVisibility(); } if (m_mdiArea->viewMode() == QMdiArea::TabbedView) { m_tileWindows->setVisible(false); m_cascadeWindows->setVisible(false); QTabWidget::TabPosition tabPosition = QTabWidget::TabPosition(group.readEntry("TabPosition", 0)); if (m_mdiArea->tabPosition() != tabPosition) m_mdiArea->setTabPosition(tabPosition); } else { m_tileWindows->setVisible(true); m_cascadeWindows->setVisible(true); } //autosave bool autoSave = group.readEntry("AutoSave", 0); if (m_autoSaveActive != autoSave) { m_autoSaveActive = autoSave; if (autoSave) m_autoSaveTimer.start(); else m_autoSaveTimer.stop(); } int interval = group.readEntry("AutoSaveInterval", 1); interval *= 60*1000; if (interval != m_autoSaveTimer.interval()) m_autoSaveTimer.setInterval(interval); } /***************************************************************************************/ /************************************** dialogs ***************************************/ /***************************************************************************************/ /*! shows the dialog with the Undo-history. */ void MainWin::historyDialog() { if (!m_project->undoStack()) return; HistoryDialog* dialog = new HistoryDialog(this, m_project->undoStack(), m_undoViewEmptyLabel); int index = m_project->undoStack()->index(); if (dialog->exec() != QDialog::Accepted) { if (m_project->undoStack()->count() != 0) m_project->undoStack()->setIndex(index); } //disable undo/redo-actions if the history was cleared //(in both cases, when accepted or rejected in the dialog) if (m_project->undoStack()->count() == 0) { m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } /*! Opens the dialog to import data to the selected workbook, spreadsheet or matrix */ void MainWin::importFileDialog(const QString& fileName) { DEBUG("MainWin::importFileDialog()"); ImportFileDialog* dlg = new ImportFileDialog(this, false, fileName); // select existing container if (m_currentAspect->inherits("Spreadsheet") || m_currentAspect->inherits("Matrix") || m_currentAspect->inherits("Workbook")) dlg->setCurrentIndex( m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits("Column")) { if (m_currentAspect->parentAspect()->inherits("Spreadsheet")) dlg->setCurrentIndex(m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); } if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importFileDialog() DONE"); } void MainWin::importSqlDialog() { DEBUG("MainWin::importSqlDialog()"); ImportSQLDatabaseDialog* dlg = new ImportSQLDatabaseDialog(this); // select existing container if (m_currentAspect->inherits("Spreadsheet") || m_currentAspect->inherits("Matrix") || m_currentAspect->inherits("Workbook")) dlg->setCurrentIndex( m_projectExplorer->currentIndex()); else if (m_currentAspect->inherits("Column")) { if (m_currentAspect->parentAspect()->inherits("Spreadsheet")) dlg->setCurrentIndex( m_aspectTreeModel->modelIndexOfAspect(m_currentAspect->parentAspect())); } if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importSqlDialog() DONE"); } void MainWin::importProjectDialog() { DEBUG("MainWin::importProjectDialog()"); ImportProjectDialog::ProjectType type; if (QObject::sender() == m_importOpjAction) type = ImportProjectDialog::ProjectOrigin; else type = ImportProjectDialog::ProjectLabPlot; ImportProjectDialog* dlg = new ImportProjectDialog(this, type); // set current folder dlg->setCurrentFolder(m_currentFolder); if (dlg->exec() == QDialog::Accepted) { dlg->importTo(statusBar()); m_project->setChanged(true); } delete dlg; DEBUG("MainWin::importProjectDialog() DONE"); } /*! opens the dialog for the export of the currently active worksheet, spreadsheet or matrix. */ void MainWin::exportDialog() { QMdiSubWindow* win = m_mdiArea->currentSubWindow(); if (!win) return; AbstractPart* part = dynamic_cast(win)->part(); if (part->exportView()) statusBar()->showMessage(i18n("%1 exported", part->name())); } void MainWin::editFitsFileDialog() { FITSHeaderEditDialog* editDialog = new FITSHeaderEditDialog(this); if (editDialog->exec() == QDialog::Accepted) { if (editDialog->saved()) statusBar()->showMessage(i18n("FITS files saved")); } } /*! adds a new file data source to the current project. */ void MainWin::newLiveDataSourceActionTriggered() { ImportFileDialog* dlg = new ImportFileDialog(this, true); if (dlg->exec() == QDialog::Accepted) { LiveDataSource* dataSource = new LiveDataSource(0, i18n("Live data source%1", 1), false); dlg->importToLiveDataSource(dataSource, statusBar()); this->addAspectToProject(dataSource); } delete dlg; } void MainWin::addAspectToProject(AbstractAspect* aspect) { const QModelIndex& index = m_projectExplorer->currentIndex(); if (index.isValid()) { AbstractAspect* parent = static_cast(index.internalPointer()); parent->folder()->addChild(aspect); } else m_project->addChild(aspect); } void MainWin::settingsDialog() { SettingsDialog* dlg = new SettingsDialog(this); connect (dlg, SIGNAL(settingsChanged()), this, SLOT(handleSettingsChanges())); dlg->exec(); } diff --git a/src/kdefrontend/SettingsDialog.cpp b/src/kdefrontend/SettingsDialog.cpp index 0b50cc084..e565fa0cf 100644 --- a/src/kdefrontend/SettingsDialog.cpp +++ b/src/kdefrontend/SettingsDialog.cpp @@ -1,113 +1,113 @@ /*************************************************************************** File : SettingsDialog.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2017 by Alexander Semke (alexander.semke@web.de) Description : application settings dialog ***************************************************************************/ /*************************************************************************** * * * 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 "SettingsDialog.h" #include "MainWin.h" #include "SettingsGeneralPage.h" #include "SettingsWorksheetPage.h" #include #include #include #include #include #include #include /** * \brief Settings dialog for Labplot. * * Contains the pages for general settings and view settings. * */ SettingsDialog::SettingsDialog(QWidget* parent) : KPageDialog(parent), m_changed(false) { setFaceType(List); - setWindowTitle(i18n("Preferences")); + setWindowTitle(i18nc("@title:window", "Preferences")); setWindowIcon(QIcon::fromTheme("preferences-other")); setAttribute(Qt::WA_DeleteOnClose); buttonBox()->addButton(QDialogButtonBox::Apply)->setEnabled(false); buttonBox()->addButton(QDialogButtonBox::RestoreDefaults); connect(buttonBox(), SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonClicked(QAbstractButton*))); m_generalPage = new SettingsGeneralPage(this); KPageWidgetItem* generalFrame = addPage(m_generalPage, i18n("General")); generalFrame->setIcon(QIcon::fromTheme("system-run")); connect(m_generalPage, SIGNAL(settingsChanged()), this, SLOT(changed())); m_worksheetPage = new SettingsWorksheetPage(this); KPageWidgetItem* worksheetFrame = addPage(m_worksheetPage, i18n("Worksheet")); worksheetFrame->setIcon(QIcon::fromTheme(QLatin1String("labplot-worksheet"))); connect(m_worksheetPage, SIGNAL(settingsChanged()), this, SLOT(changed())); const KConfigGroup dialogConfig = KSharedConfig::openConfig()->group("SettingsDialog"); KWindowConfig::restoreWindowSize(windowHandle(), dialogConfig); } SettingsDialog::~SettingsDialog(){ KConfigGroup dialogConfig = KSharedConfig::openConfig()->group("SettingsDialog"); KWindowConfig::saveWindowSize(windowHandle(), dialogConfig); } void SettingsDialog::slotButtonClicked(QAbstractButton* button) { if ((button == buttonBox()->button(QDialogButtonBox::Ok)) || (button == buttonBox()->button(QDialogButtonBox::Apply))) { if (m_changed){ applySettings(); - setWindowTitle(i18n("Preferences")); + setWindowTitle(i18nc("@title:window", "Preferences")); buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false); } } else if (button == buttonBox()->button(QDialogButtonBox::RestoreDefaults)) { const QString text(i18n("All settings will be reset to default values. Do you want to continue?")); if (KMessageBox::questionYesNo(this, text) == KMessageBox::Yes) { restoreDefaults(); - setWindowTitle(i18n("Preferences")); + setWindowTitle(i18nc("@title:window", "Preferences")); buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false); } } } void SettingsDialog::changed() { m_changed = true; - setWindowTitle(i18n("Preferences [Changed]")); + setWindowTitle(i18nc("@title:window", "Preferences [Changed]")); buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(true); } void SettingsDialog::applySettings(){ m_changed = false; m_generalPage->applySettings(); m_worksheetPage->applySettings(); KSharedConfig::openConfig()->sync(); emit settingsChanged(); } void SettingsDialog::restoreDefaults(){ m_changed = false; m_generalPage->restoreDefaults(); m_worksheetPage->restoreDefaults(); } diff --git a/src/kdefrontend/SettingsWorksheetPage.cpp b/src/kdefrontend/SettingsWorksheetPage.cpp index 1ddcb1eb0..f5bee88f7 100644 --- a/src/kdefrontend/SettingsWorksheetPage.cpp +++ b/src/kdefrontend/SettingsWorksheetPage.cpp @@ -1,168 +1,168 @@ /*************************************************************************** File : SettingsWorksheetPage.cpp Project : LabPlot Description : settings page for Worksheet -------------------------------------------------------------------- Copyright : (C) 2008-2017 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 "SettingsWorksheetPage.h" #include "tools/TeXRenderer.h" #include "kdefrontend/widgets/ThemesComboBox.h" -#include +#include #include #include /** * \brief Page for the 'General' settings of the Labplot settings dialog. */ SettingsWorksheetPage::SettingsWorksheetPage(QWidget* parent) : SettingsPage(parent), m_changed(false) { ui.setupUi(this); m_cbThemes = new ThemesComboBox(); ui.gridLayout->addWidget(m_cbThemes, 1, 4, 1, 1); const int size = ui.cbTexEngine->height(); ui.lLatexWarning->setPixmap( QIcon::fromTheme(QLatin1String("state-warning")).pixmap(size, size) ); //add available TeX typesetting engines if (TeXRenderer::executableExists(QLatin1String("lualatex"))) ui.cbTexEngine->addItem(QLatin1String("LuaLaTeX"), QLatin1String("lualatex")); if (TeXRenderer::executableExists(QLatin1String("xelatex"))) ui.cbTexEngine->addItem(QLatin1String("XeLaTex"), QLatin1String("xelatex")); if (TeXRenderer::executableExists(QLatin1String("pdflatex"))) ui.cbTexEngine->addItem(QLatin1String("pdfLaTeX"), QLatin1String("pdflatex")); if (TeXRenderer::executableExists(QLatin1String("latex"))) ui.cbTexEngine->addItem(QLatin1String("LaTeX"), QLatin1String("latex")); connect(m_cbThemes, SIGNAL(currentThemeChanged(QString)), this, SLOT(changed()) ); connect(ui.chkPresenterModeInteractive, SIGNAL(stateChanged(int)), this, SLOT(changed()) ); connect(ui.chkDoubleBuffering, SIGNAL(stateChanged(int)), this, SLOT(changed()) ); connect(ui.cbTexEngine, SIGNAL(currentIndexChanged(int)), this, SLOT(changed()) ); connect(ui.cbTexEngine, SIGNAL(currentIndexChanged(int)), this, SLOT(checkTeX(int)) ); loadSettings(); } void SettingsWorksheetPage::applySettings() { KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); group.writeEntry(QLatin1String("Theme"), m_cbThemes->currentText()); group.writeEntry(QLatin1String("PresenterModeInteractive"), ui.chkPresenterModeInteractive->isChecked()); group.writeEntry(QLatin1String("DoubleBuffering"), ui.chkDoubleBuffering->isChecked()); group.writeEntry(QLatin1String("LaTeXEngine"), ui.cbTexEngine->itemData(ui.cbTexEngine->currentIndex())); } void SettingsWorksheetPage::restoreDefaults() { loadSettings(); } void SettingsWorksheetPage::loadSettings() { const KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); m_cbThemes->setItemText(0, group.readEntry(QLatin1String("Theme"), "")); ui.chkPresenterModeInteractive->setChecked(group.readEntry(QLatin1String("PresenterModeInteractive"), false)); ui.chkDoubleBuffering->setChecked(group.readEntry(QLatin1String("DoubleBuffering"), true)); QString engine = group.readEntry(QLatin1String("LaTeXEngine"), ""); int index = -1; if (engine.isEmpty()) { //empty string was found in the settings (either the settings never saved or no tex engine was available during the last save) //->check whether the latex environment was installed in the meantime index = ui.cbTexEngine->findData(QLatin1String("xelatex")); if (index == -1) { index = ui.cbTexEngine->findData(QLatin1String("lualatex")); if (index == -1) { index = ui.cbTexEngine->findData(QLatin1String("pdflatex")); if (index == -1) index = ui.cbTexEngine->findData(QLatin1String("latex")); } } if (index != -1) { //one of the tex engines was found -> automatically save it in the settings without any user action KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); group.writeEntry(QLatin1String("LaTeXEngine"), ui.cbTexEngine->itemData(index)); } } else index = ui.cbTexEngine->findData(engine); ui.cbTexEngine->setCurrentIndex(index); checkTeX(index); } void SettingsWorksheetPage::changed() { m_changed = true; emit settingsChanged(); } /*! checks whether all tools required for latex typesetting are available. shows a warning if not. \sa TeXRenderer::active() */ void SettingsWorksheetPage::checkTeX(int engineIndex) { if (engineIndex == -1) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No LaTeX installation found or selected. LaTeX typesetting not possible.")); return; } //engine found, check the precense of other required tools (s.a. TeXRenderer.cpp): //to convert the generated PDF/PS files to PNG we need 'convert' from the ImageMagic package if (!TeXRenderer::executableExists(QLatin1String("convert"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No 'convert' found. LaTeX typesetting not possible.")); return; } QString engine = ui.cbTexEngine->itemData(engineIndex).toString(); if (engine=="latex") { //to convert the generated PS files to DVI we need 'dvips' if (!TeXRenderer::executableExists(QLatin1String("dvips"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No 'dvips' found. LaTeX typesetting not possible.")); return; } } #if defined(_WIN64) if (!TeXRenderer::executableExists(QLatin1String("gswin64c"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No Ghostscript found. LaTeX typesetting not possible.")); return; } #elif defined(HAVE_WINDOWS) if (!TeXRenderer::executableExists(QLatin1String("gswin32c"))) { ui.lLatexWarning->show(); ui.lLatexWarning->setToolTip(i18n("No Ghostscript found. LaTeX typesetting not possible.")); return; } #endif ui.lLatexWarning->hide(); } diff --git a/src/kdefrontend/TemplateHandler.cpp b/src/kdefrontend/TemplateHandler.cpp index 30306250f..66bd7f714 100644 --- a/src/kdefrontend/TemplateHandler.cpp +++ b/src/kdefrontend/TemplateHandler.cpp @@ -1,220 +1,220 @@ /*************************************************************************** File : TemplateHandler.cpp Project : LabPlot Description : Widget for handling saving and loading of templates -------------------------------------------------------------------- Copyright : (C) 2012 by Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2012-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "TemplateHandler.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class TemplateHandler \brief Provides a widget with buttons for saving and loading of templates. Emits \c loadConfig() and \c saveConfig() signals that have to be connected to the appropriate slots in the ui (mostly in the dock widgets) \ingroup kdefrontend */ TemplateHandler::TemplateHandler(QWidget *parent, ClassName name): QWidget(parent){ QHBoxLayout* horizontalLayout = new QHBoxLayout(this); horizontalLayout->setSpacing(0); horizontalLayout->setMargin(0); QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); horizontalLayout->addItem(horizontalSpacer); int size = KIconLoader::global()->currentSize(KIconLoader::MainToolbar); m_tbLoad = new QToolButton(this); m_tbLoad->setIconSize(QSize(size, size)); horizontalLayout->addWidget(m_tbLoad); m_tbSave = new QToolButton(this); m_tbSave->setIconSize(QSize(size, size)); horizontalLayout->addWidget(m_tbSave); m_tbSaveDefault = new QToolButton(this); m_tbSaveDefault->setIconSize(QSize(size, size)); horizontalLayout->addWidget(m_tbSaveDefault); // QSpacerItem* horizontalSpacer2 = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum); // horizontalLayout->addItem(horizontalSpacer2); m_tbCopy = new QToolButton(this); m_tbCopy->setIconSize(QSize(size, size)); m_tbCopy->setEnabled(false); horizontalLayout->addWidget(m_tbCopy); m_tbPaste = new QToolButton(this); m_tbPaste->setIconSize(QSize(size, size)); m_tbPaste->setEnabled(false); horizontalLayout->addWidget(m_tbPaste); m_tbLoad->setIcon(QIcon::fromTheme("document-open")); m_tbSave->setIcon(QIcon::fromTheme("document-save")); m_tbSaveDefault->setIcon(QIcon::fromTheme("document-save-as")); m_tbCopy->setIcon(QIcon::fromTheme("edit-copy")); m_tbPaste->setIcon(QIcon::fromTheme("edit-paste")); connect( m_tbLoad, SIGNAL(clicked()), this, SLOT(loadMenu())); connect( m_tbSave, SIGNAL(clicked()), this, SLOT(saveMenu())); connect( m_tbSaveDefault, SIGNAL(clicked()), this, SLOT(saveDefaults())); m_className = name; //synchronize this with the ordering in TemplateHandler::ClassName m_dirNames <<"spreadsheet"<<"matrix"<<"worksheet"<<"cartesianplot"<<"cartesianplotlegend"<<"xycurve"<<"axis"<<"custompoint"<<"histogram"; this->retranslateUi(); //disable the load-button if no templates are available yet QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + m_dirNames.at(m_className) + "/*"); m_tbLoad->setEnabled(list.size()); //TODO: implement copy&paste of properties and activate copy- and paste-buttons again m_tbCopy->hide(); m_tbPaste->hide(); } void TemplateHandler::retranslateUi(){ m_tbLoad->setToolTip(i18n("Load properties from a template")); m_tbSave->setToolTip(i18n("Save current properties as a template")); m_tbSaveDefault->setToolTip(i18n("Save current properties as default")); m_tbCopy->setToolTip(i18n("Copy properties")); m_tbPaste->setToolTip(i18n("Paste properties")); } //############################################################################## //################################## Slots #################################### //############################################################################## void TemplateHandler::loadMenu() { QMenu menu; - menu.addSection(i18n("Load from")); + menu.addSection(i18n("Load From")); QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/" + m_dirNames.at(m_className) + "/*"); for (int i = 0; i < list.size(); ++i) { QFileInfo fileinfo(list.at(i)); QAction* action = menu.addAction(fileinfo.fileName()); action->setData(QVariant(list.at(i))); } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(loadMenuSelected(QAction*))); QPoint pos(-menu.sizeHint().width()+m_tbLoad->width(),-menu.sizeHint().height()); menu.exec(m_tbLoad->mapToGlobal(pos)); } void TemplateHandler::loadMenuSelected(QAction* action) { KConfig config(action->data().toString(), KConfig::SimpleConfig); emit loadConfigRequested(config); emit info( i18n("Template \"%1\" was loaded.", action->text().remove('&')) ); } void TemplateHandler::saveMenu() { QMenu menu; - menu.addSection(i18n("Save as")); + menu.addSection(i18n("Save As")); QStringList list = QStandardPaths::locateAll(QStandardPaths::ApplicationsLocation, "templates/"+ m_dirNames.at(m_className) + "/*"); for (int i = 0; i < list.size(); ++i) { QFileInfo fileinfo(list.at(i)); QAction* action = menu.addAction(fileinfo.fileName()); menu.addAction(action); action->setShortcut(QKeySequence()); } connect(&menu, SIGNAL(triggered(QAction*)), this, SLOT(saveMenuSelected(QAction*))); // add editable action QWidgetAction* widgetAction = new QWidgetAction(this); QFrame* frame = new QFrame(this); QHBoxLayout* layout = new QHBoxLayout(frame); - QLabel* label = new QLabel(i18n("new:"), frame); + QLabel* label = new QLabel(i18n("New:"), frame); layout->addWidget(label); QLineEdit* leFilename = new QLineEdit("", frame); layout->addWidget(leFilename); connect(leFilename, SIGNAL(returnPressed(QString)), this, SLOT(saveNewSelected(QString))); connect(leFilename, SIGNAL(returnPressed(QString)), &menu, SLOT(close())); widgetAction->setDefaultWidget(frame); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+m_tbSave->width(),-menu.sizeHint().height()); menu.exec(m_tbSave->mapToGlobal(pos)); //TODO: focus is not set. why? leFilename->setFocus(); } /*! * Is called when the current properties are going to be saved as a new template. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveNewSelected(const QString& filename) { KConfig config(QStandardPaths::locate(QStandardPaths::ApplicationsLocation, "templates") + '/' + m_dirNames.at(m_className) + '/' + filename, KConfig::SimpleConfig); emit saveConfigRequested(config); //we have at least one saved template now -> enable the load button m_tbLoad->setEnabled(true); emit info( i18n("New template \"%1\" was saved.", filename) ); } /*! * Is called when the current properties are going to be saved in an already available template. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveMenuSelected(QAction* action) { KConfig config(action->data().toString()+'/'+action->text(), KConfig::SimpleConfig); emit saveConfigRequested(config); emit info( i18n("Template \"%1\" was saved.", action->text()) ); } /*! * Is called when the current properties are going to be saved as new default properties. * Emits \c saveConfigRequested, the receiver of the signal has to config.sync(). */ void TemplateHandler::saveDefaults() { KConfig config; emit saveConfigRequested(config); emit info( i18n("New default template was saved.") ); } diff --git a/src/kdefrontend/ThemeHandler.cpp b/src/kdefrontend/ThemeHandler.cpp index b190493bb..b96547db6 100644 --- a/src/kdefrontend/ThemeHandler.cpp +++ b/src/kdefrontend/ThemeHandler.cpp @@ -1,236 +1,234 @@ /*************************************************************************** File : ThemeHandler.cpp Project : LabPlot Description : Widget for handling saving and loading of themes -------------------------------------------------------------------- Copyright : (C) 2016 Prakriti Bhardwaj (p_bhardwaj14@informatik.uni-kl.de) Copyright : (C) 2016-2017 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 "ThemeHandler.h" #include "widgets/ThemesWidget.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include // #include -#include - /*! \class ThemeHandler \brief Provides a widget with buttons for loading of themes. Emits \c loadConfig() signal that have to be connected to the appropriate slots in the backend (plot widgets) \ingroup kdefrontend */ ThemeHandler::ThemeHandler(QWidget* parent) : QWidget(parent) { QHBoxLayout* horizontalLayout = new QHBoxLayout(this); horizontalLayout->setSpacing(0); horizontalLayout->setMargin(0); m_pbLoadTheme = new QPushButton(this); horizontalLayout->addWidget(m_pbLoadTheme); - m_pbLoadTheme->setText(i18n("Apply theme")); + m_pbLoadTheme->setText(i18n("Apply Theme")); // pbSaveTheme = new QPushButton(this); // horizontalLayout->addWidget(pbSaveTheme); -// pbSaveTheme->setText(i18n("Save theme")); +// pbSaveTheme->setText(i18n("Save Theme")); /* pbPublishTheme = new QPushButton(this); horizontalLayout->addWidget(pbPublishTheme); - pbPublishTheme->setText("Publish theme"); + pbPublishTheme->setText("Publish Theme"); pbPublishTheme->setEnabled(false); */ connect( m_pbLoadTheme, SIGNAL(clicked()), this, SLOT(showPanel())); // connect( pbSaveTheme, SIGNAL(clicked()), this, SLOT(saveMenu())); // connect( pbPublishTheme, SIGNAL(clicked()), this, SLOT(publishThemes())); //find all available themes files (system wide and user specific local files) //the list m_themeList contains full paths (path + file name) QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); for (const auto& dir : dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) m_themeList.append(it.next()); } m_pbLoadTheme->setEnabled(!m_themeList.isEmpty()); } void ThemeHandler::setCurrentTheme(const QString& name) { if (!name.isEmpty()) { - m_pbLoadTheme->setText(i18n("Apply theme [active '%1']").arg(name)); - m_pbLoadTheme->setToolTip(i18n("Theme '%1' is active. Click on the button to change the theme.").arg(name)); + m_pbLoadTheme->setText(i18n("Apply theme [active '%1']", name)); + m_pbLoadTheme->setToolTip(i18n("Theme '%1' is active. Click on the button to change the theme.", name)); } else { - m_pbLoadTheme->setText(i18n("Apply theme")); + m_pbLoadTheme->setText(i18n("Apply Theme")); m_pbLoadTheme->setToolTip(i18n("No theme is active. Click on the button to select a theme.")); } m_currentTheme = name; } void ThemeHandler::loadSelected(const QString& name) { emit loadThemeRequested(name); this->setCurrentTheme(name); if (!name.isEmpty()) emit info( i18n("Theme \"%1\" was loaded.", name) ); else emit info( i18n("Theming deactivated.") ); //in case a local theme file was loaded (we have write access), allow to publish it //TODO: activate this later // if (KStandardDirs::checkAccess(themeFilePath, W_OK)) { // pbPublishTheme->setEnabled(true); // m_currentLocalTheme = themeFilePath.right(themeFilePath.length() - themeFilePath.lastIndexOf(QDir::separator()) - 1); // } else { // pbPublishTheme->setEnabled(false); // m_currentLocalTheme.clear(); // } } QStringList ThemeHandler::themes() { QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); QStringList pathList; for (const auto& dir : dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) pathList.append(it.next()); } QStringList themeList; for (int i = 0; i < pathList.size(); ++i) { QFileInfo fileinfo(pathList.at(i)); themeList.append(fileinfo.fileName().split('.').at(0)); } return themeList; } const QString ThemeHandler::themeFilePath(const QString& name) { QStringList dirs = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes", QStandardPaths::LocateDirectory); QStringList themes; for (const auto& dir : dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*"), QDir::Files); while (it.hasNext()) themes.append(it.next()); } for (int i = 0; i < themes.size(); ++i) { if (themes.at(i).indexOf(name) != -1) return themes.at(i); } return QString(); } void ThemeHandler::showPanel() { QMenu menu; ThemesWidget themeWidget(&menu); connect(&themeWidget, SIGNAL(themeSelected(QString)), this, SLOT(loadSelected(QString))); connect(&themeWidget, SIGNAL(themeSelected(QString)), &menu, SLOT(close())); connect(&themeWidget, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&themeWidget); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+m_pbLoadTheme->width(),-menu.sizeHint().height()); menu.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); menu.exec(m_pbLoadTheme->mapToGlobal(pos)); } // void ThemeHandler::saveMenu() { // QMenu menu; -// menu.addSection(i18n("Save as")); +// menu.addSection(i18n("Save As")); // // // add editable action // QWidgetAction* widgetAction = new QWidgetAction(this); // QFrame* frame = new QFrame(this); // QHBoxLayout* layout = new QHBoxLayout(frame); // // QLabel* label = new QLabel(i18n("Enter name:"), frame); // layout->addWidget(label); // // QLineEdit* leFilename = new QLineEdit("", frame); // layout->addWidget(leFilename); // connect(leFilename, SIGNAL(returnPressed(QString)), this, SLOT(saveNewSelected(QString))); // connect(leFilename, SIGNAL(returnPressed(QString)), &menu, SLOT(close())); // // widgetAction->setDefaultWidget(frame); // menu.addAction(widgetAction); // // QPoint pos(-menu.sizeHint().width() + m_pbSaveTheme->width(), -menu.sizeHint().height()); // menu.exec(m_pbSaveTheme->mapToGlobal(pos)); // leFilename->setFocus(); // } // void ThemeHandler::saveNewSelected(const QString& filename) { // KConfig config(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + '/' + "themes" + '/' + filename, KConfig::SimpleConfig); // emit saveThemeRequested(config); // emit info( i18n("New theme \"%1\" was saved.", filename) ); // // m_currentLocalTheme = filename; // m_themeList.append(config.name()); // // //enable the publish button so the newly created theme can be published // //TODO: enable this later // // pbPublishTheme->setEnabled(true); // } /*! opens the dialog to upload the currently selected local theme. The publish button is only enabled if a local theme was loaded or one of the themes was modified and saved locally. */ // void ThemeHandler::publishThemes() { // int ret = KMessageBox::questionYesNo(this, -// i18n("Do you want to upload your theme %1 to public web server?").arg(m_currentLocalTheme), +// i18n("Do you want to upload your theme %1 to public web server?", m_currentLocalTheme), // i18n("Publish Theme")); // if (ret != KMessageBox::Yes) // return; // // // creating upload dialog // KNS3::UploadDialog dialog("labplot2_themes.knsrc", this); // dialog.setUploadFile(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + '/' + "themes" + '/' + m_currentLocalTheme); // dialog.setUploadName(m_currentLocalTheme); // //dialog.setDescription(); TODO: allow the user to provide a short description for the theme to be uploaded // dialog.exec(); // } diff --git a/src/kdefrontend/datasources/AsciiOptionsWidget.cpp b/src/kdefrontend/datasources/AsciiOptionsWidget.cpp index 32908f08a..07736dba9 100644 --- a/src/kdefrontend/datasources/AsciiOptionsWidget.cpp +++ b/src/kdefrontend/datasources/AsciiOptionsWidget.cpp @@ -1,191 +1,191 @@ /*************************************************************************** File : AsciiOptionsWidget.h Project : LabPlot Description : widget providing options for the import of ascii data -------------------------------------------------------------------- Copyright : (C) 2009-2017 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2017 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 "AsciiOptionsWidget.h" #include "backend/datasources/filters/AbstractFileFilter.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/lib/macros.h" -#include +#include #include #include /*! \class AsciiOptionsWidget \brief Widget providing options for the import of ascii data \ingroup kdefrontend */ AsciiOptionsWidget::AsciiOptionsWidget(QWidget* parent) : QWidget(parent) { ui.setupUi(parent); ui.cbSeparatingCharacter->addItems(AsciiFilter::separatorCharacters()); ui.cbCommentCharacter->addItems(AsciiFilter::commentCharacters()); ui.cbNumberFormat->addItems(AbstractFileFilter::numberFormats()); ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); const QString textNumberFormatShort = i18n("This option determines how the imported strings have to be converted to numbers."); const QString textNumberFormat = textNumberFormatShort + "

" + i18n( "For 'C Format', a period is used for the decimal point character and comma is used for the thousands group separator. " "Valid number representations are:" "
    " "
  • 1234.56
  • " "
  • 1,234.56
  • " "
  • etc.
  • " "
" "When using 'System locale', the system settings will be used. " "E.g., for the German local the valid number representations are:" "
    " "
  • 1234,56
  • " "
  • 1.234,56
  • " "
  • etc.
  • " "
" ); ui.lNumberFormat->setToolTip(textNumberFormatShort); ui.lNumberFormat->setWhatsThis(textNumberFormat); ui.cbNumberFormat->setToolTip(textNumberFormatShort); ui.cbNumberFormat->setWhatsThis(textNumberFormat); const QString textDateTimeFormatShort = i18n("This option determines how the imported strings have to be converted to calendar date, i.e. year, month, and day numbers in the Gregorian calendar and to time."); const QString textDateTimeFormat = textDateTimeFormatShort + "

" + i18n( "Expressions that may be used for the date part of format string:" "" "" "" "" "" "" "" "" "" "" "" "
dthe day as number without a leading zero (1 to 31).
ddthe day as number with a leading zero (01 to 31).
dddthe abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name.
ddddthe long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name.
Mthe month as number without a leading zero (1 to 12).
MMthe month as number with a leading zero (01 to 12).
MMMthe abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name.
MMMMthe long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name.
yythe year as two digit number (00 to 99).
yyyythe year as four digit number. If the year is negative, a minus sign is prepended in addition.


" "Expressions that may be used for the time part of the format string:" "" "" "" "" "" "" "" "" "" "" "" "" "" "
hthe hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)
hhthe hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)
Hthe hour without a leading zero (0 to 23, even with AM/PM display)
HHthe hour with a leading zero (00 to 23, even with AM/PM display)
mthe minute without a leading zero (0 to 59)
mmthe minute with a leading zero (00 to 59)
sthe second without a leading zero (0 to 59)
ssthe second with a leading zero (00 to 59)
zthe milliseconds without leading zeroes (0 to 999)
zzzthe milliseconds with leading zeroes (000 to 999)
AP or Ainterpret as an AM/PM time. AP must be either 'AM' or 'PM'.
ap or aInterpret as an AM/PM time. ap must be either 'am' or 'pm'.


" "Examples are:" "" "" "" "" "
dd.MM.yyyy20.07.1969
ddd MMMM d yySun July 20 69
'The day is' ddddThe day is Sunday
"); ui.lDateTimeFormat->setToolTip(textDateTimeFormatShort); ui.lDateTimeFormat->setWhatsThis(textDateTimeFormat); ui.cbDateTimeFormat->setToolTip(textDateTimeFormatShort); ui.cbDateTimeFormat->setWhatsThis(textDateTimeFormat); connect(ui.chbHeader, SIGNAL(stateChanged(int)), SLOT(headerChanged(int))); } void AsciiOptionsWidget::showAsciiHeaderOptions(bool b) { DEBUG("AsciiOptionsWidget::showAsciiHeaderOptions(" << b << ")"); ui.chbHeader->setVisible(b); ui.lVectorNames->setVisible(b); ui.kleVectorNames->setVisible(b); } /*! enables a text field for the vector names if the option "Use the first row..." was not selected. Disables it otherwise. */ void AsciiOptionsWidget::headerChanged(int state) { DEBUG("AsciiOptionsWidget::headerChanged(" << state << ")"); if (state == Qt::Checked) { ui.kleVectorNames->setEnabled(false); ui.lVectorNames->setEnabled(false); } else { ui.kleVectorNames->setEnabled(true); ui.lVectorNames->setEnabled(true); } } void AsciiOptionsWidget::applyFilterSettings(AsciiFilter* filter) const { Q_ASSERT(filter); filter->setCommentCharacter( ui.cbCommentCharacter->currentText() ); filter->setSeparatingCharacter( ui.cbSeparatingCharacter->currentText() ); filter->setNumberFormat( QLocale::Language(ui.cbNumberFormat->currentIndex()) ); filter->setDateTimeFormat(ui.cbDateTimeFormat->currentText()); filter->setCreateIndexEnabled( ui.chbCreateIndex->isChecked() ); filter->setSimplifyWhitespacesEnabled( ui.chbSimplifyWhitespaces->isChecked() ); filter->setNaNValueToZero( ui.chbConvertNaNToZero->isChecked() ); filter->setRemoveQuotesEnabled( ui.chbRemoveQuotes->isChecked() ); filter->setSkipEmptyParts( ui.chbSkipEmptyParts->isChecked() ); filter->setVectorNames( ui.kleVectorNames->text() ); filter->setHeaderEnabled( ui.chbHeader->isChecked() ); } void AsciiOptionsWidget::loadSettings() const { KConfigGroup conf(KSharedConfig::openConfig(), "ImportAscii"); //TODO: check if this works (character gets currentItem?) ui.cbCommentCharacter->setCurrentItem(conf.readEntry("CommentCharacter", "#")); ui.cbSeparatingCharacter->setCurrentItem(conf.readEntry("SeparatingCharacter", "auto")); ui.cbNumberFormat->setCurrentIndex(conf.readEntry("NumberFormat", (int)QLocale::AnyLanguage)); ui.cbDateTimeFormat->setCurrentItem(conf.readEntry("DateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz")); ui.chbCreateIndex->setChecked(conf.readEntry("CreateIndex", false)); ui.chbSimplifyWhitespaces->setChecked(conf.readEntry("SimplifyWhitespaces", true)); ui.chbConvertNaNToZero->setChecked(conf.readEntry("ConvertNaNToZero", false)); ui.chbRemoveQuotes->setChecked(conf.readEntry("RemoveQuotes", false)); ui.chbSkipEmptyParts->setChecked(conf.readEntry("SkipEmptyParts", false)); ui.chbHeader->setChecked(conf.readEntry("UseFirstRow", true)); ui.kleVectorNames->setText(conf.readEntry("Names", "")); } void AsciiOptionsWidget::saveSettings() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportAscii"); conf.writeEntry("CommentCharacter", ui.cbCommentCharacter->currentText()); conf.writeEntry("SeparatingCharacter", ui.cbSeparatingCharacter->currentText()); conf.writeEntry("NumberFormat", ui.cbNumberFormat->currentIndex()); conf.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText()); conf.writeEntry("CreateIndex", ui.chbCreateIndex->isChecked()); conf.writeEntry("SimplifyWhitespaces", ui.chbSimplifyWhitespaces->isChecked()); conf.writeEntry("ConvertNaNToZero", ui.chbConvertNaNToZero->isChecked()); conf.writeEntry("RemoveQuotes", ui.chbRemoveQuotes->isChecked()); conf.writeEntry("SkipEmptyParts", ui.chbSkipEmptyParts->isChecked()); conf.writeEntry("UseFirstRow", ui.chbHeader->isChecked()); conf.writeEntry("Names", ui.kleVectorNames->text()); } diff --git a/src/kdefrontend/datasources/DatabaseManagerDialog.cpp b/src/kdefrontend/datasources/DatabaseManagerDialog.cpp index ab94abbe2..d339a729e 100644 --- a/src/kdefrontend/datasources/DatabaseManagerDialog.cpp +++ b/src/kdefrontend/datasources/DatabaseManagerDialog.cpp @@ -1,92 +1,92 @@ /*************************************************************************** File : DatabaseManagerDialog.cc Project : LabPlot Description : dialog for managing database connections -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 "DatabaseManagerDialog.h" #include "DatabaseManagerWidget.h" -#include +#include #include #include #include #include /*! \class DatabaseManagerDialog \brief dialog for managing database connections \ingroup kdefrontend */ DatabaseManagerDialog::DatabaseManagerDialog(QWidget* parent, const QString& conn) : QDialog(parent), mainWidget(new DatabaseManagerWidget(this, conn)), m_changed(false) { setWindowIcon(QIcon::fromTheme("network-server-database")); - setWindowTitle(i18n("SQL Database Connections")); + setWindowTitle(i18nc("@title:window", "SQL Database Connections")); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); connect(mainWidget, SIGNAL(changed()), this, SLOT(changed())); connect(buttonBox->button(QDialogButtonBox::Ok),&QPushButton::clicked, this, &DatabaseManagerDialog::save); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &DatabaseManagerDialog::close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QTimer::singleShot(0, this, &DatabaseManagerDialog::loadSettings); } void DatabaseManagerDialog::loadSettings() { //restore saved settings QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "DatabaseManagerDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); } QString DatabaseManagerDialog::connection() const { return mainWidget->connection(); } DatabaseManagerDialog::~DatabaseManagerDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "DatabaseManagerDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void DatabaseManagerDialog::changed() { - setWindowTitle(i18n("SQL Database Connections [Changed]")); + setWindowTitle(i18nc("@title:window", "SQL Database Connections [Changed]")); m_changed = true; } void DatabaseManagerDialog::save() { //ok-button was clicked, save the connections if they were changed if (m_changed) mainWidget->saveConnections(); } diff --git a/src/kdefrontend/datasources/DatabaseManagerWidget.cpp b/src/kdefrontend/datasources/DatabaseManagerWidget.cpp index 78aca0acf..439f98463 100644 --- a/src/kdefrontend/datasources/DatabaseManagerWidget.cpp +++ b/src/kdefrontend/datasources/DatabaseManagerWidget.cpp @@ -1,479 +1,480 @@ /*************************************************************************** File : DatabaseManagerWidget.cpp Project : LabPlot Description : widget for managing database connections -------------------------------------------------------------------- Copyright : (C) 2017-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 "DatabaseManagerWidget.h" #include "backend/lib/macros.h" #include #include -#include +#include #include +#include #include #include #include #include /*! \class DatabaseManagerWidget \brief widget for managing database connections, embedded in \c DatabaseManagerDialog. \ingroup kdefrontend */ DatabaseManagerWidget::DatabaseManagerWidget(QWidget* parent, const QString& conn) : QWidget(parent), m_initializing(false), m_initConnName(conn) { m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + "sql_connections"; ui.setupUi(this); ui.tbAdd->setIcon(QIcon::fromTheme("list-add")); ui.tbDelete->setIcon(QIcon::fromTheme("list-remove")); ui.bOpen->setIcon(QIcon::fromTheme("document-open")); ui.bTestConnection->setIcon(QIcon::fromTheme("network-connect")); ui.tbAdd->setToolTip(i18n("Add new database connection")); ui.tbDelete->setToolTip(i18n("Delete selected database connection")); ui.bOpen->setToolTip(i18n("Open database file")); ui.bTestConnection->setToolTip(i18n("Test selected database connection")); //add the list of supported SQL drivers ui.cbDriver->addItems(QSqlDatabase::drivers()); //SIGNALs/SLOTs connect( ui.lwConnections, SIGNAL(currentRowChanged(int)), this, SLOT(connectionChanged(int)) ); connect( ui.tbAdd, SIGNAL(clicked()), this, SLOT(addConnection()) ); connect( ui.tbDelete, SIGNAL(clicked()), this, SLOT(deleteConnection()) ); connect( ui.bTestConnection, SIGNAL(clicked()), this, SLOT(testConnection()) ); connect( ui.bOpen, SIGNAL(clicked()), this, SLOT(selectFile()) ); connect( ui.cbDriver, SIGNAL(currentIndexChanged(int)), SLOT(driverChanged()) ); connect( ui.leName, SIGNAL(textChanged(QString)), this, SLOT(nameChanged(QString)) ); connect( ui.leDatabase, SIGNAL(textChanged(QString)), this, SLOT(databaseNameChanged()) ); connect( ui.leHost, SIGNAL(textChanged(QString)), this, SLOT(hostChanged()) ); connect( ui.sbPort, SIGNAL(valueChanged(int)), this, SLOT(portChanged()) ); connect( ui.leUserName, SIGNAL(textChanged(QString)), this, SLOT(userNameChanged()) ); connect( ui.lePassword, SIGNAL(textChanged(QString)), this, SLOT(passwordChanged()) ); QTimer::singleShot( 100, this, SLOT(loadConnections()) ); } QString DatabaseManagerWidget::connection() const { if (ui.lwConnections->currentItem()) return ui.lwConnections->currentItem()->text(); else return QString(); } /*! shows the settings of the currently selected connection */ void DatabaseManagerWidget::connectionChanged(int index) { if (m_initializing) return; if (index == -1) return; //show the settings for the selected connection m_initializing = true; ui.leName->setText(m_connections[index].name); ui.cbDriver->setCurrentIndex(ui.cbDriver->findText(m_connections[index].driver)); ui.leDatabase->setText(m_connections[index].dbName); if (!isFileDB(m_connections[index].driver)) { ui.leHost->setText(m_connections[index].hostName); ui.sbPort->setValue(m_connections[index].port); ui.leUserName->setText(m_connections[index].userName); ui.lePassword->setText(m_connections[index].password); } m_initializing = false; } void DatabaseManagerWidget::nameChanged(const QString& name) { //check uniqueness of the provided name bool unique = true; for(int i = 0; i < ui.lwConnections->count(); ++i) { if (ui.lwConnections->currentRow() == i) continue; if (name == ui.lwConnections->item(i)->text()) { unique = false; break; } } if (unique) { ui.leName->setStyleSheet(""); ui.lwConnections->currentItem()->setText(name); if (!m_initializing) { m_connections[ui.lwConnections->currentRow()].name = name; emit changed(); } } else ui.leName->setStyleSheet("QLineEdit{background: red;}"); } void DatabaseManagerWidget::driverChanged() { //hide non-relevant fields (like host name, etc.) for file DBs bool fileDB = isFileDB(ui.cbDriver->currentText()); ui.lHost->setVisible(!fileDB); ui.leHost->setVisible(!fileDB); ui.lPort->setVisible(!fileDB); ui.sbPort->setVisible(!fileDB); ui.bOpen->setVisible(fileDB); ui.gbAuthentication->setVisible(!fileDB); if (m_initializing) return; m_connections[ui.lwConnections->currentRow()].driver = ui.cbDriver->currentText(); emit changed(); } void DatabaseManagerWidget::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("DatabaseManagerWidget")); QString dir = conf.readEntry(QLatin1String("LastDir"), ""); QString path = QFileDialog::getOpenFileName(this, i18n("Select the database file"), dir); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry(QLatin1String("LastDir"), newDir); } ui.leDatabase->setText(path); } void DatabaseManagerWidget::hostChanged() { if (m_initializing) return; m_connections[ui.lwConnections->currentRow()].hostName = ui.leHost->text(); emit changed(); } void DatabaseManagerWidget::portChanged() { if (m_initializing) return; m_connections[ui.lwConnections->currentRow()].port = ui.sbPort->value(); emit changed(); } void DatabaseManagerWidget::databaseNameChanged() { if (m_initializing) return; QString dbName = ui.leDatabase->text().simplified(); if (isFileDB(ui.cbDriver->currentText())) { #ifndef HAVE_WINDOWS // make relative path if ( !dbName.isEmpty() && dbName.at(0) != QDir::separator()) dbName = QDir::homePath() + QDir::separator() + dbName; #endif if (!dbName.isEmpty()) { bool fileExists = QFile::exists(dbName); if (fileExists) ui.leDatabase->setStyleSheet(""); else ui.leDatabase->setStyleSheet("QLineEdit{background:red;}"); } else { ui.leDatabase->setStyleSheet(""); } } else { ui.leDatabase->setStyleSheet(""); } //don't allow to try to connect if no database name was provided ui.bTestConnection->setEnabled( !dbName.isEmpty() ); m_connections[ui.lwConnections->currentRow()].dbName = dbName; emit changed(); } void DatabaseManagerWidget::userNameChanged() { if (m_initializing) return; m_connections[ui.lwConnections->currentRow()].userName = ui.leUserName->text(); emit changed(); } void DatabaseManagerWidget::passwordChanged() { if (m_initializing) return; m_connections[ui.lwConnections->currentRow()].password = ui.lePassword->text(); emit changed(); } void DatabaseManagerWidget::addConnection() { DEBUG("Adding new connection"); SQLConnection conn; conn.name = uniqueName(); conn.driver = ui.cbDriver->currentText(); conn.hostName = QLatin1String("localhost"); if (!isFileDB(conn.driver)) conn.port = defaultPort(conn.driver); m_connections.append(conn); ui.lwConnections->addItem(conn.name); ui.lwConnections->setCurrentRow(m_connections.size()-1); m_initializing = true; //call this to properly update the widgets for the very first added connection driverChanged(); m_initializing = false; //we have now more then one connection, enable widgets ui.tbDelete->setEnabled(true); ui.leName->setEnabled(true); ui.leDatabase->setEnabled(true); ui.cbDriver->setEnabled(true); ui.leHost->setEnabled(true); ui.sbPort->setEnabled(true); ui.leUserName->setEnabled(true); ui.lePassword->setEnabled(true); } /*! removes the current selected connection. */ void DatabaseManagerWidget::deleteConnection() { int ret = KMessageBox::questionYesNo(this, - i18n("Do you really want to delete the connection '%1'?").arg(ui.lwConnections->currentItem()->text()), + i18n("Do you really want to delete the connection '%1'?", ui.lwConnections->currentItem()->text()), i18n("Delete Connection")); if (ret != KMessageBox::Yes) return; //remove the current selected connection m_connections.removeAt(ui.lwConnections->currentRow()); m_initializing = true; QListWidgetItem* item = ui.lwConnections->takeItem(ui.lwConnections->currentRow()); if (item) delete item; m_initializing = false; //show the connection for the item that was automatically selected afte the deletion connectionChanged(ui.lwConnections->currentRow()); //disable widgets if there're no connections anymore if (m_connections.size() == 0) { m_initializing = true; ui.tbDelete->setEnabled(false); ui.bTestConnection->setEnabled(false); ui.leName->clear(); ui.leName->setEnabled(false); ui.leDatabase->clear(); ui.leDatabase->setEnabled(false); ui.cbDriver->setEnabled(false); ui.leHost->clear(); ui.leHost->setEnabled(false); ui.sbPort->clear(); ui.sbPort->setEnabled(false); ui.leUserName->clear(); ui.leUserName->setEnabled(false); ui.lePassword->clear(); ui.lePassword->setEnabled(false); m_initializing = false; } emit changed(); } void DatabaseManagerWidget::loadConnections() { QDEBUG("Loading connections from " << m_configPath); m_initializing = true; KConfig config(m_configPath, KConfig::SimpleConfig); for (const auto& groupName : config.groupList()) { const KConfigGroup& group = config.group(groupName); SQLConnection conn; conn.name = groupName; conn.driver = group.readEntry("Driver",""); conn.dbName = group.readEntry("DatabaseName", ""); if (!isFileDB(conn.driver)) { conn.hostName = group.readEntry("HostName", "localhost"); conn.port = group.readEntry("Port", defaultPort(conn.driver)); conn.userName = group.readEntry("UserName", "root"); conn.password = group.readEntry("Password", ""); } m_connections.append(conn); ui.lwConnections->addItem(conn.name); } //show the first connection if available, create a new connection otherwise if (m_connections.size()) { if (!m_initConnName.isEmpty()) { QListWidgetItem* item = ui.lwConnections->findItems(m_initConnName, Qt::MatchExactly).constFirst(); if (item) ui.lwConnections->setCurrentItem(item); else ui.lwConnections->setCurrentRow(ui.lwConnections->count()-1); } else { ui.lwConnections->setCurrentRow(ui.lwConnections->count()-1); } } else { addConnection(); } //show/hide the driver dependent options driverChanged(); m_initializing = false; //show the settings of the current connection connectionChanged(ui.lwConnections->currentRow()); } void DatabaseManagerWidget::saveConnections() { QDEBUG("Saving connections to " + m_configPath); //delete saved connections KConfig config(m_configPath, KConfig::SimpleConfig); for (const auto& group : config.groupList()) config.deleteGroup(group); //save connections for (const auto& conn : m_connections) { KConfigGroup group = config.group(conn.name); group.writeEntry("Driver", conn.driver); group.writeEntry("DatabaseName", conn.dbName); if (!isFileDB(conn.driver)) { group.writeEntry("HostName", conn.hostName); group.writeEntry("Port", conn.port); group.writeEntry("UserName", conn.userName); group.writeEntry("Password", conn.password); } } config.sync(); } void DatabaseManagerWidget::testConnection() { int row = ui.lwConnections->currentRow(); //don't allow to test the connection for file DBs if the file doesn't exist if (isFileDB(ui.cbDriver->currentText())) { QString fileName = ui.leDatabase->text(); #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif if (!QFile::exists(fileName)) { KMessageBox::error(this, i18n("Failed to connect to the database '%1'.", m_connections[row].dbName), - i18n("Connection failed")); + i18n("Connection Failed")); return; } } QSqlDatabase db = QSqlDatabase::addDatabase(m_connections[row].driver); db.setDatabaseName(m_connections[row].dbName); if (!isFileDB(m_connections[row].driver)) { db.setHostName(m_connections[row].hostName); db.setPort(m_connections[row].port); db.setUserName(m_connections[row].userName); db.setPassword(m_connections[row].password); } if (db.isValid() && db.open() && db.isOpen()) { db.close(); KMessageBox::information(this, i18n("Connection to the database '%1' was successful.", m_connections[row].dbName), - i18n("Connection successful")); + i18n("Connection Successful")); } else { KMessageBox::error(this, i18n("Failed to connect to the database '%1'.", m_connections[row].dbName), - i18n("Connection failed")); + i18n("Connection Failed")); } } bool DatabaseManagerWidget::isFileDB(const QString& driver) { return (driver == QLatin1String("QSQLITE")) || (driver == QLatin1String("QSQLITE3")); } QString DatabaseManagerWidget::uniqueName() { QString name = i18n("New connection"); //TODO QStringList connection_names; for(int row = 0; row < ui.lwConnections->count(); row++) connection_names << ui.lwConnections->item(row)->text(); if (!connection_names.contains(name)) return name; QString base = name; int last_non_digit; for (last_non_digit = base.size()-1; last_non_digit>=0 && base[last_non_digit].category() == QChar::Number_DecimalDigit; --last_non_digit) base.chop(1); if (last_non_digit >=0 && base[last_non_digit].category() != QChar::Separator_Space) base.append(" "); int new_nr = name.rightRef(name.size() - base.size()).toInt(); QString new_name; do new_name = base + QString::number(++new_nr); while (connection_names.contains(new_name)); return new_name; } int DatabaseManagerWidget::defaultPort(const QString& driver) const { // QDB2 IBM DB2 (version 7.1 and above) // QIBASE Borland InterBase // QMYSQL MySQL // QOCI Oracle Call Interface Driver // QODBC Open Database Connectivity (ODBC) - Microsoft SQL Server and other ODBC-compliant databases // QPSQL PostgreSQL (versions 7.3 and above) if (driver == "QDB2") return 50000; else if (driver == "QIBASE") return 3050; else if (driver == "QMYSQL3" || driver == "QMYSQL") return 3306; else if (driver == "QOCI") return 1521; else if (driver == "QODBC") return 1433; else if (driver == "QPSQL") return 5432; else return 0; } diff --git a/src/kdefrontend/datasources/FileInfoDialog.cpp b/src/kdefrontend/datasources/FileInfoDialog.cpp index 46103efaa..c1b77d8cf 100644 --- a/src/kdefrontend/datasources/FileInfoDialog.cpp +++ b/src/kdefrontend/datasources/FileInfoDialog.cpp @@ -1,98 +1,99 @@ /*************************************************************************** File : FileInfoDialog.cpp Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- Copyright : (C) 2009-2017 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2016 Stefan-Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "FileInfoDialog.h" #include "backend/datasources/LiveDataSource.h" -#include +#include #include #include #include #include #include #include +#include /*! \class ImportWidget \brief Provides a dialog containing the information about the files to be imported. \ingroup kdefrontend */ FileInfoDialog::FileInfoDialog(QWidget* parent) : QDialog(parent) { m_textEditWidget.setReadOnly(true); m_textEditWidget.setLineWrapMode(QTextEdit::NoWrap); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(&m_textEditWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); connect(buttonBox, &QDialogButtonBox::rejected, this, &FileInfoDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &FileInfoDialog::accept); layout->addWidget(buttonBox); setWindowIcon(QIcon::fromTheme("help-about")); - setWindowTitle(i18n("File info")); + setWindowTitle(i18nc("@title:window", "File Information")); setAttribute(Qt::WA_DeleteOnClose); setLayout(layout); QTimer::singleShot(0, this, &FileInfoDialog::loadSettings); } void FileInfoDialog::loadSettings() { //restore saved settings KConfigGroup conf(KSharedConfig::openConfig(), "FileInfoDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); } FileInfoDialog::~FileInfoDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "FileInfoDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void FileInfoDialog::setFiles(QStringList& files) { QString infoString; for (const auto& fileName: files) { if(fileName.isEmpty()) continue; if (!infoString.isEmpty()) infoString += "


"; infoString += LiveDataSource::fileInfoString(fileName); } m_textEditWidget.document()->setHtml(infoString); } diff --git a/src/kdefrontend/datasources/ImportDialog.cpp b/src/kdefrontend/datasources/ImportDialog.cpp index 8607d5175..46bf53855 100644 --- a/src/kdefrontend/datasources/ImportDialog.cpp +++ b/src/kdefrontend/datasources/ImportDialog.cpp @@ -1,188 +1,188 @@ /*************************************************************************** File : ImportDialog.cc Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- Copyright : (C) 2008-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2015 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "ImportDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/core/Workbook.h" #include "backend/lib/macros.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/MainWin.h" #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include #include +#include /*! \class ImportDialog \brief Base class for other import dialogs. Provides the "Import to" section of those dialogs. \ingroup kdefrontend */ ImportDialog::ImportDialog(MainWin* parent) : QDialog(parent), vLayout(new QVBoxLayout(this)), okButton(nullptr), lPosition(nullptr), cbPosition(nullptr), cbAddTo(nullptr), m_mainWin(parent), frameAddTo(nullptr), tbNewDataContainer(nullptr), m_newDataContainerMenu(nullptr), m_aspectTreeModel(new AspectTreeModel(parent->project())) { //menu for new data container m_newDataContainerMenu = new QMenu(this); - m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-workbook-new"), i18n("new Workbook") ); - m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-spreadsheet-new"), i18n("new Spreadsheet") ); - m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-matrix-new"), i18n("new Matrix") ); + m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-workbook-new"), i18n("New Workbook") ); + m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-spreadsheet-new"), i18n("New Spreadsheet") ); + m_newDataContainerMenu->addAction( QIcon::fromTheme("labplot-matrix-new"), i18n("New Matrix") ); connect(m_newDataContainerMenu, SIGNAL(triggered(QAction*)), this, SLOT(newDataContainer(QAction*))); } ImportDialog::~ImportDialog() { if (m_aspectTreeModel) delete m_aspectTreeModel; //save the last used import position for file imports, no need to do this for live data source (cbPosition=0) if (cbPosition) { KConfigGroup conf(KSharedConfig::openConfig(), "ImportDialog"); conf.writeEntry("Position", cbPosition->currentIndex()); } } /*! creates widgets for the frame "Import-To" and sets the current model in the "Add to"-combobox. */ void ImportDialog::setModel() { //Frame for the "Import To"-Stuff frameAddTo = new QGroupBox(this); frameAddTo->setTitle(i18n("Import To")); QGridLayout *grid = new QGridLayout(frameAddTo); grid->addWidget(new QLabel(i18n("Data container"), frameAddTo), 0, 0); cbAddTo = new TreeViewComboBox(frameAddTo); cbAddTo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); grid->addWidget(cbAddTo, 0, 1); QList list; list << "Folder" << "Spreadsheet" << "Matrix" << "Workbook"; cbAddTo->setTopLevelClasses(list); list.clear(); list << "Spreadsheet" << "Matrix" << "Workbook"; m_aspectTreeModel->setSelectableAspects(list); cbAddTo->setModel(m_aspectTreeModel); tbNewDataContainer = new QToolButton(frameAddTo); tbNewDataContainer->setIcon(QIcon::fromTheme("list-add")); tbNewDataContainer->setToolTip(i18n("Add new data container")); grid->addWidget( tbNewDataContainer, 0, 2); lPosition = new QLabel(i18n("Position"), frameAddTo); lPosition->setEnabled(false); grid->addWidget(lPosition, 1, 0); cbPosition = new QComboBox(frameAddTo); cbPosition->setEnabled(false); cbPosition->addItem(i18n("Append")); cbPosition->addItem(i18n("Prepend")); cbPosition->addItem(i18n("Replace")); KConfigGroup conf(KSharedConfig::openConfig(), "ImportDialog"); cbPosition->setCurrentIndex(conf.readEntry("Position", 0)); cbPosition->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); grid->addWidget(cbPosition, 1, 1); //add the "Import to"-frame to the layout after the first main widget vLayout->insertWidget(1, frameAddTo); connect(tbNewDataContainer, SIGNAL(clicked(bool)), this, SLOT(newDataContainerMenu())); connect(cbAddTo, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkOkButton())); } void ImportDialog::setCurrentIndex(const QModelIndex& index) { DEBUG("ImportFileDialog::setCurrentIndex()"); QDEBUG(" index =" << index); cbAddTo->setCurrentModelIndex(index); QDEBUG("cbAddTo->currentModelIndex() =" << cbAddTo->currentModelIndex()); checkOkButton(); } void ImportDialog::newDataContainer(QAction* action) { DEBUG("ImportDialog::newDataContainer()"); QString name = selectedObject(); QString type = action->iconText().split(' ')[1]; if (name.isEmpty()) name = action->iconText(); bool ok; // child widgets can't have own icons QInputDialog* dlg = new QInputDialog(this); name = dlg->getText(this, i18n("Add %1", action->iconText()), i18n("%1 name:", type), QLineEdit::Normal, name, &ok); if (ok) { AbstractAspect* aspect; int actionIndex = m_newDataContainerMenu->actions().indexOf(action); if (actionIndex == 0) aspect = new Workbook(0, name); else if (actionIndex == 1) aspect = new Spreadsheet(0, name); else aspect = new Matrix(0, name); m_mainWin->addAspectToProject(aspect); QDEBUG("cbAddTo->setCurrentModelIndex() to " << m_mainWin->model()->modelIndexOfAspect(aspect)); cbAddTo->setCurrentModelIndex(m_mainWin->model()->modelIndexOfAspect(aspect)); checkOkButton(); //select "Replace" since this is the most common case when importing into a newly created container cbPosition->setCurrentIndex(2); } delete dlg; } void ImportDialog::newDataContainerMenu() { m_newDataContainerMenu->exec( tbNewDataContainer->mapToGlobal(tbNewDataContainer->rect().bottomLeft())); } diff --git a/src/kdefrontend/datasources/ImportFileDialog.cpp b/src/kdefrontend/datasources/ImportFileDialog.cpp index 51d675d3e..e1979234c 100644 --- a/src/kdefrontend/datasources/ImportFileDialog.cpp +++ b/src/kdefrontend/datasources/ImportFileDialog.cpp @@ -1,439 +1,440 @@ /*************************************************************************** File : ImportDialog.cc Project : LabPlot Description : import file data dialog -------------------------------------------------------------------- Copyright : (C) 2008-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2015 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "ImportFileDialog.h" #include "ImportFileWidget.h" #include "backend/core/AspectTreeModel.h" #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AbstractFileFilter.h" #include "backend/datasources/filters/HDF5Filter.h" #include "backend/datasources/filters/NetCDFFilter.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/core/Workbook.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/MainWin.h" #include -#include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class ImportFileDialog \brief Dialog for importing data from a file. Embeds \c ImportFileWidget and provides the standard buttons. \ingroup kdefrontend */ ImportFileDialog::ImportFileDialog(MainWin* parent, bool liveDataSource, const QString& fileName) : ImportDialog(parent), m_importFileWidget(new ImportFileWidget(this, fileName)), m_showOptions(false) { vLayout->addWidget(m_importFileWidget); //dialog buttons QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Reset |QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); m_optionsButton = buttonBox->button(QDialogButtonBox::Reset); //we highjack the default "Reset" button and use if for showing/hiding the options okButton->setEnabled(false); //ok is only available if a valid container was selected vLayout->addWidget(buttonBox); //hide the data-source related widgets if (!liveDataSource) { setModel(); //TODO: disable for file data sources m_importFileWidget->hideDataSource(); } else m_importFileWidget->initializeAndFillPortsAndBaudRates(); //Signals/Slots - connect(m_importFileWidget, SIGNAL(checkedFitsTableToMatrix(bool)), this, SLOT(checkOnFitsTableToMatrix(bool))); - connect(m_importFileWidget, SIGNAL(fileNameChanged()), this, SLOT(checkOkButton())); - connect(m_importFileWidget, SIGNAL(sourceTypeChanged()), this, SLOT(checkOkButton())); - connect(m_importFileWidget, SIGNAL(hostChanged()), this, SLOT(checkOkButton())); - connect(m_importFileWidget, SIGNAL(portChanged()), this, SLOT(checkOkButton())); - connect(m_optionsButton, SIGNAL(clicked()), this, SLOT(toggleOptions())); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); if (!liveDataSource) { - setWindowTitle(i18n("Import Data to Spreadsheet or Matrix")); + setWindowTitle(i18nc("@title:window", "Import Data to Spreadsheet or Matrix")); m_importFileWidget->hideDataSource(); } else - setWindowTitle(i18n("Add new live data source")); + setWindowTitle(i18nc("@title:window", "Add New Live Data Source")); setWindowIcon(QIcon::fromTheme("document-import-database")); QTimer::singleShot(0, this, &ImportFileDialog::loadSettings); } void ImportFileDialog::loadSettings() { //restore saved settings QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileDialog"); m_showOptions = conf.readEntry("ShowOptions", false); m_showOptions ? m_optionsButton->setText(i18n("Hide Options")) : m_optionsButton->setText(i18n("Show Options")); + m_importFileWidget->showOptions(m_showOptions); + m_importFileWidget->loadSettings(); + + //do the signal-slot connections after all settings where loaded in import file widget and check the OK button after this + connect(m_importFileWidget, SIGNAL(checkedFitsTableToMatrix(bool)), this, SLOT(checkOnFitsTableToMatrix(bool))); + connect(m_importFileWidget, SIGNAL(fileNameChanged()), this, SLOT(checkOkButton())); + connect(m_importFileWidget, SIGNAL(sourceTypeChanged()), this, SLOT(checkOkButton())); + connect(m_importFileWidget, SIGNAL(hostChanged()), this, SLOT(checkOkButton())); + connect(m_importFileWidget, SIGNAL(portChanged()), this, SLOT(checkOkButton())); + connect(m_optionsButton, SIGNAL(clicked()), this, SLOT(toggleOptions())); + + checkOkButton(); KWindowConfig::restoreWindowSize(windowHandle(), conf); } ImportFileDialog::~ImportFileDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileDialog"); conf.writeEntry("ShowOptions", m_showOptions); if (cbPosition) conf.writeEntry("Position", cbPosition->currentIndex()); KWindowConfig::saveWindowSize(windowHandle(), conf); } /*! triggers data import to the live data source \c source */ void ImportFileDialog::importToLiveDataSource(LiveDataSource* source, QStatusBar* statusBar) const { m_importFileWidget->saveSettings(source); //show a progress bar in the status bar QProgressBar* progressBar = new QProgressBar(); progressBar->setRange(0, 100); connect(source->filter(), SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QTime timer; timer.start(); source->read(); statusBar->showMessage( i18n("Live data source created in %1 seconds.", (float)timer.elapsed()/1000) ); RESET_CURSOR; statusBar->removeWidget(progressBar); source->ready(); } /*! triggers data import to the currently selected data container */ void ImportFileDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportFileDialog::importTo()"); QDEBUG(" cbAddTo->currentModelIndex() =" << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR in importTo(): No aspect available"); DEBUG(" cbAddTo->currentModelIndex().isValid() = " << cbAddTo->currentModelIndex().isValid()); DEBUG(" cbAddTo->currentModelIndex() row/column = " << cbAddTo->currentModelIndex().row() << ' ' << cbAddTo->currentModelIndex().column()); return; } if (m_importFileWidget->isFileEmpty()) { KMessageBox::information(0, i18n("No data to import."), i18n("No Data")); return; } QString fileName = m_importFileWidget->fileName(); AbstractFileFilter* filter = m_importFileWidget->currentFileFilter(); AbstractFileFilter::ImportMode mode = AbstractFileFilter::ImportMode(cbPosition->currentIndex()); //show a progress bar in the status bar QProgressBar* progressBar = new QProgressBar(); progressBar->setRange(0, 100); connect(filter, SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 100); QTime timer; timer.start(); if (aspect->inherits("Matrix")) { DEBUG(" to Matrix"); Matrix* matrix = qobject_cast(aspect); filter->readDataFromFile(fileName, matrix, mode); } else if (aspect->inherits("Spreadsheet")) { DEBUG(" to Spreadsheet"); Spreadsheet* spreadsheet = qobject_cast(aspect); DEBUG(" Calling readDataFromFile()"); filter->readDataFromFile(fileName, spreadsheet, mode); } else if (aspect->inherits("Workbook")) { DEBUG(" to Workbook"); Workbook* workbook = qobject_cast(aspect); QVector sheets = workbook->children(); QStringList names; LiveDataSource::FileType fileType = m_importFileWidget->currentFileType(); if (fileType == LiveDataSource::HDF5) names = m_importFileWidget->selectedHDF5Names(); else if (fileType == LiveDataSource::NETCDF) names = m_importFileWidget->selectedNetCDFNames(); //multiple extensions selected // multiple data sets/variables for HDF5/NetCDF if (fileType == LiveDataSource::HDF5 || fileType == LiveDataSource::NETCDF) { int nrNames = names.size(), offset = sheets.size(); int start=0; if (mode == AbstractFileFilter::Replace) start=offset; // add additional sheets for (int i = start; i < nrNames; ++i) { Spreadsheet *spreadsheet = new Spreadsheet(0, i18n("Spreadsheet")); if (mode == AbstractFileFilter::Prepend) workbook->insertChildBefore(spreadsheet,sheets[0]); else workbook->addChild(spreadsheet); } if (mode != AbstractFileFilter::Append) offset = 0; // import to sheets sheets = workbook->children(); for (int i = 0; i < nrNames; ++i) { if (fileType == LiveDataSource::HDF5) ((HDF5Filter*) filter)->setCurrentDataSetName(names[i]); else ((NetCDFFilter*) filter)->setCurrentVarName(names[i]); if (sheets[i+offset]->inherits("Matrix")) filter->readDataFromFile(fileName, qobject_cast(sheets[i+offset])); else if (sheets[i+offset]->inherits("Spreadsheet")) filter->readDataFromFile(fileName, qobject_cast(sheets[i+offset])); } } else { // single import file types // use active spreadsheet/matrix if present, else new spreadsheet Spreadsheet* spreadsheet = workbook->currentSpreadsheet(); Matrix* matrix = workbook->currentMatrix(); if (spreadsheet) filter->readDataFromFile(fileName, spreadsheet, mode); else if (matrix) filter->readDataFromFile(fileName, matrix, mode); else { spreadsheet = new Spreadsheet(0, i18n("Spreadsheet")); workbook->addChild(spreadsheet); filter->readDataFromFile(fileName, spreadsheet, mode); } } } statusBar->showMessage( i18n("File %1 imported in %2 seconds.", fileName, (float)timer.elapsed()/1000) ); RESET_CURSOR; statusBar->removeWidget(progressBar); delete filter; } void ImportFileDialog::toggleOptions() { m_importFileWidget->showOptions(!m_showOptions); m_showOptions = !m_showOptions; m_showOptions ? m_optionsButton->setText(i18n("Hide Options")) : m_optionsButton->setText(i18n("Show Options")); //resize the dialog layout()->activate(); resize( QSize(this->width(), 0).expandedTo(minimumSize()) ); } void ImportFileDialog::checkOnFitsTableToMatrix(const bool enable) { if (cbAddTo) { QDEBUG("cbAddTo->currentModelIndex() = " << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR: no aspect available."); return; } if(aspect->inherits("Matrix")) { okButton->setEnabled(enable); if (enable) okButton->setToolTip(i18n("Close the dialog and import the data.")); else okButton->setToolTip(i18n("Cannot import into a matrix since the data contains non-numerical data.")); } } } void ImportFileDialog::checkOkButton() { DEBUG("ImportFileDialog::checkOkButton()"); if (cbAddTo) { //only check for the target container when no file data source is being added QDEBUG(" cbAddTo->currentModelIndex() = " << cbAddTo->currentModelIndex()); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a data container where the data has to be imported into.")); lPosition->setEnabled(false); cbPosition->setEnabled(false); return; } else { lPosition->setEnabled(true); cbPosition->setEnabled(true); //when doing ASCII import to a matrix, hide the options for using the file header (first line) //to name the columns since the column names are fixed in a matrix const Matrix* matrix = dynamic_cast(aspect); m_importFileWidget->showAsciiHeaderOptions(matrix == NULL); } } QString fileName = m_importFileWidget->fileName(); #ifndef HAVE_WINDOWS if (!fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif switch (m_importFileWidget->currentSourceType()) { case LiveDataSource::SourceType::FileOrPipe: { DEBUG("fileName = " << fileName.toUtf8().constData()); const bool enable = QFile::exists(fileName); okButton->setEnabled(enable); if (enable) okButton->setToolTip(i18n("Close the dialog and import the data.")); else okButton->setToolTip(i18n("Provide an existing file.")); break; } case LiveDataSource::SourceType::LocalSocket: { + DEBUG(" Local Socket"); const bool enable = QFile::exists(fileName); if (enable) { - QLocalSocket* socket = new QLocalSocket(this); - socket->connectToServer(fileName, QLocalSocket::ReadOnly); - bool localSocketConnected = socket->waitForConnected(2000); - - okButton->setEnabled(localSocketConnected); - if (localSocketConnected) + QLocalSocket lsocket{this}; + DEBUG("CONNECT"); + lsocket.connectToServer(fileName, QLocalSocket::ReadOnly); + if (lsocket.waitForConnected()) { + + // this is required for server that send data as soon as connected + lsocket.waitForReadyRead(); + + DEBUG("DISCONNECT"); + lsocket.disconnectFromServer(); + // read-only socket is disconnected immediately (no waitForDisconnected()) + okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); - else - okButton->setToolTip(i18n("Couldn't connect to the provided local socket.")); - - if (socket->state() == QLocalSocket::ConnectedState) { - socket->disconnectFromServer(); - socket->waitForDisconnected(1000); - connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); - } else - delete socket; - + } else { + DEBUG("failed connect to local socket - " << lsocket.errorString().toStdString()); + okButton->setEnabled(false); + okButton->setToolTip(i18n("Could not connect to the provided local socket.")); + } } else { okButton->setEnabled(false); - okButton->setToolTip(i18n("Selected local socket doesn't exist.")); + okButton->setToolTip(i18n("Selected local socket does not exist.")); } break; } case LiveDataSource::SourceType::NetworkTcpSocket: { + DEBUG(" TCP Socket"); const bool enable = !m_importFileWidget->host().isEmpty() && !m_importFileWidget->port().isEmpty(); if (enable) { - QTcpSocket* socket = new QTcpSocket(this); - socket->connectToHost(m_importFileWidget->host(), m_importFileWidget->port().toUShort(), QTcpSocket::ReadOnly); - bool tcpSocketConnected = socket->waitForConnected(2000); - - okButton->setEnabled(tcpSocketConnected); - if (tcpSocketConnected) + QTcpSocket socket(this); + socket.connectToHost(m_importFileWidget->host(), m_importFileWidget->port().toUShort(), QTcpSocket::ReadOnly); + if (socket.waitForConnected()) { + okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); - else - okButton->setToolTip(i18n("Couldn't connect to the provided TCP socket.")); - - if (socket->state() == QTcpSocket::ConnectedState) { - socket->disconnectFromHost(); - socket->waitForDisconnected(1000); - connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); - } else - delete socket; + socket.disconnectFromHost(); + } else { + DEBUG("failed to connect to TCP socket - " << socket.errorString().toStdString()); + okButton->setEnabled(false); + okButton->setToolTip(i18n("Could not connect to the provided TCP socket.")); + } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Either the host name or the port number is missing.")); } break; } case LiveDataSource::SourceType::NetworkUdpSocket: { + DEBUG(" UDP Socket"); const bool enable = !m_importFileWidget->host().isEmpty() && !m_importFileWidget->port().isEmpty(); if (enable) { - QUdpSocket* socket = new QUdpSocket(this); - socket->connectToHost(m_importFileWidget->host(), m_importFileWidget->port().toUShort(), QUdpSocket::ReadOnly); - bool udpSocketConnected = socket->waitForConnected(2000); - - okButton->setEnabled(udpSocketConnected); - if (udpSocketConnected) + QUdpSocket socket(this); + socket.bind(QHostAddress(m_importFileWidget->host()), m_importFileWidget->port().toUShort()); + socket.connectToHost(m_importFileWidget->host(), 0, QUdpSocket::ReadOnly); + if (socket.waitForConnected()) { + okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); - else - okButton->setToolTip(i18n("Couldn't connect to the provided UDP socket.")); - - if (socket->state() == QUdpSocket::ConnectedState) { - socket->disconnectFromHost(); - socket->waitForDisconnected(1000); - connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); - } else - delete socket; + socket.disconnectFromHost(); + // read-only socket is disconnected immediately (no waitForDisconnected()) + } else { + DEBUG("failed to connect to UDP socket - " << socket.errorString().toStdString()); + okButton->setEnabled(false); + okButton->setToolTip(i18n("Could not connect to the provided UDP socket.")); + } } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Either the host name or the port number is missing.")); } break; } case LiveDataSource::SourceType::SerialPort: { const bool enable = !m_importFileWidget->serialPort().isEmpty(); if (enable) { QSerialPort* serialPort = new QSerialPort(this); serialPort->setBaudRate(m_importFileWidget->baudRate()); serialPort->setPortName(m_importFileWidget->serialPort()); bool serialPortOpened = serialPort->open(QIODevice::ReadOnly); okButton->setEnabled(serialPortOpened); if (serialPortOpened) okButton->setToolTip(i18n("Close the dialog and import the data.")); else - okButton->setToolTip(i18n("Couldn't connect to the provided UDP socket.")); + okButton->setToolTip(i18n("Could not connect to the provided serial port.")); } else { okButton->setEnabled(false); okButton->setToolTip(i18n("Serial port number is missing.")); } } } } QString ImportFileDialog::selectedObject() const { return m_importFileWidget->selectedObject(); } diff --git a/src/kdefrontend/datasources/ImportFileWidget.cpp b/src/kdefrontend/datasources/ImportFileWidget.cpp index 22f72324a..2e180afe2 100644 --- a/src/kdefrontend/datasources/ImportFileWidget.cpp +++ b/src/kdefrontend/datasources/ImportFileWidget.cpp @@ -1,1204 +1,1232 @@ /*************************************************************************** File : ImportFileWidget.cpp Project : LabPlot Description : import file data widget -------------------------------------------------------------------- Copyright : (C) 2009-2017 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 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 "ImportFileWidget.h" #include "FileInfoDialog.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/datasources/filters/HDF5Filter.h" #include "backend/datasources/filters/NetCDFFilter.h" #include "backend/datasources/filters/ImageFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/JsonFilter.h" #include "backend/datasources/filters/QJsonModel.h" #include "AsciiOptionsWidget.h" #include "BinaryOptionsWidget.h" #include "HDF5OptionsWidget.h" #include "ImageOptionsWidget.h" #include "NetCDFOptionsWidget.h" #include "FITSOptionsWidget.h" #include "JsonOptionsWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class ImportFileWidget \brief Widget for importing data from a file. \ingroup kdefrontend */ ImportFileWidget::ImportFileWidget(QWidget* parent, const QString& fileName) : QWidget(parent), m_fileName(fileName), m_fileEmpty(false), m_liveDataSource(true), m_suppressRefresh(false) { ui.setupUi(this); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leFileName->setCompleter(completer); ui.cbFileType->addItems(LiveDataSource::fileTypes()); QStringList filterItems; filterItems << i18n("Automatic") << i18n("Custom"); ui.cbFilter->addItems(filterItems); // file type specific option widgets QWidget* asciiw = new QWidget(); m_asciiOptionsWidget = std::unique_ptr(new AsciiOptionsWidget(asciiw)); ui.swOptions->insertWidget(LiveDataSource::Ascii, asciiw); QWidget* binaryw = new QWidget(); m_binaryOptionsWidget = std::unique_ptr(new BinaryOptionsWidget(binaryw)); ui.swOptions->insertWidget(LiveDataSource::Binary, binaryw); QWidget* imagew = new QWidget(); m_imageOptionsWidget = std::unique_ptr(new ImageOptionsWidget(imagew)); ui.swOptions->insertWidget(LiveDataSource::Image, imagew); QWidget* hdf5w = new QWidget(); m_hdf5OptionsWidget = std::unique_ptr(new HDF5OptionsWidget(hdf5w, this)); ui.swOptions->insertWidget(LiveDataSource::HDF5, hdf5w); QWidget* netcdfw = new QWidget(); m_netcdfOptionsWidget = std::unique_ptr(new NetCDFOptionsWidget(netcdfw, this)); ui.swOptions->insertWidget(LiveDataSource::NETCDF, netcdfw); QWidget* fitsw = new QWidget(); m_fitsOptionsWidget = std::unique_ptr(new FITSOptionsWidget(fitsw, this)); ui.swOptions->insertWidget(LiveDataSource::FITS, fitsw); QWidget* jsonw = new QWidget(); m_jsonOptionsWidget = std::unique_ptr(new JsonOptionsWidget(jsonw, this)); ui.swOptions->insertWidget(LiveDataSource::Json, jsonw); ui.tvJson->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui.tvJson->setAlternatingRowColors(true); ui.tvJson->setModel(m_jsonOptionsWidget->model()); showJsonModel(false); // the table widget for preview m_twPreview = new QTableWidget(ui.tePreview); m_twPreview->verticalHeader()->hide(); m_twPreview->setEditTriggers(QTableWidget::NoEditTriggers); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(m_twPreview); ui.tePreview->setLayout(layout); m_twPreview->hide(); // default filter ui.swOptions->setCurrentIndex(LiveDataSource::Ascii); #if !defined(HAVE_HDF5) || !defined(HAVE_NETCDF) || !defined(HAVE_FITS) const QStandardItemModel* model = qobject_cast(ui.cbFileType->model()); #endif #ifndef HAVE_HDF5 // disable HDF5 item QStandardItem* item = model->item(LiveDataSource::HDF5); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_NETCDF // disable NETCDF item QStandardItem* item2 = model->item(LiveDataSource::NETCDF); item2->setFlags(item2->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_FITS // disable FITS item QStandardItem* item3 = model->item(LiveDataSource::FITS); item3->setFlags(item3->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif ui.cbReadType->addItem(i18n("Whole file"), LiveDataSource::WholeFile); ui.lePort->setValidator( new QIntValidator(ui.lePort) ); ui.gbOptions->hide(); ui.gbUpdateOptions->hide(); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); ui.bFileInfo->setIcon( QIcon::fromTheme("help-about") ); ui.bManageFilters->setIcon( QIcon::fromTheme("configure") ); ui.bSaveFilter->setIcon( QIcon::fromTheme("document-save") ); ui.bRefreshPreview->setIcon( QIcon::fromTheme("view-refresh") ); connect( ui.leFileName, SIGNAL(textChanged(QString)), SLOT(fileNameChanged(QString)) ); connect( ui.bOpen, SIGNAL(clicked()), this, SLOT (selectFile()) ); connect( ui.bFileInfo, SIGNAL(clicked()), this, SLOT (fileInfoDialog()) ); connect( ui.bSaveFilter, SIGNAL(clicked()), this, SLOT (saveFilter()) ); connect( ui.bManageFilters, SIGNAL(clicked()), this, SLOT (manageFilters()) ); connect( ui.cbFileType, SIGNAL(currentIndexChanged(int)), SLOT(fileTypeChanged(int)) ); connect( ui.cbUpdateType, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypeChanged(int))); connect( ui.cbReadType, SIGNAL(currentIndexChanged(int)), this, SLOT(readingTypeChanged(int))); connect( ui.cbFilter, SIGNAL(activated(int)), SLOT(filterChanged(int)) ); connect( ui.bRefreshPreview, SIGNAL(clicked()), SLOT(refreshPreview()) ); connect(ui.leHost, SIGNAL(textChanged(QString)), this, SIGNAL(hostChanged())); connect(ui.lePort, SIGNAL(textChanged(QString)), this, SIGNAL(portChanged())); connect( ui.cbSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceTypeChanged(int))); connect( ui.tvJson, SIGNAL(clicked(const QModelIndex&)), this, SLOT(refreshPreview())); //TODO: implement save/load of user-defined settings later and activate these buttons again ui.bSaveFilter->hide(); ui.bManageFilters->hide(); //defer the loading of settings a bit in order to show the dialog prior to blocking the GUI in refreshPreview() QTimer::singleShot( 100, this, SLOT(loadSettings()) ); } void ImportFileWidget::loadSettings() { m_suppressRefresh = true; //load last used settings QString confName; if (m_liveDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); //settings for data type specific widgets m_asciiOptionsWidget->loadSettings(); m_binaryOptionsWidget->loadSettings(); m_imageOptionsWidget->loadSettings(); m_jsonOptionsWidget->loadSettings(); //read the source type first since settings in fileNameChanged() depend on this ui.cbSourceType->setCurrentIndex(conf.readEntry("SourceType").toInt()); //general settings ui.cbFileType->setCurrentIndex(conf.readEntry("Type", 0)); ui.cbFilter->setCurrentIndex(conf.readEntry("Filter", 0)); filterChanged(ui.cbFilter->currentIndex()); // needed if filter is not changed if (m_fileName.isEmpty()) ui.leFileName->setText(conf.readEntry("LastImportedFile", "")); else ui.leFileName->setText(m_fileName); //live data related settings ui.cbBaudRate->setCurrentIndex(conf.readEntry("BaudRate").toInt()); ui.cbReadType->setCurrentIndex(conf.readEntry("ReadType").toInt()); ui.cbSerialPort->setCurrentIndex(conf.readEntry("SerialPort").toInt()); ui.cbUpdateType->setCurrentIndex(conf.readEntry("UpdateType").toInt()); ui.leHost->setText(conf.readEntry("Host","")); ui.leKeepLastValues->setText(conf.readEntry("KeepLastNValues","")); ui.lePort->setText(conf.readEntry("Port","")); ui.sbSampleRate->setValue(conf.readEntry("SampleRate").toInt()); ui.sbUpdateInterval->setValue(conf.readEntry("UpdateInterval").toInt()); m_suppressRefresh = false; refreshPreview(); } ImportFileWidget::~ImportFileWidget() { // save current settings QString confName; if (m_liveDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); // general settings conf.writeEntry("Type", ui.cbFileType->currentIndex()); conf.writeEntry("Filter", ui.cbFilter->currentIndex()); conf.writeEntry("LastImportedFile", ui.leFileName->text()); //live data related settings conf.writeEntry("SourceType", ui.cbSourceType->currentIndex()); conf.writeEntry("UpdateType", ui.cbUpdateType->currentIndex()); conf.writeEntry("ReadType", ui.cbReadType->currentIndex()); conf.writeEntry("SampleRate", ui.sbSampleRate->value()); conf.writeEntry("KeepLastNValues", ui.leKeepLastValues->text()); conf.writeEntry("BaudRate", ui.cbBaudRate->currentIndex()); conf.writeEntry("SerialPort", ui.cbSerialPort->currentIndex()); conf.writeEntry("Host", ui.leHost->text()); conf.writeEntry("Port", ui.lePort->text()); conf.writeEntry("UpdateInterval", ui.sbUpdateInterval->value()); // data type specific settings m_asciiOptionsWidget->saveSettings(); m_binaryOptionsWidget->saveSettings(); m_imageOptionsWidget->saveSettings(); } void ImportFileWidget::hideDataSource() { m_liveDataSource = false; ui.gbUpdateOptions->hide(); ui.chbLinkFile->hide(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.lSourceType->hide(); ui.cbSourceType->hide(); ui.cbUpdateType->hide(); ui.lUpdateType->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateInterval->hide(); } void ImportFileWidget::showAsciiHeaderOptions(bool b) { m_asciiOptionsWidget->showAsciiHeaderOptions(b); } void ImportFileWidget::showJsonModel(bool b) { ui.tvJson->setVisible(b); ui.lField->setVisible(b); } void ImportFileWidget::showOptions(bool b) { ui.gbOptions->setVisible(b); if (m_liveDataSource) ui.gbUpdateOptions->setVisible(b); resize(layout()->minimumSize()); } QString ImportFileWidget::fileName() const { return ui.leFileName->text(); } QString ImportFileWidget::selectedObject() const { const QString& path = ui.leFileName->text(); //determine the file name only - QString name = path.right( path.length()-path.lastIndexOf(QDir::separator())-1 ); + QString name = path.right(path.length() - path.lastIndexOf(QDir::separator()) - 1); //strip away the extension if available if (name.indexOf('.') != -1) name = name.left(name.lastIndexOf('.')); //for multi-dimensinal formats like HDF, netCDF and FITS add the currently selected object const auto format = currentFileType(); if (format == LiveDataSource::HDF5) { const QStringList& hdf5Names = m_hdf5OptionsWidget->selectedHDF5Names(); if (hdf5Names.size()) name += hdf5Names.first(); //the names of the selected HDF5 objects already have '/' } else if (format == LiveDataSource::NETCDF) { const QStringList& names = m_netcdfOptionsWidget->selectedNetCDFNames(); if (names.size()) name += QLatin1Char('/') + names.first(); } else if (format == LiveDataSource::FITS) { const QString& extensionName = m_fitsOptionsWidget->currentExtensionName(); if (!extensionName.isEmpty()) name += QLatin1Char('/') + extensionName; } return name; } /*! * returns \c true if the number of lines to be imported from the currently selected file is zero ("file is empty"), * returns \c false otherwise. */ bool ImportFileWidget::isFileEmpty() const { return m_fileEmpty; } QString ImportFileWidget::host() const { return ui.leHost->text(); } QString ImportFileWidget::port() const { return ui.lePort->text(); } QString ImportFileWidget::serialPort() const { return ui.cbSerialPort->currentText(); } int ImportFileWidget::baudRate() const { return ui.cbBaudRate->currentText().toInt(); } /*! saves the settings to the data source \c source. */ void ImportFileWidget::saveSettings(LiveDataSource* source) const { LiveDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); LiveDataSource::UpdateType updateType = static_cast(ui.cbUpdateType->currentIndex()); LiveDataSource::SourceType sourceType = static_cast(ui.cbSourceType->currentIndex()); LiveDataSource::ReadingType readingType = static_cast(ui.cbReadType->currentIndex()); source->setComment( ui.leFileName->text() ); source->setFileType(fileType); source->setFilter(this->currentFileFilter()); source->setSourceType(sourceType); source->setReadingType(readingType); if (updateType == LiveDataSource::UpdateType::TimeInterval) source->setUpdateInterval(ui.sbUpdateInterval->value()); else source->setFileWatched(true); if (!ui.leKeepLastValues->text().isEmpty()) { source->setKeepLastValues(true); source->setKeepNvalues(ui.leKeepLastValues->text().toInt()); } source->setUpdateType(updateType); if (readingType != LiveDataSource::ReadingType::TillEnd) source->setSampleRate(ui.sbSampleRate->value()); switch (sourceType) { case LiveDataSource::SourceType::FileOrPipe: source->setFileName( ui.leFileName->text() ); source->setFileLinked( ui.chbLinkFile->isChecked() ); break; case LiveDataSource::SourceType::LocalSocket: source->setLocalSocketName(ui.leFileName->text()); break; case LiveDataSource::SourceType::NetworkTcpSocket: case LiveDataSource::SourceType::NetworkUdpSocket: source->setHost(ui.leHost->text()); source->setPort((quint16)ui.lePort->text().toInt()); break; case LiveDataSource::SourceType::SerialPort: source->setBaudRate(ui.cbBaudRate->currentText().toInt()); source->setSerialPort(ui.cbSerialPort->currentText()); break; default: break; } } /*! returns the currently used file type. */ LiveDataSource::FileType ImportFileWidget::currentFileType() const { return static_cast(ui.cbFileType->currentIndex()); } LiveDataSource::SourceType ImportFileWidget::currentSourceType() const { return static_cast(ui.cbSourceType->currentIndex()); } /*! returns the currently used filter. */ AbstractFileFilter* ImportFileWidget::currentFileFilter() const { DEBUG("currentFileFilter()"); LiveDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); switch (fileType) { case LiveDataSource::Ascii: { //TODO std::unique_ptr filter(new AsciiFilter()); AsciiFilter* filter = new AsciiFilter(); if (ui.cbFilter->currentIndex() == 0) //"automatic" filter->setAutoModeEnabled(true); else if (ui.cbFilter->currentIndex() == 1) { //"custom" filter->setAutoModeEnabled(false); m_asciiOptionsWidget->applyFilterSettings(filter); } else filter->loadFilterSettings( ui.cbFilter->currentText() ); //save the data portion to import filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } case LiveDataSource::Binary: { BinaryFilter* filter = new BinaryFilter(); if ( ui.cbFilter->currentIndex() == 0 ) //"automatic" filter->setAutoModeEnabled(true); else if ( ui.cbFilter->currentIndex() == 1 ) { //"custom" filter->setAutoModeEnabled(false); m_binaryOptionsWidget->applyFilterSettings(filter); } else { //TODO: load filter settings // filter->setFilterName( ui.cbFilter->currentText() ); } filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); return filter; } case LiveDataSource::Image: { ImageFilter* filter = new ImageFilter(); filter->setImportFormat(m_imageOptionsWidget->currentFormat()); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::HDF5: { HDF5Filter* filter = new HDF5Filter(); QStringList names = selectedHDF5Names(); if (!names.isEmpty()) filter->setCurrentDataSetName(names[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::NETCDF: { NetCDFFilter* filter = new NetCDFFilter(); if (!selectedNetCDFNames().isEmpty()) filter->setCurrentVarName(selectedNetCDFNames()[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::FITS: { FITSFilter* filter = new FITSFilter(); filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } case LiveDataSource::Json: { JsonFilter* filter = new JsonFilter(); m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex()); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } } return 0; } /*! opens a file dialog and lets the user select the file data source. */ void ImportFileWidget::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileWidget"); QString dir = conf.readEntry("LastDir", ""); QString path = QFileDialog::getOpenFileName(this, i18n("Select the File Data Source"), dir); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } ui.leFileName->setText(path); //TODO: decide whether the selection of several files should be possible // QStringList filelist = QFileDialog::getOpenFileNames(this,i18n("Select one or more files to open")); // if (! filelist.isEmpty() ) // ui.leFileName->setText(filelist.join(";")); } /************** SLOTS **************************************************************/ /*! called on file name changes. Determines the file format (ASCII, binary etc.), if the file exists, and activates the corresponding options. */ void ImportFileWidget::fileNameChanged(const QString& name) { QString fileName = name; #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif bool fileExists = QFile::exists(fileName); if (fileExists) ui.leFileName->setStyleSheet(""); else ui.leFileName->setStyleSheet("QLineEdit{background:red;}"); ui.gbOptions->setEnabled(fileExists); ui.bManageFilters->setEnabled(fileExists); ui.cbFilter->setEnabled(fileExists); ui.cbFileType->setEnabled(fileExists); ui.bFileInfo->setEnabled(fileExists); ui.gbUpdateOptions->setEnabled(fileExists); if (!fileExists) { //file doesn't exist -> delete the content preview that is still potentially //available from the previously selected file ui.tePreview->clear(); m_twPreview->clear(); m_hdf5OptionsWidget->clear(); m_netcdfOptionsWidget->clear(); m_fitsOptionsWidget->clear(); m_jsonOptionsWidget->clearModel(); emit fileNameChanged(); return; } if (currentSourceType() == LiveDataSource::FileOrPipe) { QString fileInfo; #ifndef HAVE_WINDOWS //check, if we can guess the file type by content QProcess* proc = new QProcess(this); QStringList args; args << "-b" << ui.leFileName->text(); proc->start("file", args); if (proc->waitForReadyRead(1000) == false) { QDEBUG("ERROR: reading file type of file" << fileName); return; } fileInfo = proc->readLine(); #endif QByteArray imageFormat = QImageReader::imageFormat(fileName); if (fileInfo.contains(QLatin1String("JSON")) || fileName.endsWith(QLatin1String("json"), Qt::CaseInsensitive)) { //*.json files can be recognized as ASCII. so, do the check for the json-extension as first. ui.cbFileType->setCurrentIndex(LiveDataSource::Json); m_jsonOptionsWidget->loadDocument(fileName); // update Json tree widget using current selected file } else if (fileInfo.contains(QLatin1String("compressed data")) || fileInfo.contains(QLatin1String("ASCII")) || fileName.endsWith(QLatin1String("dat"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("txt"), Qt::CaseInsensitive)) { //probably ascii data ui.cbFileType->setCurrentIndex(LiveDataSource::Ascii); } else if (fileInfo.contains(QLatin1String("Hierarchical Data Format")) || fileName.endsWith(QLatin1String("h5"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf5"), Qt::CaseInsensitive) ) { ui.cbFileType->setCurrentIndex(LiveDataSource::HDF5); // update HDF5 tree widget using current selected file m_hdf5OptionsWidget->updateContent((HDF5Filter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("NetCDF Data Format")) || fileName.endsWith(QLatin1String("nc"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("netcdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("cdf"), Qt::CaseInsensitive)) { ui.cbFileType->setCurrentIndex(LiveDataSource::NETCDF); // update NetCDF tree widget using current selected file m_netcdfOptionsWidget->updateContent((NetCDFFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("FITS image data")) || fileName.endsWith(QLatin1String("fits"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fit"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fts"), Qt::CaseInsensitive)) { #ifdef HAVE_FITS ui.cbFileType->setCurrentIndex(LiveDataSource::FITS); #endif // update FITS tree widget using current selected file m_fitsOptionsWidget->updateContent((FITSFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains("image") || fileInfo.contains("bitmap") || !imageFormat.isEmpty()) { ui.cbFileType->setCurrentIndex(LiveDataSource::Image); } else ui.cbFileType->setCurrentIndex(LiveDataSource::Binary); } refreshPreview(); emit fileNameChanged(); } /*! saves the current filter settings */ void ImportFileWidget::saveFilter() { bool ok; QString text = QInputDialog::getText(this, i18n("Save Filter Settings as"), i18n("Filter name:"), QLineEdit::Normal, i18n("new filter"), &ok); if (ok && !text.isEmpty()) { //TODO //AsciiFilter::saveFilter() } } /*! opens a dialog for managing all available predefined filters. */ void ImportFileWidget::manageFilters() { //TODO } /*! Depending on the selected file type, activates the corresponding options in the data portion tab and populates the combobox with the available pre-defined fllter settings for the selected type. */ void ImportFileWidget::fileTypeChanged(int fileType) { ui.swOptions->setCurrentIndex(fileType); //default ui.lFilter->show(); ui.cbFilter->show(); //if we switch from netCDF-format (only two tabs available), add the data preview-tab again if (ui.tabWidget->count() == 2) { ui.tabWidget->setTabText(0, i18n("Data format")); ui.tabWidget->insertTab(1, ui.tabDataPreview, i18n("Preview")); } ui.lPreviewLines->show(); ui.sbPreviewLines->show(); ui.lStartColumn->show(); ui.sbStartColumn->show(); ui.lEndColumn->show(); ui.sbEndColumn->show(); showJsonModel(false); switch (fileType) { case LiveDataSource::Ascii: break; case LiveDataSource::Binary: ui.lStartColumn->hide(); ui.sbStartColumn->hide(); ui.lEndColumn->hide(); ui.sbEndColumn->hide(); break; case LiveDataSource::HDF5: case LiveDataSource::NETCDF: ui.lFilter->hide(); ui.cbFilter->hide(); // hide global preview tab. we have our own ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; case LiveDataSource::Image: ui.lPreviewLines->hide(); ui.sbPreviewLines->hide(); ui.lFilter->hide(); ui.cbFilter->hide(); break; case LiveDataSource::FITS: ui.lFilter->hide(); ui.cbFilter->hide(); ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; case LiveDataSource::Json: ui.lFilter->hide(); ui.cbFilter->hide(); showJsonModel(true); break; default: DEBUG("unknown file type"); } m_hdf5OptionsWidget->clear(); m_netcdfOptionsWidget->clear(); int lastUsedFilterIndex = ui.cbFilter->currentIndex(); ui.cbFilter->clear(); ui.cbFilter->addItem( i18n("Automatic") ); ui.cbFilter->addItem( i18n("Custom") ); //TODO: populate the combobox with the available pre-defined filter settings for the selected type ui.cbFilter->setCurrentIndex(lastUsedFilterIndex); filterChanged(lastUsedFilterIndex); refreshPreview(); } const QStringList ImportFileWidget::selectedHDF5Names() const { return m_hdf5OptionsWidget->selectedHDF5Names(); } const QStringList ImportFileWidget::selectedNetCDFNames() const { return m_netcdfOptionsWidget->selectedNetCDFNames(); } const QStringList ImportFileWidget::selectedFITSExtensions() const { return m_fitsOptionsWidget->selectedFITSExtensions(); } /*! shows the dialog with the information about the file(s) to be imported. */ void ImportFileWidget::fileInfoDialog() { QStringList files = ui.leFileName->text().split(';'); FileInfoDialog* dlg = new FileInfoDialog(this); dlg->setFiles(files); dlg->exec(); } /*! enables the options if the filter "custom" was chosen. Disables the options otherwise. */ void ImportFileWidget::filterChanged(int index) { // ignore filter for these formats if (ui.cbFileType->currentIndex() == LiveDataSource::HDF5 || ui.cbFileType->currentIndex() == LiveDataSource::NETCDF || ui.cbFileType->currentIndex() == LiveDataSource::Image || ui.cbFileType->currentIndex() == LiveDataSource::FITS || ui.cbFileType->currentIndex() == LiveDataSource::Json) { ui.swOptions->setEnabled(true); return; } if (index == 0) { // "automatic" ui.swOptions->setEnabled(false); ui.bSaveFilter->setEnabled(false); } else if (index == 1) { //custom ui.swOptions->setEnabled(true); ui.bSaveFilter->setEnabled(true); } else { // predefined filter settings were selected. //load and show them in the GUI. //TODO } } void ImportFileWidget::refreshPreview() { if (m_suppressRefresh) return; WAIT_CURSOR; QString fileName = ui.leFileName->text(); #ifndef HAVE_WINDOWS if (!fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif DEBUG("refreshPreview() file name = " << fileName.toStdString()); QVector importedStrings; LiveDataSource::FileType fileType = (LiveDataSource::FileType)ui.cbFileType->currentIndex(); // generic table widget if (fileType == LiveDataSource::Ascii || fileType == LiveDataSource::Binary || fileType == LiveDataSource::Json) m_twPreview->show(); else m_twPreview->hide(); int lines = ui.sbPreviewLines->value(); bool ok = true; QTableWidget* tmpTableWidget{nullptr}; QStringList vectorNameList; QVector columnModes; switch (fileType) { case LiveDataSource::Ascii: { + DEBUG("ASCII"); ui.tePreview->clear(); AsciiFilter* filter = static_cast(this->currentFileFilter()); switch (currentSourceType()) { case LiveDataSource::SourceType::FileOrPipe: { + DEBUG(" FileOrPipe"); importedStrings = filter->preview(fileName, lines); break; } case LiveDataSource::SourceType::LocalSocket: { QLocalSocket lsocket{this}; + DEBUG("Local socket: CONNECT PREVIEW"); lsocket.connectToServer(fileName, QLocalSocket::ReadOnly); - if (lsocket.waitForConnected(20000)) { - QDEBUG("connected to local socket " << fileName); - if ( lsocket.waitForReadyRead(500) ) + if (lsocket.waitForConnected()) { + DEBUG("connected to local socket " << fileName.toStdString()); + if (lsocket.waitForReadyRead()) importedStrings = filter->preview(lsocket); - } else - QDEBUG("failed to connect to local socket " << fileName << " - " << lsocket.errorString()); + DEBUG("Local socket: DISCONNECT PREVIEW"); + lsocket.disconnectFromServer(); + // read-only socket is disconnected immediately (no waitForDisconnected()) + } else { + DEBUG("failed connect to local socket " << fileName.toStdString() << " - " << lsocket.errorString().toStdString()); + } - //TODO: lsocket.disconnectFromServer(); break; } case LiveDataSource::SourceType::NetworkTcpSocket: { + DEBUG(" TCPSocket"); QTcpSocket tcpSocket{this}; tcpSocket.connectToHost(host(), port().toInt(), QTcpSocket::ReadOnly); - if (tcpSocket.waitForConnected(2000)) { + if (tcpSocket.waitForConnected()) { DEBUG("connected to TCP socket"); - if ( tcpSocket.waitForReadyRead(500) ) + if ( tcpSocket.waitForReadyRead() ) importedStrings = filter->preview(tcpSocket); tcpSocket.disconnectFromHost(); - } else - QDEBUG("failed to connect to TCP socket " << " - " << tcpSocket.errorString()); + } else { + DEBUG("failed to connect to TCP socket " << " - " << tcpSocket.errorString().toStdString()); + } break; } case LiveDataSource::SourceType::NetworkUdpSocket: { QUdpSocket udpSocket{this}; - udpSocket.connectToHost(host(), port().toInt(), QUdpSocket::ReadOnly); - if (udpSocket.waitForConnected(2000)) { - DEBUG("connected to UDP socket"); - if ( udpSocket.waitForReadyRead(500) ) - importedStrings = filter->preview(udpSocket); - + DEBUG("UDP Socket: CONNECT PREVIEW, state = " << udpSocket.state()); + udpSocket.bind(QHostAddress(host()), port().toInt()); + udpSocket.connectToHost(host(), 0, QUdpSocket::ReadOnly); + if (udpSocket.waitForConnected()) { + DEBUG(" connected to UDP socket " << host().toStdString() << ':' << port().toInt()); + if (!udpSocket.waitForReadyRead(2000) ) + DEBUG(" ERROR: not ready for read after 2 sec"); + if (udpSocket.hasPendingDatagrams()) { + DEBUG(" has pending data"); + } else { + DEBUG(" has no pending data"); + } + importedStrings = filter->preview(udpSocket); + + DEBUG("UDP Socket: DISCONNECT PREVIEW, state = " << udpSocket.state()); udpSocket.disconnectFromHost(); - connect(&udpSocket, SIGNAL(disconnected()), &udpSocket, SLOT(deleteLater())); - } else - QDEBUG("failed to connect to UDP socket " << " - " << udpSocket.errorString()); + } else { + DEBUG("failed to connect to UDP socket " << " - " << udpSocket.errorString().toStdString()); + } break; } case LiveDataSource::SourceType::SerialPort: { + DEBUG(" SerialPort"); QSerialPort sPort{this}; sPort.setBaudRate(baudRate()); sPort.setPortName(serialPort()); if (sPort.open(QIODevice::ReadOnly)) { bool canread = sPort.waitForReadyRead(500); if (canread) importedStrings = filter->preview(sPort); sPort.close(); } break; } } tmpTableWidget = m_twPreview; vectorNameList = filter->vectorNames(); columnModes = filter->columnModes(); break; } case LiveDataSource::Binary: { + DEBUG("Binary"); ui.tePreview->clear(); BinaryFilter *filter = (BinaryFilter *)this->currentFileFilter(); importedStrings = filter->preview(fileName, lines); tmpTableWidget = m_twPreview; break; } case LiveDataSource::Image: { + DEBUG("Image"); ui.tePreview->clear(); QImage image(fileName); QTextCursor cursor = ui.tePreview->textCursor(); cursor.insertImage(image); RESET_CURSOR; return; } case LiveDataSource::HDF5: { DEBUG(" HDF5"); HDF5Filter *filter = (HDF5Filter *)this->currentFileFilter(); lines = m_hdf5OptionsWidget->lines(); importedStrings = filter->readCurrentDataSet(fileName, NULL, ok, AbstractFileFilter::Replace, lines); tmpTableWidget = m_hdf5OptionsWidget->previewWidget(); break; } case LiveDataSource::NETCDF: { DEBUG(" NETCDF"); NetCDFFilter *filter = (NetCDFFilter *)this->currentFileFilter(); lines = m_netcdfOptionsWidget->lines(); importedStrings = filter->readCurrentVar(fileName, NULL, AbstractFileFilter::Replace, lines); tmpTableWidget = m_netcdfOptionsWidget->previewWidget(); break; } case LiveDataSource::FITS: { DEBUG(" FITS"); FITSFilter* filter = (FITSFilter*)this->currentFileFilter(); lines = m_fitsOptionsWidget->lines(); // update file name (may be any file type) m_fitsOptionsWidget->updateContent(filter, fileName); QString extensionName = m_fitsOptionsWidget->extensionName(&ok); if (!extensionName.isEmpty()) { DEBUG(" extension name = " << extensionName.toStdString()); fileName = extensionName; } DEBUG(" file name = " << fileName.toStdString()); bool readFitsTableToMatrix; importedStrings = filter->readChdu(fileName, &readFitsTableToMatrix, lines); emit checkedFitsTableToMatrix(readFitsTableToMatrix); tmpTableWidget = m_fitsOptionsWidget->previewWidget(); break; } case LiveDataSource::Json: { ui.tePreview->clear(); m_jsonOptionsWidget->loadDocument(fileName); JsonFilter *filter = (JsonFilter*)this->currentFileFilter(); m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex()); importedStrings = filter->preview(fileName); tmpTableWidget = m_twPreview; columnModes = filter->columnModes(); break; } } // fill the table widget tmpTableWidget->setRowCount(0); tmpTableWidget->setColumnCount(0); if( !importedStrings.isEmpty() ) { //QDEBUG("importedStrings =" << importedStrings); if (!ok) { // show imported strings as error message tmpTableWidget->setRowCount(1); tmpTableWidget->setColumnCount(1); QTableWidgetItem* item = new QTableWidgetItem(); item->setText(importedStrings[0][0]); tmpTableWidget->setItem(0, 0, item); } else { //TODO: maxrows not used const int rows = qMax(importedStrings.size(), 1); const int maxColumns = 300; tmpTableWidget->setRowCount(rows); for (int i = 0; i < rows; ++i) { QDEBUG(importedStrings[i]); int cols = importedStrings[i].size() > maxColumns ? maxColumns : importedStrings[i].size(); // new if (cols > tmpTableWidget->columnCount()) tmpTableWidget->setColumnCount(cols); for (int j = 0; j < cols; ++j) { QTableWidgetItem* item = new QTableWidgetItem(importedStrings[i][j]); tmpTableWidget->setItem(i, j, item); } } // set header if columnMode available for (int i = 0; i < qMin(tmpTableWidget->columnCount(), columnModes.size()); ++i) { QString columnName = QString::number(i+1); if (i < vectorNameList.size()) columnName = vectorNameList[i]; auto* item = new QTableWidgetItem(columnName + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes[i]) + QLatin1String("}")); item->setTextAlignment(Qt::AlignLeft); item->setIcon(AbstractColumn::iconForMode(columnModes[i])); tmpTableWidget->setHorizontalHeaderItem(i, item); } } tmpTableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); m_fileEmpty = false; } else { m_fileEmpty = true; } RESET_CURSOR; } void ImportFileWidget::updateTypeChanged(int idx) { LiveDataSource::UpdateType type = static_cast(idx); if (type == LiveDataSource::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); ui.lUpdateIntervalUnit->show(); } else if (type == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateIntervalUnit->hide(); } } void ImportFileWidget::readingTypeChanged(int idx) { LiveDataSource::ReadingType type = static_cast(idx); if (type == LiveDataSource::ReadingType::TillEnd || type == LiveDataSource::ReadingType::WholeFile) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else { ui.lSampleRate->show(); ui.sbSampleRate->show(); } + + if (type == LiveDataSource::ReadingType::WholeFile) { + ui.lKeepLastValues->hide(); + ui.leKeepLastValues->hide(); + } else { + ui.lKeepLastValues->show(); + ui.leKeepLastValues->show(); + } } void ImportFileWidget::sourceTypeChanged(int idx) { LiveDataSource::SourceType type = static_cast(idx); switch (type) { case LiveDataSource::SourceType::FileOrPipe: { ui.lFileName->show(); ui.leFileName->show(); ui.bFileInfo->show(); ui.bOpen->show(); ui.chbLinkFile->show(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); fileNameChanged(ui.leFileName->text()); int itemIdx = -1; for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) { itemIdx = i; break; } } if (itemIdx == -1) ui.cbReadType->addItem(i18n("Whole file"), LiveDataSource::WholeFile); break; } case LiveDataSource::SourceType::LocalSocket: ui.lFileName->show(); ui.leFileName->show(); ui.bOpen->show(); ui.bFileInfo->hide(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.chbLinkFile->hide(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); ui.cbFileType->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; case LiveDataSource::SourceType::NetworkTcpSocket: case LiveDataSource::SourceType::NetworkUdpSocket: ui.lHost->show(); ui.leHost->show(); ui.lePort->show(); ui.lPort->show(); ui.lBaudRate->hide(); ui.cbBaudRate->hide(); ui.lSerialPort->hide(); ui.cbSerialPort->hide(); ui.lFileName->hide(); ui.leFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); ui.chbLinkFile->hide(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); ui.cbFileType->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; case LiveDataSource::SourceType::SerialPort: ui.lBaudRate->show(); ui.cbBaudRate->show(); ui.lSerialPort->show(); ui.cbSerialPort->show(); ui.lHost->hide(); ui.leHost->hide(); ui.lePort->hide(); ui.lPort->hide(); ui.lFileName->hide(); ui.leFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); ui.chbLinkFile->hide(); ui.cbFileType->setEnabled(true); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; default: break; } //"update options" groupbox can be deactived for "file and pipe" if the file is invalid. //Activate the groupbox when switching from "file and pipe" to a different sourcy type. if (type != LiveDataSource::SourceType::FileOrPipe) ui.gbUpdateOptions->setEnabled(true); emit sourceTypeChanged(); refreshPreview(); } void ImportFileWidget::initializeAndFillPortsAndBaudRates() { for (int i = 2; i < ui.swOptions->count(); ++i) ui.swOptions->removeWidget(ui.swOptions->widget(i)); const int size = ui.cbFileType->count(); for (int i = 2; i < size; ++i) ui.cbFileType->removeItem(2); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.cbBaudRate->addItems(LiveDataSource::supportedBaudRates()); ui.cbSerialPort->addItems(LiveDataSource::availablePorts()); ui.leKeepLastValues->setValidator(new QIntValidator(2, 100000)); ui.tabWidget->removeTab(2); } diff --git a/src/kdefrontend/datasources/ImportFileWidget.h b/src/kdefrontend/datasources/ImportFileWidget.h index 4c92f1342..5d4572639 100644 --- a/src/kdefrontend/datasources/ImportFileWidget.h +++ b/src/kdefrontend/datasources/ImportFileWidget.h @@ -1,122 +1,122 @@ /*************************************************************************** File : ImportFileWidget.h Project : LabPlot Description : import file data widget -------------------------------------------------------------------- Copyright : (C) 2009-2017 by Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Copyright : (C) 2009-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 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 * * * ***************************************************************************/ #ifndef IMPORTFILEWIDGET_H #define IMPORTFILEWIDGET_H #include "ui_importfilewidget.h" #include "backend/datasources/LiveDataSource.h" #include class AbstractFileFilter; class AsciiOptionsWidget; class BinaryOptionsWidget; class HDF5OptionsWidget; class ImageOptionsWidget; class NetCDFOptionsWidget; class FITSOptionsWidget; class JsonOptionsWidget; class QTableWidget; class ImportFileWidget : public QWidget { Q_OBJECT public: explicit ImportFileWidget(QWidget*, const QString& fileName = QString()); ~ImportFileWidget(); void showOptions(bool); void saveSettings(LiveDataSource*) const; LiveDataSource::FileType currentFileType() const; LiveDataSource::SourceType currentSourceType() const; AbstractFileFilter* currentFileFilter() const; QString fileName() const; QString selectedObject() const; bool isFileEmpty() const; const QStringList selectedHDF5Names() const; const QStringList selectedNetCDFNames() const; const QStringList selectedFITSExtensions() const; void hideDataSource(); void showAsciiHeaderOptions(bool); void showJsonModel(bool); QString host() const; QString port() const; QString serialPort() const; int baudRate() const; void initializeAndFillPortsAndBaudRates(); private: Ui::ImportFileWidget ui; std::unique_ptr m_asciiOptionsWidget; std::unique_ptr m_binaryOptionsWidget; std::unique_ptr m_hdf5OptionsWidget; std::unique_ptr m_imageOptionsWidget; std::unique_ptr m_netcdfOptionsWidget; std::unique_ptr m_fitsOptionsWidget; std::unique_ptr m_jsonOptionsWidget; QTableWidget* m_twPreview; const QString& m_fileName; bool m_fileEmpty; bool m_liveDataSource; bool m_suppressRefresh; +public slots: + void loadSettings(); private slots: void fileNameChanged(const QString&); void fileTypeChanged(int); void updateTypeChanged(int); void sourceTypeChanged(int); void readingTypeChanged(int); void saveFilter(); void manageFilters(); void filterChanged(int); void selectFile(); void fileInfoDialog(); void refreshPreview(); - void loadSettings(); signals: void fileNameChanged(); void sourceTypeChanged(); void hostChanged(); void portChanged(); - void checkedFitsTableToMatrix(const bool enable); friend class HDF5OptionsWidget; // to access refreshPreview() friend class NetCDFOptionsWidget; // to access refreshPreview() and others friend class FITSOptionsWidget; friend class JsonOptionsWidget; }; #endif diff --git a/src/kdefrontend/datasources/ImportProjectDialog.cpp b/src/kdefrontend/datasources/ImportProjectDialog.cpp index c4fff439c..81cb19b1a 100644 --- a/src/kdefrontend/datasources/ImportProjectDialog.cpp +++ b/src/kdefrontend/datasources/ImportProjectDialog.cpp @@ -1,412 +1,413 @@ /*************************************************************************** File : ImportProjectDialog.cpp Project : LabPlot Description : import project dialog -------------------------------------------------------------------- Copyright : (C) 2017 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 "ImportProjectDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/datasources/projects/LabPlotProjectParser.h" #include "backend/datasources/projects/OriginProjectParser.h" #include "kdefrontend/MainWin.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include #include #include #include -#include +#include #include +#include #include /*! \class ImportProjectDialog \brief Dialog for importing project files. \ingroup kdefrontend */ ImportProjectDialog::ImportProjectDialog(MainWin* parent, ProjectType type) : QDialog(parent), m_mainWin(parent), m_projectParser(nullptr), m_projectType(type), m_aspectTreeModel(new AspectTreeModel(parent->project())) { QVBoxLayout* vLayout = new QVBoxLayout(this); //main widget QWidget* mainWidget = new QWidget(this); ui.setupUi(mainWidget); ui.chbUnusedObjects->hide(); vLayout->addWidget(mainWidget); ui.tvPreview->setAnimated(true); ui.tvPreview->setAlternatingRowColors(true); ui.tvPreview->setSelectionBehavior(QAbstractItemView::SelectRows); ui.tvPreview->setSelectionMode(QAbstractItemView::ExtendedSelection); ui.tvPreview->setUniformRowHeights(true); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); m_cbAddTo = new TreeViewComboBox(ui.gbImportTo); m_cbAddTo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); ui.gbImportTo->layout()->addWidget(m_cbAddTo); QList list; list << "Folder"; m_cbAddTo->setTopLevelClasses(list); m_aspectTreeModel->setSelectableAspects(list); m_cbAddTo->setModel(m_aspectTreeModel); m_bNewFolder = new QPushButton(ui.gbImportTo); m_bNewFolder->setIcon(QIcon::fromTheme("list-add")); m_bNewFolder->setToolTip(i18n("Add new folder")); ui.gbImportTo->layout()->addWidget(m_bNewFolder); //dialog buttons m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); vLayout->addWidget(m_buttonBox); //ok-button is only enabled if some project objects were selected (s.a. ImportProjectDialog::selectionChanged()) m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); //Signals/Slots connect(ui.leFileName, SIGNAL(textChanged(QString)), SLOT(fileNameChanged(QString))); connect(ui.bOpen, SIGNAL(clicked()), this, SLOT (selectFile())); connect(m_bNewFolder, SIGNAL(clicked()), this, SLOT(newFolder())); connect(ui.chbUnusedObjects, &QCheckBox::stateChanged, this, &ImportProjectDialog::refreshPreview); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QString title; switch (m_projectType) { case (ProjectLabPlot): m_projectParser = new LabPlotProjectParser(); title = i18n("Import LabPlot Project"); break; case (ProjectOrigin): m_projectParser = new OriginProjectParser(); title = i18n("Import Origin Project"); break; } //dialog title and icon setWindowTitle(title); setWindowIcon(QIcon::fromTheme("document-import")); QTimer::singleShot(0, this, &ImportProjectDialog::loadSettings); } void ImportProjectDialog::loadSettings() { //restore saved settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); QString lastImportedFile; switch (m_projectType) { case (ProjectLabPlot): lastImportedFile = QLatin1String("LastImportedLabPlotProject"); break; case (ProjectOrigin): lastImportedFile = QLatin1String("LastImportedOriginProject"); break; } QApplication::processEvents(QEventLoop::AllEvents, 100); ui.leFileName->setText(conf.readEntry(lastImportedFile, "")); } ImportProjectDialog::~ImportProjectDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); QString lastImportedFile; switch (m_projectType) { case (ProjectLabPlot): lastImportedFile = QLatin1String("LastImportedLabPlotProject"); break; case (ProjectOrigin): lastImportedFile = QLatin1String("LastImportedOriginProject"); break; } conf.writeEntry(lastImportedFile, ui.leFileName->text()); } void ImportProjectDialog::setCurrentFolder(const Folder* folder) { m_cbAddTo->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(folder)); } void ImportProjectDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportProjectDialog::importTo()"); //determine the selected objects, convert the model indexes to string pathes const QModelIndexList& indexes = ui.tvPreview->selectionModel()->selectedIndexes(); QStringList selectedPathes; for (int i = 0; i < indexes.size()/4; ++i) { QModelIndex index = indexes.at(i*4); const AbstractAspect* aspect = static_cast(index.internalPointer()); //path of the current aspect and the pathes of all aspects it depends on selectedPathes << aspect->path(); QDEBUG(" aspect path: " << aspect->path()); for (const auto* depAspect : aspect->dependsOn()) selectedPathes << depAspect->path(); } selectedPathes.removeDuplicates(); Folder* targetFolder = static_cast(m_cbAddTo->currentModelIndex().internalPointer()); //check whether the selected pathes already exist in the target folder and warn the user const QString& targetFolderPath = targetFolder->path(); const Project* targetProject = targetFolder->project(); QStringList targetAllPathes; for (const auto* aspect : targetProject->children(AbstractAspect::Recursive)) { if (!dynamic_cast(aspect)) targetAllPathes << aspect->path(); } QStringList existingPathes; for (const auto& path : selectedPathes) { const QString& newPath = targetFolderPath + path.right(path.length() - path.indexOf('/')); if (targetAllPathes.indexOf(newPath) != -1) existingPathes << path; } QDEBUG("project objects to be imported: " << selectedPathes); QDEBUG("all already available project objects: " << targetAllPathes); QDEBUG("already available project objects to be overwritten: " << existingPathes); if (!existingPathes.isEmpty()) { QString msg = i18np("The object listed below already exists in target folder and will be overwritten:", "The objects listed below already exist in target folder and will be overwritten:", existingPathes.size()); msg += '\n'; for (const auto& path : existingPathes) msg += '\n' + path.right(path.length() - path.indexOf('/') - 1); //strip away the name of the root folder "Project" msg += "\n\n" + i18n("Do you want to proceed?"); const int rc = KMessageBox::warningYesNo(0, msg, i18n("Override existing objects?")); if (rc == KMessageBox::No) return; } //show a progress bar in the status bar QProgressBar* progressBar = new QProgressBar(); progressBar->setMinimum(0); progressBar->setMaximum(100); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QApplication::processEvents(QEventLoop::AllEvents, 100); //import the selected project objects into the specified folder QTime timer; timer.start(); connect(m_projectParser, SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); if (m_projectType == ProjectOrigin && ui.chbUnusedObjects->isVisible() && ui.chbUnusedObjects->isChecked()) reinterpret_cast(m_projectParser)->setImportUnusedObjects(true); m_projectParser->importTo(targetFolder, selectedPathes); statusBar->showMessage( i18n("Project data imported in %1 seconds.", (float)timer.elapsed()/1000) ); QApplication::restoreOverrideCursor(); statusBar->removeWidget(progressBar); } /*! * show the content of the project in the tree view */ void ImportProjectDialog::refreshPreview() { QString project = ui.leFileName->text(); m_projectParser->setProjectFileName(project); if (m_projectType == ProjectOrigin) { OriginProjectParser* originParser = reinterpret_cast(m_projectParser); if (originParser->hasUnusedObjects()) ui.chbUnusedObjects->show(); else ui.chbUnusedObjects->hide(); originParser->setImportUnusedObjects(ui.chbUnusedObjects->isVisible() && ui.chbUnusedObjects->isChecked()); } ui.tvPreview->setModel(m_projectParser->model()); connect(ui.tvPreview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)) ); //show top-level containers only if (ui.tvPreview->model()) { QModelIndex root = ui.tvPreview->model()->index(0,0); showTopLevelOnly(root); } //extand the tree to show all available top-level objects and adjust the header sizes ui.tvPreview->expandAll(); ui.tvPreview->header()->resizeSections(QHeaderView::ResizeToContents); } /*! Hides the non-toplevel items of the model used in the tree view. */ void ImportProjectDialog::showTopLevelOnly(const QModelIndex& index) { int rows = index.model()->rowCount(index); for (int i = 0; i < rows; ++i) { QModelIndex child = index.child(i, 0); showTopLevelOnly(child); const AbstractAspect* aspect = static_cast(child.internalPointer()); ui.tvPreview->setRowHidden(i, index, !isTopLevel(aspect)); } } /*! checks whether \c aspect is one of the allowed top level types */ bool ImportProjectDialog::isTopLevel(const AbstractAspect* aspect) const { foreach (const char* classString, m_projectParser->topLevelClasses()) { if (aspect->inherits(classString)) return true; } return false; } //############################################################################## //################################# SLOTS #################################### //############################################################################## void ImportProjectDialog::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { Q_UNUSED(deselected); //determine the dependent objects and select/deselect them too const QModelIndexList& indexes = selected.indexes(); if (indexes.isEmpty()) return; //for the just selected aspect, determine all the objects it depends on and select them, too //TODO: we need a better "selection", maybe with tri-state check boxes in the tree view const AbstractAspect* aspect = static_cast(indexes.at(0).internalPointer()); const QVector aspects = aspect->dependsOn(); AspectTreeModel* model = reinterpret_cast(ui.tvPreview->model()); for (const auto* aspect : aspects) { QModelIndex index = model->modelIndexOfAspect(aspect, 0); ui.tvPreview->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); } //Ok-button is only enabled if some project objects were selected bool enable = (selected.indexes().size() != 0); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); if (enable) m_buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18n("Close the dialog and import the selected objects.")); else m_buttonBox->button(QDialogButtonBox::Ok)->setToolTip(i18n("Select object(s) to be imported.")); } /*! opens a file dialog and lets the user select the project file. */ void ImportProjectDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportProjectDialog"); QString title; QString lastDir; QString supportedFormats; QString lastDirConfEntryName; switch (m_projectType) { case (ProjectLabPlot): title = i18n("Open LabPlot Project"); lastDirConfEntryName = QLatin1String("LastImportLabPlotProjectDir"); supportedFormats = i18n("LabPlot Projects (%1)", Project::supportedExtensions()); break; case (ProjectOrigin): title = i18n("Open Origin Project"); lastDirConfEntryName = QLatin1String("LastImportOriginProjecttDir"); supportedFormats = i18n("Origin Projects (%1)", OriginProjectParser::supportedExtensions()); break; } lastDir = conf.readEntry(lastDirConfEntryName, ""); QString path = QFileDialog::getOpenFileName(this, title, lastDir, supportedFormats); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != lastDir) conf.writeEntry(lastDirConfEntryName, newDir); } ui.leFileName->setText(path); refreshPreview(); } void ImportProjectDialog::fileNameChanged(const QString& name) { QString fileName = name; #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif bool fileExists = QFile::exists(fileName); if (fileExists) ui.leFileName->setStyleSheet(""); else ui.leFileName->setStyleSheet("QLineEdit{background:red;}"); if (!fileExists) { //file doesn't exist -> delete the content preview that is still potentially //available from the previously selected file ui.tvPreview->setModel(nullptr); m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); return; } refreshPreview(); } void ImportProjectDialog::newFolder() { QString path = ui.leFileName->text(); QString name = path.right( path.length()-path.lastIndexOf(QDir::separator())-1 ); bool ok; QInputDialog* dlg = new QInputDialog(this); name = dlg->getText(this, i18n("Add new folder"), i18n("Folder name:"), QLineEdit::Normal, name, &ok); if (ok) { Folder* folder = new Folder(name); m_mainWin->addAspectToProject(folder); m_cbAddTo->setCurrentModelIndex(m_mainWin->model()->modelIndexOfAspect(folder)); } delete dlg; } diff --git a/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp b/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp index fe538c364..5e9ef43ff 100644 --- a/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp +++ b/src/kdefrontend/datasources/ImportSQLDatabaseDialog.cpp @@ -1,175 +1,176 @@ /*************************************************************************** File : ImportSQLDatabaseDialog.cpp Project : LabPlot Description : import SQL dataase dialog -------------------------------------------------------------------- Copyright : (C) 2016 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2016-2017 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 "ImportSQLDatabaseDialog.h" #include "ImportSQLDatabaseWidget.h" #include "backend/core/AspectTreeModel.h" #include "backend/lib/macros.h" #include "kdefrontend/MainWin.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include "backend/core/Workbook.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include -#include +#include +#include #include /*! \class ImportSQLDatabaseDialog \brief Dialog for importing data from a SQL database. Embeds \c ImportSQLDatabaseWidget and provides the standard buttons. \ingroup kdefrontend */ ImportSQLDatabaseDialog::ImportSQLDatabaseDialog(MainWin* parent) : ImportDialog(parent), importSQLDatabaseWidget(new ImportSQLDatabaseWidget(this)) { vLayout->addWidget(importSQLDatabaseWidget); - setWindowTitle(i18n("Import Data to Spreadsheet or Matrix")); + setWindowTitle(i18nc("@title:window", "Import Data to Spreadsheet or Matrix")); setWindowIcon(QIcon::fromTheme("document-import-database")); setModel(); //dialog buttons QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(false); //ok is only available if a valid container was selected vLayout->addWidget(buttonBox); //Signals/Slots connect(importSQLDatabaseWidget, SIGNAL(stateChanged()), this, SLOT(checkOkButton())); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); QTimer::singleShot(0, this, &ImportSQLDatabaseDialog::loadSettings); } void ImportSQLDatabaseDialog::loadSettings() { //restore saved settings QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "ImportSQLDatabaseDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); } ImportSQLDatabaseDialog::~ImportSQLDatabaseDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ImportSQLDatabaseDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ImportSQLDatabaseDialog::importTo(QStatusBar* statusBar) const { DEBUG("ImportSQLDatabaseDialog::import()"); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { DEBUG("ERROR: No aspect available!"); return; } AbstractFileFilter::ImportMode mode = AbstractFileFilter::ImportMode(cbPosition->currentIndex()); //show a progress bar in the status bar QProgressBar* progressBar = new QProgressBar(); progressBar->setMinimum(0); progressBar->setMaximum(100); connect(importSQLDatabaseWidget, SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); WAIT_CURSOR; QApplication::processEvents(QEventLoop::AllEvents, 100); QTime timer; timer.start(); if (aspect->inherits("Matrix")) { Matrix* matrix = qobject_cast(aspect); importSQLDatabaseWidget->read(matrix, mode); } else if (aspect->inherits("Spreadsheet")) { Spreadsheet* spreadsheet = qobject_cast(aspect); importSQLDatabaseWidget->read(spreadsheet, mode); } else if (aspect->inherits("Workbook")) { // use active spreadsheet or matrix (only if numeric data is going to be improted) if present, // create a new spreadsheet in the selected workbook otherwise Workbook* workbook = qobject_cast(aspect); Spreadsheet* spreadsheet = workbook->currentSpreadsheet(); Matrix* matrix = workbook->currentMatrix(); if (spreadsheet) importSQLDatabaseWidget->read(spreadsheet, mode); else if (matrix && importSQLDatabaseWidget->isNumericData()) importSQLDatabaseWidget->read(matrix, mode); else { spreadsheet = new Spreadsheet(0, i18n("Spreadsheet")); workbook->addChild(spreadsheet); importSQLDatabaseWidget->read(spreadsheet, mode); } } statusBar->showMessage( i18n("Data imported in %1 seconds.", (float)timer.elapsed()/1000) ); RESET_CURSOR; statusBar->removeWidget(progressBar); } QString ImportSQLDatabaseDialog::selectedObject() const { return importSQLDatabaseWidget->selectedTable(); } void ImportSQLDatabaseDialog::checkOkButton() { DEBUG("ImportSQLDatabaseDialog::checkOkButton()"); AbstractAspect* aspect = static_cast(cbAddTo->currentModelIndex().internalPointer()); if (!aspect) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a data container where the data has to be imported into.")); cbPosition->setEnabled(false); return; } //check whether a valid connection and an object to import were selected if (!importSQLDatabaseWidget->isValid()) { okButton->setEnabled(false); okButton->setToolTip(i18n("Select a valid database object (table or query result set) that has to be imported.")); cbPosition->setEnabled(false); return; } //for matrix containers allow to import only numerical data if (dynamic_cast(aspect) && !importSQLDatabaseWidget->isNumericData()) { okButton->setEnabled(false); okButton->setToolTip(i18n("Cannot import into a matrix since the data contains non-numerical data.")); cbPosition->setEnabled(false); return; } okButton->setEnabled(true); okButton->setToolTip(i18n("Close the dialog and import the data.")); cbPosition->setEnabled(true); } diff --git a/src/kdefrontend/datasources/ImportSQLDatabaseWidget.cpp b/src/kdefrontend/datasources/ImportSQLDatabaseWidget.cpp index b5b8fcd2c..0cf0ff397 100644 --- a/src/kdefrontend/datasources/ImportSQLDatabaseWidget.cpp +++ b/src/kdefrontend/datasources/ImportSQLDatabaseWidget.cpp @@ -1,446 +1,448 @@ /*************************************************************************** File : ImportSQLDatabaseWidget.cpp Project : LabPlot Description : Datapicker -------------------------------------------------------------------- Copyright : (C) 2016 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2016-2017 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 "ImportSQLDatabaseWidget.h" #include "DatabaseManagerDialog.h" #include "DatabaseManagerWidget.h" #include "backend/datasources/AbstractDataSource.h" #include "backend/datasources/filters/AbstractFileFilter.h" #include "backend/lib/macros.h" +#include #include -#include +#include #include +#include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include #include #include #endif #include #include ImportSQLDatabaseWidget::ImportSQLDatabaseWidget(QWidget* parent) : QWidget(parent), m_cols(0), m_rows(0), m_databaseTreeModel(0), m_initializing(0), m_valid(false), m_numeric(false) { ui.setupUi(this); ui.cbImportFrom->addItem(i18n("Table")); ui.cbImportFrom->addItem(i18n("Custom query")); ui.bDatabaseManager->setIcon(QIcon::fromTheme("network-server-database")); ui.twPreview->setEditTriggers(QAbstractItemView::NoEditTriggers); ui.cbNumberFormat->addItems(AbstractFileFilter::numberFormats()); ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teQuery->document()); m_highlighter->setDefinition(m_repository.definitionForName("SQL")); m_highlighter->setTheme( (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme) ); #endif m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + "sql_connections"; connect( ui.cbConnection, SIGNAL(currentIndexChanged(int)), SLOT(connectionChanged()) ); connect( ui.cbImportFrom, SIGNAL(currentIndexChanged(int)), SLOT(importFromChanged(int)) ); connect( ui.bDatabaseManager, SIGNAL(clicked()), this, SLOT(showDatabaseManager()) ); connect( ui.lwTables, SIGNAL(currentRowChanged(int)), this, SLOT(refreshPreview()) ); connect( ui.bRefreshPreview, SIGNAL(clicked()), this, SLOT(refreshPreview()) ); //defer the loading of settings a bit in order to show the dialog prior to blocking the GUI in refreshPreview() QTimer::singleShot( 100, this, SLOT(loadSettings()) ); } void ImportSQLDatabaseWidget::loadSettings() { m_initializing = true; //read available connections readConnections(); //load last used connection and other settings KConfigGroup config(KSharedConfig::openConfig(), "ImportSQLDatabaseWidget"); ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(config.readEntry("Connection", ""))); ui.cbImportFrom->setCurrentIndex(config.readEntry("ImportFrom", 0)); importFromChanged(ui.cbImportFrom->currentIndex()); ui.cbNumberFormat->setCurrentIndex(config.readEntry("NumberFormat", (int)QLocale::AnyLanguage)); ui.cbDateTimeFormat->setCurrentItem(config.readEntry("DateTimeFormat", "yyyy-dd-MM hh:mm:ss:zzz")); QList defaultSizes; defaultSizes << 100 << 100; ui.splitterMain->setSizes(config.readEntry("SplitterMainSizes", defaultSizes)); ui.splitterPreview->setSizes(config.readEntry("SplitterPreviewSizes", defaultSizes)); //TODO m_initializing = false; //all settings loaded -> trigger the selection of the last used connection in order to get the data preview connectionChanged(); } ImportSQLDatabaseWidget::~ImportSQLDatabaseWidget() { // save current settings KConfigGroup config(KSharedConfig::openConfig(), "ImportSQLDatabaseWidget"); config.writeEntry("Connection", ui.cbConnection->currentText()); config.writeEntry("ImportFrom", ui.cbImportFrom->currentIndex()); config.writeEntry("NumberFormat", ui.cbNumberFormat->currentText()); config.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText()); config.writeEntry("SplitterMainSizes", ui.splitterMain->sizes()); config.writeEntry("SplitterPreviewSizes", ui.splitterPreview->sizes()); //TODO } /*! * in case the import from a table is selected, returns the currently selected database table. * returns empty string otherwise. */ QString ImportSQLDatabaseWidget::selectedTable() const { if (ui.cbImportFrom->currentIndex() == 0) { if (ui.lwTables->currentItem()) return ui.lwTables->currentItem()->text(); } return QString(); } /*! returns \c true if a working connections was selected and a table (or custom query) is provided and ready to be imported. returns \c false otherwise. */ bool ImportSQLDatabaseWidget::isValid() const { return m_valid; } /*! returns \c true if the selected table or the result of a custom query contains numeric data only. returns \c false otherwise. */ bool ImportSQLDatabaseWidget::isNumericData() const { return m_numeric; } /*! loads all available saved connections */ void ImportSQLDatabaseWidget::readConnections() { DEBUG("ImportSQLDatabaseWidget: reading available connections"); KConfig config(m_configPath, KConfig::SimpleConfig); for (const auto& name : config.groupList()) ui.cbConnection->addItem(name); } void ImportSQLDatabaseWidget::connectionChanged() { if (m_initializing) return; QDEBUG("ImportSQLDatabaseWidget: connecting to " + ui.cbConnection->currentText()); //clear the previously shown content ui.teQuery->clear(); ui.lwTables->clear(); ui.twPreview->clear(); if (ui.cbConnection->currentIndex() == -1) return; //connection name was changed, determine the current connections settings KConfig config(m_configPath, KConfig::SimpleConfig); KConfigGroup group = config.group(ui.cbConnection->currentText()); //open the selected connection const QString driver = group.readEntry("Driver"); m_db = QSqlDatabase::addDatabase(driver); m_db.setDatabaseName( group.readEntry("DatabaseName") ); if (!DatabaseManagerWidget::isFileDB(driver)) { m_db.setHostName( group.readEntry("HostName") ); m_db.setPort( group.readEntry("Port", 0) ); m_db.setUserName( group.readEntry("UserName") ); m_db.setPassword( group.readEntry("Password") ); } if (!m_db.open()) { KMessageBox::error(this, i18n("Failed to connect to the database '%1'. Please check the connection settings.", ui.cbConnection->currentText()), - i18n("Connection failed")); + i18n("Connection Failed")); setInvalid(); return; } //show all available database tables if (m_db.tables().size()) { ui.lwTables->addItems(m_db.tables()); ui.lwTables->setCurrentRow(0); for (int i = 0; i < ui.lwTables->count(); ++i) ui.lwTables->item(i)->setIcon(QIcon::fromTheme("view-form-table")); } else setInvalid(); } void ImportSQLDatabaseWidget::refreshPreview() { if (!ui.lwTables->currentItem()) { setInvalid(); return; } WAIT_CURSOR; ui.twPreview->clear(); //execute the current query (select on a table or a custom query) const QString& query = currentQuery(true); if (query.isEmpty()) { RESET_CURSOR; setInvalid(); return; } QSqlQuery q; q.prepare(currentQuery(true)); q.setForwardOnly(true); q.exec(); if (!q.isActive()) { RESET_CURSOR; if (!q.lastError().databaseText().isEmpty()) - KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to execute query")); + KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to Execute Query")); setInvalid(); return; } //resize the table to the number of columns (=number of fields in the result set) m_cols = q.record().count(); ui.twPreview->setColumnCount(m_cols); //determine the names and the data type (column modes) of the table columns. //check whether we have numerical data only by checking the data types of the first record. m_columnNames.clear(); m_columnModes.clear(); bool numeric = true; QLocale::Language numberFormat = (QLocale::Language)ui.cbNumberFormat->currentIndex(); const QString& dateTimeFormat = ui.cbDateTimeFormat->currentText(); q.next(); //go to the first record // ui.twPreview->setRowCount(1); //add the first row for the check boxes for (int i = 0; i < m_cols; ++i) { //name m_columnNames << q.record().fieldName(i); //value and type const QString valueString = q.record().value(i).toString(); AbstractColumn::ColumnMode mode = AbstractFileFilter::columnMode(valueString, dateTimeFormat, numberFormat); m_columnModes << mode; if (mode != AbstractColumn::Numeric) numeric = false; //header item QTableWidgetItem* item = new QTableWidgetItem(m_columnNames[i] + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, mode) + QLatin1String("}")); item->setTextAlignment(Qt::AlignLeft); item->setIcon(AbstractColumn::iconForMode(mode)); ui.twPreview->setHorizontalHeaderItem(i, item); //create checked items // QTableWidgetItem* itemChecked = new QTableWidgetItem(); // itemChecked->setCheckState(Qt::Checked); // ui.twPreview->setItem(0, i, itemChecked); } //preview the data const bool customQuery = (ui.cbImportFrom->currentIndex() != 0); int row = 0; do { for(int col = 0; col < m_cols; ++col) { ui.twPreview->setRowCount(row+1); ui.twPreview->setItem(row, col, new QTableWidgetItem(q.value(col).toString()) ); } row++; //in case a custom query is executed, check whether the row number limit is reached if (customQuery && row >= ui.sbPreviewLines->value()) break; } while (q.next()); ui.twPreview->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); setValid(); if (numeric != m_numeric) { m_numeric = numeric; emit stateChanged(); } RESET_CURSOR; } void ImportSQLDatabaseWidget::importFromChanged(int index) { if (index==0) { //import from a table ui.gbQuery->hide(); ui.lwTables->show(); } else { //import the result set of a custom query ui.gbQuery->show(); ui.lwTables->hide(); ui.twPreview->clear(); } refreshPreview(); } void ImportSQLDatabaseWidget::read(AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { if (!dataSource) return; WAIT_CURSOR; //execute the current query (select on a table or a custom query) QSqlQuery q; // q.setForwardOnly(true); //TODO: crashes most probably because of q.last() and q.first() below q.prepare(currentQuery()); if (!q.exec() || !q.isActive()) { RESET_CURSOR; if (!q.lastError().databaseText().isEmpty()) - KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to execute query")); + KMessageBox::error(this, q.lastError().databaseText(), i18n("Unable to Execute Query")); setInvalid(); return; } //determine the number of rows/records to read q.last(); const int rows = q.at()+1; q.first(); // pointers to the actual data containers //columnOffset indexes the "start column" in the datasource. Data will be imported starting from this column. QVector dataContainer; int columnOffset = dataSource->prepareImport(dataContainer, importMode, rows, m_cols, m_columnNames, m_columnModes); //number and DateTime formatting const QString& dateTimeFormat = ui.cbDateTimeFormat->currentText(); const QLocale numberFormat = QLocale((QLocale::Language)ui.cbNumberFormat->currentIndex()); //read the data int row = 0; do { for(int col = 0; col < m_cols; ++col) { const QString valueString = q.record().value(col).toString(); // set value depending on data type switch (m_columnModes[col]) { case AbstractColumn::Numeric: { bool isNumber; const double value = numberFormat.toDouble(valueString, &isNumber); static_cast*>(dataContainer[col])->operator[](row) = (isNumber ? value : NAN); break; } case AbstractColumn::Integer: { bool isNumber; const int value = numberFormat.toInt(valueString, &isNumber); static_cast*>(dataContainer[col])->operator[](row) = (isNumber ? value : NAN); break; } case AbstractColumn::DateTime: { const QDateTime valueDateTime = QDateTime::fromString(valueString, dateTimeFormat); static_cast*>(dataContainer[col])->operator[](row) = valueDateTime.isValid() ? valueDateTime : QDateTime(); break; } case AbstractColumn::Text: static_cast*>(dataContainer[col])->operator[](row) = valueString; break; case AbstractColumn::Month: // never happens case AbstractColumn::Day: break; } } row++; emit completed(100 * row/rows); } while (q.next()); dataSource->finalizeImport(columnOffset, 1, m_cols, dateTimeFormat, importMode); RESET_CURSOR; } QString ImportSQLDatabaseWidget::currentQuery(bool preview) { QString query; const bool customQuery = (ui.cbImportFrom->currentIndex() != 0); if ( !customQuery ) { const QString& tableName = ui.lwTables->currentItem()->text(); if (!preview) { query = QLatin1String("SELECT * FROM ") + tableName; } else { //preview the content of the currently selected table const QString& driver = m_db.driverName(); const QString& limit = QString::number(ui.sbPreviewLines->value()); if ( (driver == QLatin1String("QSQLITE3")) || (driver == QLatin1String("QMYSQL3")) || (driver == QLatin1String("QPSQL")) || (driver == QLatin1String("QSQLITE")) || (driver == QLatin1String("QMYSQL")) ) query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" LIMIT ") + limit; else if (driver == QLatin1String("QOCI")) query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" ROWNUM<=") + limit; else if (driver == QLatin1String("QDB2")) query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" FETCH FIRST ") + limit + QLatin1String(" ROWS ONLY"); else if (driver == QLatin1String("QIBASE")) query = QLatin1String("SELECT * FROM ") + tableName + QLatin1String(" ROWS ") + limit; else query = QLatin1String("SELECT TOP ") + limit + QLatin1String(" * FROM ") + tableName; } } else { //preview the result of a custom query query = ui.teQuery->toPlainText().simplified(); } return query; } /*! shows the database manager where the connections are created and edited. The selected connection is selected in the connection combo box in this widget. **/ void ImportSQLDatabaseWidget::showDatabaseManager() { DatabaseManagerDialog* dlg = new DatabaseManagerDialog(this, ui.cbConnection->currentText()); if (dlg->exec() == QDialog::Accepted) { //re-read the available connections to be in sync with the changes in DatabaseManager ui.cbConnection->clear(); readConnections(); //select the connection the user has selected in DataabaseManager QString conn = dlg->connection(); ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(conn)); } delete dlg; } void ImportSQLDatabaseWidget::setInvalid() { if (m_valid) { m_valid = false; emit stateChanged(); } } void ImportSQLDatabaseWidget::setValid() { if (!m_valid) { m_valid = true; emit stateChanged(); } } diff --git a/src/kdefrontend/datasources/JsonOptionsWidget.cpp b/src/kdefrontend/datasources/JsonOptionsWidget.cpp index 3a958c4e9..8241f8219 100644 --- a/src/kdefrontend/datasources/JsonOptionsWidget.cpp +++ b/src/kdefrontend/datasources/JsonOptionsWidget.cpp @@ -1,163 +1,163 @@ #include "ImportFileWidget.h" #include "JsonOptionsWidget.h" #include "backend/datasources/filters/QJsonModel.h" #include "backend/datasources/filters/AbstractFileFilter.h" #include "backend/datasources/filters/JsonFilter.h" -#include +#include #include #include #include /*! \class JsonOptionsWidget \brief Widget providing options for the import of json data \ingroup kdefrontend */ JsonOptionsWidget::JsonOptionsWidget(QWidget* parent, ImportFileWidget* fileWidget) : QWidget(parent), m_fileWidget(fileWidget), m_model(new QJsonModel()) { ui.setupUi(parent); ui.cbNumberFormat->addItems(AbstractFileFilter::numberFormats()); ui.cbDateTimeFormat->addItems(AbstractColumn::dateTimeFormats()); setTooltips(); } void JsonOptionsWidget::applyFilterSettings(JsonFilter* filter, const QModelIndex& index) const { Q_ASSERT(filter); filter->setModelRows(getIndexRows(index)); filter->setNumberFormat( QLocale::Language(ui.cbNumberFormat->currentIndex())); filter->setDateTimeFormat(ui.cbDateTimeFormat->currentText()); filter->setCreateIndexEnabled(ui.chbCreateIndex->isChecked()); filter->setNaNValueToZero(ui.chbConvertNaNToZero->isChecked()); //TODO: change this after implementation other row types filter->setDataRowType(QJsonValue::Array); if(!index.isValid()) return; QJsonTreeItem *item = static_cast(index.internalPointer()); if(item->childCount() < 1) return; filter->setDataRowType(item->child(0)->type()); } void JsonOptionsWidget::clearModel() { m_model->clear(); } void JsonOptionsWidget::loadSettings() const { KConfigGroup conf(KSharedConfig::openConfig(), "ImportJson"); ui.cbNumberFormat->setCurrentIndex(conf.readEntry("NumberFormat", (int)QLocale::AnyLanguage)); ui.cbDateTimeFormat->setCurrentItem(conf.readEntry("DateTimeFormat", "yyyy-MM-dd hh:mm:ss.zzz")); ui.chbCreateIndex->setChecked(conf.readEntry("CreateIndex", false)); ui.chbConvertNaNToZero->setChecked(conf.readEntry("ConvertNaNToZero", false)); } void JsonOptionsWidget::saveSettings() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportJson"); conf.writeEntry("NumberFormat", ui.cbNumberFormat->currentIndex()); conf.writeEntry("DateTimeFormat", ui.cbDateTimeFormat->currentText()); conf.writeEntry("CreateIndex", ui.chbCreateIndex->isChecked()); conf.writeEntry("ConvertNaNToZero", ui.chbConvertNaNToZero->isChecked()); } void JsonOptionsWidget::loadDocument(QString filename) { if(m_filename == filename) return; else m_filename = filename; KFilterDev device(m_filename); if (!device.open(QIODevice::ReadOnly)) return; if (device.atEnd() && !device.isSequential()) // empty file return; if(!m_model->loadJson(device.readAll())) m_model->clear(); } QJsonModel* JsonOptionsWidget::model() { return m_model; } void JsonOptionsWidget::setTooltips() { const QString textNumberFormatShort = i18n("This option determines how the imported strings have to be converted to numbers."); const QString textNumberFormat = textNumberFormatShort + "

" + i18n( "For 'C Format', a period is used for the decimal point character and comma is used for the thousands group separator. " "Valid number representations are:" "
    " "
  • 1234.56
  • " "
  • 1,234.56
  • " "
  • etc.
  • " "
" "When using 'System locale', the system settings will be used. " "E.g., for the German local the valid number representations are:" "
    " "
  • 1234,56
  • " "
  • 1.234,56
  • " "
  • etc.
  • " "
" ); ui.lNumberFormat->setToolTip(textNumberFormatShort); ui.lNumberFormat->setWhatsThis(textNumberFormat); ui.cbNumberFormat->setToolTip(textNumberFormatShort); ui.cbNumberFormat->setWhatsThis(textNumberFormat); const QString textDateTimeFormatShort = i18n("This option determines how the imported strings have to be converted to calendar date, i.e. year, month, and day numbers in the Gregorian calendar and to time."); const QString textDateTimeFormat = textDateTimeFormatShort + "

" + i18n( "Expressions that may be used for the date part of format string:" "" "" "" "" "" "" "" "" "" "" "" "
dthe day as number without a leading zero (1 to 31).
ddthe day as number with a leading zero (01 to 31).
dddthe abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name.
ddddthe long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name.
Mthe month as number without a leading zero (1 to 12).
MMthe month as number with a leading zero (01 to 12).
MMMthe abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name.
MMMMthe long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name.
yythe year as two digit number (00 to 99).
yyyythe year as four digit number. If the year is negative, a minus sign is prepended in addition.


" "Expressions that may be used for the time part of the format string:" "" "" "" "" "" "" "" "" "" "" "" "" "" "
hthe hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)
hhthe hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)
Hthe hour without a leading zero (0 to 23, even with AM/PM display)
HHthe hour with a leading zero (00 to 23, even with AM/PM display)
mthe minute without a leading zero (0 to 59)
mmthe minute with a leading zero (00 to 59)
sthe second without a leading zero (0 to 59)
ssthe second with a leading zero (00 to 59)
zthe milliseconds without leading zeroes (0 to 999)
zzzthe milliseconds with leading zeroes (000 to 999)
AP or Ainterpret as an AM/PM time. AP must be either 'AM' or 'PM'.
ap or aInterpret as an AM/PM time. ap must be either 'am' or 'pm'.


" "Examples are:" "" "" "" "" "
dd.MM.yyyy20.07.1969
ddd MMMM d yySun July 20 69
'The day is' ddddThe day is Sunday
"); ui.lDateTimeFormat->setToolTip(textDateTimeFormatShort); ui.lDateTimeFormat->setWhatsThis(textDateTimeFormat); ui.cbDateTimeFormat->setToolTip(textDateTimeFormatShort); ui.cbDateTimeFormat->setWhatsThis(textDateTimeFormat); } QVector JsonOptionsWidget::getIndexRows(const QModelIndex &index) const { QVector rows; QModelIndex current = index; while(current.isValid()){ rows.prepend(current.row()); current = current.parent(); } return rows; } \ No newline at end of file diff --git a/src/kdefrontend/dockwidgets/AxisDock.cpp b/src/kdefrontend/dockwidgets/AxisDock.cpp index 397bf9854..05fe9e959 100644 --- a/src/kdefrontend/dockwidgets/AxisDock.cpp +++ b/src/kdefrontend/dockwidgets/AxisDock.cpp @@ -1,1909 +1,1909 @@ /*************************************************************************** File : AxisDock.cpp Project : LabPlot Description : axes widget class -------------------------------------------------------------------- Copyright : (C) 2011-2014 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2013 Stefan Gerlach (stefan.gerlach@uni-konstanz.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 "AxisDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/worksheet/Worksheet.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/widgets/LabelWidget.h" #include #include #include #include #include /*! \class AxisDock \brief Provides a widget for editing the properties of the axes currently selected in the project explorer. \ingroup kdefrontend */ AxisDock::AxisDock(QWidget* parent):QWidget(parent), m_axis(0), m_aspectTreeModel(0), m_dataChanged(0), m_initializing(false) { ui.setupUi(this); //"Title"-tab QHBoxLayout* hboxLayout = new QHBoxLayout(ui.tabTitle); labelWidget = new LabelWidget(ui.tabTitle); labelWidget->setFixedLabelMode(true); hboxLayout->addWidget(labelWidget); hboxLayout->setContentsMargins(2,2,2,2); hboxLayout->setSpacing(2); //"Ticks"-tab QGridLayout* layout = static_cast(ui.tabTicks->layout()); cbMajorTicksColumn = new TreeViewComboBox(ui.tabTicks); layout->addWidget(cbMajorTicksColumn, 5, 2); cbMinorTicksColumn = new TreeViewComboBox(ui.tabTicks); layout->addWidget(cbMinorTicksColumn, 18, 2); //adjust layouts in the tabs for (int i=0; icount(); ++i) { layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //********************************** Slots ********************************************** //"General"-tab connect(ui.leName, &QLineEdit::textChanged, this, &AxisDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &AxisDock::commentChanged); connect( ui.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( ui.cbOrientation, SIGNAL(currentIndexChanged(int)), this, SLOT(orientationChanged(int)) ); connect( ui.cbPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(positionChanged(int)) ); connect( ui.lePosition, SIGNAL(textChanged(QString)), this, SLOT(positionChanged()) ); connect( ui.cbScale, SIGNAL(currentIndexChanged(int)), this, SLOT(scaleChanged(int)) ); connect( ui.chkAutoScale, SIGNAL(stateChanged(int)), this, SLOT(autoScaleChanged(int)) ); connect( ui.leStart, SIGNAL(textChanged(QString)), this, SLOT(startChanged()) ); connect( ui.leEnd, SIGNAL(textChanged(QString)), this, SLOT(endChanged()) ); connect( ui.leZeroOffset, SIGNAL(textChanged(QString)), this, SLOT(zeroOffsetChanged()) ); connect( ui.leScalingFactor, SIGNAL(textChanged(QString)), this, SLOT(scalingFactorChanged()) ); //"Line"-tab connect( ui.cbLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(lineStyleChanged(int)) ); connect( ui.kcbLineColor, SIGNAL(changed(QColor)), this, SLOT(lineColorChanged(QColor)) ); connect( ui.sbLineWidth, SIGNAL(valueChanged(double)), this, SLOT(lineWidthChanged(double)) ); connect( ui.sbLineOpacity, SIGNAL(valueChanged(int)), this, SLOT(lineOpacityChanged(int)) ); connect( ui.cbArrowPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(arrowPositionChanged(int)) ); connect( ui.cbArrowType, SIGNAL(currentIndexChanged(int)), this, SLOT(arrowTypeChanged(int)) ); connect( ui.sbArrowSize, SIGNAL(valueChanged(int)), this, SLOT(arrowSizeChanged(int)) ); //"Major ticks"-tab connect( ui.cbMajorTicksDirection, SIGNAL(currentIndexChanged(int)), this, SLOT(majorTicksDirectionChanged(int)) ); connect( ui.cbMajorTicksType, SIGNAL(currentIndexChanged(int)), this, SLOT(majorTicksTypeChanged(int)) ); connect( ui.sbMajorTicksNumber, SIGNAL(valueChanged(int)), this, SLOT(majorTicksNumberChanged(int)) ); connect( ui.leMajorTicksIncrement, SIGNAL(textChanged(QString)), this, SLOT(majorTicksIncrementChanged()) ); connect( cbMajorTicksColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(majorTicksColumnChanged(QModelIndex)) ); connect( ui.cbMajorTicksLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(majorTicksLineStyleChanged(int)) ); connect( ui.kcbMajorTicksColor, SIGNAL(changed(QColor)), this, SLOT(majorTicksColorChanged(QColor)) ); connect( ui.sbMajorTicksWidth, SIGNAL(valueChanged(double)), this, SLOT(majorTicksWidthChanged(double)) ); connect( ui.sbMajorTicksLength, SIGNAL(valueChanged(double)), this, SLOT(majorTicksLengthChanged(double)) ); connect( ui.sbMajorTicksOpacity, SIGNAL(valueChanged(int)), this, SLOT(majorTicksOpacityChanged(int)) ); //"Minor ticks"-tab connect( ui.cbMinorTicksDirection, SIGNAL(currentIndexChanged(int)), this, SLOT(minorTicksDirectionChanged(int)) ); connect( ui.cbMinorTicksType, SIGNAL(currentIndexChanged(int)), this, SLOT(minorTicksTypeChanged(int)) ); connect( ui.sbMinorTicksNumber, SIGNAL(valueChanged(int)), this, SLOT(minorTicksNumberChanged(int)) ); connect( ui.leMinorTicksIncrement, SIGNAL(textChanged(QString)), this, SLOT(minorTicksIncrementChanged()) ); connect( cbMinorTicksColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(minorTicksColumnChanged(QModelIndex)) ); connect( ui.cbMinorTicksLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(minorTicksLineStyleChanged(int)) ); connect( ui.kcbMinorTicksColor, SIGNAL(changed(QColor)), this, SLOT(minorTicksColorChanged(QColor)) ); connect( ui.sbMinorTicksWidth, SIGNAL(valueChanged(double)), this, SLOT(minorTicksWidthChanged(double)) ); connect( ui.sbMinorTicksLength, SIGNAL(valueChanged(double)), this, SLOT(minorTicksLengthChanged(double)) ); connect( ui.sbMinorTicksOpacity, SIGNAL(valueChanged(int)), this, SLOT(minorTicksOpacityChanged(int)) ); //"Extra ticks"-tab //"Tick labels"-tab connect( ui.cbLabelsFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(labelsFormatChanged(int)) ); connect( ui.sbLabelsPrecision, SIGNAL(valueChanged(int)), this, SLOT(labelsPrecisionChanged(int)) ); connect( ui.chkLabelsAutoPrecision, SIGNAL(stateChanged(int)), this, SLOT(labelsAutoPrecisionChanged(int)) ); connect( ui.cbLabelsPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(labelsPositionChanged(int)) ); connect( ui.sbLabelsOffset, SIGNAL(valueChanged(double)), this, SLOT(labelsOffsetChanged(double)) ); connect( ui.sbLabelsRotation, SIGNAL(valueChanged(int)), this, SLOT(labelsRotationChanged(int)) ); connect( ui.kfrLabelsFont, SIGNAL(fontSelected(QFont)), this, SLOT(labelsFontChanged(QFont)) ); connect( ui.kcbLabelsFontColor, SIGNAL(changed(QColor)), this, SLOT(labelsFontColorChanged(QColor)) ); connect( ui.leLabelsPrefix, SIGNAL(textChanged(QString)), this, SLOT(labelsPrefixChanged()) ); connect( ui.leLabelsSuffix, SIGNAL(textChanged(QString)), this, SLOT(labelsSuffixChanged()) ); connect( ui.sbLabelsOpacity, SIGNAL(valueChanged(int)), this, SLOT(labelsOpacityChanged(int)) ); /* connect( ui.sbLabelsPrecision, SIGNAL(valueChanged(int)), this, SLOT(slotDataChanged()) ); connect( ui.cbLabelsFormat, SIGNAL(currentIndexChanged(QString)), this, SLOT(labelFormatChanged(QString)) ); connect( ui.leLabelsDateFormat, SIGNAL(textChanged(QString)), this, SLOT(slotDataChanged()) ); */ //"Grid"-tab connect( ui.cbMajorGridStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(majorGridStyleChanged(int)) ); connect( ui.kcbMajorGridColor, SIGNAL(changed(QColor)), this, SLOT(majorGridColorChanged(QColor)) ); connect( ui.sbMajorGridWidth, SIGNAL(valueChanged(double)), this, SLOT(majorGridWidthChanged(double)) ); connect( ui.sbMajorGridOpacity, SIGNAL(valueChanged(int)), this, SLOT(majorGridOpacityChanged(int)) ); connect( ui.cbMinorGridStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(minorGridStyleChanged(int)) ); connect( ui.kcbMinorGridColor, SIGNAL(changed(QColor)), this, SLOT(minorGridColorChanged(QColor)) ); connect( ui.sbMinorGridWidth, SIGNAL(valueChanged(double)), this, SLOT(minorGridWidthChanged(double)) ); connect( ui.sbMinorGridOpacity, SIGNAL(valueChanged(int)), this, SLOT(minorGridOpacityChanged(int)) ); TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::Axis); ui.verticalLayout->addWidget(templateHandler); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); init(); } AxisDock::~AxisDock() { if (m_aspectTreeModel) delete m_aspectTreeModel; } void AxisDock::init() { m_initializing=true; //Validators ui.lePosition->setValidator( new QDoubleValidator(ui.lePosition) ); ui.leStart->setValidator( new QDoubleValidator(ui.leStart) ); ui.leEnd->setValidator( new QDoubleValidator(ui.leEnd) ); ui.leZeroOffset->setValidator( new QDoubleValidator(ui.leZeroOffset) ); ui.leScalingFactor->setValidator( new QDoubleValidator(ui.leScalingFactor) ); ui.leMajorTicksIncrement->setValidator( new QDoubleValidator(ui.leMajorTicksIncrement) ); ui.leMinorTicksIncrement->setValidator( new QDoubleValidator(ui.leMinorTicksIncrement) ); //TODO move this stuff to retranslateUI() - ui.cbPosition->addItem(i18n("top")); - ui.cbPosition->addItem(i18n("bottom")); - ui.cbPosition->addItem(i18n("centered")); - ui.cbPosition->addItem(i18n("custom")); + ui.cbPosition->addItem(i18n("Top")); + ui.cbPosition->addItem(i18n("Bottom")); + ui.cbPosition->addItem(i18n("Centered")); + ui.cbPosition->addItem(i18n("Custom")); - ui.cbScale->addItem( i18n("linear") ); + ui.cbScale->addItem( i18n("Linear") ); ui.cbScale->addItem( QLatin1String("log(x)") ); ui.cbScale->addItem( QLatin1String("log2(x)") ); ui.cbScale->addItem( QLatin1String("ln(x)") ); ui.cbScale->addItem( QLatin1String("sqrt(x)") ); ui.cbScale->addItem( QLatin1String("x^2") ); - ui.cbOrientation->addItem( i18n("horizontal") ); - ui.cbOrientation->addItem( i18n("vertical") ); + ui.cbOrientation->addItem( i18n("Horizontal") ); + ui.cbOrientation->addItem( i18n("Vertical") ); //Arrows - ui.cbArrowType->addItem( i18n("no arrow") ); - ui.cbArrowType->addItem( i18n("simple, small") ); - ui.cbArrowType->addItem( i18n("simple, big") ); - ui.cbArrowType->addItem( i18n("filled, small") ); - ui.cbArrowType->addItem( i18n("filled, big") ); - ui.cbArrowType->addItem( i18n("semi-filled, small") ); - ui.cbArrowType->addItem( i18n("semi-filled, big") ); + ui.cbArrowType->addItem( i18n("No arrow") ); + ui.cbArrowType->addItem( i18n("Simple, Small") ); + ui.cbArrowType->addItem( i18n("Simple, Big") ); + ui.cbArrowType->addItem( i18n("Filled, Small") ); + ui.cbArrowType->addItem( i18n("Filled, Big") ); + ui.cbArrowType->addItem( i18n("Semi-filled, Small") ); + ui.cbArrowType->addItem( i18n("Semi-filled, Big") ); QPainter pa; pa.setPen( QPen(Qt::SolidPattern, 0) ); QPixmap pm(20, 20); ui.cbArrowType->setIconSize( QSize(20,20) ); //no arrow pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); pa.end(); ui.cbArrowType->setItemIcon(0, pm); //simple, small float cos_phi = cos(3.14159/6); pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.drawLine(3,10,17,10); pa.drawLine(17,10, 10, 10-5*cos_phi); pa.drawLine(17,10, 10, 10+5*cos_phi); pa.end(); ui.cbArrowType->setItemIcon(1, pm); //simple, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.drawLine(3,10,17,10); pa.drawLine(17,10, 10, 10-10*cos_phi); pa.drawLine(17,10, 10, 10+10*cos_phi); pa.end(); ui.cbArrowType->setItemIcon(2, pm); //filled, small pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); QPointF points3[3] = {QPointF(17, 10), QPointF(10, 10-4*cos_phi), QPointF(10, 10+4*cos_phi) }; pa.drawPolygon(points3, 3); pa.end(); ui.cbArrowType->setItemIcon(3, pm); //filled, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); QPointF points4[3] = {QPointF(17, 10), QPointF(10, 10-10*cos_phi), QPointF(10, 10+10*cos_phi) }; pa.drawPolygon(points4, 3); pa.end(); ui.cbArrowType->setItemIcon(4, pm); //semi-filled, small pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); QPointF points5[4] = {QPointF(17, 10), QPointF(10, 10-4*cos_phi), QPointF(13, 10), QPointF(10, 10+4*cos_phi) }; pa.drawPolygon(points5, 4); pa.end(); ui.cbArrowType->setItemIcon(5, pm); //semi-filled, big pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); QPointF points6[4] = {QPointF(17, 10), QPointF(10, 10-10*cos_phi), QPointF(13, 10), QPointF(10, 10+10*cos_phi) }; pa.drawPolygon(points6, 4); pa.end(); ui.cbArrowType->setItemIcon(6, pm); - ui.cbArrowPosition->addItem( i18n("left") ); - ui.cbArrowPosition->addItem( i18n("right") ); - ui.cbArrowPosition->addItem( i18n("both") ); + ui.cbArrowPosition->addItem( i18n("Left") ); + ui.cbArrowPosition->addItem( i18n("Right") ); + ui.cbArrowPosition->addItem( i18n("Both") ); - ui.cbMajorTicksDirection->addItem( i18n("none") ); - ui.cbMajorTicksDirection->addItem( i18n("in") ); - ui.cbMajorTicksDirection->addItem( i18n("out") ); - ui.cbMajorTicksDirection->addItem( i18n("in and out") ); + ui.cbMajorTicksDirection->addItem( i18n("None") ); + ui.cbMajorTicksDirection->addItem( i18n("In") ); + ui.cbMajorTicksDirection->addItem( i18n("Out") ); + ui.cbMajorTicksDirection->addItem( i18n("In and Out") ); ui.cbMajorTicksType->addItem( i18n("Number") ); ui.cbMajorTicksType->addItem( i18n("Increment") ); ui.cbMajorTicksType->addItem( i18n("Custom column") ); - ui.cbMinorTicksDirection->addItem( i18n("none") ); - ui.cbMinorTicksDirection->addItem( i18n("in") ); - ui.cbMinorTicksDirection->addItem( i18n("out") ); - ui.cbMinorTicksDirection->addItem( i18n("in and out") ); + ui.cbMinorTicksDirection->addItem( i18n("None") ); + ui.cbMinorTicksDirection->addItem( i18n("In") ); + ui.cbMinorTicksDirection->addItem( i18n("Out") ); + ui.cbMinorTicksDirection->addItem( i18n("In and Out") ); ui.cbMinorTicksType->addItem( i18n("Number") ); ui.cbMinorTicksType->addItem( i18n("Increment") ); ui.cbMinorTicksType->addItem( i18n("Custom column") ); GuiTools::updatePenStyles(ui.cbLineStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, QColor(Qt::black)); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, QColor(Qt::black)); //labels - ui.cbLabelsPosition->addItem(i18n("no labels")); - ui.cbLabelsPosition->addItem(i18n("top")); - ui.cbLabelsPosition->addItem(i18n("bottom")); + ui.cbLabelsPosition->addItem(i18n("No labels")); + ui.cbLabelsPosition->addItem(i18n("Top")); + ui.cbLabelsPosition->addItem(i18n("Bottom")); ui.cbLabelsFormat->addItem( i18n("Decimal notation") ); ui.cbLabelsFormat->addItem( i18n("Scientific notation") ); ui.cbLabelsFormat->addItem( i18n("Powers of 10") ); ui.cbLabelsFormat->addItem( i18n("Powers of 2") ); ui.cbLabelsFormat->addItem( i18n("Powers of e") ); ui.cbLabelsFormat->addItem( i18n("Multiples of π") ); m_initializing=false; } void AxisDock::setModel() { QList list; list<<"Folder"<<"Spreadsheet"<<"FileDataSource"<<"Column"; cbMajorTicksColumn->setTopLevelClasses(list); cbMinorTicksColumn->setTopLevelClasses(list); list.clear(); list<<"Column"; m_aspectTreeModel->setSelectableAspects(list); cbMajorTicksColumn->setModel(m_aspectTreeModel); cbMinorTicksColumn->setModel(m_aspectTreeModel); } /*! sets the axes. The properties of the axes in the list \c list can be edited in this widget. */ void AxisDock::setAxes(QList list) { m_initializing=true; m_axesList=list; m_axis=list.first(); Q_ASSERT(m_axis != nullptr); m_aspectTreeModel = new AspectTreeModel(m_axis->project()); this->setModel(); labelWidget->setAxes(list); //if there are more then one axis in the list, disable the tab "general" if (list.size() == 1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_axis->name()); ui.leComment->setText(m_axis->comment()); this->setModelIndexFromColumn(cbMajorTicksColumn, m_axis->majorTicksColumn()); this->setModelIndexFromColumn(cbMinorTicksColumn, m_axis->minorTicksColumn()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); cbMajorTicksColumn->setCurrentModelIndex(QModelIndex()); cbMinorTicksColumn->setCurrentModelIndex(QModelIndex()); } //show the properties of the first axis this->load(); // general connect(m_axis, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(axisDescriptionChanged(const AbstractAspect*))); connect(m_axis, SIGNAL(orientationChanged(Axis::AxisOrientation)), this, SLOT(axisOrientationChanged(Axis::AxisOrientation))); connect(m_axis, SIGNAL(positionChanged(Axis::AxisPosition)), this, SLOT(axisPositionChanged(Axis::AxisPosition))); connect(m_axis, SIGNAL(scaleChanged(Axis::AxisScale)), this, SLOT(axisScaleChanged(Axis::AxisScale))); connect(m_axis, SIGNAL(autoScaleChanged(bool)), this, SLOT(axisAutoScaleChanged(bool))); connect(m_axis, SIGNAL(startChanged(double)), this, SLOT(axisStartChanged(double))); connect(m_axis, SIGNAL(endChanged(double)), this, SLOT(axisEndChanged(double))); connect(m_axis, SIGNAL(zeroOffsetChanged(qreal)), this, SLOT(axisZeroOffsetChanged(qreal))); connect(m_axis, SIGNAL(scalingFactorChanged(qreal)), this, SLOT(axisScalingFactorChanged(qreal))); // line connect(m_axis, SIGNAL(linePenChanged(QPen)), this, SLOT(axisLinePenChanged(QPen))); connect(m_axis, SIGNAL(lineOpacityChanged(qreal)), this, SLOT(axisLineOpacityChanged(qreal))); connect(m_axis, SIGNAL(arrowTypeChanged(Axis::ArrowType)), this, SLOT(axisArrowTypeChanged(Axis::ArrowType))); connect(m_axis, SIGNAL(arrowPositionChanged(Axis::ArrowPosition)), this, SLOT(axisArrowPositionChanged(Axis::ArrowPosition))); connect(m_axis, SIGNAL(arrowSizeChanged(qreal)), this, SLOT(axisArrowSizeChanged(qreal))); // ticks connect(m_axis, SIGNAL(majorTicksDirectionChanged(Axis::TicksDirection)), this, SLOT(axisMajorTicksDirectionChanged(Axis::TicksDirection))); connect(m_axis, SIGNAL(majorTicksTypeChanged(Axis::TicksType)), this, SLOT(axisMajorTicksTypeChanged(Axis::TicksType))); connect(m_axis, SIGNAL(majorTicksNumberChanged(int)), this, SLOT(axisMajorTicksNumberChanged(int))); connect(m_axis, SIGNAL(majorTicksIncrementChanged(qreal)), this, SLOT(axisMajorTicksIncrementChanged(qreal))); connect(m_axis, SIGNAL(majorTicksPenChanged(QPen)), this, SLOT(axisMajorTicksPenChanged(QPen))); connect(m_axis, SIGNAL(majorTicksLengthChanged(qreal)), this, SLOT(axisMajorTicksLengthChanged(qreal))); connect(m_axis, SIGNAL(majorTicksOpacityChanged(qreal)), this, SLOT(axisMajorTicksOpacityChanged(qreal))); connect(m_axis, SIGNAL(minorTicksDirectionChanged(Axis::TicksDirection)), this, SLOT(axisMinorTicksDirectionChanged(Axis::TicksDirection))); connect(m_axis, SIGNAL(minorTicksTypeChanged(Axis::TicksType)), this, SLOT(axisMinorTicksTypeChanged(Axis::TicksType))); connect(m_axis, SIGNAL(minorTicksNumberChanged(int)), this, SLOT(axisMinorTicksNumberChanged(int))); connect(m_axis, SIGNAL(minorTicksIncrementChanged(qreal)), this, SLOT(axisMinorTicksIncrementChanged(qreal))); connect(m_axis, SIGNAL(minorTicksPenChanged(QPen)), this, SLOT(axisMinorTicksPenChanged(QPen))); connect(m_axis, SIGNAL(minorTicksLengthChanged(qreal)), this, SLOT(axisMinorTicksLengthChanged(qreal))); connect(m_axis, SIGNAL(minorTicksOpacityChanged(qreal)), this, SLOT(axisMinorTicksOpacityChanged(qreal))); // labels connect(m_axis, SIGNAL(labelsFormatChanged(Axis::LabelsFormat)), this, SLOT(axisLabelsFormatChanged(Axis::LabelsFormat))); connect(m_axis, SIGNAL(labelsAutoPrecisionChanged(bool)), this, SLOT(axisLabelsAutoPrecisionChanged(bool))); connect(m_axis, SIGNAL(labelsPrecisionChanged(int)), this, SLOT(axisLabelsPrecisionChanged(int))); connect(m_axis, SIGNAL(labelsPositionChanged(Axis::LabelsPosition)), this, SLOT(axisLabelsPositionChanged(Axis::LabelsPosition))); connect(m_axis, SIGNAL(labelsOffsetChanged(double)), this, SLOT(axisLabelsOffsetChanged(double))); connect(m_axis, SIGNAL(labelsRotationAngleChanged(qreal)), this, SLOT(axisLabelsRotationAngleChanged(qreal))); connect(m_axis, SIGNAL(labelsFontChanged(QFont)), this, SLOT(axisLabelsFontChanged(QFont))); connect(m_axis, SIGNAL(labelsColorChanged(QColor)), this, SLOT(axisLabelsFontColorChanged(QColor))); connect(m_axis, SIGNAL(labelsPrefixChanged(QString)), this, SLOT(axisLabelsPrefixChanged(QString))); connect(m_axis, SIGNAL(labelsSuffixChanged(QString)), this, SLOT(axisLabelsSuffixChanged(QString))); connect(m_axis, SIGNAL(labelsOpacityChanged(qreal)), this, SLOT(axisLabelsOpacityChanged(qreal))); // grids connect(m_axis, SIGNAL(majorGridPenChanged(QPen)), this, SLOT(axisMajorGridPenChanged(QPen))); connect(m_axis, SIGNAL(majorGridOpacityChanged(qreal)), this, SLOT(axisMajorGridOpacityChanged(qreal))); connect(m_axis, SIGNAL(minorGridPenChanged(QPen)), this, SLOT(axisMinorGridPenChanged(QPen))); connect(m_axis, SIGNAL(minorGridOpacityChanged(qreal)), this, SLOT(axisMinorGridOpacityChanged(qreal))); connect(m_axis, SIGNAL(visibilityChanged(bool)), this, SLOT(axisVisibilityChanged(bool))); m_initializing = false; } void AxisDock::activateTitleTab() { ui.tabWidget->setCurrentWidget(ui.tabTitle); } void AxisDock::setModelIndexFromColumn(TreeViewComboBox* cb, const AbstractColumn* column) { if (column) cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else cb->setCurrentModelIndex(QModelIndex()); } //************************************************************* //********** SLOTs for changes triggered in AxisDock ********** //************************************************************* //"General"-tab void AxisDock::nameChanged() { if (m_initializing) return; m_axis->setName(ui.leName->text()); } void AxisDock::commentChanged() { if (m_initializing) return; m_axis->setComment(ui.leComment->text()); } void AxisDock::visibilityChanged(bool state) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setVisible(state); } /*! called if the orientation (horizontal or vertical) of the current axis is changed. */ void AxisDock::orientationChanged(int index) { Axis::AxisOrientation orientation = (Axis::AxisOrientation)index; if (orientation == Axis::AxisHorizontal) { - ui.cbPosition->setItemText(0, i18n("top") ); - ui.cbPosition->setItemText(1, i18n("bottom") ); - ui.cbLabelsPosition->setItemText(1, i18n("top") ); - ui.cbLabelsPosition->setItemText(2, i18n("bottom") ); + ui.cbPosition->setItemText(0, i18n("Top") ); + ui.cbPosition->setItemText(1, i18n("Bottom") ); + ui.cbLabelsPosition->setItemText(1, i18n("Top") ); + ui.cbLabelsPosition->setItemText(2, i18n("Bottom") ); ui.cbScale->setItemText(1, QLatin1String("log(x)") ); ui.cbScale->setItemText(2, QLatin1String("log2(x)") ); ui.cbScale->setItemText(3, QLatin1String("ln(x)") ); ui.cbScale->setItemText(4, QLatin1String("sqrt(x)") ); ui.cbScale->setItemText(5, QLatin1String("x^2") ); } else { //vertical - ui.cbPosition->setItemText(0, i18n("left") ); - ui.cbPosition->setItemText(1, i18n("right") ); - ui.cbLabelsPosition->setItemText(1, i18n("right") ); - ui.cbLabelsPosition->setItemText(2, i18n("left") ); + ui.cbPosition->setItemText(0, i18n("Left") ); + ui.cbPosition->setItemText(1, i18n("Right") ); + ui.cbLabelsPosition->setItemText(1, i18n("Right") ); + ui.cbLabelsPosition->setItemText(2, i18n("Left") ); ui.cbScale->setItemText(1, QLatin1String("log(y)") ); ui.cbScale->setItemText(2, QLatin1String("log2(y)") ); ui.cbScale->setItemText(3, QLatin1String("ln(y)") ); ui.cbScale->setItemText(4, QLatin1String("sqrt(y)") ); ui.cbScale->setItemText(5, QLatin1String("y^2") ); } if (m_initializing) return; //depending on the current orientation we need to update axis possition and labels position //axis position, map from the current index in the combobox to the enum value in Axis::AxisPosition Axis::AxisPosition axisPosition; int posIndex = ui.cbPosition->currentIndex(); if (orientation == Axis::AxisHorizontal) { if (posIndex>1) posIndex += 2; axisPosition = Axis::AxisPosition(posIndex); } else { axisPosition = Axis::AxisPosition(posIndex+2); } //labels position posIndex = ui.cbLabelsPosition->currentIndex(); Axis::LabelsPosition labelsPosition = Axis::LabelsPosition(posIndex); for (auto* axis : m_axesList) { axis->beginMacro(i18n("%1: set axis orientation", axis->name())); axis->setOrientation(orientation); axis->setPosition(axisPosition); axis->setLabelsPosition(labelsPosition); axis->endMacro(); } } /*! called if one of the predefined axis positions (top, bottom, left, right, center or custom) was changed. */ void AxisDock::positionChanged(int index) { if (index == -1) return; //we occasionally get -1 here, nothing to do in this case if (index == 3) ui.lePosition->setVisible(true); else ui.lePosition->setVisible(false); if (m_initializing) return; //map from the current index in the combo box to the enum value in Axis::AxisPosition, //depends on the current orientation Axis::AxisPosition position; if ( ui.cbOrientation->currentIndex() == 0 ) { if (index>1) index += 2; position = Axis::AxisPosition(index); } else { position = Axis::AxisPosition(index+2); } for (auto* axis : m_axesList) axis->setPosition(position); } /*! called when the custom position of the axis in the corresponding LineEdit is changed. */ void AxisDock::positionChanged() { if (m_initializing) return; double offset = ui.lePosition->text().toDouble(); for (auto* axis : m_axesList) axis->setOffset(offset); } void AxisDock::scaleChanged(int index) { if (m_initializing) return; Axis::AxisScale scale = (Axis::AxisScale)index; for (auto* axis : m_axesList) axis->setScale(scale); } void AxisDock::autoScaleChanged(int index) { bool autoScale = index == Qt::Checked; ui.leStart->setEnabled(!autoScale); ui.leEnd->setEnabled(!autoScale); if (m_initializing) return; for (auto* axis : m_axesList) axis->setAutoScale(autoScale); } void AxisDock::startChanged() { if (m_initializing) return; double value = ui.leStart->text().toDouble(); //check first, whether the value for the lower limit is valid for the log- and square root scaling. If not, set the default values. Axis::AxisScale scale = Axis::AxisScale(ui.cbScale->currentIndex()); if (scale == Axis::ScaleLog10 || scale == Axis::ScaleLog2 || scale == Axis::ScaleLn) { if (value <= 0) { KMessageBox::sorry(this, i18n("The axes lower limit has a non-positive value. Default minimal value will be used."), i18n("Wrong lower limit value") ); ui.leStart->setText( "0.01" ); value=0.01; } } else if (scale == Axis::ScaleSqrt) { if (value < 0) { KMessageBox::sorry(this, i18n("The axes lower limit has a negative value. Default minimal value will be used."), i18n("Wrong lower limit value") ); ui.leStart->setText( "0" ); value=0; } } for (auto* axis : m_axesList) axis->setStart(value); } void AxisDock::endChanged() { if (m_initializing) return; double value = ui.leEnd->text().toDouble(); for (auto* axis : m_axesList) axis->setEnd(value); } void AxisDock::zeroOffsetChanged() { if (m_initializing) return; double offset = ui.leZeroOffset->text().toDouble(); for (auto* axis : m_axesList) axis->setZeroOffset(offset); } void AxisDock::scalingFactorChanged() { if (m_initializing) return; double scalingFactor = ui.leScalingFactor->text().toDouble(); for (auto* axis : m_axesList) axis->setScalingFactor(scalingFactor); } // "Line"-tab void AxisDock::lineStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lLineColor->setEnabled(b); ui.kcbLineColor->setEnabled(b); ui.lLineWidth->setEnabled(b); ui.sbLineWidth->setEnabled(b); ui.lLineOpacity->setEnabled(b); ui.sbLineOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->linePen(); pen.setStyle(penStyle); axis->setLinePen(pen); } } void AxisDock::lineColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->linePen(); pen.setColor(color); axis->setLinePen(pen); } m_initializing=true; GuiTools::updatePenStyles(ui.cbLineStyle, color); m_initializing=false; } void AxisDock::lineWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->linePen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setLinePen(pen); } } void AxisDock::lineOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setLineOpacity(opacity); } void AxisDock::arrowTypeChanged(int index) { Axis::ArrowType type = (Axis::ArrowType)index; if (type == Axis::NoArrow) { ui.cbArrowPosition->setEnabled(false); ui.sbArrowSize->setEnabled(false); } else { ui.cbArrowPosition->setEnabled(true); ui.sbArrowSize->setEnabled(true); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setArrowType(type); } void AxisDock::arrowPositionChanged(int index) { if (m_initializing) return; Axis::ArrowPosition position = (Axis::ArrowPosition)index; for (auto* axis : m_axesList) axis->setArrowPosition(position); } void AxisDock::arrowSizeChanged(int value) { if (m_initializing) return; float v = Worksheet::convertToSceneUnits(value, Worksheet::Point); for (auto* axis : m_axesList) axis->setArrowSize(v); } //"Major ticks" tab void AxisDock::majorTicksDirectionChanged(int index) { Axis::TicksDirection direction = Axis::TicksDirection(index); bool b = (direction != Axis::noTicks); ui.lMajorTicksType->setEnabled(b); ui.cbMajorTicksType->setEnabled(b); ui.lMajorTicksType->setEnabled(b); ui.cbMajorTicksType->setEnabled(b); ui.lMajorTicksNumber->setEnabled(b); ui.sbMajorTicksNumber->setEnabled(b); ui.lMajorTicksIncrement->setEnabled(b); ui.leMajorTicksIncrement->setEnabled(b); ui.lMajorTicksLineStyle->setEnabled(b); ui.cbMajorTicksLineStyle->setEnabled(b); if (b) { Qt::PenStyle penStyle=Qt::PenStyle(ui.cbMajorTicksLineStyle->currentIndex()); b = (penStyle != Qt::NoPen); } ui.lMajorTicksColor->setEnabled(b); ui.kcbMajorTicksColor->setEnabled(b); ui.lMajorTicksWidth->setEnabled(b); ui.sbMajorTicksWidth->setEnabled(b); ui.lMajorTicksLength->setEnabled(b); ui.sbMajorTicksLength->setEnabled(b); ui.lMajorTicksOpacity->setEnabled(b); ui.sbMajorTicksOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksDirection(direction); } /*! called if the current style of the ticks (Number or Increment) is changed. Shows/hides the corresponding widgets. */ void AxisDock::majorTicksTypeChanged(int index) { Axis::TicksType type = Axis::TicksType(index); if ( type == Axis::TicksTotalNumber) { ui.lMajorTicksNumber->show(); ui.sbMajorTicksNumber->show(); ui.lMajorTicksIncrement->hide(); ui.leMajorTicksIncrement->hide(); ui.lMajorTicksColumn->hide(); cbMajorTicksColumn->hide(); } else if ( type == Axis::TicksIncrement) { ui.lMajorTicksNumber->hide(); ui.sbMajorTicksNumber->hide(); ui.lMajorTicksIncrement->show(); ui.leMajorTicksIncrement->show(); ui.lMajorTicksColumn->hide(); cbMajorTicksColumn->hide(); } else { ui.lMajorTicksNumber->hide(); ui.sbMajorTicksNumber->hide(); ui.lMajorTicksIncrement->hide(); ui.leMajorTicksIncrement->hide(); ui.lMajorTicksColumn->show(); cbMajorTicksColumn->show(); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksType(type); } void AxisDock::majorTicksNumberChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksNumber(value); } void AxisDock::majorTicksIncrementChanged() { if (m_initializing) return; double value = ui.leMajorTicksIncrement->text().toDouble(); if (value<0) value = -1*value; //don't allow negative values for (auto* axis : m_axesList) axis->setMajorTicksIncrement(value); } void AxisDock::majorTicksLineStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); bool b=(penStyle != Qt::NoPen); ui.lMajorTicksColor->setEnabled(b); ui.kcbMajorTicksColor->setEnabled(b); ui.lMajorTicksWidth->setEnabled(b); ui.sbMajorTicksWidth->setEnabled(b); ui.lMajorTicksLength->setEnabled(b); ui.sbMajorTicksLength->setEnabled(b); ui.lMajorTicksOpacity->setEnabled(b); ui.sbMajorTicksOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorTicksPen(); pen.setStyle(penStyle); axis->setMajorTicksPen(pen); } } void AxisDock::majorTicksColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column != nullptr); } for (auto* axis : m_axesList) axis->setMajorTicksColumn(column); } void AxisDock::majorTicksColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorTicksPen(); pen.setColor(color); axis->setMajorTicksPen(pen); } m_initializing=true; GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, color); m_initializing=false; } void AxisDock::majorTicksWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorTicksPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); axis->setMajorTicksPen(pen); } } void AxisDock::majorTicksLengthChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMajorTicksLength( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::majorTicksOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMajorTicksOpacity(opacity); } //"Minor ticks" tab void AxisDock::minorTicksDirectionChanged(int index) { Axis::TicksDirection direction = Axis::TicksDirection(index); bool b = (direction != Axis::noTicks); ui.lMinorTicksType->setEnabled(b); ui.cbMinorTicksType->setEnabled(b); ui.lMinorTicksType->setEnabled(b); ui.cbMinorTicksType->setEnabled(b); ui.lMinorTicksNumber->setEnabled(b); ui.sbMinorTicksNumber->setEnabled(b); ui.lMinorTicksIncrement->setEnabled(b); ui.leMinorTicksIncrement->setEnabled(b); ui.lMinorTicksLineStyle->setEnabled(b); ui.cbMinorTicksLineStyle->setEnabled(b); if (b) { Qt::PenStyle penStyle=Qt::PenStyle(ui.cbMinorTicksLineStyle->currentIndex()); b = (penStyle != Qt::NoPen); } ui.lMinorTicksColor->setEnabled(b); ui.kcbMinorTicksColor->setEnabled(b); ui.lMinorTicksWidth->setEnabled(b); ui.sbMinorTicksWidth->setEnabled(b); ui.lMinorTicksLength->setEnabled(b); ui.sbMinorTicksLength->setEnabled(b); ui.lMinorTicksOpacity->setEnabled(b); ui.sbMinorTicksOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksDirection(direction); } void AxisDock::minorTicksTypeChanged(int index) { Axis::TicksType type = Axis::TicksType(index); if ( type == Axis::TicksTotalNumber) { ui.lMinorTicksNumber->show(); ui.sbMinorTicksNumber->show(); ui.lMinorTicksIncrement->hide(); ui.leMinorTicksIncrement->hide(); ui.lMinorTicksColumn->hide(); cbMinorTicksColumn->hide(); } else if ( type == Axis::TicksIncrement) { ui.lMinorTicksNumber->hide(); ui.sbMinorTicksNumber->hide(); ui.lMinorTicksIncrement->show(); ui.leMinorTicksIncrement->show(); ui.lMinorTicksColumn->hide(); cbMinorTicksColumn->hide(); } else { ui.lMinorTicksNumber->hide(); ui.sbMinorTicksNumber->hide(); ui.lMinorTicksIncrement->hide(); ui.leMinorTicksIncrement->hide(); ui.lMinorTicksColumn->show(); cbMinorTicksColumn->show(); } if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksType(type); } void AxisDock::minorTicksNumberChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksNumber(value); } void AxisDock::minorTicksIncrementChanged() { if (m_initializing) return; double value = ui.leMinorTicksIncrement->text().toDouble(); if (value<0) value = -1*value; //don't allow negative values for (auto* axis : m_axesList) axis->setMinorTicksIncrement(value); } void AxisDock::minorTicksColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); Q_ASSERT(column != nullptr); for (auto* axis : m_axesList) axis->setMinorTicksColumn(column); } void AxisDock::minorTicksLineStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); bool b=(penStyle != Qt::NoPen); ui.lMinorTicksColor->setEnabled(b); ui.kcbMinorTicksColor->setEnabled(b); ui.lMinorTicksWidth->setEnabled(b); ui.sbMinorTicksWidth->setEnabled(b); ui.lMinorTicksLength->setEnabled(b); ui.sbMinorTicksLength->setEnabled(b); ui.lMinorTicksOpacity->setEnabled(b); ui.sbMinorTicksOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorTicksPen(); pen.setStyle(penStyle); axis->setMinorTicksPen(pen); } } void AxisDock::minorTicksColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorTicksPen(); pen.setColor(color); axis->setMinorTicksPen(pen); } m_initializing=true; GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, color); m_initializing=false; } void AxisDock::minorTicksWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorTicksPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); axis->setMinorTicksPen(pen); } } void AxisDock::minorTicksLengthChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setMinorTicksLength( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::minorTicksOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMinorTicksOpacity(opacity); } //"Tick labels"-tab void AxisDock::labelsFormatChanged(int index) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsFormat(Axis::LabelsFormat(index)); } void AxisDock::labelsPrecisionChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsPrecision(value); } void AxisDock::labelsAutoPrecisionChanged(int state) { bool checked = (state == Qt::Checked); ui.sbLabelsPrecision->setEnabled(!checked); if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsAutoPrecision(checked); } void AxisDock::labelsPositionChanged(int index) { Axis::LabelsPosition position = Axis::LabelsPosition(index); bool b = (position != Axis::NoLabels); ui.lLabelsOffset->setEnabled(b); ui.sbLabelsOffset->setEnabled(b); ui.lLabelsRotation->setEnabled(b); ui.sbLabelsRotation->setEnabled(b); ui.lLabelsFont->setEnabled(b); ui.kfrLabelsFont->setEnabled(b); ui.lLabelsColor->setEnabled(b); ui.kcbLabelsFontColor->setEnabled(b); ui.lLabelsPrefix->setEnabled(b); ui.leLabelsPrefix->setEnabled(b); ui.lLabelsSuffix->setEnabled(b); ui.leLabelsSuffix->setEnabled(b); ui.lLabelsOpacity->setEnabled(b); ui.sbLabelsOpacity->setEnabled(b); if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsPosition(position); } void AxisDock::labelsOffsetChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsOffset( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void AxisDock::labelsRotationChanged(int value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsRotationAngle(value); } void AxisDock::labelsPrefixChanged() { if (m_initializing) return; QString prefix = ui.leLabelsPrefix->text(); for (auto* axis : m_axesList) axis->setLabelsPrefix(prefix); } void AxisDock::labelsSuffixChanged() { if (m_initializing) return; QString suffix = ui.leLabelsSuffix->text(); for (auto* axis : m_axesList) axis->setLabelsSuffix(suffix); } void AxisDock::labelsFontChanged(const QFont& font) { if (m_initializing) return; QFont labelsFont = font; labelsFont.setPixelSize( Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Point) ); for (auto* axis : m_axesList) axis->setLabelsFont( labelsFont ); } void AxisDock::labelsFontColorChanged(const QColor& color) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setLabelsColor(color); } void AxisDock::labelsOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setLabelsOpacity(opacity); } // "Grid"-tab //major grid void AxisDock::majorGridStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMajorGridColor->setEnabled(b); ui.kcbMajorGridColor->setEnabled(b); ui.lMajorGridWidth->setEnabled(b); ui.sbMajorGridWidth->setEnabled(b); ui.lMajorGridOpacity->setEnabled(b); ui.sbMajorGridOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorGridPen(); pen.setStyle(penStyle); axis->setMajorGridPen(pen); } } void AxisDock::majorGridColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorGridPen(); pen.setColor(color); axis->setMajorGridPen(pen); } m_initializing=true; GuiTools::updatePenStyles(ui.cbMajorGridStyle, color); m_initializing=false; } void AxisDock::majorGridWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->majorGridPen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setMajorGridPen(pen); } } void AxisDock::majorGridOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMajorGridOpacity(opacity); } //minor grid void AxisDock::minorGridStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); bool b = (penStyle != Qt::NoPen); ui.lMinorGridColor->setEnabled(b); ui.kcbMinorGridColor->setEnabled(b); ui.lMinorGridWidth->setEnabled(b); ui.sbMinorGridWidth->setEnabled(b); ui.lMinorGridOpacity->setEnabled(b); ui.sbMinorGridOpacity->setEnabled(b); if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorGridPen(); pen.setStyle(penStyle); axis->setMinorGridPen(pen); } } void AxisDock::minorGridColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorGridPen(); pen.setColor(color); axis->setMinorGridPen(pen); } m_initializing=true; GuiTools::updatePenStyles(ui.cbMinorGridStyle, color); m_initializing=false; } void AxisDock::minorGridWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* axis : m_axesList) { pen=axis->minorGridPen(); pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Point)); axis->setMinorGridPen(pen); } } void AxisDock::minorGridOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* axis : m_axesList) axis->setMinorGridOpacity(opacity); } //************************************************************* //************ SLOTs for changes triggered in Axis ************ //************************************************************* void AxisDock::axisDescriptionChanged(const AbstractAspect* aspect) { if (m_axis != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) { ui.leName->setText(aspect->name()); } else if (aspect->comment() != ui.leComment->text()) { ui.leComment->setText(aspect->comment()); } m_initializing = false; } void AxisDock::axisOrientationChanged(Axis::AxisOrientation orientation) { m_initializing = true; ui.cbOrientation->setCurrentIndex( (int)orientation ); m_initializing = false; } void AxisDock::axisPositionChanged(Axis::AxisPosition position) { m_initializing = true; //map from the enum Axis::AxisOrientation to the index in the combo box int index(position); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); m_initializing = false; } void AxisDock::axisPositionChanged(float value) { m_initializing = true; ui.lePosition->setText( QString::number(value) ); m_initializing = false; } void AxisDock::axisScaleChanged(Axis::AxisScale scale) { m_initializing = true; ui.cbScale->setCurrentIndex( (int)scale ); m_initializing = false; } void AxisDock::axisAutoScaleChanged(bool on) { m_initializing = true; ui.chkAutoScale->setChecked(on); m_initializing = false; } void AxisDock::axisStartChanged(double value) { m_initializing = true; ui.leStart->setText( QString::number(value) ); m_initializing = false; } void AxisDock::axisEndChanged(double value) { m_initializing = true; ui.leEnd->setText( QString::number(value) ); m_initializing = false; } void AxisDock::axisZeroOffsetChanged(qreal value) { m_initializing = true; ui.leZeroOffset->setText( QString::number(value) ); m_initializing = false; } void AxisDock::axisScalingFactorChanged(qreal value) { m_initializing = true; ui.leScalingFactor->setText( QString::number(value) ); m_initializing = false; } //line void AxisDock::axisLinePenChanged(const QPen& pen) { m_initializing = true; ui.cbLineStyle->setCurrentIndex( pen.style() ); ui.kcbLineColor->setColor( pen.color() ); GuiTools::updatePenStyles(ui.cbLineStyle, pen.color() ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Point) ); m_initializing = false; } void AxisDock::axisArrowTypeChanged(Axis::ArrowType type) { m_initializing = true; ui.cbArrowType->setCurrentIndex( (int)type); m_initializing = false; } void AxisDock::axisLineOpacityChanged(qreal opacity) { m_initializing = true; ui.sbLineOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisArrowPositionChanged(Axis::ArrowPosition position) { m_initializing = true; ui.cbArrowPosition->setCurrentIndex( (int)position ); m_initializing = false; } void AxisDock::axisArrowSizeChanged(qreal size) { m_initializing = true; ui.sbArrowSize->setValue( (int)Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } //major ticks void AxisDock::axisMajorTicksDirectionChanged(Axis::TicksDirection direction) { m_initializing = true; ui.cbMajorTicksDirection->setCurrentIndex(direction); m_initializing = false; } void AxisDock::axisMajorTicksTypeChanged(Axis::TicksType type) { m_initializing = true; ui.cbMajorTicksType->setCurrentIndex(type); m_initializing = false; } void AxisDock::axisMajorTicksNumberChanged(int number) { m_initializing = true; ui.sbMajorTicksNumber->setValue(number); m_initializing = false; } void AxisDock::axisMajorTicksIncrementChanged(qreal increment) { m_initializing = true; ui.leMajorTicksIncrement->setText( QString::number(increment)); m_initializing = false; } void AxisDock::axisMajorTicksPenChanged(const QPen& pen) { m_initializing = true; ui.cbMajorTicksLineStyle->setCurrentIndex(pen.style()); ui.kcbMajorTicksColor->setColor(pen.color()); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMajorTicksLengthChanged(qreal length) { m_initializing = true; ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits(length,Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMajorTicksOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMajorTicksOpacity->setValue( round(opacity*100.0)); m_initializing = false; } //minor ticks void AxisDock::axisMinorTicksDirectionChanged(Axis::TicksDirection direction) { m_initializing = true; ui.cbMinorTicksDirection->setCurrentIndex(direction); m_initializing = false; } void AxisDock::axisMinorTicksTypeChanged(Axis::TicksType type) { m_initializing = true; ui.cbMinorTicksType->setCurrentIndex(type); m_initializing = false; } void AxisDock::axisMinorTicksNumberChanged(int number) { m_initializing = true; ui.sbMinorTicksNumber->setValue(number); m_initializing = false; } void AxisDock::axisMinorTicksIncrementChanged(qreal increment) { m_initializing = true; ui.leMinorTicksIncrement->setText( QString::number(increment)); m_initializing = false; } void AxisDock::axisMinorTicksPenChanged(const QPen& pen) { m_initializing = true; ui.cbMinorTicksLineStyle->setCurrentIndex(pen.style()); ui.kcbMinorTicksColor->setColor(pen.color()); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMinorTicksLengthChanged(qreal length) { m_initializing = true; ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(length,Worksheet::Point) ); m_initializing = false; } void AxisDock::axisMinorTicksOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMinorTicksOpacity->setValue(round(opacity*100.0)); m_initializing = false; } //labels void AxisDock::axisLabelsFormatChanged(Axis::LabelsFormat format) { m_initializing = true; ui.cbLabelsFormat->setCurrentIndex(format); m_initializing = false; } void AxisDock::axisLabelsAutoPrecisionChanged(bool on) { m_initializing = true; ui.chkLabelsAutoPrecision->setChecked((int) on); m_initializing = false; } void AxisDock::axisLabelsPrecisionChanged(int precision) { m_initializing = true; ui.sbLabelsPrecision->setValue(precision); m_initializing = false; } void AxisDock::axisLabelsPositionChanged(Axis::LabelsPosition position) { m_initializing = true; ui.cbLabelsPosition->setCurrentIndex(position); m_initializing = false; } void AxisDock::axisLabelsOffsetChanged(double offset) { m_initializing = true; ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(offset, Worksheet::Point) ); m_initializing = false; } void AxisDock::axisLabelsRotationAngleChanged(qreal rotation) { m_initializing = true; ui.sbLabelsRotation->setValue(rotation); m_initializing = false; } void AxisDock::axisLabelsFontChanged(const QFont& font) { m_initializing = true; //we need to set the font size in points for KFontRequester QFont newFont(font); newFont.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont(newFont); m_initializing = false; } void AxisDock::axisLabelsFontColorChanged(const QColor& color) { m_initializing = true; ui.kcbLabelsFontColor->setColor(color); m_initializing = false; } void AxisDock::axisLabelsPrefixChanged(const QString& prefix) { m_initializing = true; ui.leLabelsPrefix->setText(prefix); m_initializing = false; } void AxisDock::axisLabelsSuffixChanged(const QString& suffix) { m_initializing = true; ui.leLabelsSuffix->setText(suffix); m_initializing = false; } void AxisDock::axisLabelsOpacityChanged(qreal opacity) { m_initializing = true; ui.sbLabelsOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } //grid void AxisDock::axisMajorGridPenChanged(const QPen& pen) { m_initializing = true; ui.cbMajorGridStyle->setCurrentIndex((int) pen.style()); ui.kcbMajorGridColor->setColor(pen.color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, pen.color()); ui.sbMajorGridWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void AxisDock::axisMajorGridOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMajorGridOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisMinorGridPenChanged(const QPen& pen) { m_initializing = true; ui.cbMinorGridStyle->setCurrentIndex((int) pen.style()); ui.kcbMinorGridColor->setColor(pen.color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, pen.color()); ui.sbMinorGridWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void AxisDock::axisMinorGridOpacityChanged(qreal opacity) { m_initializing = true; ui.sbMinorGridOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void AxisDock::axisVisibilityChanged(bool on) { m_initializing = true; ui.chkVisible->setChecked(on); m_initializing = false; } //************************************************************* //************************* Settings ************************** //************************************************************* void AxisDock::load() { //General ui.chkVisible->setChecked( m_axis->isVisible() ); ui.cbOrientation->setCurrentIndex( (int) m_axis->orientation() ); int index = (int) m_axis->position(); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); ui.lePosition->setText( QString::number( m_axis->offset()) ); ui.cbScale->setCurrentIndex( (int) m_axis->scale() ); ui.chkAutoScale->setChecked( m_axis->autoScale() ); ui.leStart->setText( QString::number(m_axis->start()) ); ui.leEnd->setText( QString::number(m_axis->end()) ); ui.leZeroOffset->setText( QString::number(m_axis->zeroOffset()) ); ui.leScalingFactor->setText( QString::number(m_axis->scalingFactor()) ); //Line ui.cbLineStyle->setCurrentIndex( (int) m_axis->linePen().style() ); ui.kcbLineColor->setColor( m_axis->linePen().color() ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->linePen().widthF(),Worksheet::Point) ); ui.sbLineOpacity->setValue( round(m_axis->lineOpacity()*100.0) ); ui.cbArrowType->setCurrentIndex( (int)m_axis->arrowType() ); ui.cbArrowPosition->setCurrentIndex( (int)m_axis->arrowPosition() ); ui.sbArrowSize->setValue( (int)Worksheet::convertFromSceneUnits(m_axis->arrowSize(), Worksheet::Point) ); //Major ticks ui.cbMajorTicksDirection->setCurrentIndex( (int) m_axis->majorTicksDirection() ); ui.cbMajorTicksType->setCurrentIndex( (int) m_axis->majorTicksType() ); ui.sbMajorTicksNumber->setValue( m_axis->majorTicksNumber() ); ui.leMajorTicksIncrement->setText( QString::number(m_axis->majorTicksIncrement()) ); ui.cbMajorTicksLineStyle->setCurrentIndex( (int) m_axis->majorTicksPen().style() ); ui.kcbMajorTicksColor->setColor( m_axis->majorTicksPen().color() ); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits( m_axis->majorTicksPen().widthF(),Worksheet::Point) ); ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits( m_axis->majorTicksLength(),Worksheet::Point) ); ui.sbMajorTicksOpacity->setValue( round(m_axis->majorTicksOpacity()*100.0) ); //Minor ticks ui.cbMinorTicksDirection->setCurrentIndex( (int) m_axis->minorTicksDirection() ); ui.cbMinorTicksType->setCurrentIndex( (int) m_axis->minorTicksType() ); ui.sbMinorTicksNumber->setValue( m_axis->minorTicksNumber() ); ui.leMinorTicksIncrement->setText( QString::number( m_axis->minorTicksIncrement()) ); ui.cbMinorTicksLineStyle->setCurrentIndex( (int) m_axis->minorTicksPen().style() ); ui.kcbMinorTicksColor->setColor( m_axis->minorTicksPen().color() ); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->minorTicksPen().widthF(),Worksheet::Point) ); ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(m_axis->minorTicksLength(),Worksheet::Point) ); ui.sbMinorTicksOpacity->setValue( round(m_axis->minorTicksOpacity()*100.0) ); //Extra ticks //TODO // Tick label ui.cbLabelsFormat->setCurrentIndex( (int) m_axis->labelsFormat() ); ui.chkLabelsAutoPrecision->setChecked( (int) m_axis->labelsAutoPrecision() ); ui.sbLabelsPrecision->setValue( (int)m_axis->labelsPrecision() ); ui.cbLabelsPosition->setCurrentIndex( (int) m_axis->labelsPosition() ); ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(m_axis->labelsOffset(),Worksheet::Point) ); ui.sbLabelsRotation->setValue( m_axis->labelsRotationAngle() ); //we need to set the font size in points for KFontRequester QFont font = m_axis->labelsFont(); font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont( font ); ui.kcbLabelsFontColor->setColor( m_axis->labelsColor() ); ui.leLabelsPrefix->setText( m_axis->labelsPrefix() ); ui.leLabelsSuffix->setText( m_axis->labelsSuffix() ); ui.sbLabelsOpacity->setValue( round(m_axis->labelsOpacity()*100.0) ); //Grid ui.cbMajorGridStyle->setCurrentIndex( (int) m_axis->majorGridPen().style() ); ui.kcbMajorGridColor->setColor( m_axis->majorGridPen().color() ); ui.sbMajorGridWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->majorGridPen().widthF(),Worksheet::Point) ); ui.sbMajorGridOpacity->setValue( round(m_axis->majorGridOpacity()*100.0) ); ui.cbMinorGridStyle->setCurrentIndex( (int) m_axis->minorGridPen().style() ); ui.kcbMinorGridColor->setColor( m_axis->minorGridPen().color() ); ui.sbMinorGridWidth->setValue( Worksheet::convertFromSceneUnits(m_axis->minorGridPen().widthF(),Worksheet::Point) ); ui.sbMinorGridOpacity->setValue( round(m_axis->minorGridOpacity()*100.0) ); m_initializing=true; GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); this->majorTicksTypeChanged(ui.cbMajorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, ui.kcbMajorTicksColor->color()); this->minorTicksTypeChanged(ui.cbMinorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, ui.kcbMinorTicksColor->color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, ui.kcbMajorGridColor->color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, ui.kcbMinorGridColor->color()); m_initializing=false; } void AxisDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_axesList.size(); if (size>1) m_axis->beginMacro(i18n("%1 axes: template \"%2\" loaded", size, name)); else m_axis->beginMacro(i18n("%1: template \"%2\" loaded", m_axis->name(), name)); this->loadConfig(config); m_axis->endMacro(); } void AxisDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "Axis" ); //General ui.cbOrientation->setCurrentIndex( group.readEntry("Orientation", (int) m_axis->orientation()) ); int index = group.readEntry("Position", (int) m_axis->position()); if (index > 1) ui.cbPosition->setCurrentIndex(index-2); else ui.cbPosition->setCurrentIndex(index); ui.lePosition->setText( QString::number( group.readEntry("PositionOffset", m_axis->offset())) ); ui.cbScale->setCurrentIndex( group.readEntry("Scale", (int) m_axis->scale()) ); ui.chkAutoScale->setChecked(group.readEntry("AutoScale", m_axis->autoScale())); ui.leStart->setText( QString::number( group.readEntry("Start", m_axis->start())) ); ui.leEnd->setText( QString::number( group.readEntry("End", m_axis->end())) ); ui.leZeroOffset->setText( QString::number( group.readEntry("ZeroOffset", m_axis->zeroOffset())) ); ui.leScalingFactor->setText( QString::number( group.readEntry("ScalingFactor", m_axis->scalingFactor())) ); //Title KConfigGroup axisLabelGroup = config.group("AxisLabel"); labelWidget->loadConfig(axisLabelGroup); //Line ui.cbLineStyle->setCurrentIndex( group.readEntry("LineStyle", (int) m_axis->linePen().style()) ); ui.kcbLineColor->setColor( group.readEntry("LineColor", m_axis->linePen().color()) ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("LineWidth", m_axis->linePen().widthF()),Worksheet::Point) ); ui.sbLineOpacity->setValue( round(group.readEntry("LineOpacity", m_axis->lineOpacity())*100.0) ); ui.cbArrowType->setCurrentIndex( group.readEntry("ArrowType", (int) m_axis->arrowType()) ); ui.cbArrowPosition->setCurrentIndex( group.readEntry("ArrowPosition", (int) m_axis->arrowPosition()) ); ui.sbArrowSize->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ArrowSize", m_axis->arrowSize()), Worksheet::Point) ); //Major ticks ui.cbMajorTicksDirection->setCurrentIndex( group.readEntry("MajorTicksDirection", (int) m_axis->majorTicksDirection()) ); ui.cbMajorTicksType->setCurrentIndex( group.readEntry("MajorTicksType", (int) m_axis->majorTicksType()) ); ui.sbMajorTicksNumber->setValue( group.readEntry("MajorTicksNumber", m_axis->majorTicksNumber()) ); ui.leMajorTicksIncrement->setText( QString::number( group.readEntry("MajorTicksIncrement", m_axis->majorTicksIncrement())) ); ui.cbMajorTicksLineStyle->setCurrentIndex( group.readEntry("MajorTicksLineStyle", (int) m_axis->majorTicksPen().style()) ); ui.kcbMajorTicksColor->setColor( group.readEntry("MajorTicksColor", m_axis->majorTicksPen().color()) ); ui.sbMajorTicksWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorTicksWidth", m_axis->majorTicksPen().widthF()),Worksheet::Point) ); ui.sbMajorTicksLength->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorTicksLength", m_axis->majorTicksLength()),Worksheet::Point) ); ui.sbMajorTicksOpacity->setValue( round(group.readEntry("MajorTicksOpacity", m_axis->majorTicksOpacity())*100.0) ); //Minor ticks ui.cbMinorTicksDirection->setCurrentIndex( group.readEntry("MinorTicksDirection", (int) m_axis->minorTicksDirection()) ); ui.cbMinorTicksType->setCurrentIndex( group.readEntry("MinorTicksType", (int) m_axis->minorTicksType()) ); ui.sbMinorTicksNumber->setValue( group.readEntry("MinorTicksNumber", m_axis->minorTicksNumber()) ); ui.leMinorTicksIncrement->setText( QString::number( group.readEntry("MinorTicksIncrement", m_axis->minorTicksIncrement())) ); ui.cbMinorTicksLineStyle->setCurrentIndex( group.readEntry("MinorTicksLineStyle", (int) m_axis->minorTicksPen().style()) ); ui.kcbMinorTicksColor->setColor( group.readEntry("MinorTicksColor", m_axis->minorTicksPen().color()) ); ui.sbMinorTicksWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorTicksWidth", m_axis->minorTicksPen().widthF()),Worksheet::Point) ); ui.sbMinorTicksLength->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorTicksLength", m_axis->minorTicksLength()),Worksheet::Point) ); ui.sbMinorTicksOpacity->setValue( round(group.readEntry("MinorTicksOpacity", m_axis->minorTicksOpacity())*100.0) ); //Extra ticks //TODO // Tick label ui.cbLabelsFormat->setCurrentIndex( group.readEntry("LabelsFormat", (int) m_axis->labelsFormat()) ); ui.chkLabelsAutoPrecision->setChecked( group.readEntry("LabelsAutoPrecision", (int) m_axis->labelsAutoPrecision()) ); ui.sbLabelsPrecision->setValue( group.readEntry("LabelsPrecision", (int)m_axis->labelsPrecision()) ); ui.cbLabelsPosition->setCurrentIndex( group.readEntry("LabelsPosition", (int) m_axis->labelsPosition()) ); ui.sbLabelsOffset->setValue( Worksheet::convertFromSceneUnits(group.readEntry("LabelsOffset", m_axis->labelsOffset()), Worksheet::Point) ); ui.sbLabelsRotation->setValue( group.readEntry("LabelsRotation", m_axis->labelsRotationAngle()) ); //we need to set the font size in points for KFontRequester QFont font = m_axis->labelsFont(); font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelsFont->setFont( group.readEntry("LabelsFont", font) ); ui.kcbLabelsFontColor->setColor( group.readEntry("LabelsFontColor", m_axis->labelsColor()) ); ui.leLabelsPrefix->setText( group.readEntry("LabelsPrefix", m_axis->labelsPrefix()) ); ui.leLabelsSuffix->setText( group.readEntry("LabelsSuffix", m_axis->labelsSuffix()) ); ui.sbLabelsOpacity->setValue( round(group.readEntry("LabelsOpacity", m_axis->labelsOpacity())*100.0) ); //Grid ui.cbMajorGridStyle->setCurrentIndex( group.readEntry("MajorGridStyle", (int) m_axis->majorGridPen().style()) ); ui.kcbMajorGridColor->setColor( group.readEntry("MajorGridColor", m_axis->majorGridPen().color()) ); ui.sbMajorGridWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MajorGridWidth", m_axis->majorGridPen().widthF()),Worksheet::Point) ); ui.sbMajorGridOpacity->setValue( round(group.readEntry("MajorGridOpacity", m_axis->majorGridOpacity())*100.0) ); ui.cbMinorGridStyle->setCurrentIndex( group.readEntry("MinorGridStyle", (int) m_axis->minorGridPen().style()) ); ui.kcbMinorGridColor->setColor( group.readEntry("MinorGridColor", m_axis->minorGridPen().color()) ); ui.sbMinorGridWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("MinorGridWidth", m_axis->minorGridPen().widthF()),Worksheet::Point) ); ui.sbMinorGridOpacity->setValue( round(group.readEntry("MinorGridOpacity", m_axis->minorGridOpacity())*100.0) ); m_initializing=true; GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); this->majorTicksTypeChanged(ui.cbMajorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMajorTicksLineStyle, ui.kcbMajorTicksColor->color()); this->minorTicksTypeChanged(ui.cbMinorTicksType->currentIndex()); GuiTools::updatePenStyles(ui.cbMinorTicksLineStyle, ui.kcbMinorTicksColor->color()); GuiTools::updatePenStyles(ui.cbMajorGridStyle, ui.kcbMajorGridColor->color()); GuiTools::updatePenStyles(ui.cbMinorGridStyle, ui.kcbMinorGridColor->color()); m_initializing=false; } void AxisDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "Axis" ); //General group.writeEntry("Orientation", ui.cbOrientation->currentIndex()); if (ui.cbPosition->currentIndex() == 2) { group.writeEntry("Position", (int)Axis::AxisCentered); } else if (ui.cbPosition->currentIndex() == 3) { group.writeEntry("Position", (int)Axis::AxisCustom); } else { if ( ui.cbOrientation->currentIndex() == Axis::AxisHorizontal ) group.writeEntry("Position", ui.cbPosition->currentIndex()); else group.writeEntry("Position", ui.cbPosition->currentIndex()+2); } group.writeEntry("PositionOffset", ui.lePosition->text()); group.writeEntry("Scale", ui.cbScale->currentIndex()); group.writeEntry("Start", ui.leStart->text()); group.writeEntry("End", ui.leEnd->text()); group.writeEntry("ZeroOffset", ui.leZeroOffset->text()); group.writeEntry("ScalingFactor", ui.leScalingFactor->text()); //Title KConfigGroup axisLabelGroup = config.group("AxisLabel"); labelWidget->saveConfig(axisLabelGroup); //Line group.writeEntry("LineStyle", ui.cbLineStyle->currentIndex()); group.writeEntry("LineColor", ui.kcbLineColor->color()); group.writeEntry("LineWidth", Worksheet::convertToSceneUnits(ui.sbLineWidth->value(), Worksheet::Point)); group.writeEntry("LineOpacity", ui.sbLineOpacity->value()/100); //Major ticks group.writeEntry("MajorTicksDirection", ui.cbMajorTicksDirection->currentIndex()); group.writeEntry("MajorTicksType", ui.cbMajorTicksType->currentIndex()); group.writeEntry("MajorTicksNumber", ui.sbMajorTicksNumber->value()); group.writeEntry("MajorTicksIncrement", ui.leMajorTicksIncrement->text()); group.writeEntry("MajorTicksLineStyle", ui.cbMajorTicksLineStyle->currentIndex()); group.writeEntry("MajorTicksColor", ui.kcbMajorTicksColor->color()); group.writeEntry("MajorTicksWidth", Worksheet::convertToSceneUnits(ui.sbMajorTicksWidth->value(),Worksheet::Point)); group.writeEntry("MajorTicksLength", Worksheet::convertToSceneUnits(ui.sbMajorTicksLength->value(),Worksheet::Point)); group.writeEntry("MajorTicksOpacity", ui.sbMajorTicksOpacity->value()/100); //Minor ticks group.writeEntry("MinorTicksDirection", ui.cbMinorTicksDirection->currentIndex()); group.writeEntry("MinorTicksType", ui.cbMinorTicksType->currentIndex()); group.writeEntry("MinorTicksNumber", ui.sbMinorTicksNumber->value()); group.writeEntry("MinorTicksIncrement", ui.leMinorTicksIncrement->text()); group.writeEntry("MinorTicksLineStyle", ui.cbMinorTicksLineStyle->currentIndex()); group.writeEntry("MinorTicksColor", ui.kcbMinorTicksColor->color()); group.writeEntry("MinorTicksWidth", Worksheet::convertFromSceneUnits(ui.sbMinorTicksWidth->value(),Worksheet::Point)); group.writeEntry("MinorTicksLength", Worksheet::convertFromSceneUnits(ui.sbMinorTicksLength->value(),Worksheet::Point)); group.writeEntry("MinorTicksOpacity", ui.sbMinorTicksOpacity->value()/100); //Extra ticks // TODO // Tick label group.writeEntry("LabelsFormat", ui.cbLabelsFormat->currentIndex()); group.writeEntry("LabelsAutoPrecision", ui.chkLabelsAutoPrecision->isChecked()); group.writeEntry("LabelsPrecision", ui.sbLabelsPrecision->value()); group.writeEntry("LabelsPosition", ui.cbLabelsPosition->currentIndex()); group.writeEntry("LabelsOffset", Worksheet::convertToSceneUnits(ui.sbLabelsOffset->value(), Worksheet::Point)); group.writeEntry("LabelsRotation", ui.sbLabelsRotation->value()); group.writeEntry("LabelsFont", ui.kfrLabelsFont->font()); group.writeEntry("LabelsFontColor", ui.kcbLabelsFontColor->color()); group.writeEntry("LabelsPrefix", ui.leLabelsPrefix->text()); group.writeEntry("LabelsSuffix", ui.leLabelsSuffix->text()); group.writeEntry("LabelsOpacity", ui.sbLabelsOpacity->value()/100); //Grid group.writeEntry("MajorGridStyle", ui.cbMajorGridStyle->currentIndex()); group.writeEntry("MajorGridColor", ui.kcbMajorGridColor->color()); group.writeEntry("MajorGridWidth", Worksheet::convertToSceneUnits(ui.sbMajorGridWidth->value(), Worksheet::Point)); group.writeEntry("MajorGridOpacity", ui.sbMajorGridOpacity->value()/100); group.writeEntry("MinorGridStyle", ui.cbMinorGridStyle->currentIndex()); group.writeEntry("MinorGridColor", ui.kcbMinorGridColor->color()); group.writeEntry("MinorGridWidth", Worksheet::convertToSceneUnits(ui.sbMinorGridWidth->value(), Worksheet::Point)); group.writeEntry("MinorGridOpacity", ui.sbMinorGridOpacity->value()/100); config.sync(); } diff --git a/src/kdefrontend/dockwidgets/CartesianPlotDock.cpp b/src/kdefrontend/dockwidgets/CartesianPlotDock.cpp index 09b06612e..8e76867f1 100644 --- a/src/kdefrontend/dockwidgets/CartesianPlotDock.cpp +++ b/src/kdefrontend/dockwidgets/CartesianPlotDock.cpp @@ -1,1482 +1,1484 @@ /*************************************************************************** File : CartesianPlotDock.cpp Project : LabPlot Description : widget for cartesian plot properties -------------------------------------------------------------------- Copyright : (C) 2011-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2013 by Stefan Gerlach (stefan.gerlach@uni-konstanz.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 "CartesianPlotDock.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/core/column/Column.h" #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/ThemeHandler.h" #include #include #include #include #include #include #include #include #include +#include + /*! \class CartesianPlotDock \brief Provides a widget for editing the properties of the cartesian plot currently selected in the project explorer. \ingroup kdefrontend */ CartesianPlotDock::CartesianPlotDock(QWidget *parent): QWidget(parent), m_plot(0), labelWidget(0), m_initializing(false) { ui.setupUi(this); //"General"-tab QButtonGroup* rangeButtonsGroup(new QButtonGroup); rangeButtonsGroup->addButton(ui.rbRangeFirst); rangeButtonsGroup->addButton(ui.rbRangeLast); rangeButtonsGroup->addButton(ui.rbRangeFree); //"Range breaks"-tab ui.bAddXBreak->setIcon( QIcon::fromTheme("list-add") ); ui.bRemoveXBreak->setIcon( QIcon::fromTheme("list-remove") ); ui.cbXBreak->addItem("1"); ui.bAddYBreak->setIcon( QIcon::fromTheme("list-add") ); ui.bRemoveYBreak->setIcon( QIcon::fromTheme("list-remove") ); ui.cbYBreak->addItem("1"); //"Background"-tab ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leBackgroundFileName->setCompleter(completer); //"Title"-tab QHBoxLayout* hboxLayout = new QHBoxLayout(ui.tabTitle); labelWidget=new LabelWidget(ui.tabTitle); hboxLayout->addWidget(labelWidget); hboxLayout->setContentsMargins(2,2,2,2); hboxLayout->setSpacing(2); //adjust layouts in the tabs for (int i = 0; i < ui.tabWidget->count(); ++i) { QGridLayout* layout = qobject_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //Validators ui.leRangeFirst->setValidator( new QIntValidator(ui.leRangeFirst) ); ui.leRangeLast->setValidator( new QIntValidator(ui.leRangeLast) ); ui.leXBreakStart->setValidator( new QDoubleValidator(ui.leXBreakStart) ); ui.leXBreakEnd->setValidator( new QDoubleValidator(ui.leXBreakEnd) ); ui.leYBreakStart->setValidator( new QDoubleValidator(ui.leYBreakStart) ); ui.leYBreakEnd->setValidator( new QDoubleValidator(ui.leYBreakEnd) ); //SIGNAL/SLOT //General connect(ui.leName, &QLineEdit::textChanged, this, &CartesianPlotDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &CartesianPlotDock::commentChanged); connect( ui.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( ui.sbLeft, SIGNAL(valueChanged(double)), this, SLOT(geometryChanged()) ); connect( ui.sbTop, SIGNAL(valueChanged(double)), this, SLOT(geometryChanged()) ); connect( ui.sbWidth, SIGNAL(valueChanged(double)), this, SLOT(geometryChanged()) ); connect( ui.sbHeight, SIGNAL(valueChanged(double)), this, SLOT(geometryChanged()) ); connect( ui.leRangeFirst, SIGNAL(textChanged(QString)), this, SLOT(rangeFirstChanged(QString)) ); connect( ui.leRangeLast, SIGNAL(textChanged(QString)), this, SLOT(rangeLastChanged(QString)) ); connect( rangeButtonsGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(rangeTypeChanged()) ); connect( ui.chkAutoScaleX, SIGNAL(stateChanged(int)), this, SLOT(autoScaleXChanged(int)) ); connect( ui.leXMin, SIGNAL(textChanged(QString)), this, SLOT(xMinChanged()) ); connect( ui.leXMax, SIGNAL(textChanged(QString)), this, SLOT(xMaxChanged()) ); connect( ui.cbXScaling, SIGNAL(currentIndexChanged(int)), this, SLOT(xScaleChanged(int)) ); connect( ui.chkAutoScaleY, SIGNAL(stateChanged(int)), this, SLOT(autoScaleYChanged(int)) ); connect( ui.leYMin, SIGNAL(textChanged(QString)), this, SLOT(yMinChanged()) ); connect( ui.leYMax, SIGNAL(textChanged(QString)), this, SLOT(yMaxChanged()) ); connect( ui.cbYScaling, SIGNAL(currentIndexChanged(int)), this, SLOT(yScaleChanged(int)) ); //Range breaks connect( ui.chkXBreak, SIGNAL(toggled(bool)), this, SLOT(toggleXBreak(bool)) ); connect( ui.bAddXBreak, SIGNAL(clicked()), this, SLOT(addXBreak()) ); connect( ui.bRemoveXBreak, SIGNAL(clicked()), this, SLOT(removeXBreak()) ); connect( ui.cbXBreak, SIGNAL(currentIndexChanged(int)), this, SLOT(currentXBreakChanged(int)) ); connect( ui.leXBreakStart, SIGNAL(textChanged(QString)), this, SLOT(xBreakStartChanged()) ); connect( ui.leXBreakEnd, SIGNAL(textChanged(QString)), this, SLOT(xBreakEndChanged()) ); connect( ui.sbXBreakPosition, SIGNAL(valueChanged(int)), this, SLOT(xBreakPositionChanged(int)) ); connect( ui.cbXBreakStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(xBreakStyleChanged(int)) ); connect( ui.chkYBreak, SIGNAL(toggled(bool)), this, SLOT(toggleYBreak(bool)) ); connect( ui.bAddYBreak, SIGNAL(clicked()), this, SLOT(addYBreak()) ); connect( ui.bRemoveYBreak, SIGNAL(clicked()), this, SLOT(removeYBreak()) ); connect( ui.cbYBreak, SIGNAL(currentIndexChanged(int)), this, SLOT(currentYBreakChanged(int)) ); connect( ui.leYBreakStart, SIGNAL(textChanged(QString)), this, SLOT(yBreakStartChanged()) ); connect( ui.leYBreakEnd, SIGNAL(textChanged(QString)), this, SLOT(yBreakEndChanged()) ); connect( ui.sbYBreakPosition, SIGNAL(valueChanged(int)), this, SLOT(yBreakPositionChanged(int)) ); connect( ui.cbYBreakStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(yBreakStyleChanged(int)) ); //Background connect( ui.cbBackgroundType, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundTypeChanged(int)) ); connect( ui.cbBackgroundColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundColorStyleChanged(int)) ); connect( ui.cbBackgroundImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundImageStyleChanged(int)) ); connect( ui.cbBackgroundBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundBrushStyleChanged(int)) ); connect( ui.bOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile()) ); connect( ui.leBackgroundFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.leBackgroundFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbBackgroundFirstColor, SIGNAL(changed(QColor)), this, SLOT(backgroundFirstColorChanged(QColor)) ); connect( ui.kcbBackgroundSecondColor, SIGNAL(changed(QColor)), this, SLOT(backgroundSecondColorChanged(QColor)) ); connect( ui.sbBackgroundOpacity, SIGNAL(valueChanged(int)), this, SLOT(backgroundOpacityChanged(int)) ); //Border connect( ui.cbBorderStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(borderStyleChanged(int)) ); connect( ui.kcbBorderColor, SIGNAL(changed(QColor)), this, SLOT(borderColorChanged(QColor)) ); connect( ui.sbBorderWidth, SIGNAL(valueChanged(double)), this, SLOT(borderWidthChanged(double)) ); connect( ui.sbBorderCornerRadius, SIGNAL(valueChanged(double)), this, SLOT(borderCornerRadiusChanged(double)) ); connect( ui.sbBorderOpacity, SIGNAL(valueChanged(int)), this, SLOT(borderOpacityChanged(int)) ); //Padding connect( ui.sbPaddingHorizontal, SIGNAL(valueChanged(double)), this, SLOT(horizontalPaddingChanged(double)) ); connect( ui.sbPaddingVertical, SIGNAL(valueChanged(double)), this, SLOT(verticalPaddingChanged(double)) ); //theme and template handlers QFrame* frame = new QFrame(this); QHBoxLayout* layout = new QHBoxLayout(frame); m_themeHandler = new ThemeHandler(this); layout->addWidget(m_themeHandler); connect(m_themeHandler, SIGNAL(loadThemeRequested(QString)), this, SLOT(loadTheme(QString))); connect(m_themeHandler, SIGNAL(saveThemeRequested(KConfig&)), this, SLOT(saveTheme(KConfig&))); connect(m_themeHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); //connect(this, SIGNAL(saveThemeEnable(bool)), m_themeHandler, SLOT(saveThemeEnable(bool))); TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::CartesianPlot); layout->addWidget(templateHandler); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); ui.verticalLayout->addWidget(frame); //TODO: activate the tab again once the functionality is implemented ui.tabWidget->removeTab(2); init(); } CartesianPlotDock::~CartesianPlotDock() { } void CartesianPlotDock::init() { this->retranslateUi(); /* //TODO: activate later once range breaking is implemented //create icons for the different styles for scale breaking QPainter pa; pa.setPen( QPen(Qt::SolidPattern, 0) ); QPixmap pm(20, 20); ui.cbXBreakStyle->setIconSize( QSize(20,20) ); ui.cbYBreakStyle->setIconSize( QSize(20,20) ); //simple pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,8,10); pa.drawLine(12,10,17,10); pa.end(); ui.cbXBreakStyle->setItemIcon(0, pm); ui.cbYBreakStyle->setItemIcon(0, pm); //vertical pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,8,10); pa.drawLine(12,10,17,10); pa.drawLine(8,14,8,6); pa.drawLine(12,14,12,6); pa.end(); ui.cbXBreakStyle->setItemIcon(1, pm); ui.cbYBreakStyle->setItemIcon(1, pm); //sloped pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,8,10); pa.drawLine(12,10,17,10); pa.drawLine(6,14,10,6); pa.drawLine(10,14,14,6); pa.end(); ui.cbXBreakStyle->setItemIcon(2, pm); ui.cbYBreakStyle->setItemIcon(2, pm); */ } void CartesianPlotDock::setPlots(QList list) { m_initializing = true; m_plotList = list; m_plot = list.first(); QList labels; for (auto* plot: list) labels.append(plot->title()); labelWidget->setLabels(labels); //if there is more then one plot in the list, disable the name and comment fields in the tab "general" if (list.size() == 1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_plot->name()); ui.leComment->setText(m_plot->comment()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first plot this->load(); //update active widgets backgroundTypeChanged(ui.cbBackgroundType->currentIndex()); m_themeHandler->setCurrentTheme(m_plot->theme()); //Deactivate the geometry related widgets, if the worksheet layout is active. //Currently, a plot can only be a child of the worksheet itself, so we only need to ask the parent aspect (=worksheet). //TODO redesign this, if the hierarchy will be changend in future (a plot is a child of a new object group/container or so) Worksheet* w = dynamic_cast(m_plot->parentAspect()); if (w) { bool b = (w->layout() == Worksheet::NoLayout); ui.sbTop->setEnabled(b); ui.sbLeft->setEnabled(b); ui.sbWidth->setEnabled(b); ui.sbHeight->setEnabled(b); connect(w, SIGNAL(layoutChanged(Worksheet::Layout)), this, SLOT(layoutChanged(Worksheet::Layout))); } //SIGNALs/SLOTs connect( m_plot, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(plotDescriptionChanged(const AbstractAspect*)) ); connect( m_plot, SIGNAL(rectChanged(QRectF&)), this, SLOT(plotRectChanged(QRectF&)) ); connect( m_plot, SIGNAL(rangeTypeChanged(CartesianPlot::RangeType)), this, SLOT(plotRangeTypeChanged(CartesianPlot::RangeType)) ); connect( m_plot, SIGNAL(rangeFirstValuesChanged(int)), this, SLOT(plotRangeFirstValuesChanged(int)) ); connect( m_plot, SIGNAL(rangeLastValuesChanged(int)), this, SLOT(plotRangeLastValuesChanged(int)) ); connect( m_plot, SIGNAL(xAutoScaleChanged(bool)), this, SLOT(plotXAutoScaleChanged(bool)) ); connect( m_plot, SIGNAL(xMinChanged(double)), this, SLOT(plotXMinChanged(double)) ); connect( m_plot, SIGNAL(xMaxChanged(double)), this, SLOT(plotXMaxChanged(double)) ); connect( m_plot, SIGNAL(xScaleChanged(int)), this, SLOT(plotXScaleChanged(int)) ); connect( m_plot, SIGNAL(yAutoScaleChanged(bool)), this, SLOT(plotYAutoScaleChanged(bool)) ); connect( m_plot, SIGNAL(yMinChanged(double)), this, SLOT(plotYMinChanged(double)) ); connect( m_plot, SIGNAL(yMaxChanged(double)), this, SLOT(plotYMaxChanged(double)) ); connect( m_plot, SIGNAL(yScaleChanged(int)), this, SLOT(plotYScaleChanged(int)) ); connect( m_plot, SIGNAL(visibleChanged(bool)), this, SLOT(plotVisibleChanged(bool)) ); //range breaks connect( m_plot, SIGNAL(xRangeBreakingEnabledChanged(bool)), this, SLOT(plotXRangeBreakingEnabledChanged(bool)) ); connect( m_plot, SIGNAL(xRangeBreaksChanged(CartesianPlot::RangeBreaks)), this, SLOT(plotXRangeBreaksChanged(CartesianPlot::RangeBreaks)) ); connect( m_plot, SIGNAL(yRangeBreakingEnabledChanged(bool)), this, SLOT(plotYRangeBreakingEnabledChanged(bool)) ); connect( m_plot, SIGNAL(yRangeBreaksChanged(CartesianPlot::RangeBreaks)), this, SLOT(plotYRangeBreaksChanged(CartesianPlot::RangeBreaks)) ); // Plot Area connect( m_plot->plotArea(), SIGNAL(backgroundTypeChanged(PlotArea::BackgroundType)), this, SLOT(plotBackgroundTypeChanged(PlotArea::BackgroundType)) ); connect( m_plot->plotArea(), SIGNAL(backgroundColorStyleChanged(PlotArea::BackgroundColorStyle)), this, SLOT(plotBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle)) ); connect( m_plot->plotArea(), SIGNAL(backgroundImageStyleChanged(PlotArea::BackgroundImageStyle)), this, SLOT(plotBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle)) ); connect( m_plot->plotArea(), SIGNAL(backgroundBrushStyleChanged(Qt::BrushStyle)), this, SLOT(plotBackgroundBrushStyleChanged(Qt::BrushStyle)) ); connect( m_plot->plotArea(), SIGNAL(backgroundFirstColorChanged(QColor&)), this, SLOT(plotBackgroundFirstColorChanged(QColor&)) ); connect( m_plot->plotArea(), SIGNAL(backgroundSecondColorChanged(QColor&)), this, SLOT(plotBackgroundSecondColorChanged(QColor&)) ); connect( m_plot->plotArea(), SIGNAL(backgroundFileNameChanged(QString&)), this, SLOT(plotBackgroundFileNameChanged(QString&)) ); connect( m_plot->plotArea(), SIGNAL(backgroundOpacityChanged(float)), this, SLOT(plotBackgroundOpacityChanged(float)) ); connect( m_plot->plotArea(), SIGNAL(borderPenChanged(QPen&)), this, SLOT(plotBorderPenChanged(QPen&)) ); connect( m_plot->plotArea(), SIGNAL(borderOpacityChanged(float)), this, SLOT(plotBorderOpacityChanged(float)) ); connect( m_plot, SIGNAL(horizontalPaddingChanged(float)), this, SLOT(plotHorizontalPaddingChanged(float)) ); connect( m_plot, SIGNAL(verticalPaddingChanged(float)), this, SLOT(plotVerticalPaddingChanged(float)) ); m_initializing = false; } void CartesianPlotDock::activateTitleTab() { ui.tabWidget->setCurrentWidget(ui.tabTitle); } //************************************************************ //**** SLOTs for changes triggered in CartesianPlotDock ****** //************************************************************ void CartesianPlotDock::retranslateUi() { m_initializing = true; //general - ui.cbXScaling->addItem( i18n("linear") ); + ui.cbXScaling->addItem( i18n("Linear") ); ui.cbXScaling->addItem( i18n("log(x)") ); ui.cbXScaling->addItem( i18n("log2(x)") ); ui.cbXScaling->addItem( i18n("ln(x)") ); ui.cbYScaling->addItem( i18n("linear") ); ui.cbYScaling->addItem( i18n("log(y)") ); ui.cbYScaling->addItem( i18n("log2(y)") ); ui.cbYScaling->addItem( i18n("ln(y)") ); //scale breakings - ui.cbXBreakStyle->addItem( i18n("simple") ); - ui.cbXBreakStyle->addItem( i18n("vertical") ); - ui.cbXBreakStyle->addItem( i18n("sloped") ); + ui.cbXBreakStyle->addItem( i18n("Simple") ); + ui.cbXBreakStyle->addItem( i18n("Vertical") ); + ui.cbXBreakStyle->addItem( i18n("Sloped") ); - ui.cbYBreakStyle->addItem( i18n("simple") ); - ui.cbYBreakStyle->addItem( i18n("vertical") ); - ui.cbYBreakStyle->addItem( i18n("sloped") ); + ui.cbYBreakStyle->addItem( i18n("Simple") ); + ui.cbYBreakStyle->addItem( i18n("Vertical") ); + ui.cbYBreakStyle->addItem( i18n("Sloped") ); //plot area - ui.cbBackgroundType->addItem(i18n("color")); - ui.cbBackgroundType->addItem(i18n("image")); - ui.cbBackgroundType->addItem(i18n("pattern")); - - ui.cbBackgroundColorStyle->addItem(i18n("single color")); - ui.cbBackgroundColorStyle->addItem(i18n("horizontal gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("vertical gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from top left)")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from bottom left)")); - ui.cbBackgroundColorStyle->addItem(i18n("radial gradient")); - - ui.cbBackgroundImageStyle->addItem(i18n("scaled and cropped")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled, keep proportions")); - ui.cbBackgroundImageStyle->addItem(i18n("centered")); - ui.cbBackgroundImageStyle->addItem(i18n("tiled")); - ui.cbBackgroundImageStyle->addItem(i18n("center tiled")); + ui.cbBackgroundType->addItem(i18n("Color")); + ui.cbBackgroundType->addItem(i18n("Image")); + ui.cbBackgroundType->addItem(i18n("Pattern")); + + ui.cbBackgroundColorStyle->addItem(i18n("Single Color")); + ui.cbBackgroundColorStyle->addItem(i18n("Horizontal Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Vertical Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Top Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Bottom Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Radial Gradient")); + + ui.cbBackgroundImageStyle->addItem(i18n("Scaled and Cropped")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled, Keep Proportions")); + ui.cbBackgroundImageStyle->addItem(i18n("Centered")); + ui.cbBackgroundImageStyle->addItem(i18n("Tiled")); + ui.cbBackgroundImageStyle->addItem(i18n("Center Tiled")); GuiTools::updatePenStyles(ui.cbBorderStyle, Qt::black); GuiTools::updateBrushStyles(ui.cbBackgroundBrushStyle, Qt::SolidPattern); m_initializing = false; } // "General"-tab void CartesianPlotDock::nameChanged() { if (m_initializing) return; m_plot->setName(ui.leName->text()); } void CartesianPlotDock::commentChanged() { if (m_initializing) return; m_plot->setComment(ui.leComment->text()); } void CartesianPlotDock::visibilityChanged(bool state) { if (m_initializing) return; for (auto* plot: m_plotList) plot->setVisible(state); } void CartesianPlotDock::geometryChanged() { if (m_initializing) return; float x = Worksheet::convertToSceneUnits(ui.sbLeft->value(), Worksheet::Centimeter); float y = Worksheet::convertToSceneUnits(ui.sbTop->value(), Worksheet::Centimeter); float w = Worksheet::convertToSceneUnits(ui.sbWidth->value(), Worksheet::Centimeter); float h = Worksheet::convertToSceneUnits(ui.sbHeight->value(), Worksheet::Centimeter); QRectF rect(x,y,w,h); m_plot->setRect(rect); } /*! Called when the layout in the worksheet gets changed. Enables/disables the geometry widgets if the layout was deactivated/activated. Shows the new geometry values of the first plot if the layout was activated. */ void CartesianPlotDock::layoutChanged(Worksheet::Layout layout) { bool b = (layout == Worksheet::NoLayout); ui.sbTop->setEnabled(b); ui.sbLeft->setEnabled(b); ui.sbWidth->setEnabled(b); ui.sbHeight->setEnabled(b); } void CartesianPlotDock::rangeTypeChanged() { CartesianPlot::RangeType type; if (ui.rbRangeFirst->isChecked()) { ui.leRangeFirst->setEnabled(true); ui.leRangeLast->setEnabled(false); type = CartesianPlot::RangeFirst; } else if (ui.rbRangeLast->isChecked()) { ui.leRangeFirst->setEnabled(false); ui.leRangeLast->setEnabled(true); type = CartesianPlot::RangeLast; } else { ui.leRangeFirst->setEnabled(false); ui.leRangeLast->setEnabled(false); type = CartesianPlot::RangeFree; } if (m_initializing) return; for (auto* plot: m_plotList) plot->setRangeType(type); } void CartesianPlotDock::rangeFirstChanged(const QString& text) { if (m_initializing) return; const int value = text.toInt(); for (auto* plot: m_plotList) plot->setRangeFirstValues(value); } void CartesianPlotDock::rangeLastChanged(const QString & text) { if (m_initializing) return; const int value = text.toInt(); for (auto* plot: m_plotList) plot->setRangeLastValues(value); } void CartesianPlotDock::autoScaleXChanged(int state) { bool checked = (state==Qt::Checked); ui.leXMin->setEnabled(!checked); ui.leXMax->setEnabled(!checked); if (m_initializing) return; for (auto* plot: m_plotList) plot->setAutoScaleX(checked); } void CartesianPlotDock::xMinChanged() { if (m_initializing) return; float value = ui.leXMin->text().toDouble(); for (auto* plot: m_plotList) plot->setXMin(value); } void CartesianPlotDock::xMaxChanged() { if (m_initializing) return; float value = ui.leXMax->text().toDouble(); for (auto* plot: m_plotList) plot->setXMax(value); } /*! called on scale changes (linear, log) for the x-axis */ void CartesianPlotDock::xScaleChanged(int scale) { if (m_initializing) return; for (auto* plot: m_plotList) plot->setXScale((CartesianPlot::Scale) scale); } void CartesianPlotDock::autoScaleYChanged(int state) { bool checked = (state==Qt::Checked); ui.leYMin->setEnabled(!checked); ui.leYMax->setEnabled(!checked); if (m_initializing) return; for (auto* plot: m_plotList) plot->setAutoScaleY(checked); } void CartesianPlotDock::yMinChanged() { if (m_initializing) return; float value = ui.leYMin->text().toDouble(); for (auto* plot: m_plotList) plot->setYMin(value); } void CartesianPlotDock::yMaxChanged() { if (m_initializing) return; float value = ui.leYMax->text().toDouble(); for (auto* plot: m_plotList) plot->setYMax(value); } /*! called on scale changes (linear, log) for the y-axis */ void CartesianPlotDock::yScaleChanged(int index) { if (m_initializing) return; CartesianPlot::Scale scale = (CartesianPlot::Scale)index; for (auto* plot: m_plotList) plot->setYScale(scale); } // "Range Breaks"-tab // x-range breaks void CartesianPlotDock::toggleXBreak(bool b) { ui.frameXBreakEdit->setEnabled(b); ui.leXBreakStart->setEnabled(b); ui.leXBreakEnd->setEnabled(b); ui.sbXBreakPosition->setEnabled(b); ui.cbXBreakStyle->setEnabled(b); if (m_initializing) return; for (auto* plot: m_plotList) plot->setXRangeBreakingEnabled(b); } void CartesianPlotDock::addXBreak() { ui.bRemoveXBreak->setVisible(true); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); CartesianPlot::RangeBreak b; breaks.list<setXRangeBreaks(breaks); ui.cbXBreak->addItem(QString::number(ui.cbXBreak->count()+1)); ui.cbXBreak->setCurrentIndex(ui.cbXBreak->count()-1); } void CartesianPlotDock::removeXBreak() { ui.bRemoveXBreak->setVisible(m_plot->xRangeBreaks().list.size()>1); int index = ui.cbXBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); breaks.list.takeAt(index); breaks.lastChanged = -1; for (auto* plot: m_plotList) plot->setXRangeBreaks(breaks); ui.cbXBreak->clear(); for (int i = 1; i <= breaks.list.size(); ++i) ui.cbXBreak->addItem(QString::number(i)); if (index < ui.cbXBreak->count()-1) ui.cbXBreak->setCurrentIndex(index); else ui.cbXBreak->setCurrentIndex(ui.cbXBreak->count()-1); ui.bRemoveXBreak->setVisible(ui.cbXBreak->count()!=1); } void CartesianPlotDock::currentXBreakChanged(int index) { if (m_initializing) return; if (index == -1) return; m_initializing = true; const CartesianPlot::RangeBreak rangeBreak = m_plot->xRangeBreaks().list.at(index); QString str = std::isnan(rangeBreak.start) ? "" : QString::number(rangeBreak.start); ui.leXBreakStart->setText(str); str = std::isnan(rangeBreak.end) ? "" : QString::number(rangeBreak.end); ui.leXBreakEnd->setText(str); ui.sbXBreakPosition->setValue(rangeBreak.position*100); ui.cbXBreakStyle->setCurrentIndex((int)rangeBreak.style); m_initializing = false; } void CartesianPlotDock::xBreakStartChanged() { if (m_initializing) return; int index = ui.cbXBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); breaks.list[index].start = ui.leXBreakStart->text().toDouble(); breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setXRangeBreaks(breaks); } void CartesianPlotDock::xBreakEndChanged() { if (m_initializing) return; int index = ui.cbXBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); breaks.list[index].end = ui.leXBreakEnd->text().toDouble(); breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setXRangeBreaks(breaks); } void CartesianPlotDock::xBreakPositionChanged(int value) { if (m_initializing) return; int index = ui.cbXBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); breaks.list[index].position = (float)value/100.; breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setXRangeBreaks(breaks); } void CartesianPlotDock::xBreakStyleChanged(int styleIndex) { if (m_initializing) return; int index = ui.cbXBreak->currentIndex(); CartesianPlot::RangeBreakStyle style = CartesianPlot::RangeBreakStyle(styleIndex); CartesianPlot::RangeBreaks breaks = m_plot->xRangeBreaks(); breaks.list[index].style = style; breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setXRangeBreaks(breaks); } // y-range breaks void CartesianPlotDock::toggleYBreak(bool b) { ui.frameYBreakEdit->setEnabled(b); ui.leYBreakStart->setEnabled(b); ui.leYBreakEnd->setEnabled(b); ui.sbYBreakPosition->setEnabled(b); ui.cbYBreakStyle->setEnabled(b); if (m_initializing) return; for (auto* plot: m_plotList) plot->setYRangeBreakingEnabled(b); } void CartesianPlotDock::addYBreak() { ui.bRemoveYBreak->setVisible(true); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); CartesianPlot::RangeBreak b; breaks.list << b; breaks.lastChanged = breaks.list.size() - 1; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); ui.cbYBreak->addItem(QString::number(ui.cbYBreak->count()+1)); ui.cbYBreak->setCurrentIndex(ui.cbYBreak->count()-1); } void CartesianPlotDock::removeYBreak() { ui.bRemoveYBreak->setVisible(m_plot->yRangeBreaks().list.size()>1); int index = ui.cbYBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); breaks.list.takeAt(index); breaks.lastChanged = -1; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); ui.cbYBreak->clear(); for (int i = 1; i <= breaks.list.size(); ++i) ui.cbYBreak->addItem(QString::number(i)); if (index < ui.cbYBreak->count()-1) ui.cbYBreak->setCurrentIndex(index); else ui.cbYBreak->setCurrentIndex(ui.cbYBreak->count()-1); ui.bRemoveYBreak->setVisible(ui.cbYBreak->count() != 1); } void CartesianPlotDock::currentYBreakChanged(int index) { if (m_initializing) return; if (index == -1) return; m_initializing = true; const CartesianPlot::RangeBreak rangeBreak = m_plot->yRangeBreaks().list.at(index); QString str = std::isnan(rangeBreak.start) ? "" : QString::number(rangeBreak.start); ui.leYBreakStart->setText(str); str = std::isnan(rangeBreak.end) ? "" : QString::number(rangeBreak.end); ui.leYBreakEnd->setText(str); ui.sbYBreakPosition->setValue(rangeBreak.position*100); ui.cbYBreakStyle->setCurrentIndex((int)rangeBreak.style); m_initializing = false; } void CartesianPlotDock::yBreakStartChanged() { if (m_initializing) return; int index = ui.cbYBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); breaks.list[index].start = ui.leYBreakStart->text().toDouble(); breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); } void CartesianPlotDock::yBreakEndChanged() { if (m_initializing) return; int index = ui.cbYBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); breaks.list[index].end = ui.leYBreakEnd->text().toDouble(); breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); } void CartesianPlotDock::yBreakPositionChanged(int value) { if (m_initializing) return; int index = ui.cbYBreak->currentIndex(); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); breaks.list[index].position = (float)value/100.; breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); } void CartesianPlotDock::yBreakStyleChanged(int styleIndex) { if (m_initializing) return; int index = ui.cbYBreak->currentIndex(); CartesianPlot::RangeBreakStyle style = CartesianPlot::RangeBreakStyle(styleIndex); CartesianPlot::RangeBreaks breaks = m_plot->yRangeBreaks(); breaks.list[index].style = style; breaks.lastChanged = index; for (auto* plot: m_plotList) plot->setYRangeBreaks(breaks); } // "Plot area"-tab void CartesianPlotDock::backgroundTypeChanged(int index) { PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color) { ui.lBackgroundColorStyle->show(); ui.cbBackgroundColorStyle->show(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle) ui.cbBackgroundColorStyle->currentIndex(); if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } } else if (type == PlotArea::Image) { ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->show(); ui.cbBackgroundImageStyle->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->show(); ui.leBackgroundFileName->show(); ui.bOpen->show(); ui.lBackgroundFirstColor->hide(); ui.kcbBackgroundFirstColor->hide(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else if (type == PlotArea::Pattern) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->show(); ui.cbBackgroundBrushStyle->show(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } if (m_initializing) return; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundType(type); } void CartesianPlotDock::backgroundColorStyleChanged(int index) { PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); } if (m_initializing) return; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundColorStyle(style); } void CartesianPlotDock::backgroundImageStyleChanged(int index) { if (m_initializing) return; PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundImageStyle(style); } void CartesianPlotDock::backgroundBrushStyleChanged(int index) { if (m_initializing) return; Qt::BrushStyle style = (Qt::BrushStyle)index; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundBrushStyle(style); } void CartesianPlotDock::backgroundFirstColorChanged(const QColor& c) { if (m_initializing) return; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundFirstColor(c); } void CartesianPlotDock::backgroundSecondColorChanged(const QColor& c) { if (m_initializing) return; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundSecondColor(c); } /*! opens a file dialog and lets the user select the image file. */ void CartesianPlotDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "CartesianPlotDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const auto& format: QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastImageDir", newDir); } ui.leBackgroundFileName->setText( path ); for (auto* plot: m_plotList) plot->plotArea()->setBackgroundFileName(path); } void CartesianPlotDock::fileNameChanged() { if (m_initializing) return; QString fileName = ui.leBackgroundFileName->text(); if (!fileName.isEmpty() && !QFile::exists(fileName)) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); for (auto* plot: m_plotList) plot->plotArea()->setBackgroundFileName(fileName); } void CartesianPlotDock::backgroundOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* plot: m_plotList) plot->plotArea()->setBackgroundOpacity(opacity); } // "Border"-tab void CartesianPlotDock::borderStyleChanged(int index) { if (m_initializing) return; Qt::PenStyle penStyle = Qt::PenStyle(index); QPen pen; for (auto* plot: m_plotList) { pen = plot->plotArea()->borderPen(); pen.setStyle(penStyle); plot->plotArea()->setBorderPen(pen); } } void CartesianPlotDock::borderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* plot: m_plotList) { pen = plot->plotArea()->borderPen(); pen.setColor(color); plot->plotArea()->setBorderPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, color); m_initializing = false; } void CartesianPlotDock::borderWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* plot: m_plotList) { pen = plot->plotArea()->borderPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); plot->plotArea()->setBorderPen(pen); } } void CartesianPlotDock::borderCornerRadiusChanged(double value) { if (m_initializing) return; for (auto* plot: m_plotList) plot->plotArea()->setBorderCornerRadius(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); } void CartesianPlotDock::borderOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* plot: m_plotList) plot->plotArea()->setBorderOpacity(opacity); } void CartesianPlotDock::horizontalPaddingChanged(double value) { if (m_initializing) return; for (auto* plot: m_plotList) plot->setHorizontalPadding(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); } void CartesianPlotDock::verticalPaddingChanged(double value) { if (m_initializing) return; for (auto* plot: m_plotList) plot->setVerticalPadding(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); } //************************************************************* //****** SLOTs for changes triggered in CartesianPlot ********* //************************************************************* //general void CartesianPlotDock::plotDescriptionChanged(const AbstractAspect* aspect) { if (m_plot != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) ui.leName->setText(aspect->name()); else if (aspect->comment() != ui.leComment->text()) ui.leComment->setText(aspect->comment()); m_initializing = false; } void CartesianPlotDock::plotRectChanged(QRectF& rect) { m_initializing = true; ui.sbLeft->setValue(Worksheet::convertFromSceneUnits(rect.x(), Worksheet::Centimeter)); ui.sbTop->setValue(Worksheet::convertFromSceneUnits(rect.y(), Worksheet::Centimeter)); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(rect.width(), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(rect.height(), Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotDock::plotRangeTypeChanged(CartesianPlot::RangeType type) { m_initializing = true; switch (type) { case CartesianPlot::RangeFree: ui.rbRangeFree->setChecked(true); break; case CartesianPlot::RangeFirst: ui.rbRangeFirst->setChecked(true); break; case CartesianPlot::RangeLast: ui.rbRangeLast->setChecked(true); break; } m_initializing = false; } void CartesianPlotDock::plotRangeFirstValuesChanged(int value) { m_initializing = true; ui.leRangeFirst->setText(QString::number(value)); m_initializing = false; } void CartesianPlotDock::plotRangeLastValuesChanged(int value) { m_initializing = true; ui.leRangeLast->setText(QString::number(value)); m_initializing = false; } void CartesianPlotDock::plotXAutoScaleChanged(bool value) { m_initializing = true; ui.chkAutoScaleX->setChecked(value); m_initializing = false; } void CartesianPlotDock::plotXMinChanged(double value) { m_initializing = true; ui.leXMin->setText( QString::number(value) ); m_initializing = false; } void CartesianPlotDock::plotXMaxChanged(double value) { m_initializing = true; ui.leXMax->setText( QString::number(value) ); m_initializing = false; } void CartesianPlotDock::plotXScaleChanged(int scale) { m_initializing = true; ui.cbXScaling->setCurrentIndex( scale ); m_initializing = false; } void CartesianPlotDock::plotYAutoScaleChanged(bool value) { m_initializing = true; ui.chkAutoScaleY->setChecked(value); m_initializing = false; } void CartesianPlotDock::plotYMinChanged(double value) { m_initializing = true; ui.leYMin->setText( QString::number(value) ); m_initializing = false; } void CartesianPlotDock::plotYMaxChanged(double value) { m_initializing = true; ui.leYMax->setText( QString::number(value) ); m_initializing = false; } void CartesianPlotDock::plotYScaleChanged(int scale) { m_initializing = true; ui.cbYScaling->setCurrentIndex( scale ); m_initializing = false; } void CartesianPlotDock::plotVisibleChanged(bool on) { m_initializing = true; ui.chkVisible->setChecked(on); m_initializing = false; } //range breaks void CartesianPlotDock::plotXRangeBreakingEnabledChanged(bool on) { m_initializing = true; ui.chkXBreak->setChecked(on); m_initializing = false; } void CartesianPlotDock::plotXRangeBreaksChanged(const CartesianPlot::RangeBreaks& breaks) { Q_UNUSED(breaks); } void CartesianPlotDock::plotYRangeBreakingEnabledChanged(bool on) { m_initializing = true; ui.chkYBreak->setChecked(on); m_initializing = false; } void CartesianPlotDock::plotYRangeBreaksChanged(const CartesianPlot::RangeBreaks& breaks) { Q_UNUSED(breaks); } //background void CartesianPlotDock::plotBackgroundTypeChanged(PlotArea::BackgroundType type) { m_initializing = true; ui.cbBackgroundType->setCurrentIndex(type); m_initializing = false; } void CartesianPlotDock::plotBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle style) { m_initializing = true; ui.cbBackgroundColorStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotDock::plotBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle style) { m_initializing = true; ui.cbBackgroundImageStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotDock::plotBackgroundBrushStyleChanged(Qt::BrushStyle style) { m_initializing = true; ui.cbBackgroundBrushStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotDock::plotBackgroundFirstColorChanged(QColor& color) { m_initializing = true; ui.kcbBackgroundFirstColor->setColor(color); m_initializing = false; } void CartesianPlotDock::plotBackgroundSecondColorChanged(QColor& color) { m_initializing = true; ui.kcbBackgroundSecondColor->setColor(color); m_initializing = false; } void CartesianPlotDock::plotBackgroundFileNameChanged(QString& filename) { m_initializing = true; ui.leBackgroundFileName->setText(filename); m_initializing = false; } void CartesianPlotDock::plotBackgroundOpacityChanged(float opacity) { m_initializing = true; ui.sbBackgroundOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void CartesianPlotDock::plotBorderPenChanged(QPen& pen) { m_initializing = true; if (ui.cbBorderStyle->currentIndex() != pen.style()) ui.cbBorderStyle->setCurrentIndex(pen.style()); if (ui.kcbBorderColor->color() != pen.color()) ui.kcbBorderColor->setColor(pen.color()); if (ui.sbBorderWidth->value() != pen.widthF()) ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void CartesianPlotDock::plotBorderCornerRadiusChanged(float value) { m_initializing = true; ui.sbBorderCornerRadius->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotDock::plotBorderOpacityChanged(float value) { m_initializing = true; float v = (float)value*100.; ui.sbBorderOpacity->setValue(v); m_initializing = false; } void CartesianPlotDock::plotHorizontalPaddingChanged(float value) { m_initializing = true; ui.sbPaddingHorizontal->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotDock::plotVerticalPaddingChanged(float value) { m_initializing = true; ui.sbPaddingVertical->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } //************************************************************* //******************** SETTINGS ******************************* //************************************************************* void CartesianPlotDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index != -1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_plotList.size(); if (size > 1) m_plot->beginMacro(i18n("%1 cartesian plots: template \"%2\" loaded", size, name)); else m_plot->beginMacro(i18n("%1: template \"%2\" loaded", m_plot->name(), name)); this->loadConfig(config); m_plot->endMacro(); } void CartesianPlotDock::load() { //General-tab ui.chkVisible->setChecked(m_plot->isVisible()); ui.sbLeft->setValue(Worksheet::convertFromSceneUnits(m_plot->rect().x(), Worksheet::Centimeter)); ui.sbTop->setValue(Worksheet::convertFromSceneUnits(m_plot->rect().y(), Worksheet::Centimeter)); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(m_plot->rect().width(), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(m_plot->rect().height(), Worksheet::Centimeter)); switch (m_plot->rangeType()) { case CartesianPlot::RangeFree: ui.rbRangeFree->setChecked(true); break; case CartesianPlot::RangeFirst: ui.rbRangeFirst->setChecked(true); break; case CartesianPlot::RangeLast: ui.rbRangeLast->setChecked(true); break; } rangeTypeChanged(); ui.leRangeFirst->setText( QString::number(m_plot->rangeFirstValues()) ); ui.leRangeLast->setText( QString::number(m_plot->rangeLastValues()) ); ui.chkAutoScaleX->setChecked(m_plot->autoScaleX()); ui.leXMin->setText( QString::number(m_plot->xMin()) ); ui.leXMax->setText( QString::number(m_plot->xMax()) ); ui.cbXScaling->setCurrentIndex( (int) m_plot->xScale() ); ui.chkAutoScaleY->setChecked(m_plot->autoScaleY()); ui.leYMin->setText( QString::number(m_plot->yMin()) ); ui.leYMax->setText( QString::number(m_plot->yMax()) ); ui.cbYScaling->setCurrentIndex( (int)m_plot->yScale() ); //Title labelWidget->load(); //x-range breaks, show the first break ui.chkXBreak->setChecked(m_plot->xRangeBreakingEnabled()); this->toggleXBreak(m_plot->xRangeBreakingEnabled()); ui.bRemoveXBreak->setVisible(m_plot->xRangeBreaks().list.size()>1); ui.cbXBreak->clear(); if (!m_plot->xRangeBreaks().list.isEmpty()) { for (int i = 1; i <= m_plot->xRangeBreaks().list.size(); ++i) ui.cbXBreak->addItem(QString::number(i)); } else ui.cbXBreak->addItem("1"); ui.cbXBreak->setCurrentIndex(0); //y-range breaks, show the first break ui.chkYBreak->setChecked(m_plot->yRangeBreakingEnabled()); this->toggleYBreak(m_plot->yRangeBreakingEnabled()); ui.bRemoveYBreak->setVisible(m_plot->yRangeBreaks().list.size()>1); ui.cbYBreak->clear(); if (!m_plot->yRangeBreaks().list.isEmpty()) { for (int i = 1; i <= m_plot->yRangeBreaks().list.size(); ++i) ui.cbYBreak->addItem(QString::number(i)); } else ui.cbYBreak->addItem("1"); ui.cbYBreak->setCurrentIndex(0); //"Plot Area"-tab //Background ui.cbBackgroundType->setCurrentIndex( (int)m_plot->plotArea()->backgroundType() ); ui.cbBackgroundColorStyle->setCurrentIndex( (int) m_plot->plotArea()->backgroundColorStyle() ); ui.cbBackgroundImageStyle->setCurrentIndex( (int) m_plot->plotArea()->backgroundImageStyle() ); ui.cbBackgroundBrushStyle->setCurrentIndex( (int) m_plot->plotArea()->backgroundBrushStyle() ); ui.leBackgroundFileName->setText( m_plot->plotArea()->backgroundFileName() ); ui.kcbBackgroundFirstColor->setColor( m_plot->plotArea()->backgroundFirstColor() ); ui.kcbBackgroundSecondColor->setColor( m_plot->plotArea()->backgroundSecondColor() ); ui.sbBackgroundOpacity->setValue( round(m_plot->plotArea()->backgroundOpacity()*100.0) ); //highlight the text field for the background image red if an image is used and cannot be found if (!m_plot->plotArea()->backgroundFileName().isEmpty() && !QFile::exists(m_plot->plotArea()->backgroundFileName())) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); //Padding ui.sbPaddingHorizontal->setValue( Worksheet::convertFromSceneUnits(m_plot->horizontalPadding(), Worksheet::Centimeter) ); ui.sbPaddingVertical->setValue( Worksheet::convertFromSceneUnits(m_plot->verticalPadding(), Worksheet::Centimeter) ); //Border ui.kcbBorderColor->setColor( m_plot->plotArea()->borderPen().color() ); ui.cbBorderStyle->setCurrentIndex( (int) m_plot->plotArea()->borderPen().style() ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_plot->plotArea()->borderPen().widthF(), Worksheet::Point) ); ui.sbBorderCornerRadius->setValue( Worksheet::convertFromSceneUnits(m_plot->plotArea()->borderCornerRadius(), Worksheet::Centimeter) ); ui.sbBorderOpacity->setValue( round(m_plot->plotArea()->borderOpacity()*100) ); GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); } void CartesianPlotDock::loadConfig(KConfig& config) { KConfigGroup group = config.group("CartesianPlot"); //General //we don't load/save the settings in the general-tab, since they are not style related. //It doesn't make sense to load/save them in the template. //This data is read in CartesianPlotDock::setPlots(). //Title KConfigGroup plotTitleGroup = config.group("CartesianPlotTitle"); labelWidget->loadConfig(plotTitleGroup); //Scale breakings //TODO //Background-tab ui.cbBackgroundType->setCurrentIndex( group.readEntry("BackgroundType", (int) m_plot->plotArea()->backgroundType()) ); ui.cbBackgroundColorStyle->setCurrentIndex( group.readEntry("BackgroundColorStyle", (int) m_plot->plotArea()->backgroundColorStyle()) ); ui.cbBackgroundImageStyle->setCurrentIndex( group.readEntry("BackgroundImageStyle", (int) m_plot->plotArea()->backgroundImageStyle()) ); ui.cbBackgroundBrushStyle->setCurrentIndex( group.readEntry("BackgroundBrushStyle", (int) m_plot->plotArea()->backgroundBrushStyle()) ); ui.leBackgroundFileName->setText( group.readEntry("BackgroundFileName", m_plot->plotArea()->backgroundFileName()) ); ui.kcbBackgroundFirstColor->setColor( group.readEntry("BackgroundFirstColor", m_plot->plotArea()->backgroundFirstColor()) ); ui.kcbBackgroundSecondColor->setColor( group.readEntry("BackgroundSecondColor", m_plot->plotArea()->backgroundSecondColor()) ); ui.sbBackgroundOpacity->setValue( round(group.readEntry("BackgroundOpacity", m_plot->plotArea()->backgroundOpacity())*100.0) ); ui.sbPaddingHorizontal->setValue(Worksheet::convertFromSceneUnits(group.readEntry("HorizontalPadding", m_plot->horizontalPadding()), Worksheet::Centimeter)); ui.sbPaddingVertical->setValue(Worksheet::convertFromSceneUnits(group.readEntry("VerticalPadding", m_plot->verticalPadding()), Worksheet::Centimeter)); //Border-tab ui.kcbBorderColor->setColor( group.readEntry("BorderColor", m_plot->plotArea()->borderPen().color()) ); ui.cbBorderStyle->setCurrentIndex( group.readEntry("BorderStyle", (int) m_plot->plotArea()->borderPen().style()) ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_plot->plotArea()->borderPen().widthF()), Worksheet::Point) ); ui.sbBorderCornerRadius->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderCornerRadius", m_plot->plotArea()->borderCornerRadius()), Worksheet::Centimeter) ); ui.sbBorderOpacity->setValue( group.readEntry("BorderOpacity", m_plot->plotArea()->borderOpacity())*100 ); m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); m_initializing = false; } void CartesianPlotDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group("CartesianPlot"); //General //we don't load/save the settings in the general-tab, since they are not style related. //It doesn't make sense to load/save them in the template. //Title KConfigGroup plotTitleGroup = config.group("CartesianPlotTitle"); labelWidget->saveConfig(plotTitleGroup); //Scale breakings //TODO //Background group.writeEntry("BackgroundType", ui.cbBackgroundType->currentIndex()); group.writeEntry("BackgroundColorStyle", ui.cbBackgroundColorStyle->currentIndex()); group.writeEntry("BackgroundImageStyle", ui.cbBackgroundImageStyle->currentIndex()); group.writeEntry("BackgroundBrushStyle", ui.cbBackgroundBrushStyle->currentIndex()); group.writeEntry("BackgroundFileName", ui.leBackgroundFileName->text()); group.writeEntry("BackgroundFirstColor", ui.kcbBackgroundFirstColor->color()); group.writeEntry("BackgroundSecondColor", ui.kcbBackgroundSecondColor->color()); group.writeEntry("BackgroundOpacity", ui.sbBackgroundOpacity->value()/100.0); group.writeEntry("HorizontalPadding", Worksheet::convertToSceneUnits(ui.sbPaddingHorizontal->value(), Worksheet::Centimeter)); group.writeEntry("VerticalPadding", Worksheet::convertToSceneUnits(ui.sbPaddingVertical->value(), Worksheet::Centimeter)); //Border group.writeEntry("BorderStyle", ui.cbBorderStyle->currentIndex()); group.writeEntry("BorderColor", ui.kcbBorderColor->color()); group.writeEntry("BorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Point)); group.writeEntry("BorderCornerRadius", Worksheet::convertToSceneUnits(ui.sbBorderCornerRadius->value(), Worksheet::Centimeter)); group.writeEntry("BorderOpacity", ui.sbBorderOpacity->value()/100.0); config.sync(); } void CartesianPlotDock::loadTheme(const QString& theme) { for (auto* plot: m_plotList) plot->setTheme(theme); } void CartesianPlotDock::saveTheme(KConfig& config) const { if(!m_plotList.isEmpty()) m_plotList.at(0)->saveTheme(config); } diff --git a/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.cpp b/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.cpp index 5b9e5e093..0d1c3bb37 100644 --- a/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.cpp +++ b/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.cpp @@ -1,1051 +1,1052 @@ /*************************************************************************** File : CartesianPlotLegendDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2013-2016 by Alexander Semke (alexander.semke@web.de) Description : widget for cartesian plot legend properties ***************************************************************************/ /*************************************************************************** * * * 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 "CartesianPlotLegendDock.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/Worksheet.h" #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/TemplateHandler.h" #include #include #include #include #include #include +#include /*! \class CartesianPlotLegendDock \brief Provides a widget for editing the properties of the cartesian plot legend currently selected in the project explorer. \ingroup kdefrontend */ CartesianPlotLegendDock::CartesianPlotLegendDock(QWidget* parent) : QWidget(parent), m_legend(nullptr), labelWidget(nullptr), m_initializing(false) { ui.setupUi(this); //"Title"-tab QHBoxLayout* hboxLayout = new QHBoxLayout(ui.tabTitle); labelWidget = new LabelWidget(ui.tabTitle); labelWidget->setNoGeometryMode(true); hboxLayout->addWidget(labelWidget); hboxLayout->setContentsMargins(2,2,2,2); hboxLayout->setSpacing(2); //"Background"-tab ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leBackgroundFileName->setCompleter(completer); //adjust layouts in the tabs for (int i=0; icount(); ++i) { QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //SIGNAL/SLOT //General connect(ui.leName, &QLineEdit::textChanged, this, &CartesianPlotLegendDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &CartesianPlotLegendDock::commentChanged); connect( ui.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( ui.kfrLabelFont, SIGNAL(fontSelected(QFont)), this, SLOT(labelFontChanged(QFont)) ); connect( ui.kcbLabelColor, SIGNAL(changed(QColor)), this, SLOT(labelColorChanged(QColor)) ); connect( ui.cbOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(labelOrderChanged(int)) ); connect( ui.sbLineSymbolWidth, SIGNAL(valueChanged(double)), this, SLOT(lineSymbolWidthChanged(double)) ); connect( ui.cbPositionX, SIGNAL(currentIndexChanged(int)), this, SLOT(positionXChanged(int)) ); connect( ui.cbPositionY, SIGNAL(currentIndexChanged(int)), this, SLOT(positionYChanged(int)) ); connect( ui.sbPositionX, SIGNAL(valueChanged(double)), this, SLOT(customPositionXChanged(double)) ); connect( ui.sbPositionY, SIGNAL(valueChanged(double)), this, SLOT(customPositionYChanged(double)) ); //Background connect( ui.cbBackgroundType, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundTypeChanged(int)) ); connect( ui.cbBackgroundColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundColorStyleChanged(int)) ); connect( ui.cbBackgroundImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundImageStyleChanged(int)) ); connect( ui.cbBackgroundBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundBrushStyleChanged(int)) ); connect( ui.bOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile()) ); connect( ui.leBackgroundFileName, SIGNAL(returnPressed()), this, SLOT(fileNameChanged()) ); connect( ui.leBackgroundFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbBackgroundFirstColor, SIGNAL(changed(QColor)), this, SLOT(backgroundFirstColorChanged(QColor)) ); connect( ui.kcbBackgroundSecondColor, SIGNAL(changed(QColor)), this, SLOT(backgroundSecondColorChanged(QColor)) ); connect( ui.sbBackgroundOpacity, SIGNAL(valueChanged(int)), this, SLOT(backgroundOpacityChanged(int)) ); //Border connect( ui.cbBorderStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(borderStyleChanged(int)) ); connect( ui.kcbBorderColor, SIGNAL(changed(QColor)), this, SLOT(borderColorChanged(QColor)) ); connect( ui.sbBorderWidth, SIGNAL(valueChanged(double)), this, SLOT(borderWidthChanged(double)) ); connect( ui.sbBorderCornerRadius, SIGNAL(valueChanged(double)), this, SLOT(borderCornerRadiusChanged(double)) ); connect( ui.sbBorderOpacity, SIGNAL(valueChanged(int)), this, SLOT(borderOpacityChanged(int)) ); //Layout connect( ui.sbLayoutTopMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutTopMarginChanged(double)) ); connect( ui.sbLayoutBottomMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutBottomMarginChanged(double)) ); connect( ui.sbLayoutLeftMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutLeftMarginChanged(double)) ); connect( ui.sbLayoutRightMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutRightMarginChanged(double)) ); connect( ui.sbLayoutHorizontalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutHorizontalSpacingChanged(double)) ); connect( ui.sbLayoutVerticalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutVerticalSpacingChanged(double)) ); connect( ui.sbLayoutColumnCount, SIGNAL(valueChanged(int)), this, SLOT(layoutColumnCountChanged(int)) ); TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::CartesianPlotLegend); ui.verticalLayout->addWidget(templateHandler); templateHandler->show(); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); init(); } void CartesianPlotLegendDock::init() { this->retranslateUi(); } void CartesianPlotLegendDock::setLegends(QList list) { m_initializing = true; m_legendList = list; m_legend=list.first(); //if there is more then one legend in the list, disable the tab "general" if (list.size()==1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_legend->name()); ui.leComment->setText(m_legend->comment()); }else{ ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first curve this->load(); //on the very first start the column count shown in UI is 1. //if the this count for m_legend is also 1 then the slot layoutColumnCountChanged is not called //and we need to disable the "order" widgets here. ui.lOrder->setVisible(m_legend->layoutColumnCount()!=1); ui.cbOrder->setVisible(m_legend->layoutColumnCount()!=1); //legend title QList labels; for (auto* legend : list) labels.append(legend->title()); labelWidget->setLabels(labels); //update active widgets backgroundTypeChanged(ui.cbBackgroundType->currentIndex()); //SIGNALs/SLOTs //General connect( m_legend, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(legendDescriptionChanged(const AbstractAspect*)) ); connect( m_legend, SIGNAL(labelFontChanged(QFont&)), this, SLOT(legendLabelFontChanged(QFont&)) ); connect( m_legend, SIGNAL(labelColorChanged(QColor&)), this, SLOT(legendLabelColorChanged(QColor&)) ); connect( m_legend, SIGNAL(labelColumnMajorChanged(bool)), this, SLOT(legendLabelOrderChanged(bool)) ); connect( m_legend, SIGNAL(positionChanged(CartesianPlotLegend::PositionWrapper)), this, SLOT(legendPositionChanged(CartesianPlotLegend::PositionWrapper)) ); connect( m_legend, SIGNAL(lineSymbolWidthChanged(float)), this, SLOT(legendLineSymbolWidthChanged(float)) ); connect(m_legend, SIGNAL(visibilityChanged(bool)), this, SLOT(legendVisibilityChanged(bool))); //background connect( m_legend, SIGNAL(backgroundTypeChanged(PlotArea::BackgroundType)), this, SLOT(legendBackgroundTypeChanged(PlotArea::BackgroundType)) ); connect( m_legend, SIGNAL(backgroundColorStyleChanged(PlotArea::BackgroundColorStyle)), this, SLOT(legendBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle)) ); connect( m_legend, SIGNAL(backgroundImageStyleChanged(PlotArea::BackgroundImageStyle)), this, SLOT(legendBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle)) ); connect( m_legend, SIGNAL(backgroundBrushStyleChanged(Qt::BrushStyle)), this, SLOT(legendBackgroundBrushStyleChanged(Qt::BrushStyle)) ); connect( m_legend, SIGNAL(backgroundFirstColorChanged(QColor&)), this, SLOT(legendBackgroundFirstColorChanged(QColor&)) ); connect( m_legend, SIGNAL(backgroundSecondColorChanged(QColor&)), this, SLOT(legendBackgroundSecondColorChanged(QColor&)) ); connect( m_legend, SIGNAL(backgroundFileNameChanged(QString&)), this, SLOT(legendBackgroundFileNameChanged(QString&)) ); connect( m_legend, SIGNAL(backgroundOpacityChanged(float)), this, SLOT(legendBackgroundOpacityChanged(float)) ); connect( m_legend, SIGNAL(borderPenChanged(QPen&)), this, SLOT(legendBorderPenChanged(QPen&)) ); connect( m_legend, SIGNAL(borderCornerRadiusChanged(float)), this, SLOT(legendBorderCornerRadiusChanged(float)) ); connect( m_legend, SIGNAL(borderOpacityChanged(float)), this, SLOT(legendBorderOpacityChanged(float)) ); //layout connect(m_legend,SIGNAL(layoutTopMarginChanged(float)),this,SLOT(legendLayoutTopMarginChanged(float))); connect(m_legend,SIGNAL(layoutBottomMarginChanged(float)),this,SLOT(legendLayoutBottomMarginChanged(float))); connect(m_legend,SIGNAL(layoutLeftMarginChanged(float)),this,SLOT(legendLayoutLeftMarginChanged(float))); connect(m_legend,SIGNAL(layoutRightMarginChanged(float)),this,SLOT(legendLayoutRightMarginChanged(float))); connect(m_legend,SIGNAL(layoutVerticalSpacingChanged(float)),this,SLOT(legendLayoutVerticalSpacingChanged(float))); connect(m_legend,SIGNAL(layoutHorizontalSpacingChanged(float)),this,SLOT(legendLayoutHorizontalSpacingChanged(float))); connect(m_legend,SIGNAL(layoutColumnCountChanged(int)),this,SLOT(legendLayoutColumnCountChanged(int))); m_initializing = false; } void CartesianPlotLegendDock::activateTitleTab() const{ ui.tabWidget->setCurrentWidget(ui.tabTitle); } //************************************************************ //** SLOTs for changes triggered in CartesianPlotLegendDock ** //************************************************************ void CartesianPlotLegendDock::retranslateUi() { m_initializing = true; - ui.cbBackgroundType->addItem(i18n("color")); - ui.cbBackgroundType->addItem(i18n("image")); - ui.cbBackgroundType->addItem(i18n("pattern")); - - ui.cbBackgroundColorStyle->addItem(i18n("single color")); - ui.cbBackgroundColorStyle->addItem(i18n("horizontal gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("vertical gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from top left)")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from bottom left)")); - ui.cbBackgroundColorStyle->addItem(i18n("radial gradient")); - - ui.cbBackgroundImageStyle->addItem(i18n("scaled and cropped")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled, keep proportions")); - ui.cbBackgroundImageStyle->addItem(i18n("centered")); - ui.cbBackgroundImageStyle->addItem(i18n("tiled")); - ui.cbBackgroundImageStyle->addItem(i18n("center tiled")); - - ui.cbOrder->addItem(i18n("column major")); - ui.cbOrder->addItem(i18n("row major")); - - ui.cbPositionX->addItem(i18n("left")); - ui.cbPositionX->addItem(i18n("center")); - ui.cbPositionX->addItem(i18n("right")); - ui.cbPositionX->addItem(i18n("custom")); - ui.cbPositionY->addItem(i18n("top")); - ui.cbPositionY->addItem(i18n("center")); - ui.cbPositionY->addItem(i18n("bottom")); - ui.cbPositionY->addItem(i18n("custom")); + ui.cbBackgroundType->addItem(i18n("Color")); + ui.cbBackgroundType->addItem(i18n("Image")); + ui.cbBackgroundType->addItem(i18n("Pattern")); + + ui.cbBackgroundColorStyle->addItem(i18n("Single Color")); + ui.cbBackgroundColorStyle->addItem(i18n("Horizontal Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Vertical Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Top Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Bottom Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Radial Gradient")); + + ui.cbBackgroundImageStyle->addItem(i18n("Scaled and Cropped")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled, Keep Proportions")); + ui.cbBackgroundImageStyle->addItem(i18n("Centered")); + ui.cbBackgroundImageStyle->addItem(i18n("Tiled")); + ui.cbBackgroundImageStyle->addItem(i18n("Center Tiled")); + + ui.cbOrder->addItem(i18n("Column Major")); + ui.cbOrder->addItem(i18n("Row Major")); + + ui.cbPositionX->addItem(i18n("Left")); + ui.cbPositionX->addItem(i18n("Center")); + ui.cbPositionX->addItem(i18n("Right")); + ui.cbPositionX->addItem(i18n("Custom")); + ui.cbPositionY->addItem(i18n("Top")); + ui.cbPositionY->addItem(i18n("Center")); + ui.cbPositionY->addItem(i18n("Bottom")); + ui.cbPositionY->addItem(i18n("Custom")); GuiTools::updatePenStyles(ui.cbBorderStyle, Qt::black); GuiTools::updateBrushStyles(ui.cbBackgroundBrushStyle, Qt::SolidPattern); m_initializing = false; } // "General"-tab void CartesianPlotLegendDock::nameChanged() { if (m_initializing) return; m_legend->setName(ui.leName->text()); } void CartesianPlotLegendDock::commentChanged() { if (m_initializing) return; m_legend->setComment(ui.leComment->text()); } void CartesianPlotLegendDock::visibilityChanged(bool state) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setVisible(state); } //General void CartesianPlotLegendDock::labelFontChanged(const QFont& font) { if (m_initializing) return; QFont labelsFont = font; labelsFont.setPixelSize( Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Point) ); for (auto* legend : m_legendList) legend->setLabelFont(labelsFont); } void CartesianPlotLegendDock::labelColorChanged(const QColor& color) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLabelColor(color); } void CartesianPlotLegendDock::labelOrderChanged(const int index) { if (m_initializing) return; bool columnMajor = (index==0); for (auto* legend : m_legendList) legend->setLabelColumnMajor(columnMajor); } void CartesianPlotLegendDock::lineSymbolWidthChanged(double value) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLineSymbolWidth(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); } /*! called when legend's current horizontal position relative to its parent (left, center, right, custom ) is changed. */ void CartesianPlotLegendDock::positionXChanged(int index) { //Enable/disable the spinbox for the x- oordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionX->count()-1 ) { ui.sbPositionX->setEnabled(true); }else{ ui.sbPositionX->setEnabled(false); } if (m_initializing) return; CartesianPlotLegend::PositionWrapper position = m_legend->position(); position.horizontalPosition = CartesianPlotLegend::HorizontalPosition(index); for (auto* legend : m_legendList) legend->setPosition(position); } /*! called when legend's current horizontal position relative to its parent (top, center, bottom, custom ) is changed. */ void CartesianPlotLegendDock::positionYChanged(int index) { //Enable/disable the spinbox for the y- oordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionY->count()-1 ) { ui.sbPositionY->setEnabled(true); }else{ ui.sbPositionY->setEnabled(false); } if (m_initializing) return; CartesianPlotLegend::PositionWrapper position = m_legend->position(); position.verticalPosition = CartesianPlotLegend::VerticalPosition(index); for (auto* legend : m_legendList) legend->setPosition(position); } void CartesianPlotLegendDock::customPositionXChanged(double value) { if (m_initializing) return; CartesianPlotLegend::PositionWrapper position = m_legend->position(); position.point.setX(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* legend : m_legendList) legend->setPosition(position); } void CartesianPlotLegendDock::customPositionYChanged(double value) { if (m_initializing) return; CartesianPlotLegend::PositionWrapper position = m_legend->position(); position.point.setY(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* legend : m_legendList) legend->setPosition(position); } // "Background"-tab void CartesianPlotLegendDock::backgroundTypeChanged(int index) { PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color) { ui.lBackgroundColorStyle->show(); ui.cbBackgroundColorStyle->show(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle) ui.cbBackgroundColorStyle->currentIndex(); if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); }else{ ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } }else if (type == PlotArea::Image) { ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->show(); ui.cbBackgroundImageStyle->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->show(); ui.leBackgroundFileName->show(); ui.bOpen->show(); ui.lBackgroundFirstColor->hide(); ui.kcbBackgroundFirstColor->hide(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); }else if (type == PlotArea::Pattern) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->show(); ui.cbBackgroundBrushStyle->show(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } if (m_initializing) return; for (auto* legend : m_legendList) legend->setBackgroundType(type); } void CartesianPlotLegendDock::backgroundColorStyleChanged(int index) { PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); }else{ ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); } if (m_initializing) return; for (auto* legend : m_legendList) legend->setBackgroundColorStyle(style); } void CartesianPlotLegendDock::backgroundImageStyleChanged(int index) { if (m_initializing) return; PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; for (auto* legend : m_legendList) legend->setBackgroundImageStyle(style); } void CartesianPlotLegendDock::backgroundBrushStyleChanged(int index) { if (m_initializing) return; Qt::BrushStyle style = (Qt::BrushStyle)index; for (auto* legend : m_legendList) legend->setBackgroundBrushStyle(style); } void CartesianPlotLegendDock::backgroundFirstColorChanged(const QColor& c) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setBackgroundFirstColor(c); } void CartesianPlotLegendDock::backgroundSecondColorChanged(const QColor& c) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setBackgroundSecondColor(c); } /*! opens a file dialog and lets the user select the image file. */ void CartesianPlotLegendDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "CartesianPlotLegendDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const QByteArray& format : QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastImageDir", newDir); } ui.leBackgroundFileName->setText( path ); for (auto* legend : m_legendList) legend->setBackgroundFileName(path); } void CartesianPlotLegendDock::fileNameChanged() { if (m_initializing) return; QString fileName = ui.leBackgroundFileName->text(); if (!fileName.isEmpty() && !QFile::exists(fileName)) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); for (auto* legend : m_legendList) legend->setBackgroundFileName(fileName); } void CartesianPlotLegendDock::backgroundOpacityChanged(int value) { if (m_initializing) return; float opacity = (float)value/100.; for (auto* legend : m_legendList) legend->setBackgroundOpacity(opacity); } // "Border"-tab void CartesianPlotLegendDock::borderStyleChanged(int index) { if (m_initializing) return; Qt::PenStyle penStyle=Qt::PenStyle(index); QPen pen; for (auto* legend : m_legendList) { pen = legend->borderPen(); pen.setStyle(penStyle); legend->setBorderPen(pen); } } void CartesianPlotLegendDock::borderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* legend : m_legendList) { pen = legend->borderPen(); pen.setColor(color); legend->setBorderPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, color); m_initializing = false; } void CartesianPlotLegendDock::borderWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* legend : m_legendList) { pen = legend->borderPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); legend->setBorderPen(pen); } } void CartesianPlotLegendDock::borderCornerRadiusChanged(double value) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setBorderCornerRadius(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); } void CartesianPlotLegendDock::borderOpacityChanged(int value) { if (m_initializing) return; float opacity = (float)value/100.; for (auto* legend : m_legendList) legend->setBorderOpacity(opacity); } //Layout void CartesianPlotLegendDock::layoutTopMarginChanged(double margin) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutTopMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutBottomMarginChanged(double margin) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutBottomMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutLeftMarginChanged(double margin) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutLeftMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutRightMarginChanged(double margin) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutRightMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutHorizontalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutHorizontalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutVerticalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutVerticalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void CartesianPlotLegendDock::layoutColumnCountChanged(int count) { ui.lOrder->setVisible(count!=1); ui.cbOrder->setVisible(count!=1); if (m_initializing) return; for (auto* legend : m_legendList) legend->setLayoutColumnCount(count); } //************************************************************* //**** SLOTs for changes triggered in CartesianPlotLegend ***** //************************************************************* //General void CartesianPlotLegendDock::legendDescriptionChanged(const AbstractAspect* aspect) { if (m_legend != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) { ui.leName->setText(aspect->name()); } else if (aspect->comment() != ui.leComment->text()) { ui.leComment->setText(aspect->comment()); } m_initializing = false; } void CartesianPlotLegendDock::legendLabelFontChanged(QFont& font) { m_initializing = true; //we need to set the font size in points for KFontRequester QFont f(font); f.setPointSizeF( Worksheet::convertFromSceneUnits(f.pixelSize(), Worksheet::Point) ); ui.kfrLabelFont->setFont(f); m_initializing = false; } void CartesianPlotLegendDock::legendLabelColorChanged(QColor& color) { m_initializing = true; ui.kcbLabelColor->setColor(color); m_initializing = false; } void CartesianPlotLegendDock::legendLabelOrderChanged(bool b) { m_initializing = true; if (b) ui.cbOrder->setCurrentIndex(0); //column major else ui.cbOrder->setCurrentIndex(1); //row major m_initializing = false; } void CartesianPlotLegendDock::legendLineSymbolWidthChanged(float value) { m_initializing = true; ui.sbLineSymbolWidth->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendPositionChanged(const CartesianPlotLegend::PositionWrapper& position) { m_initializing = true; ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(position.point.x(), Worksheet::Centimeter) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(position.point.y(), Worksheet::Centimeter) ); ui.cbPositionX->setCurrentIndex( position.horizontalPosition ); ui.cbPositionY->setCurrentIndex( position.verticalPosition ); m_initializing = false; } void CartesianPlotLegendDock::legendVisibilityChanged(bool on) { m_initializing = true; ui.chkVisible->setChecked(on); m_initializing = false; } //Background void CartesianPlotLegendDock::legendBackgroundTypeChanged(PlotArea::BackgroundType type) { m_initializing = true; ui.cbBackgroundType->setCurrentIndex(type); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle style) { m_initializing = true; ui.cbBackgroundColorStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle style) { m_initializing = true; ui.cbBackgroundImageStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundBrushStyleChanged(Qt::BrushStyle style) { m_initializing = true; ui.cbBackgroundBrushStyle->setCurrentIndex(style); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundFirstColorChanged(QColor& color) { m_initializing = true; ui.kcbBackgroundFirstColor->setColor(color); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundSecondColorChanged(QColor& color) { m_initializing = true; ui.kcbBackgroundSecondColor->setColor(color); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundFileNameChanged(QString& filename) { m_initializing = true; ui.leBackgroundFileName->setText(filename); m_initializing = false; } void CartesianPlotLegendDock::legendBackgroundOpacityChanged(float opacity) { m_initializing = true; ui.sbBackgroundOpacity->setValue( qRound(opacity*100.0) ); m_initializing = false; } //Border void CartesianPlotLegendDock::legendBorderPenChanged(QPen& pen) { if (m_initializing) return; m_initializing = true; if (ui.cbBorderStyle->currentIndex() != pen.style()) ui.cbBorderStyle->setCurrentIndex(pen.style()); if (ui.kcbBorderColor->color() != pen.color()) ui.kcbBorderColor->setColor(pen.color()); if (ui.sbBorderWidth->value() != pen.widthF()) ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void CartesianPlotLegendDock::legendBorderCornerRadiusChanged(float value) { m_initializing = true; ui.sbBorderCornerRadius->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendBorderOpacityChanged(float opacity) { m_initializing = true; ui.sbBorderOpacity->setValue( qRound(opacity*100.0) ); m_initializing = false; } //Layout void CartesianPlotLegendDock::legendLayoutTopMarginChanged(float value) { m_initializing = true; ui.sbLayoutTopMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutBottomMarginChanged(float value) { m_initializing = true; ui.sbLayoutBottomMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutLeftMarginChanged(float value) { m_initializing = true; ui.sbLayoutLeftMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutRightMarginChanged(float value) { m_initializing = true; ui.sbLayoutRightMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutVerticalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutVerticalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutHorizontalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutHorizontalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void CartesianPlotLegendDock::legendLayoutColumnCountChanged(int value) { m_initializing = true; ui.sbLayoutColumnCount->setValue(value); m_initializing = false; } //************************************************************* //******************** SETTINGS ******************************* //************************************************************* void CartesianPlotLegendDock::load() { //General-tab ui.chkVisible->setChecked( m_legend->isVisible() ); //we need to set the font size in points for KFontRequester QFont font = m_legend->labelFont(); font.setPointSizeF( qRound(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); // qDebug()<<"font size " << font.pixelSize() << " " << font.pointSizeF(); ui.kfrLabelFont->setFont(font); ui.kcbLabelColor->setColor( m_legend->labelColor() ); bool columnMajor = m_legend->labelColumnMajor(); if (columnMajor) ui.cbOrder->setCurrentIndex(0); //column major else ui.cbOrder->setCurrentIndex(1); //row major ui.sbLineSymbolWidth->setValue( Worksheet::convertFromSceneUnits(m_legend->lineSymbolWidth(), Worksheet::Centimeter) ); //Background-tab ui.cbBackgroundType->setCurrentIndex( (int) m_legend->backgroundType() ); ui.cbBackgroundColorStyle->setCurrentIndex( (int) m_legend->backgroundColorStyle() ); ui.cbBackgroundImageStyle->setCurrentIndex( (int) m_legend->backgroundImageStyle() ); ui.cbBackgroundBrushStyle->setCurrentIndex( (int) m_legend->backgroundBrushStyle() ); ui.leBackgroundFileName->setText( m_legend->backgroundFileName() ); ui.kcbBackgroundFirstColor->setColor( m_legend->backgroundFirstColor() ); ui.kcbBackgroundSecondColor->setColor( m_legend->backgroundSecondColor() ); ui.sbBackgroundOpacity->setValue( qRound(m_legend->backgroundOpacity()*100.0) ); //highlight the text field for the background image red if an image is used and cannot be found if (!m_legend->backgroundFileName().isEmpty() && !QFile::exists(m_legend->backgroundFileName())) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); //Border ui.kcbBorderColor->setColor( m_legend->borderPen().color() ); ui.cbBorderStyle->setCurrentIndex( (int) m_legend->borderPen().style() ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_legend->borderPen().widthF(), Worksheet::Point) ); ui.sbBorderCornerRadius->setValue( Worksheet::convertFromSceneUnits(m_legend->borderCornerRadius(), Worksheet::Centimeter) ); ui.sbBorderOpacity->setValue( qRound(m_legend->borderOpacity()*100.0) ); // Layout ui.sbLayoutTopMargin->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutTopMargin(), Worksheet::Centimeter) ); ui.sbLayoutBottomMargin->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutBottomMargin(), Worksheet::Centimeter) ); ui.sbLayoutLeftMargin->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutLeftMargin(), Worksheet::Centimeter) ); ui.sbLayoutRightMargin->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutRightMargin(), Worksheet::Centimeter) ); ui.sbLayoutHorizontalSpacing->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutHorizontalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutVerticalSpacing->setValue( Worksheet::convertFromSceneUnits(m_legend->layoutVerticalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutColumnCount->setValue( m_legend->layoutColumnCount() ); m_initializing=true; GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); m_initializing=false; } void CartesianPlotLegendDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_legendList.size(); if (size>1) m_legend->beginMacro(i18n("%1 cartesian plot legends: template \"%2\" loaded", size, name)); else m_legend->beginMacro(i18n("%1: template \"%2\" loaded", m_legend->name(), name)); this->loadConfig(config); m_legend->endMacro(); } void CartesianPlotLegendDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "CartesianPlotLegend" ); //General-tab ui.chkVisible->setChecked( group.readEntry("Visible", m_legend->isVisible()) ); //we need to set the font size in points for KFontRequester QFont font = m_legend->labelFont(); font.setPointSizeF( qRound(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrLabelFont->setFont( group.readEntry("LabelFont", font) ); ui.kcbLabelColor->setColor( group.readEntry("LabelColor", m_legend->labelColor()) ); bool columnMajor = group.readEntry("LabelColumMajor", m_legend->labelColumnMajor()); if (columnMajor) ui.cbOrder->setCurrentIndex(0); //column major else ui.cbOrder->setCurrentIndex(1); //row major ui.sbLineSymbolWidth->setValue(group.readEntry("LineSymbolWidth", Worksheet::convertFromSceneUnits(m_legend->lineSymbolWidth(), Worksheet::Centimeter)) ); //Background-tab ui.cbBackgroundType->setCurrentIndex( group.readEntry("BackgroundType", (int) m_legend->backgroundType()) ); ui.cbBackgroundColorStyle->setCurrentIndex( group.readEntry("BackgroundColorStyle", (int) m_legend->backgroundColorStyle()) ); ui.cbBackgroundImageStyle->setCurrentIndex( group.readEntry("BackgroundImageStyle", (int) m_legend->backgroundImageStyle()) ); ui.cbBackgroundBrushStyle->setCurrentIndex( group.readEntry("BackgroundBrushStyle", (int) m_legend->backgroundBrushStyle()) ); ui.leBackgroundFileName->setText( group.readEntry("BackgroundFileName", m_legend->backgroundFileName()) ); ui.kcbBackgroundFirstColor->setColor( group.readEntry("BackgroundFirstColor", m_legend->backgroundFirstColor()) ); ui.kcbBackgroundSecondColor->setColor( group.readEntry("BackgroundSecondColor", m_legend->backgroundSecondColor()) ); ui.sbBackgroundOpacity->setValue( qRound(group.readEntry("BackgroundOpacity", m_legend->backgroundOpacity())*100.0) ); //Border ui.kcbBorderColor->setColor( group.readEntry("BorderColor", m_legend->borderPen().color()) ); ui.cbBorderStyle->setCurrentIndex( group.readEntry("BorderStyle", (int) m_legend->borderPen().style()) ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_legend->borderPen().widthF()), Worksheet::Point) ); ui.sbBorderCornerRadius->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderCornerRadius", m_legend->borderCornerRadius()), Worksheet::Centimeter) ); ui.sbBorderOpacity->setValue( qRound(group.readEntry("BorderOpacity", m_legend->borderOpacity())*100.0) ); // Layout ui.sbLayoutTopMargin->setValue(group.readEntry("LayoutTopMargin", Worksheet::convertFromSceneUnits(m_legend->layoutTopMargin(), Worksheet::Centimeter)) ); ui.sbLayoutBottomMargin->setValue(group.readEntry("LayoutBottomMargin", Worksheet::convertFromSceneUnits(m_legend->layoutBottomMargin(), Worksheet::Centimeter)) ); ui.sbLayoutLeftMargin->setValue(group.readEntry("LayoutLeftMargin", Worksheet::convertFromSceneUnits(m_legend->layoutLeftMargin(), Worksheet::Centimeter)) ); ui.sbLayoutRightMargin->setValue(group.readEntry("LayoutRightMargin", Worksheet::convertFromSceneUnits(m_legend->layoutRightMargin(), Worksheet::Centimeter)) ); ui.sbLayoutHorizontalSpacing->setValue(group.readEntry("LayoutHorizontalSpacing", Worksheet::convertFromSceneUnits(m_legend->layoutHorizontalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutVerticalSpacing->setValue(group.readEntry("LayoutVerticalSpacing", Worksheet::convertFromSceneUnits(m_legend->layoutVerticalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutColumnCount->setValue(group.readEntry("LayoutColumnCount", m_legend->layoutColumnCount())); //Title group = config.group("PlotLegend"); labelWidget->loadConfig(group); m_initializing=true; GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); m_initializing=false; } void CartesianPlotLegendDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "CartesianPlotLegend" ); //General-tab group.writeEntry("Visible", ui.chkVisible->isChecked()); QFont font = m_legend->labelFont(); font.setPointSizeF( Worksheet::convertFromSceneUnits(font.pointSizeF(), Worksheet::Point) ); group.writeEntry("LabelFont", font); group.writeEntry("LabelColor", ui.kcbLabelColor->color()); group.writeEntry("LabelColumMajorOrder", ui.cbOrder->currentIndex()==0);// true for "column major", false for "row major" group.writeEntry("LineSymbolWidth", Worksheet::convertToSceneUnits(ui.sbLineSymbolWidth->value(), Worksheet::Centimeter)); //Background group.writeEntry("BackgroundType", ui.cbBackgroundType->currentIndex()); group.writeEntry("BackgroundColorStyle", ui.cbBackgroundColorStyle->currentIndex()); group.writeEntry("BackgroundImageStyle", ui.cbBackgroundImageStyle->currentIndex()); group.writeEntry("BackgroundBrushStyle", ui.cbBackgroundBrushStyle->currentIndex()); group.writeEntry("BackgroundFileName", ui.leBackgroundFileName->text()); group.writeEntry("BackgroundFirstColor", ui.kcbBackgroundFirstColor->color()); group.writeEntry("BackgroundSecondColor", ui.kcbBackgroundSecondColor->color()); group.writeEntry("BackgroundOpacity", ui.sbBackgroundOpacity->value()/100.0); //Border group.writeEntry("BorderStyle", ui.cbBorderStyle->currentIndex()); group.writeEntry("BorderColor", ui.kcbBorderColor->color()); group.writeEntry("BorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Point)); group.writeEntry("BorderCornerRadius", Worksheet::convertToSceneUnits(ui.sbBorderCornerRadius->value(), Worksheet::Centimeter)); group.writeEntry("BorderOpacity", ui.sbBorderOpacity->value()/100.0); //Layout group.writeEntry("LayoutTopMargin",Worksheet::convertToSceneUnits(ui.sbLayoutTopMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutBottomMargin",Worksheet::convertToSceneUnits(ui.sbLayoutBottomMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutLeftMargin",Worksheet::convertToSceneUnits(ui.sbLayoutLeftMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutRightMargin",Worksheet::convertToSceneUnits(ui.sbLayoutRightMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutVerticalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutVerticalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutHorizontalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutHorizontalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutColumnCount", ui.sbLayoutColumnCount->value()); //Title group = config.group("PlotLegend"); labelWidget->saveConfig(group); config.sync(); } diff --git a/src/kdefrontend/dockwidgets/ColumnDock.cpp b/src/kdefrontend/dockwidgets/ColumnDock.cpp index 117892b44..3e13a3df2 100644 --- a/src/kdefrontend/dockwidgets/ColumnDock.cpp +++ b/src/kdefrontend/dockwidgets/ColumnDock.cpp @@ -1,423 +1,423 @@ /*************************************************************************** File : ColumnDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2017 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2013-2017 by Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for column properties ***************************************************************************/ /*************************************************************************** * * * 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 "ColumnDock.h" #include "backend/core/AbstractFilter.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/datasources/LiveDataSource.h" #include "backend/spreadsheet/Spreadsheet.h" #include /*! \class ColumnDock \brief Provides a widget for editing the properties of the spreadsheet columns currently selected in the project explorer. \ingroup kdefrontend */ ColumnDock::ColumnDock(QWidget* parent) : QWidget(parent), m_column(0), m_initializing(false) { ui.setupUi(this); connect(ui.leName, &QLineEdit::textChanged, this, &ColumnDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &ColumnDock::commentChanged); connect(ui.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int))); connect(ui.cbFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(formatChanged(int))); connect(ui.sbPrecision, SIGNAL(valueChanged(int)), this, SLOT(precisionChanged(int)) ); connect(ui.cbPlotDesignation, SIGNAL(currentIndexChanged(int)), this, SLOT(plotDesignationChanged(int))); retranslateUi(); } void ColumnDock::setColumns(QList list) { m_initializing = true; m_columnsList = list; m_column = list.first(); //check whether we have non-editable columns (e.g. columns for residuals calculated in XYFitCurve) bool nonEditable = false; for (auto* col: m_columnsList) { Spreadsheet* s = dynamic_cast(col->parentAspect()); if (s) { if (dynamic_cast(s)) { nonEditable = true; break; } } else { nonEditable = true; break; } } if (list.size() == 1) { //names and comments of non-editable columns in a file data source can be changed. if (!nonEditable && dynamic_cast(m_column->parentAspect()) != 0) { ui.leName->setEnabled(false); ui.leComment->setEnabled(false); } else { ui.leName->setEnabled(true); ui.leComment->setEnabled(true); } ui.leName->setText(m_column->name()); ui.leComment->setText(m_column->comment()); } else { ui.leName->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first column AbstractColumn::ColumnMode columnMode = m_column->columnMode(); ui.cbType->setCurrentIndex(ui.cbType->findData((int)columnMode)); //disable widgets if we have at least one non-editable column ui.cbType->setEnabled(!nonEditable); ui.lFormat->setVisible(!nonEditable); ui.cbFormat->setVisible(!nonEditable); ui.lPrecision->setVisible(!nonEditable); ui.sbPrecision->setVisible(!nonEditable); ui.lPlotDesignation->setVisible(!nonEditable); ui.cbPlotDesignation->setVisible(!nonEditable); if (nonEditable) { m_initializing = false; return; } this->updateFormatWidgets(columnMode); switch(columnMode) { case AbstractColumn::Numeric: { Double2StringFilter* filter = static_cast(m_column->outputFilter()); ui.cbFormat->setCurrentIndex(ui.cbFormat->findData(filter->numericFormat())); ui.sbPrecision->setValue(filter->numDigits()); break; } case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { DateTime2StringFilter* filter = static_cast(m_column->outputFilter()); DEBUG(" set column format: " << filter->format().toStdString()); ui.cbFormat->setCurrentIndex(ui.cbFormat->findData(filter->format())); break; } case AbstractColumn::Integer: // nothing to set case AbstractColumn::Text: break; } ui.cbPlotDesignation->setCurrentIndex( int(m_column->plotDesignation()) ); // slots connect(m_column, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(columnDescriptionChanged(const AbstractAspect*))); connect(m_column->outputFilter(), SIGNAL(formatChanged()),this, SLOT(columnFormatChanged())); connect(m_column->outputFilter(), SIGNAL(digitsChanged()),this, SLOT(columnPrecisionChanged())); connect(m_column, SIGNAL(plotDesignationChanged(const AbstractColumn*)),this, SLOT(columnPlotDesignationChanged(const AbstractColumn*))); m_initializing = false; } /*! depending on the currently selected column type (column mode) updates the widgets for the column format, shows/hides the allowed widgets, fills the corresponding combobox with the possible entries. Called when the type (column mode) is changed. */ void ColumnDock::updateFormatWidgets(const AbstractColumn::ColumnMode columnMode) { ui.cbFormat->clear(); switch (columnMode) { case AbstractColumn::Numeric: ui.cbFormat->addItem(i18n("Decimal"), QVariant('f')); ui.cbFormat->addItem(i18n("Scientific (e)"), QVariant('e')); ui.cbFormat->addItem(i18n("Scientific (E)"), QVariant('E')); ui.cbFormat->addItem(i18n("Automatic (g)"), QVariant('g')); ui.cbFormat->addItem(i18n("Automatic (G)"), QVariant('G')); break; case AbstractColumn::Month: ui.cbFormat->addItem(i18n("Number without leading zero"), QVariant("M")); ui.cbFormat->addItem(i18n("Number with leading zero"), QVariant("MM")); ui.cbFormat->addItem(i18n("Abbreviated month name"), QVariant("MMM")); ui.cbFormat->addItem(i18n("Full month name"), QVariant("MMMM")); break; case AbstractColumn::Day: ui.cbFormat->addItem(i18n("Number without leading zero"), QVariant("d")); ui.cbFormat->addItem(i18n("Number with leading zero"), QVariant("dd")); ui.cbFormat->addItem(i18n("Abbreviated day name"), QVariant("ddd")); ui.cbFormat->addItem(i18n("Full day name"), QVariant("dddd")); break; case AbstractColumn::DateTime: for (const auto& s: AbstractColumn::dateTimeFormats()) ui.cbFormat->addItem(s, QVariant(s)); break; case AbstractColumn::Integer: case AbstractColumn::Text: break; } if (columnMode == AbstractColumn::Numeric) { ui.lPrecision->show(); ui.sbPrecision->show(); } else { ui.lPrecision->hide(); ui.sbPrecision->hide(); } if (columnMode == AbstractColumn::Text || columnMode == AbstractColumn::Integer) { ui.lFormat->hide(); ui.cbFormat->hide(); } else { ui.lFormat->show(); ui.cbFormat->show(); } if (columnMode == AbstractColumn::DateTime) { ui.cbFormat->setEditable(true); ui.cbFormat->setCurrentItem("yyyy-MM-dd hh:mm:ss.zzz"); } else { ui.cbFormat->setEditable(false); ui.cbFormat->setCurrentIndex(0); } } //************************************************************* //******** SLOTs for changes triggered in ColumnDock ********** //************************************************************* void ColumnDock::retranslateUi() { m_initializing = true; ui.cbType->clear(); ui.cbType->addItem(i18n("Numeric"), QVariant(int(AbstractColumn::Numeric))); ui.cbType->addItem(i18n("Integer"), QVariant(int(AbstractColumn::Integer))); ui.cbType->addItem(i18n("Text"), QVariant(int(AbstractColumn::Text))); ui.cbType->addItem(i18n("Month names"), QVariant(int(AbstractColumn::Month))); ui.cbType->addItem(i18n("Day names"), QVariant(int(AbstractColumn::Day))); ui.cbType->addItem(i18n("Date and time"), QVariant(int(AbstractColumn::DateTime))); ui.cbPlotDesignation->clear(); - ui.cbPlotDesignation->addItem(i18n("none")); + ui.cbPlotDesignation->addItem(i18n("None")); ui.cbPlotDesignation->addItem(i18n("X")); ui.cbPlotDesignation->addItem(i18n("Y")); ui.cbPlotDesignation->addItem(i18n("Z")); ui.cbPlotDesignation->addItem(i18n("X-error")); ui.cbPlotDesignation->addItem(i18n("X-error -")); ui.cbPlotDesignation->addItem(i18n("X-error +")); ui.cbPlotDesignation->addItem(i18n("Y-error")); ui.cbPlotDesignation->addItem(i18n("Y-error -")); ui.cbPlotDesignation->addItem(i18n("Y-error +")); m_initializing = false; } void ColumnDock::nameChanged() { if (m_initializing) return; m_columnsList.first()->setName(ui.leName->text()); } void ColumnDock::commentChanged() { if (m_initializing) return; m_columnsList.first()->setComment(ui.leComment->text()); } /*! called when the type (column mode - numeric, text etc.) of the column was changed. */ void ColumnDock::typeChanged(int index) { DEBUG("ColumnDock::typeChanged()"); if (m_initializing) return; AbstractColumn::ColumnMode columnMode = (AbstractColumn::ColumnMode)ui.cbType->itemData(index).toInt(); int format_index = ui.cbFormat->currentIndex(); m_initializing = true; this->updateFormatWidgets(columnMode); m_initializing = false; switch(columnMode) { case AbstractColumn::Numeric: { int digits = ui.sbPrecision->value(); for (auto* col: m_columnsList) { col->beginMacro(i18n("%1: change column type", col->name())); col->setColumnMode(columnMode); Double2StringFilter* filter = static_cast(col->outputFilter()); filter->setNumericFormat(ui.cbFormat->itemData(format_index).toChar().toLatin1()); filter->setNumDigits(digits); col->endMacro(); } break; } case AbstractColumn::Integer: case AbstractColumn::Text: for (auto* col: m_columnsList) col->setColumnMode(columnMode); break; case AbstractColumn::Month: case AbstractColumn::Day: for (auto* col: m_columnsList) { col->beginMacro(i18n("%1: change column type", col->name())); // the format is saved as item data QString format = ui.cbFormat->itemData(ui.cbFormat->currentIndex()).toString(); col->setColumnMode(columnMode); DateTime2StringFilter* filter = static_cast(col->outputFilter()); filter->setFormat(format); col->endMacro(); } break; case AbstractColumn::DateTime: for (auto* col: m_columnsList) { col->beginMacro(i18n("%1: change column type", col->name())); // the format is the current text QString format = ui.cbFormat->currentText(); col->setColumnMode(columnMode); DateTime2StringFilter* filter = static_cast(col->outputFilter()); filter->setFormat(format); col->endMacro(); } break; } DEBUG("ColumnDock::typeChanged() DONE"); } /*! called when the format for the current type (column mode) was changed. */ void ColumnDock::formatChanged(int index) { DEBUG("ColumnDock::formatChanged()"); if (m_initializing) return; AbstractColumn::ColumnMode mode = (AbstractColumn::ColumnMode)ui.cbType->itemData(ui.cbType->currentIndex()).toInt(); int format_index = index; switch(mode) { case AbstractColumn::Numeric: { for (auto* col: m_columnsList) { Double2StringFilter* filter = static_cast(col->outputFilter()); filter->setNumericFormat(ui.cbFormat->itemData(format_index).toChar().toLatin1()); } break; } case AbstractColumn::Integer: case AbstractColumn::Text: break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { QString format = ui.cbFormat->itemData(ui.cbFormat->currentIndex()).toString(); for (auto* col: m_columnsList) { DateTime2StringFilter* filter = static_cast(col->outputFilter()); filter->setFormat(format); } break; } } DEBUG("ColumnDock::formatChanged() DONE"); } void ColumnDock::precisionChanged(int digits) { if (m_initializing) return; for (auto* col: m_columnsList) { Double2StringFilter* filter = static_cast(col->outputFilter()); filter->setNumDigits(digits); } } void ColumnDock::plotDesignationChanged(int index) { if (m_initializing) return; AbstractColumn::PlotDesignation pd = AbstractColumn::PlotDesignation(index); for (auto* col: m_columnsList) col->setPlotDesignation(pd); } //************************************************************* //********* SLOTs for changes triggered in Column ************* //************************************************************* void ColumnDock::columnDescriptionChanged(const AbstractAspect* aspect) { if (m_column != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) ui.leName->setText(aspect->name()); else if (aspect->comment() != ui.leComment->text()) ui.leComment->setText(aspect->comment()); m_initializing = false; } void ColumnDock::columnFormatChanged() { DEBUG("ColumnDock::columnFormatChanged()"); m_initializing = true; AbstractColumn::ColumnMode columnMode = m_column->columnMode(); switch(columnMode) { case AbstractColumn::Numeric: { Double2StringFilter* filter = static_cast(m_column->outputFilter()); ui.cbFormat->setCurrentIndex(ui.cbFormat->findData(filter->numericFormat())); break; } case AbstractColumn::Integer: case AbstractColumn::Text: break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { DateTime2StringFilter* filter = static_cast(m_column->outputFilter()); ui.cbFormat->setCurrentIndex(ui.cbFormat->findData(filter->format())); break; } } m_initializing = false; } void ColumnDock::columnPrecisionChanged() { m_initializing = true; Double2StringFilter* filter = static_cast(m_column->outputFilter()); ui.sbPrecision->setValue(filter->numDigits()); m_initializing = false; } void ColumnDock::columnPlotDesignationChanged(const AbstractColumn* col) { m_initializing = true; ui.cbPlotDesignation->setCurrentIndex( int(col->plotDesignation()) ); m_initializing = false; } diff --git a/src/kdefrontend/dockwidgets/CustomPointDock.cpp b/src/kdefrontend/dockwidgets/CustomPointDock.cpp index df123b3dc..5647797fa 100644 --- a/src/kdefrontend/dockwidgets/CustomPointDock.cpp +++ b/src/kdefrontend/dockwidgets/CustomPointDock.cpp @@ -1,503 +1,503 @@ /*************************************************************************** File : CustomPointDock.cpp Project : LabPlot Description : widget for Datapicker-Point properties -------------------------------------------------------------------- Copyright : (C) 2015 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 "CustomPointDock.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CustomPoint.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/GuiTools.h" #include #include -#include +#include #include #include CustomPointDock::CustomPointDock(QWidget *parent): QWidget(parent), m_point(nullptr) { ui.setupUi(this); //Validators ui.lePositionX->setValidator( new QDoubleValidator(ui.lePositionX) ); ui.lePositionY->setValidator( new QDoubleValidator(ui.lePositionY) ); //adjust layouts in the tabs for (int i=0; icount(); ++i) { QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //SLOTS //General connect(ui.leName, &QLineEdit::textChanged, this, &CustomPointDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &CustomPointDock::commentChanged); connect( ui.lePositionX, SIGNAL(returnPressed()), this, SLOT(positionXChanged()) ); connect( ui.lePositionY, SIGNAL(returnPressed()), this, SLOT(positionYChanged()) ); connect( ui.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); //Symbols connect( ui.cbSymbolStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolStyleChanged(int)) ); connect( ui.sbSymbolSize, SIGNAL(valueChanged(double)), this, SLOT(symbolSizeChanged(double)) ); connect( ui.sbSymbolRotation, SIGNAL(valueChanged(int)), this, SLOT(symbolRotationChanged(int)) ); connect( ui.sbSymbolOpacity, SIGNAL(valueChanged(int)), this, SLOT(symbolOpacityChanged(int)) ); connect( ui.cbSymbolFillingStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolFillingStyleChanged(int)) ); connect( ui.kcbSymbolFillingColor, SIGNAL(changed(QColor)), this, SLOT(symbolFillingColorChanged(QColor)) ); connect( ui.cbSymbolBorderStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolBorderStyleChanged(int)) ); connect( ui.kcbSymbolBorderColor, SIGNAL(changed(QColor)), this, SLOT(symbolBorderColorChanged(QColor)) ); connect( ui.sbSymbolBorderWidth, SIGNAL(valueChanged(double)), this, SLOT(symbolBorderWidthChanged(double)) ); //Template handler TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::CustomPoint); ui.verticalLayout->addWidget(templateHandler); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); init(); } void CustomPointDock::init() { m_initializing = true; GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, Qt::black); QPainter pa; int iconSize = 20; QPixmap pm(iconSize, iconSize); QPen pen(Qt::SolidPattern, 0); ui.cbSymbolStyle->setIconSize(QSize(iconSize, iconSize)); QTransform trafo; trafo.scale(15, 15); for (int i=0; i<18; ++i) { Symbol::Style style = (Symbol::Style)i; pm.fill(Qt::transparent); pa.begin(&pm); pa.setPen( pen ); pa.setRenderHint(QPainter::Antialiasing); pa.translate(iconSize/2,iconSize/2); pa.drawPath(trafo.map(Symbol::pathFromStyle(style))); pa.end(); ui.cbSymbolStyle->addItem(QIcon(pm), Symbol::nameFromStyle(style)); } GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, Qt::black); m_initializing = false; } void CustomPointDock::setPoints(QList list) { m_initializing=true; m_pointsList = list; m_point = list.first(); Q_ASSERT(m_point); //if there are more then one point in the list, disable the comment and name widgets in the tab "general" if (list.size()==1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_point->name()); ui.leComment->setText(m_point->comment()); }else{ ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first custom point this->load(); //SIGNALs/SLOTs // general connect(m_point, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(pointDescriptionChanged(const AbstractAspect*))); connect(m_point, SIGNAL(positionChanged(QPointF)), this, SLOT(pointPositionChanged(QPointF))); connect(m_point, SIGNAL(visibleChanged(bool)), this, SLOT(pointVisibilityChanged(bool))); //symbol connect(m_point, SIGNAL(symbolStyleChanged(Symbol::Style)), this, SLOT(pointSymbolStyleChanged(Symbol::Style))); connect(m_point, SIGNAL(symbolSizeChanged(qreal)), this, SLOT(pointSymbolSizeChanged(qreal))); connect(m_point, SIGNAL(symbolRotationAngleChanged(qreal)), this, SLOT(pointSymbolRotationAngleChanged(qreal))); connect(m_point, SIGNAL(symbolOpacityChanged(qreal)), this, SLOT(pointSymbolOpacityChanged(qreal))); connect(m_point, SIGNAL(symbolBrushChanged(QBrush)), this, SLOT(pointSymbolBrushChanged(QBrush))); connect(m_point, SIGNAL(symbolPenChanged(QPen)), this, SLOT(pointSymbolPenChanged(QPen))); } //********************************************************** //**** SLOTs for changes triggered in CustomPointDock ****** //********************************************************** //"General"-tab void CustomPointDock::nameChanged() { if (m_initializing) return; m_point->setName(ui.leName->text()); } void CustomPointDock::commentChanged() { if (m_initializing) return; m_point->setComment(ui.leComment->text()); } void CustomPointDock::positionXChanged() { if (m_initializing) return; QPointF pos = m_point->position(); float x = ui.lePositionX->text().toFloat(); pos.setX(x); for (auto* point : m_pointsList) point->setPosition(pos); } void CustomPointDock::positionYChanged() { if (m_initializing) return; QPointF pos = m_point->position(); float y = ui.lePositionY->text().toFloat(); pos.setY(y); for (auto* point : m_pointsList) point->setPosition(pos); } void CustomPointDock::visibilityChanged(bool state) { if (m_initializing) return; m_point->beginMacro(i18n("%1 CustomPoints: visibility changed", m_pointsList.count())); for (auto* point : m_pointsList) point->setVisible(state); m_point->endMacro(); } void CustomPointDock::symbolStyleChanged(int index) { Symbol::Style style = Symbol::Style(index); //enable/disable the filling options in the GUI depending on the currently selected points. if (style!=Symbol::Line && style!=Symbol::Cross) { ui.cbSymbolFillingStyle->setEnabled(true); bool noBrush = (Qt::BrushStyle(ui.cbSymbolFillingStyle->currentIndex())==Qt::NoBrush); ui.kcbSymbolFillingColor->setEnabled(!noBrush); } else { ui.kcbSymbolFillingColor->setEnabled(false); ui.cbSymbolFillingStyle->setEnabled(false); } bool noLine = (Qt::PenStyle(ui.cbSymbolBorderStyle->currentIndex())== Qt::NoPen); ui.kcbSymbolBorderColor->setEnabled(!noLine); ui.sbSymbolBorderWidth->setEnabled(!noLine); if (m_initializing) return; m_point->beginMacro(i18n("%1 CustomPoints: style changed", m_pointsList.count())); for (auto* point : m_pointsList) point->setSymbolStyle(style); m_point->endMacro(); } void CustomPointDock::symbolSizeChanged(double value) { if (m_initializing) return; m_point->beginMacro(i18n("%1 CustomPoints: size changed", m_pointsList.count())); for (auto* point : m_pointsList) point->setSymbolSize( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); m_point->endMacro(); } void CustomPointDock::symbolRotationChanged(int value) { if (m_initializing) return; m_point->beginMacro(i18n("%1 CustomPoints: rotation changed", m_pointsList.count())); for (auto* point : m_pointsList) point->setSymbolRotationAngle(value); m_point->endMacro(); } void CustomPointDock::symbolOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; m_point->beginMacro(i18n("%1 CustomPoints: opacity changed", m_pointsList.count())); for (auto* point : m_pointsList) point->setSymbolOpacity(opacity); m_point->endMacro(); } void CustomPointDock::symbolFillingStyleChanged(int index) { Qt::BrushStyle brushStyle = Qt::BrushStyle(index); ui.kcbSymbolFillingColor->setEnabled(!(brushStyle==Qt::NoBrush)); if (m_initializing) return; QBrush brush; m_point->beginMacro(i18n("%1 CustomPoints: filling style changed", m_pointsList.count())); for (auto* point : m_pointsList) { brush = point->symbolBrush(); brush.setStyle(brushStyle); point->setSymbolBrush(brush); } m_point->endMacro(); } void CustomPointDock::symbolFillingColorChanged(const QColor& color) { if (m_initializing) return; QBrush brush; m_point->beginMacro(i18n("%1 CustomPoints: filling color changed", m_pointsList.count())); for (auto* point : m_pointsList) { brush = point->symbolBrush(); brush.setColor(color); point->setSymbolBrush(brush); } m_point->endMacro(); m_initializing = true; GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, color ); m_initializing = false; } void CustomPointDock::symbolBorderStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); if ( penStyle == Qt::NoPen ) { ui.kcbSymbolBorderColor->setEnabled(false); ui.sbSymbolBorderWidth->setEnabled(false); } else { ui.kcbSymbolBorderColor->setEnabled(true); ui.sbSymbolBorderWidth->setEnabled(true); } if (m_initializing) return; QPen pen; m_point->beginMacro(i18n("%1 CustomPoints: border style changed", m_pointsList.count())); for (auto* point : m_pointsList) { pen = point->symbolPen(); pen.setStyle(penStyle); point->setSymbolPen(pen); } m_point->endMacro(); } void CustomPointDock::symbolBorderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; m_point->beginMacro(i18n("%1 CustomPoints: border color changed", m_pointsList.count())); for (auto* point : m_pointsList) { pen = point->symbolPen(); pen.setColor(color); point->setSymbolPen(pen); } m_point->endMacro(); m_initializing = true; GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, color); m_initializing = false; } void CustomPointDock::symbolBorderWidthChanged(double value) { if (m_initializing) return; QPen pen; m_point->beginMacro(i18n("%1 CustomPoints: border width changed", m_pointsList.count())); for (auto* point : m_pointsList) { pen = point->symbolPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); point->setSymbolPen(pen); } m_point->endMacro(); } //********************************************************* //**** SLOTs for changes triggered in CustomPoint ********* //********************************************************* //"General"-tab void CustomPointDock::pointDescriptionChanged(const AbstractAspect* aspect) { if (m_point != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) { ui.leName->setText(aspect->name()); } else if (aspect->comment() != ui.leComment->text()) { ui.leComment->setText(aspect->comment()); } m_initializing = false; } void CustomPointDock::pointPositionChanged(QPointF position) { m_initializing = true; ui.lePositionX->setText(QString::number(position.x())); ui.lePositionY->setText(QString::number(position.y())); m_initializing = false; } //"Symbol"-tab void CustomPointDock::pointSymbolStyleChanged(Symbol::Style style) { m_initializing = true; ui.cbSymbolStyle->setCurrentIndex((int)style); m_initializing = false; } void CustomPointDock::pointSymbolSizeChanged(qreal size) { m_initializing = true; ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } void CustomPointDock::pointSymbolRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbSymbolRotation->setValue(qRound(angle)); m_initializing = false; } void CustomPointDock::pointSymbolOpacityChanged(qreal opacity) { m_initializing = true; ui.sbSymbolOpacity->setValue( qRound(opacity*100.0) ); m_initializing = false; } void CustomPointDock::pointSymbolBrushChanged(const QBrush& brush) { m_initializing = true; ui.cbSymbolFillingStyle->setCurrentIndex((int) brush.style()); ui.kcbSymbolFillingColor->setColor(brush.color()); GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, brush.color()); m_initializing = false; } void CustomPointDock::pointSymbolPenChanged(const QPen& pen) { m_initializing = true; ui.cbSymbolBorderStyle->setCurrentIndex( (int) pen.style()); ui.kcbSymbolBorderColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, pen.color()); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Point)); m_initializing = false; } void CustomPointDock::pointVisibilityChanged(bool on) { m_initializing = true; ui.chkVisible->setChecked(on); m_initializing = false; } //********************************************************** //******************** SETTINGS **************************** //********************************************************** void CustomPointDock::load() { if (m_point == NULL) return; m_initializing = true; ui.lePositionX->setText(QString::number(m_point->position().x())); ui.lePositionY->setText(QString::number(m_point->position().y())); ui.cbSymbolStyle->setCurrentIndex( (int)m_point->symbolStyle() ); ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(m_point->symbolSize(), Worksheet::Point) ); ui.sbSymbolRotation->setValue( m_point->symbolRotationAngle() ); ui.sbSymbolOpacity->setValue( qRound(m_point->symbolOpacity()*100.0) ); ui.cbSymbolFillingStyle->setCurrentIndex( (int) m_point->symbolBrush().style() ); ui.kcbSymbolFillingColor->setColor( m_point->symbolBrush().color() ); ui.cbSymbolBorderStyle->setCurrentIndex( (int) m_point->symbolPen().style() ); ui.kcbSymbolBorderColor->setColor( m_point->symbolPen().color() ); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_point->symbolPen().widthF(), Worksheet::Point) ); ui.chkVisible->setChecked( m_point->isVisible() ); m_initializing = false; } void CustomPointDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_pointsList.size(); if (size>1) m_point->beginMacro(i18n("%1 custom points: template \"%2\" loaded", size, name)); else m_point->beginMacro(i18n("%1: template \"%2\" loaded", m_point->name(), name)); this->loadConfig(config); m_point->endMacro(); } void CustomPointDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "CustomPoint" ); ui.cbSymbolStyle->setCurrentIndex( group.readEntry("SymbolStyle", (int)m_point->symbolStyle()) ); ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(group.readEntry("SymbolSize", m_point->symbolSize()), Worksheet::Point) ); ui.sbSymbolRotation->setValue( group.readEntry("SymbolRotation", m_point->symbolRotationAngle()) ); ui.sbSymbolOpacity->setValue( qRound(group.readEntry("SymbolOpacity", m_point->symbolOpacity())*100.0) ); ui.cbSymbolFillingStyle->setCurrentIndex( group.readEntry("SymbolFillingStyle", (int) m_point->symbolBrush().style()) ); ui.kcbSymbolFillingColor->setColor( group.readEntry("SymbolFillingColor", m_point->symbolBrush().color()) ); ui.cbSymbolBorderStyle->setCurrentIndex( group.readEntry("SymbolBorderStyle", (int) m_point->symbolPen().style()) ); ui.kcbSymbolBorderColor->setColor( group.readEntry("SymbolBorderColor", m_point->symbolPen().color()) ); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("SymbolBorderWidth",m_point->symbolPen().widthF()), Worksheet::Point) ); m_initializing=true; GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, ui.kcbSymbolFillingColor->color()); GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, ui.kcbSymbolBorderColor->color()); m_initializing=false; } void CustomPointDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "CustomPoint" ); group.writeEntry("SymbolStyle", ui.cbSymbolStyle->currentText()); group.writeEntry("SymbolSize", Worksheet::convertToSceneUnits(ui.sbSymbolSize->value(),Worksheet::Point)); group.writeEntry("SymbolRotation", ui.sbSymbolRotation->value()); group.writeEntry("SymbolOpacity", ui.sbSymbolOpacity->value()/100 ); group.writeEntry("SymbolFillingStyle", ui.cbSymbolFillingStyle->currentIndex()); group.writeEntry("SymbolFillingColor", ui.kcbSymbolFillingColor->color()); group.writeEntry("SymbolBorderStyle", ui.cbSymbolBorderStyle->currentIndex()); group.writeEntry("SymbolBorderColor", ui.kcbSymbolBorderColor->color()); group.writeEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(ui.sbSymbolBorderWidth->value(),Worksheet::Point)); config.sync(); } diff --git a/src/kdefrontend/dockwidgets/HistogramDock.cpp b/src/kdefrontend/dockwidgets/HistogramDock.cpp index e88157cb7..d51dbd284 100644 --- a/src/kdefrontend/dockwidgets/HistogramDock.cpp +++ b/src/kdefrontend/dockwidgets/HistogramDock.cpp @@ -1,1089 +1,1090 @@ /*************************************************************************** File : HistogramDock.cpp Project : LabPlot Description : widget for Histogram properties -------------------------------------------------------------------- Copyright : (C) 2016 Anu Mittal (anu22mittal@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 "HistogramDock.h" #include "backend/worksheet/plots/cartesian/Histogram.h" #include "backend/worksheet/Worksheet.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/GuiTools.h" #include #include #include #include #include #include #include #include #include +#include /*! \class HistogramDock \brief Provides a widget for editing the properties of the Histograms (2D-curves) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ HistogramDock::HistogramDock(QWidget* parent) : QWidget(parent), cbXColumn(nullptr), m_curve(nullptr), m_aspectTreeModel(nullptr), m_initializing(false) { ui.setupUi(this); //Tab "Values" QGridLayout* gridLayout = qobject_cast(ui.tabValues->layout()); cbValuesColumn = new TreeViewComboBox(ui.tabValues); gridLayout->addWidget(cbValuesColumn, 2, 2, 1, 1); //Tab "Filling" ui.cbFillingColorStyle->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); ui.bFillingOpen->setIcon( QIcon::fromTheme("document-open") ); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leFillingFileName->setCompleter(completer); //adjust layouts in the tabs for (int i=0; icount(); ++i){ QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //Slots //Values connect( ui.cbValuesType, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesTypeChanged(int)) ); connect( cbValuesColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(valuesColumnChanged(QModelIndex)) ); connect( ui.cbValuesPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesPositionChanged(int)) ); connect( ui.sbValuesDistance, SIGNAL(valueChanged(double)), this, SLOT(valuesDistanceChanged(double)) ); connect( ui.sbValuesRotation, SIGNAL(valueChanged(int)), this, SLOT(valuesRotationChanged(int)) ); connect( ui.sbValuesOpacity, SIGNAL(valueChanged(int)), this, SLOT(valuesOpacityChanged(int)) ); //TODO connect( ui.cbValuesFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesColumnFormatChanged(int)) ); connect( ui.leValuesPrefix, SIGNAL(returnPressed()), this, SLOT(valuesPrefixChanged()) ); connect( ui.leValuesSuffix, SIGNAL(returnPressed()), this, SLOT(valuesSuffixChanged()) ); connect( ui.kfrValuesFont, SIGNAL(fontSelected(QFont)), this, SLOT(valuesFontChanged(QFont)) ); connect( ui.kcbValuesColor, SIGNAL(changed(QColor)), this, SLOT(valuesColorChanged(QColor)) ); //Filling connect( ui.cbFillingPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingPositionChanged(int)) ); connect( ui.cbFillingType, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingTypeChanged(int)) ); connect( ui.cbFillingColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingColorStyleChanged(int)) ); connect( ui.cbFillingImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingImageStyleChanged(int)) ); connect( ui.cbFillingBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingBrushStyleChanged(int)) ); connect( ui.bFillingOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile())); connect( ui.leFillingFileName, SIGNAL(returnPressed()), this, SLOT(fileNameChanged()) ); connect( ui.leFillingFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbFillingFirstColor, SIGNAL(changed(QColor)), this, SLOT(fillingFirstColorChanged(QColor)) ); connect( ui.kcbFillingSecondColor, SIGNAL(changed(QColor)), this, SLOT(fillingSecondColorChanged(QColor)) ); connect( ui.sbFillingOpacity, SIGNAL(valueChanged(int)), this, SLOT(fillingOpacityChanged(int)) ); //template handler TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::Histogram); ui.verticalLayout->addWidget(templateHandler); templateHandler->show(); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); retranslateUi(); init(); } HistogramDock::~HistogramDock() { if (m_aspectTreeModel) delete m_aspectTreeModel; } //TODO: very similiar to ColumnDock void HistogramDock::updateValuesFormatWidgets(const AbstractColumn::ColumnMode columnMode) { ui.cbValuesFormat->clear(); switch (columnMode) { case AbstractColumn::Numeric: ui.cbValuesFormat->addItem(i18n("Decimal"), QVariant('f')); ui.cbValuesFormat->addItem(i18n("Scientific (e)"), QVariant('e')); ui.cbValuesFormat->addItem(i18n("Scientific (E)"), QVariant('E')); ui.cbValuesFormat->addItem(i18n("Automatic (e)"), QVariant('g')); ui.cbValuesFormat->addItem(i18n("Automatic (E)"), QVariant('G')); break; case AbstractColumn::Integer: break; case AbstractColumn::Text: ui.cbValuesFormat->addItem(i18n("Text"), QVariant()); break; case AbstractColumn::Month: ui.cbValuesFormat->addItem(i18n("Number without leading zero"), QVariant("M")); ui.cbValuesFormat->addItem(i18n("Number with leading zero"), QVariant("MM")); ui.cbValuesFormat->addItem(i18n("Abbreviated month name"), QVariant("MMM")); ui.cbValuesFormat->addItem(i18n("Full month name"), QVariant("MMMM")); break; case AbstractColumn::Day: ui.cbValuesFormat->addItem(i18n("Number without leading zero"), QVariant("d")); ui.cbValuesFormat->addItem(i18n("Number with leading zero"), QVariant("dd")); ui.cbValuesFormat->addItem(i18n("Abbreviated day name"), QVariant("ddd")); ui.cbValuesFormat->addItem(i18n("Full day name"), QVariant("dddd")); break; case AbstractColumn::DateTime: for (const auto& s: AbstractColumn::dateFormats()) ui.cbValuesFormat->addItem(s, QVariant(s)); for (const auto& s: AbstractColumn::timeFormats()) ui.cbValuesFormat->addItem(s, QVariant(s)); for (const auto& s1: AbstractColumn::dateFormats()) for (const auto& s2: AbstractColumn::timeFormats()) ui.cbValuesFormat->addItem(s1 + ' ' + s2, QVariant(s1 + ' ' + s2)); break; } ui.cbValuesFormat->setCurrentIndex(0); if (columnMode == AbstractColumn::Numeric) { ui.lValuesPrecision->show(); ui.sbValuesPrecision->show(); } else { ui.lValuesPrecision->hide(); ui.sbValuesPrecision->hide(); } if (columnMode == AbstractColumn::Text) { ui.lValuesFormatTop->hide(); ui.lValuesFormat->hide(); ui.cbValuesFormat->hide(); } else { ui.lValuesFormatTop->show(); ui.lValuesFormat->show(); ui.cbValuesFormat->show(); ui.cbValuesFormat->setCurrentIndex(0); } if (columnMode == AbstractColumn::DateTime) { ui.cbValuesFormat->setEditable(true); } else { ui.cbValuesFormat->setEditable(false); } } //TODO: very similiar to ColumnDock void HistogramDock::showValuesColumnFormat(const Column* column){ if (!column){ // no valid column is available // -> hide all the format properties widgets (equivalent to showing the properties of the column mode "Text") this->updateValuesFormatWidgets(AbstractColumn::Text); }else{ AbstractColumn::ColumnMode columnMode = column->columnMode(); //update the format widgets for the new column mode this->updateValuesFormatWidgets(columnMode); //show the actuall formating properties switch(columnMode) { case AbstractColumn::Numeric:{ Double2StringFilter * filter = static_cast(column->outputFilter()); ui.cbValuesFormat->setCurrentIndex(ui.cbValuesFormat->findData(filter->numericFormat())); ui.sbValuesPrecision->setValue(filter->numDigits()); break; } case AbstractColumn::Text: case AbstractColumn::Integer: break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { DateTime2StringFilter * filter = static_cast(column->outputFilter()); ui.cbValuesFormat->setCurrentIndex(ui.cbValuesFormat->findData(filter->format())); break; } } } } void HistogramDock::setModelIndexFromColumn(TreeViewComboBox* cb, const AbstractColumn* column) { if (column) cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else cb->setCurrentModelIndex(QModelIndex()); } void HistogramDock::retranslateUi() { //TODO: // uiGeneralTab.lName->setText(i18n("Name")); // uiGeneralTab.lComment->setText(i18n("Comment")); // uiGeneralTab.chkVisible->setText(i18n("Visible")); // uiGeneralTab.lXColumn->setText(i18n("x-data")); // uiGeneralTab.lYColumn->setText(i18n("y-data")); //TODO updatePenStyles, updateBrushStyles for all comboboxes } // "General"-tab void HistogramDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void HistogramDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void HistogramDock::visibilityChanged(bool state){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setVisible(state); } void HistogramDock::valuesColorChanged(const QColor& color){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setValuesColor(color); } void HistogramDock::init(){ //Values - ui.cbValuesType->addItem(i18n("no values")); + ui.cbValuesType->addItem(i18n("No values")); ui.cbValuesType->addItem("y"); - ui.cbValuesType->addItem(i18n("custom column")); + ui.cbValuesType->addItem(i18n("Custom Column")); - ui.cbValuesPosition->addItem(i18n("above")); - ui.cbValuesPosition->addItem(i18n("below")); - ui.cbValuesPosition->addItem(i18n("left")); - ui.cbValuesPosition->addItem(i18n("right")); + ui.cbValuesPosition->addItem(i18n("Above")); + ui.cbValuesPosition->addItem(i18n("Below")); + ui.cbValuesPosition->addItem(i18n("Left")); + ui.cbValuesPosition->addItem(i18n("Right")); //Filling ui.cbFillingPosition->clear(); - ui.cbFillingPosition->addItem(i18n("none")); - ui.cbFillingPosition->addItem(i18n("above")); - ui.cbFillingPosition->addItem(i18n("below")); - ui.cbFillingPosition->addItem(i18n("zero baseline")); - ui.cbFillingPosition->addItem(i18n("left")); - ui.cbFillingPosition->addItem(i18n("right")); + ui.cbFillingPosition->addItem(i18n("None")); + ui.cbFillingPosition->addItem(i18n("Above")); + ui.cbFillingPosition->addItem(i18n("Below")); + ui.cbFillingPosition->addItem(i18n("Zero Baseline")); + ui.cbFillingPosition->addItem(i18n("Left")); + ui.cbFillingPosition->addItem(i18n("Right")); ui.cbFillingType->clear(); - ui.cbFillingType->addItem(i18n("color")); - ui.cbFillingType->addItem(i18n("image")); - ui.cbFillingType->addItem(i18n("pattern")); + ui.cbFillingType->addItem(i18n("Color")); + ui.cbFillingType->addItem(i18n("Image")); + ui.cbFillingType->addItem(i18n("Pattern")); ui.cbFillingColorStyle->clear(); - ui.cbFillingColorStyle->addItem(i18n("single color")); - ui.cbFillingColorStyle->addItem(i18n("horizontal linear gradient")); - ui.cbFillingColorStyle->addItem(i18n("vertical linear gradient")); - ui.cbFillingColorStyle->addItem(i18n("diagonal linear gradient (start from top left)")); - ui.cbFillingColorStyle->addItem(i18n("diagonal linear gradient (start from bottom left)")); - ui.cbFillingColorStyle->addItem(i18n("radial gradient")); + ui.cbFillingColorStyle->addItem(i18n("Single Color")); + ui.cbFillingColorStyle->addItem(i18n("Horizontal Linear Gradient")); + ui.cbFillingColorStyle->addItem(i18n("Vertical Linear Gradient")); + ui.cbFillingColorStyle->addItem(i18n("Diagonal Linear Gradient (Start From Top Left)")); + ui.cbFillingColorStyle->addItem(i18n("Diagonal Linear Gradient (Start From Bottom Left)")); + ui.cbFillingColorStyle->addItem(i18n("Radial Gradient")); ui.cbFillingImageStyle->clear(); - ui.cbFillingImageStyle->addItem(i18n("scaled and cropped")); - ui.cbFillingImageStyle->addItem(i18n("scaled")); - ui.cbFillingImageStyle->addItem(i18n("scaled, keep proportions")); - ui.cbFillingImageStyle->addItem(i18n("centered")); - ui.cbFillingImageStyle->addItem(i18n("tiled")); - ui.cbFillingImageStyle->addItem(i18n("center tiled")); + ui.cbFillingImageStyle->addItem(i18n("Scaled and Cropped")); + ui.cbFillingImageStyle->addItem(i18n("Scaled")); + ui.cbFillingImageStyle->addItem(i18n("Scaled, Keep Proportions")); + ui.cbFillingImageStyle->addItem(i18n("Centered")); + ui.cbFillingImageStyle->addItem(i18n("Tiled")); + ui.cbFillingImageStyle->addItem(i18n("Center Tiled")); GuiTools::updateBrushStyles(ui.cbFillingBrushStyle, Qt::SolidPattern); } void HistogramDock::setModel() { QList list; list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<< "Histogram" <<"XYInterpolationCurve"<<"XYFitCurve"<<"XYFourierFilterCurve"; if (cbXColumn) { cbXColumn->setTopLevelClasses(list); } cbValuesColumn->setTopLevelClasses(list); list.clear(); list<<"Column"; m_aspectTreeModel->setSelectableAspects(list); if (cbXColumn) cbXColumn->setModel(m_aspectTreeModel); cbValuesColumn->setModel(m_aspectTreeModel); } void HistogramDock::setCurves(QList list){ m_initializing=true; m_curvesList=list; m_curve=list.first(); Q_ASSERT(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); setModel(); initGeneralTab(); initTabs(); m_initializing=false; } void HistogramDock::initGeneralTab(){ //if there are more then one curve in the list, disable the content in the tab "general" if (m_curvesList.size()==1){ uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.lXColumn->setEnabled(true); cbXColumn->setEnabled(true); this->setModelIndexFromColumn(cbXColumn, m_curve->xColumn()); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.lXColumn->setEnabled(false); cbXColumn->setEnabled(false); cbXColumn->setCurrentModelIndex(QModelIndex()); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve const Histogram::HistogramData& data = m_curve->histogramData(); uiGeneralTab.cbHistogramType->setCurrentIndex(data.type); uiGeneralTab.cbBins->setCurrentIndex(data.binsOption); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); connect(m_curve, SIGNAL(linePenChanged(QPen)), this, SLOT(curveLinePenChanged(QPen))); connect(m_curve, SIGNAL(visibilityChanged(bool)), this, SLOT(curveVisibilityChanged(bool))); uiGeneralTab.pbRecalculate->setEnabled(m_curve->isSourceDataChangedSinceLastPlot()); //Slots connect(m_curve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_curve, SIGNAL(histogramDataChanged(Histogram::HistogramData)), this, SLOT(curveHistogramDataChanged(Histogram::HistogramData))); } //************************************************************* //**** SLOTs for changes triggered in HistogramDock ***** //************************************************************* void HistogramDock::recalculateClicked() { Histogram::HistogramData data; if( data.type != (Histogram::HistogramType)uiGeneralTab.cbHistogramType->currentIndex()) data.type = (Histogram::HistogramType)uiGeneralTab.cbHistogramType->currentIndex(); data.binsOption= (Histogram::BinsOption)uiGeneralTab.cbBins->currentIndex(); data.binValue = uiGeneralTab.sbBins->value(); // m_curve->retransform(); for (auto* curve : m_curvesList) dynamic_cast(curve)->setHistogramData(data); uiGeneralTab.pbRecalculate->setEnabled(false); } void HistogramDock::enableRecalculate() const { if (m_initializing) return; uiGeneralTab.pbRecalculate->setEnabled(true); } void HistogramDock::curveLinePenChanged(const QPen& pen) { m_initializing = true; uiGeneralTab.kcbLineColor->setColor( pen.color()); m_initializing = false; } //Values-Tab void HistogramDock::curveValuesTypeChanged(Histogram::ValuesType type) { m_initializing = true; ui.cbValuesType->setCurrentIndex((int) type); m_initializing = false; } void HistogramDock::curveValuesColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromColumn(cbValuesColumn, column); m_initializing = false; } void HistogramDock::curveValuesPositionChanged(Histogram::ValuesPosition position) { m_initializing = true; ui.cbValuesPosition->setCurrentIndex((int) position); m_initializing = false; } void HistogramDock::curveValuesDistanceChanged(qreal distance) { m_initializing = true; ui.sbValuesDistance->setValue( Worksheet::convertFromSceneUnits(distance, Worksheet::Point) ); m_initializing = false; } void HistogramDock::curveValuesRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbValuesRotation->setValue(angle); m_initializing = false; } void HistogramDock::curveValuesOpacityChanged(qreal opacity) { m_initializing = true; ui.sbValuesOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void HistogramDock::curveValuesPrefixChanged(const QString& prefix) { m_initializing = true; ui.leValuesPrefix->setText(prefix); m_initializing = false; } void HistogramDock::curveValuesSuffixChanged(const QString& suffix) { m_initializing = true; ui.leValuesSuffix->setText(suffix); m_initializing = false; } void HistogramDock::curveValuesFontChanged(QFont font) { m_initializing = true; font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrValuesFont->setFont(font); m_initializing = false; } void HistogramDock::curveValuesColorChanged(QColor color) { m_initializing = true; ui.kcbValuesColor->setColor(color); m_initializing = false; } /*! called when the type of the values (none, x, y, (x,y) etc.) was changed. */ void HistogramDock::valuesTypeChanged(int index) { Histogram::ValuesType valuesType = Histogram::ValuesType(index); if (valuesType == Histogram::NoValues){ //no values are to paint -> deactivate all the pertinent widgets ui.cbValuesPosition->setEnabled(false); ui.lValuesColumn->hide(); cbValuesColumn->hide(); ui.sbValuesDistance->setEnabled(false); ui.sbValuesRotation->setEnabled(false); ui.sbValuesOpacity->setEnabled(false); ui.cbValuesFormat->setEnabled(false); ui.cbValuesFormat->setEnabled(false); ui.sbValuesPrecision->setEnabled(false); ui.leValuesPrefix->setEnabled(false); ui.leValuesSuffix->setEnabled(false); ui.kfrValuesFont->setEnabled(false); ui.kcbValuesColor->setEnabled(false); } else { ui.cbValuesPosition->setEnabled(true); ui.sbValuesDistance->setEnabled(true); ui.sbValuesRotation->setEnabled(true); ui.sbValuesOpacity->setEnabled(true); ui.cbValuesFormat->setEnabled(true); ui.sbValuesPrecision->setEnabled(true); ui.leValuesPrefix->setEnabled(true); ui.leValuesSuffix->setEnabled(true); ui.kfrValuesFont->setEnabled(true); ui.kcbValuesColor->setEnabled(true); const Column* column; if (valuesType == Histogram::ValuesCustomColumn) { ui.lValuesColumn->show(); cbValuesColumn->show(); column= static_cast(cbValuesColumn->currentModelIndex().internalPointer()); } else { ui.lValuesColumn->hide(); cbValuesColumn->hide(); column = static_cast(m_curve->xColumn()); } this->showValuesColumnFormat(column); } if (m_initializing) return; for (auto* curve: m_curvesList) curve->setValuesType(valuesType); } /*! called when the custom column for the values was changed. */ void HistogramDock::valuesColumnChanged(const QModelIndex& index){ if (m_initializing) return; Column* column = static_cast(index.internalPointer()); this->showValuesColumnFormat(column); for (auto* curve: m_curvesList) { //TODO save also the format of the currently selected column for the values (precision etc.) curve->setValuesColumn(column); } } void HistogramDock::curveVisibilityChanged(bool on) { m_initializing = true; uiGeneralTab.chkVisible->setChecked(on); m_initializing = false; } void HistogramDock::valuesPositionChanged(int index){ if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesPosition(Histogram::ValuesPosition(index)); } void HistogramDock::valuesDistanceChanged(double value){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setValuesDistance( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void HistogramDock::valuesRotationChanged(int value){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setValuesRotationAngle(value); } void HistogramDock::valuesOpacityChanged(int value){ if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve: m_curvesList) curve->setValuesOpacity(opacity); } void HistogramDock::valuesPrefixChanged(){ if (m_initializing) return; QString prefix = ui.leValuesPrefix->text(); for (auto* curve: m_curvesList) curve->setValuesPrefix(prefix); } void HistogramDock::valuesSuffixChanged(){ if (m_initializing) return; QString suffix = ui.leValuesSuffix->text(); for (auto* curve: m_curvesList) curve->setValuesSuffix(suffix); } void HistogramDock::valuesFontChanged(const QFont& font){ if (m_initializing) return; QFont valuesFont = font; valuesFont.setPixelSize( Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Point) ); for (auto* curve: m_curvesList) curve->setValuesFont(valuesFont); } //Filling void HistogramDock::curveFillingPositionChanged(Histogram::FillingPosition position) { m_initializing = true; ui.cbFillingPosition->setCurrentIndex((int)position); m_initializing = false; } void HistogramDock::curveFillingTypeChanged(PlotArea::BackgroundType type){ m_initializing = true; ui.cbFillingType->setCurrentIndex(type); m_initializing = false; } void HistogramDock::curveFillingColorStyleChanged(PlotArea::BackgroundColorStyle style){ m_initializing = true; ui.cbFillingColorStyle->setCurrentIndex(style); m_initializing = false; } void HistogramDock::curveFillingImageStyleChanged(PlotArea::BackgroundImageStyle style){ m_initializing = true; ui.cbFillingImageStyle->setCurrentIndex(style); m_initializing = false; } void HistogramDock::curveFillingBrushStyleChanged(Qt::BrushStyle style){ m_initializing = true; ui.cbFillingBrushStyle->setCurrentIndex(style); m_initializing = false; } void HistogramDock::curveFillingFirstColorChanged(QColor& color){ m_initializing = true; ui.kcbFillingFirstColor->setColor(color); m_initializing = false; } void HistogramDock::curveFillingSecondColorChanged(QColor& color){ m_initializing = true; ui.kcbFillingSecondColor->setColor(color); m_initializing = false; } void HistogramDock::curveFillingFileNameChanged(QString& filename){ m_initializing = true; ui.leFillingFileName->setText(filename); m_initializing = false; } void HistogramDock::curveFillingOpacityChanged(float opacity){ m_initializing = true; ui.sbFillingOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void HistogramDock::initTabs() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1){ this->setModelIndexFromColumn(cbValuesColumn, m_curve->valuesColumn()); }else { cbValuesColumn->setCurrentModelIndex(QModelIndex()); } //show the properties of the first curve KConfig config("", KConfig::SimpleConfig); loadConfig(config); //Slots //Values-Tab connect(m_curve, SIGNAL(valuesTypeChanged(Histogram::ValuesType)), this, SLOT(curveValuesTypeChanged(Histogram::ValuesType))); connect(m_curve, SIGNAL(valuesColumnChanged(const AbstractColumn*)), this, SLOT(curveValuesColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(valuesPositionChanged(Histogram::ValuesPosition)), this, SLOT(curveValuesPositionChanged(Histogram::ValuesPosition))); connect(m_curve, SIGNAL(valuesDistanceChanged(qreal)), this, SLOT(curveValuesDistanceChanged(qreal))); connect(m_curve, SIGNAL(valuesOpacityChanged(qreal)), this, SLOT(curveValuesOpacityChanged(qreal))); connect(m_curve, SIGNAL(valuesRotationAngleChanged(qreal)), this, SLOT(curveValuesRotationAngleChanged(qreal))); connect(m_curve, SIGNAL(valuesPrefixChanged(QString)), this, SLOT(curveValuesPrefixChanged(QString))); connect(m_curve, SIGNAL(valuesSuffixChanged(QString)), this, SLOT(curveValuesSuffixChanged(QString))); connect(m_curve, SIGNAL(valuesFontChanged(QFont)), this, SLOT(curveValuesFontChanged(QFont))); connect(m_curve, SIGNAL(valuesColorChanged(QColor)), this, SLOT(curveValuesColorChanged(QColor))); //Filling-Tab connect( m_curve, SIGNAL(fillingPositionChanged(Histogram::FillingPosition)), this, SLOT(curveFillingPositionChanged(Histogram::FillingPosition)) ); connect( m_curve, SIGNAL(fillingTypeChanged(PlotArea::BackgroundType)), this, SLOT(curveFillingTypeChanged(PlotArea::BackgroundType)) ); connect( m_curve, SIGNAL(fillingColorStyleChanged(PlotArea::BackgroundColorStyle)), this, SLOT(curveFillingColorStyleChanged(PlotArea::BackgroundColorStyle)) ); connect( m_curve, SIGNAL(fillingImageStyleChanged(PlotArea::BackgroundImageStyle)), this, SLOT(curveFillingImageStyleChanged(PlotArea::BackgroundImageStyle)) ); connect( m_curve, SIGNAL(fillingBrushStyleChanged(Qt::BrushStyle)), this, SLOT(curveFillingBrushStyleChanged(Qt::BrushStyle)) ); connect( m_curve, SIGNAL(fillingFirstColorChanged(QColor&)), this, SLOT(curveFillingFirstColorChanged(QColor&)) ); connect( m_curve, SIGNAL(fillingSecondColorChanged(QColor&)), this, SLOT(curveFillingSecondColorChanged(QColor&)) ); connect( m_curve, SIGNAL(fillingFileNameChanged(QString&)), this, SLOT(curveFillingFileNameChanged(QString&)) ); connect( m_curve, SIGNAL(fillingOpacityChanged(float)), this, SLOT(curveFillingOpacityChanged(float)) ); } //Filling-tab void HistogramDock::fillingPositionChanged(int index){ Histogram::FillingPosition fillingPosition = Histogram::FillingPosition(index); bool b = (fillingPosition != Histogram::NoFilling); ui.cbFillingType->setEnabled(b); ui.cbFillingColorStyle->setEnabled(b); ui.cbFillingBrushStyle->setEnabled(b); ui.cbFillingImageStyle->setEnabled(b); ui.kcbFillingFirstColor->setEnabled(b); ui.kcbFillingSecondColor->setEnabled(b); ui.leFillingFileName->setEnabled(b); ui.bFillingOpen->setEnabled(b); ui.sbFillingOpacity->setEnabled(b); if (m_initializing) return; for (auto* curve: m_curvesList) curve->setFillingPosition(fillingPosition); } void HistogramDock::fillingTypeChanged(int index){ PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color){ ui.lFillingColorStyle->show(); ui.cbFillingColorStyle->show(); ui.lFillingImageStyle->hide(); ui.cbFillingImageStyle->hide(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); ui.lFillingFileName->hide(); ui.leFillingFileName->hide(); ui.bFillingOpen->hide(); ui.lFillingFirstColor->show(); ui.kcbFillingFirstColor->show(); PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle) ui.cbFillingColorStyle->currentIndex(); if (style == PlotArea::SingleColor){ ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); }else{ ui.lFillingFirstColor->setText(i18n("First Color")); ui.lFillingSecondColor->show(); ui.kcbFillingSecondColor->show(); } }else if(type == PlotArea::Image){ ui.lFillingColorStyle->hide(); ui.cbFillingColorStyle->hide(); ui.lFillingImageStyle->show(); ui.cbFillingImageStyle->show(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); ui.lFillingFileName->show(); ui.leFillingFileName->show(); ui.bFillingOpen->show(); ui.lFillingFirstColor->hide(); ui.kcbFillingFirstColor->hide(); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); }else if(type == PlotArea::Pattern) { ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingColorStyle->hide(); ui.cbFillingColorStyle->hide(); ui.lFillingImageStyle->hide(); ui.cbFillingImageStyle->hide(); ui.lFillingBrushStyle->show(); ui.cbFillingBrushStyle->show(); ui.lFillingFileName->hide(); ui.leFillingFileName->hide(); ui.bFillingOpen->hide(); ui.lFillingFirstColor->show(); ui.kcbFillingFirstColor->show(); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } if (m_initializing) return; for (auto* curve: m_curvesList) curve->setFillingType(type); } void HistogramDock::fillingColorStyleChanged(int index){ PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor){ ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } else { ui.lFillingFirstColor->setText(i18n("First Color")); ui.lFillingSecondColor->show(); ui.kcbFillingSecondColor->show(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); } if (m_initializing) return; for (auto* curve: m_curvesList) curve->setFillingColorStyle(style); } void HistogramDock::fillingImageStyleChanged(int index){ if (m_initializing) return; PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; for (auto* curve: m_curvesList) curve->setFillingImageStyle(style); } void HistogramDock::fillingBrushStyleChanged(int index){ if (m_initializing) return; Qt::BrushStyle style = (Qt::BrushStyle)index; for (auto* curve: m_curvesList) curve->setFillingBrushStyle(style); } void HistogramDock::fillingFirstColorChanged(const QColor& c){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setFillingFirstColor(c); } void HistogramDock::fillingSecondColorChanged(const QColor& c){ if (m_initializing) return; for (auto* curve: m_curvesList) curve->setFillingSecondColor(c); } /*! opens a file dialog and lets the user select the image file. */ void HistogramDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "HistogramDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const QByteArray& format : QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastImageDir", newDir); } ui.leFillingFileName->setText( path ); for (auto* curve: m_curvesList) curve->setFillingFileName(path); } void HistogramDock::fileNameChanged(){ if (m_initializing) return; QString fileName = ui.leFillingFileName->text(); for (auto* curve: m_curvesList) curve->setFillingFileName(fileName); } void HistogramDock::fillingOpacityChanged(int value){ if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve: m_curvesList) curve->setFillingOpacity(opacity); } void HistogramDock::loadConfig(KConfig& config) { KConfigGroup group = config.group(QLatin1String("Histogram")); //General //we don't load/save the settings in the general-tab, since they are not style related. //It doesn't make sense to load/save them in the template. //This data is read in HistogramDock::setCurves(). //Values ui.cbValuesType->setCurrentIndex( group.readEntry("ValuesType", (int) m_curve->valuesType()) ); ui.cbValuesPosition->setCurrentIndex( group.readEntry("ValuesPosition", (int) m_curve->valuesPosition()) ); ui.sbValuesDistance->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ValuesDistance", m_curve->valuesDistance()), Worksheet::Point) ); ui.sbValuesRotation->setValue( group.readEntry("ValuesRotation", m_curve->valuesRotationAngle()) ); ui.sbValuesOpacity->setValue( round(group.readEntry("ValuesOpacity",m_curve->valuesOpacity())*100.0) ); ui.leValuesPrefix->setText( group.readEntry("ValuesPrefix", m_curve->valuesPrefix()) ); ui.leValuesSuffix->setText( group.readEntry("ValuesSuffix", m_curve->valuesSuffix()) ); QFont valuesFont = m_curve->valuesFont(); valuesFont.setPointSizeF( round(Worksheet::convertFromSceneUnits(valuesFont.pixelSize(), Worksheet::Point)) ); ui.kfrValuesFont->setFont( group.readEntry("ValuesFont", valuesFont) ); ui.kcbValuesColor->setColor( group.readEntry("ValuesColor", m_curve->valuesColor()) ); //Filling ui.cbFillingPosition->setCurrentIndex( group.readEntry("FillingPosition", (int) m_curve->fillingPosition()) ); ui.cbFillingType->setCurrentIndex( group.readEntry("FillingType", (int) m_curve->fillingType()) ); ui.cbFillingColorStyle->setCurrentIndex( group.readEntry("FillingColorStyle", (int) m_curve->fillingColorStyle()) ); ui.cbFillingImageStyle->setCurrentIndex( group.readEntry("FillingImageStyle", (int) m_curve->fillingImageStyle()) ); ui.cbFillingBrushStyle->setCurrentIndex( group.readEntry("FillingBrushStyle", (int) m_curve->fillingBrushStyle()) ); ui.leFillingFileName->setText( group.readEntry("FillingFileName", m_curve->fillingFileName()) ); ui.kcbFillingFirstColor->setColor( group.readEntry("FillingFirstColor", m_curve->fillingFirstColor()) ); ui.kcbFillingSecondColor->setColor( group.readEntry("FillingSecondColor", m_curve->fillingSecondColor()) ); ui.sbFillingOpacity->setValue( round(group.readEntry("FillingOpacity", m_curve->fillingOpacity())*100.0) ); } void HistogramDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); // Tab "General" QGridLayout* gridLayout = qobject_cast(generalTab->layout()); cbXColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXColumn, 2, 2, 1, 1); //show the properties of the first curve //bins option uiGeneralTab.cbBins->addItem(i18n("By Number")); uiGeneralTab.cbBins->addItem(i18n("By width")); uiGeneralTab.cbBins->addItem(i18n("Square-root rule")); uiGeneralTab.cbBins->addItem(i18n("Rice rule")); uiGeneralTab.cbBins->addItem(i18n("Sturgis rule")); //types options uiGeneralTab.cbHistogramType->addItem(i18n("Ordinary Histogram")); uiGeneralTab.cbHistogramType->addItem(i18n("Cumulative Histogram")); uiGeneralTab.cbHistogramType->addItem(i18n("AvgShifted Histogram")); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); //General connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &HistogramDock::nameChanged); connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &HistogramDock::commentChanged); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.kcbLineColor, SIGNAL(changed(QColor)), this, SLOT(lineColorChanged(QColor)) ); connect( cbXColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xColumnChanged(QModelIndex)) ); connect( uiGeneralTab.cbHistogramType, SIGNAL(currentIndexChanged(int)), this, SLOT(histogramTypeChanged(int)) ); connect( uiGeneralTab.cbBins, SIGNAL(currentIndexChanged(int)), this, SLOT(binsOptionChanged(int)) ); connect( uiGeneralTab.sbBins, SIGNAL(valueChanged(int)), this, SLOT(binValueChanged(int)) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); } void HistogramDock::histogramTypeChanged(int index) { Histogram::HistogramType histogramType = Histogram::HistogramType(index); m_curve->setHistrogramType(histogramType); enableRecalculate(); } void HistogramDock::binValueChanged(int value) { m_curve->setBinValue(value); enableRecalculate(); } void HistogramDock::binsOptionChanged(int index){ Histogram::BinsOption binsOption = Histogram::BinsOption(index); m_curve->setbinsOption(binsOption); enableRecalculate(); } void HistogramDock::lineColorChanged(const QColor& color){ if (m_initializing) return; QPen pen; for (auto* curve: m_curvesList) { pen = curve->linePen(); pen.setColor(color); curve->setLinePen(pen); } } void HistogramDock::xColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column); } for (auto* curve : m_curvesList) { curve->setXColumn(column); } } void HistogramDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_curvesList.size(); if (size>1) m_curve->beginMacro(i18n("%1 xy-curves: template \"%2\" loaded", size, name)); else m_curve->beginMacro(i18n("%1: template \"%2\" loaded", m_curve->name(), name)); this->loadConfig(config); m_curve->endMacro(); } void HistogramDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "Histogram" ); //Values group.writeEntry("ValuesType", ui.cbValuesType->currentIndex()); group.writeEntry("ValuesPosition", ui.cbValuesPosition->currentIndex()); group.writeEntry("ValuesDistance", Worksheet::convertToSceneUnits(ui.sbValuesDistance->value(),Worksheet::Point)); group.writeEntry("ValuesRotation", ui.sbValuesRotation->value()); group.writeEntry("ValuesOpacity", ui.sbValuesOpacity->value()/100); group.writeEntry("ValuesPrefix", ui.leValuesPrefix->text()); group.writeEntry("ValuesSuffix", ui.leValuesSuffix->text()); group.writeEntry("ValuesFont", ui.kfrValuesFont->font()); group.writeEntry("ValuesColor", ui.kcbValuesColor->color()); //Filling group.writeEntry("FillingPosition", ui.cbFillingPosition->currentIndex()); group.writeEntry("FillingType", ui.cbFillingType->currentIndex()); group.writeEntry("FillingColorStyle", ui.cbFillingColorStyle->currentIndex()); group.writeEntry("FillingImageStyle", ui.cbFillingImageStyle->currentIndex()); group.writeEntry("FillingBrushStyle", ui.cbFillingBrushStyle->currentIndex()); group.writeEntry("FillingFileName", ui.leFillingFileName->text()); group.writeEntry("FillingFirstColor", ui.kcbFillingFirstColor->color()); group.writeEntry("FillingSecondColor", ui.kcbFillingSecondColor->color()); group.writeEntry("FillingOpacity", ui.sbFillingOpacity->value()/100.0); config.sync(); } //************************************************************* //*********** SLOTs for changes triggered in Histogram ********** //************************************************************* //General-Tab void HistogramDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) { uiGeneralTab.leName->setText(aspect->name()); } else if (aspect->comment() != uiGeneralTab.leComment->text()) { uiGeneralTab.leComment->setText(aspect->comment()); } m_initializing = false; } void HistogramDock::curveHistogramDataChanged(Histogram::HistogramData data) { m_initializing = true; uiGeneralTab.cbHistogramType->setCurrentIndex(data.type); uiGeneralTab.cbBins->setCurrentIndex(data.binsOption); uiGeneralTab.sbBins->setValue(data.binValue); m_initializing = false; } diff --git a/src/kdefrontend/dockwidgets/LiveDataDock.cpp b/src/kdefrontend/dockwidgets/LiveDataDock.cpp index ac48df68d..3f0284701 100644 --- a/src/kdefrontend/dockwidgets/LiveDataDock.cpp +++ b/src/kdefrontend/dockwidgets/LiveDataDock.cpp @@ -1,218 +1,218 @@ /*************************************************************************** File : LiveDataDock.cpp Project : LabPlot Description : Dock widget for live data properties -------------------------------------------------------------------- Copyright : (C) 2017 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 "LiveDataDock.h" -#include +#include LiveDataDock::LiveDataDock(QWidget* parent) : QWidget(parent), m_paused(false) { ui.setupUi(this); ui.bUpdateNow->setIcon(QIcon::fromTheme(QLatin1String("view-refresh"))); connect(ui.bPausePlayReading, &QPushButton::clicked, this, &LiveDataDock::pauseContinueReading); connect(ui.bUpdateNow, &QPushButton::clicked, this, &LiveDataDock::updateNow); connect(ui.sbUpdateInterval, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::updateIntervalChanged); connect(ui.leKeepNValues, &QLineEdit::textChanged, this, &LiveDataDock::keepNvaluesChanged); connect(ui.sbSampleRate, static_cast(&QSpinBox::valueChanged), this, &LiveDataDock::sampleRateChanged); connect(ui.cbUpdateType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::updateTypeChanged); connect(ui.cbReadingType, static_cast(&QComboBox::currentIndexChanged), this, &LiveDataDock::readingTypeChanged); } LiveDataDock::~LiveDataDock() { } /*! * \brief Sets the live data sources of this dock widget * \param sources */ void LiveDataDock::setLiveDataSources(const QList& sources) { m_liveDataSources = sources; const LiveDataSource* const fds = sources.at(0); ui.sbUpdateInterval->setValue(fds->updateInterval()); ui.cbUpdateType->setCurrentIndex(static_cast(fds->updateType())); ui.cbReadingType->setCurrentIndex(static_cast(fds->readingType())); if (fds->updateType() == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); } if (fds->isPaused()) { ui.bPausePlayReading->setText(i18n("Continue reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { ui.bPausePlayReading->setText(i18n("Pause reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } if(!fds->keepLastValues()) { ui.leKeepNValues->hide(); ui.lKeepNvalues->hide(); } else { ui.leKeepNValues->setValidator(new QIntValidator(2, 100000)); ui.leKeepNValues->setText(QString::number(fds->keepNvalues())); } if (fds->sourceType() != LiveDataSource::SourceType::FileOrPipe) { int itemIdx = -1; for (int i = 0; i < ui.cbReadingType->count(); ++i) { if (ui.cbReadingType->itemText(i) == QLatin1String("Read whole file")) { itemIdx = i; break; } } if (itemIdx != -1) { ui.cbReadingType->removeItem(itemIdx); } } if (fds->readingType() == LiveDataSource::ReadingType::TillEnd) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else if (fds->readingType() == LiveDataSource::ReadingType::WholeFile) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else ui.sbSampleRate->setValue(fds->sampleRate()); } /*! * \brief Modifies the sample rate of the live data sources * \param sampleRate */ void LiveDataDock::sampleRateChanged(int sampleRate) { for (auto* source : m_liveDataSources) source->setSampleRate(sampleRate); } /*! * \brief Updates the live data sources now */ void LiveDataDock::updateNow() { for (auto* source : m_liveDataSources) source->updateNow(); } /*! * \brief LiveDataDock::updateTypeChanged * \param idx */ void LiveDataDock::updateTypeChanged(int idx) { LiveDataSource::UpdateType type = static_cast(idx); if (type == LiveDataSource::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); for (auto* source: m_liveDataSources) { source->setUpdateType(type); source->setUpdateInterval(ui.sbUpdateInterval->value()); source->setFileWatched(false); } } else if (type == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); for (auto* source: m_liveDataSources) { source->setFileWatched(true); source->setUpdateType(type); } } } /*! * \brief Handles the change of the reading type in the dock widget * \param idx */ void LiveDataDock::readingTypeChanged(int idx) { LiveDataSource::ReadingType type = static_cast(idx); if (type == LiveDataSource::ReadingType::TillEnd) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else { ui.lSampleRate->show(); ui.sbSampleRate->show(); } for (auto* source : m_liveDataSources) source->setReadingType(type); } /*! * \brief Modifies the update interval of the live data sources * \param updateInterval */ void LiveDataDock::updateIntervalChanged(int updateInterval) { for (auto* source : m_liveDataSources) source->setUpdateInterval(updateInterval); } /*! * \brief Modifies the number of samples to keep in each of the live data sources * \param keepNvalues */ void LiveDataDock::keepNvaluesChanged(const QString& keepNvalues) { for (auto* source : m_liveDataSources) source->setKeepNvalues(keepNvalues.toInt()); } /*! * \brief Pauses the reading of the live data source */ void LiveDataDock::pauseReading() { for (auto* source: m_liveDataSources) source->pauseReading(); } /*! * \brief Continues the reading of the live data source */ void LiveDataDock::continueReading() { for (auto* source: m_liveDataSources) source->continueReading(); } /*! * \brief Handles the pausing/continuing of reading of the live data source */ void LiveDataDock::pauseContinueReading() { m_paused = !m_paused; if (m_paused) { pauseReading(); ui.bPausePlayReading->setText(i18n("Continue reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-record"))); } else { continueReading(); ui.bPausePlayReading->setText(i18n("Pause reading")); ui.bPausePlayReading->setIcon(QIcon::fromTheme(QLatin1String("media-playback-pause"))); } } diff --git a/src/kdefrontend/dockwidgets/NoteDock.cpp b/src/kdefrontend/dockwidgets/NoteDock.cpp index 91c7a1c49..1adc3c242 100644 --- a/src/kdefrontend/dockwidgets/NoteDock.cpp +++ b/src/kdefrontend/dockwidgets/NoteDock.cpp @@ -1,129 +1,129 @@ /*************************************************************************** File : NotesDock.cpp Project : LabPlot Description : Dock for configuring notes -------------------------------------------------------------------- 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 "NoteDock.h" #include "kdefrontend/TemplateHandler.h" #include #include -#include +#include NoteDock::NoteDock(QWidget *parent) : QWidget(parent), m_initializing(false), m_notes(0) { ui.setupUi(this); connect(ui.leName, &QLineEdit::textChanged, this, [this]() { nameChanged(ui.leName->text()); }); connect(ui.leComment, &QLineEdit::textChanged, this, [this]() { commentChanged(ui.leComment->text()); }); connect(ui.kcbBgColor, &KColorButton::changed, this, &NoteDock::backgroundColorChanged); connect(ui.kcbTextColor, &KColorButton::changed, this, &NoteDock::textColorChanged); connect(ui.kfrTextFont, &KFontRequester::fontSelected, this, &NoteDock::textFontChanged); TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::Worksheet); ui.gridLayout->addWidget(templateHandler, 8, 3); templateHandler->show(); connect(templateHandler, &TemplateHandler::loadConfigRequested, this, &NoteDock::loadConfigFromTemplate); connect(templateHandler, &TemplateHandler::saveConfigRequested, this, &NoteDock::saveConfigAsTemplate); } void NoteDock::setNotesList(QList< Note* > list) { m_notesList = list; m_notes = list.first(); m_initializing=true; ui.leName->setText(m_notes->name()); ui.kcbBgColor->setColor(m_notes->backgroundColor()); ui.kcbTextColor->setColor(m_notes->textColor()); ui.kfrTextFont->setFont(m_notes->textFont()); m_initializing=false; } //************************************************************* //********** SLOTs for changes triggered in NoteDock ********** //************************************************************* void NoteDock::nameChanged(const QString& name) { if (m_initializing) return; m_notes->setName(name); } void NoteDock::commentChanged(const QString& name) { if (m_initializing) return; m_notes->setComment(name); } void NoteDock::backgroundColorChanged(const QColor& color) { if (m_initializing) return; for (auto* note : m_notesList) note->setBackgroundColor(color); } void NoteDock::textColorChanged(const QColor& color) { if (m_initializing) return; for (auto* note : m_notesList) note->setTextColor(color); } void NoteDock::textFontChanged(const QFont& font) { if (m_initializing) return; for (auto* note : m_notesList) note->setTextFont(font); } //************************************************************* //************************* Settings ************************** //************************************************************* void NoteDock::loadConfigFromTemplate(KConfig& config) { QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); KConfigGroup group = config.group("Notes"); ui.kcbBgColor->setColor(group.readEntry("BackgroundColor", m_notes->backgroundColor())); ui.kcbTextColor->setColor(group.readEntry("TextColor", m_notes->textColor())); ui.kfrTextFont->setFont(group.readEntry("TextColor", m_notes->textFont())); } void NoteDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group("Notes"); group.writeEntry("BackgroundColor", ui.kcbBgColor->color()); group.writeEntry("TextColor", ui.kcbTextColor->color()); group.writeEntry("TextFont", ui.kfrTextFont->font()); } diff --git a/src/kdefrontend/dockwidgets/WorksheetDock.cpp b/src/kdefrontend/dockwidgets/WorksheetDock.cpp index b9b8f0188..612c1db07 100644 --- a/src/kdefrontend/dockwidgets/WorksheetDock.cpp +++ b/src/kdefrontend/dockwidgets/WorksheetDock.cpp @@ -1,955 +1,956 @@ /*************************************************************************** File : WorksheetDock.cpp Project : LabPlot Description : widget for worksheet properties -------------------------------------------------------------------- Copyright : (C) 2010-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2013 by Stefan Gerlach (stefan.gerlach@uni-konstanz.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 "WorksheetDock.h" #include "kdefrontend/GuiTools.h" #include "kdefrontend/ThemeHandler.h" #include "kdefrontend/TemplateHandler.h" #include #include #include #include #include #include #include #include +#include // a couple of standard sizes in mm, taken from qprinter.cpp const int numOfPaperSizes = 30; const float qt_paperSizes[numOfPaperSizes][2] = { {210, 297}, // A4 {176, 250}, // B5 {215.9f, 279.4f}, // Letter {215.9f, 355.6f}, // Legal {190.5f, 254}, // Executive {841, 1189}, // A0 {594, 841}, // A1 {420, 594}, // A2 {297, 420}, // A3 {148, 210}, // A5 {105, 148}, // A6 {74, 105}, // A7 {52, 74}, // A8 {37, 52}, // A8 {1000, 1414}, // B0 {707, 1000}, // B1 {31, 44}, // B10 {500, 707}, // B2 {353, 500}, // B3 {250, 353}, // B4 {125, 176}, // B6 {88, 125}, // B7 {62, 88}, // B8 {33, 62}, // B9 {163, 229}, // C5E {105, 241}, // US Common {110, 220}, // DLE {210, 330}, // Folio {431.8f, 279.4f}, // Ledger {279.4f, 431.8f} // Tabloid }; /*! \class WorksheetDock \brief Provides a widget for editing the properties of the worksheets currently selected in the project explorer. \ingroup kdefrontend */ WorksheetDock::WorksheetDock(QWidget *parent): QWidget(parent), m_worksheet(0) { ui.setupUi(this); //Background-tab ui.cbBackgroundColorStyle->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leBackgroundFileName->setCompleter(completer); //adjust layouts in the tabs for (int i=0; icount(); ++i) { QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //SLOTs //General connect(ui.leName, &QLineEdit::textChanged, this, &WorksheetDock::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &WorksheetDock::commentChanged); connect( ui.cbSize, SIGNAL(currentIndexChanged(int)), this, SLOT(sizeChanged(int)) ); connect( ui.sbWidth, SIGNAL(valueChanged(double)), this, SLOT(sizeChanged()) ); connect( ui.sbHeight, SIGNAL(valueChanged(double)), this, SLOT(sizeChanged()) ); connect( ui.cbOrientation, SIGNAL(currentIndexChanged(int)), this, SLOT(orientationChanged(int)) ); //Background connect( ui.cbBackgroundType, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundTypeChanged(int)) ); connect( ui.cbBackgroundColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundColorStyleChanged(int)) ); connect( ui.cbBackgroundImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundImageStyleChanged(int)) ); connect( ui.cbBackgroundBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(backgroundBrushStyleChanged(int)) ); connect( ui.bOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile())); connect( ui.leBackgroundFileName, SIGNAL(returnPressed()), this, SLOT(fileNameChanged()) ); connect( ui.leBackgroundFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbBackgroundFirstColor, SIGNAL(changed(QColor)), this, SLOT(backgroundFirstColorChanged(QColor)) ); connect( ui.kcbBackgroundSecondColor, SIGNAL(changed(QColor)), this, SLOT(backgroundSecondColorChanged(QColor)) ); connect( ui.sbBackgroundOpacity, SIGNAL(valueChanged(int)), this, SLOT(backgroundOpacityChanged(int)) ); //Layout connect( ui.chScaleContent, SIGNAL(clicked(bool)), this, SLOT(scaleContentChanged(bool)) ); connect( ui.sbLayoutTopMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutTopMarginChanged(double)) ); connect( ui.sbLayoutBottomMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutBottomMarginChanged(double)) ); connect( ui.sbLayoutLeftMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutLeftMarginChanged(double)) ); connect( ui.sbLayoutRightMargin, SIGNAL(valueChanged(double)), this, SLOT(layoutRightMarginChanged(double)) ); connect( ui.sbLayoutHorizontalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutHorizontalSpacingChanged(double)) ); connect( ui.sbLayoutVerticalSpacing, SIGNAL(valueChanged(double)), this, SLOT(layoutVerticalSpacingChanged(double)) ); connect( ui.sbLayoutRowCount, SIGNAL(valueChanged(int)), this, SLOT(layoutRowCountChanged(int)) ); connect( ui.sbLayoutColumnCount, SIGNAL(valueChanged(int)), this, SLOT(layoutColumnCountChanged(int)) ); //theme and template handlers QFrame* frame = new QFrame(this); QHBoxLayout* layout = new QHBoxLayout(frame); m_themeHandler = new ThemeHandler(this); layout->addWidget(m_themeHandler); connect(m_themeHandler, SIGNAL(loadThemeRequested(QString)), this, SLOT(loadTheme(QString))); connect(m_themeHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::Worksheet); layout->addWidget(templateHandler); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); ui.verticalLayout->addWidget(frame); this->retranslateUi(); } void WorksheetDock::setWorksheets(QList list) { m_initializing = true; m_worksheetList = list; m_worksheet = list.first(); //if there are more then one worksheet in the list, disable the name and comment field in the tab "general" if (list.size()==1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_worksheet->name()); ui.leComment->setText(m_worksheet->comment()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } //show the properties of the first worksheet this->load(); this->worksheetLayoutChanged(m_worksheet->layout()); m_themeHandler->setCurrentTheme(m_worksheet->theme()); connect(m_worksheet, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(worksheetDescriptionChanged(const AbstractAspect*))); connect(m_worksheet, SIGNAL(pageRectChanged(QRectF)),this, SLOT(worksheetPageRectChanged(QRectF))); connect(m_worksheet,SIGNAL(scaleContentChanged(bool)),this,SLOT(worksheetScaleContentChanged(bool))); connect(m_worksheet,SIGNAL(backgroundTypeChanged(PlotArea::BackgroundType)),this,SLOT(worksheetBackgroundTypeChanged(PlotArea::BackgroundType))); connect(m_worksheet,SIGNAL(backgroundColorStyleChanged(PlotArea::BackgroundColorStyle)),this,SLOT(worksheetBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle))); connect(m_worksheet,SIGNAL(backgroundImageStyleChanged(PlotArea::BackgroundImageStyle)),this,SLOT(worksheetBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle))); connect(m_worksheet,SIGNAL(backgroundBrushStyleChanged(Qt::BrushStyle)),this,SLOT(worksheetBackgroundBrushStyleChanged(Qt::BrushStyle))); connect(m_worksheet,SIGNAL(backgroundFirstColorChanged(QColor)),this,SLOT(worksheetBackgroundFirstColorChanged(QColor))); connect(m_worksheet,SIGNAL(backgroundSecondColorChanged(QColor)),this,SLOT(worksheetBackgroundSecondColorChanged(QColor))); connect(m_worksheet,SIGNAL(backgroundFileNameChanged(QString)),this,SLOT(worksheetBackgroundFileNameChanged(QString))); connect(m_worksheet,SIGNAL(backgroundOpacityChanged(float)),this,SLOT(worksheetBackgroundOpacityChanged(float))); connect(m_worksheet,SIGNAL(layoutChanged(Worksheet::Layout)),this,SLOT(worksheetLayoutChanged(Worksheet::Layout))); connect(m_worksheet,SIGNAL(layoutTopMarginChanged(float)),this,SLOT(worksheetLayoutTopMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutBottomMarginChanged(float)),this,SLOT(worksheetLayoutBottomMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutLeftMarginChanged(float)),this,SLOT(worksheetLayoutLeftMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutRightMarginChanged(float)),this,SLOT(worksheetLayoutRightMarginChanged(float))); connect(m_worksheet,SIGNAL(layoutVerticalSpacingChanged(float)),this,SLOT(worksheetLayoutVerticalSpacingChanged(float))); connect(m_worksheet,SIGNAL(layoutHorizontalSpacingChanged(float)),this,SLOT(worksheetLayoutHorizontalSpacingChanged(float))); connect(m_worksheet,SIGNAL(layoutRowCountChanged(int)),this,SLOT(worksheetLayoutRowCountChanged(int))); connect(m_worksheet,SIGNAL(layoutColumnCountChanged(int)),this,SLOT(worksheetLayoutColumnCountChanged(int))); connect(m_worksheet,SIGNAL(themeChanged(QString)),m_themeHandler,SLOT(setCurrentTheme(QString))); m_initializing = false; } /*! Checks whether the size is one of the QPrinter::PaperSize and updates Size and Orientation checkbox when width/height changes. */ void WorksheetDock::updatePaperSize() { if (m_worksheet->useViewSize()) { ui.cbSize->setCurrentIndex(0); return; } int i=0; //In UI we use cm, so we need to convert to mm first before we check with qt_paperSizes float w=(float)ui.sbWidth->value()*10; float h=(float)ui.sbHeight->value()*10; //check the portrait-orientation first while ( isetCurrentIndex(0); //a QPrinter::PaperSize in portrait-orientation was found } else { //check for the landscape-orientation i=0; while ( isetCurrentIndex(1); //a QPrinter::PaperSize in landscape-orientation was found } //determine the position of the QPrinter::PaperSize in the combobox for (int index = 0; index < numOfPaperSizes+1; ++index) { if (ui.cbSize->itemData(index+2).toInt() == i) { ui.cbSize->setCurrentIndex(index+2); break; } } } //************************************************************* //****** SLOTs for changes triggered in WorksheetDock ********* //************************************************************* void WorksheetDock::retranslateUi() { m_initializing = true; //Geometry ui.cbOrientation->clear(); - ui.cbOrientation->addItem(i18n("portrait")); - ui.cbOrientation->addItem(i18n("landscape")); + ui.cbOrientation->addItem(i18n("Portrait")); + ui.cbOrientation->addItem(i18n("Landscape")); ui.cbSize->clear(); - ui.cbSize->addItem(i18n("view size")); + ui.cbSize->addItem(i18n("View Size")); ui.cbSize->addItem(QString("A0"), QPrinter::A0); ui.cbSize->addItem(QString("A1"), QPrinter::A1); ui.cbSize->addItem(QString("A2"), QPrinter::A2); ui.cbSize->addItem(QString("A3"), QPrinter::A3); ui.cbSize->addItem(QString("A4"), QPrinter::A4); ui.cbSize->addItem(QString("A5"), QPrinter::A5); ui.cbSize->addItem(QString("A6"), QPrinter::A6); ui.cbSize->addItem(QString("A7"), QPrinter::A7); ui.cbSize->addItem(QString("A8"), QPrinter::A8); ui.cbSize->addItem(QString("A9"), QPrinter::A9); ui.cbSize->addItem(QString("B0"), QPrinter::B0); ui.cbSize->addItem(QString("B1"), QPrinter::B1); ui.cbSize->addItem(QString("B2"), QPrinter::B2); ui.cbSize->addItem(QString("B3"), QPrinter::B3); ui.cbSize->addItem(QString("B4"), QPrinter::B4); ui.cbSize->addItem(QString("B5"), QPrinter::B5); ui.cbSize->addItem(QString("B6"), QPrinter::B6); ui.cbSize->addItem(QString("B7"), QPrinter::B7); ui.cbSize->addItem(QString("B8"), QPrinter::B8); ui.cbSize->addItem(QString("B9"), QPrinter::B9); ui.cbSize->addItem(QString("B10"), QPrinter::B10); ui.cbSize->addItem(QString("C5E"), QPrinter::C5E); ui.cbSize->addItem(QString("DLE"), QPrinter::DLE); ui.cbSize->addItem(i18n("Executive"), QPrinter::Executive); ui.cbSize->addItem(i18n("Folio"), QPrinter::Folio); ui.cbSize->addItem(i18n("Ledger"), QPrinter::Ledger); ui.cbSize->addItem(i18n("Legal"), QPrinter::Legal); ui.cbSize->addItem(i18n("Letter"), QPrinter::Letter); ui.cbSize->addItem(i18n("Tabloid"), QPrinter::Tabloid); ui.cbSize->addItem(i18n("US Common #10 Envelope"), QPrinter::Comm10E); ui.cbSize->addItem(i18n("Custom"), QPrinter::Custom); ui.cbSize->insertSeparator(1); //Background ui.cbBackgroundType->clear(); - ui.cbBackgroundType->addItem(i18n("color")); - ui.cbBackgroundType->addItem(i18n("image")); - ui.cbBackgroundType->addItem(i18n("pattern")); + ui.cbBackgroundType->addItem(i18n("Color")); + ui.cbBackgroundType->addItem(i18n("Image")); + ui.cbBackgroundType->addItem(i18n("Pattern")); ui.cbBackgroundColorStyle->clear(); - ui.cbBackgroundColorStyle->addItem(i18n("single color")); - ui.cbBackgroundColorStyle->addItem(i18n("horizontal gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("vertical gradient")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from top left)")); - ui.cbBackgroundColorStyle->addItem(i18n("diag. gradient (from bottom left)")); - ui.cbBackgroundColorStyle->addItem(i18n("radial gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Single Color")); + ui.cbBackgroundColorStyle->addItem(i18n("Horizontal Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Vertical Gradient")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Top Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Diag. Gradient (From Bottom Left)")); + ui.cbBackgroundColorStyle->addItem(i18n("Radial Gradient")); ui.cbBackgroundImageStyle->clear(); - ui.cbBackgroundImageStyle->addItem(i18n("scaled and cropped")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled")); - ui.cbBackgroundImageStyle->addItem(i18n("scaled, keep proportions")); - ui.cbBackgroundImageStyle->addItem(i18n("centered")); - ui.cbBackgroundImageStyle->addItem(i18n("tiled")); - ui.cbBackgroundImageStyle->addItem(i18n("center tiled")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled and Cropped")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled")); + ui.cbBackgroundImageStyle->addItem(i18n("Scaled, Keep Proportions")); + ui.cbBackgroundImageStyle->addItem(i18n("Centered")); + ui.cbBackgroundImageStyle->addItem(i18n("Tiled")); + ui.cbBackgroundImageStyle->addItem(i18n("Center Tiled")); GuiTools::updateBrushStyles(ui.cbBackgroundBrushStyle, Qt::SolidPattern); m_initializing = false; } // "General"-tab void WorksheetDock::nameChanged() { if (m_initializing) return; m_worksheet->setName(ui.leName->text()); } void WorksheetDock::commentChanged() { if (m_initializing) return; m_worksheet->setComment(ui.leComment->text()); } void WorksheetDock::scaleContentChanged(bool scaled) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setScaleContent(scaled); } void WorksheetDock::sizeChanged(int i) { int index = ui.cbSize->itemData(i).toInt(); if (index==QPrinter::Custom) { ui.sbWidth->setEnabled(true); ui.sbHeight->setEnabled(true); ui.lOrientation->hide(); ui.cbOrientation->hide(); } else { ui.sbWidth->setEnabled(false); ui.sbHeight->setEnabled(false); if (i==0) { //no orientation available when using the complete view size (first item in the combox is selected) ui.lOrientation->hide(); ui.cbOrientation->hide(); } else { ui.lOrientation->show(); ui.cbOrientation->show(); } } if (m_initializing) return; if (i==0) { //use the complete view size (first item in the combox is selected) for (auto* worksheet : m_worksheetList) worksheet->setUseViewSize(true); } else if (index==QPrinter::Custom) { if (m_worksheet->useViewSize()) { for (auto* worksheet : m_worksheetList) worksheet->setUseViewSize(false); } } else { //determine the width and the height of the to be used predefined layout float w, h; if (ui.cbOrientation->currentIndex() == 0) { w=qt_paperSizes[index][0]; h=qt_paperSizes[index][1]; } else { w=qt_paperSizes[index][1]; h=qt_paperSizes[index][0]; } m_initializing = true; //w and h from qt_paperSizes above are in mm, in UI we show everything in cm ui.sbWidth->setValue(w/10); ui.sbHeight->setValue(h/10); m_initializing=false; w = Worksheet::convertToSceneUnits(w, Worksheet::Millimeter); h = Worksheet::convertToSceneUnits(h, Worksheet::Millimeter); for (auto* worksheet : m_worksheetList) { worksheet->setUseViewSize(false); worksheet->setPageRect(QRect(0,0,w,h)); } } } void WorksheetDock::sizeChanged() { if (m_initializing) return; int w = Worksheet::convertToSceneUnits(ui.sbWidth->value(), Worksheet::Centimeter); int h = Worksheet::convertToSceneUnits(ui.sbHeight->value(), Worksheet::Centimeter); for (auto* worksheet : m_worksheetList) worksheet->setPageRect(QRect(0,0,w,h)); } void WorksheetDock::orientationChanged(int index) { Q_UNUSED(index); if (m_initializing) return; this->sizeChanged(ui.cbSize->currentIndex()); } // "Background"-tab void WorksheetDock::backgroundTypeChanged(int index) { PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color) { ui.lBackgroundColorStyle->show(); ui.cbBackgroundColorStyle->show(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle) ui.cbBackgroundColorStyle->currentIndex(); if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } } else if (type == PlotArea::Image) { ui.lBackgroundFirstColor->hide(); ui.kcbBackgroundFirstColor->hide(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->show(); ui.cbBackgroundImageStyle->show(); ui.lBackgroundBrushStyle->hide(); ui.cbBackgroundBrushStyle->hide(); ui.lBackgroundFileName->show(); ui.leBackgroundFileName->show(); ui.bOpen->show(); } else if (type == PlotArea::Pattern) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundFirstColor->show(); ui.kcbBackgroundFirstColor->show(); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); ui.lBackgroundColorStyle->hide(); ui.cbBackgroundColorStyle->hide(); ui.lBackgroundImageStyle->hide(); ui.cbBackgroundImageStyle->hide(); ui.lBackgroundBrushStyle->show(); ui.cbBackgroundBrushStyle->show(); ui.lBackgroundFileName->hide(); ui.leBackgroundFileName->hide(); ui.bOpen->hide(); } if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundType(type); } void WorksheetDock::backgroundColorStyleChanged(int index) { PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor) { ui.lBackgroundFirstColor->setText(i18n("Color")); ui.lBackgroundSecondColor->hide(); ui.kcbBackgroundSecondColor->hide(); } else { ui.lBackgroundFirstColor->setText(i18n("First color")); ui.lBackgroundSecondColor->show(); ui.kcbBackgroundSecondColor->show(); } if (m_initializing) return; int size = m_worksheetList.size(); if (size>1) { m_worksheet->beginMacro(i18n("%1 worksheets: background color style changed", size)); for (auto* w : m_worksheetList) w->setBackgroundColorStyle(style); m_worksheet->endMacro(); } else m_worksheet->setBackgroundColorStyle(style); } void WorksheetDock::backgroundImageStyleChanged(int index) { if (m_initializing) return; PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundImageStyle(style); } void WorksheetDock::backgroundBrushStyleChanged(int index) { if (m_initializing) return; Qt::BrushStyle style = (Qt::BrushStyle)index; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundBrushStyle(style); } void WorksheetDock::backgroundFirstColorChanged(const QColor& c) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFirstColor(c); } void WorksheetDock::backgroundSecondColorChanged(const QColor& c) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundSecondColor(c); } void WorksheetDock::backgroundOpacityChanged(int value) { if (m_initializing) return; float opacity = (float)value/100; for (auto* worksheet : m_worksheetList) worksheet->setBackgroundOpacity(opacity); } //"Layout"-tab void WorksheetDock::layoutTopMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutTopMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutBottomMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutBottomMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutLeftMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutLeftMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutRightMarginChanged(double margin) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutRightMargin(Worksheet::convertToSceneUnits(margin, Worksheet::Centimeter)); } void WorksheetDock::layoutHorizontalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutHorizontalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void WorksheetDock::layoutVerticalSpacingChanged(double spacing) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutVerticalSpacing(Worksheet::convertToSceneUnits(spacing, Worksheet::Centimeter)); } void WorksheetDock::layoutRowCountChanged(int count) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutRowCount(count); } void WorksheetDock::layoutColumnCountChanged(int count) { if (m_initializing) return; for (auto* worksheet : m_worksheetList) worksheet->setLayoutColumnCount(count); } /*! opens a file dialog and lets the user select the image file. */ void WorksheetDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "WorksheetDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const QByteArray& format : QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastImageDir", newDir); } ui.leBackgroundFileName->setText( path ); for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFileName(path); } void WorksheetDock::fileNameChanged() { if (m_initializing) return; QString fileName = ui.leBackgroundFileName->text(); if (!fileName.isEmpty() && !QFile::exists(fileName)) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); for (auto* worksheet : m_worksheetList) worksheet->setBackgroundFileName(fileName); } //************************************************************* //******** SLOTs for changes triggered in Worksheet *********** //************************************************************* void WorksheetDock::worksheetDescriptionChanged(const AbstractAspect* aspect) { if (m_worksheet != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) ui.leName->setText(aspect->name()); else if (aspect->comment() != ui.leComment->text()) ui.leComment->setText(aspect->comment()); m_initializing = false; } void WorksheetDock::worksheetScaleContentChanged(bool scaled) { m_initializing = true; ui.chScaleContent->setChecked(scaled); m_initializing = false; } void WorksheetDock::worksheetPageRectChanged(const QRectF& rect) { m_initializing = true; ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(rect.width(), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(rect.height(), Worksheet::Centimeter)); updatePaperSize(); m_initializing = false; } void WorksheetDock::worksheetBackgroundTypeChanged(PlotArea::BackgroundType type) { m_initializing = true; ui.cbBackgroundType->setCurrentIndex(type); m_initializing = false; } void WorksheetDock::worksheetBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle style) { m_initializing = true; ui.cbBackgroundColorStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle style) { m_initializing = true; ui.cbBackgroundImageStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundBrushStyleChanged(Qt::BrushStyle style) { m_initializing = true; ui.cbBackgroundBrushStyle->setCurrentIndex(style); m_initializing = false; } void WorksheetDock::worksheetBackgroundFirstColorChanged(const QColor& color) { m_initializing = true; ui.kcbBackgroundFirstColor->setColor(color); m_initializing = false; } void WorksheetDock::worksheetBackgroundSecondColorChanged(const QColor& color) { m_initializing = true; ui.kcbBackgroundSecondColor->setColor(color); m_initializing = false; } void WorksheetDock::worksheetBackgroundFileNameChanged(const QString& name) { m_initializing = true; ui.leBackgroundFileName->setText(name); m_initializing = false; } void WorksheetDock::worksheetBackgroundOpacityChanged(float opacity) { m_initializing = true; ui.sbBackgroundOpacity->setValue( qRound(opacity*100.0) ); m_initializing = false; } void WorksheetDock::worksheetLayoutChanged(Worksheet::Layout layout) { bool b = (layout != Worksheet::NoLayout); ui.sbLayoutTopMargin->setEnabled(b); ui.sbLayoutBottomMargin->setEnabled(b); ui.sbLayoutLeftMargin->setEnabled(b); ui.sbLayoutRightMargin->setEnabled(b); ui.sbLayoutHorizontalSpacing->setEnabled(b); ui.sbLayoutVerticalSpacing->setEnabled(b); ui.sbLayoutRowCount->setEnabled(b); ui.sbLayoutColumnCount->setEnabled(b); if (b) { bool grid = (layout == Worksheet::GridLayout); ui.lGrid->setVisible(grid); ui.lRowCount->setVisible(grid); ui.sbLayoutRowCount->setVisible(grid); ui.lColumnCount->setVisible(grid); ui.sbLayoutColumnCount->setVisible(grid); } else { ui.lGrid->setVisible(true); ui.lRowCount->setVisible(true); ui.sbLayoutRowCount->setVisible(true); ui.lColumnCount->setVisible(true); ui.sbLayoutColumnCount->setVisible(true); } } void WorksheetDock::worksheetLayoutTopMarginChanged(float value) { m_initializing = true; ui.sbLayoutTopMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutBottomMarginChanged(float value) { m_initializing = true; ui.sbLayoutBottomMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutLeftMarginChanged(float value) { m_initializing = true; ui.sbLayoutLeftMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutRightMarginChanged(float value) { m_initializing = true; ui.sbLayoutRightMargin->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutVerticalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutVerticalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutHorizontalSpacingChanged(float value) { m_initializing = true; ui.sbLayoutHorizontalSpacing->setValue(Worksheet::convertFromSceneUnits(value, Worksheet::Centimeter)); m_initializing = false; } void WorksheetDock::worksheetLayoutRowCountChanged(int value) { m_initializing = true; ui.sbLayoutRowCount->setValue(value); m_initializing = false; } void WorksheetDock::worksheetLayoutColumnCountChanged(int value) { m_initializing = true; ui.sbLayoutColumnCount->setValue(value); m_initializing = false; } //************************************************************* //******************** SETTINGS ******************************* //************************************************************* void WorksheetDock::load() { // Geometry ui.chScaleContent->setChecked(m_worksheet->scaleContent()); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits( m_worksheet->pageRect().width(), Worksheet::Centimeter) ); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits( m_worksheet->pageRect().height(), Worksheet::Centimeter) ); updatePaperSize(); // Background-tab ui.cbBackgroundType->setCurrentIndex( (int) m_worksheet->backgroundType() ); ui.cbBackgroundColorStyle->setCurrentIndex( (int) m_worksheet->backgroundColorStyle() ); ui.cbBackgroundImageStyle->setCurrentIndex( (int) m_worksheet->backgroundImageStyle() ); ui.cbBackgroundBrushStyle->setCurrentIndex( (int) m_worksheet->backgroundBrushStyle() ); ui.leBackgroundFileName->setText( m_worksheet->backgroundFileName() ); ui.kcbBackgroundFirstColor->setColor( m_worksheet->backgroundFirstColor() ); ui.kcbBackgroundSecondColor->setColor( m_worksheet->backgroundSecondColor() ); ui.sbBackgroundOpacity->setValue( qRound(m_worksheet->backgroundOpacity()*100) ); //highlight the text field for the background image red if an image is used and cannot be found if (!m_worksheet->backgroundFileName().isEmpty() && !QFile::exists(m_worksheet->backgroundFileName())) ui.leBackgroundFileName->setStyleSheet("QLineEdit{background:red;}"); else ui.leBackgroundFileName->setStyleSheet(""); // Layout ui.sbLayoutTopMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutTopMargin(), Worksheet::Centimeter) ); ui.sbLayoutBottomMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutBottomMargin(), Worksheet::Centimeter) ); ui.sbLayoutLeftMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutLeftMargin(), Worksheet::Centimeter) ); ui.sbLayoutRightMargin->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutRightMargin(), Worksheet::Centimeter) ); ui.sbLayoutHorizontalSpacing->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutHorizontalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutVerticalSpacing->setValue( Worksheet::convertFromSceneUnits(m_worksheet->layoutVerticalSpacing(), Worksheet::Centimeter) ); ui.sbLayoutRowCount->setValue( m_worksheet->layoutRowCount() ); ui.sbLayoutColumnCount->setValue( m_worksheet->layoutColumnCount() ); } void WorksheetDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_worksheetList.size(); if (size>1) m_worksheet->beginMacro(i18n("%1 worksheets: template \"%2\" loaded", size, name)); else m_worksheet->beginMacro(i18n("%1: template \"%2\" loaded", m_worksheet->name(), name)); this->loadConfig(config); m_worksheet->endMacro(); } void WorksheetDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "Worksheet" ); // Geometry ui.chScaleContent->setChecked(group.readEntry("ScaleContent", false)); ui.sbWidth->setValue(Worksheet::convertFromSceneUnits(group.readEntry("Width", m_worksheet->pageRect().width()), Worksheet::Centimeter)); ui.sbHeight->setValue(Worksheet::convertFromSceneUnits(group.readEntry("Height", m_worksheet->pageRect().height()), Worksheet::Centimeter)); updatePaperSize(); // Background-tab ui.cbBackgroundType->setCurrentIndex( group.readEntry("BackgroundType", (int) m_worksheet->backgroundType()) ); ui.cbBackgroundColorStyle->setCurrentIndex( group.readEntry("BackgroundColorStyle", (int) m_worksheet->backgroundColorStyle()) ); ui.cbBackgroundImageStyle->setCurrentIndex( group.readEntry("BackgroundImageStyle", (int) m_worksheet->backgroundImageStyle()) ); ui.cbBackgroundBrushStyle->setCurrentIndex( group.readEntry("BackgroundBrushStyle", (int) m_worksheet->backgroundBrushStyle()) ); ui.leBackgroundFileName->setText( group.readEntry("BackgroundFileName", m_worksheet->backgroundFileName()) ); ui.kcbBackgroundFirstColor->setColor( group.readEntry("BackgroundFirstColor", m_worksheet->backgroundFirstColor()) ); ui.kcbBackgroundSecondColor->setColor( group.readEntry("BackgroundSecondColor", m_worksheet->backgroundSecondColor()) ); ui.sbBackgroundOpacity->setValue( qRound(group.readEntry("BackgroundOpacity", m_worksheet->backgroundOpacity())*100) ); // Layout ui.sbLayoutTopMargin->setValue(group.readEntry("LayoutTopMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutTopMargin(), Worksheet::Centimeter)) ); ui.sbLayoutBottomMargin->setValue(group.readEntry("LayoutBottomMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutBottomMargin(), Worksheet::Centimeter)) ); ui.sbLayoutLeftMargin->setValue(group.readEntry("LayoutLeftMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutLeftMargin(), Worksheet::Centimeter)) ); ui.sbLayoutRightMargin->setValue(group.readEntry("LayoutRightMargin", Worksheet::convertFromSceneUnits(m_worksheet->layoutRightMargin(), Worksheet::Centimeter)) ); ui.sbLayoutHorizontalSpacing->setValue(group.readEntry("LayoutHorizontalSpacing", Worksheet::convertFromSceneUnits(m_worksheet->layoutHorizontalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutVerticalSpacing->setValue(group.readEntry("LayoutVerticalSpacing", Worksheet::convertFromSceneUnits(m_worksheet->layoutVerticalSpacing(), Worksheet::Centimeter)) ); ui.sbLayoutRowCount->setValue(group.readEntry("LayoutRowCount", m_worksheet->layoutRowCount())); ui.sbLayoutColumnCount->setValue(group.readEntry("LayoutColumnCount", m_worksheet->layoutColumnCount())); } void WorksheetDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "Worksheet" ); //General group.writeEntry("ScaleContent",ui.chScaleContent->isChecked()); group.writeEntry("UseViewSize",ui.cbSize->currentIndex()==0); group.writeEntry("Width",Worksheet::convertToSceneUnits(ui.sbWidth->value(), Worksheet::Centimeter)); group.writeEntry("Height",Worksheet::convertToSceneUnits(ui.sbHeight->value(), Worksheet::Centimeter)); //Background group.writeEntry("BackgroundType",ui.cbBackgroundType->currentIndex()); group.writeEntry("BackgroundColorStyle", ui.cbBackgroundColorStyle->currentIndex()); group.writeEntry("BackgroundImageStyle", ui.cbBackgroundImageStyle->currentIndex()); group.writeEntry("BackgroundBrushStyle", ui.cbBackgroundBrushStyle->currentIndex()); group.writeEntry("BackgroundFileName", ui.leBackgroundFileName->text()); group.writeEntry("BackgroundFirstColor", ui.kcbBackgroundFirstColor->color()); group.writeEntry("BackgroundSecondColor", ui.kcbBackgroundSecondColor->color()); group.writeEntry("BackgroundOpacity", ui.sbBackgroundOpacity->value()/100.0); //Layout group.writeEntry("LayoutTopMargin",Worksheet::convertToSceneUnits(ui.sbLayoutTopMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutBottomMargin",Worksheet::convertToSceneUnits(ui.sbLayoutBottomMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutLeftMargin",Worksheet::convertToSceneUnits(ui.sbLayoutLeftMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutRightMargin",Worksheet::convertToSceneUnits(ui.sbLayoutRightMargin->value(), Worksheet::Centimeter)); group.writeEntry("LayoutVerticalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutVerticalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutHorizontalSpacing",Worksheet::convertToSceneUnits(ui.sbLayoutHorizontalSpacing->value(), Worksheet::Centimeter)); group.writeEntry("LayoutRowCount", ui.sbLayoutRowCount->value()); group.writeEntry("LayoutColumnCount", ui.sbLayoutColumnCount->value()); config.sync(); } void WorksheetDock::loadTheme(const QString& theme) { for (auto* worksheet : m_worksheetList) worksheet->setTheme(theme); } diff --git a/src/kdefrontend/dockwidgets/XYCurveDock.cpp b/src/kdefrontend/dockwidgets/XYCurveDock.cpp index 4bbe377e4..6e9a30dc5 100644 --- a/src/kdefrontend/dockwidgets/XYCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYCurveDock.cpp @@ -1,2236 +1,2237 @@ /*************************************************************************** File : XYCurveDock.cpp Project : LabPlot Description : widget for XYCurve properties -------------------------------------------------------------------- Copyright : (C) 2010-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2017 Stefan Gerlach (stefan.gerlach@uni-konstanz.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 "XYCurveDock.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/Worksheet.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/core/datatypes/Double2StringFilter.h" #include "backend/core/datatypes/DateTime2StringFilter.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/TemplateHandler.h" #include "kdefrontend/GuiTools.h" #include #include #include #include #include #include #include #include #include +#include /*! \class XYCurveDock \brief Provides a widget for editing the properties of the XYCurves (2D-curves) currently selected in the project explorer. If more than one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYCurveDock::XYCurveDock(QWidget* parent) : QWidget(parent), cbXColumn(nullptr), cbYColumn(nullptr), m_curve(nullptr), m_aspectTreeModel(nullptr) { ui.setupUi(this); //Tab "Values" QGridLayout* gridLayout = qobject_cast(ui.tabValues->layout()); cbValuesColumn = new TreeViewComboBox(ui.tabValues); gridLayout->addWidget(cbValuesColumn, 2, 2, 1, 1); //Tab "Filling" ui.cbFillingColorStyle->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); ui.bFillingOpen->setIcon( QIcon::fromTheme("document-open") ); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leFillingFileName->setCompleter(completer); //Tab "Error bars" gridLayout = qobject_cast(ui.tabErrorBars->layout()); cbXErrorPlusColumn = new TreeViewComboBox(ui.tabErrorBars); gridLayout->addWidget(cbXErrorPlusColumn, 2, 2, 1, 1); cbXErrorMinusColumn = new TreeViewComboBox(ui.tabErrorBars); gridLayout->addWidget(cbXErrorMinusColumn, 3, 2, 1, 1); cbYErrorPlusColumn = new TreeViewComboBox(ui.tabErrorBars); gridLayout->addWidget(cbYErrorPlusColumn, 7, 2, 1, 1); cbYErrorMinusColumn = new TreeViewComboBox(ui.tabErrorBars); gridLayout->addWidget(cbYErrorMinusColumn, 8, 2, 1, 1); //adjust layouts in the tabs for (int i=0; icount(); ++i) { QGridLayout* layout = dynamic_cast(ui.tabWidget->widget(i)->layout()); if (!layout) continue; layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); } //Slots //Lines connect( ui.cbLineType, SIGNAL(currentIndexChanged(int)), this, SLOT(lineTypeChanged(int)) ); connect( ui.sbLineInterpolationPointsCount, SIGNAL(valueChanged(int)), this, SLOT(lineInterpolationPointsCountChanged(int)) ); connect( ui.chkLineSkipGaps, SIGNAL(clicked(bool)), this, SLOT(lineSkipGapsChanged(bool)) ); connect( ui.cbLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(lineStyleChanged(int)) ); connect( ui.kcbLineColor, SIGNAL(changed(QColor)), this, SLOT(lineColorChanged(QColor)) ); connect( ui.sbLineWidth, SIGNAL(valueChanged(double)), this, SLOT(lineWidthChanged(double)) ); connect( ui.sbLineOpacity, SIGNAL(valueChanged(int)), this, SLOT(lineOpacityChanged(int)) ); connect( ui.cbDropLineType, SIGNAL(currentIndexChanged(int)), this, SLOT(dropLineTypeChanged(int)) ); connect( ui.cbDropLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(dropLineStyleChanged(int)) ); connect( ui.kcbDropLineColor, SIGNAL(changed(QColor)), this, SLOT(dropLineColorChanged(QColor)) ); connect( ui.sbDropLineWidth, SIGNAL(valueChanged(double)), this, SLOT(dropLineWidthChanged(double)) ); connect( ui.sbDropLineOpacity, SIGNAL(valueChanged(int)), this, SLOT(dropLineOpacityChanged(int)) ); //Symbol connect( ui.cbSymbolStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolsStyleChanged(int)) ); connect( ui.sbSymbolSize, SIGNAL(valueChanged(double)), this, SLOT(symbolsSizeChanged(double)) ); connect( ui.sbSymbolRotation, SIGNAL(valueChanged(int)), this, SLOT(symbolsRotationChanged(int)) ); connect( ui.sbSymbolOpacity, SIGNAL(valueChanged(int)), this, SLOT(symbolsOpacityChanged(int)) ); connect( ui.cbSymbolFillingStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolsFillingStyleChanged(int)) ); connect( ui.kcbSymbolFillingColor, SIGNAL(changed(QColor)), this, SLOT(symbolsFillingColorChanged(QColor)) ); connect( ui.cbSymbolBorderStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(symbolsBorderStyleChanged(int)) ); connect( ui.kcbSymbolBorderColor, SIGNAL(changed(QColor)), this, SLOT(symbolsBorderColorChanged(QColor)) ); connect( ui.sbSymbolBorderWidth, SIGNAL(valueChanged(double)), this, SLOT(symbolsBorderWidthChanged(double)) ); //Values connect( ui.cbValuesType, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesTypeChanged(int)) ); connect( cbValuesColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(valuesColumnChanged(QModelIndex)) ); connect( ui.cbValuesPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesPositionChanged(int)) ); connect( ui.sbValuesDistance, SIGNAL(valueChanged(double)), this, SLOT(valuesDistanceChanged(double)) ); connect( ui.sbValuesRotation, SIGNAL(valueChanged(int)), this, SLOT(valuesRotationChanged(int)) ); connect( ui.sbValuesOpacity, SIGNAL(valueChanged(int)), this, SLOT(valuesOpacityChanged(int)) ); //TODO connect( ui.cbValuesFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesColumnFormatChanged(int)) ); connect( ui.leValuesPrefix, SIGNAL(returnPressed()), this, SLOT(valuesPrefixChanged()) ); connect( ui.leValuesSuffix, SIGNAL(returnPressed()), this, SLOT(valuesSuffixChanged()) ); connect( ui.kfrValuesFont, SIGNAL(fontSelected(QFont)), this, SLOT(valuesFontChanged(QFont)) ); connect( ui.kcbValuesColor, SIGNAL(changed(QColor)), this, SLOT(valuesColorChanged(QColor)) ); //Filling connect( ui.cbFillingPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingPositionChanged(int)) ); connect( ui.cbFillingType, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingTypeChanged(int)) ); connect( ui.cbFillingColorStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingColorStyleChanged(int)) ); connect( ui.cbFillingImageStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingImageStyleChanged(int)) ); connect( ui.cbFillingBrushStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingBrushStyleChanged(int)) ); connect(ui.bFillingOpen, SIGNAL(clicked(bool)), this, SLOT(selectFile())); connect( ui.leFillingFileName, SIGNAL(returnPressed()), this, SLOT(fileNameChanged()) ); connect( ui.leFillingFileName, SIGNAL(textChanged(QString)), this, SLOT(fileNameChanged()) ); connect( ui.kcbFillingFirstColor, SIGNAL(changed(QColor)), this, SLOT(fillingFirstColorChanged(QColor)) ); connect( ui.kcbFillingSecondColor, SIGNAL(changed(QColor)), this, SLOT(fillingSecondColorChanged(QColor)) ); connect( ui.sbFillingOpacity, SIGNAL(valueChanged(int)), this, SLOT(fillingOpacityChanged(int)) ); //Error bars connect( ui.cbXErrorType, SIGNAL(currentIndexChanged(int)), this, SLOT(xErrorTypeChanged(int)) ); connect( cbXErrorPlusColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xErrorPlusColumnChanged(QModelIndex)) ); connect( cbXErrorMinusColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xErrorMinusColumnChanged(QModelIndex)) ); connect( ui.cbYErrorType, SIGNAL(currentIndexChanged(int)), this, SLOT(yErrorTypeChanged(int)) ); connect( cbYErrorPlusColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yErrorPlusColumnChanged(QModelIndex)) ); connect( cbYErrorMinusColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yErrorMinusColumnChanged(QModelIndex)) ); connect( ui.cbErrorBarsType, SIGNAL(currentIndexChanged(int)), this, SLOT(errorBarsTypeChanged(int)) ); connect( ui.sbErrorBarsCapSize, SIGNAL(valueChanged(double)), this, SLOT(errorBarsCapSizeChanged(double)) ); connect( ui.cbErrorBarsStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(errorBarsStyleChanged(int)) ); connect( ui.kcbErrorBarsColor, SIGNAL(changed(QColor)), this, SLOT(errorBarsColorChanged(QColor)) ); connect( ui.sbErrorBarsWidth, SIGNAL(valueChanged(double)), this, SLOT(errorBarsWidthChanged(double)) ); connect( ui.sbErrorBarsOpacity, SIGNAL(valueChanged(int)), this, SLOT(errorBarsOpacityChanged(int)) ); //template handler TemplateHandler* templateHandler = new TemplateHandler(this, TemplateHandler::XYCurve); ui.verticalLayout->addWidget(templateHandler); templateHandler->show(); connect(templateHandler, SIGNAL(loadConfigRequested(KConfig&)), this, SLOT(loadConfigFromTemplate(KConfig&))); connect(templateHandler, SIGNAL(saveConfigRequested(KConfig&)), this, SLOT(saveConfigAsTemplate(KConfig&))); connect(templateHandler, SIGNAL(info(QString)), this, SIGNAL(info(QString))); retranslateUi(); init(); } XYCurveDock::~XYCurveDock() { if (m_aspectTreeModel) delete m_aspectTreeModel; } void XYCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); // Tab "General" QGridLayout* gridLayout = qobject_cast(generalTab->layout()); cbXColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXColumn, 2, 2, 1, 1); cbYColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYColumn, 3, 2, 1, 1); //General connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYCurveDock::nameChanged); connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYCurveDock::commentChanged); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( cbXColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xColumnChanged(QModelIndex)) ); connect( cbYColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yColumnChanged(QModelIndex)) ); } void XYCurveDock::init() { m_initializing = true; //Line - ui.cbLineType->addItem(i18n("none")); - ui.cbLineType->addItem(i18n("line")); - ui.cbLineType->addItem(i18n("horiz. start")); - ui.cbLineType->addItem(i18n("vert. start")); - ui.cbLineType->addItem(i18n("horiz. midpoint")); - ui.cbLineType->addItem(i18n("vert. midpoint")); + ui.cbLineType->addItem(i18n("None")); + ui.cbLineType->addItem(i18n("Line")); + ui.cbLineType->addItem(i18n("Horiz. Start")); + ui.cbLineType->addItem(i18n("Vert. Start")); + ui.cbLineType->addItem(i18n("Horiz. Midpoint")); + ui.cbLineType->addItem(i18n("Vert. Midpoint")); ui.cbLineType->addItem(i18n("2-segments")); ui.cbLineType->addItem(i18n("3-segments")); - ui.cbLineType->addItem(i18n("cubic spline (natural)")); - ui.cbLineType->addItem(i18n("cubic spline (periodic)")); - ui.cbLineType->addItem(i18n("Akima-spline (natural)")); - ui.cbLineType->addItem(i18n("Akima-spline (periodic)")); + ui.cbLineType->addItem(i18n("Cubic Spline (Natural)")); + ui.cbLineType->addItem(i18n("Cubic Spline (Periodic)")); + ui.cbLineType->addItem(i18n("Akima-spline (Natural)")); + ui.cbLineType->addItem(i18n("Akima-spline (Periodic)")); QPainter pa; //TODO size of the icon depending on the actuall height of the combobox? int iconSize = 20; QPixmap pm(iconSize, iconSize); ui.cbLineType->setIconSize(QSize(iconSize, iconSize)); QPen pen(Qt::SolidPattern, 0); const QColor& color = (palette().color(QPalette::Base).lightness() < 128) ? Qt::white : Qt::black; pen.setColor(color); pa.setPen( pen ); //no line pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.end(); ui.cbLineType->setItemIcon(0, pm); //line pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,17,17); pa.end(); ui.cbLineType->setItemIcon(1, pm); pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,17,3); pa.drawLine(17,3,17,17); pa.end(); ui.cbLineType->setItemIcon(2, pm); pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,3,17); pa.drawLine(3,17,17,17); pa.end(); ui.cbLineType->setItemIcon(3, pm); //horizontal midpoint pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,10,3); pa.drawLine(10,3,10,17); pa.drawLine(10,17,17,17); pa.end(); ui.cbLineType->setItemIcon(4, pm); //vertical midpoint pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,3,10); pa.drawLine(3,10,17,10); pa.drawLine(17,10,17,17); pa.end(); ui.cbLineType->setItemIcon(5, pm); //2-segments pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 8,8,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,10,10); pa.end(); ui.cbLineType->setItemIcon(6, pm); //3-segments pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 8,8,4,4); pa.drawEllipse( 15,15,4,4); pa.drawLine(3,3,17,17); pa.end(); ui.cbLineType->setItemIcon(7, pm); //natural spline pm.fill(Qt::transparent); pa.begin( &pm ); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.drawEllipse( 1,1,4,4); pa.drawEllipse( 15,15,4,4); pa.rotate(45); pa.drawArc(2*sqrt(2),-4,17*sqrt(2),20,30*16,120*16); pa.end(); ui.cbLineType->setItemIcon(8, pm); ui.cbLineType->setItemIcon(9, pm); ui.cbLineType->setItemIcon(10, pm); ui.cbLineType->setItemIcon(11, pm); GuiTools::updatePenStyles(ui.cbLineStyle, Qt::black); //Drop lines - ui.cbDropLineType->addItem(i18n("no drop lines")); - ui.cbDropLineType->addItem(i18n("drop lines, X")); - ui.cbDropLineType->addItem(i18n("drop lines, Y")); - ui.cbDropLineType->addItem(i18n("drop lines, XY")); - ui.cbDropLineType->addItem(i18n("drop lines, X, zero baseline")); - ui.cbDropLineType->addItem(i18n("drop lines, X, min baseline")); - ui.cbDropLineType->addItem(i18n("drop lines, X, max baseline")); + ui.cbDropLineType->addItem(i18n("No Drop Lines")); + ui.cbDropLineType->addItem(i18n("Drop Lines, X")); + ui.cbDropLineType->addItem(i18n("Drop Lines, Y")); + ui.cbDropLineType->addItem(i18n("Drop Lines, XY")); + ui.cbDropLineType->addItem(i18n("Drop Lines, X, Zero Baseline")); + ui.cbDropLineType->addItem(i18n("Drop Lines, X, Min Baseline")); + ui.cbDropLineType->addItem(i18n("Drop Lines, X, Max Baseline")); GuiTools::updatePenStyles(ui.cbDropLineStyle, Qt::black); //Symbols GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, Qt::black); ui.cbSymbolStyle->setIconSize(QSize(iconSize, iconSize)); QTransform trafo; trafo.scale(15, 15); - ui.cbSymbolStyle->addItem(i18n("none")); + ui.cbSymbolStyle->addItem(i18n("None")); for (int i = 1; i < 19; ++i) { //TODO: use enum count Symbol::Style style = (Symbol::Style)i; pm.fill(Qt::transparent); pa.begin(&pm); pa.setPen(pen); pa.setRenderHint(QPainter::Antialiasing); pa.translate(iconSize/2,iconSize/2); pa.drawPath(trafo.map(Symbol::pathFromStyle(style))); pa.end(); ui.cbSymbolStyle->addItem(QIcon(pm), Symbol::nameFromStyle(style)); } GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, Qt::black); m_initializing = false; //Values - ui.cbValuesType->addItem(i18n("no values")); + ui.cbValuesType->addItem(i18n("No Values")); ui.cbValuesType->addItem("x"); ui.cbValuesType->addItem("y"); ui.cbValuesType->addItem("x, y"); ui.cbValuesType->addItem("(x, y)"); - ui.cbValuesType->addItem(i18n("custom column")); + ui.cbValuesType->addItem(i18n("Custom Column")); - ui.cbValuesPosition->addItem(i18n("above")); - ui.cbValuesPosition->addItem(i18n("below")); - ui.cbValuesPosition->addItem(i18n("left")); - ui.cbValuesPosition->addItem(i18n("right")); + ui.cbValuesPosition->addItem(i18n("Above")); + ui.cbValuesPosition->addItem(i18n("Below")); + ui.cbValuesPosition->addItem(i18n("Left")); + ui.cbValuesPosition->addItem(i18n("Right")); //Filling ui.cbFillingPosition->clear(); - ui.cbFillingPosition->addItem(i18n("none")); - ui.cbFillingPosition->addItem(i18n("above")); - ui.cbFillingPosition->addItem(i18n("below")); - ui.cbFillingPosition->addItem(i18n("zero baseline")); - ui.cbFillingPosition->addItem(i18n("left")); - ui.cbFillingPosition->addItem(i18n("right")); + ui.cbFillingPosition->addItem(i18n("None")); + ui.cbFillingPosition->addItem(i18n("Above")); + ui.cbFillingPosition->addItem(i18n("Below")); + ui.cbFillingPosition->addItem(i18n("Zero Baseline")); + ui.cbFillingPosition->addItem(i18n("Left")); + ui.cbFillingPosition->addItem(i18n("Right")); ui.cbFillingType->clear(); - ui.cbFillingType->addItem(i18n("color")); - ui.cbFillingType->addItem(i18n("image")); - ui.cbFillingType->addItem(i18n("pattern")); + ui.cbFillingType->addItem(i18n("Color")); + ui.cbFillingType->addItem(i18n("Image")); + ui.cbFillingType->addItem(i18n("Pattern")); ui.cbFillingColorStyle->clear(); - ui.cbFillingColorStyle->addItem(i18n("single color")); - ui.cbFillingColorStyle->addItem(i18n("horizontal gradient")); - ui.cbFillingColorStyle->addItem(i18n("vertical gradient")); - ui.cbFillingColorStyle->addItem(i18n("diag. gradient (from top left)")); - ui.cbFillingColorStyle->addItem(i18n("diag. gradient (from bottom left)")); - ui.cbFillingColorStyle->addItem(i18n("radial gradient")); + ui.cbFillingColorStyle->addItem(i18n("Single Color")); + ui.cbFillingColorStyle->addItem(i18n("Horizontal Gradient")); + ui.cbFillingColorStyle->addItem(i18n("Vertical Gradient")); + ui.cbFillingColorStyle->addItem(i18n("Diag. Gradient (From Top Left)")); + ui.cbFillingColorStyle->addItem(i18n("Diag. Gradient (From Bottom Left)")); + ui.cbFillingColorStyle->addItem(i18n("Radial Gradient")); ui.cbFillingImageStyle->clear(); - ui.cbFillingImageStyle->addItem(i18n("scaled and cropped")); - ui.cbFillingImageStyle->addItem(i18n("scaled")); - ui.cbFillingImageStyle->addItem(i18n("scaled, keep proportions")); - ui.cbFillingImageStyle->addItem(i18n("centered")); - ui.cbFillingImageStyle->addItem(i18n("tiled")); - ui.cbFillingImageStyle->addItem(i18n("center tiled")); + ui.cbFillingImageStyle->addItem(i18n("Scaled and Cropped")); + ui.cbFillingImageStyle->addItem(i18n("Scaled")); + ui.cbFillingImageStyle->addItem(i18n("Scaled, Keep Proportions")); + ui.cbFillingImageStyle->addItem(i18n("Centered")); + ui.cbFillingImageStyle->addItem(i18n("Tiled")); + ui.cbFillingImageStyle->addItem(i18n("Center Tiled")); GuiTools::updateBrushStyles(ui.cbFillingBrushStyle, Qt::SolidPattern); //Error-bars pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.drawLine(3,10,17,10);//vert. line pa.drawLine(10,3,10,17);//hor. line pa.end(); - ui.cbErrorBarsType->addItem(i18n("bars")); + ui.cbErrorBarsType->addItem(i18n("Bars")); ui.cbErrorBarsType->setItemIcon(0, pm); pm.fill(Qt::transparent); pa.begin( &pm ); pa.setRenderHint(QPainter::Antialiasing); pa.setBrush(Qt::SolidPattern); pa.drawLine(3,10,17,10); //vert. line pa.drawLine(10,3,10,17); //hor. line pa.drawLine(7,3,13,3); //upper cap pa.drawLine(7,17,13,17); //bottom cap pa.drawLine(3,7,3,13); //left cap pa.drawLine(17,7,17,13); //right cap pa.end(); - ui.cbErrorBarsType->addItem(i18n("bars with ends")); + ui.cbErrorBarsType->addItem(i18n("Bars with Ends")); ui.cbErrorBarsType->setItemIcon(1, pm); - ui.cbXErrorType->addItem(i18n("no")); - ui.cbXErrorType->addItem(i18n("symmetric")); - ui.cbXErrorType->addItem(i18n("asymmetric")); + ui.cbXErrorType->addItem(i18n("No")); + ui.cbXErrorType->addItem(i18n("Symmetric")); + ui.cbXErrorType->addItem(i18n("Asymmetric")); - ui.cbYErrorType->addItem(i18n("no")); - ui.cbYErrorType->addItem(i18n("symmetric")); - ui.cbYErrorType->addItem(i18n("asymmetric")); + ui.cbYErrorType->addItem(i18n("No")); + ui.cbYErrorType->addItem(i18n("Symmetric")); + ui.cbYErrorType->addItem(i18n("Asymmetric")); GuiTools::updatePenStyles(ui.cbErrorBarsStyle, Qt::black); } void XYCurveDock::setModel() { m_aspectTreeModel->enableNumericColumnsOnly(true); m_aspectTreeModel->enableShowPlotDesignation(true); QList list; list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; if (cbXColumn) { cbXColumn->setTopLevelClasses(list); cbYColumn->setTopLevelClasses(list); } cbValuesColumn->setTopLevelClasses(list); cbXErrorMinusColumn->setTopLevelClasses(list); cbXErrorPlusColumn->setTopLevelClasses(list); cbYErrorMinusColumn->setTopLevelClasses(list); cbYErrorPlusColumn->setTopLevelClasses(list); list.clear(); list<<"Column"<<"XYCurve"; m_aspectTreeModel->setSelectableAspects(list); if (cbXColumn) { cbXColumn->setModel(m_aspectTreeModel); cbYColumn->setModel(m_aspectTreeModel); } cbValuesColumn->setModel(m_aspectTreeModel); cbXErrorMinusColumn->setModel(m_aspectTreeModel); cbXErrorPlusColumn->setModel(m_aspectTreeModel); cbYErrorMinusColumn->setModel(m_aspectTreeModel); cbYErrorPlusColumn->setModel(m_aspectTreeModel); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); Q_ASSERT(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); setModel(); initGeneralTab(); initTabs(); m_initializing=false; } void XYCurveDock::initGeneralTab() { //if there are more than one curve in the list, disable the content in the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.lXColumn->setEnabled(true); cbXColumn->setEnabled(true); uiGeneralTab.lYColumn->setEnabled(true); cbYColumn->setEnabled(true); this->setModelIndexFromAspect(cbXColumn, m_curve->xColumn()); this->setModelIndexFromAspect(cbYColumn, m_curve->yColumn()); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); } else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.lXColumn->setEnabled(false); cbXColumn->setEnabled(false); uiGeneralTab.lYColumn->setEnabled(false); cbYColumn->setEnabled(false); cbXColumn->setCurrentModelIndex(QModelIndex()); cbYColumn->setCurrentModelIndex(QModelIndex()); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_curve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_curve, SIGNAL(xColumnChanged(const AbstractColumn*)), this, SLOT(curveXColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(yColumnChanged(const AbstractColumn*)), this, SLOT(curveYColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(visibilityChanged(bool)), this, SLOT(curveVisibilityChanged(bool))); } void XYCurveDock::initTabs() { //if there are more than one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { this->setModelIndexFromAspect(cbValuesColumn, m_curve->valuesColumn()); this->setModelIndexFromAspect(cbXErrorPlusColumn, m_curve->xErrorPlusColumn()); this->setModelIndexFromAspect(cbXErrorMinusColumn, m_curve->xErrorMinusColumn()); this->setModelIndexFromAspect(cbYErrorPlusColumn, m_curve->yErrorPlusColumn()); this->setModelIndexFromAspect(cbYErrorMinusColumn, m_curve->yErrorMinusColumn()); } else { cbValuesColumn->setCurrentModelIndex(QModelIndex()); cbXErrorPlusColumn->setCurrentModelIndex(QModelIndex()); cbXErrorMinusColumn->setCurrentModelIndex(QModelIndex()); cbYErrorPlusColumn->setCurrentModelIndex(QModelIndex()); cbYErrorMinusColumn->setCurrentModelIndex(QModelIndex()); } //show the properties of the first curve load(); //Slots //Line-Tab connect(m_curve, SIGNAL(lineTypeChanged(XYCurve::LineType)), this, SLOT(curveLineTypeChanged(XYCurve::LineType))); connect(m_curve, SIGNAL(lineSkipGapsChanged(bool)), this, SLOT(curveLineSkipGapsChanged(bool))); connect(m_curve, SIGNAL(lineInterpolationPointsCountChanged(int)), this, SLOT(curveLineInterpolationPointsCountChanged(int))); connect(m_curve, SIGNAL(linePenChanged(QPen)), this, SLOT(curveLinePenChanged(QPen))); connect(m_curve, SIGNAL(lineOpacityChanged(qreal)), this, SLOT(curveLineOpacityChanged(qreal))); connect(m_curve, SIGNAL(dropLineTypeChanged(XYCurve::DropLineType)), this, SLOT(curveDropLineTypeChanged(XYCurve::DropLineType))); connect(m_curve, SIGNAL(dropLinePenChanged(QPen)), this, SLOT(curveDropLinePenChanged(QPen))); connect(m_curve, SIGNAL(dropLineOpacityChanged(qreal)), this, SLOT(curveDropLineOpacityChanged(qreal))); //Symbol-Tab connect(m_curve, SIGNAL(symbolsStyleChanged(Symbol::Style)), this, SLOT(curveSymbolsStyleChanged(Symbol::Style))); connect(m_curve, SIGNAL(symbolsSizeChanged(qreal)), this, SLOT(curveSymbolsSizeChanged(qreal))); connect(m_curve, SIGNAL(symbolsRotationAngleChanged(qreal)), this, SLOT(curveSymbolsRotationAngleChanged(qreal))); connect(m_curve, SIGNAL(symbolsOpacityChanged(qreal)), this, SLOT(curveSymbolsOpacityChanged(qreal))); connect(m_curve, SIGNAL(symbolsBrushChanged(QBrush)), this, SLOT(curveSymbolsBrushChanged(QBrush))); connect(m_curve, SIGNAL(symbolsPenChanged(QPen)), this, SLOT(curveSymbolsPenChanged(QPen))); //Values-Tab connect(m_curve, SIGNAL(valuesTypeChanged(XYCurve::ValuesType)), this, SLOT(curveValuesTypeChanged(XYCurve::ValuesType))); connect(m_curve, SIGNAL(valuesColumnChanged(const AbstractColumn*)), this, SLOT(curveValuesColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(valuesPositionChanged(XYCurve::ValuesPosition)), this, SLOT(curveValuesPositionChanged(XYCurve::ValuesPosition))); connect(m_curve, SIGNAL(valuesDistanceChanged(qreal)), this, SLOT(curveValuesDistanceChanged(qreal))); connect(m_curve, SIGNAL(valuesOpacityChanged(qreal)), this, SLOT(curveValuesOpacityChanged(qreal))); connect(m_curve, SIGNAL(valuesRotationAngleChanged(qreal)), this, SLOT(curveValuesRotationAngleChanged(qreal))); connect(m_curve, SIGNAL(valuesPrefixChanged(QString)), this, SLOT(curveValuesPrefixChanged(QString))); connect(m_curve, SIGNAL(valuesSuffixChanged(QString)), this, SLOT(curveValuesSuffixChanged(QString))); connect(m_curve, SIGNAL(valuesFontChanged(QFont)), this, SLOT(curveValuesFontChanged(QFont))); connect(m_curve, SIGNAL(valuesColorChanged(QColor)), this, SLOT(curveValuesColorChanged(QColor))); //Filling-Tab connect( m_curve, SIGNAL(fillingPositionChanged(XYCurve::FillingPosition)), this, SLOT(curveFillingPositionChanged(XYCurve::FillingPosition)) ); connect( m_curve, SIGNAL(fillingTypeChanged(PlotArea::BackgroundType)), this, SLOT(curveFillingTypeChanged(PlotArea::BackgroundType)) ); connect( m_curve, SIGNAL(fillingColorStyleChanged(PlotArea::BackgroundColorStyle)), this, SLOT(curveFillingColorStyleChanged(PlotArea::BackgroundColorStyle)) ); connect( m_curve, SIGNAL(fillingImageStyleChanged(PlotArea::BackgroundImageStyle)), this, SLOT(curveFillingImageStyleChanged(PlotArea::BackgroundImageStyle)) ); connect( m_curve, SIGNAL(fillingBrushStyleChanged(Qt::BrushStyle)), this, SLOT(curveFillingBrushStyleChanged(Qt::BrushStyle)) ); connect( m_curve, SIGNAL(fillingFirstColorChanged(QColor&)), this, SLOT(curveFillingFirstColorChanged(QColor&)) ); connect( m_curve, SIGNAL(fillingSecondColorChanged(QColor&)), this, SLOT(curveFillingSecondColorChanged(QColor&)) ); connect( m_curve, SIGNAL(fillingFileNameChanged(QString&)), this, SLOT(curveFillingFileNameChanged(QString&)) ); connect( m_curve, SIGNAL(fillingOpacityChanged(float)), this, SLOT(curveFillingOpacityChanged(float)) ); //"Error bars"-Tab connect(m_curve, SIGNAL(xErrorTypeChanged(XYCurve::ErrorType)), this, SLOT(curveXErrorTypeChanged(XYCurve::ErrorType))); connect(m_curve, SIGNAL(xErrorPlusColumnChanged(const AbstractColumn*)), this, SLOT(curveXErrorPlusColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(xErrorMinusColumnChanged(const AbstractColumn*)), this, SLOT(curveXErrorMinusColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(yErrorTypeChanged(XYCurve::ErrorType)), this, SLOT(curveYErrorTypeChanged(XYCurve::ErrorType))); connect(m_curve, SIGNAL(yErrorPlusColumnChanged(const AbstractColumn*)), this, SLOT(curveYErrorPlusColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(yErrorMinusColumnChanged(const AbstractColumn*)), this, SLOT(curveYErrorMinusColumnChanged(const AbstractColumn*))); connect(m_curve, SIGNAL(errorBarsCapSizeChanged(qreal)), this, SLOT(curveErrorBarsCapSizeChanged(qreal))); connect(m_curve, SIGNAL(errorBarsTypeChanged(XYCurve::ErrorBarsType)), this, SLOT(curveErrorBarsTypeChanged(XYCurve::ErrorBarsType))); connect(m_curve, SIGNAL(errorBarsPenChanged(QPen)), this, SLOT(curveErrorBarsPenChanged(QPen))); connect(m_curve, SIGNAL(errorBarsOpacityChanged(qreal)), this, SLOT(curveErrorBarsOpacityChanged(qreal))); } /*! depending on the currently selected values column type (column mode) updates the widgets for the values column format, shows/hides the allowed widgets, fills the corresponding combobox with the possible entries. Called when the values column was changed. synchronize this function with ColumnDock::updateFormat. */ void XYCurveDock::updateValuesFormatWidgets(const AbstractColumn::ColumnMode columnMode) { ui.cbValuesFormat->clear(); switch (columnMode) { case AbstractColumn::Numeric: ui.cbValuesFormat->addItem(i18n("Decimal"), QVariant('f')); ui.cbValuesFormat->addItem(i18n("Scientific (e)"), QVariant('e')); ui.cbValuesFormat->addItem(i18n("Scientific (E)"), QVariant('E')); ui.cbValuesFormat->addItem(i18n("Automatic (e)"), QVariant('g')); ui.cbValuesFormat->addItem(i18n("Automatic (E)"), QVariant('G')); break; case AbstractColumn::Integer: break; case AbstractColumn::Text: ui.cbValuesFormat->addItem(i18n("Text"), QVariant()); break; case AbstractColumn::Month: ui.cbValuesFormat->addItem(i18n("Number without leading zero"), QVariant("M")); ui.cbValuesFormat->addItem(i18n("Number with leading zero"), QVariant("MM")); ui.cbValuesFormat->addItem(i18n("Abbreviated month name"), QVariant("MMM")); ui.cbValuesFormat->addItem(i18n("Full month name"), QVariant("MMMM")); break; case AbstractColumn::Day: ui.cbValuesFormat->addItem(i18n("Number without leading zero"), QVariant("d")); ui.cbValuesFormat->addItem(i18n("Number with leading zero"), QVariant("dd")); ui.cbValuesFormat->addItem(i18n("Abbreviated day name"), QVariant("ddd")); ui.cbValuesFormat->addItem(i18n("Full day name"), QVariant("dddd")); break; case AbstractColumn::DateTime: { for (const auto& s: AbstractColumn::dateFormats()) ui.cbValuesFormat->addItem(s, QVariant(s)); for (const auto& s: AbstractColumn::timeFormats()) ui.cbValuesFormat->addItem(s, QVariant(s)); for (const auto& s1: AbstractColumn::dateFormats()) { for (const auto& s2: AbstractColumn::timeFormats()) ui.cbValuesFormat->addItem(s1 + ' ' + s2, QVariant(s1 + ' ' + s2)); } break; } } if (columnMode == AbstractColumn::Numeric) { ui.lValuesPrecision->show(); ui.sbValuesPrecision->show(); } else { ui.lValuesPrecision->hide(); ui.sbValuesPrecision->hide(); } if (columnMode == AbstractColumn::Text) { ui.lValuesFormatTop->hide(); ui.lValuesFormat->hide(); ui.cbValuesFormat->hide(); } else { ui.lValuesFormatTop->show(); ui.lValuesFormat->show(); ui.cbValuesFormat->show(); } if (columnMode == AbstractColumn::DateTime) { ui.cbValuesFormat->setCurrentItem("yyyy-MM-dd hh:mm:ss.zzz"); ui.cbValuesFormat->setEditable(true); } else { ui.cbValuesFormat->setCurrentIndex(0); ui.cbValuesFormat->setEditable(false); } } /*! shows the formating properties of the column \c column. Called, when a new column for the values was selected - either by changing the type of the values (none, x, y, etc.) or by selecting a new custom column for the values. */ void XYCurveDock::showValuesColumnFormat(const Column* column) { if (!column) { // no valid column is available // -> hide all the format properties widgets (equivalent to showing the properties of the column mode "Text") this->updateValuesFormatWidgets(AbstractColumn::Text); } else { AbstractColumn::ColumnMode columnMode = column->columnMode(); //update the format widgets for the new column mode this->updateValuesFormatWidgets(columnMode); //show the actuall formating properties switch (columnMode) { case AbstractColumn::Numeric: { Double2StringFilter* filter = static_cast(column->outputFilter()); ui.cbValuesFormat->setCurrentIndex(ui.cbValuesFormat->findData(filter->numericFormat())); ui.sbValuesPrecision->setValue(filter->numDigits()); break; } case AbstractColumn::Integer: case AbstractColumn::Text: break; case AbstractColumn::Month: case AbstractColumn::Day: case AbstractColumn::DateTime: { DateTime2StringFilter* filter = static_cast(column->outputFilter()); DEBUG(" column values format = " << filter->format().toStdString()); ui.cbValuesFormat->setCurrentIndex(ui.cbValuesFormat->findData(filter->format())); break; } } } } void XYCurveDock::setModelIndexFromAspect(TreeViewComboBox* cb, const AbstractAspect* aspect) { if (aspect) cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(aspect)); else cb->setCurrentModelIndex(QModelIndex()); } //************************************************************* //********** SLOTs for changes triggered in XYCurveDock ******** //************************************************************* void XYCurveDock::retranslateUi() { //TODO: // uiGeneralTab.lName->setText(i18n("Name")); // uiGeneralTab.lComment->setText(i18n("Comment")); // uiGeneralTab.chkVisible->setText(i18n("Visible")); // uiGeneralTab.lXColumn->setText(i18n("x-data")); // uiGeneralTab.lYColumn->setText(i18n("y-data")); //TODO updatePenStyles, updateBrushStyles for all comboboxes } // "General"-tab void XYCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYCurveDock::xColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column); } for (auto* curve : m_curvesList) curve->setXColumn(column); } void XYCurveDock::yColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column); } for (auto* curve : m_curvesList) curve->setYColumn(column); } void XYCurveDock::visibilityChanged(bool state) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setVisible(state); } // "Line"-tab void XYCurveDock::lineTypeChanged(int index) { XYCurve::LineType lineType = XYCurve::LineType(index); if ( lineType == XYCurve::NoLine) { ui.chkLineSkipGaps->setEnabled(false); ui.cbLineStyle->setEnabled(false); ui.kcbLineColor->setEnabled(false); ui.sbLineWidth->setEnabled(false); ui.sbLineOpacity->setEnabled(false); ui.lLineInterpolationPointsCount->hide(); ui.sbLineInterpolationPointsCount->hide(); } else { ui.chkLineSkipGaps->setEnabled(true); ui.cbLineStyle->setEnabled(true); ui.kcbLineColor->setEnabled(true); ui.sbLineWidth->setEnabled(true); ui.sbLineOpacity->setEnabled(true); if (lineType == XYCurve::SplineCubicNatural || lineType == XYCurve::SplineCubicPeriodic || lineType == XYCurve::SplineAkimaNatural || lineType == XYCurve::SplineAkimaPeriodic) { ui.lLineInterpolationPointsCount->show(); ui.sbLineInterpolationPointsCount->show(); ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } else { ui.lLineInterpolationPointsCount->hide(); ui.sbLineInterpolationPointsCount->hide(); ui.lLineSkipGaps->show(); ui.chkLineSkipGaps->show(); } } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setLineType(lineType); } void XYCurveDock::lineSkipGapsChanged(bool skip) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setLineSkipGaps(skip); } void XYCurveDock::lineInterpolationPointsCountChanged(int count) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setLineInterpolationPointsCount(count); } void XYCurveDock::lineStyleChanged(int index) { if (m_initializing) return; Qt::PenStyle penStyle=Qt::PenStyle(index); QPen pen; for (auto* curve : m_curvesList) { pen=curve->linePen(); pen.setStyle(penStyle); curve->setLinePen(pen); } } void XYCurveDock::lineColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->linePen(); pen.setColor(color); curve->setLinePen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbLineStyle, color); m_initializing = false; } void XYCurveDock::lineWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->linePen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); curve->setLinePen(pen); } } void XYCurveDock::lineOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setLineOpacity(opacity); } void XYCurveDock::dropLineTypeChanged(int index) { XYCurve::DropLineType dropLineType = XYCurve::DropLineType(index); if ( dropLineType == XYCurve::NoDropLine) { ui.cbDropLineStyle->setEnabled(false); ui.kcbDropLineColor->setEnabled(false); ui.sbDropLineWidth->setEnabled(false); ui.sbDropLineOpacity->setEnabled(false); } else { ui.cbDropLineStyle->setEnabled(true); ui.kcbDropLineColor->setEnabled(true); ui.sbDropLineWidth->setEnabled(true); ui.sbDropLineOpacity->setEnabled(true); } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setDropLineType(dropLineType); } void XYCurveDock::dropLineStyleChanged(int index) { if (m_initializing) return; Qt::PenStyle penStyle=Qt::PenStyle(index); QPen pen; for (auto* curve : m_curvesList) { pen=curve->dropLinePen(); pen.setStyle(penStyle); curve->setDropLinePen(pen); } } void XYCurveDock::dropLineColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->dropLinePen(); pen.setColor(color); curve->setDropLinePen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbDropLineStyle, color); m_initializing = false; } void XYCurveDock::dropLineWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->dropLinePen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); curve->setDropLinePen(pen); } } void XYCurveDock::dropLineOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setDropLineOpacity(opacity); } //"Symbol"-tab void XYCurveDock::symbolsStyleChanged(int index) { Symbol::Style style = Symbol::Style(index); if (style == Symbol::NoSymbols) { ui.sbSymbolSize->setEnabled(false); ui.sbSymbolRotation->setEnabled(false); ui.sbSymbolOpacity->setEnabled(false); ui.kcbSymbolFillingColor->setEnabled(false); ui.cbSymbolFillingStyle->setEnabled(false); ui.cbSymbolBorderStyle->setEnabled(false); ui.kcbSymbolBorderColor->setEnabled(false); ui.sbSymbolBorderWidth->setEnabled(false); } else { ui.sbSymbolSize->setEnabled(true); ui.sbSymbolRotation->setEnabled(true); ui.sbSymbolOpacity->setEnabled(true); //enable/disable the symbol filling options in the GUI depending on the currently selected symbol. if (style!=Symbol::Line && style!=Symbol::Cross) { ui.cbSymbolFillingStyle->setEnabled(true); bool noBrush = (Qt::BrushStyle(ui.cbSymbolFillingStyle->currentIndex()) == Qt::NoBrush); ui.kcbSymbolFillingColor->setEnabled(!noBrush); } else { ui.kcbSymbolFillingColor->setEnabled(false); ui.cbSymbolFillingStyle->setEnabled(false); } ui.cbSymbolBorderStyle->setEnabled(true); bool noLine = (Qt::PenStyle(ui.cbSymbolBorderStyle->currentIndex()) == Qt::NoPen); ui.kcbSymbolBorderColor->setEnabled(!noLine); ui.sbSymbolBorderWidth->setEnabled(!noLine); } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setSymbolsStyle(style); } void XYCurveDock::symbolsSizeChanged(double value) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setSymbolsSize( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void XYCurveDock::symbolsRotationChanged(int value) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setSymbolsRotationAngle(value); } void XYCurveDock::symbolsOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setSymbolsOpacity(opacity); } void XYCurveDock::symbolsFillingStyleChanged(int index) { Qt::BrushStyle brushStyle = Qt::BrushStyle(index); ui.kcbSymbolFillingColor->setEnabled(!(brushStyle == Qt::NoBrush)); if (m_initializing) return; QBrush brush; for (auto* curve : m_curvesList) { brush=curve->symbolsBrush(); brush.setStyle(brushStyle); curve->setSymbolsBrush(brush); } } void XYCurveDock::symbolsFillingColorChanged(const QColor& color) { if (m_initializing) return; QBrush brush; for (auto* curve : m_curvesList) { brush=curve->symbolsBrush(); brush.setColor(color); curve->setSymbolsBrush(brush); } m_initializing = true; GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, color ); m_initializing = false; } void XYCurveDock::symbolsBorderStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); if ( penStyle == Qt::NoPen ) { ui.kcbSymbolBorderColor->setEnabled(false); ui.sbSymbolBorderWidth->setEnabled(false); } else { ui.kcbSymbolBorderColor->setEnabled(true); ui.sbSymbolBorderWidth->setEnabled(true); } if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->symbolsPen(); pen.setStyle(penStyle); curve->setSymbolsPen(pen); } } void XYCurveDock::symbolsBorderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->symbolsPen(); pen.setColor(color); curve->setSymbolsPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, color); m_initializing = false; } void XYCurveDock::symbolsBorderWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->symbolsPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); curve->setSymbolsPen(pen); } } //Values-tab /*! called when the type of the values (none, x, y, (x,y) etc.) was changed. */ void XYCurveDock::valuesTypeChanged(int index) { XYCurve::ValuesType valuesType = XYCurve::ValuesType(index); if (valuesType == XYCurve::NoValues) { //no values are to paint -> deactivate all the pertinent widgets ui.cbValuesPosition->setEnabled(false); ui.lValuesColumn->hide(); cbValuesColumn->hide(); ui.sbValuesDistance->setEnabled(false); ui.sbValuesRotation->setEnabled(false); ui.sbValuesOpacity->setEnabled(false); ui.cbValuesFormat->setEnabled(false); ui.cbValuesFormat->setEnabled(false); ui.sbValuesPrecision->setEnabled(false); ui.leValuesPrefix->setEnabled(false); ui.leValuesSuffix->setEnabled(false); ui.kfrValuesFont->setEnabled(false); ui.kcbValuesColor->setEnabled(false); } else { ui.cbValuesPosition->setEnabled(true); ui.sbValuesDistance->setEnabled(true); ui.sbValuesRotation->setEnabled(true); ui.sbValuesOpacity->setEnabled(true); ui.cbValuesFormat->setEnabled(true); ui.sbValuesPrecision->setEnabled(true); ui.leValuesPrefix->setEnabled(true); ui.leValuesSuffix->setEnabled(true); ui.kfrValuesFont->setEnabled(true); ui.kcbValuesColor->setEnabled(true); const Column* column; if (valuesType == XYCurve::ValuesCustomColumn) { ui.lValuesColumn->show(); cbValuesColumn->show(); column= static_cast(cbValuesColumn->currentModelIndex().internalPointer()); } else { ui.lValuesColumn->hide(); cbValuesColumn->hide(); if (valuesType == XYCurve::ValuesY) column = static_cast(m_curve->yColumn()); else column = static_cast(m_curve->xColumn()); } this->showValuesColumnFormat(column); } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesType(valuesType); } /*! called when the custom column for the values was changed. */ void XYCurveDock::valuesColumnChanged(const QModelIndex& index) { if (m_initializing) return; Column* column= static_cast(index.internalPointer()); this->showValuesColumnFormat(column); for (auto* curve : m_curvesList) { //TODO save also the format of the currently selected column for the values (precision etc.) curve->setValuesColumn(column); } } void XYCurveDock::valuesPositionChanged(int index) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesPosition(XYCurve::ValuesPosition(index)); } void XYCurveDock::valuesDistanceChanged(double value) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesDistance( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void XYCurveDock::valuesRotationChanged(int value) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesRotationAngle(value); } void XYCurveDock::valuesOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setValuesOpacity(opacity); } void XYCurveDock::valuesPrefixChanged() { if (m_initializing) return; QString prefix = ui.leValuesPrefix->text(); for (auto* curve : m_curvesList) curve->setValuesPrefix(prefix); } void XYCurveDock::valuesSuffixChanged() { if (m_initializing) return; QString suffix = ui.leValuesSuffix->text(); for (auto* curve : m_curvesList) curve->setValuesSuffix(suffix); } void XYCurveDock::valuesFontChanged(const QFont& font) { if (m_initializing) return; QFont valuesFont = font; valuesFont.setPixelSize( Worksheet::convertToSceneUnits(font.pointSizeF(), Worksheet::Point) ); for (auto* curve : m_curvesList) curve->setValuesFont(valuesFont); } void XYCurveDock::valuesColorChanged(const QColor& color) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setValuesColor(color); } //Filling-tab void XYCurveDock::fillingPositionChanged(int index) { XYCurve::FillingPosition fillingPosition = XYCurve::FillingPosition(index); bool b = (fillingPosition != XYCurve::NoFilling); ui.cbFillingType->setEnabled(b); ui.cbFillingColorStyle->setEnabled(b); ui.cbFillingBrushStyle->setEnabled(b); ui.cbFillingImageStyle->setEnabled(b); ui.kcbFillingFirstColor->setEnabled(b); ui.kcbFillingSecondColor->setEnabled(b); ui.leFillingFileName->setEnabled(b); ui.bFillingOpen->setEnabled(b); ui.sbFillingOpacity->setEnabled(b); if (m_initializing) return; for (auto* curve : m_curvesList) curve->setFillingPosition(fillingPosition); } void XYCurveDock::fillingTypeChanged(int index) { PlotArea::BackgroundType type = (PlotArea::BackgroundType)index; if (type == PlotArea::Color) { ui.lFillingColorStyle->show(); ui.cbFillingColorStyle->show(); ui.lFillingImageStyle->hide(); ui.cbFillingImageStyle->hide(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); ui.lFillingFileName->hide(); ui.leFillingFileName->hide(); ui.bFillingOpen->hide(); ui.lFillingFirstColor->show(); ui.kcbFillingFirstColor->show(); PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle) ui.cbFillingColorStyle->currentIndex(); if (style == PlotArea::SingleColor) { ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } else { ui.lFillingFirstColor->setText(i18n("First color")); ui.lFillingSecondColor->show(); ui.kcbFillingSecondColor->show(); } } else if (type == PlotArea::Image) { ui.lFillingColorStyle->hide(); ui.cbFillingColorStyle->hide(); ui.lFillingImageStyle->show(); ui.cbFillingImageStyle->show(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); ui.lFillingFileName->show(); ui.leFillingFileName->show(); ui.bFillingOpen->show(); ui.lFillingFirstColor->hide(); ui.kcbFillingFirstColor->hide(); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } else if (type == PlotArea::Pattern) { ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingColorStyle->hide(); ui.cbFillingColorStyle->hide(); ui.lFillingImageStyle->hide(); ui.cbFillingImageStyle->hide(); ui.lFillingBrushStyle->show(); ui.cbFillingBrushStyle->show(); ui.lFillingFileName->hide(); ui.leFillingFileName->hide(); ui.bFillingOpen->hide(); ui.lFillingFirstColor->show(); ui.kcbFillingFirstColor->show(); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setFillingType(type); } void XYCurveDock::fillingColorStyleChanged(int index) { PlotArea::BackgroundColorStyle style = (PlotArea::BackgroundColorStyle)index; if (style == PlotArea::SingleColor) { ui.lFillingFirstColor->setText(i18n("Color")); ui.lFillingSecondColor->hide(); ui.kcbFillingSecondColor->hide(); } else { ui.lFillingFirstColor->setText(i18n("First color")); ui.lFillingSecondColor->show(); ui.kcbFillingSecondColor->show(); ui.lFillingBrushStyle->hide(); ui.cbFillingBrushStyle->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) curve->setFillingColorStyle(style); } void XYCurveDock::fillingImageStyleChanged(int index) { if (m_initializing) return; PlotArea::BackgroundImageStyle style = (PlotArea::BackgroundImageStyle)index; for (auto* curve : m_curvesList) curve->setFillingImageStyle(style); } void XYCurveDock::fillingBrushStyleChanged(int index) { if (m_initializing) return; Qt::BrushStyle style = (Qt::BrushStyle)index; for (auto* curve : m_curvesList) curve->setFillingBrushStyle(style); } void XYCurveDock::fillingFirstColorChanged(const QColor& c) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setFillingFirstColor(c); m_initializing = true; GuiTools::updateBrushStyles(ui.cbFillingBrushStyle, c); m_initializing = false; } void XYCurveDock::fillingSecondColorChanged(const QColor& c) { if (m_initializing) return; for (auto* curve : m_curvesList) curve->setFillingSecondColor(c); } /*! opens a file dialog and lets the user select the image file. */ void XYCurveDock::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "XYCurveDock"); QString dir = conf.readEntry("LastImageDir", ""); QString formats; for (const QByteArray& format : QImageReader::supportedImageFormats()) { QString f = "*." + QString(format.constData()); formats.isEmpty() ? formats+=f : formats+=' '+f; } QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastImageDir", newDir); } ui.leFillingFileName->setText( path ); for (auto* curve : m_curvesList) curve->setFillingFileName(path); } void XYCurveDock::fileNameChanged() { if (m_initializing) return; QString fileName = ui.leFillingFileName->text(); for (auto* curve : m_curvesList) curve->setFillingFileName(fileName); } void XYCurveDock::fillingOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setFillingOpacity(opacity); } //"Error bars"-Tab void XYCurveDock::xErrorTypeChanged(int index) const { if (index == 0) { //no error ui.lXErrorDataPlus->setVisible(false); cbXErrorPlusColumn->setVisible(false); ui.lXErrorDataMinus->setVisible(false); cbXErrorMinusColumn->setVisible(false); } else if (index == 1) { //symmetric error ui.lXErrorDataPlus->setVisible(true); cbXErrorPlusColumn->setVisible(true); ui.lXErrorDataMinus->setVisible(false); cbXErrorMinusColumn->setVisible(false); ui.lXErrorDataPlus->setText(i18n("Data, +-")); } else if (index == 2) { //asymmetric error ui.lXErrorDataPlus->setVisible(true); cbXErrorPlusColumn->setVisible(true); ui.lXErrorDataMinus->setVisible(true); cbXErrorMinusColumn->setVisible(true); ui.lXErrorDataPlus->setText(i18n("Data, +")); } bool b = (index!=0 || ui.cbYErrorType->currentIndex()!=0); ui.lErrorFormat->setVisible(b); ui.lErrorBarsType->setVisible(b); ui.cbErrorBarsType->setVisible(b); ui.lErrorBarsStyle->setVisible(b); ui.cbErrorBarsStyle->setVisible(b); ui.lErrorBarsColor->setVisible(b); ui.kcbErrorBarsColor->setVisible(b); ui.lErrorBarsWidth->setVisible(b); ui.sbErrorBarsWidth->setVisible(b); ui.lErrorBarsOpacity->setVisible(b); ui.sbErrorBarsOpacity->setVisible(b); if (m_initializing) return; for (auto* curve : m_curvesList) curve->setXErrorType(XYCurve::ErrorType(index)); } void XYCurveDock::xErrorPlusColumnChanged(const QModelIndex& index) const { Q_UNUSED(index); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); Q_ASSERT(column); for (auto* curve : m_curvesList) curve->setXErrorPlusColumn(column); } void XYCurveDock::xErrorMinusColumnChanged(const QModelIndex& index) const { Q_UNUSED(index); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); Q_ASSERT(column); for (auto* curve : m_curvesList) curve->setXErrorMinusColumn(column); } void XYCurveDock::yErrorTypeChanged(int index) const { if (index == 0) { //no error ui.lYErrorDataPlus->setVisible(false); cbYErrorPlusColumn->setVisible(false); ui.lYErrorDataMinus->setVisible(false); cbYErrorMinusColumn->setVisible(false); } else if (index == 1) { //symmetric error ui.lYErrorDataPlus->setVisible(true); cbYErrorPlusColumn->setVisible(true); ui.lYErrorDataMinus->setVisible(false); cbYErrorMinusColumn->setVisible(false); ui.lYErrorDataPlus->setText(i18n("Data, +-")); } else if (index == 2) { //asymmetric error ui.lYErrorDataPlus->setVisible(true); cbYErrorPlusColumn->setVisible(true); ui.lYErrorDataMinus->setVisible(true); cbYErrorMinusColumn->setVisible(true); ui.lYErrorDataPlus->setText(i18n("Data, +")); } bool b = (index!=0 || ui.cbXErrorType->currentIndex()!=0); ui.lErrorFormat->setVisible(b); ui.lErrorBarsType->setVisible(b); ui.cbErrorBarsType->setVisible(b); ui.lErrorBarsStyle->setVisible(b); ui.cbErrorBarsStyle->setVisible(b); ui.lErrorBarsColor->setVisible(b); ui.kcbErrorBarsColor->setVisible(b); ui.lErrorBarsWidth->setVisible(b); ui.sbErrorBarsWidth->setVisible(b); ui.lErrorBarsOpacity->setVisible(b); ui.sbErrorBarsOpacity->setVisible(b); if (m_initializing) return; for (auto* curve : m_curvesList) curve->setYErrorType(XYCurve::ErrorType(index)); } void XYCurveDock::yErrorPlusColumnChanged(const QModelIndex& index) const { Q_UNUSED(index); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); Q_ASSERT(column); for (auto* curve : m_curvesList) curve->setYErrorPlusColumn(column); } void XYCurveDock::yErrorMinusColumnChanged(const QModelIndex& index) const { Q_UNUSED(index); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); Q_ASSERT(column); for (auto* curve : m_curvesList) curve->setYErrorMinusColumn(column); } void XYCurveDock::errorBarsTypeChanged(int index) const { XYCurve::ErrorBarsType type = XYCurve::ErrorBarsType(index); bool b = (type == XYCurve::ErrorBarsWithEnds); ui.lErrorBarsCapSize->setVisible(b); ui.sbErrorBarsCapSize->setVisible(b); if (m_initializing) return; for (auto* curve : m_curvesList) curve->setErrorBarsType(type); } void XYCurveDock::errorBarsCapSizeChanged(double value) const { if (m_initializing) return; float size = Worksheet::convertToSceneUnits(value, Worksheet::Point); for (auto* curve : m_curvesList) curve->setErrorBarsCapSize(size); } void XYCurveDock::errorBarsStyleChanged(int index) const { if (m_initializing) return; Qt::PenStyle penStyle=Qt::PenStyle(index); QPen pen; for (auto* curve : m_curvesList) { pen=curve->errorBarsPen(); pen.setStyle(penStyle); curve->setErrorBarsPen(pen); } } void XYCurveDock::errorBarsColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->errorBarsPen(); pen.setColor(color); curve->setErrorBarsPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbErrorBarsStyle, color); m_initializing = false; } void XYCurveDock::errorBarsWidthChanged(double value) const { if (m_initializing) return; QPen pen; for (auto* curve : m_curvesList) { pen=curve->errorBarsPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); curve->setErrorBarsPen(pen); } } void XYCurveDock::errorBarsOpacityChanged(int value) const { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curvesList) curve->setErrorBarsOpacity(opacity); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYCurveDock::curveXColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbXColumn, column); m_initializing = false; } void XYCurveDock::curveYColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbYColumn, column); m_initializing = false; } void XYCurveDock::curveVisibilityChanged(bool on) { m_initializing = true; uiGeneralTab.chkVisible->setChecked(on); m_initializing = false; } //Line-Tab void XYCurveDock::curveLineTypeChanged(XYCurve::LineType type) { m_initializing = true; ui.cbLineType->setCurrentIndex( (int) type); m_initializing = false; } void XYCurveDock::curveLineSkipGapsChanged(bool skip) { m_initializing = true; ui.chkLineSkipGaps->setChecked(skip); m_initializing = false; } void XYCurveDock::curveLineInterpolationPointsCountChanged(int count) { m_initializing = true; ui.sbLineInterpolationPointsCount->setValue(count); m_initializing = false; } void XYCurveDock::curveLinePenChanged(const QPen& pen) { m_initializing = true; ui.cbLineStyle->setCurrentIndex( (int)pen.style()); ui.kcbLineColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbLineStyle, pen.color()); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits( pen.widthF(), Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveLineOpacityChanged(qreal opacity) { m_initializing = true; ui.sbLineOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void XYCurveDock::curveDropLineTypeChanged(XYCurve::DropLineType type) { m_initializing = true; ui.cbDropLineType->setCurrentIndex( (int)type ); m_initializing = false; } void XYCurveDock::curveDropLinePenChanged(const QPen& pen) { m_initializing = true; ui.cbDropLineStyle->setCurrentIndex( (int) pen.style()); ui.kcbDropLineColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbDropLineStyle, pen.color()); ui.sbDropLineWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveDropLineOpacityChanged(qreal opacity) { m_initializing = true; ui.sbDropLineOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } //Symbol-Tab void XYCurveDock::curveSymbolsStyleChanged(Symbol::Style style) { m_initializing = true; ui.cbSymbolStyle->setCurrentIndex((int)style); m_initializing = false; } void XYCurveDock::curveSymbolsSizeChanged(qreal size) { m_initializing = true; ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveSymbolsRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbSymbolRotation->setValue(angle); m_initializing = false; } void XYCurveDock::curveSymbolsOpacityChanged(qreal opacity) { m_initializing = true; ui.sbSymbolOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void XYCurveDock::curveSymbolsBrushChanged(const QBrush& brush) { m_initializing = true; ui.cbSymbolFillingStyle->setCurrentIndex((int) brush.style()); ui.kcbSymbolFillingColor->setColor(brush.color()); GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, brush.color()); m_initializing = false; } void XYCurveDock::curveSymbolsPenChanged(const QPen& pen) { m_initializing = true; ui.cbSymbolBorderStyle->setCurrentIndex( (int) pen.style()); ui.kcbSymbolBorderColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, pen.color()); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Point)); m_initializing = false; } //Values-Tab void XYCurveDock::curveValuesTypeChanged(XYCurve::ValuesType type) { m_initializing = true; ui.cbValuesType->setCurrentIndex((int) type); m_initializing = false; } void XYCurveDock::curveValuesColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbValuesColumn, column); m_initializing = false; } void XYCurveDock::curveValuesPositionChanged(XYCurve::ValuesPosition position) { m_initializing = true; ui.cbValuesPosition->setCurrentIndex((int) position); m_initializing = false; } void XYCurveDock::curveValuesDistanceChanged(qreal distance) { m_initializing = true; ui.sbValuesDistance->setValue( Worksheet::convertFromSceneUnits(distance, Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveValuesRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbValuesRotation->setValue(angle); m_initializing = false; } void XYCurveDock::curveValuesOpacityChanged(qreal opacity) { m_initializing = true; ui.sbValuesOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void XYCurveDock::curveValuesPrefixChanged(const QString& prefix) { m_initializing = true; ui.leValuesPrefix->setText(prefix); m_initializing = false; } void XYCurveDock::curveValuesSuffixChanged(const QString& suffix) { m_initializing = true; ui.leValuesSuffix->setText(suffix); m_initializing = false; } void XYCurveDock::curveValuesFontChanged(QFont font) { m_initializing = true; font.setPointSizeF( round(Worksheet::convertFromSceneUnits(font.pixelSize(), Worksheet::Point)) ); ui.kfrValuesFont->setFont(font); m_initializing = false; } void XYCurveDock::curveValuesColorChanged(QColor color) { m_initializing = true; ui.kcbValuesColor->setColor(color); m_initializing = false; } //Filling void XYCurveDock::curveFillingPositionChanged(XYCurve::FillingPosition position) { m_initializing = true; ui.cbFillingPosition->setCurrentIndex((int)position); m_initializing = false; } void XYCurveDock::curveFillingTypeChanged(PlotArea::BackgroundType type) { m_initializing = true; ui.cbFillingType->setCurrentIndex(type); m_initializing = false; } void XYCurveDock::curveFillingColorStyleChanged(PlotArea::BackgroundColorStyle style) { m_initializing = true; ui.cbFillingColorStyle->setCurrentIndex(style); m_initializing = false; } void XYCurveDock::curveFillingImageStyleChanged(PlotArea::BackgroundImageStyle style) { m_initializing = true; ui.cbFillingImageStyle->setCurrentIndex(style); m_initializing = false; } void XYCurveDock::curveFillingBrushStyleChanged(Qt::BrushStyle style) { m_initializing = true; ui.cbFillingBrushStyle->setCurrentIndex(style); m_initializing = false; } void XYCurveDock::curveFillingFirstColorChanged(QColor& color) { m_initializing = true; ui.kcbFillingFirstColor->setColor(color); GuiTools::updateBrushStyles(ui.cbFillingBrushStyle, color); m_initializing = false; } void XYCurveDock::curveFillingSecondColorChanged(QColor& color) { m_initializing = true; ui.kcbFillingSecondColor->setColor(color); m_initializing = false; } void XYCurveDock::curveFillingFileNameChanged(QString& filename) { m_initializing = true; ui.leFillingFileName->setText(filename); m_initializing = false; } void XYCurveDock::curveFillingOpacityChanged(float opacity) { m_initializing = true; ui.sbFillingOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } //"Error bars"-Tab void XYCurveDock::curveXErrorTypeChanged(XYCurve::ErrorType type) { m_initializing = true; ui.cbXErrorType->setCurrentIndex((int) type); m_initializing = false; } void XYCurveDock::curveXErrorPlusColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbXErrorPlusColumn, column); m_initializing = false; } void XYCurveDock::curveXErrorMinusColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbXErrorMinusColumn, column); m_initializing = false; } void XYCurveDock::curveYErrorTypeChanged(XYCurve::ErrorType type) { m_initializing = true; ui.cbYErrorType->setCurrentIndex((int) type); m_initializing = false; } void XYCurveDock::curveYErrorPlusColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbYErrorPlusColumn, column); m_initializing = false; } void XYCurveDock::curveYErrorMinusColumnChanged(const AbstractColumn* column) { m_initializing = true; this->setModelIndexFromAspect(cbYErrorMinusColumn, column); m_initializing = false; } void XYCurveDock::curveErrorBarsCapSizeChanged(qreal size) { m_initializing = true; ui.sbErrorBarsCapSize->setValue( Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveErrorBarsTypeChanged(XYCurve::ErrorBarsType type) { m_initializing = true; ui.cbErrorBarsType->setCurrentIndex( (int) type); m_initializing = false; } void XYCurveDock::curveErrorBarsPenChanged(const QPen& pen) { m_initializing = true; ui.cbErrorBarsStyle->setCurrentIndex( (int) pen.style()); ui.kcbErrorBarsColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbErrorBarsStyle, pen.color()); ui.sbErrorBarsWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point) ); m_initializing = false; } void XYCurveDock::curveErrorBarsOpacityChanged(qreal opacity) { m_initializing = true; ui.sbErrorBarsOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } //************************************************************* //************************* Settings ************************** //************************************************************* void XYCurveDock::load() { //General //This data is read in XYCurveDock::setCurves(). //Line ui.cbLineType->setCurrentIndex( (int) m_curve->lineType() ); ui.chkLineSkipGaps->setChecked( m_curve->lineSkipGaps() ); ui.sbLineInterpolationPointsCount->setValue( m_curve->lineInterpolationPointsCount() ); ui.cbLineStyle->setCurrentIndex( (int) m_curve->linePen().style() ); ui.kcbLineColor->setColor( m_curve->linePen().color() ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(m_curve->linePen().widthF(), Worksheet::Point) ); ui.sbLineOpacity->setValue( round(m_curve->lineOpacity()*100.0) ); //Drop lines ui.cbDropLineType->setCurrentIndex( (int) m_curve->dropLineType() ); ui.cbDropLineStyle->setCurrentIndex( (int) m_curve->dropLinePen().style() ); ui.kcbDropLineColor->setColor( m_curve->dropLinePen().color() ); ui.sbDropLineWidth->setValue( Worksheet::convertFromSceneUnits(m_curve->dropLinePen().widthF(),Worksheet::Point) ); ui.sbDropLineOpacity->setValue( round(m_curve->dropLineOpacity()*100.0) ); //Symbols //TODO: character ui.cbSymbolStyle->setCurrentIndex( (int)m_curve->symbolsStyle() ); ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(m_curve->symbolsSize(), Worksheet::Point) ); ui.sbSymbolRotation->setValue( m_curve->symbolsRotationAngle() ); ui.sbSymbolOpacity->setValue( round(m_curve->symbolsOpacity()*100.0) ); ui.cbSymbolFillingStyle->setCurrentIndex( (int) m_curve->symbolsBrush().style() ); ui.kcbSymbolFillingColor->setColor( m_curve->symbolsBrush().color() ); ui.cbSymbolBorderStyle->setCurrentIndex( (int) m_curve->symbolsPen().style() ); ui.kcbSymbolBorderColor->setColor( m_curve->symbolsPen().color() ); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_curve->symbolsPen().widthF(), Worksheet::Point) ); //Values ui.cbValuesType->setCurrentIndex( (int) m_curve->valuesType() ); ui.cbValuesPosition->setCurrentIndex( (int) m_curve->valuesPosition() ); ui.sbValuesDistance->setValue( Worksheet::convertFromSceneUnits(m_curve->valuesDistance(), Worksheet::Point) ); ui.sbValuesRotation->setValue( m_curve->valuesRotationAngle() ); ui.sbValuesOpacity->setValue( round(m_curve->valuesOpacity()*100.0) ); ui.leValuesPrefix->setText( m_curve->valuesPrefix() ); ui.leValuesSuffix->setText( m_curve->valuesSuffix() ); QFont valuesFont = m_curve->valuesFont(); valuesFont.setPointSizeF( round(Worksheet::convertFromSceneUnits(valuesFont.pixelSize(), Worksheet::Point)) ); ui.kfrValuesFont->setFont(valuesFont); ui.kcbValuesColor->setColor( m_curve->valuesColor() ); //Filling ui.cbFillingPosition->setCurrentIndex( (int) m_curve->fillingPosition() ); ui.cbFillingType->setCurrentIndex( (int)m_curve->fillingType() ); ui.cbFillingColorStyle->setCurrentIndex( (int) m_curve->fillingColorStyle() ); ui.cbFillingImageStyle->setCurrentIndex( (int) m_curve->fillingImageStyle() ); ui.cbFillingBrushStyle->setCurrentIndex( (int) m_curve->fillingBrushStyle() ); ui.leFillingFileName->setText( m_curve->fillingFileName() ); ui.kcbFillingFirstColor->setColor( m_curve->fillingFirstColor() ); ui.kcbFillingSecondColor->setColor( m_curve->fillingSecondColor() ); ui.sbFillingOpacity->setValue( round(m_curve->fillingOpacity()*100.0) ); //Error bars ui.cbXErrorType->setCurrentIndex( (int) m_curve->xErrorType() ); ui.cbYErrorType->setCurrentIndex( (int) m_curve->yErrorType() ); ui.cbErrorBarsType->setCurrentIndex( (int) m_curve->errorBarsType() ); ui.sbErrorBarsCapSize->setValue( Worksheet::convertFromSceneUnits(m_curve->errorBarsCapSize(), Worksheet::Point) ); ui.cbErrorBarsStyle->setCurrentIndex( (int) m_curve->errorBarsPen().style() ); ui.kcbErrorBarsColor->setColor( m_curve->errorBarsPen().color() ); ui.sbErrorBarsWidth->setValue( Worksheet::convertFromSceneUnits(m_curve->errorBarsPen().widthF(),Worksheet::Point) ); ui.sbErrorBarsOpacity->setValue( round(m_curve->errorBarsOpacity()*100.0) ); m_initializing=true; GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); GuiTools::updatePenStyles(ui.cbDropLineStyle, ui.kcbDropLineColor->color()); GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, ui.kcbSymbolFillingColor->color()); GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, ui.kcbSymbolBorderColor->color()); GuiTools::updatePenStyles(ui.cbErrorBarsStyle, ui.kcbErrorBarsColor->color()); m_initializing=false; } void XYCurveDock::loadConfigFromTemplate(KConfig& config) { //extract the name of the template from the file name QString name; int index = config.name().lastIndexOf(QDir::separator()); if (index!=-1) name = config.name().right(config.name().size() - index - 1); else name = config.name(); int size = m_curvesList.size(); if (size>1) m_curve->beginMacro(i18n("%1 xy-curves: template \"%2\" loaded", size, name)); else m_curve->beginMacro(i18n("%1: template \"%2\" loaded", m_curve->name(), name)); this->loadConfig(config); m_curve->endMacro(); } void XYCurveDock::loadConfig(KConfig& config) { KConfigGroup group = config.group( "XYCurve" ); //General //we don't load/save the settings in the general-tab, since they are not style related. //It doesn't make sense to load/save them in the template. //This data is read in XYCurveDock::setCurves(). //Line ui.cbLineType->setCurrentIndex( group.readEntry("LineType", (int) m_curve->lineType()) ); ui.chkLineSkipGaps->setChecked( group.readEntry("LineSkipGaps", m_curve->lineSkipGaps()) ); ui.sbLineInterpolationPointsCount->setValue( group.readEntry("LineInterpolationPointsCount", m_curve->lineInterpolationPointsCount()) ); ui.cbLineStyle->setCurrentIndex( group.readEntry("LineStyle", (int) m_curve->linePen().style()) ); ui.kcbLineColor->setColor( group.readEntry("LineColor", m_curve->linePen().color()) ); ui.sbLineWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("LineWidth", m_curve->linePen().widthF()), Worksheet::Point) ); ui.sbLineOpacity->setValue( round(group.readEntry("LineOpacity", m_curve->lineOpacity())*100.0) ); //Drop lines ui.cbDropLineType->setCurrentIndex( group.readEntry("DropLineType", (int) m_curve->dropLineType()) ); ui.cbDropLineStyle->setCurrentIndex( group.readEntry("DropLineStyle", (int) m_curve->dropLinePen().style()) ); ui.kcbDropLineColor->setColor( group.readEntry("DropLineColor", m_curve->dropLinePen().color()) ); ui.sbDropLineWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("DropLineWidth", m_curve->dropLinePen().widthF()),Worksheet::Point) ); ui.sbDropLineOpacity->setValue( round(group.readEntry("DropLineOpacity", m_curve->dropLineOpacity())*100.0) ); //Symbols //TODO: character ui.cbSymbolStyle->setCurrentIndex( group.readEntry("SymbolStyle", (int)m_curve->symbolsStyle()) ); ui.sbSymbolSize->setValue( Worksheet::convertFromSceneUnits(group.readEntry("SymbolSize", m_curve->symbolsSize()), Worksheet::Point) ); ui.sbSymbolRotation->setValue( group.readEntry("SymbolRotation", m_curve->symbolsRotationAngle()) ); ui.sbSymbolOpacity->setValue( round(group.readEntry("SymbolOpacity", m_curve->symbolsOpacity())*100.0) ); ui.cbSymbolFillingStyle->setCurrentIndex( group.readEntry("SymbolFillingStyle", (int) m_curve->symbolsBrush().style()) ); ui.kcbSymbolFillingColor->setColor( group.readEntry("SymbolFillingColor", m_curve->symbolsBrush().color()) ); ui.cbSymbolBorderStyle->setCurrentIndex( group.readEntry("SymbolBorderStyle", (int) m_curve->symbolsPen().style()) ); ui.kcbSymbolBorderColor->setColor( group.readEntry("SymbolBorderColor", m_curve->symbolsPen().color()) ); ui.sbSymbolBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("SymbolBorderWidth",m_curve->symbolsPen().widthF()), Worksheet::Point) ); //Values ui.cbValuesType->setCurrentIndex( group.readEntry("ValuesType", (int) m_curve->valuesType()) ); ui.cbValuesPosition->setCurrentIndex( group.readEntry("ValuesPosition", (int) m_curve->valuesPosition()) ); ui.sbValuesDistance->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ValuesDistance", m_curve->valuesDistance()), Worksheet::Point) ); ui.sbValuesRotation->setValue( group.readEntry("ValuesRotation", m_curve->valuesRotationAngle()) ); ui.sbValuesOpacity->setValue( round(group.readEntry("ValuesOpacity",m_curve->valuesOpacity())*100.0) ); ui.leValuesPrefix->setText( group.readEntry("ValuesPrefix", m_curve->valuesPrefix()) ); ui.leValuesSuffix->setText( group.readEntry("ValuesSuffix", m_curve->valuesSuffix()) ); QFont valuesFont = m_curve->valuesFont(); valuesFont.setPointSizeF( round(Worksheet::convertFromSceneUnits(valuesFont.pixelSize(), Worksheet::Point)) ); ui.kfrValuesFont->setFont( group.readEntry("ValuesFont", valuesFont) ); ui.kcbValuesColor->setColor( group.readEntry("ValuesColor", m_curve->valuesColor()) ); //Filling ui.cbFillingPosition->setCurrentIndex( group.readEntry("FillingPosition", (int) m_curve->fillingPosition()) ); ui.cbFillingType->setCurrentIndex( group.readEntry("FillingType", (int) m_curve->fillingType()) ); ui.cbFillingColorStyle->setCurrentIndex( group.readEntry("FillingColorStyle", (int) m_curve->fillingColorStyle()) ); ui.cbFillingImageStyle->setCurrentIndex( group.readEntry("FillingImageStyle", (int) m_curve->fillingImageStyle()) ); ui.cbFillingBrushStyle->setCurrentIndex( group.readEntry("FillingBrushStyle", (int) m_curve->fillingBrushStyle()) ); ui.leFillingFileName->setText( group.readEntry("FillingFileName", m_curve->fillingFileName()) ); ui.kcbFillingFirstColor->setColor( group.readEntry("FillingFirstColor", m_curve->fillingFirstColor()) ); ui.kcbFillingSecondColor->setColor( group.readEntry("FillingSecondColor", m_curve->fillingSecondColor()) ); ui.sbFillingOpacity->setValue( round(group.readEntry("FillingOpacity", m_curve->fillingOpacity())*100.0) ); //Error bars ui.cbXErrorType->setCurrentIndex( group.readEntry("XErrorType", (int) m_curve->xErrorType()) ); ui.cbYErrorType->setCurrentIndex( group.readEntry("YErrorType", (int) m_curve->yErrorType()) ); ui.cbErrorBarsType->setCurrentIndex( group.readEntry("ErrorBarsType", (int) m_curve->errorBarsType()) ); ui.sbErrorBarsCapSize->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ErrorBarsCapSize", m_curve->errorBarsCapSize()), Worksheet::Point) ); ui.cbErrorBarsStyle->setCurrentIndex( group.readEntry("ErrorBarsStyle", (int) m_curve->errorBarsPen().style()) ); ui.kcbErrorBarsColor->setColor( group.readEntry("ErrorBarsColor", m_curve->errorBarsPen().color()) ); ui.sbErrorBarsWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("ErrorBarsWidth", m_curve->errorBarsPen().widthF()),Worksheet::Point) ); ui.sbErrorBarsOpacity->setValue( round(group.readEntry("ErrorBarsOpacity", m_curve->errorBarsOpacity())*100.0) ); m_initializing=true; GuiTools::updatePenStyles(ui.cbLineStyle, ui.kcbLineColor->color()); GuiTools::updatePenStyles(ui.cbDropLineStyle, ui.kcbDropLineColor->color()); GuiTools::updateBrushStyles(ui.cbSymbolFillingStyle, ui.kcbSymbolFillingColor->color()); GuiTools::updatePenStyles(ui.cbSymbolBorderStyle, ui.kcbSymbolBorderColor->color()); GuiTools::updatePenStyles(ui.cbErrorBarsStyle, ui.kcbErrorBarsColor->color()); GuiTools::updateBrushStyles(ui.cbFillingBrushStyle, ui.kcbFillingFirstColor->color()); m_initializing=false; } void XYCurveDock::saveConfigAsTemplate(KConfig& config) { KConfigGroup group = config.group( "XYCurve" ); //General //we don't load/save the settings in the general-tab, since they are not style related. //It doesn't make sense to load/save them in the template. group.writeEntry("LineType", ui.cbLineType->currentIndex()); group.writeEntry("LineSkipGaps", ui.chkLineSkipGaps->isChecked()); group.writeEntry("LineInterpolationPointsCount", ui.sbLineInterpolationPointsCount->value() ); group.writeEntry("LineStyle", ui.cbLineStyle->currentIndex()); group.writeEntry("LineColor", ui.kcbLineColor->color()); group.writeEntry("LineWidth", Worksheet::convertToSceneUnits(ui.sbLineWidth->value(),Worksheet::Point) ); group.writeEntry("LineOpacity", ui.sbLineOpacity->value()/100 ); //Drop Line group.writeEntry("DropLineType", ui.cbDropLineType->currentIndex()); group.writeEntry("DropLineStyle", ui.cbDropLineStyle->currentIndex()); group.writeEntry("DropLineColor", ui.kcbDropLineColor->color()); group.writeEntry("DropLineWidth", Worksheet::convertToSceneUnits(ui.sbDropLineWidth->value(),Worksheet::Point) ); group.writeEntry("DropLineOpacity", ui.sbDropLineOpacity->value()/100 ); //Symbol (TODO: character) group.writeEntry("SymbolStyle", ui.cbSymbolStyle->currentIndex()); group.writeEntry("SymbolSize", Worksheet::convertToSceneUnits(ui.sbSymbolSize->value(),Worksheet::Point)); group.writeEntry("SymbolRotation", ui.sbSymbolRotation->value()); group.writeEntry("SymbolOpacity", ui.sbSymbolOpacity->value()/100 ); group.writeEntry("SymbolFillingStyle", ui.cbSymbolFillingStyle->currentIndex()); group.writeEntry("SymbolFillingColor", ui.kcbSymbolFillingColor->color()); group.writeEntry("SymbolBorderStyle", ui.cbSymbolBorderStyle->currentIndex()); group.writeEntry("SymbolBorderColor", ui.kcbSymbolBorderColor->color()); group.writeEntry("SymbolBorderWidth", Worksheet::convertToSceneUnits(ui.sbSymbolBorderWidth->value(),Worksheet::Point)); //Values group.writeEntry("ValuesType", ui.cbValuesType->currentIndex()); group.writeEntry("ValuesPosition", ui.cbValuesPosition->currentIndex()); group.writeEntry("ValuesDistance", Worksheet::convertToSceneUnits(ui.sbValuesDistance->value(),Worksheet::Point)); group.writeEntry("ValuesRotation", ui.sbValuesRotation->value()); group.writeEntry("ValuesOpacity", ui.sbValuesOpacity->value()/100); group.writeEntry("ValuesPrefix", ui.leValuesPrefix->text()); group.writeEntry("ValuesSuffix", ui.leValuesSuffix->text()); group.writeEntry("ValuesFont", ui.kfrValuesFont->font()); group.writeEntry("ValuesColor", ui.kcbValuesColor->color()); //Filling group.writeEntry("FillingPosition", ui.cbFillingPosition->currentIndex()); group.writeEntry("FillingType", ui.cbFillingType->currentIndex()); group.writeEntry("FillingColorStyle", ui.cbFillingColorStyle->currentIndex()); group.writeEntry("FillingImageStyle", ui.cbFillingImageStyle->currentIndex()); group.writeEntry("FillingBrushStyle", ui.cbFillingBrushStyle->currentIndex()); group.writeEntry("FillingFileName", ui.leFillingFileName->text()); group.writeEntry("FillingFirstColor", ui.kcbFillingFirstColor->color()); group.writeEntry("FillingSecondColor", ui.kcbFillingSecondColor->color()); group.writeEntry("FillingOpacity", ui.sbFillingOpacity->value()/100.0); //Error bars group.writeEntry("XErrorType", ui.cbXErrorType->currentIndex()); group.writeEntry("YErrorType", ui.cbYErrorType->currentIndex()); group.writeEntry("ErrorBarsType", ui.cbErrorBarsType->currentIndex()); group.writeEntry("ErrorBarsCapSize", Worksheet::convertToSceneUnits(ui.sbErrorBarsCapSize->value(),Worksheet::Point) ); group.writeEntry("ErrorBarsStyle", ui.cbErrorBarsStyle->currentIndex()); group.writeEntry("ErrorBarsColor", ui.kcbErrorBarsColor->color()); group.writeEntry("ErrorBarsWidth", Worksheet::convertToSceneUnits(ui.sbErrorBarsWidth->value(),Worksheet::Point) ); group.writeEntry("ErrorBarsOpacity", ui.sbErrorBarsOpacity->value()/100 ); config.sync(); } diff --git a/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp b/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp index f5af2de0e..6985b24bf 100644 --- a/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYDataReductionCurveDock.cpp @@ -1,698 +1,698 @@ /*************************************************************************** File : XYDataReductionCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of data reduction curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYDataReductionCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include #include #include /*! \class XYDataReductionCurveDock \brief Provides a widget for editing the properties of the XYDataReductionCurves (2D-curves defined by an data reduction) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYDataReductionCurveDock::XYDataReductionCurveDock(QWidget* parent, QStatusBar* sb) : XYCurveDock(parent), statusBar(sb), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_dataReductionCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYDataReductionCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 3); for (int i=0; i < NSL_GEOM_LINESIM_TYPE_COUNT; ++i) uiGeneralTab.cbType->addItem(i18n(nsl_geom_linesim_type_name[i])); uiGeneralTab.cbType->setItemData(nsl_geom_linesim_type_visvalingam_whyatt, i18n("This method is much slower than any other"), Qt::ToolTipRole); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbTolerance->setRange(0.0, std::numeric_limits::max()); uiGeneralTab.sbTolerance2->setRange(0.0, std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYDataReductionCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYDataReductionCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.chkAuto, SIGNAL(clicked(bool)), this, SLOT(autoToleranceChanged()) ); connect( uiGeneralTab.sbTolerance, SIGNAL(valueChanged(double)), this, SLOT(toleranceChanged()) ); connect( uiGeneralTab.chkAuto2, SIGNAL(clicked(bool)), this, SLOT(autoTolerance2Changed()) ); connect( uiGeneralTab.sbTolerance2, SIGNAL(valueChanged(double)), this, SLOT(tolerance2Changed()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYDataReductionCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); } else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_dataReductionCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_dataReductionCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_dataReductionCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_dataReductionCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_dataReductionCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_dataReductionData.autoRange); uiGeneralTab.sbMin->setValue(m_dataReductionData.xRange.first()); uiGeneralTab.sbMax->setValue(m_dataReductionData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbType->setCurrentIndex(m_dataReductionData.type); this->typeChanged(); uiGeneralTab.chkAuto->setChecked(m_dataReductionData.autoTolerance); this->autoToleranceChanged(); uiGeneralTab.sbTolerance->setValue(m_dataReductionData.tolerance); this->toleranceChanged(); uiGeneralTab.chkAuto2->setChecked(m_dataReductionData.autoTolerance2); this->autoTolerance2Changed(); uiGeneralTab.sbTolerance2->setValue(m_dataReductionData.tolerance2); this->tolerance2Changed(); this->showDataReductionResult(); //enable the "recalculate"-button if the source data was changed since the last dataReduction uiGeneralTab.pbRecalculate->setEnabled(m_dataReductionCurve->isSourceDataChangedSinceLastRecalc()); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_dataReductionCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_dataReductionCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_dataReductionCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_dataReductionCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_dataReductionCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_dataReductionCurve, SIGNAL(dataReductionDataChanged(XYDataReductionCurve::DataReductionData)), this, SLOT(curveDataReductionDataChanged(XYDataReductionCurve::DataReductionData))); connect(m_dataReductionCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYDataReductionCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYDataReductionCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_dataReductionCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_dataReductionData = m_dataReductionCurve->dataReductionData(); initGeneralTab(); initTabs(); m_initializing=false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYDataReductionCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYDataReductionCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYDataReductionCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYDataReductionCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = dynamic_cast(aspect); // // disable deriv orders and accuracies that need more data points // this->updateSettings(dataSourceCurve->xColumn()); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYDataReductionCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); //TODO: this->updateSettings(column); ? if (column != 0 && uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } updateTolerance(); updateTolerance2(); } void XYDataReductionCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); updateTolerance(); updateTolerance2(); } void XYDataReductionCurveDock::updateTolerance() { const AbstractColumn* xDataColumn = nullptr; const AbstractColumn* yDataColumn = nullptr; if (m_dataReductionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { xDataColumn = m_dataReductionCurve->xDataColumn(); yDataColumn = m_dataReductionCurve->yDataColumn(); } else { if (m_dataReductionCurve->dataSourceCurve()) { xDataColumn = m_dataReductionCurve->dataSourceCurve()->xColumn(); yDataColumn = m_dataReductionCurve->dataSourceCurve()->yColumn(); } } if(xDataColumn == nullptr || yDataColumn == nullptr) return; //copy all valid data points for calculating tolerance to temporary vectors QVector xdataVector; QVector ydataVector; const double xmin = m_dataReductionData.xRange.first(); const double xmax = m_dataReductionData.xRange.last(); for (int row=0; rowrowCount(); ++row) { //only copy those data where _all_ values (for x and y, if given) are valid if (!std::isnan(xDataColumn->valueAt(row)) && !std::isnan(yDataColumn->valueAt(row)) && !xDataColumn->isMasked(row) && !yDataColumn->isMasked(row)) { // only when inside given range if (xDataColumn->valueAt(row) >= xmin && xDataColumn->valueAt(row) <= xmax) { xdataVector.append(xDataColumn->valueAt(row)); ydataVector.append(yDataColumn->valueAt(row)); } } } if(xdataVector.size() > 1) uiGeneralTab.cbType->setEnabled(true); else { uiGeneralTab.cbType->setEnabled(false); return; } DEBUG("automatic tolerance:"); DEBUG("clip_diag_perpoint =" << nsl_geom_linesim_clip_diag_perpoint(xdataVector.data(), ydataVector.data(), (size_t)xdataVector.size())); DEBUG("clip_area_perpoint =" << nsl_geom_linesim_clip_area_perpoint(xdataVector.data(), ydataVector.data(), (size_t)xdataVector.size())); DEBUG("avg_dist_perpoint =" << nsl_geom_linesim_avg_dist_perpoint(xdataVector.data(), ydataVector.data(), (size_t)xdataVector.size())); nsl_geom_linesim_type type = (nsl_geom_linesim_type)uiGeneralTab.cbType->currentIndex(); if (type == nsl_geom_linesim_type_raddist || type == nsl_geom_linesim_type_opheim) m_dataReductionData.tolerance = 10. * nsl_geom_linesim_clip_diag_perpoint(xdataVector.data(), ydataVector.data(), (size_t)xdataVector.size()); else if (type == nsl_geom_linesim_type_visvalingam_whyatt) m_dataReductionData.tolerance = 0.1 * nsl_geom_linesim_clip_area_perpoint(xdataVector.data(), ydataVector.data(), (size_t)xdataVector.size()); else if (type == nsl_geom_linesim_type_douglas_peucker_variant) m_dataReductionData.tolerance = xdataVector.size()/10.; // reduction to 10% else m_dataReductionData.tolerance = 2.*nsl_geom_linesim_avg_dist_perpoint(xdataVector.data(), ydataVector.data(), xdataVector.size()); //m_dataReductionData.tolerance = nsl_geom_linesim_clip_diag_perpoint(xdataVector.data(), ydataVector.data(), xdataVector.size()); uiGeneralTab.sbTolerance->setValue(m_dataReductionData.tolerance); } void XYDataReductionCurveDock::updateTolerance2() { nsl_geom_linesim_type type = (nsl_geom_linesim_type)uiGeneralTab.cbType->currentIndex(); if (type == nsl_geom_linesim_type_perpdist) uiGeneralTab.sbTolerance2->setValue(10); else if (type == nsl_geom_linesim_type_opheim) uiGeneralTab.sbTolerance2->setValue(5*uiGeneralTab.sbTolerance->value()); else if (type == nsl_geom_linesim_type_lang) uiGeneralTab.sbTolerance2->setValue(10); } void XYDataReductionCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_dataReductionData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_dataReductionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_dataReductionCurve->xDataColumn(); else { if (m_dataReductionCurve->dataSourceCurve()) xDataColumn = m_dataReductionCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYDataReductionCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_dataReductionData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDataReductionCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_dataReductionData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDataReductionCurveDock::typeChanged() { nsl_geom_linesim_type type = (nsl_geom_linesim_type)uiGeneralTab.cbType->currentIndex(); m_dataReductionData.type = type; switch (type) { case nsl_geom_linesim_type_douglas_peucker: case nsl_geom_linesim_type_raddist: case nsl_geom_linesim_type_interp: case nsl_geom_linesim_type_reumann_witkam: uiGeneralTab.lOption->setText(i18n("Tolerance (distance)")); uiGeneralTab.sbTolerance->setDecimals(6); uiGeneralTab.sbTolerance->setMinimum(0); uiGeneralTab.sbTolerance->setSingleStep(0.01); uiGeneralTab.lOption2->hide(); uiGeneralTab.chkAuto2->hide(); uiGeneralTab.sbTolerance2->hide(); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); break; case nsl_geom_linesim_type_douglas_peucker_variant: uiGeneralTab.lOption->setText(i18n("Number of points")); uiGeneralTab.sbTolerance->setDecimals(0); uiGeneralTab.sbTolerance->setMinimum(2); uiGeneralTab.sbTolerance->setSingleStep(1); uiGeneralTab.lOption2->hide(); uiGeneralTab.chkAuto2->hide(); uiGeneralTab.sbTolerance2->hide(); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); break; case nsl_geom_linesim_type_nthpoint: uiGeneralTab.lOption->setText(i18n("Step size")); uiGeneralTab.sbTolerance->setValue(10); uiGeneralTab.sbTolerance->setDecimals(0); uiGeneralTab.sbTolerance->setMinimum(1); uiGeneralTab.sbTolerance->setSingleStep(1); uiGeneralTab.lOption2->hide(); uiGeneralTab.chkAuto2->hide(); uiGeneralTab.sbTolerance2->hide(); break; case nsl_geom_linesim_type_perpdist: // repeat option uiGeneralTab.lOption->setText(i18n("Tolerance (distance)")); uiGeneralTab.sbTolerance->setDecimals(6); uiGeneralTab.sbTolerance->setMinimum(0); uiGeneralTab.sbTolerance->setSingleStep(0.01); uiGeneralTab.sbTolerance2->show(); uiGeneralTab.lOption2->show(); uiGeneralTab.chkAuto2->show(); uiGeneralTab.lOption2->setText(i18n("Repeats")); uiGeneralTab.sbTolerance2->setDecimals(0); uiGeneralTab.sbTolerance2->setMinimum(1); uiGeneralTab.sbTolerance2->setSingleStep(1); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); if (uiGeneralTab.chkAuto2->isChecked()) updateTolerance2(); break; case nsl_geom_linesim_type_visvalingam_whyatt: uiGeneralTab.lOption->setText(i18n("Tolerance (area)")); uiGeneralTab.sbTolerance->setDecimals(6); uiGeneralTab.sbTolerance->setMinimum(0); uiGeneralTab.sbTolerance->setSingleStep(0.01); uiGeneralTab.lOption2->hide(); uiGeneralTab.chkAuto2->hide(); uiGeneralTab.sbTolerance2->hide(); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); break; case nsl_geom_linesim_type_opheim: // min/max tol options uiGeneralTab.lOption->setText(i18n(" Min. Tolerance")); uiGeneralTab.sbTolerance->setDecimals(6); uiGeneralTab.sbTolerance->setMinimum(0); uiGeneralTab.sbTolerance->setSingleStep(0.01); uiGeneralTab.lOption2->setText(i18n("Max. Tolerance")); uiGeneralTab.lOption2->show(); uiGeneralTab.chkAuto2->show(); uiGeneralTab.sbTolerance2->show(); uiGeneralTab.sbTolerance2->setDecimals(6); uiGeneralTab.sbTolerance2->setMinimum(0); uiGeneralTab.sbTolerance2->setSingleStep(0.01); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); if (uiGeneralTab.chkAuto2->isChecked()) updateTolerance2(); break; case nsl_geom_linesim_type_lang: // distance/region uiGeneralTab.lOption->setText(i18n("Tolerance (distance)")); uiGeneralTab.sbTolerance->setDecimals(6); uiGeneralTab.sbTolerance->setMinimum(0); uiGeneralTab.sbTolerance->setSingleStep(0.01); uiGeneralTab.lOption2->setText(i18n("Search region")); uiGeneralTab.lOption2->show(); uiGeneralTab.chkAuto2->show(); uiGeneralTab.sbTolerance2->show(); uiGeneralTab.sbTolerance2->setDecimals(0); uiGeneralTab.sbTolerance2->setMinimum(1); uiGeneralTab.sbTolerance2->setSingleStep(1); if (uiGeneralTab.chkAuto->isChecked()) updateTolerance(); if (uiGeneralTab.chkAuto2->isChecked()) updateTolerance2(); break; } uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDataReductionCurveDock::autoToleranceChanged() { bool autoTolerance = (bool)uiGeneralTab.chkAuto->isChecked(); m_dataReductionData.autoTolerance = autoTolerance; if (autoTolerance) { uiGeneralTab.sbTolerance->setEnabled(false); updateTolerance(); } else uiGeneralTab.sbTolerance->setEnabled(true); } void XYDataReductionCurveDock::toleranceChanged() { m_dataReductionData.tolerance = uiGeneralTab.sbTolerance->value(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDataReductionCurveDock::autoTolerance2Changed() { bool autoTolerance2 = (bool)uiGeneralTab.chkAuto2->isChecked(); m_dataReductionData.autoTolerance2 = autoTolerance2; if (autoTolerance2) { uiGeneralTab.sbTolerance2->setEnabled(false); updateTolerance2(); } else uiGeneralTab.sbTolerance2->setEnabled(true); } void XYDataReductionCurveDock::tolerance2Changed() { m_dataReductionData.tolerance2 = uiGeneralTab.sbTolerance2->value(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDataReductionCurveDock::recalculateClicked() { //show a progress bar in the status bar QProgressBar* progressBar = new QProgressBar(); progressBar->setMinimum(0); progressBar->setMaximum(100); connect(m_curve, SIGNAL(completed(int)), progressBar, SLOT(setValue(int))); statusBar->clearMessage(); statusBar->addWidget(progressBar, 1); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataReductionData(m_dataReductionData); QApplication::restoreOverrideCursor(); statusBar->removeWidget(progressBar); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Data reduction status: ") + m_dataReductionCurve->dataReductionResult().status); } void XYDataReductionCurveDock::enableRecalculate() const { if (m_initializing) return; //no dataReductioning possible without the x- and y-data bool hasSourceData = false; if (m_dataReductionCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_dataReductionCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the dataReduction */ void XYDataReductionCurveDock::showDataReductionResult() { const XYDataReductionCurve::DataReductionResult& dataReductionResult = m_dataReductionCurve->dataReductionResult(); if (!dataReductionResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + dataReductionResult.status + "
"; + QString str = i18n("status: %1", dataReductionResult.status) + "
"; if (!dataReductionResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (dataReductionResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(dataReductionResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(dataReductionResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(dataReductionResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(dataReductionResult.elapsedTime)) + "
"; str += "
"; - str += i18n("number of points: %1").arg(QString::number(dataReductionResult.npoints)) + "
"; - str += i18n("positional squared error: %1").arg(QString::number(dataReductionResult.posError)) + "
"; - str += i18n("area error: %1").arg(QString::number(dataReductionResult.areaError)) + "
"; + str += i18n("number of points: %1", QString::number(dataReductionResult.npoints)) + "
"; + str += i18n("positional squared error: %1", QString::number(dataReductionResult.posError)) + "
"; + str += i18n("area error: %1", QString::number(dataReductionResult.areaError)) + "
"; uiGeneralTab.teResult->setText(str); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYDataReductionCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYDataReductionCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYDataReductionCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYDataReductionCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYDataReductionCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYDataReductionCurveDock::curveDataReductionDataChanged(const XYDataReductionCurve::DataReductionData& dataReductionData) { m_initializing = true; m_dataReductionData = dataReductionData; //uiGeneralTab.cbType->setCurrentIndex(m_dataReductionData.type); //this->typeChanged(); this->showDataReductionResult(); m_initializing = false; } void XYDataReductionCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp index bdf1cab83..eb5809524 100644 --- a/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYDifferentiationCurveDock.cpp @@ -1,589 +1,589 @@ /*************************************************************************** File : XYDifferentiationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of differentiation curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYDifferentiationCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include extern "C" { #include "backend/nsl/nsl_diff.h" } /*! \class XYDifferentiationCurveDock \brief Provides a widget for editing the properties of the XYDifferentiationCurves (2D-curves defined by a differentiation) currently selected in the project explorer. If more than one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYDifferentiationCurveDock::XYDifferentiationCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_differentiationCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYDifferentiationCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 3); for (int i=0; i < NSL_DIFF_DERIV_ORDER_COUNT; ++i) uiGeneralTab.cbDerivOrder->addItem(i18n(nsl_diff_deriv_order_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon( QIcon::fromTheme("run-build") ); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYDifferentiationCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYDifferentiationCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbDerivOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(derivOrderChanged()) ); connect( uiGeneralTab.sbAccOrder, SIGNAL(valueChanged(int)), this, SLOT(accOrderChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYDifferentiationCurveDock::initGeneralTab() { //if there are more than one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_differentiationCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_differentiationCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_differentiationCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_differentiationCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_differentiationCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_differentiationData.autoRange); uiGeneralTab.sbMin->setValue(m_differentiationData.xRange.first()); uiGeneralTab.sbMax->setValue(m_differentiationData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbDerivOrder->setCurrentIndex(m_differentiationData.derivOrder); this->derivOrderChanged(); uiGeneralTab.sbAccOrder->setValue(m_differentiationData.accOrder); this->accOrderChanged(); this->showDifferentiationResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_differentiationCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_differentiationCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_differentiationCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_differentiationCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_differentiationCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_differentiationCurve, SIGNAL(differentiationDataChanged(XYDifferentiationCurve::DifferentiationData)), this, SLOT(curveDifferentiationDataChanged(XYDifferentiationCurve::DifferentiationData))); connect(m_differentiationCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYDifferentiationCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYDifferentiationCurveDock::setCurves(QList list) { m_initializing = true; m_curvesList = list; m_curve = list.first(); m_differentiationCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_differentiationData = m_differentiationCurve->differentiationData(); initGeneralTab(); initTabs(); m_initializing = false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYDifferentiationCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYDifferentiationCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYDifferentiationCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYDifferentiationCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = dynamic_cast(aspect); // disable deriv orders and accuracies that need more data points this->updateSettings(dataSourceCurve->xColumn()); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYDifferentiationCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); // disable deriv orders and accuracies that need more data points this->updateSettings(column); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); } void XYDifferentiationCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } /*! * disable deriv orders and accuracies that need more data points */ void XYDifferentiationCurveDock::updateSettings(const AbstractColumn* column) { if (!column) return; if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } size_t n=0; for (int row=0; row < column->rowCount(); ++row) if (!std::isnan(column->valueAt(row)) && !column->isMasked(row)) n++; const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbDerivOrder->model()); QStandardItem* item = model->item(nsl_diff_deriv_order_first); if (n < 3) item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); else { item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); if (n < 5) uiGeneralTab.sbAccOrder->setMinimum(2); } item = model->item(nsl_diff_deriv_order_second); if (n < 3) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_second) uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first); } else { item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); if (n < 4) uiGeneralTab.sbAccOrder->setMinimum(1); else if (n < 5) uiGeneralTab.sbAccOrder->setMinimum(2); } item = model->item(nsl_diff_deriv_order_third); if (n < 5) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_third) uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_diff_deriv_order_fourth); if (n < 5) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_fourth) uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first); } else { item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); if (n < 7) uiGeneralTab.sbAccOrder->setMinimum(1); } item = model->item(nsl_diff_deriv_order_fifth); if (n < 7) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_fifth) uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_diff_deriv_order_sixth); if (n < 7) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbDerivOrder->currentIndex() == nsl_diff_deriv_order_sixth) uiGeneralTab.cbDerivOrder->setCurrentIndex(nsl_diff_deriv_order_first); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); } void XYDifferentiationCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_differentiationData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_differentiationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_differentiationCurve->xDataColumn(); else { if (m_differentiationCurve->dataSourceCurve()) xDataColumn = m_differentiationCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYDifferentiationCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_differentiationData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDifferentiationCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_differentiationData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDifferentiationCurveDock::derivOrderChanged() { const nsl_diff_deriv_order_type derivOrder = (nsl_diff_deriv_order_type)uiGeneralTab.cbDerivOrder->currentIndex(); m_differentiationData.derivOrder = derivOrder; // update avail. accuracies switch (derivOrder) { case nsl_diff_deriv_order_first: uiGeneralTab.sbAccOrder->setMinimum(2); uiGeneralTab.sbAccOrder->setMaximum(4); uiGeneralTab.sbAccOrder->setSingleStep(2); uiGeneralTab.sbAccOrder->setValue(4); break; case nsl_diff_deriv_order_second: uiGeneralTab.sbAccOrder->setMinimum(1); uiGeneralTab.sbAccOrder->setMaximum(3); uiGeneralTab.sbAccOrder->setSingleStep(1); uiGeneralTab.sbAccOrder->setValue(3); break; case nsl_diff_deriv_order_third: uiGeneralTab.sbAccOrder->setMinimum(2); uiGeneralTab.sbAccOrder->setMaximum(2); break; case nsl_diff_deriv_order_fourth: uiGeneralTab.sbAccOrder->setMinimum(1); uiGeneralTab.sbAccOrder->setMaximum(3); uiGeneralTab.sbAccOrder->setSingleStep(2); uiGeneralTab.sbAccOrder->setValue(3); break; case nsl_diff_deriv_order_fifth: uiGeneralTab.sbAccOrder->setMinimum(2); uiGeneralTab.sbAccOrder->setMaximum(2); break; case nsl_diff_deriv_order_sixth: uiGeneralTab.sbAccOrder->setMinimum(1); uiGeneralTab.sbAccOrder->setMaximum(1); break; } uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDifferentiationCurveDock::accOrderChanged() { int accOrder = (int)uiGeneralTab.sbAccOrder->value(); m_differentiationData.accOrder = accOrder; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYDifferentiationCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) if (curve != 0) dynamic_cast(curve)->setDifferentiationData(m_differentiationData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Differentiation status: ") + m_differentiationCurve->differentiationResult().status); QApplication::restoreOverrideCursor(); } void XYDifferentiationCurveDock::enableRecalculate() const { if (m_initializing) return; //no differentiation possible without the x- and y-data bool hasSourceData = false; if (m_differentiationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_differentiationCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the differentiation */ void XYDifferentiationCurveDock::showDifferentiationResult() { const XYDifferentiationCurve::DifferentiationResult& differentiationResult = m_differentiationCurve->differentiationResult(); if (!differentiationResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + differentiationResult.status + "
"; + QString str = i18n("status: %1", differentiationResult.status) + "
"; if (!differentiationResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (differentiationResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(differentiationResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(differentiationResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(differentiationResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(differentiationResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last differentiation uiGeneralTab.pbRecalculate->setEnabled(m_differentiationCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*** SLOTs for changes triggered in XYDifferentiationCurve *** //************************************************************* //General-Tab void XYDifferentiationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYDifferentiationCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYDifferentiationCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYDifferentiationCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYDifferentiationCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYDifferentiationCurveDock::curveDifferentiationDataChanged(const XYDifferentiationCurve::DifferentiationData& differentiationData) { m_initializing = true; m_differentiationData = differentiationData; uiGeneralTab.cbDerivOrder->setCurrentIndex(m_differentiationData.derivOrder); this->derivOrderChanged(); uiGeneralTab.sbAccOrder->setValue(m_differentiationData.accOrder); this->accOrderChanged(); this->showDifferentiationResult(); m_initializing = false; } void XYDifferentiationCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp index 0cedf4505..471cd32ea 100644 --- a/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYEquationCurveDock.cpp @@ -1,376 +1,378 @@ /*************************************************************************** File : XYEquationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of equation curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYEquationCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYEquationCurve.h" #include "backend/gsl/ExpressionParser.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include #include #include #include #include /*! \class XYEquationCurveDock \brief Provides a widget for editing the properties of the XYEquationCurves (2D-curves defined by a mathematical equation) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYEquationCurveDock::XYEquationCurveDock(QWidget *parent): XYCurveDock(parent), m_equationCurve(0) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYEquationCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout ) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); uiGeneralTab.tbConstants1->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); uiGeneralTab.tbFunctions1->setIcon( QIcon::fromTheme("preferences-desktop-font") ); uiGeneralTab.tbConstants2->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); uiGeneralTab.tbFunctions2->setIcon( QIcon::fromTheme("preferences-desktop-font") ); - uiGeneralTab.cbType->addItem(i18n("cartesian")); - uiGeneralTab.cbType->addItem(i18n("polar")); - uiGeneralTab.cbType->addItem(i18n("parametric")); -// uiGeneralTab.cbType->addItem(i18n("implicit")); + uiGeneralTab.cbType->addItem(i18n("Cartesian")); + uiGeneralTab.cbType->addItem(i18n("Polar")); + uiGeneralTab.cbType->addItem(i18n("Parametric")); +// uiGeneralTab.cbType->addItem(i18n("Implicit")); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); uiGeneralTab.teEquation2->setExpressionType(XYEquationCurve::Parametric); uiGeneralTab.teEquation1->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2); uiGeneralTab.teEquation2->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()*2); uiGeneralTab.teMin->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()); uiGeneralTab.teMax->setMaximumHeight(uiGeneralTab.leName->sizeHint().height()); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYEquationCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYEquationCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int)) ); connect( uiGeneralTab.teEquation1, SIGNAL(expressionChanged()), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.teEquation2, SIGNAL(expressionChanged()), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.tbConstants1, SIGNAL(clicked()), this, SLOT(showConstants()) ); connect( uiGeneralTab.tbFunctions1, SIGNAL(clicked()), this, SLOT(showFunctions()) ); connect( uiGeneralTab.tbConstants2, SIGNAL(clicked()), this, SLOT(showConstants()) ); connect( uiGeneralTab.tbFunctions2, SIGNAL(clicked()), this, SLOT(showFunctions()) ); connect( uiGeneralTab.teMin, SIGNAL(expressionChanged()), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.teMax, SIGNAL(expressionChanged()), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.sbCount, SIGNAL(valueChanged(int)), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYEquationCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_equationCurve = dynamic_cast(m_curve); Q_ASSERT(m_equationCurve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); XYCurveDock::setModel(); initGeneralTab(); initTabs(); uiGeneralTab.pbRecalculate->setEnabled(false); m_initializing=false; } void XYEquationCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1){ uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve const XYEquationCurve* equationCurve = dynamic_cast(m_curve); Q_ASSERT(equationCurve); const XYEquationCurve::EquationData& data = equationCurve->equationData(); uiGeneralTab.cbType->setCurrentIndex(data.type); this->typeChanged(data.type); uiGeneralTab.teEquation1->setText(data.expression1); uiGeneralTab.teEquation2->setText(data.expression2); uiGeneralTab.teMin->setText(data.min); uiGeneralTab.teMax->setText(data.max); uiGeneralTab.sbCount->setValue(data.count); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_equationCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_equationCurve, SIGNAL(equationDataChanged(XYEquationCurve::EquationData)), this, SLOT(curveEquationDataChanged(XYEquationCurve::EquationData))); } //************************************************************* //**** SLOTs for changes triggered in XYEquationCurveDock ***** //************************************************************* void XYEquationCurveDock::nameChanged(){ if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYEquationCurveDock::commentChanged(){ if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYEquationCurveDock::typeChanged(int index) { XYEquationCurve::EquationType type = XYEquationCurve::EquationType(index); if (type==XYEquationCurve::Cartesian) { uiGeneralTab.lEquation1->setText("y=f(x)"); uiGeneralTab.lEquation2->hide(); uiGeneralTab.teEquation2->hide(); uiGeneralTab.tbFunctions2->hide(); uiGeneralTab.tbConstants2->hide(); uiGeneralTab.lMin->show(); uiGeneralTab.lMax->show(); uiGeneralTab.teMin->show(); uiGeneralTab.teMax->show(); uiGeneralTab.lMin->setText(i18n("x, min")); uiGeneralTab.lMax->setText(i18n("x, max")); } else if (type==XYEquationCurve::Polar) { uiGeneralTab.lEquation1->setText(QString::fromUtf8("r(φ)")); uiGeneralTab.lEquation2->hide(); uiGeneralTab.teEquation2->hide(); uiGeneralTab.tbFunctions2->hide(); uiGeneralTab.tbConstants2->hide(); uiGeneralTab.lMin->show(); uiGeneralTab.lMax->show(); uiGeneralTab.teMin->show(); uiGeneralTab.teMax->show(); uiGeneralTab.lMin->setText(i18n("φ, min")); uiGeneralTab.lMax->setText(i18n("φ, max")); } else if (type==XYEquationCurve::Parametric) { uiGeneralTab.lEquation1->setText("x=f(t)"); uiGeneralTab.lEquation2->setText("y=f(t)"); uiGeneralTab.lEquation2->show(); uiGeneralTab.teEquation2->show(); uiGeneralTab.tbFunctions2->show(); uiGeneralTab.tbConstants2->show(); uiGeneralTab.lMin->show(); uiGeneralTab.lMax->show(); uiGeneralTab.teMin->show(); uiGeneralTab.teMax->show(); uiGeneralTab.lMin->setText(i18n("t, min")); uiGeneralTab.lMax->setText(i18n("t, max")); } else if (type==XYEquationCurve::Implicit) { uiGeneralTab.lEquation1->setText("f(x,y)"); uiGeneralTab.lEquation2->hide(); uiGeneralTab.teEquation2->hide(); uiGeneralTab.tbFunctions2->hide(); uiGeneralTab.tbConstants2->hide(); uiGeneralTab.lMin->hide(); uiGeneralTab.lMax->hide(); uiGeneralTab.teMin->hide(); uiGeneralTab.teMax->hide(); } uiGeneralTab.teEquation1->setExpressionType(type); this->enableRecalculate(); } void XYEquationCurveDock::recalculateClicked() { XYEquationCurve::EquationData data; data.type = (XYEquationCurve::EquationType)uiGeneralTab.cbType->currentIndex(); data.expression1 = uiGeneralTab.teEquation1->document()->toPlainText(); data.expression2 = uiGeneralTab.teEquation2->document()->toPlainText(); data.min = uiGeneralTab.teMin->document()->toPlainText(); data.max = uiGeneralTab.teMax->document()->toPlainText(); data.count = uiGeneralTab.sbCount->value(); for (auto* curve : m_curvesList) dynamic_cast(curve)->setEquationData(data); uiGeneralTab.pbRecalculate->setEnabled(false); } void XYEquationCurveDock::showConstants() { QMenu menu; ConstantsWidget constants(&menu); if (QObject::sender()==uiGeneralTab.tbConstants1) connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant1(QString))); else connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant2(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); if (QObject::sender()==uiGeneralTab.tbConstants1) { QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbConstants1->width(),-menu.sizeHint().height()); menu.exec(uiGeneralTab.tbConstants1->mapToGlobal(pos)); } else { QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbConstants2->width(),-menu.sizeHint().height()); menu.exec(uiGeneralTab.tbConstants2->mapToGlobal(pos)); } } void XYEquationCurveDock::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); if (QObject::sender()==uiGeneralTab.tbFunctions1) connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction1(QString))); else connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction2(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); if (QObject::sender()==uiGeneralTab.tbFunctions1) { QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbFunctions1->width(),-menu.sizeHint().height()); menu.exec(uiGeneralTab.tbFunctions1->mapToGlobal(pos)); } else { QPoint pos(-menu.sizeHint().width()+uiGeneralTab.tbFunctions2->width(),-menu.sizeHint().height()); menu.exec(uiGeneralTab.tbFunctions2->mapToGlobal(pos)); } } void XYEquationCurveDock::insertFunction1(const QString& str) { + //TODO: not all functions have only one argument XYEquationCurve::EquationType type = XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex()); if (type==XYEquationCurve::Cartesian) uiGeneralTab.teEquation1->insertPlainText(str + "(x)"); else if (type==XYEquationCurve::Polar) uiGeneralTab.teEquation1->insertPlainText(str + "(phi)"); else if (type==XYEquationCurve::Parametric) uiGeneralTab.teEquation1->insertPlainText(str + "(t)"); } void XYEquationCurveDock::insertConstant1(const QString& str) { uiGeneralTab.teEquation1->insertPlainText(str); } void XYEquationCurveDock::insertFunction2(const QString& str) { + //TODO: not all functions have only one argument uiGeneralTab.teEquation2->insertPlainText(str + "(t)"); } void XYEquationCurveDock::insertConstant2(const QString& str) { uiGeneralTab.teEquation2->insertPlainText(str); } void XYEquationCurveDock::enableRecalculate() const { if (m_initializing) return; //check whether the formular expressions are correct bool valid = false; XYEquationCurve::EquationType type = XYEquationCurve::EquationType(uiGeneralTab.cbType->currentIndex()); if (type!=XYEquationCurve::Parametric) valid = uiGeneralTab.teEquation1->isValid(); else valid = (uiGeneralTab.teEquation1->isValid() && uiGeneralTab.teEquation2->isValid()); valid = (valid && uiGeneralTab.teMin->isValid() && uiGeneralTab.teMax->isValid()); uiGeneralTab.pbRecalculate->setEnabled(valid); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYEquationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) { uiGeneralTab.leName->setText(aspect->name()); } else if (aspect->comment() != uiGeneralTab.leComment->text()) { uiGeneralTab.leComment->setText(aspect->comment()); } m_initializing = false; } void XYEquationCurveDock::curveEquationDataChanged(const XYEquationCurve::EquationData& data) { m_initializing = true; uiGeneralTab.cbType->setCurrentIndex(data.type); uiGeneralTab.teEquation1->setText(data.expression1); uiGeneralTab.teEquation2->setText(data.expression2); uiGeneralTab.teMin->setText(data.min); uiGeneralTab.teMax->setText(data.max); uiGeneralTab.sbCount->setValue(data.count); m_initializing = false; } diff --git a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp index 80c265bd2..8e3427617 100644 --- a/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFitCurveDock.cpp @@ -1,1215 +1,1217 @@ /*************************************************************************** File : XYFitCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of fit curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYFitCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/lib/macros.h" #include "backend/gsl/ExpressionParser.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include "kdefrontend/widgets/FitOptionsWidget.h" #include "kdefrontend/widgets/FitParametersWidget.h" #include #include #include #include #include #include extern "C" { #include "backend/nsl/nsl_sf_stats.h" } /*! \class XYFitCurveDock \brief Provides a widget for editing the properties of the XYFitCurves (2D-curves defined by a fit model) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFitCurveDock::XYFitCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(0), cbXDataColumn(0), cbYDataColumn(0), cbXErrorColumn(0), cbYErrorColumn(0), m_fitCurve(0) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYFitCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = qobject_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2, 2, 2, 2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 6, 4, 1, 4); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 7, 4, 1, 4); cbXErrorColumn = new TreeViewComboBox(generalTab); cbXErrorColumn->setEnabled(false); uiGeneralTab.hlXError->addWidget(cbXErrorColumn); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 8, 4, 1, 4); cbYErrorColumn = new TreeViewComboBox(generalTab); cbYErrorColumn->setEnabled(false); uiGeneralTab.hlYWeight->addWidget(cbYErrorColumn); // X/Y-Weight for(int i = 0; i < NSL_FIT_WEIGHT_TYPE_COUNT; i++) { uiGeneralTab.cbXWeight->addItem(nsl_fit_weight_type_name[i]); uiGeneralTab.cbYWeight->addItem(nsl_fit_weight_type_name[i]); } uiGeneralTab.cbXWeight->setCurrentIndex(nsl_fit_weight_no); uiGeneralTab.cbYWeight->setCurrentIndex(nsl_fit_weight_no); for(int i = 0; i < NSL_FIT_MODEL_CATEGORY_COUNT; i++) uiGeneralTab.cbCategory->addItem(nsl_fit_model_category_name[i]); //show the fit-model category for the currently selected default (first) fit-model category categoryChanged(uiGeneralTab.cbCategory->currentIndex()); uiGeneralTab.teEquation->setMaximumHeight(uiGeneralTab.leName->sizeHint().height() * 2); //use white background in the preview label QPalette p; p.setColor(QPalette::Window, Qt::white); uiGeneralTab.lFuncPic->setAutoFillBackground(true); uiGeneralTab.lFuncPic->setPalette(p); uiGeneralTab.tbConstants->setIcon(QIcon::fromTheme("labplot-format-text-symbol")); uiGeneralTab.tbFunctions->setIcon(QIcon::fromTheme("preferences-desktop-font")); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); uiGeneralTab.twLog->setEditTriggers(QAbstractItemView::NoEditTriggers); uiGeneralTab.twParameters->setEditTriggers(QAbstractItemView::NoEditTriggers); uiGeneralTab.twGoodness->setEditTriggers(QAbstractItemView::NoEditTriggers); //don't allow word wrapping in the log-table for the multi-line iterations string uiGeneralTab.twLog->setWordWrap(false); // context menus uiGeneralTab.twParameters->setContextMenuPolicy(Qt::CustomContextMenu); uiGeneralTab.twGoodness->setContextMenuPolicy(Qt::CustomContextMenu); uiGeneralTab.twLog->setContextMenuPolicy(Qt::CustomContextMenu); connect(uiGeneralTab.twParameters, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultParametersContextMenuRequest(QPoint)) ); connect(uiGeneralTab.twGoodness, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultGoodnessContextMenuRequest(QPoint)) ); connect(uiGeneralTab.twLog, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(resultLogContextMenuRequest(QPoint)) ); uiGeneralTab.twLog->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); uiGeneralTab.twGoodness->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); uiGeneralTab.twGoodness->item(0, 1)->setText(UTF8_QSTRING("χ²")); uiGeneralTab.twGoodness->item(1, 1)->setText(i18n("reduced") + ' ' + UTF8_QSTRING("χ²") + " (" + UTF8_QSTRING("χ²") + "/dof)"); uiGeneralTab.twGoodness->item(3, 1)->setText(UTF8_QSTRING("R²")); uiGeneralTab.twGoodness->item(4, 1)->setText(UTF8_QSTRING("R̄²")); uiGeneralTab.twGoodness->item(5, 0)->setText(UTF8_QSTRING("χ²") + ' ' + i18n("test")); uiGeneralTab.twGoodness->item(5, 1)->setText("P > " + UTF8_QSTRING("χ²")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect(uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFitCurveDock::nameChanged); connect(uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFitCurveDock::commentChanged); connect(uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool))); connect(uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int))); connect(uiGeneralTab.cbXWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(xWeightChanged(int))); connect(uiGeneralTab.cbYWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(yWeightChanged(int))); connect(uiGeneralTab.cbCategory, SIGNAL(currentIndexChanged(int)), this, SLOT(categoryChanged(int))); connect(uiGeneralTab.cbModel, SIGNAL(currentIndexChanged(int)), this, SLOT(modelTypeChanged(int))); connect(uiGeneralTab.sbDegree, SIGNAL(valueChanged(int)), this, SLOT(updateModelEquation())); connect(uiGeneralTab.teEquation, SIGNAL(expressionChanged()), this, SLOT(expressionChanged())); connect(uiGeneralTab.tbConstants, SIGNAL(clicked()), this, SLOT(showConstants())); connect(uiGeneralTab.tbFunctions, SIGNAL(clicked()), this, SLOT(showFunctions())); connect(uiGeneralTab.pbParameters, SIGNAL(clicked()), this, SLOT(showParameters())); connect(uiGeneralTab.pbOptions, SIGNAL(clicked()), this, SLOT(showOptions())); connect(uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked())); connect(cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex))); connect(cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex))); connect(cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex))); connect(cbXErrorColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xErrorColumnChanged(QModelIndex))); connect(cbYErrorColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yErrorColumnChanged(QModelIndex))); } void XYFitCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size() == 1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); } else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_fitCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_fitCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_fitCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_fitCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_fitCurve->yDataColumn()); XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, m_fitCurve->xErrorColumn()); XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, m_fitCurve->yErrorColumn()); int tmpModelType = m_fitData.modelType; // save type because it's reset when category changes if (m_fitData.modelCategory == nsl_fit_model_custom) uiGeneralTab.cbCategory->setCurrentIndex(uiGeneralTab.cbCategory->count() - 1); else uiGeneralTab.cbCategory->setCurrentIndex(m_fitData.modelCategory); m_fitData.modelType = tmpModelType; if (m_fitData.modelCategory != nsl_fit_model_custom) uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType); uiGeneralTab.cbXWeight->setCurrentIndex(m_fitData.xWeightsType); uiGeneralTab.cbYWeight->setCurrentIndex(m_fitData.yWeightsType); uiGeneralTab.sbDegree->setValue(m_fitData.degree); updateModelEquation(); this->showFitResult(); uiGeneralTab.chkVisible->setChecked(m_curve->isVisible()); //Slots connect(m_fitCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_fitCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_fitCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_fitCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(xErrorColumnChanged(const AbstractColumn*)), this, SLOT(curveXErrorColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(yErrorColumnChanged(const AbstractColumn*)), this, SLOT(curveYErrorColumnChanged(const AbstractColumn*))); connect(m_fitCurve, SIGNAL(fitDataChanged(XYFitCurve::FitData)), this, SLOT(curveFitDataChanged(XYFitCurve::FitData))); connect(m_fitCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYFitCurveDock::setModel() { QList list; list << "Folder" << "Datapicker" << "Worksheet" << "CartesianPlot" << "XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list << "Folder" << "Workbook" << "Spreadsheet" << "FileDataSource" << "Column" << "CantorWorksheet" << "Datapicker"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXErrorColumn->setTopLevelClasses(list); cbYErrorColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); cbXErrorColumn->setModel(m_aspectTreeModel); cbYErrorColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFitCurveDock::setCurves(QList list) { DEBUG("XYFitCurveDock::setCurves()"); m_initializing = true; m_curvesList = list; m_curve = list.first(); m_fitCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_fitData = m_fitCurve->fitData(); initGeneralTab(); initTabs(); m_initializing = false; // enable/disable "recalculate" enableRecalculate(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFitCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFitCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFitCurveDock::dataSourceTypeChanged(int index) { const XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYFitCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYFitCurveDock::xDataColumnChanged(const QModelIndex& index) { DEBUG("XYFitCurveDock::xDataColumnChanged()"); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); } void XYFitCurveDock::yDataColumnChanged(const QModelIndex& index) { DEBUG("XYFitCurveDock::yDataColumnChanged()"); if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); // set model dependent start values from new data XYFitCurve::initStartValues(m_fitData, m_curve); } void XYFitCurveDock::xErrorColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXErrorColumn(column); } void XYFitCurveDock::yErrorColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYErrorColumn(column); } void XYFitCurveDock::xWeightChanged(int index) { DEBUG("xWeightChanged() weight = " << nsl_fit_weight_type_name[index]); m_fitData.xWeightsType = (nsl_fit_weight_type)index; // enable/disable weight column switch ((nsl_fit_weight_type)index) { case nsl_fit_weight_no: case nsl_fit_weight_statistical: case nsl_fit_weight_statistical_fit: case nsl_fit_weight_relative: case nsl_fit_weight_relative_fit: cbXErrorColumn->setEnabled(false); uiGeneralTab.lXErrorCol->setEnabled(false); break; case nsl_fit_weight_instrumental: case nsl_fit_weight_direct: case nsl_fit_weight_inverse: cbXErrorColumn->setEnabled(true); uiGeneralTab.lXErrorCol->setEnabled(true); break; } enableRecalculate(); } void XYFitCurveDock::yWeightChanged(int index) { DEBUG("yWeightChanged() weight = " << nsl_fit_weight_type_name[index]); m_fitData.yWeightsType = (nsl_fit_weight_type)index; // enable/disable weight column switch ((nsl_fit_weight_type)index) { case nsl_fit_weight_no: case nsl_fit_weight_statistical: case nsl_fit_weight_statistical_fit: case nsl_fit_weight_relative: case nsl_fit_weight_relative_fit: cbYErrorColumn->setEnabled(false); uiGeneralTab.lYErrorCol->setEnabled(false); break; case nsl_fit_weight_instrumental: case nsl_fit_weight_direct: case nsl_fit_weight_inverse: cbYErrorColumn->setEnabled(true); uiGeneralTab.lYErrorCol->setEnabled(true); break; } enableRecalculate(); } /*! * called when the fit model category (basic functions, peak functions etc.) was changed. * In the combobox for the model type shows the model types for the current category \index and calls \c modelTypeChanged() * to update the model type dependent widgets in the general-tab. */ void XYFitCurveDock::categoryChanged(int index) { if (index == nsl_fit_model_custom) { DEBUG("categoryChanged() category = \"nsl_fit_model_custom\""); } else { DEBUG("categoryChanged() category = \"" << nsl_fit_model_category_name[index] << "\""); } if (uiGeneralTab.cbCategory->currentIndex() == uiGeneralTab.cbCategory->count() - 1) m_fitData.modelCategory = nsl_fit_model_custom; else m_fitData.modelCategory = (nsl_fit_model_category)index; m_initializing = true; uiGeneralTab.cbModel->clear(); uiGeneralTab.cbModel->show(); uiGeneralTab.lModel->show(); switch (m_fitData.modelCategory) { case nsl_fit_model_basic: for(int i = 0; i < NSL_FIT_MODEL_BASIC_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_basic_name[i]); break; case nsl_fit_model_peak: { for(int i = 0; i < NSL_FIT_MODEL_PEAK_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_peak_name[i]); #if defined(_MSC_VER) // disable voigt model const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbModel->model()); QStandardItem* item = model->item(nsl_fit_model_voigt); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); #endif break; } case nsl_fit_model_growth: for(int i = 0; i < NSL_FIT_MODEL_GROWTH_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_fit_model_growth_name[i]); break; case nsl_fit_model_distribution: { for(int i = 0; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) uiGeneralTab.cbModel->addItem(nsl_sf_stats_distribution_name[i]); // not-used items are disabled here const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbModel->model()); for(int i = 1; i < NSL_SF_STATS_DISTRIBUTION_COUNT; i++) { // unused distributions if (i == nsl_sf_stats_levy_alpha_stable || i == nsl_sf_stats_levy_skew_alpha_stable || i == nsl_sf_stats_bernoulli) { QStandardItem* item = model->item(i); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } } break; } case nsl_fit_model_custom: uiGeneralTab.cbModel->addItem(i18n("Custom")); uiGeneralTab.cbModel->hide(); uiGeneralTab.lModel->hide(); } //show the fit-model for the currently selected default (first) fit-model m_fitData.modelType = 0; uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType); modelTypeChanged(m_fitData.modelType); m_initializing = false; enableRecalculate(); } /*! * called when the fit model type (polynomial, power, etc.) was changed. * Updates the model type dependent widgets in the general-tab and calls \c updateModelEquation() to update the preview pixmap. */ void XYFitCurveDock::modelTypeChanged(int index) { DEBUG("modelTypeChanged() type = " << index << ", initializing = " << m_initializing); // leave if there is no selection if(index == -1) return; unsigned int type = 0; bool custom = false; if (m_fitData.modelCategory == nsl_fit_model_custom) custom = true; else type = (unsigned int)index; m_fitData.modelType = type; uiGeneralTab.teEquation->setReadOnly(!custom); uiGeneralTab.tbFunctions->setVisible(custom); uiGeneralTab.tbConstants->setVisible(custom); // default settings uiGeneralTab.lDegree->setText(i18n("Degree")); uiGeneralTab.sbDegree->setValue(1); // TODO: reset start values switch (m_fitData.modelCategory) { case nsl_fit_model_basic: switch (type) { case nsl_fit_model_polynomial: case nsl_fit_model_fourier: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(10); uiGeneralTab.sbDegree->setValue(1); break; case nsl_fit_model_power: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(2); uiGeneralTab.sbDegree->setValue(1); break; case nsl_fit_model_exponential: uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(10); uiGeneralTab.sbDegree->setValue(1); break; default: uiGeneralTab.lDegree->setVisible(false); uiGeneralTab.sbDegree->setVisible(false); } break; case nsl_fit_model_peak: // all models support multiple peaks uiGeneralTab.lDegree->setText(i18n("Number of peaks")); uiGeneralTab.lDegree->setVisible(true); uiGeneralTab.sbDegree->setVisible(true); uiGeneralTab.sbDegree->setMaximum(9); uiGeneralTab.sbDegree->setValue(1); break; case nsl_fit_model_growth: case nsl_fit_model_distribution: case nsl_fit_model_custom: uiGeneralTab.lDegree->setVisible(false); uiGeneralTab.sbDegree->setVisible(false); } this->updateModelEquation(); } /*! * Show the preview pixmap of the fit model expression for the current model category and type. * Called when the model type or the degree of the model were changed. */ void XYFitCurveDock::updateModelEquation() { DEBUG("XYFitCurveDock::updateModelEquation()"); if (m_fitData.modelCategory == nsl_fit_model_custom) { DEBUG("XYFitCurveDock::updateModelEquation() category = nsl_fit_model_custom, type = " << m_fitData.modelType); } else { DEBUG("XYFitCurveDock::updateModelEquation() category = " << nsl_fit_model_category_name[m_fitData.modelCategory] << ", type = " << m_fitData.modelType); } //this function can also be called when the value for the degree was changed -> update the fit data structure int degree = uiGeneralTab.sbDegree->value(); m_fitData.degree = degree; XYFitCurve::initFitData(m_fitData); // set model dependent start values from curve data XYFitCurve::initStartValues(m_fitData, m_curve); // variables/parameter that are known QStringList vars = {"x"}; vars << m_fitData.paramNames; uiGeneralTab.teEquation->setVariables(vars); // set formula picture uiGeneralTab.lEquation->setText(QLatin1String("f(x) =")); QString file; switch (m_fitData.modelCategory) { case nsl_fit_model_basic: { // formula pic depends on degree QString numSuffix = QString::number(degree); if (degree > 4) numSuffix = '4'; if ((nsl_fit_model_type_basic)m_fitData.modelType == nsl_fit_model_power && degree > 2) numSuffix = '2'; file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_basic_pic_name[m_fitData.modelType]) + numSuffix + ".png"); break; } case nsl_fit_model_peak: { // formula pic depends on number of peaks QString numSuffix = QString::number(degree); if (degree > 4) numSuffix = '4'; file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_peak_pic_name[m_fitData.modelType]) + numSuffix + ".png"); break; } case nsl_fit_model_growth: file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/fit_models/" + QString(nsl_fit_model_growth_pic_name[m_fitData.modelType]) + ".png"); break; case nsl_fit_model_distribution: file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/gsl_distributions/" + QString(nsl_sf_stats_distribution_pic_name[m_fitData.modelType]) + ".png"); // change label if (m_fitData.modelType == nsl_sf_stats_poisson) uiGeneralTab.lEquation->setText(QLatin1String("f(k)/A =")); else uiGeneralTab.lEquation->setText(QLatin1String("f(x)/A =")); break; case nsl_fit_model_custom: uiGeneralTab.teEquation->show(); uiGeneralTab.teEquation->clear(); uiGeneralTab.teEquation->insertPlainText(m_fitData.model); uiGeneralTab.lFuncPic->hide(); } if (m_fitData.modelCategory != nsl_fit_model_custom) { DEBUG("Model pixmap path = " << file.toStdString()); uiGeneralTab.lFuncPic->setPixmap(file); uiGeneralTab.lFuncPic->show(); uiGeneralTab.teEquation->hide(); } enableRecalculate(); } void XYFitCurveDock::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbConstants->width(), -menu.sizeHint().height()); menu.exec(uiGeneralTab.tbConstants->mapToGlobal(pos)); } void XYFitCurveDock::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.tbFunctions->width(), -menu.sizeHint().height()); menu.exec(uiGeneralTab.tbFunctions->mapToGlobal(pos)); } /*! * Update parameter by parsing expression * Only called for custom fit model */ void XYFitCurveDock::updateParameterList() { DEBUG("XYFitCurveDock::updateParameterList()"); // use current model function m_fitData.model = uiGeneralTab.teEquation->toPlainText(); ExpressionParser* parser = ExpressionParser::getInstance(); QStringList vars; // variables that are known vars << "x"; //TODO: others? m_fitData.paramNames = m_fitData.paramNamesUtf8 = parser->getParameter(m_fitData.model, vars); // if number of parameter changed int oldNumberOfParameter = m_fitData.paramStartValues.size(); int numberOfParameter = m_fitData.paramNames.size(); DEBUG(" old number of parameter: " << oldNumberOfParameter << " new number of parameter: " << numberOfParameter); if (numberOfParameter != oldNumberOfParameter) { m_fitData.paramStartValues.resize(numberOfParameter); m_fitData.paramFixed.resize(numberOfParameter); m_fitData.paramLowerLimits.resize(numberOfParameter); m_fitData.paramUpperLimits.resize(numberOfParameter); } if (numberOfParameter > oldNumberOfParameter) { for (int i = oldNumberOfParameter; i < numberOfParameter; ++i) { m_fitData.paramStartValues[i] = 1.0; m_fitData.paramFixed[i] = false; m_fitData.paramLowerLimits[i] = -std::numeric_limits::max(); m_fitData.paramUpperLimits[i] = std::numeric_limits::max(); } } parametersChanged(); } /*! * open parameter dialog to change parameter settings */ void XYFitCurveDock::showParameters() { DEBUG("XYFitCurveDock::showParameters()"); if (m_fitData.modelCategory == nsl_fit_model_custom) updateParameterList(); QMenu menu; FitParametersWidget w(&menu, &m_fitData); connect(&w, SIGNAL(finished()), &menu, SLOT(close())); connect(&w, SIGNAL(parametersChanged()), this, SLOT(parametersChanged())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&w); menu.addAction(widgetAction); menu.setMinimumWidth(w.width()); menu.setTearOffEnabled(true); //menu.setWindowFlags(menu.windowFlags() & Qt::MSWindowsFixedSizeDialogHint); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.pbParameters->width(), 0); menu.exec(uiGeneralTab.pbParameters->mapToGlobal(pos)); } /*! * called when parameter names and/or start values for the model were changed * also called from parameter widget */ void XYFitCurveDock::parametersChanged() { DEBUG("XYFitCurveDock::parametersChanged() m_initializing = " << m_initializing); //parameter names were (probably) changed -> set the new names in EquationTextEdit uiGeneralTab.teEquation->setVariables(m_fitData.paramNames); if (m_initializing) return; enableRecalculate(); } void XYFitCurveDock::showOptions() { QMenu menu; FitOptionsWidget w(&menu, &m_fitData, m_fitCurve); connect(&w, SIGNAL(finished()), &menu, SLOT(close())); connect(&w, SIGNAL(optionsChanged()), this, SLOT(enableRecalculate())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&w); menu.addAction(widgetAction); menu.setTearOffEnabled(true); //menu.setWindowFlags(menu.windowFlags() & Qt::MSWindowsFixedSizeDialogHint); QPoint pos(-menu.sizeHint().width() + uiGeneralTab.pbParameters->width(), 0); menu.exec(uiGeneralTab.pbOptions->mapToGlobal(pos)); } void XYFitCurveDock::insertFunction(const QString& str) const { //TODO: not all function have only one argument! uiGeneralTab.teEquation->insertPlainText(str + "(x)"); } void XYFitCurveDock::insertConstant(const QString& str) const { uiGeneralTab.teEquation->insertPlainText(str); } void XYFitCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_fitData.degree = uiGeneralTab.sbDegree->value(); if (m_fitData.modelCategory == nsl_fit_model_custom) updateParameterList(); for (XYCurve* curve: m_curvesList) dynamic_cast(curve)->setFitData(m_fitData); this->showFitResult(); uiGeneralTab.pbRecalculate->setEnabled(false); - emit info(i18n("Fit status: ") + m_fitCurve->fitResult().status); + emit info(i18n("Fit status: %1", m_fitCurve->fitResult().status)); QApplication::restoreOverrideCursor(); } void XYFitCurveDock::expressionChanged() { DEBUG("XYFitCurveDock::expressionChanged()"); if (m_initializing) return; // update parameter list for custom model if (m_fitData.modelCategory == nsl_fit_model_custom) updateParameterList(); enableRecalculate(); } void XYFitCurveDock::enableRecalculate() const { DEBUG("XYFitCurveDock::enableRecalculate()"); if (m_initializing) { DEBUG(" initializing"); } if (m_initializing || m_fitCurve == nullptr) return; //no fitting possible without the x- and y-data bool hasSourceData = false; if (m_fitCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX != 0 && aspectY != 0); } else { hasSourceData = (m_fitCurve->dataSourceCurve() != nullptr); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); if (hasSourceData) { DEBUG(" enable and preview"); // PREVIEW as soon as recalculate is enabled - m_fitCurve->evaluate(true); + //TODO: this breaks loading a project with fit curve + // (sets starting values again!) + //m_fitCurve->evaluate(true); //TODO: Test (breaks context menu fit) // m_fitCurve->dataChanged(); } else { DEBUG(" disable"); } } void XYFitCurveDock::resultCopySelection() { QTableWidget* tw{nullptr}; int currentTab = uiGeneralTab.twResults->currentIndex(); DEBUG("current tab = " << currentTab); if (currentTab == 0) tw = uiGeneralTab.twParameters; else if (currentTab == 1) tw = uiGeneralTab.twGoodness; else if (currentTab == 2) tw = uiGeneralTab.twLog; else return; const QTableWidgetSelectionRange& range = tw->selectedRanges().constFirst(); QString str; for (int i = 0; i < range.rowCount(); ++i) { if (i > 0) str += '\n'; for (int j = 0; j < range.columnCount(); ++j) { if (j > 0) str += '\t'; str += tw->item(range.topRow() + i, range.leftColumn() + j)->text(); } } str += '\n'; QApplication::clipboard()->setText(str); DEBUG(QApplication::clipboard()->text().toStdString()); } void XYFitCurveDock::resultCopyAll() { const XYFitCurve::FitResult& fitResult = m_fitCurve->fitResult(); int currentTab = uiGeneralTab.twResults->currentIndex(); QString str; if (currentTab == 0) { str = i18n("Parameters:") + '\n'; const int np = fitResult.paramValues.size(); for (int i = 0; i < np; i++) { if (m_fitData.paramFixed.at(i)) str += m_fitData.paramNamesUtf8.at(i) + QString(" = ") + QString::number(fitResult.paramValues.at(i)) + '\n'; else { str += m_fitData.paramNamesUtf8.at(i) + QString(" = ") + QString::number(fitResult.paramValues.at(i)) + UTF8_QSTRING("±") + QString::number(fitResult.errorValues.at(i)) + " (" + QString::number(100.*fitResult.errorValues.at(i)/std::abs(fitResult.paramValues.at(i)), 'g', 3) + " %)\n"; const double margin = fitResult.tdist_marginValues.at(i); QString tdistValueString; if (fitResult.tdist_tValues.at(i) < std::numeric_limits::max()) tdistValueString = QString::number(fitResult.tdist_tValues.at(i), 'g', 3); else tdistValueString = UTF8_QSTRING("∞"); str += " (" + i18n("t statistic:") + ' ' + tdistValueString + ", " + i18n("p value:") + ' ' + QString::number(fitResult.tdist_pValues.at(i), 'g', 3) + ", " + i18n("conf. interval:") + ' '; if (std::abs(fitResult.tdist_tValues.at(i)) < 1.e6) { str += QString::number(fitResult.paramValues.at(i) - margin) + " .. " + QString::number(fitResult.paramValues.at(i) + margin) + ")\n"; } else { str += i18n("too small"); } } } } else if (currentTab == 1) { str = i18n("Goodness of fit:") + '\n'; str += i18n("sum of squared residuals") + " (" + UTF8_QSTRING("χ²") + "): " + QString::number(fitResult.sse) + '\n'; if (fitResult.dof != 0) { str += i18n("reduced") + ' ' + UTF8_QSTRING("χ²") + ": " + QString::number(fitResult.rms) + '\n'; str += i18n("root mean square error") + " (RMSE): " + QString::number(fitResult.rsd) + '\n'; str += i18n("coefficient of determination") + " (" + UTF8_QSTRING("R²") + "): " + QString::number(fitResult.rsquare, 'g', 15) + '\n'; str += i18n("adj. coefficient of determination")+ " (" + UTF8_QSTRING("R̄²") + "): " + QString::number(fitResult.rsquareAdj, 'g', 15) + "\n\n"; str += i18n("P > ") + UTF8_QSTRING("χ²") + ": " + QString::number(fitResult.chisq_p, 'g', 3) + '\n'; str += i18n("F statistic") + ": " + QString::number(fitResult.fdist_F, 'g', 3) + '\n'; str += i18n("P > F") + ": " + QString::number(fitResult.fdist_p, 'g', 3) + '\n'; } str += i18n("mean absolute error:") + ' ' + QString::number(fitResult.mae) + '\n'; str += i18n("Akaike information criterion:") + ' ' + QString::number(fitResult.aic) + '\n'; str += i18n("Bayesian information criterion:") + ' ' + QString::number(fitResult.bic) + '\n'; } else if (currentTab == 2) { str = i18n("status:") + ' ' + fitResult.status + '\n'; str += i18n("iterations:") + ' ' + QString::number(fitResult.iterations) + '\n'; str += i18n("tolerance:") + ' ' + QString::number(m_fitData.eps) + '\n'; if (fitResult.elapsedTime > 1000) str += i18n("calculation time: %1 s", fitResult.elapsedTime/1000) + '\n'; else str += i18n("calculation time: %1 ms", fitResult.elapsedTime) + '\n'; str += i18n("degrees of freedom:") + ' ' + QString::number(fitResult.dof) + '\n'; str += i18n("number of parameters:") + ' ' + QString::number(fitResult.paramValues.size()) + '\n'; str += i18n("fit range:") + ' ' + QString::number(m_fitData.fitRange.first()) + " .. " + QString::number(m_fitData.fitRange.last()) + '\n'; str += i18n("Iterations:") + '\n'; for (const auto &s: m_fitData.paramNamesUtf8) str += s + '\t'; str += UTF8_QSTRING("χ²"); const QStringList iterations = fitResult.solverOutput.split(';'); for (const auto &s: iterations) if (!s.isEmpty()) str += '\n' + s; } QApplication::clipboard()->setText(str); DEBUG(QApplication::clipboard()->text().toStdString()); } void XYFitCurveDock::resultParametersContextMenuRequest(QPoint pos) { QMenu *contextMenu = new QMenu; contextMenu->addAction("Copy selection", this, SLOT(resultCopySelection())); contextMenu->addAction("Copy all", this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twParameters->mapToGlobal(pos)); } void XYFitCurveDock::resultGoodnessContextMenuRequest(QPoint pos) { QMenu *contextMenu = new QMenu; contextMenu->addAction("Copy selection", this, SLOT(resultCopySelection())); contextMenu->addAction("Copy all", this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twGoodness->mapToGlobal(pos)); } void XYFitCurveDock::resultLogContextMenuRequest(QPoint pos) { QMenu *contextMenu = new QMenu; contextMenu->addAction("Copy selection", this, SLOT(resultCopySelection())); contextMenu->addAction("Copy all", this, SLOT(resultCopyAll())); contextMenu->exec(uiGeneralTab.twLog->mapToGlobal(pos)); } /*! * show the result and details of fit */ void XYFitCurveDock::showFitResult() { DEBUG("XYFitCurveDock::showFitResult()"); //clear the previous result uiGeneralTab.twParameters->setRowCount(0); for (int row = 0; row < uiGeneralTab.twGoodness->rowCount(); ++row) uiGeneralTab.twGoodness->item(row, 2)->setText(""); for (int row = 0; row < uiGeneralTab.twLog->rowCount(); ++row) uiGeneralTab.twLog->item(row, 1)->setText(""); const XYFitCurve::FitResult& fitResult = m_fitCurve->fitResult(); if (!fitResult.available) { DEBUG("fit result not available"); return; } // Log uiGeneralTab.twLog->item(0, 1)->setText(fitResult.status); if (!fitResult.valid) { DEBUG("fit result not valid"); return; } uiGeneralTab.twLog->item(1, 1)->setText(QString::number(fitResult.iterations)); uiGeneralTab.twLog->item(2, 1)->setText(QString::number(m_fitData.eps)); if (fitResult.elapsedTime > 1000) uiGeneralTab.twLog->item(3, 1)->setText(QString::number(fitResult.elapsedTime/1000) + " s"); else uiGeneralTab.twLog->item(3, 1)->setText(QString::number(fitResult.elapsedTime) + " ms"); uiGeneralTab.twLog->item(4, 1)->setText(QString::number(fitResult.dof)); uiGeneralTab.twLog->item(5, 1)->setText(QString::number(fitResult.paramValues.size())); uiGeneralTab.twLog->item(6, 1)->setText(QString::number(m_fitData.fitRange.first()) + " .. " + QString::number(m_fitData.fitRange.last()) ); // show all iterations QString str; for (const auto &s: m_fitData.paramNamesUtf8) str += s + '\t'; str += UTF8_QSTRING("χ²"); const QStringList iterations = fitResult.solverOutput.split(';'); for (const auto &s: iterations) if (!s.isEmpty()) str += '\n' + s; uiGeneralTab.twLog->item(7, 1)->setText(str); uiGeneralTab.twLog->resizeRowsToContents(); // Parameters const int np = m_fitData.paramNames.size(); uiGeneralTab.twParameters->setRowCount(np); QStringList headerLabels; headerLabels << i18n("Name") << i18n("Value") << i18n("Error") << i18n("Error, %") << i18n("t statistic") << QLatin1String("P > |t|") << i18n("Conf. Interval"); uiGeneralTab.twParameters->setHorizontalHeaderLabels(headerLabels); for (int i = 0; i < np; i++) { const double paramValue = fitResult.paramValues.at(i); const double errorValue = fitResult.errorValues.at(i); QTableWidgetItem* item = new QTableWidgetItem(m_fitData.paramNamesUtf8.at(i)); item->setBackground(QApplication::palette().color(QPalette::Window)); uiGeneralTab.twParameters->setItem(i, 0, item); item = new QTableWidgetItem(QString::number(paramValue)); uiGeneralTab.twParameters->setItem(i, 1, item); if (!m_fitData.paramFixed.at(i)) { if (!std::isnan(errorValue)) { item = new QTableWidgetItem(QString::number(errorValue, 'g', 6)); uiGeneralTab.twParameters->setItem(i, 2, item); item = new QTableWidgetItem(QString::number(100.*errorValue/std::abs(paramValue), 'g', 3)); uiGeneralTab.twParameters->setItem(i, 3, item); } else { item = new QTableWidgetItem(UTF8_QSTRING("∞")); uiGeneralTab.twParameters->setItem(i, 2, item); item = new QTableWidgetItem(UTF8_QSTRING("∞")); uiGeneralTab.twParameters->setItem(i, 3, item); } // t values QString tdistValueString; if (fitResult.tdist_tValues.at(i) < std::numeric_limits::max()) tdistValueString = QString::number(fitResult.tdist_tValues.at(i), 'g', 3); else tdistValueString = UTF8_QSTRING("∞"); item = new QTableWidgetItem(tdistValueString); uiGeneralTab.twParameters->setItem(i, 4, item); // p values const double p = fitResult.tdist_pValues.at(i); item = new QTableWidgetItem(QString::number(p, 'g', 3)); // color p values depending on value if (p > 0.05) item->setTextColor(QApplication::palette().color(QPalette::LinkVisited)); else if (p > 0.01) item->setTextColor(Qt::darkGreen); else if (p > 0.001) item->setTextColor(Qt::darkCyan); else if (p > 0.0001) item->setTextColor(QApplication::palette().color(QPalette::Link)); else item->setTextColor(QApplication::palette().color(QPalette::Highlight)); uiGeneralTab.twParameters->setItem(i, 5, item); // Conf. interval if (!std::isnan(errorValue)) { const double margin = fitResult.tdist_marginValues.at(i); if (fitResult.tdist_tValues.at(i) < 1.e6) item = new QTableWidgetItem(QString::number(paramValue - margin) + QLatin1String(" .. ") + QString::number(paramValue + margin)); else item = new QTableWidgetItem(i18n("too small")); uiGeneralTab.twParameters->setItem(i, 6, item); } } } // Goodness of fit uiGeneralTab.twGoodness->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); uiGeneralTab.twGoodness->item(0, 2)->setText(QString::number(fitResult.sse)); if (fitResult.dof != 0) { uiGeneralTab.twGoodness->item(1, 2)->setText(QString::number(fitResult.rms)); uiGeneralTab.twGoodness->item(2, 2)->setText(QString::number(fitResult.rsd)); uiGeneralTab.twGoodness->item(3, 2)->setText(QString::number(fitResult.rsquare, 'g', 15)); uiGeneralTab.twGoodness->item(4, 2)->setText(QString::number(fitResult.rsquareAdj, 'g', 15)); // chi^2 and F test p-values uiGeneralTab.twGoodness->item(5, 2)->setText(QString::number(fitResult.chisq_p, 'g', 3)); uiGeneralTab.twGoodness->item(6, 2)->setText(QString::number(fitResult.fdist_F, 'g', 3)); uiGeneralTab.twGoodness->item(7, 2)->setText(QString::number(fitResult.fdist_p, 'g', 3)); uiGeneralTab.twGoodness->item(9, 2)->setText(QString::number(fitResult.aic, 'g', 3)); uiGeneralTab.twGoodness->item(10, 2)->setText(QString::number(fitResult.bic, 'g', 3)); } uiGeneralTab.twGoodness->item(8, 2)->setText(QString::number(fitResult.mae)); //resize the table headers to fit the new content uiGeneralTab.twLog->resizeColumnsToContents(); uiGeneralTab.twParameters->resizeColumnsToContents(); //twGoodness doesn't have any header -> resize sections uiGeneralTab.twGoodness->resizeColumnToContents(0); uiGeneralTab.twGoodness->resizeColumnToContents(1); uiGeneralTab.twGoodness->resizeColumnToContents(2); //enable the "recalculate"-button if the source data was changed since the last fit uiGeneralTab.pbRecalculate->setEnabled(m_fitCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFitCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFitCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYFitCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYFitCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFitCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFitCurveDock::curveXErrorColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXErrorColumn, column); m_initializing = false; } void XYFitCurveDock::curveYErrorColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYErrorColumn, column); m_initializing = false; } /*! * called when fit data of fit curve changes */ void XYFitCurveDock::curveFitDataChanged(const XYFitCurve::FitData& fitData) { DEBUG("XYFitCurveDock::curveFitDataChanged()"); m_initializing = true; m_fitData = fitData; if (m_fitData.modelCategory != nsl_fit_model_custom) uiGeneralTab.cbModel->setCurrentIndex(m_fitData.modelType); uiGeneralTab.sbDegree->setValue(m_fitData.degree); this->showFitResult(); m_initializing = false; } void XYFitCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp index a50bbe16d..a8d90c988 100644 --- a/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierFilterCurveDock.cpp @@ -1,698 +1,698 @@ /*************************************************************************** File : XYFourierFilterCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of Fourier filter curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYFourierFilterCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYFourierFilterCurveDock \brief Provides a widget for editing the properties of the XYFourierFilterCurves (2D-curves defined by a Fourier filter) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFourierFilterCurveDock::XYFourierFilterCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_filterCurve(nullptr) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYFourierFilterCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); for(int i=0; i < NSL_FILTER_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_filter_type_name[i])); for(int i=0; i < NSL_FILTER_FORM_COUNT; i++) uiGeneralTab.cbForm->addItem(i18n(nsl_filter_form_name[i])); for(int i=0; i < NSL_FILTER_CUTOFF_UNIT_COUNT; i++) { uiGeneralTab.cbUnit->addItem(i18n(nsl_filter_cutoff_unit_name[i])); uiGeneralTab.cbUnit2->addItem(i18n(nsl_filter_cutoff_unit_name[i])); } uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierFilterCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbForm, SIGNAL(currentIndexChanged(int)), this, SLOT(formChanged()) ); connect( uiGeneralTab.sbOrder, SIGNAL(valueChanged(int)), this, SLOT(orderChanged()) ); connect( uiGeneralTab.sbCutoff, SIGNAL(valueChanged(double)), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.sbCutoff2, SIGNAL(valueChanged(double)), this, SLOT(enableRecalculate()) ); connect( uiGeneralTab.cbUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged()) ); connect( uiGeneralTab.cbUnit2, SIGNAL(currentIndexChanged(int)), this, SLOT(unit2Changed()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYFourierFilterCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1){ uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_filterCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_filterCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_filterCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_filterCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_filterCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_filterData.autoRange); uiGeneralTab.sbMin->setValue(m_filterData.xRange.first()); uiGeneralTab.sbMax->setValue(m_filterData.xRange.last()); this->autoRangeChanged(); uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); this->typeChanged(); uiGeneralTab.cbForm->setCurrentIndex(m_filterData.form); this->formChanged(); uiGeneralTab.sbOrder->setValue((int)m_filterData.order); uiGeneralTab.cbUnit->setCurrentIndex(m_filterData.unit); this->unitChanged(); // after unit has set uiGeneralTab.sbCutoff->setValue(m_filterData.cutoff); uiGeneralTab.cbUnit2->setCurrentIndex(m_filterData.unit2); this->unit2Changed(); // after unit has set uiGeneralTab.sbCutoff2->setValue(m_filterData.cutoff2); this->showFilterResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_filterCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_filterCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_filterCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_filterCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_filterCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_filterCurve, SIGNAL(filterDataChanged(XYFourierFilterCurve::FilterData)), this, SLOT(curveFilterDataChanged(XYFourierFilterCurve::FilterData))); connect(m_filterCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYFourierFilterCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFourierFilterCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_filterCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_filterData = m_filterCurve->filterData(); initGeneralTab(); initTabs(); m_initializing=false; } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFourierFilterCurveDock::nameChanged(){ if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFourierFilterCurveDock::commentChanged(){ if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFourierFilterCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYFourierFilterCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = nullptr; if (aspect) dataSourceCurve = dynamic_cast(aspect); // update range of cutoff spin boxes (like a unit change) unitChanged(); unit2Changed(); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYFourierFilterCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // update range of cutoff spin boxes (like a unit change) unitChanged(); unit2Changed(); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYFourierFilterCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYFourierFilterCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_filterData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYFourierFilterCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_filterData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierFilterCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_filterData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierFilterCurveDock::typeChanged() { nsl_filter_type type = (nsl_filter_type)uiGeneralTab.cbType->currentIndex(); m_filterData.type = type; switch (type) { case nsl_filter_type_low_pass: case nsl_filter_type_high_pass: uiGeneralTab.lCutoff->setText(i18n("Cutoff")); uiGeneralTab.lCutoff2->setVisible(false); uiGeneralTab.sbCutoff2->setVisible(false); uiGeneralTab.cbUnit2->setVisible(false); break; case nsl_filter_type_band_pass: case nsl_filter_type_band_reject: uiGeneralTab.lCutoff2->setVisible(true); uiGeneralTab.lCutoff->setText(i18n("Lower Cutoff")); uiGeneralTab.lCutoff2->setText(i18n("Upper Cutoff")); uiGeneralTab.sbCutoff2->setVisible(true); uiGeneralTab.cbUnit2->setVisible(true); break; //TODO /* case nsl_filter_type_threshold: uiGeneralTab.lCutoff->setText(i18n("Value")); uiGeneralTab.lCutoff2->setVisible(false); uiGeneralTab.sbCutoff2->setVisible(false); uiGeneralTab.cbUnit2->setVisible(false); */ } enableRecalculate(); } void XYFourierFilterCurveDock::formChanged() { nsl_filter_form form = (nsl_filter_form)uiGeneralTab.cbForm->currentIndex(); m_filterData.form = form; switch (form) { case nsl_filter_form_ideal: uiGeneralTab.sbOrder->setVisible(false); uiGeneralTab.lOrder->setVisible(false); break; case nsl_filter_form_butterworth: case nsl_filter_form_chebyshev_i: case nsl_filter_form_chebyshev_ii: case nsl_filter_form_legendre: case nsl_filter_form_bessel: uiGeneralTab.sbOrder->setVisible(true); uiGeneralTab.lOrder->setVisible(true); break; } enableRecalculate(); } void XYFourierFilterCurveDock::orderChanged() { m_filterData.order = (unsigned int)uiGeneralTab.sbOrder->value(); enableRecalculate(); } void XYFourierFilterCurveDock::unitChanged() { nsl_filter_cutoff_unit unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit->currentIndex(); nsl_filter_cutoff_unit oldUnit = m_filterData.unit; double oldValue = uiGeneralTab.sbCutoff->value(); m_filterData.unit = unit; int n = 100; double f = 1.0; // sample frequency const AbstractColumn* xDataColumn = nullptr; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn != nullptr) { n = xDataColumn->rowCount(); double range = xDataColumn->maximum() - xDataColumn->minimum(); f = (n-1)/range/2.; DEBUG(" n =" << n << " sample frequency =" << f); } switch (unit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setDecimals(6); uiGeneralTab.sbCutoff->setMaximum(f); uiGeneralTab.sbCutoff->setSingleStep(0.01*f); uiGeneralTab.sbCutoff->setSuffix(" Hz"); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setValue(oldValue*f); break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setValue(oldValue*f/n); break; } break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setDecimals(6); uiGeneralTab.sbCutoff->setMaximum(1.0); uiGeneralTab.sbCutoff->setSingleStep(0.01); uiGeneralTab.sbCutoff->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setValue(oldValue/f); break; case nsl_filter_cutoff_unit_fraction: break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setValue(oldValue/n); break; } break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff->setDecimals(0); uiGeneralTab.sbCutoff->setSingleStep(1); uiGeneralTab.sbCutoff->setMaximum(n); uiGeneralTab.sbCutoff->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff->setValue(oldValue*n/f); break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff->setValue(oldValue*n); break; case nsl_filter_cutoff_unit_index: break; } break; } enableRecalculate(); } void XYFourierFilterCurveDock::unit2Changed() { nsl_filter_cutoff_unit unit = (nsl_filter_cutoff_unit)uiGeneralTab.cbUnit2->currentIndex(); nsl_filter_cutoff_unit oldUnit = m_filterData.unit2; double oldValue = uiGeneralTab.sbCutoff2->value(); m_filterData.unit2 = unit; int n = 100; double f = 1.0; // sample frequency const AbstractColumn* xDataColumn = nullptr; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_filterCurve->xDataColumn(); else { if (m_filterCurve->dataSourceCurve()) xDataColumn = m_filterCurve->dataSourceCurve()->xColumn(); } if (xDataColumn != nullptr) { n = xDataColumn->rowCount(); double range = xDataColumn->maximum() - xDataColumn->minimum(); f = (n-1)/range/2.; DEBUG(" n =" << n << " sample frequency =" << f); } switch (unit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setDecimals(6); uiGeneralTab.sbCutoff2->setMaximum(f); uiGeneralTab.sbCutoff2->setSingleStep(0.01*f); uiGeneralTab.sbCutoff2->setSuffix(" Hz"); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setValue(oldValue*f); break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setValue(oldValue*f/n); break; } break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setDecimals(6); uiGeneralTab.sbCutoff2->setMaximum(1.0); uiGeneralTab.sbCutoff2->setSingleStep(0.01); uiGeneralTab.sbCutoff2->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setValue(oldValue/f); break; case nsl_filter_cutoff_unit_fraction: break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setValue(oldValue/n); break; } break; case nsl_filter_cutoff_unit_index: uiGeneralTab.sbCutoff2->setDecimals(0); uiGeneralTab.sbCutoff2->setSingleStep(1); uiGeneralTab.sbCutoff2->setMaximum(n); uiGeneralTab.sbCutoff2->setSuffix(""); switch (oldUnit) { case nsl_filter_cutoff_unit_frequency: uiGeneralTab.sbCutoff2->setValue(oldValue*n/f); break; case nsl_filter_cutoff_unit_fraction: uiGeneralTab.sbCutoff2->setValue(oldValue*n); break; case nsl_filter_cutoff_unit_index: break; } break; } enableRecalculate(); } void XYFourierFilterCurveDock::recalculateClicked() { m_filterData.cutoff = uiGeneralTab.sbCutoff->value(); m_filterData.cutoff2 = uiGeneralTab.sbCutoff2->value(); if ((m_filterData.type == nsl_filter_type_band_pass || m_filterData.type == nsl_filter_type_band_reject) && m_filterData.cutoff2 <= m_filterData.cutoff) { KMessageBox::sorry(this, i18n("The band width is <= 0 since lower cutoff value is not smaller than upper cutoff value. Please fix this."), i18n("band width <= 0") ); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setFilterData(m_filterData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Fourier-Filter status: ") + m_filterCurve->filterResult().status); QApplication::restoreOverrideCursor(); } void XYFourierFilterCurveDock::enableRecalculate() const { if (m_initializing) return; //no filtering possible without the x- and y-data bool hasSourceData = false; if (m_filterCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_filterCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the filter */ void XYFourierFilterCurveDock::showFilterResult() { const XYFourierFilterCurve::FilterResult& filterResult = m_filterCurve->filterResult(); if (!filterResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + filterResult.status + "
"; + QString str = i18n("status: %1", filterResult.status) + "
"; if (!filterResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (filterResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(filterResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(filterResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(filterResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(filterResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last filter uiGeneralTab.pbRecalculate->setEnabled(m_filterCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFourierFilterCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFourierFilterCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYFourierFilterCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYFourierFilterCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFourierFilterCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFourierFilterCurveDock::curveFilterDataChanged(const XYFourierFilterCurve::FilterData& filterData) { m_initializing = true; m_filterData = filterData; uiGeneralTab.cbType->setCurrentIndex(m_filterData.type); this->typeChanged(); this->showFilterResult(); m_initializing = false; } void XYFourierFilterCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp index e8e27e68f..5ff09da24 100644 --- a/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYFourierTransformCurveDock.cpp @@ -1,411 +1,411 @@ /*************************************************************************** File : XYFourierTransformCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of Fourier transform curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYFourierTransformCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYFourierTransformCurveDock \brief Provides a widget for editing the properties of the XYFourierTransformCurves (2D-curves defined by a Fourier transform) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYFourierTransformCurveDock::XYFourierTransformCurveDock(QWidget *parent): XYCurveDock(parent), cbXDataColumn(0), cbYDataColumn(0), m_transformCurve(0) { //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYFourierTransformCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 5, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 6, 2, 1, 2); for (int i=0; i < NSL_SF_WINDOW_TYPE_COUNT; i++) uiGeneralTab.cbWindowType->addItem(i18n(nsl_sf_window_type_name[i])); for (int i=0; i < NSL_DFT_RESULT_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_dft_result_type_name[i])); for (int i=0; i < NSL_DFT_XSCALE_COUNT; i++) uiGeneralTab.cbXScale->addItem(i18n(nsl_dft_xscale_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYFourierTransformCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbWindowType, SIGNAL(currentIndexChanged(int)), this, SLOT(windowTypeChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbTwoSided, SIGNAL(stateChanged(int)), this, SLOT(twoSidedChanged()) ); connect( uiGeneralTab.cbShifted, SIGNAL(stateChanged(int)), this, SLOT(shiftedChanged()) ); connect( uiGeneralTab.cbXScale, SIGNAL(currentIndexChanged(int)), this, SLOT(xScaleChanged()) ); // connect( uiGeneralTab.pbOptions, SIGNAL(clicked()), this, SLOT(showOptions()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); } void XYFourierTransformCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_transformCurve = dynamic_cast(m_curve); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_transformCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_transformCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_transformData.autoRange); uiGeneralTab.sbMin->setValue(m_transformData.xRange.first()); uiGeneralTab.sbMax->setValue(m_transformData.xRange.last()); this->autoRangeChanged(); uiGeneralTab.cbWindowType->setCurrentIndex(m_transformData.windowType); this->windowTypeChanged(); uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); this->typeChanged(); uiGeneralTab.cbTwoSided->setChecked(m_transformData.twoSided); this->twoSidedChanged(); // show/hide shifted check box uiGeneralTab.cbShifted->setChecked(m_transformData.shifted); this->shiftedChanged(); uiGeneralTab.cbXScale->setCurrentIndex(m_transformData.xScale); this->xScaleChanged(); this->showTransformResult(); //enable the "recalculate"-button if the source data was changed since the last transform uiGeneralTab.pbRecalculate->setEnabled(m_transformCurve->isSourceDataChangedSinceLastRecalc()); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_transformCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_transformCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_transformCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_transformCurve, SIGNAL(transformDataChanged(XYFourierTransformCurve::TransformData)), this, SLOT(curveTransformDataChanged(XYFourierTransformCurve::TransformData))); connect(m_transformCurve, SIGNAL(sourceDataChangedSinceLastTransform()), this, SLOT(enableRecalculate())); } void XYFourierTransformCurveDock::setModel() { QList list; list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYFourierTransformCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_transformCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_transformData = m_transformCurve->transformData(); initGeneralTab(); initTabs(); m_initializing=false; } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYFourierTransformCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYFourierTransformCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYFourierTransformCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } } } void XYFourierTransformCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYFourierTransformCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_transformData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); m_transformCurve = dynamic_cast(m_curve); if (m_transformCurve->xDataColumn()) { uiGeneralTab.sbMin->setValue(m_transformCurve->xDataColumn()->minimum()); uiGeneralTab.sbMax->setValue(m_transformCurve->xDataColumn()->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYFourierTransformCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_transformData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierTransformCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_transformData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYFourierTransformCurveDock::windowTypeChanged() { nsl_sf_window_type windowType = (nsl_sf_window_type)uiGeneralTab.cbWindowType->currentIndex(); m_transformData.windowType = windowType; enableRecalculate(); } void XYFourierTransformCurveDock::typeChanged() { nsl_dft_result_type type = (nsl_dft_result_type)uiGeneralTab.cbType->currentIndex(); m_transformData.type = type; enableRecalculate(); } void XYFourierTransformCurveDock::twoSidedChanged() { bool twoSided = uiGeneralTab.cbTwoSided->isChecked(); m_transformData.twoSided = twoSided; if (twoSided) uiGeneralTab.cbShifted->setEnabled(true); else { uiGeneralTab.cbShifted->setEnabled(false); uiGeneralTab.cbShifted->setChecked(false); } enableRecalculate(); } void XYFourierTransformCurveDock::shiftedChanged() { bool shifted = uiGeneralTab.cbShifted->isChecked(); m_transformData.shifted = shifted; enableRecalculate(); } void XYFourierTransformCurveDock::xScaleChanged() { nsl_dft_xscale xScale = (nsl_dft_xscale)uiGeneralTab.cbXScale->currentIndex(); m_transformData.xScale = xScale; enableRecalculate(); } void XYFourierTransformCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setTransformData(m_transformData); uiGeneralTab.pbRecalculate->setEnabled(false); emit info(i18n("Fourier transformation status: ") + m_transformCurve->transformResult().status); QApplication::restoreOverrideCursor(); } void XYFourierTransformCurveDock::enableRecalculate() const { if (m_initializing) return; //no transforming possible without the x- and y-data AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); bool data = (aspectX!=0 && aspectY!=0); uiGeneralTab.pbRecalculate->setEnabled(data); } /*! * show the result and details of the transform */ void XYFourierTransformCurveDock::showTransformResult() { const XYFourierTransformCurve::TransformResult& transformResult = m_transformCurve->transformResult(); if (!transformResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + transformResult.status + "
"; + QString str = i18n("status: %1", transformResult.status) + "
"; if (!transformResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (transformResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(transformResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(transformResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(transformResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(transformResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYFourierTransformCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYFourierTransformCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYFourierTransformCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYFourierTransformCurveDock::curveTransformDataChanged(const XYFourierTransformCurve::TransformData& transformData) { m_initializing = true; m_transformData = transformData; uiGeneralTab.cbType->setCurrentIndex(m_transformData.type); this->typeChanged(); this->showTransformResult(); m_initializing = false; } void XYFourierTransformCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp index 590193043..55e23024b 100644 --- a/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYIntegrationCurveDock.cpp @@ -1,504 +1,504 @@ /*************************************************************************** File : XYIntegrationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Description : widget for editing properties of integration curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYIntegrationCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include extern "C" { #include "backend/nsl/nsl_int.h" } /*! \class XYIntegrationCurveDock \brief Provides a widget for editing the properties of the XYIntegrationCurves (2D-curves defined by a integration) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYIntegrationCurveDock::XYIntegrationCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_integrationCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYIntegrationCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 3); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 3); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 3); for (int i=0; i < NSL_INT_NETHOD_COUNT; i++) uiGeneralTab.cbMethod->addItem(i18n(nsl_int_method_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYIntegrationCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYIntegrationCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbMethod, SIGNAL(currentIndexChanged(int)), this, SLOT(methodChanged()) ); connect( uiGeneralTab.cbAbsolute, SIGNAL(clicked(bool)), this, SLOT(absoluteChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYIntegrationCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_integrationCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_integrationCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_integrationCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_integrationCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_integrationCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_integrationData.autoRange); uiGeneralTab.sbMin->setValue(m_integrationData.xRange.first()); uiGeneralTab.sbMax->setValue(m_integrationData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbMethod->setCurrentIndex(m_integrationData.method); this->methodChanged(); uiGeneralTab.cbAbsolute->setChecked(m_integrationData.absolute); this->absoluteChanged(); this->showIntegrationResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_integrationCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_integrationCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_integrationCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_integrationCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_integrationCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_integrationCurve, SIGNAL(integrationDataChanged(XYIntegrationCurve::IntegrationData)), this, SLOT(curveIntegrationDataChanged(XYIntegrationCurve::IntegrationData))); connect(m_integrationCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYIntegrationCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbDataSourceCurve->setModel(m_aspectTreeModel); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYIntegrationCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_integrationCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_integrationData = m_integrationCurve->integrationData(); initGeneralTab(); initTabs(); m_initializing=false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYIntegrationCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYIntegrationCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYIntegrationCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYIntegrationCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = dynamic_cast(aspect); // disable integration orders and accuracies that need more data points this->updateSettings(dataSourceCurve->xColumn()); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYIntegrationCurveDock::xDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } // disable integration methods that need more data points this->updateSettings(column); } } /*! * disable deriv orders and accuracies that need more data points */ void XYIntegrationCurveDock::updateSettings(const AbstractColumn* column) { if (!column) return; //TODO // size_t n=0; // for (int row=0; row < column->rowCount(); row++) // if (!std::isnan(column->valueAt(row)) && !column->isMasked(row)) // n++; } void XYIntegrationCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYIntegrationCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_integrationData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_integrationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_integrationCurve->xDataColumn(); else { if (m_integrationCurve->dataSourceCurve()) xDataColumn = m_integrationCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYIntegrationCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_integrationData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYIntegrationCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_integrationData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYIntegrationCurveDock::methodChanged() { nsl_int_method_type method = (nsl_int_method_type)uiGeneralTab.cbMethod->currentIndex(); m_integrationData.method = method; // update absolute option switch (method) { case nsl_int_method_rectangle: case nsl_int_method_trapezoid: uiGeneralTab.cbAbsolute->setEnabled(true); break; case nsl_int_method_simpson: case nsl_int_method_simpson_3_8: uiGeneralTab.cbAbsolute->setChecked(false); uiGeneralTab.cbAbsolute->setEnabled(false); } uiGeneralTab.pbRecalculate->setEnabled(true); } void XYIntegrationCurveDock::absoluteChanged() { bool absolute = uiGeneralTab.cbAbsolute->isChecked(); m_integrationData.absolute = absolute; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYIntegrationCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setIntegrationData(m_integrationData); uiGeneralTab.pbRecalculate->setEnabled(false); - emit info(i18n("Integration status: ") + m_integrationCurve->integrationResult().status); + emit info(i18n("Integration status: %1", m_integrationCurve->integrationResult().status)); QApplication::restoreOverrideCursor(); } void XYIntegrationCurveDock::enableRecalculate() const { if (m_initializing) return; //no integration possible without the x- and y-data bool hasSourceData = false; if (m_integrationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_integrationCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the integration */ void XYIntegrationCurveDock::showIntegrationResult() { const XYIntegrationCurve::IntegrationResult& integrationResult = m_integrationCurve->integrationResult(); if (!integrationResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + integrationResult.status + "
"; + QString str = i18n("status: %1", integrationResult.status) + "
"; if (!integrationResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (integrationResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(integrationResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(integrationResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(integrationResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(integrationResult.elapsedTime)) + "
"; - str += i18n("value: ") + QString::number(integrationResult.value) + "
"; + str += i18n("value: %1", QString::number(integrationResult.value)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last integration uiGeneralTab.pbRecalculate->setEnabled(m_integrationCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYIntegrationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYIntegrationCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYIntegrationCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYIntegrationCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYIntegrationCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYIntegrationCurveDock::curveIntegrationDataChanged(const XYIntegrationCurve::IntegrationData& integrationData) { m_initializing = true; m_integrationData = integrationData; uiGeneralTab.cbMethod->setCurrentIndex(m_integrationData.method); this->methodChanged(); uiGeneralTab.cbAbsolute->setChecked(m_integrationData.absolute); this->absoluteChanged(); this->showIntegrationResult(); m_initializing = false; } void XYIntegrationCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp index 06c88ce4a..2d534cc4d 100644 --- a/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYInterpolationCurveDock.cpp @@ -1,735 +1,735 @@ /*************************************************************************** File : XYInterpolationCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 20016-2017 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of interpolation curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYInterpolationCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include extern "C" { #include // gsl_interp types } #include // isnan /*! \class XYInterpolationCurveDock \brief Provides a widget for editing the properties of the XYInterpolationCurves (2D-curves defined by an interpolation) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYInterpolationCurveDock::XYInterpolationCurveDock(QWidget* parent): XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_interpolationCurve(nullptr), dataPoints(0) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYInterpolationCurveDock::setupGeneral() { QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 2); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); for (int i=0; i < NSL_INTERP_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_interp_type_name[i])); #if GSL_MAJOR_VERSION < 2 // disable Steffen spline item const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbType->model()); QStandardItem* item = model->item(nsl_interp_type_steffen); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); #endif for (int i=0; i < NSL_INTERP_PCH_VARIANT_COUNT; i++) uiGeneralTab.cbVariant->addItem(i18n(nsl_interp_pch_variant_name[i])); for (int i=0; i < NSL_INTERP_EVALUATE_COUNT; i++) uiGeneralTab.cbEval->addItem(i18n(nsl_interp_evaluate_name[i])); uiGeneralTab.cbPointsMode->addItem(i18n("Auto (5x data points)")); uiGeneralTab.cbPointsMode->addItem(i18n("Multiple of data points")); uiGeneralTab.cbPointsMode->addItem(i18n("Custom")); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYInterpolationCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYInterpolationCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.cbVariant, SIGNAL(currentIndexChanged(int)), this, SLOT(variantChanged()) ); connect( uiGeneralTab.sbTension, SIGNAL(valueChanged(double)), this, SLOT(tensionChanged()) ); connect( uiGeneralTab.sbContinuity, SIGNAL(valueChanged(double)), this, SLOT(continuityChanged()) ); connect( uiGeneralTab.sbBias, SIGNAL(valueChanged(double)), this, SLOT(biasChanged()) ); connect( uiGeneralTab.cbEval, SIGNAL(currentIndexChanged(int)), this, SLOT(evaluateChanged()) ); connect( uiGeneralTab.sbPoints, SIGNAL(valueChanged(double)), this, SLOT(numberOfPointsChanged()) ); connect( uiGeneralTab.cbPointsMode, SIGNAL(currentIndexChanged(int)), this, SLOT(pointsModeChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYInterpolationCurveDock::initGeneralTab() { //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_interpolationCurve = dynamic_cast(m_curve); Q_ASSERT(m_interpolationCurve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_interpolationCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_interpolationCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_interpolationCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_interpolationCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_interpolationData.autoRange); uiGeneralTab.sbMin->setValue(m_interpolationData.xRange.first()); uiGeneralTab.sbMax->setValue(m_interpolationData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbType->setCurrentIndex(m_interpolationData.type); this->typeChanged(); uiGeneralTab.cbVariant->setCurrentIndex(m_interpolationData.variant); this->variantChanged(); uiGeneralTab.sbTension->setValue(m_interpolationData.tension); uiGeneralTab.sbContinuity->setValue(m_interpolationData.continuity); uiGeneralTab.sbBias->setValue(m_interpolationData.bias); uiGeneralTab.cbEval->setCurrentIndex(m_interpolationData.evaluate); if (m_interpolationData.pointsMode == XYInterpolationCurve::Multiple) uiGeneralTab.sbPoints->setValue(m_interpolationData.npoints/5.); else uiGeneralTab.sbPoints->setValue(m_interpolationData.npoints); uiGeneralTab.cbPointsMode->setCurrentIndex(m_interpolationData.pointsMode); this->showInterpolationResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_interpolationCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_interpolationCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_interpolationCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_interpolationCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_interpolationCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_interpolationCurve, SIGNAL(interpolationDataChanged(XYInterpolationCurve::InterpolationData)), this, SLOT(curveInterpolationDataChanged(XYInterpolationCurve::InterpolationData))); connect(m_interpolationCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYInterpolationCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for(XYCurve* curve: m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYInterpolationCurveDock::setCurves(QList list) { m_initializing=true; m_curvesList=list; m_curve=list.first(); m_interpolationCurve = dynamic_cast(m_curve); Q_ASSERT(m_interpolationCurve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_interpolationData = m_interpolationCurve->interpolationData(); initGeneralTab(); initTabs(); m_initializing=false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYInterpolationCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYInterpolationCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYInterpolationCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for(XYCurve* curve: m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYInterpolationCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = 0; if (aspect) { dataSourceCurve = dynamic_cast(aspect); Q_ASSERT(dataSourceCurve); } // disable types that need more data points this->updateSettings(dataSourceCurve->xColumn()); if (m_initializing) return; for(XYCurve* curve: m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYInterpolationCurveDock::xDataColumnChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column); } this->updateSettings(column); if (m_initializing) return; for(XYCurve* curve: m_curvesList) dynamic_cast(curve)->setXDataColumn(column); } void XYInterpolationCurveDock::updateSettings(const AbstractColumn* column) { if (!column) return; // disable types that need more data points if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } unsigned int n=0; for (int row=0; row < column->rowCount(); row++) if (!std::isnan(column->valueAt(row)) && !column->isMasked(row)) n++; dataPoints = n; if(m_interpolationData.pointsMode == XYInterpolationCurve::Auto) pointsModeChanged(); const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbType->model()); QStandardItem* item = model->item(nsl_interp_type_polynomial); if (dataPoints < gsl_interp_type_min_size(gsl_interp_polynomial) || dataPoints > 100) { // not good for many points item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_polynomial) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_interp_type_cspline); if (dataPoints < gsl_interp_type_min_size(gsl_interp_cspline)) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_cspline) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_interp_type_cspline_periodic); if (dataPoints < gsl_interp_type_min_size(gsl_interp_cspline_periodic)) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_cspline_periodic) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_interp_type_akima); if (dataPoints < gsl_interp_type_min_size(gsl_interp_akima)) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_akima) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); item = model->item(nsl_interp_type_akima_periodic); if (dataPoints < gsl_interp_type_min_size(gsl_interp_akima_periodic)) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_akima_periodic) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); #if GSL_MAJOR_VERSION >= 2 item = model->item(nsl_interp_type_steffen); if (dataPoints < gsl_interp_type_min_size(gsl_interp_steffen)) { item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); if (uiGeneralTab.cbType->currentIndex() == nsl_interp_type_steffen) uiGeneralTab.cbType->setCurrentIndex(0); } else item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); #endif // own types work with 2 or more data points } void XYInterpolationCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = 0; if (aspect) { column = dynamic_cast(aspect); Q_ASSERT(column); } for(XYCurve* curve: m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYInterpolationCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_interpolationData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_interpolationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_interpolationCurve->xDataColumn(); else { if (m_interpolationCurve->dataSourceCurve()) xDataColumn = m_interpolationCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYInterpolationCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_interpolationData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_interpolationData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::typeChanged() { nsl_interp_type type = (nsl_interp_type)uiGeneralTab.cbType->currentIndex(); m_interpolationData.type = type; switch (type) { case nsl_interp_type_pch: uiGeneralTab.lVariant->show(); uiGeneralTab.cbVariant->show(); break; case nsl_interp_type_linear: case nsl_interp_type_polynomial: case nsl_interp_type_cspline: case nsl_interp_type_cspline_periodic: case nsl_interp_type_akima: case nsl_interp_type_akima_periodic: case nsl_interp_type_steffen: case nsl_interp_type_cosine: case nsl_interp_type_exponential: case nsl_interp_type_rational: uiGeneralTab.lVariant->hide(); uiGeneralTab.cbVariant->hide(); uiGeneralTab.cbVariant->setCurrentIndex(nsl_interp_pch_variant_finite_difference); uiGeneralTab.lParameter->hide(); uiGeneralTab.lTension->hide(); uiGeneralTab.sbTension->hide(); uiGeneralTab.lContinuity->hide(); uiGeneralTab.sbContinuity->hide(); uiGeneralTab.lBias->hide(); uiGeneralTab.sbBias->hide(); } uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::variantChanged() { nsl_interp_pch_variant variant = (nsl_interp_pch_variant)uiGeneralTab.cbVariant->currentIndex(); m_interpolationData.variant = variant; switch (variant) { case nsl_interp_pch_variant_finite_difference: uiGeneralTab.lParameter->hide(); uiGeneralTab.lTension->hide(); uiGeneralTab.sbTension->hide(); uiGeneralTab.lContinuity->hide(); uiGeneralTab.sbContinuity->hide(); uiGeneralTab.lBias->hide(); uiGeneralTab.sbBias->hide(); break; case nsl_interp_pch_variant_catmull_rom: uiGeneralTab.lParameter->show(); uiGeneralTab.lTension->show(); uiGeneralTab.sbTension->show(); uiGeneralTab.sbTension->setEnabled(false); uiGeneralTab.sbTension->setValue(0.0); uiGeneralTab.lContinuity->hide(); uiGeneralTab.sbContinuity->hide(); uiGeneralTab.lBias->hide(); uiGeneralTab.sbBias->hide(); break; case nsl_interp_pch_variant_cardinal: uiGeneralTab.lParameter->show(); uiGeneralTab.lTension->show(); uiGeneralTab.sbTension->show(); uiGeneralTab.sbTension->setEnabled(true); uiGeneralTab.lContinuity->hide(); uiGeneralTab.sbContinuity->hide(); uiGeneralTab.lBias->hide(); uiGeneralTab.sbBias->hide(); break; case nsl_interp_pch_variant_kochanek_bartels: uiGeneralTab.lParameter->show(); uiGeneralTab.lTension->show(); uiGeneralTab.sbTension->show(); uiGeneralTab.sbTension->setEnabled(true); uiGeneralTab.lContinuity->show(); uiGeneralTab.sbContinuity->show(); uiGeneralTab.lBias->show(); uiGeneralTab.sbBias->show(); break; } uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::tensionChanged() { m_interpolationData.tension = uiGeneralTab.sbTension->value(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::continuityChanged() { m_interpolationData.continuity = uiGeneralTab.sbContinuity->value(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::biasChanged() { m_interpolationData.bias = uiGeneralTab.sbBias->value(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::evaluateChanged() { m_interpolationData.evaluate = (nsl_interp_evaluate)uiGeneralTab.cbEval->currentIndex(); uiGeneralTab.pbRecalculate->setEnabled(true); } void XYInterpolationCurveDock::pointsModeChanged() { XYInterpolationCurve::PointsMode mode = (XYInterpolationCurve::PointsMode)uiGeneralTab.cbPointsMode->currentIndex(); switch (mode) { case XYInterpolationCurve::Auto: uiGeneralTab.sbPoints->setEnabled(false); uiGeneralTab.sbPoints->setDecimals(0); uiGeneralTab.sbPoints->setSingleStep(1.0); uiGeneralTab.sbPoints->setValue(5*dataPoints); break; case XYInterpolationCurve::Multiple: uiGeneralTab.sbPoints->setEnabled(true); if(m_interpolationData.pointsMode != XYInterpolationCurve::Multiple && dataPoints > 0) { uiGeneralTab.sbPoints->setDecimals(2); uiGeneralTab.sbPoints->setValue(uiGeneralTab.sbPoints->value()/(double)dataPoints); uiGeneralTab.sbPoints->setSingleStep(0.01); } break; case XYInterpolationCurve::Custom: uiGeneralTab.sbPoints->setEnabled(true); if(m_interpolationData.pointsMode == XYInterpolationCurve::Multiple) { uiGeneralTab.sbPoints->setDecimals(0); uiGeneralTab.sbPoints->setSingleStep(1.0); uiGeneralTab.sbPoints->setValue(uiGeneralTab.sbPoints->value()*dataPoints); } break; } m_interpolationData.pointsMode = mode; } void XYInterpolationCurveDock::numberOfPointsChanged() { if(uiGeneralTab.cbPointsMode->currentIndex() == XYInterpolationCurve::Multiple) m_interpolationData.npoints = uiGeneralTab.sbPoints->value()*dataPoints; else m_interpolationData.npoints = uiGeneralTab.sbPoints->value(); // warn if points is smaller than data points QPalette palette = uiGeneralTab.sbPoints->palette(); if(m_interpolationData.npoints < dataPoints) palette.setColor(QPalette::Text, Qt::red); else palette.setColor(QPalette::Text, Qt::black); uiGeneralTab.sbPoints->setPalette(palette); enableRecalculate(); } void XYInterpolationCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for(XYCurve* curve: m_curvesList) dynamic_cast(curve)->setInterpolationData(m_interpolationData); uiGeneralTab.pbRecalculate->setEnabled(false); - emit info(i18n("Interpolation status: ") + m_interpolationCurve->interpolationResult().status); + emit info(i18n("Interpolation status: %1", m_interpolationCurve->interpolationResult().status)); QApplication::restoreOverrideCursor(); } void XYInterpolationCurveDock::enableRecalculate() const { if (m_initializing) return; //no interpolation possible without the x- and y-data bool hasSourceData = false; if (m_interpolationCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_interpolationCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the interpolation */ void XYInterpolationCurveDock::showInterpolationResult() { const XYInterpolationCurve::InterpolationResult& interpolationResult = m_interpolationCurve->interpolationResult(); if (!interpolationResult.available) { uiGeneralTab.teResult->clear(); return; } - QString str = i18n("status:") + ' ' + interpolationResult.status + "
"; + QString str = i18n("status: %1", interpolationResult.status) + "
"; if (!interpolationResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (interpolationResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(interpolationResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(interpolationResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(interpolationResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(interpolationResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last interpolation uiGeneralTab.pbRecalculate->setEnabled(m_interpolationCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYInterpolationCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) { uiGeneralTab.leName->setText(aspect->name()); } else if (aspect->comment() != uiGeneralTab.leComment->text()) { uiGeneralTab.leComment->setText(aspect->comment()); } m_initializing = false; } void XYInterpolationCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYInterpolationCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYInterpolationCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYInterpolationCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYInterpolationCurveDock::curveInterpolationDataChanged(const XYInterpolationCurve::InterpolationData& data) { m_initializing = true; m_interpolationData = data; uiGeneralTab.cbType->setCurrentIndex(m_interpolationData.type); this->typeChanged(); this->showInterpolationResult(); m_initializing = false; } void XYInterpolationCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp index 53c4f92f6..057c70e60 100644 --- a/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp +++ b/src/kdefrontend/dockwidgets/XYSmoothCurveDock.cpp @@ -1,593 +1,593 @@ /*************************************************************************** File : XYSmoothCurveDock.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2016 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Description : widget for editing properties of smooth curves ***************************************************************************/ /*************************************************************************** * * * 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 "XYSmoothCurveDock.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include /*! \class XYSmoothCurveDock \brief Provides a widget for editing the properties of the XYSmoothCurves (2D-curves defined by an smooth) currently selected in the project explorer. If more then one curves are set, the properties of the first column are shown. The changes of the properties are applied to all curves. The exclusions are the name, the comment and the datasets (columns) of the curves - these properties can only be changed if there is only one single curve. \ingroup kdefrontend */ XYSmoothCurveDock::XYSmoothCurveDock(QWidget* parent) : XYCurveDock(parent), cbDataSourceCurve(nullptr), cbXDataColumn(nullptr), cbYDataColumn(nullptr), m_smoothCurve(nullptr) { //hide the line connection type ui.cbLineType->setDisabled(true); //remove the tab "Error bars" ui.tabWidget->removeTab(5); } /*! * // Tab "General" */ void XYSmoothCurveDock::setupGeneral() { DEBUG("XYSmoothCurveDock::setupGeneral()"); QWidget* generalTab = new QWidget(ui.tabGeneral); uiGeneralTab.setupUi(generalTab); QGridLayout* gridLayout = dynamic_cast(generalTab->layout()); if (gridLayout) { gridLayout->setContentsMargins(2,2,2,2); gridLayout->setHorizontalSpacing(2); gridLayout->setVerticalSpacing(2); } uiGeneralTab.cbDataSourceType->addItem(i18n("Spreadsheet")); uiGeneralTab.cbDataSourceType->addItem(i18n("XY-Curve")); cbDataSourceCurve = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbDataSourceCurve, 5, 2, 1, 2); cbXDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbXDataColumn, 6, 2, 1, 2); cbYDataColumn = new TreeViewComboBox(generalTab); gridLayout->addWidget(cbYDataColumn, 7, 2, 1, 2); for (int i=0; i < NSL_SMOOTH_TYPE_COUNT; i++) uiGeneralTab.cbType->addItem(i18n(nsl_smooth_type_name[i])); for (int i=0; i < NSL_SMOOTH_WEIGHT_TYPE_COUNT; i++) uiGeneralTab.cbWeight->addItem(i18n(nsl_smooth_weight_type_name[i])); for (int i=0; i < NSL_SMOOTH_PAD_MODE_COUNT; i++) uiGeneralTab.cbMode->addItem(i18n(nsl_smooth_pad_mode_name[i])); uiGeneralTab.sbMin->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.sbMax->setRange(-std::numeric_limits::max(), std::numeric_limits::max()); uiGeneralTab.pbRecalculate->setIcon(QIcon::fromTheme("run-build")); QHBoxLayout* layout = new QHBoxLayout(ui.tabGeneral); layout->setMargin(0); layout->addWidget(generalTab); //Slots connect( uiGeneralTab.leName, &QLineEdit::textChanged, this, &XYSmoothCurveDock::nameChanged ); connect( uiGeneralTab.leComment, &QLineEdit::textChanged, this, &XYSmoothCurveDock::commentChanged ); connect( uiGeneralTab.chkVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); connect( uiGeneralTab.cbDataSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(dataSourceTypeChanged(int)) ); connect( uiGeneralTab.cbAutoRange, SIGNAL(clicked(bool)), this, SLOT(autoRangeChanged()) ); connect( uiGeneralTab.sbMin, SIGNAL(valueChanged(double)), this, SLOT(xRangeMinChanged()) ); connect( uiGeneralTab.sbMax, SIGNAL(valueChanged(double)), this, SLOT(xRangeMaxChanged()) ); connect( uiGeneralTab.cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged()) ); connect( uiGeneralTab.sbPoints, SIGNAL(valueChanged(int)), this, SLOT(pointsChanged()) ); connect( uiGeneralTab.cbWeight, SIGNAL(currentIndexChanged(int)), this, SLOT(weightChanged()) ); connect( uiGeneralTab.sbPercentile, SIGNAL(valueChanged(double)), this, SLOT(percentileChanged()) ); connect( uiGeneralTab.sbOrder, SIGNAL(valueChanged(int)), this, SLOT(orderChanged()) ); connect( uiGeneralTab.cbMode, SIGNAL(currentIndexChanged(int)), this, SLOT(modeChanged()) ); connect( uiGeneralTab.sbLeftValue, SIGNAL(valueChanged(double)), this, SLOT(valueChanged()) ); connect( uiGeneralTab.sbRightValue, SIGNAL(valueChanged(double)), this, SLOT(valueChanged()) ); connect( uiGeneralTab.pbRecalculate, SIGNAL(clicked()), this, SLOT(recalculateClicked()) ); connect( cbDataSourceCurve, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(dataSourceCurveChanged(QModelIndex)) ); connect( cbXDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(xDataColumnChanged(QModelIndex)) ); connect( cbYDataColumn, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(yDataColumnChanged(QModelIndex)) ); } void XYSmoothCurveDock::initGeneralTab() { DEBUG("XYSmoothCurveDock::initGeneralTab()"); //if there are more then one curve in the list, disable the tab "general" if (m_curvesList.size()==1) { uiGeneralTab.lName->setEnabled(true); uiGeneralTab.leName->setEnabled(true); uiGeneralTab.lComment->setEnabled(true); uiGeneralTab.leComment->setEnabled(true); uiGeneralTab.leName->setText(m_curve->name()); uiGeneralTab.leComment->setText(m_curve->comment()); }else { uiGeneralTab.lName->setEnabled(false); uiGeneralTab.leName->setEnabled(false); uiGeneralTab.lComment->setEnabled(false); uiGeneralTab.leComment->setEnabled(false); uiGeneralTab.leName->setText(""); uiGeneralTab.leComment->setText(""); } //show the properties of the first curve m_smoothCurve = dynamic_cast(m_curve); uiGeneralTab.cbDataSourceType->setCurrentIndex(m_smoothCurve->dataSourceType()); this->dataSourceTypeChanged(uiGeneralTab.cbDataSourceType->currentIndex()); XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, m_smoothCurve->dataSourceCurve()); XYCurveDock::setModelIndexFromAspect(cbXDataColumn, m_smoothCurve->xDataColumn()); XYCurveDock::setModelIndexFromAspect(cbYDataColumn, m_smoothCurve->yDataColumn()); uiGeneralTab.cbAutoRange->setChecked(m_smoothData.autoRange); uiGeneralTab.sbMin->setValue(m_smoothData.xRange.first()); uiGeneralTab.sbMax->setValue(m_smoothData.xRange.last()); this->autoRangeChanged(); // update list of selectable types xDataColumnChanged(cbXDataColumn->currentModelIndex()); uiGeneralTab.cbType->setCurrentIndex(m_smoothData.type); typeChanged(); // needed, when type does not change uiGeneralTab.sbPoints->setValue((int)m_smoothData.points); uiGeneralTab.cbWeight->setCurrentIndex(m_smoothData.weight); uiGeneralTab.sbPercentile->setValue(m_smoothData.percentile); uiGeneralTab.sbOrder->setValue((int)m_smoothData.order); uiGeneralTab.cbMode->setCurrentIndex(m_smoothData.mode); modeChanged(); // needed, when mode does not change uiGeneralTab.sbLeftValue->setValue(m_smoothData.lvalue); uiGeneralTab.sbRightValue->setValue(m_smoothData.rvalue); valueChanged(); this->showSmoothResult(); uiGeneralTab.chkVisible->setChecked( m_curve->isVisible() ); //Slots connect(m_smoothCurve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)), this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect(m_smoothCurve, SIGNAL(dataSourceTypeChanged(XYAnalysisCurve::DataSourceType)), this, SLOT(curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType))); connect(m_smoothCurve, SIGNAL(dataSourceCurveChanged(const XYCurve*)), this, SLOT(curveDataSourceCurveChanged(const XYCurve*))); connect(m_smoothCurve, SIGNAL(xDataColumnChanged(const AbstractColumn*)), this, SLOT(curveXDataColumnChanged(const AbstractColumn*))); connect(m_smoothCurve, SIGNAL(yDataColumnChanged(const AbstractColumn*)), this, SLOT(curveYDataColumnChanged(const AbstractColumn*))); connect(m_smoothCurve, SIGNAL(smoothDataChanged(XYSmoothCurve::SmoothData)), this, SLOT(curveSmoothDataChanged(XYSmoothCurve::SmoothData))); connect(m_smoothCurve, SIGNAL(sourceDataChanged()), this, SLOT(enableRecalculate())); } void XYSmoothCurveDock::setModel() { QList list; list<<"Folder"<<"Datapicker"<<"Worksheet"<<"CartesianPlot"<<"XYCurve"; cbDataSourceCurve->setTopLevelClasses(list); QList hiddenAspects; for (auto* curve : m_curvesList) hiddenAspects << curve; cbDataSourceCurve->setHiddenAspects(hiddenAspects); list.clear(); list<<"Folder"<<"Workbook"<<"Datapicker"<<"DatapickerCurve"<<"Spreadsheet" <<"FileDataSource"<<"Column"<<"Worksheet"<<"CartesianPlot"<<"XYFitCurve"<<"CantorWorksheet"; cbXDataColumn->setTopLevelClasses(list); cbYDataColumn->setTopLevelClasses(list); cbXDataColumn->setModel(m_aspectTreeModel); cbYDataColumn->setModel(m_aspectTreeModel); XYCurveDock::setModel(); } /*! sets the curves. The properties of the curves in the list \c list can be edited in this widget. */ void XYSmoothCurveDock::setCurves(QList list) { DEBUG("XYSmoothCurveDock::setCurves()"); m_initializing=true; m_curvesList=list; m_curve=list.first(); m_smoothCurve = dynamic_cast(m_curve); m_aspectTreeModel = new AspectTreeModel(m_curve->project()); this->setModel(); m_smoothData = m_smoothCurve->smoothData(); initGeneralTab(); initTabs(); m_initializing=false; //hide the "skip gaps" option after the curves were set ui.lLineSkipGaps->hide(); ui.chkLineSkipGaps->hide(); } //************************************************************* //**** SLOTs for changes triggered in XYFitCurveDock ***** //************************************************************* void XYSmoothCurveDock::nameChanged() { if (m_initializing) return; m_curve->setName(uiGeneralTab.leName->text()); } void XYSmoothCurveDock::commentChanged() { if (m_initializing) return; m_curve->setComment(uiGeneralTab.leComment->text()); } void XYSmoothCurveDock::dataSourceTypeChanged(int index) { XYAnalysisCurve::DataSourceType type = (XYAnalysisCurve::DataSourceType)index; if (type == XYAnalysisCurve::DataSourceSpreadsheet) { uiGeneralTab.lDataSourceCurve->hide(); cbDataSourceCurve->hide(); uiGeneralTab.lXColumn->show(); cbXDataColumn->show(); uiGeneralTab.lYColumn->show(); cbYDataColumn->show(); } else { uiGeneralTab.lDataSourceCurve->show(); cbDataSourceCurve->show(); uiGeneralTab.lXColumn->hide(); cbXDataColumn->hide(); uiGeneralTab.lYColumn->hide(); cbYDataColumn->hide(); } if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceType(type); } void XYSmoothCurveDock::dataSourceCurveChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); XYCurve* dataSourceCurve = dynamic_cast(aspect); if (m_initializing) return; for (auto* curve : m_curvesList) dynamic_cast(curve)->setDataSourceCurve(dataSourceCurve); } void XYSmoothCurveDock::xDataColumnChanged(const QModelIndex& index) { AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setXDataColumn(column); // disable types that need more data points if (column != nullptr) { if (uiGeneralTab.cbAutoRange->isChecked()) { uiGeneralTab.sbMin->setValue(column->minimum()); uiGeneralTab.sbMax->setValue(column->maximum()); } unsigned int n = 0; for (int row = 0; row < column->rowCount(); row++) if (!std::isnan(column->valueAt(row)) && !column->isMasked(row)) n++; // set maximum of sbPoints to number of columns uiGeneralTab.sbPoints->setMaximum((int)n); } } void XYSmoothCurveDock::yDataColumnChanged(const QModelIndex& index) { if (m_initializing) return; AbstractAspect* aspect = static_cast(index.internalPointer()); AbstractColumn* column = dynamic_cast(aspect); for (auto* curve : m_curvesList) dynamic_cast(curve)->setYDataColumn(column); } void XYSmoothCurveDock::autoRangeChanged() { bool autoRange = uiGeneralTab.cbAutoRange->isChecked(); m_smoothData.autoRange = autoRange; if (autoRange) { uiGeneralTab.lMin->setEnabled(false); uiGeneralTab.sbMin->setEnabled(false); uiGeneralTab.lMax->setEnabled(false); uiGeneralTab.sbMax->setEnabled(false); const AbstractColumn* xDataColumn = 0; if (m_smoothCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) xDataColumn = m_smoothCurve->xDataColumn(); else { if (m_smoothCurve->dataSourceCurve()) xDataColumn = m_smoothCurve->dataSourceCurve()->xColumn(); } if (xDataColumn) { uiGeneralTab.sbMin->setValue(xDataColumn->minimum()); uiGeneralTab.sbMax->setValue(xDataColumn->maximum()); } } else { uiGeneralTab.lMin->setEnabled(true); uiGeneralTab.sbMin->setEnabled(true); uiGeneralTab.lMax->setEnabled(true); uiGeneralTab.sbMax->setEnabled(true); } } void XYSmoothCurveDock::xRangeMinChanged() { double xMin = uiGeneralTab.sbMin->value(); m_smoothData.xRange.first() = xMin; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYSmoothCurveDock::xRangeMaxChanged() { double xMax = uiGeneralTab.sbMax->value(); m_smoothData.xRange.last() = xMax; uiGeneralTab.pbRecalculate->setEnabled(true); } void XYSmoothCurveDock::typeChanged() { nsl_smooth_type type = (nsl_smooth_type)uiGeneralTab.cbType->currentIndex(); m_smoothData.type = type; const QStandardItemModel* model = qobject_cast(uiGeneralTab.cbMode->model()); QStandardItem* pad_interp_item = model->item(nsl_smooth_pad_interp); if (type == nsl_smooth_type_moving_average || type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.lWeight->show(); uiGeneralTab.cbWeight->show(); // disable interp pad model for MA and MAL pad_interp_item->setFlags(pad_interp_item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } else { uiGeneralTab.lWeight->hide(); uiGeneralTab.cbWeight->hide(); pad_interp_item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); } if (type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.sbPoints->setSingleStep(1); uiGeneralTab.sbPoints->setMinimum(2); uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } else { uiGeneralTab.sbPoints->setSingleStep(2); uiGeneralTab.sbPoints->setMinimum(3); if (m_smoothData.mode == nsl_smooth_pad_constant) { uiGeneralTab.lRightValue->show(); uiGeneralTab.sbRightValue->show(); } } if (type == nsl_smooth_type_percentile) { uiGeneralTab.lPercentile->show(); uiGeneralTab.sbPercentile->show(); // disable interp pad model for MA and MAL pad_interp_item->setFlags(pad_interp_item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } else { uiGeneralTab.lPercentile->hide(); uiGeneralTab.sbPercentile->hide(); } if (type == nsl_smooth_type_savitzky_golay) { uiGeneralTab.lOrder->show(); uiGeneralTab.sbOrder->show(); } else { uiGeneralTab.lOrder->hide(); uiGeneralTab.sbOrder->hide(); } enableRecalculate(); } void XYSmoothCurveDock::pointsChanged() { m_smoothData.points = (unsigned int)uiGeneralTab.sbPoints->value(); // set maximum order uiGeneralTab.sbOrder->setMaximum((int)m_smoothData.points - 1); enableRecalculate(); } void XYSmoothCurveDock::weightChanged() { m_smoothData.weight = (nsl_smooth_weight_type)uiGeneralTab.cbWeight->currentIndex(); enableRecalculate(); } void XYSmoothCurveDock::percentileChanged() { m_smoothData.percentile = uiGeneralTab.sbPercentile->value(); enableRecalculate(); } void XYSmoothCurveDock::orderChanged() { m_smoothData.order = (unsigned int)uiGeneralTab.sbOrder->value(); enableRecalculate(); } void XYSmoothCurveDock::modeChanged() { m_smoothData.mode = (nsl_smooth_pad_mode)(uiGeneralTab.cbMode->currentIndex()); if (m_smoothData.mode == nsl_smooth_pad_constant) { uiGeneralTab.lLeftValue->show(); uiGeneralTab.sbLeftValue->show(); if (m_smoothData.type == nsl_smooth_type_moving_average_lagged) { uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } else { uiGeneralTab.lRightValue->show(); uiGeneralTab.sbRightValue->show(); } } else { uiGeneralTab.lLeftValue->hide(); uiGeneralTab.sbLeftValue->hide(); uiGeneralTab.lRightValue->hide(); uiGeneralTab.sbRightValue->hide(); } enableRecalculate(); } void XYSmoothCurveDock::valueChanged() { m_smoothData.lvalue = uiGeneralTab.sbLeftValue->value(); m_smoothData.rvalue = uiGeneralTab.sbRightValue->value(); enableRecalculate(); } void XYSmoothCurveDock::recalculateClicked() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); for (auto* curve : m_curvesList) dynamic_cast(curve)->setSmoothData(m_smoothData); uiGeneralTab.pbRecalculate->setEnabled(false); - emit info(i18n("Smoothing status: ") + m_smoothCurve->smoothResult().status); + emit info(i18n("Smoothing status: %1", m_smoothCurve->smoothResult().status)); QApplication::restoreOverrideCursor(); } void XYSmoothCurveDock::enableRecalculate() const { if (m_initializing) return; //no smoothing possible without the x- and y-data bool hasSourceData = false; if (m_smoothCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet) { AbstractAspect* aspectX = static_cast(cbXDataColumn->currentModelIndex().internalPointer()); AbstractAspect* aspectY = static_cast(cbYDataColumn->currentModelIndex().internalPointer()); hasSourceData = (aspectX!=0 && aspectY!=0); } else { hasSourceData = (m_smoothCurve->dataSourceCurve() != NULL); } uiGeneralTab.pbRecalculate->setEnabled(hasSourceData); } /*! * show the result and details of the smooth */ void XYSmoothCurveDock::showSmoothResult() { const XYSmoothCurve::SmoothResult& smoothResult = m_smoothCurve->smoothResult(); if (!smoothResult.available) { uiGeneralTab.teResult->clear(); return; } //const XYSmoothCurve::SmoothData& smoothData = m_smoothCurve->smoothData(); - QString str = i18n("status:") + ' ' + smoothResult.status + "
"; + QString str = i18n("status: %1", smoothResult.status) + "
"; if (!smoothResult.valid) { uiGeneralTab.teResult->setText(str); return; //result is not valid, there was an error which is shown in the status-string, nothing to show more. } if (smoothResult.elapsedTime>1000) - str += i18n("calculation time: %1 s").arg(QString::number(smoothResult.elapsedTime/1000)) + "
"; + str += i18n("calculation time: %1 s", QString::number(smoothResult.elapsedTime/1000)) + "
"; else - str += i18n("calculation time: %1 ms").arg(QString::number(smoothResult.elapsedTime)) + "
"; + str += i18n("calculation time: %1 ms", QString::number(smoothResult.elapsedTime)) + "
"; str += "

"; uiGeneralTab.teResult->setText(str); //enable the "recalculate"-button if the source data was changed since the last smooth uiGeneralTab.pbRecalculate->setEnabled(m_smoothCurve->isSourceDataChangedSinceLastRecalc()); } //************************************************************* //*********** SLOTs for changes triggered in XYCurve ********** //************************************************************* //General-Tab void XYSmoothCurveDock::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != uiGeneralTab.leName->text()) uiGeneralTab.leName->setText(aspect->name()); else if (aspect->comment() != uiGeneralTab.leComment->text()) uiGeneralTab.leComment->setText(aspect->comment()); m_initializing = false; } void XYSmoothCurveDock::curveDataSourceTypeChanged(XYAnalysisCurve::DataSourceType type) { m_initializing = true; uiGeneralTab.cbDataSourceType->setCurrentIndex(type); m_initializing = false; } void XYSmoothCurveDock::curveDataSourceCurveChanged(const XYCurve* curve) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbDataSourceCurve, curve); m_initializing = false; } void XYSmoothCurveDock::curveXDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbXDataColumn, column); m_initializing = false; } void XYSmoothCurveDock::curveYDataColumnChanged(const AbstractColumn* column) { m_initializing = true; XYCurveDock::setModelIndexFromAspect(cbYDataColumn, column); m_initializing = false; } void XYSmoothCurveDock::curveSmoothDataChanged(const XYSmoothCurve::SmoothData& smoothData) { m_initializing = true; m_smoothData = smoothData; uiGeneralTab.cbType->setCurrentIndex(m_smoothData.type); this->showSmoothResult(); m_initializing = false; } void XYSmoothCurveDock::dataChanged() { this->enableRecalculate(); } diff --git a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp index 54c2d597b..81bec3e35 100644 --- a/src/kdefrontend/matrix/MatrixFunctionDialog.cpp +++ b/src/kdefrontend/matrix/MatrixFunctionDialog.cpp @@ -1,254 +1,255 @@ /*************************************************************************** File : MatrixFunctionDialog.cpp Project : LabPlot Description : Dialog for generating matrix values from a mathematical function ------------------------------------------------------------------------ Copyright : (C) 2015-2016 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "MatrixFunctionDialog.h" #include "backend/lib/macros.h" #include "backend/matrix/Matrix.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" extern "C" { #include "backend/gsl/parser.h" } #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif /*! \class MatrixFunctionDialog \brief Dialog for generating matrix values from a mathematical function. \ingroup kdefrontend */ -MatrixFunctionDialog::MatrixFunctionDialog(Matrix* m, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_matrix(m) { +MatrixFunctionDialog::MatrixFunctionDialog(Matrix* m, QWidget* parent) : QDialog(parent), m_matrix(m) { Q_ASSERT(m_matrix); - setWindowTitle(i18n("Function values")); + setWindowTitle(i18nc("@title:window", "Function values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); QStringList vars; vars << "x" << "y"; ui.teEquation->setVariables(vars); ui.teEquation->setFocus(); ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); QString info = '[' + QString::number(m_matrix->xStart()) + ", " + QString::number(m_matrix->xEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->columnCount()); ui.lXInfo->setText(info); info = '[' + QString::number(m_matrix->yStart()) + ", " + QString::number(m_matrix->yEnd()) + "], " + i18np("%1 value", "%1 values", m_matrix->rowCount()); ui.lYInfo->setText(info); ui.teEquation->setPlainText(m_matrix->formula()); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout_2->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MatrixFunctionDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &MatrixFunctionDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &MatrixFunctionDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate function values")); connect(ui.teEquation, &ExpressionTextEdit::expressionChanged, this, &MatrixFunctionDialog::checkValues); connect(ui.tbConstants, &QToolButton::clicked, this, &MatrixFunctionDialog::showConstants); connect(ui.tbFunctions, &QToolButton::clicked, this, &MatrixFunctionDialog::showFunctions); connect(m_okButton, &QPushButton::clicked, this, &MatrixFunctionDialog::generate); resize(QSize(300,0).expandedTo(minimumSize())); } void MatrixFunctionDialog::checkValues() { if (!ui.teEquation->isValid()) { m_okButton->setEnabled(false); return; } m_okButton->setEnabled(true); } void MatrixFunctionDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, &ConstantsWidget::constantSelected, this, &MatrixFunctionDialog::insertConstant); connect(&constants, &ConstantsWidget::constantSelected, &menu, &QMenu::close); connect(&constants, &ConstantsWidget::canceled, &menu, &QMenu::close); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void MatrixFunctionDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, &FunctionsWidget::functionSelected, this, &MatrixFunctionDialog::insertFunction); connect(&functions, &FunctionsWidget::functionSelected, &menu, &QMenu::close); connect(&functions, &FunctionsWidget::canceled, &menu, &QMenu::close); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); menu.exec(ui.tbFunctions->mapToGlobal(pos)); } void MatrixFunctionDialog::insertFunction(const QString& str) { + //TODO: not all functions have only one argument ui.teEquation->insertPlainText(str + "(x)"); } void MatrixFunctionDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } /* task class for parallel fill (not used) */ class GenerateValueTask : public QRunnable { public: GenerateValueTask(int startCol, int endCol, QVector>& matrixData, double xStart, double yStart, double xStep, double yStep, char* func): m_startCol(startCol), m_endCol(endCol), m_matrixData(matrixData), m_xStart(xStart), m_yStart(yStart), m_xStep(xStep), m_yStep(yStep), m_func(func) { }; void run() { const int rows = m_matrixData[m_startCol].size(); double x = m_xStart; double y = m_yStart; DEBUG("FILL col"<name())); //TODO: data types QVector>* new_data = static_cast>*>(m_matrix->data()); QByteArray funcba = ui.teEquation->toPlainText().toLocal8Bit(); char* func = funcba.data(); // check if rows or cols == 1 double diff = m_matrix->xEnd() - m_matrix->xStart(); double xStep = 0.0; if (m_matrix->columnCount() > 1) xStep = diff/double(m_matrix->columnCount() - 1); diff = m_matrix->yEnd() - m_matrix->yStart(); double yStep = 0.0; if (m_matrix->rowCount() > 1) yStep = diff/double(m_matrix->rowCount() - 1); #ifndef NDEBUG QElapsedTimer timer; timer.start(); #endif //TODO: too slow because every parser thread needs an own symbol_table // idea: use pool->maxThreadCount() symbol tables and reuse them? /* double yStart = m_matrix->yStart(); const int cols = m_matrix->columnCount(); QThreadPool* pool = QThreadPool::globalInstance(); int range = ceil(double(cols)/pool->maxThreadCount()); DEBUG("Starting" << pool->maxThreadCount() << "threads. cols =" << cols << ": range =" << range); for (int i = 0; i < pool->maxThreadCount(); ++i) { const int start = i*range; int end = (i+1)*range; if (end > cols) end = cols; qDebug() << "start/end: " << start << end; const double xStart = m_matrix->xStart() + xStep*start; GenerateValueTask* task = new GenerateValueTask(start, end, new_data, xStart, yStart, xStep, yStep, func); task->setAutoDelete(false); pool->start(task); } pool->waitForDone(); */ double x = 0, y = 0; parser_var vars[] = {{"x", x}, {"y", y}}; for (int col = 0; col < m_matrix->columnCount(); ++col) { vars[0].value = x; for (int row = 0; row < m_matrix->rowCount(); ++row) { vars[1].value = y; (new_data->operator[](col))[row] = parse_with_vars(func, vars, 2); y += yStep; } y = m_matrix->yStart(); x += xStep; } // Timing DEBUG("elapsed time =" << timer.elapsed() << "ms"); m_matrix->setFormula(ui.teEquation->toPlainText()); m_matrix->setData(new_data); m_matrix->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/matrix/MatrixFunctionDialog.h b/src/kdefrontend/matrix/MatrixFunctionDialog.h index 6f32e478e..f6c4b8823 100644 --- a/src/kdefrontend/matrix/MatrixFunctionDialog.h +++ b/src/kdefrontend/matrix/MatrixFunctionDialog.h @@ -1,55 +1,55 @@ /*************************************************************************** File : MatrixFunctionDialog.h Project : LabPlot Description : Dialog for generating matrix values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2015 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 MATRIXFUNCTIONDIALOG_H #define MATRIXFUNCTIONDIALOG_H #include "ui_matrixfunctionwidget.h" #include class Matrix; class QPushButton; class MatrixFunctionDialog : public QDialog { Q_OBJECT public: - explicit MatrixFunctionDialog(Matrix*, QWidget* parent = 0, Qt::WFlags = 0); + explicit MatrixFunctionDialog(Matrix*, QWidget* parent = 0); private: Ui::MatrixFunctionWidget ui; Matrix* m_matrix; QPushButton* m_okButton; private slots: void generate(); void checkValues(); void showConstants(); void showFunctions(); void insertFunction(const QString&); void insertConstant(const QString&); }; #endif diff --git a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp index 513f80aa8..4b5bfb75c 100644 --- a/src/kdefrontend/spreadsheet/DropValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/DropValuesDialog.cpp @@ -1,346 +1,346 @@ /*************************************************************************** File : DropValuesDialog.cpp Project : LabPlot Description : Dialog for droping and masking values in columns -------------------------------------------------------------------- Copyright : (C) 2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "DropValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include #include #include /*! \class DropValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ -DropValuesDialog::DropValuesDialog(Spreadsheet* s, bool mask, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), +DropValuesDialog::DropValuesDialog(Spreadsheet* s, bool mask, QWidget* parent) : QDialog(parent), m_spreadsheet(s), m_mask(mask) { - setWindowTitle(i18n("Drop values")); + setWindowTitle(i18nc("@title:window", "Drop Values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - ui.cbOperator->addItem(i18n("equal to")); - ui.cbOperator->addItem(i18n("between (including end points)")); - ui.cbOperator->addItem(i18n("between (excluding end points)")); - ui.cbOperator->addItem(i18n("greater then")); - ui.cbOperator->addItem(i18n("greater then or equal to")); - ui.cbOperator->addItem(i18n("lesser then")); - ui.cbOperator->addItem(i18n("lesser then or equal to")); + ui.cbOperator->addItem(i18n("Equal To")); + ui.cbOperator->addItem(i18n("Between (Including End Points)")); + ui.cbOperator->addItem(i18n("Between (Excluding End Points)")); + ui.cbOperator->addItem(i18n("Greater Than")); + ui.cbOperator->addItem(i18n("Greater Than Or Equal To")); + ui.cbOperator->addItem(i18n("Lesser Than")); + ui.cbOperator->addItem(i18n("Lesser Than Or Equal To")); ui.leValue1->setValidator( new QDoubleValidator(ui.leValue1) ); ui.leValue2->setValidator( new QDoubleValidator(ui.leValue2) ); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.horizontalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &DropValuesDialog::close); if (m_mask) { m_okButton->setText(i18n("&Mask")); m_okButton->setToolTip(i18n("Mask values in the specified region")); ui.lMode->setText(i18n("Mask values")); - setWindowTitle(i18n("Mask values")); + setWindowTitle(i18nc("@title:window", "Mask Values")); } else { m_okButton->setText(i18n("&Drop")); m_okButton->setToolTip(i18n("Drop values in the specified region")); } connect(ui.cbOperator, static_cast(&QComboBox::currentIndexChanged), this, &DropValuesDialog::operatorChanged ); connect(m_okButton, &QPushButton::clicked, this, &DropValuesDialog::okClicked); connect(btnBox, &QDialogButtonBox::accepted, this, &DropValuesDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &DropValuesDialog::reject); resize( QSize(400,0).expandedTo(minimumSize()) ); operatorChanged(0); } void DropValuesDialog::setColumns(QVector columns) { m_columns = columns; } void DropValuesDialog::operatorChanged(int index) const { bool value2 = (index==1) || (index==2); ui.lMin->setVisible(value2); ui.lMax->setVisible(value2); ui.lAnd->setVisible(value2); ui.leValue2->setVisible(value2); } void DropValuesDialog::okClicked() const { if (m_mask) maskValues(); else dropValues(); } //TODO: m_column->setMasked() is slow, we need direct access to the masked-container -> redesign class MaskValuesTask : public QRunnable { public: MaskValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; } void run() { m_column->setSuppressDataChangedSignal(true); bool changed = false; QVector* data = static_cast* >(m_column->data()); //equal to if (m_operator == 0) { for (int i=0; isize(); ++i) { if (data->at(i) == m_value1) { m_column->setMasked(i, true); changed = true; } } } //between (including end points) else if (m_operator == 1) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1 && data->at(i) <= m_value2) { m_column->setMasked(i, true); changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1 && data->at(i) < m_value2) { m_column->setMasked(i, true); changed = true; } } } //greater than else if (m_operator == 3) { for (int i=0; isize(); ++i) { if (data->at(i) > m_value1) { m_column->setMasked(i, true); changed = true; } } } //greater than or equal to else if (m_operator == 4) { for (int i=0; isize(); ++i) { if (data->at(i) >= m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser than else if (m_operator == 5) { for (int i=0; isize(); ++i) { if (data->at(i) < m_value1) { m_column->setMasked(i, true); changed = true; } } } //lesser than or equal to else if (m_operator == 6) { for (int i=0; isize(); ++i) { if (data->at(i) <= m_value1) { m_column->setMasked(i, true); changed = true; } } } m_column->setSuppressDataChangedSignal(false); if (changed) m_column->setChanged(); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; class DropValuesTask : public QRunnable { public: DropValuesTask(Column* col, int op, double value1, double value2){ m_column = col; m_operator = op; m_value1 = value1; m_value2 = value2; } void run() { bool changed = false; QVector* data = static_cast* >(m_column->data()); QVector new_data(*data); //equal to if (m_operator == 0) { for (int i=0; i= m_value1 && new_data[i] <= m_value2) { new_data[i] = NAN; changed = true; } } } //between (excluding end points) else if (m_operator == 2) { for (int i=0; i m_value1 && new_data[i] < m_value2) { new_data[i] = NAN; changed = true; } } } //greater than else if (m_operator == 3) { for (int i=0; i m_value1) { new_data[i] = NAN; changed = true; } } } //greater than or equal to else if (m_operator == 4) { for (int i=0; i= m_value1) { new_data[i] = NAN; changed = true; } } } //lesser than else if (m_operator == 5) { for (int i=0; ireplaceValues(0, new_data); } private: Column* m_column; int m_operator; double m_value1; double m_value2; }; void DropValuesDialog::maskValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: mask values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); for(Column* col: m_columns) { MaskValuesTask* task = new MaskValuesTask(col, op, value1, value2); task->run(); //TODO: writing to the undo-stack in Column::setMasked() is not tread-safe -> redesign // QThreadPool::globalInstance()->start(task); } //wait until all columns were processed // QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } void DropValuesDialog::dropValues() const { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18n("%1: drop values", m_spreadsheet->name())); const int op = ui.cbOperator->currentIndex(); const double value1 = ui.leValue1->text().toDouble(); const double value2 = ui.leValue2->text().toDouble(); for(Column* col: m_columns) { DropValuesTask* task = new DropValuesTask(col, op, value1, value2); QThreadPool::globalInstance()->start(task); } //wait until all columns were processed QThreadPool::globalInstance()->waitForDone(); m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/DropValuesDialog.h b/src/kdefrontend/spreadsheet/DropValuesDialog.h index 5ce039276..0e0d8910b 100644 --- a/src/kdefrontend/spreadsheet/DropValuesDialog.h +++ b/src/kdefrontend/spreadsheet/DropValuesDialog.h @@ -1,60 +1,60 @@ /*************************************************************************** File : DropValuesDialog.h Project : LabPlot Description : Dialog for droping and masking values in columns -------------------------------------------------------------------- Copyright : (C) 2015 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 DROPVALUESDIALOG_H #define DROPVALUESDIALOG_H #include "ui_dropvalueswidget.h" #include class Column; class Spreadsheet; class QPushButton; class DropValuesDialog : public QDialog { Q_OBJECT public: - explicit DropValuesDialog(Spreadsheet* s, bool mask = false, QWidget* parent = 0, Qt::WFlags fl = 0); + explicit DropValuesDialog(Spreadsheet* s, bool mask = false, QWidget* parent = 0); void setColumns(QVector); private: Ui::DropValuesWidget ui; QVector m_columns; Spreadsheet* m_spreadsheet; bool m_mask; void dropValues() const; void maskValues() const; QPushButton* m_okButton; private slots: void operatorChanged(int) const; void okClicked() const; }; #endif diff --git a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp index 87c8b0c23..7ef0278be 100644 --- a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.cpp @@ -1,181 +1,181 @@ /*************************************************************************** File : EquidistantValuesDialog.cpp Project : LabPlot Description : Dialog for generating equidistant numbers -------------------------------------------------------------------- Copyright : (C) 2014 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "EquidistantValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include /*! \class EquidistantValuesDialog \brief Dialog for equidistant values. \ingroup kdefrontend */ -EquidistantValuesDialog::EquidistantValuesDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { +EquidistantValuesDialog::EquidistantValuesDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), m_spreadsheet(s) { - setWindowTitle(i18n("Equidistant values")); + setWindowTitle(i18nc("@title:window", "Equidistant Values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.cbType->addItem(i18n("Number")); ui.cbType->addItem(i18n("Increment")); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &EquidistantValuesDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &EquidistantValuesDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &EquidistantValuesDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate equidistant values")); ui.leFrom->setClearButtonEnabled(true); ui.leTo->setClearButtonEnabled(true); ui.leIncrement->setClearButtonEnabled(true); ui.leNumber->setClearButtonEnabled(true); ui.leFrom->setValidator( new QDoubleValidator(ui.leFrom) ); ui.leTo->setValidator( new QDoubleValidator(ui.leTo) ); ui.leIncrement->setValidator( new QDoubleValidator(ui.leIncrement) ); ui.leNumber->setValidator( new QIntValidator(ui.leNumber) ); ui.leFrom->setText("1"); ui.leTo->setText("100"); ui.leIncrement->setText("1"); connect( ui.cbType, static_cast(&QComboBox::currentIndexChanged), this, &EquidistantValuesDialog::typeChanged); connect( ui.leFrom, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); connect( ui.leTo, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); connect( ui.leNumber, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); connect( ui.leIncrement, &QLineEdit::textChanged, this, &EquidistantValuesDialog::checkValues); connect(m_okButton, &QPushButton::clicked, this, &EquidistantValuesDialog::generate); //generated data the default this->typeChanged(0); resize( QSize(300,0).expandedTo(minimumSize()) ); } void EquidistantValuesDialog::setColumns(const QVector& columns) { m_columns = columns; ui.leNumber->setText( QString::number(m_columns.first()->rowCount()) ); } void EquidistantValuesDialog::typeChanged(int index) { if (index==0) { //fixed number ui.lIncrement->hide(); ui.leIncrement->hide(); ui.lNumber->show(); ui.leNumber->show(); } else { //fixed increment ui.lIncrement->show(); ui.leIncrement->show(); ui.lNumber->hide(); ui.leNumber->hide(); } } void EquidistantValuesDialog::checkValues() { if (ui.leFrom->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leTo->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.cbType->currentIndex() == 0) { if (ui.leNumber->text().simplified().isEmpty() || ui.leNumber->text().simplified().toInt()==0) { m_okButton->setEnabled(false); return; } } else { if (ui.leIncrement->text().simplified().isEmpty() || qFuzzyIsNull(ui.leIncrement->text().simplified().toDouble())) { m_okButton->setEnabled(false); return; } } m_okButton->setEnabled(true); } void EquidistantValuesDialog::generate() { Q_ASSERT(m_spreadsheet); WAIT_CURSOR; m_spreadsheet->beginMacro(i18np("%1: fill column with equidistant numbers", "%1: fill columns with equidistant numbers", m_spreadsheet->name(), m_columns.size())); double start = ui.leFrom->text().toDouble(); double end = ui.leTo->text().toDouble(); int number; double dist; if (ui.cbType->currentIndex()==0) { //fixed number number = ui.leNumber->text().toInt(); if (number!=1) dist = (end - start)/ (number - 1); else dist = 0; } else { //fixed increment dist = ui.leIncrement->text().toDouble(); number = (end-start)/dist + 1; } if (m_spreadsheet->rowCount()setRowCount(number); for (auto* col : m_columns) { col->setSuppressDataChangedSignal(true); if (m_spreadsheet->rowCount()>number) col->clear(); for (int i=0; isetValueAt(i, start + dist*i); } col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.h b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.h index 79743d9b9..36aa54b3e 100644 --- a/src/kdefrontend/spreadsheet/EquidistantValuesDialog.h +++ b/src/kdefrontend/spreadsheet/EquidistantValuesDialog.h @@ -1,57 +1,57 @@ /*************************************************************************** File : EquidistantValuesDialog.h Project : LabPlot Description : Dialog for generating equidistant values -------------------------------------------------------------------- Copyright : (C) 2014 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 EQUIDISTANTVALUESDIALOG_H #define EQUIDISTANTVALUESDIALOG_H #include "ui_equidistantvalueswidget.h" #include class Column; class Spreadsheet; class QPushButton; class EquidistantValuesDialog : public QDialog { Q_OBJECT public: - explicit EquidistantValuesDialog(Spreadsheet* s, QWidget* parent = 0, Qt::WFlags fl = 0); + explicit EquidistantValuesDialog(Spreadsheet* s, QWidget* parent = 0); void setColumns(const QVector&); private: Ui::EquidistantValuesWidget ui; QVector m_columns; Spreadsheet* m_spreadsheet; QPushButton* m_okButton; private slots: void generate(); void typeChanged(int index); void checkValues(); }; #endif diff --git a/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp b/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp index 493d2ddf4..3fa63744c 100644 --- a/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp +++ b/src/kdefrontend/spreadsheet/ExportSpreadsheetDialog.cpp @@ -1,477 +1,477 @@ /*************************************************************************** File : ExportSpreadsheetDialog.cpp Project : LabPlot Description : export spreadsheet dialog -------------------------------------------------------------------- Copyright : (C) 2014-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "ExportSpreadsheetDialog.h" #include "ui_exportspreadsheetwidget.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class ExportSpreadsheetDialog \brief Dialog for exporting a spreadsheet to a file. \ingroup kdefrontend */ ExportSpreadsheetDialog::ExportSpreadsheetDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ExportSpreadsheetWidget()), m_showOptions(true), m_matrixMode(false), m_format(Format::ASCII) { ui->setupUi(this); ui->gbOptions->hide(); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_showOptionsButton = new QPushButton; connect(btnBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonClicked(QAbstractButton*))); btnBox->addButton(m_showOptionsButton, QDialogButtonBox::ActionRole); ui->verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui->leFileName->setCompleter(completer); ui->cbFormat->addItem("ASCII"); ui->cbFormat->addItem("Binary"); ui->cbFormat->addItem("LaTeX"); ui->cbFormat->addItem("FITS"); ui->cbSeparator->addItem("TAB"); ui->cbSeparator->addItem("SPACE"); ui->cbSeparator->addItem(","); ui->cbSeparator->addItem(";"); ui->cbSeparator->addItem(":"); ui->cbSeparator->addItem(",TAB"); ui->cbSeparator->addItem(";TAB"); ui->cbSeparator->addItem(":TAB"); ui->cbSeparator->addItem(",SPACE"); ui->cbSeparator->addItem(";SPACE"); ui->cbSeparator->addItem(":SPACE"); - ui->cbLaTeXExport->addItem(i18n("Export spreadsheet")); - ui->cbLaTeXExport->addItem(i18n("Export selection")); + ui->cbLaTeXExport->addItem(i18n("Export Spreadsheet")); + ui->cbLaTeXExport->addItem(i18n("Export Selection")); ui->bOpen->setIcon( QIcon::fromTheme("document-open") ); ui->leFileName->setFocus(); connect(btnBox, &QDialogButtonBox::accepted, this, &ExportSpreadsheetDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &ExportSpreadsheetDialog::reject); connect(ui->bOpen, &QPushButton::clicked, this, &ExportSpreadsheetDialog::selectFile); connect(ui->leFileName, &QLineEdit::textChanged, this, &ExportSpreadsheetDialog::fileNameChanged ); connect(m_showOptionsButton, &QPushButton::clicked, this, &ExportSpreadsheetDialog::toggleOptions); connect(ui->cbFormat, static_cast(&QComboBox::currentIndexChanged), this, &ExportSpreadsheetDialog::formatChanged); connect(ui->cbExportToFITS, static_cast(&QComboBox::currentIndexChanged), this, &ExportSpreadsheetDialog::fitsExportToChanged); - setWindowTitle(i18n("Export spreadsheet")); + setWindowTitle(i18nc("@title:window", "Export Spreadsheet")); setWindowIcon(QIcon::fromTheme("document-export-database")); QTimer::singleShot(0, this, &ExportSpreadsheetDialog::loadSettings); } void ExportSpreadsheetDialog::loadSettings() { //restore saved settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); ui->cbFormat->setCurrentIndex(conf.readEntry("Format", 0)); ui->chkExportHeader->setChecked(conf.readEntry("Header", true)); ui->cbSeparator->setCurrentItem(conf.readEntry("Separator", "TAB")); ui->chkHeaders->setChecked(conf.readEntry("LaTeXHeaders", true)); ui->chkGridLines->setChecked(conf.readEntry("LaTeXGridLines", true)); ui->chkCaptions->setChecked(conf.readEntry("LaTeXCaptions", true)); ui->chkEmptyRows->setChecked(conf.readEntry("LaTeXSkipEmpty", false)); ui->cbLaTeXExport->setCurrentIndex(conf.readEntry("ExportOnly", 0)); ui->chkMatrixHHeader->setChecked(conf.readEntry("MatrixHorizontalHeader", true)); ui->chkMatrixVHeader->setChecked(conf.readEntry("MatrixVerticalHeader", true)); ui->chkMatrixVHeader->setChecked(conf.readEntry("FITSSpreadsheetColumnsUnits", true)); ui->cbExportToFITS->setCurrentIndex(conf.readEntry("FITSTo", 0)); m_showOptions = conf.readEntry("ShowOptions", false); ui->gbOptions->setVisible(m_showOptions); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); } ExportSpreadsheetDialog::~ExportSpreadsheetDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Header", ui->chkExportHeader->isChecked()); conf.writeEntry("Separator", ui->cbSeparator->currentIndex()); conf.writeEntry("ShowOptions", m_showOptions); conf.writeEntry("LaTeXHeaders", ui->chkHeaders->isChecked()); conf.writeEntry("LaTeXGridLines", ui->chkGridLines->isChecked()); conf.writeEntry("LaTeXCaptions", ui->chkCaptions->isChecked()); conf.writeEntry("LaTeXSkipEmpty", ui->chkEmptyRows->isChecked()); conf.writeEntry("ExportOnly", ui->cbLaTeXExport->currentIndex()); conf.writeEntry("MatrixVerticalHeader", ui->chkMatrixVHeader->isChecked()); conf.writeEntry("MatrixHorizontalHeader", ui->chkMatrixHHeader->isChecked()); conf.writeEntry("FITSTo", ui->cbExportToFITS->currentIndex()); conf.writeEntry("FITSSpreadsheetColumnsUnits", ui->chkColumnsAsUnits->isChecked()); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ExportSpreadsheetDialog::setFileName(const QString& name) { KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); QString dir = conf.readEntry("LastDir", ""); if (dir.isEmpty()) dir = QDir::homePath(); ui->leFileName->setText(dir + QDir::separator() + name); this->formatChanged(ui->cbFormat->currentIndex()); } void ExportSpreadsheetDialog::fitsExportToChanged(int idx) { if (idx == 0) { ui->chkColumnsAsUnits->hide(); ui->lColumnAsUnits->hide(); } else { if (!m_matrixMode) { ui->chkColumnsAsUnits->show(); ui->lColumnAsUnits->show(); } } } void ExportSpreadsheetDialog::setMatrixMode(bool b) { if (b) { - setWindowTitle(i18n("Export matrix")); + setWindowTitle(i18nc("@title:window", "Export Matrix")); ui->lExportHeader->hide(); ui->chkExportHeader->hide(); ui->lEmptyRows->hide(); ui->chkEmptyRows->hide(); if (ui->cbFormat->currentIndex() != 3) { ui->chkMatrixHHeader->show(); ui->chkMatrixVHeader->show(); ui->lMatrixHHeader->show(); ui->lMatrixVHeader->show(); } ui->lHeader->hide(); ui->chkHeaders->hide(); ui->cbLaTeXExport->setItemText(0,i18n("Export matrix")); ui->cbExportToFITS->setCurrentIndex(0); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); m_matrixMode = b; } } QString ExportSpreadsheetDialog::path() const { return ui->leFileName->text(); } int ExportSpreadsheetDialog::exportToFits() const { return ui->cbExportToFITS->currentIndex(); } bool ExportSpreadsheetDialog::exportHeader() const { return ui->chkExportHeader->isChecked(); } bool ExportSpreadsheetDialog::captions() const { return ui->chkCaptions->isChecked(); } bool ExportSpreadsheetDialog::exportLatexHeader() const { return ui->chkHeaders->isChecked(); } bool ExportSpreadsheetDialog::gridLines() const { return ui->chkGridLines->isChecked(); } bool ExportSpreadsheetDialog::skipEmptyRows() const { return ui->chkEmptyRows->isChecked(); } bool ExportSpreadsheetDialog::exportSelection() const { return ui->cbLaTeXExport->currentIndex() == 1; } bool ExportSpreadsheetDialog::entireSpreadheet() const { return ui->cbLaTeXExport->currentIndex() == 0; } bool ExportSpreadsheetDialog::matrixHorizontalHeader() const { return ui->chkMatrixHHeader->isChecked(); } bool ExportSpreadsheetDialog::matrixVerticalHeader() const { return ui->chkMatrixVHeader->isChecked(); } bool ExportSpreadsheetDialog::commentsAsUnitsFits() const { return ui->chkColumnsAsUnits->isChecked(); } QString ExportSpreadsheetDialog::separator() const { return ui->cbSeparator->currentText(); } void ExportSpreadsheetDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) okClicked(); else if (button == m_cancelButton) { reject(); } } void ExportSpreadsheetDialog::setExportToImage(bool possible) { if (!possible) { ui->cbExportToFITS->setCurrentIndex(1); ui->cbExportToFITS->setItemData(0, 0, Qt::UserRole - 1); } } //SLOTS void ExportSpreadsheetDialog::okClicked() { if (format() != FITS) if ( QFile::exists(ui->leFileName->text()) ) { int r=KMessageBox::questionYesNo(this, i18n("The file already exists. Do you really want to overwrite it?"), i18n("Export")); if (r==KMessageBox::No) return; } KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Header", ui->chkExportHeader->isChecked()); conf.writeEntry("Separator", ui->cbSeparator->currentText()); QString path = ui->leFileName->text(); if (!path.isEmpty()) { QString dir = conf.readEntry("LastDir", ""); ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastDir", newDir); } } accept(); } /*! Shows/hides the GroupBox with export options in this dialog. */ void ExportSpreadsheetDialog::toggleOptions() { m_showOptions = !m_showOptions; ui->gbOptions->setVisible(m_showOptions); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); //resize the dialog resize(layout()->minimumSize()); layout()->activate(); resize( QSize(this->width(),0).expandedTo(minimumSize()) ); } /*! opens a file dialog and lets the user select the file. */ void ExportSpreadsheetDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ExportSpreadsheetDialog"); QString dir = conf.readEntry("LastDir", ""); QString format; if (ui->cbFormat->currentIndex() == 0) format = i18n("Text files (*.txt *.dat *.csv)"); else if (ui->cbFormat->currentIndex() == 1) format = i18n("Binary files (*.*)"); else if (ui->cbFormat->currentIndex() == 2) format = i18n("LaTeX files (*.tex)"); else format = i18n("FITS files (*.fits *.fit *.fts)"); const QString path = QFileDialog::getSaveFileName(this, i18n("Export to file"), dir, format); if (!path.isEmpty()) { ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastDir", newDir); } } } /*! called when the output format was changed. Adjusts the extension for the specified file. */ void ExportSpreadsheetDialog::formatChanged(int index) { QStringList extensions; extensions << ".txt" << ".bin" << ".tex" << ".fits"; QString path = ui->leFileName->text(); int i = path.indexOf("."); if (index != 1) { if (i==-1) path = path + extensions.at(index); else path=path.left(i) + extensions.at(index); } if (ui->cbFormat->currentIndex() == 2) { ui->cbSeparator->hide(); ui->lSeparator->hide(); ui->chkCaptions->show(); ui->chkGridLines->show(); ui->lExportArea->show(); ui->lGridLines->show(); ui->lCaptions->show(); ui->cbLaTeXExport->show(); if (!m_matrixMode) { ui->lHeader->show(); ui->chkHeaders->show(); ui->lEmptyRows->show(); ui->chkEmptyRows->show(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); } else { ui->lMatrixHHeader->show(); ui->lMatrixVHeader->show(); ui->chkMatrixHHeader->show(); ui->chkMatrixVHeader->show(); } ui->cbExportToFITS->hide(); ui->lExportToFITS->hide(); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); //FITS } else if(ui->cbFormat->currentIndex() == 3) { ui->lCaptions->hide(); ui->lEmptyRows->hide(); ui->lExportArea->hide(); ui->lGridLines->hide(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->lSeparator->hide(); ui->lHeader->hide(); ui->chkEmptyRows->hide(); ui->chkHeaders->hide(); ui->chkExportHeader->hide(); ui->lExportHeader->hide(); ui->chkGridLines->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); ui->chkCaptions->hide(); ui->cbLaTeXExport->hide(); ui->cbSeparator->hide(); ui->cbExportToFITS->show(); ui->lExportToFITS->show(); if (!m_matrixMode) { if (ui->cbExportToFITS->currentIndex() == 1) { ui->lColumnAsUnits->show(); ui->chkColumnsAsUnits->show(); } } } else { ui->cbSeparator->show(); ui->lSeparator->show(); ui->chkCaptions->hide(); ui->chkEmptyRows->hide(); ui->chkGridLines->hide(); ui->lEmptyRows->hide(); ui->lExportArea->hide(); ui->lGridLines->hide(); ui->lCaptions->hide(); ui->cbLaTeXExport->hide(); ui->lMatrixHHeader->hide(); ui->lMatrixVHeader->hide(); ui->chkMatrixHHeader->hide(); ui->chkMatrixVHeader->hide(); ui->lHeader->hide(); ui->chkHeaders->hide(); ui->cbExportToFITS->hide(); ui->lExportToFITS->hide(); ui->lColumnAsUnits->hide(); ui->chkColumnsAsUnits->hide(); } if (!m_matrixMode) { ui->chkExportHeader->show(); ui->lExportHeader->show(); } else { ui->chkExportHeader->hide(); ui->lExportHeader->hide(); } if (ui->cbFormat->currentIndex() == 3) { ui->chkExportHeader->hide(); ui->lExportHeader->hide(); } setFormat(static_cast(index)); ui->leFileName->setText(path); } void ExportSpreadsheetDialog::setExportSelection(bool enable) { if (!enable) { const QStandardItemModel* areaToExportModel = qobject_cast(ui->cbLaTeXExport->model()); QStandardItem* item = areaToExportModel->item(1); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled)); } } void ExportSpreadsheetDialog::setFormat(Format format) { m_format = format; } void ExportSpreadsheetDialog::setExportTo(const QStringList &to) { ui->cbExportToFITS->addItems(to); } ExportSpreadsheetDialog::Format ExportSpreadsheetDialog::format() const { return m_format; } void ExportSpreadsheetDialog::fileNameChanged(const QString& name) { m_okButton->setEnabled(!name.simplified().isEmpty()); } diff --git a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp index 095283ed5..2524577ef 100644 --- a/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/FunctionValuesDialog.cpp @@ -1,366 +1,367 @@ /*************************************************************************** File : FunctionValuesDialog.cpp Project : LabPlot Description : Dialog for generating values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2014-2015 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "FunctionValuesDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/column/Column.h" #include "backend/core/Project.h" #include "backend/gsl/ExpressionParser.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include "kdefrontend/widgets/ConstantsWidget.h" #include "kdefrontend/widgets/FunctionsWidget.h" #include #include #include #include #include #include #include #include /*! \class FunctionValuesDialog \brief Dialog for generating values from a mathematical function. \ingroup kdefrontend */ -FunctionValuesDialog::FunctionValuesDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { +FunctionValuesDialog::FunctionValuesDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), m_spreadsheet(s) { Q_ASSERT(s != nullptr); - setWindowTitle(i18n("Function values")); + setWindowTitle(i18nc("@title:window", "Function Values")); ui.setupUi(this); setAttribute(Qt::WA_DeleteOnClose); ui.tbConstants->setIcon( QIcon::fromTheme("labplot-format-text-symbol") ); ui.tbConstants->setIcon( QIcon::fromTheme("format-text-symbol") ); ui.tbFunctions->setIcon( QIcon::fromTheme("preferences-desktop-font") ); ui.teEquation->setMaximumHeight(QLineEdit().sizeHint().height()*2); ui.teEquation->setFocus(); m_topLevelClasses<<"Folder"<<"Workbook"<<"Spreadsheet"<<"FileDataSource"<<"Column"; m_selectableClasses<<"Column"; // needed for buggy compiler #if __cplusplus < 201103L m_aspectTreeModel = std::auto_ptr(new AspectTreeModel(m_spreadsheet->project())); #else m_aspectTreeModel = std::unique_ptr(new AspectTreeModel(m_spreadsheet->project())); #endif m_aspectTreeModel->setSelectableAspects(m_selectableClasses); m_aspectTreeModel->enableNumericColumnsOnly(true); m_aspectTreeModel->enableNonEmptyNumericColumnsOnly(true); ui.bAddVariable->setIcon(QIcon::fromTheme("list-add")); ui.bAddVariable->setToolTip(i18n("Add new variable")); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); connect(btnBox, &QDialogButtonBox::accepted, this, &FunctionValuesDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &FunctionValuesDialog::reject); m_okButton->setText(i18n("&Generate")); m_okButton->setToolTip(i18n("Generate function values")); connect( ui.bAddVariable, SIGNAL(pressed()), this, SLOT(addVariable()) ); connect( ui.teEquation, SIGNAL(expressionChanged()), this, SLOT(checkValues()) ); connect( ui.tbConstants, SIGNAL(clicked()), this, SLOT(showConstants()) ); connect( ui.tbFunctions, SIGNAL(clicked()), this, SLOT(showFunctions()) ); connect(m_okButton, &QPushButton::clicked, this, &FunctionValuesDialog::generate); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(300, 0).expandedTo(minimumSize())); } FunctionValuesDialog::~FunctionValuesDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "FunctionValuesDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void FunctionValuesDialog::setColumns(QVector columns) { m_columns = columns; ui.teEquation->setPlainText(m_columns.first()->formula()); const QStringList& variableNames = m_columns.first()->formulaVariableNames(); if (!variableNames.size()) { //no formula was used for this column -> add the first variable "x" addVariable(); m_variableNames[0]->setText("x"); } else { //formula and variables are available const QStringList& columnPathes = m_columns.first()->formulaVariableColumnPathes(); //add all available variables and select the corresponding columns const QVector cols = m_spreadsheet->project()->children("Column", AbstractAspect::Recursive); for (int i = 0; i < variableNames.size(); ++i) { addVariable(); m_variableNames[i]->setText(variableNames.at(i)); for (const auto* aspect : cols) { if (aspect->path() == columnPathes.at(i)) { const AbstractColumn* column = dynamic_cast(aspect); if (column) m_variableDataColumns[i]->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(column)); else m_variableDataColumns[i]->setCurrentModelIndex(QModelIndex()); break; } } } } } /*! check the user input and enables/disables the Ok-button depending on the correctness of the input */ void FunctionValuesDialog::checkValues() { //check whether the formula syntax is correct if (!ui.teEquation->isValid()) { m_okButton->setEnabled(false); return; } //check whether for the variables where a name was provided also a column was selected. for (int i = 0; i < m_variableDataColumns.size(); ++i) { if (m_variableNames.at(i)->text().simplified().isEmpty()) continue; TreeViewComboBox* cb = m_variableDataColumns.at(i); AbstractAspect* aspect = static_cast(cb->currentModelIndex().internalPointer()); if (!aspect) { m_okButton->setEnabled(false); return; } /* Column* column = dynamic_cast(aspect); DEBUG("row count = " << (static_cast* >(column->data()))->size()); if (!column || column->rowCount() < 1) { m_okButton->setEnabled(false); //Warning: x column is empty return; } */ } m_okButton->setEnabled(true); } void FunctionValuesDialog::showConstants() { QMenu menu; ConstantsWidget constants(&menu); connect(&constants, SIGNAL(constantSelected(QString)), this, SLOT(insertConstant(QString))); connect(&constants, SIGNAL(constantSelected(QString)), &menu, SLOT(close())); connect(&constants, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&constants); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbConstants->width(),-menu.sizeHint().height()); menu.exec(ui.tbConstants->mapToGlobal(pos)); } void FunctionValuesDialog::showFunctions() { QMenu menu; FunctionsWidget functions(&menu); connect(&functions, SIGNAL(functionSelected(QString)), this, SLOT(insertFunction(QString))); connect(&functions, SIGNAL(functionSelected(QString)), &menu, SLOT(close())); connect(&functions, SIGNAL(canceled()), &menu, SLOT(close())); QWidgetAction* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&functions); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbFunctions->width(),-menu.sizeHint().height()); menu.exec(ui.tbFunctions->mapToGlobal(pos)); } void FunctionValuesDialog::insertFunction(const QString& str) { + //TODO: not all functions have only one argument ui.teEquation->insertPlainText(str + "(x)"); } void FunctionValuesDialog::insertConstant(const QString& str) { ui.teEquation->insertPlainText(str); } void FunctionValuesDialog::addVariable() { QGridLayout* layout = dynamic_cast(ui.frameVariables->layout()); int row = m_variableNames.size(); //text field for the variable name QLineEdit* le = new QLineEdit(); le->setMaximumWidth(30); connect(le, SIGNAL(textChanged(QString)), this, SLOT(variableNameChanged())); layout->addWidget(le, row, 0, 1, 1); m_variableNames << le; //label for the "="-sign QLabel* l = new QLabel("="); layout->addWidget(l, row, 1, 1, 1); m_variableLabels << l; //combo box for the data column TreeViewComboBox* cb = new TreeViewComboBox(); cb->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); connect( cb, SIGNAL(currentModelIndexChanged(QModelIndex)), this, SLOT(checkValues()) ); layout->addWidget(cb, row, 2, 1, 1); m_variableDataColumns << cb; cb->setTopLevelClasses(m_topLevelClasses); cb->setModel(m_aspectTreeModel.get()); cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(m_spreadsheet->column(0))); //move the add-button to the next row layout->removeWidget(ui.bAddVariable); layout->addWidget(ui.bAddVariable, row+1,3, 1, 1); //add delete-button for the just added variable if (row != 0) { QToolButton* b = new QToolButton(); b->setIcon(QIcon::fromTheme("list-remove")); b->setToolTip(i18n("Delete variable")); layout->addWidget(b, row, 3, 1, 1); m_variableDeleteButtons<setText(i18n("Variables:")); //TODO: adjust the tab-ordering after new widgets were added } void FunctionValuesDialog::deleteVariable() { QObject* ob = QObject::sender(); int index = m_variableDeleteButtons.indexOf(qobject_cast(ob)) ; delete m_variableNames.takeAt(index+1); delete m_variableLabels.takeAt(index+1); delete m_variableDataColumns.takeAt(index+1); delete m_variableDeleteButtons.takeAt(index); variableNameChanged(); checkValues(); //adjust the layout resize( QSize(width(),0).expandedTo(minimumSize()) ); m_variableNames.size() > 1 ? ui.lVariable->setText(i18n("Variables:")) : ui.lVariable->setText(i18n("Variable:")); //TODO: adjust the tab-ordering after some widgets were deleted } void FunctionValuesDialog::variableNameChanged() { QStringList vars; QString text; for (int i = 0; i < m_variableNames.size(); ++i) { QString name = m_variableNames.at(i)->text().simplified(); if (!name.isEmpty()) { vars<name(), m_columns.size())); //determine variable names and the data vectors of the specified columns QStringList variableNames; QStringList columnPathes; QVector*> xVectors; QVector xColumns; int maxRowCount = m_spreadsheet->rowCount(); for (int i = 0; i < m_variableNames.size(); ++i) { variableNames << m_variableNames.at(i)->text().simplified(); AbstractAspect* aspect = static_cast(m_variableDataColumns.at(i)->currentModelIndex().internalPointer()); Q_ASSERT(aspect); Column* column = dynamic_cast(aspect); Q_ASSERT(column); columnPathes << column->path(); xColumns << column; xVectors << static_cast* >(column->data()); if (column->rowCount() > maxRowCount) maxRowCount = column->rowCount(); } //resize the spreadsheet if one of the data vectors from other spreadsheet(s) has more elements then the current spreadsheet. if (m_spreadsheet->rowCount() < maxRowCount) m_spreadsheet->setRowCount(maxRowCount); //create new vector for storing the calculated values //the vectors with the variable data can be smaller then the result vector. So, not all values in the result vector might get initialized. //->"clean" the result vector first QVector new_data(maxRowCount); for (int i = 0; i < new_data.size(); ++i) new_data[i] = NAN; //evaluate the expression for f(x_1, x_2, ...) and write the calculated values into a new vector. ExpressionParser* parser = ExpressionParser::getInstance(); const QString& expression = ui.teEquation->toPlainText(); parser->evaluateCartesian(expression, variableNames, xVectors, &new_data); //set the new values and store the expression, variable names and the used data columns for (auto* col : m_columns) { col->setFormula(expression, variableNames, columnPathes); col->replaceValues(0, new_data); } m_spreadsheet->endMacro(); RESET_CURSOR; } diff --git a/src/kdefrontend/spreadsheet/FunctionValuesDialog.h b/src/kdefrontend/spreadsheet/FunctionValuesDialog.h index 75706cc76..65860f4bb 100644 --- a/src/kdefrontend/spreadsheet/FunctionValuesDialog.h +++ b/src/kdefrontend/spreadsheet/FunctionValuesDialog.h @@ -1,80 +1,80 @@ /*************************************************************************** File : FunctionValuesDialog.h Project : LabPlot Description : Dialog for generating values from a mathematical function -------------------------------------------------------------------- Copyright : (C) 2014-2015 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 FUNCTIONVALUESDIALOG_H #define FUNCTIONVALUESDIALOG_H #include "ui_functionvalueswidget.h" #include #include class Column; class Spreadsheet; class TreeViewComboBox; class AspectTreeModel; class QPushButton; class QLineEdit; class FunctionValuesDialog : public QDialog { Q_OBJECT public: - explicit FunctionValuesDialog(Spreadsheet* s, QWidget* parent = 0, Qt::WFlags fl = 0); + explicit FunctionValuesDialog(Spreadsheet* s, QWidget* parent = 0); ~FunctionValuesDialog(); void setColumns(QVector); private: Ui::FunctionValuesWidget ui; QVector m_columns; Spreadsheet* m_spreadsheet; #if __cplusplus < 201103L std::auto_ptr m_aspectTreeModel; #else std::unique_ptr m_aspectTreeModel; #endif QList m_topLevelClasses; QList m_selectableClasses; QList m_variableNames; QList m_variableLabels; QList m_variableDataColumns; QList m_variableDeleteButtons; QPushButton* m_okButton; private slots: void generate(); void checkValues(); void showConstants(); void showFunctions(); void insertFunction(const QString&); void insertConstant(const QString&); void addVariable(); void deleteVariable(); void variableNameChanged(); }; #endif diff --git a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp index 3b147e925..07323c1da 100644 --- a/src/kdefrontend/spreadsheet/PlotDataDialog.cpp +++ b/src/kdefrontend/spreadsheet/PlotDataDialog.cpp @@ -1,565 +1,581 @@ /*************************************************************************** File : PlotDataDialog.cpp Project : LabPlot Description : Dialog for generating plots for the spreadsheet data -------------------------------------------------------------------- Copyright : (C) 2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "PlotDataDialog.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/Project.h" #include "backend/core/column/Column.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h" #include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h" #include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h" #include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h" #include "backend/worksheet/plots/cartesian/XYSmoothCurve.h" #include "backend/worksheet/plots/cartesian/XYFitCurve.h" #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/TextLabel.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "commonfrontend/widgets/TreeViewComboBox.h" #include #include #include #include #include #include #include "ui_plotdatawidget.h" /*! \class PlotDataDialog \brief Dialog for generating plots for the spreadsheet data. \ingroup kdefrontend */ -PlotDataDialog::PlotDataDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), +PlotDataDialog::PlotDataDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), ui(new Ui::PlotDataWidget()), m_spreadsheet(s), m_plotsModel(new AspectTreeModel(m_spreadsheet->project())), m_worksheetsModel(new AspectTreeModel(m_spreadsheet->project())), m_analysisAction(Differentiation), m_analysisMode(false) { setAttribute(Qt::WA_DeleteOnClose); - setWindowTitle(i18n("Plot spreadsheet data")); + setWindowTitle(i18nc("@title:window", "Plot Spreadsheet Data")); setWindowIcon(QIcon::fromTheme("office-chart-line")); QWidget* mainWidget = new QWidget(this); ui->setupUi(mainWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = buttonBox->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); m_okButton->setToolTip(i18n("Plot the selected data")); m_okButton->setText(i18n("&Plot")); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(mainWidget); layout->addWidget(buttonBox); setLayout(layout); //create combox boxes for the existing plots and worksheets QGridLayout* gridLayout = dynamic_cast(ui->gbPlotPlacement->layout()); cbExistingPlots = new TreeViewComboBox(ui->gbPlotPlacement); cbExistingPlots->setMinimumWidth(250);//TODO: use proper sizeHint in TreeViewComboBox gridLayout->addWidget(cbExistingPlots, 0, 1, 1, 1); cbExistingWorksheets = new TreeViewComboBox(ui->gbPlotPlacement); cbExistingWorksheets->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); gridLayout->addWidget(cbExistingWorksheets, 1, 1, 1, 1); QList list; list<<"Folder"<<"Worksheet"<<"CartesianPlot"; cbExistingPlots->setTopLevelClasses(list); list.clear(); list<<"CartesianPlot"; m_plotsModel->setSelectableAspects(list); cbExistingPlots->setModel(m_plotsModel); + //select the first available plot, if available + auto plots = m_spreadsheet->project()->children(AbstractAspect::Recursive); + if (!plots.isEmpty()) { + const auto plot = plots.first(); + cbExistingPlots->setCurrentModelIndex(m_plotsModel->modelIndexOfAspect(plot)); + } + list.clear(); list<<"Folder"<<"Worksheet"; cbExistingWorksheets->setTopLevelClasses(list); list.clear(); list<<"Worksheet"; m_worksheetsModel->setSelectableAspects(list); cbExistingWorksheets->setModel(m_worksheetsModel); + //select the first available worksheet, if available + auto worksheets = m_spreadsheet->project()->children(AbstractAspect::Recursive); + if (!worksheets.isEmpty()) { + const auto worksheet = worksheets.first(); + cbExistingWorksheets->setCurrentModelIndex(m_worksheetsModel->modelIndexOfAspect(worksheet)); + } + //hide the check box for creation of original data, only shown if analysis curves are to be created ui->chkCreateDataCurve->setVisible(false); //SIGNALs/SLOTs - connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::plot); + connect(buttonBox, &QDialogButtonBox::accepted, this, [=]() { hide(); plot(); }); connect(buttonBox, &QDialogButtonBox::rejected, this, &PlotDataDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &PlotDataDialog::accept); connect(ui->rbCurvePlacement1, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); connect(ui->rbCurvePlacement2, &QRadioButton::toggled, this, &PlotDataDialog::curvePlacementChanged); connect(ui->rbPlotPlacement1, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(ui->rbPlotPlacement2, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(ui->rbPlotPlacement3, &QRadioButton::toggled, this, &PlotDataDialog::plotPlacementChanged); connect(cbExistingPlots, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); connect(cbExistingWorksheets, &TreeViewComboBox::currentModelIndexChanged, this, &PlotDataDialog::checkOkButton); QTimer::singleShot(0, this, &PlotDataDialog::loadSettings); } void PlotDataDialog::loadSettings() { //restore saved settings if available QApplication::processEvents(QEventLoop::AllEvents, 0); const KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog"); if (conf.exists()) { KWindowConfig::restoreWindowSize(windowHandle(), conf); int index = conf.readEntry("CurvePlacement", 0); if (index == 2) ui->rbCurvePlacement2->setChecked(true); index = conf.readEntry("PlotPlacement", 0); if (index == 2) ui->rbPlotPlacement2->setChecked(true); if (index == 3) ui->rbPlotPlacement3->setChecked(true); } else resize( QSize(0,0).expandedTo(minimumSize()) ); processColumns(); plotPlacementChanged(); } PlotDataDialog::~PlotDataDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "PlotDataDialog"); int index = 0; if (ui->rbCurvePlacement1->isChecked()) index = 1; if (ui->rbCurvePlacement2->isChecked()) index = 2; conf.writeEntry("CurvePlacement", index); if (ui->rbPlotPlacement1->isChecked()) index = 1; if (ui->rbPlotPlacement2->isChecked()) index = 2; if (ui->rbPlotPlacement3->isChecked()) index = 3; conf.writeEntry("PlotPlacement", index); KWindowConfig::saveWindowSize(windowHandle(), conf); delete m_plotsModel; delete m_worksheetsModel; } void PlotDataDialog::setAnalysisAction(AnalysisAction action) { m_analysisAction = action; m_analysisMode = true; ui->chkCreateDataCurve->setVisible(true); } void PlotDataDialog::processColumns() { //columns to plot SpreadsheetView* view = reinterpret_cast(m_spreadsheet->view()); QVector selectedColumns = view->selectedColumns(true); if (!selectedColumns.size()) { //use all spreadsheet columns if no columns are selected //skip error columns for (Column* col : m_spreadsheet->children()) - if (col->plotDesignation() == AbstractColumn::X || col->plotDesignation() == AbstractColumn::Y) + if (col->plotDesignation() == AbstractColumn::X || col->plotDesignation() == AbstractColumn::Y + || col->plotDesignation() == AbstractColumn::NoDesignation) m_columns << col; //disable everything if the spreadsheet doesn't have any columns if (!m_columns.size()) { ui->gbData->setEnabled(false); ui->gbCurvePlacement->setEnabled(false); ui->gbPlotPlacement->setEnabled(false); return; } } else { //use selected columns, skip error columns for (Column* col : selectedColumns) - if (col->plotDesignation() == AbstractColumn::X || col->plotDesignation() == AbstractColumn::Y) + if (col->plotDesignation() == AbstractColumn::X || col->plotDesignation() == AbstractColumn::Y + || col->plotDesignation() == AbstractColumn::NoDesignation) m_columns << col; } //determine the column names and the name of the first column having "X" as the plot designation QList columnNames; QString xColumnName; for(const Column* column : m_columns) { columnNames << column->name(); if (xColumnName.isEmpty() && column->plotDesignation() == AbstractColumn::X) xColumnName = column->name(); } if (xColumnName.isEmpty()) { //no X-column was selected -> look for the first non-selected X-column left to the first selected column const int index = m_spreadsheet->indexOfChild(selectedColumns.first()) - 1; if (index >= 0) { for (int i = index; i >= 0; --i) { Column* column = m_spreadsheet->column(i); if (column->plotDesignation() == AbstractColumn::X) { xColumnName = column->name(); m_columns.prepend(column); columnNames.prepend(xColumnName); break; } } } } m_columnComboBoxes << ui->cbXColumn; m_columnComboBoxes << ui->cbYColumn; //ui-widget only has one combobox for the y-data -> add additional comboboxes dynamically if required if (m_columns.size()>2) { QGridLayout* gridLayout = dynamic_cast(ui->scrollAreaYColumns->widget()->layout()); for (int i = 2; i < m_columns.size(); ++i) { QLabel* label = new QLabel(i18n("Y-data")); QComboBox* comboBox = new QComboBox(); gridLayout->addWidget(label, i+1, 0, 1, 1); gridLayout->addWidget(comboBox, i+1, 2, 1, 1); m_columnComboBoxes << comboBox; } } else { //two columns provided, only one curve is possible -> hide the curve placement options ui->rbCurvePlacement1->setChecked(true); ui->gbCurvePlacement->hide(); ui->gbPlotPlacement->setTitle(i18n("Add curve to")); } //show all selected/available column names in the data comboboxes for(QComboBox* const comboBox : m_columnComboBoxes) comboBox->addItems(columnNames); if (!xColumnName.isEmpty()) { //show in the X-data combobox the first column having X as the plot designation ui->cbXColumn->setCurrentIndex(ui->cbXColumn->findText(xColumnName)); //for the remaining columns, show the names in the comboboxes for the Y-data //TODO: handle columns with error-designations int yColumnIndex = 1; //the index of the first Y-data comboBox in m_columnComboBoxes for(const QString& name : columnNames) { if (name != xColumnName) { QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; comboBox->setCurrentIndex(comboBox->findText(name)); yColumnIndex++; } } } else { //no column with "x plot designation" is selected, simply show all columns in the order they were selected. //first selected column will serve as the x-column. int yColumnIndex = 0; for(const QString& name : columnNames) { QComboBox* comboBox = m_columnComboBoxes[yColumnIndex]; comboBox->setCurrentIndex(comboBox->findText(name)); yColumnIndex++; } } } void PlotDataDialog::plot() { DEBUG("PlotDataDialog::plot()"); WAIT_CURSOR; if (ui->rbPlotPlacement1->isChecked()) { //add curves to an existing plot AbstractAspect* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); CartesianPlot* plot = dynamic_cast(aspect); plot->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); addCurvesToPlot(plot); plot->endMacro(); } else if (ui->rbPlotPlacement2->isChecked()) { //add curves to a new plot in an existing worksheet AbstractAspect* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); Worksheet* worksheet = dynamic_cast(aspect); worksheet->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); if (ui->rbCurvePlacement1->isChecked()) { //all curves in one plot CartesianPlot* plot = new CartesianPlot( i18n("Plot data from %1", m_spreadsheet->name()) ); plot->initDefault(CartesianPlot::FourAxes); //set the axis titles befor we add the plot to the worksheet //set the x-axis names const QString& xColumnName = ui->cbXColumn->currentText(); for (auto axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal) { axis->title()->setText(xColumnName); break; } } //if we only have one single y-column to plot, we can set the title of the y-axes if (m_columnComboBoxes.size() == 2) { const QString& yColumnName = m_columnComboBoxes[1]->currentText(); for (auto axis : plot->children()) { if (axis->orientation() == Axis::AxisVertical) { axis->title()->setText(yColumnName); break; } } } worksheet->addChild(plot); addCurvesToPlot(plot); } else { //one plot per curve addCurvesToPlots(worksheet); } worksheet->endMacro(); } else { //add curves to a new plot(s) in a new worksheet AbstractAspect* parent = m_spreadsheet->parentAspect(); parent->beginMacro( i18n("Plot data from %1", m_spreadsheet->name()) ); Worksheet* worksheet = new Worksheet(0, i18n("Plot data from %1", m_spreadsheet->name())); parent->addChild(worksheet); if (ui->rbCurvePlacement1->isChecked()) { //all curves in one plot CartesianPlot* plot = new CartesianPlot( i18n("Plot data from %1", m_spreadsheet->name()) ); plot->initDefault(CartesianPlot::FourAxes); //set the axis titles befor we add the plot to the worksheet //set the x-axis names const QString& xColumnName = ui->cbXColumn->currentText(); for (auto axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal) { axis->title()->setText(xColumnName); break; } } //if we only have one single y-column to plot, we can set the title of the y-axes if (m_columnComboBoxes.size() == 2) { const QString& yColumnName = m_columnComboBoxes[1]->currentText(); for (auto axis : plot->children()) { if (axis->orientation() == Axis::AxisVertical) { axis->title()->setText(yColumnName); break; } } } worksheet->addChild(plot); addCurvesToPlot(plot); } else { //one plot per curve addCurvesToPlots(worksheet); } parent->endMacro(); } RESET_CURSOR; } Column* PlotDataDialog::columnFromName(const QString& name) const { for(auto* column : m_columns) { if (column->name() == name) return column; } return 0; } /*! * * for the selected columns in this dialog, creates a curve in the already existing plot \c plot. */ void PlotDataDialog::addCurvesToPlot(CartesianPlot* plot) const { QApplication::processEvents(QEventLoop::AllEvents, 100); Column* xColumn = columnFromName(ui->cbXColumn->currentText()); for (int i = 1; i < m_columnComboBoxes.size(); ++i) { QComboBox* comboBox = m_columnComboBoxes[i]; const QString& name = comboBox->currentText(); Column* yColumn = columnFromName(name); addCurve(name, xColumn, yColumn, plot); } plot->scaleAuto(); } /*! * for the selected columns in this dialog, creates a plot and a curve in the already existing worksheet \c worksheet. */ void PlotDataDialog::addCurvesToPlots(Worksheet* worksheet) const { QApplication::processEvents(QEventLoop::AllEvents, 100); worksheet->setSuppressLayoutUpdate(true); const QString& xColumnName = ui->cbXColumn->currentText(); Column* xColumn = columnFromName(xColumnName); for (int i = 1; i < m_columnComboBoxes.size(); ++i) { QComboBox* comboBox = m_columnComboBoxes[i]; const QString& name = comboBox->currentText(); Column* yColumn = columnFromName(name); CartesianPlot* plot = new CartesianPlot(i18n("Plot %1", name)); plot->initDefault(CartesianPlot::FourAxes); //set the axis names in the new plot bool xSet = false; bool ySet = false; for (auto axis : plot->children()) { if (axis->orientation() == Axis::AxisHorizontal && !xSet) { axis->title()->setText(xColumnName); xSet = true; } else if (axis->orientation() == Axis::AxisVertical && !ySet) { axis->title()->setText(name); ySet = true; } } worksheet->addChild(plot); addCurve(name, xColumn, yColumn, plot); plot->scaleAuto(); } worksheet->setSuppressLayoutUpdate(false); worksheet->updateLayout(); } /*! * helper function that does the actual creation of the curve and adding it as child to the \c plot. */ void PlotDataDialog::addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot* plot) const { DEBUG("PlotDataDialog::addCurve()"); if (!m_analysisMode) { XYCurve* curve = new XYCurve(name); curve->suppressRetransform(true); curve->setXColumn(xColumn); curve->setYColumn(yColumn); curve->suppressRetransform(false); plot->addChild(curve); } else { bool createDataCurve = ui->chkCreateDataCurve->isChecked(); XYCurve* curve = 0; if (createDataCurve) { curve = new XYCurve(name); curve->suppressRetransform(true); curve->setXColumn(xColumn); curve->setYColumn(yColumn); curve->suppressRetransform(false); plot->addChild(curve); } XYAnalysisCurve* analysisCurve = nullptr; switch (m_analysisAction) { case DataReduction: analysisCurve = new XYDataReductionCurve(i18n("Reduction of '%1'", name)); break; case Differentiation: analysisCurve = new XYDifferentiationCurve(i18n("Derivative of '%1'", name)); break; case Integration: analysisCurve = new XYIntegrationCurve(i18n("Integral of '%1'", name)); break; case Interpolation: analysisCurve = new XYInterpolationCurve(i18n("Interpolation of '%1'", name)); break; case Smoothing: analysisCurve = new XYSmoothCurve(i18n("Smoothing of '%1'", name)); break; case FitLinear: case FitPower: case FitExp1: case FitExp2: case FitInvExp: case FitGauss: case FitCauchyLorentz: case FitTan: case FitTanh: case FitErrFunc: case FitCustom: analysisCurve = new XYFitCurve(i18n("Fit to '%1'", name)); static_cast(analysisCurve)->initFitData(m_analysisAction); static_cast(analysisCurve)->initStartValues(curve); break; case FourierFilter: analysisCurve = new XYFourierFilterCurve(i18n("Fourier Filter of '%1'", name)); break; } if (analysisCurve != nullptr) { analysisCurve->suppressRetransform(true); analysisCurve->setXDataColumn(xColumn); analysisCurve->setYDataColumn(yColumn); analysisCurve->recalculate(); analysisCurve->suppressRetransform(false); plot->addChild(analysisCurve); } } } //################################################################ //########################## Slots ############################### //################################################################ void PlotDataDialog::curvePlacementChanged() { if (ui->rbCurvePlacement1->isChecked()) { ui->rbPlotPlacement1->setEnabled(true); ui->rbPlotPlacement2->setText(i18n("new plot in an existing worksheet")); ui->rbPlotPlacement3->setText(i18n("new plot in a new worksheet")); } else { ui->rbPlotPlacement1->setEnabled(false); if (ui->rbPlotPlacement1->isChecked()) ui->rbPlotPlacement2->setChecked(true); ui->rbPlotPlacement2->setText(i18n("new plots in an existing worksheet")); ui->rbPlotPlacement3->setText(i18n("new plots in a new worksheet")); } } void PlotDataDialog::plotPlacementChanged() { if (ui->rbPlotPlacement1->isChecked()) { cbExistingPlots->setEnabled(true); cbExistingWorksheets->setEnabled(false); } else if (ui->rbPlotPlacement2->isChecked()) { cbExistingPlots->setEnabled(false); cbExistingWorksheets->setEnabled(true); } else { cbExistingPlots->setEnabled(false); cbExistingWorksheets->setEnabled(false); } checkOkButton(); } void PlotDataDialog::checkOkButton() { bool enable = false; QString msg; if (ui->rbPlotPlacement1->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingPlots->currentModelIndex().internalPointer()); enable = (aspect!=NULL); if (!enable) msg = i18n("An already existing plot has to be selected."); } else if (ui->rbPlotPlacement2->isChecked()) { AbstractAspect* aspect = static_cast(cbExistingWorksheets->currentModelIndex().internalPointer()); enable = (aspect!=NULL); if (!enable) msg = i18n("An already existing worksheet has to be selected."); } else enable = true; m_okButton->setEnabled(enable); if (enable) m_okButton->setToolTip(i18n("Close the dialog and plot the data.")); else m_okButton->setToolTip(msg); } diff --git a/src/kdefrontend/spreadsheet/PlotDataDialog.h b/src/kdefrontend/spreadsheet/PlotDataDialog.h index 210e75d9b..5fef40f65 100644 --- a/src/kdefrontend/spreadsheet/PlotDataDialog.h +++ b/src/kdefrontend/spreadsheet/PlotDataDialog.h @@ -1,88 +1,88 @@ /*************************************************************************** File : PlotDataDialog.h Project : LabPlot Description : Dialog for generating plots for the spreadsheet data -------------------------------------------------------------------- Copyright : (C) 2017 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 PLOTDATADIALOG_H #define PLOTDATADIALOG_H namespace Ui { class PlotDataWidget; } #include class QComboBox; class AspectTreeModel; class CartesianPlot; class Column; class Spreadsheet; class TreeViewComboBox; class Worksheet; class PlotDataDialog : public QDialog { Q_OBJECT public: enum AnalysisAction {DataReduction, Differentiation, Integration, Interpolation, Smoothing, FitLinear, FitPower, FitExp1, FitExp2, FitInvExp, FitGauss, FitCauchyLorentz, FitTan, FitTanh, FitErrFunc, FitCustom, FourierFilter}; - explicit PlotDataDialog(Spreadsheet*, QWidget* parent = 0, Qt::WFlags fl = 0); + explicit PlotDataDialog(Spreadsheet*, QWidget* parent = 0); ~PlotDataDialog() override; void setAnalysisAction(AnalysisAction); private: Ui::PlotDataWidget* ui; QPushButton* m_okButton; Spreadsheet* m_spreadsheet; TreeViewComboBox* cbExistingPlots; TreeViewComboBox* cbExistingWorksheets; QVector m_columns; QVector m_columnComboBoxes; AspectTreeModel* m_plotsModel; AspectTreeModel* m_worksheetsModel; AnalysisAction m_analysisAction; bool m_analysisMode; void processColumns(); void addCurvesToPlot(CartesianPlot*) const; void addCurvesToPlots(Worksheet*) const; void addCurve(const QString& name, Column* xColumn, Column* yColumn, CartesianPlot*) const; Column* columnFromName(const QString&) const; protected slots: virtual void checkOkButton(); private slots: void plot(); void curvePlacementChanged(); void plotPlacementChanged(); void loadSettings(); }; #endif diff --git a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp index e63f1ecc9..9e15dfb69 100644 --- a/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp +++ b/src/kdefrontend/spreadsheet/RandomValuesDialog.cpp @@ -1,729 +1,729 @@ /*************************************************************************** File : RandomValuesDialog.cpp Project : LabPlot Description : Dialog for generating non-uniformly distributed random numbers -------------------------------------------------------------------- Copyright : (C) 2014 by Alexander Semke (alexander.semke@web.de) Copyright : (C) 2016-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "RandomValuesDialog.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include "backend/spreadsheet/Spreadsheet.h" #include #include #include #include #include #include #include #include extern "C" { #include #include "backend/nsl/nsl_sf_stats.h" #include #include } /*! \class RandomValuesDialog \brief Dialog for generating non-uniform random numbers. \ingroup kdefrontend */ -RandomValuesDialog::RandomValuesDialog(Spreadsheet* s, QWidget* parent, Qt::WFlags fl) : QDialog(parent, fl), m_spreadsheet(s) { - setWindowTitle(i18n("Random values")); +RandomValuesDialog::RandomValuesDialog(Spreadsheet* s, QWidget* parent) : QDialog(parent), m_spreadsheet(s) { + setWindowTitle(i18nc("@title:window", "Random Values")); QWidget* mainWidget = new QWidget(this); ui.setupUi(mainWidget); QVBoxLayout *layout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_okButton = buttonBox->button(QDialogButtonBox::Ok); m_okButton->setDefault(true); m_okButton->setToolTip(i18n("Generate random values according to the selected distribution")); m_okButton->setText(i18n("&Generate")); connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &RandomValuesDialog::close); connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &RandomValuesDialog::reject); layout->addWidget(mainWidget); layout->addWidget(buttonBox); setLayout(layout); setAttribute(Qt::WA_DeleteOnClose); for (int i = 0; i < NSL_SF_STATS_DISTRIBUTION_RNG_COUNT; i++) ui.cbDistribution->addItem(i18n(nsl_sf_stats_distribution_name[i]), i); //use white background in the preview label QPalette p; p.setColor(QPalette::Window, Qt::white); ui.lFuncPic->setAutoFillBackground(true); ui.lFuncPic->setPalette(p); ui.leParameter1->setClearButtonEnabled(true); ui.leParameter2->setClearButtonEnabled(true); ui.leParameter3->setClearButtonEnabled(true); ui.leParameter1->setValidator( new QDoubleValidator(ui.leParameter1) ); ui.leParameter2->setValidator( new QDoubleValidator(ui.leParameter2) ); ui.leParameter3->setValidator( new QDoubleValidator(ui.leParameter3) ); connect(ui.cbDistribution, static_cast(&QComboBox::currentIndexChanged), this, &RandomValuesDialog::distributionChanged); connect(ui.leParameter1, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(ui.leParameter2, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(ui.leParameter3, &QLineEdit::textChanged, this, &RandomValuesDialog::checkValues); connect(buttonBox, &QDialogButtonBox::accepted, this, &RandomValuesDialog::generate); //restore saved settings if available const KConfigGroup conf(KSharedConfig::openConfig(), "RandomValuesDialog"); if (conf.exists()) { ui.cbDistribution->setCurrentIndex(conf.readEntry("Distribution", 0)); this->distributionChanged(ui.cbDistribution->currentIndex()); //if index=0 no signal is emmited above, call this slot directly here ui.leParameter1->setText(conf.readEntry("Parameter1")); ui.leParameter2->setText(conf.readEntry("Parameter2")); ui.leParameter3->setText(conf.readEntry("Parameter3")); KWindowConfig::restoreWindowSize(windowHandle(), conf); } else { //Gaussian distribution as default this->distributionChanged(0); resize( QSize(400,0).expandedTo(minimumSize()) ); } } RandomValuesDialog::~RandomValuesDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "RandomValuesDialog"); conf.writeEntry("Distribution", ui.cbDistribution->currentIndex()); conf.writeEntry("Parameter1", ui.leParameter1->text()); conf.writeEntry("Parameter2", ui.leParameter2->text()); conf.writeEntry("Parameter3", ui.leParameter3->text()); KWindowConfig::saveWindowSize(windowHandle(), conf); } void RandomValuesDialog::setColumns(QVector columns) { m_columns = columns; } void RandomValuesDialog::distributionChanged(int index) { nsl_sf_stats_distribution dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt(); // default settings (used by most distributions) ui.lParameter1->show(); ui.leParameter1->show(); ui.lParameter2->show(); ui.leParameter2->show(); ui.lParameter3->hide(); ui.leParameter3->hide(); ui.lFunc->setText("p(x) ="); switch (dist) { case nsl_sf_stats_gaussian: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gaussian_tail: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.lParameter3->setText("a ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_exponential: ui.lParameter1->setText(UTF8_QSTRING("λ =")); ui.leParameter1->setText("1.0"); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_laplace: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_exponential_power: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.lParameter3->setText("b ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_cauchy_lorentz: ui.lParameter1->setText(UTF8_QSTRING("γ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_rayleigh: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_rayleigh_tail: ui.lParameter1->setText(UTF8_QSTRING("μ =")); ui.lParameter2->setText(UTF8_QSTRING("σ =")); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_landau: ui.lParameter1->hide(); ui.leParameter1->hide(); ui.lParameter2->hide(); ui.leParameter2->hide(); break; case nsl_sf_stats_levy_alpha_stable: ui.lParameter1->setText("c ="); ui.lParameter2->setText(UTF8_QSTRING("α =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_levy_skew_alpha_stable: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("c =")); ui.lParameter2->setText(UTF8_QSTRING("α =")); ui.lParameter3->setText(UTF8_QSTRING("β =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_flat: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("0.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gamma: ui.lParameter1->setText(UTF8_QSTRING("θ =")); ui.lParameter2->setText("k ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_weibull: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText("k ="); ui.lParameter2->setText(UTF8_QSTRING("λ =")); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("1.0"); break; case nsl_sf_stats_beta: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_gumbel1: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("β =")); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_gumbel2: ui.lParameter3->show(); ui.leParameter3->show(); ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.lParameter3->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); ui.leParameter3->setText("0.0"); break; case nsl_sf_stats_pareto: ui.lParameter1->setText("a ="); ui.lParameter2->setText("b ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_lognormal: ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_chi_squared: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText("n ="); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_fdist: ui.lParameter1->setText(UTF8_QSTRING("ν₁ =")); ui.lParameter2->setText(UTF8_QSTRING("ν₂ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("1.0"); break; case nsl_sf_stats_tdist: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lParameter1->setText(UTF8_QSTRING("ν =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_logistic: ui.lParameter1->setText(UTF8_QSTRING("σ =")); ui.lParameter2->setText(UTF8_QSTRING("μ =")); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("0.0"); break; case nsl_sf_stats_poisson: ui.lParameter2->hide(); ui.leParameter2->hide(); ui.lFunc->setText("p(k) ="); ui.lParameter1->setText(UTF8_QSTRING("λ =")); ui.leParameter1->setText("1.0"); break; case nsl_sf_stats_bernoulli: case nsl_sf_stats_geometric: case nsl_sf_stats_logarithmic: ui.lParameter2->hide(); ui.leParameter2->hide(); if (dist == nsl_sf_stats_bernoulli) ui.lFunc->setText(""); else ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("p ="); ui.leParameter1->setText("0.5"); break; case nsl_sf_stats_binomial: case nsl_sf_stats_negative_binomial: case nsl_sf_stats_pascal: ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("p ="); ui.lParameter2->setText("n ="); ui.leParameter1->setText("0.5"); ui.leParameter2->setText("100"); break; case nsl_sf_stats_hypergeometric: ui.lParameter3->show(); ui.leParameter3->show(); ui.lFunc->setText("p(k) ="); ui.lParameter1->setText("n1 ="); ui.lParameter2->setText("n2 ="); ui.lParameter3->setText("t ="); ui.leParameter1->setText("1.0"); ui.leParameter2->setText("2.0"); ui.leParameter3->setText("3.0"); break; case nsl_sf_stats_maxwell_boltzmann: // additional non-GSL distros case nsl_sf_stats_sech: case nsl_sf_stats_levy: case nsl_sf_stats_frechet: break; } QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/gsl_distributions/" + QString(nsl_sf_stats_distribution_pic_name[dist]) + ".png"); DEBUG("Distribution pixmap path = " << file.toStdString()); ui.lFuncPic->setPixmap(QPixmap(file)); } void RandomValuesDialog::checkValues() { if (ui.leParameter1->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leParameter2->isVisible() && ui.leParameter2->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } if (ui.leParameter3->isVisible() && ui.leParameter3->text().simplified().isEmpty()) { m_okButton->setEnabled(false); return; } m_okButton->setEnabled(true); return; } void RandomValuesDialog::generate() { Q_ASSERT(m_spreadsheet); //create a generator chosen by the environment variable GSL_RNG_TYPE gsl_rng_env_setup(); const gsl_rng_type* T = gsl_rng_default; gsl_rng* r = gsl_rng_alloc(T); WAIT_CURSOR; for (auto* col : m_columns) col->setSuppressDataChangedSignal(true); m_spreadsheet->beginMacro(i18np("%1: fill column with non-uniform random numbers", "%1: fill columns with non-uniform random numbers", m_spreadsheet->name(), m_columns.size())); const int index = ui.cbDistribution->currentIndex(); const nsl_sf_stats_distribution dist = (nsl_sf_stats_distribution)ui.cbDistribution->itemData(index).toInt(); DEBUG("random number distribution: " << nsl_sf_stats_distribution_name[dist]); const int rows = m_spreadsheet->rowCount(); QVector new_data(rows); switch (dist) { case nsl_sf_stats_gaussian: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); DEBUG(" mu = " << mu << ", sigma = " << sigma); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gaussian(r, sigma) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gaussian_tail: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); double a = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gaussian_tail(r, a, sigma) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_exponential: { double l = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { //GSL uses the inverse for exp. distrib. for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_exponential(r, 1./l) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_laplace: { double mu = ui.leParameter1->text().toDouble(); double s = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_laplace(r, s) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_exponential_power: { double mu = ui.leParameter1->text().toDouble(); double a = ui.leParameter2->text().toDouble(); double b = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_exppow(r, a, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_cauchy_lorentz: { double gamma = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_cauchy(r, gamma) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_rayleigh: { double s = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_rayleigh(r, s); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_rayleigh_tail: { double mu = ui.leParameter1->text().toDouble(); double sigma = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_rayleigh_tail(r, mu, sigma); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_landau: for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_landau(r); col->replaceValues(0, new_data); } break; case nsl_sf_stats_levy_alpha_stable: { double c = ui.leParameter1->text().toDouble(); double alpha = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_levy(r, c, alpha); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_levy_skew_alpha_stable: { double c = ui.leParameter1->text().toDouble(); double alpha = ui.leParameter2->text().toDouble(); double beta = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_levy_skew(r, c, alpha, beta); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gamma: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gamma(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_flat: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_flat(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_lognormal: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_lognormal(r, mu, s); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_chi_squared: { double n = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_chisq(r, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_fdist: { double nu1 = ui.leParameter1->text().toDouble(); double nu2 = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_fdist(r, nu1, nu2); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_tdist: { double nu = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_tdist(r, nu); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_beta: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_beta(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_logistic: { double s = ui.leParameter1->text().toDouble(); double mu = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_logistic(r, s) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_pareto: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_pareto(r, a, b); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_weibull: { double k = ui.leParameter1->text().toDouble(); double l = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_weibull(r, l, k) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gumbel1: { double s = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gumbel1(r, 1./s, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_gumbel2: { double a = ui.leParameter1->text().toDouble(); double b = ui.leParameter2->text().toDouble(); double mu = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_gumbel2(r, a, b) + mu; col->replaceValues(0, new_data); } break; } case nsl_sf_stats_poisson: { double l = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_poisson(r, l); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_bernoulli: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_bernoulli(r, p); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_binomial(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_negative_binomial: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_negative_binomial(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_pascal: { double p = ui.leParameter1->text().toDouble(); double n = ui.leParameter2->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_pascal(r, p, n); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_geometric: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_geometric(r, p); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_hypergeometric: { double n1 = ui.leParameter1->text().toDouble(); double n2 = ui.leParameter2->text().toDouble(); double t = ui.leParameter3->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_hypergeometric(r, n1, n2, t); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_logarithmic: { double p = ui.leParameter1->text().toDouble(); for (auto* col : m_columns) { for (int i = 0; i < rows; ++i) new_data[i] = gsl_ran_logarithmic(r, p); col->replaceValues(0, new_data); } break; } case nsl_sf_stats_maxwell_boltzmann: // additional non-GSL distros case nsl_sf_stats_sech: case nsl_sf_stats_levy: case nsl_sf_stats_frechet: break; } for (auto* col : m_columns) { col->setSuppressDataChangedSignal(false); col->setChanged(); } m_spreadsheet->endMacro(); RESET_CURSOR; gsl_rng_free(r); } diff --git a/src/kdefrontend/spreadsheet/RandomValuesDialog.h b/src/kdefrontend/spreadsheet/RandomValuesDialog.h index e0546cf10..0d2bda92d 100644 --- a/src/kdefrontend/spreadsheet/RandomValuesDialog.h +++ b/src/kdefrontend/spreadsheet/RandomValuesDialog.h @@ -1,58 +1,58 @@ /*************************************************************************** File : RandomValuesDialog.h Project : LabPlot Description : Dialog for generating uniformly and non-uniformly distributed random numbers -------------------------------------------------------------------- Copyright : (C) 2014 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 RANDOMVALUESDIALOG_H #define RANDOMVALUESDIALOG_H #include #include "ui_randomvalueswidget.h" class Column; class Spreadsheet; class QPushButton; class RandomValuesDialog : public QDialog { Q_OBJECT public: - explicit RandomValuesDialog(Spreadsheet* s, QWidget* parent = 0, Qt::WFlags fl = 0); + explicit RandomValuesDialog(Spreadsheet* s, QWidget* parent = 0); ~RandomValuesDialog(); void setColumns(QVector); private: Ui::RandomValuesWidget ui; QVector m_columns; Spreadsheet* m_spreadsheet; QPushButton* m_okButton; private slots: void generate(); void distributionChanged(int index); void checkValues(); }; #endif diff --git a/src/kdefrontend/spreadsheet/SortDialog.cpp b/src/kdefrontend/spreadsheet/SortDialog.cpp index 31fc82600..02bd2362a 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.cpp +++ b/src/kdefrontend/spreadsheet/SortDialog.cpp @@ -1,128 +1,128 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "SortDialog.h" #include #include #include #include #include -#include +#include #include #include #include /*! \class SortDialog \brief Dialog for sorting the columns in a spreadsheet. \ingroup kdefrontend */ -SortDialog::SortDialog( QWidget* parent, Qt::WFlags fl ) : QDialog( parent, fl ){ +SortDialog::SortDialog( QWidget* parent ) : QDialog( parent ){ setWindowIcon(QIcon::fromTheme("view-sort-ascending")); - setWindowTitle(i18n("Sort columns")); + setWindowTitle(i18nc("@title:window", "Sort Columns")); setSizeGripEnabled(true); setAttribute(Qt::WA_DeleteOnClose); QGroupBox* widget = new QGroupBox(i18n("Options")); QGridLayout* layout = new QGridLayout(widget); layout->setSpacing(4); layout->setContentsMargins(4,4,4,4); layout->addWidget( new QLabel( i18n("Order")), 0, 0 ); m_cbOrdering = new QComboBox(); m_cbOrdering->addItem(QIcon::fromTheme("view-sort-ascending"), i18n("Ascending")); m_cbOrdering->addItem(QIcon::fromTheme("view-sort-descending"), i18n("Descending")); layout->addWidget(m_cbOrdering, 0, 1 ); m_lType = new QLabel(i18n("Sort columns")); layout->addWidget( m_lType, 1, 0 ); m_cbType = new QComboBox(); m_cbType->addItem(i18n("Separately")); m_cbType->addItem(i18n("Together")); layout->addWidget(m_cbType, 1, 1 ); m_cbType->setCurrentIndex(Together); m_lColumns = new QLabel(i18n("Leading column")); layout->addWidget( m_lColumns, 2, 0 ); m_cbColumns = new QComboBox(); layout->addWidget(m_cbColumns, 2, 1); layout->setRowStretch(3, 1); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Sort")); connect(buttonBox, &QDialogButtonBox::accepted, this, &SortDialog::sortColumns); connect(buttonBox, &QDialogButtonBox::rejected, this, &SortDialog::reject); connect(buttonBox, &QDialogButtonBox::accepted, this, &SortDialog::accept); layout->addWidget(buttonBox); setLayout(layout); connect(m_cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(changeType(int))); this->resize(400,0); } void SortDialog::sortColumns(){ Column* leading; if(m_cbType->currentIndex() == Together) leading = m_columns.at(m_cbColumns->currentIndex()); else leading = 0; emit sort(leading, m_columns, m_cbOrdering->currentIndex() == Ascending ); } void SortDialog::setColumns(QVector columns){ m_columns = columns; for(int i=0; iaddItem( m_columns.at(i)->name() ); m_cbColumns->setCurrentIndex(0); if (m_columns.size() == 1){ m_lType->hide(); m_cbType->hide(); m_lColumns->hide(); m_cbColumns->hide(); } } void SortDialog::changeType(int Type){ if(Type == Together) m_cbColumns->setEnabled(true); else m_cbColumns->setEnabled(false); } diff --git a/src/kdefrontend/spreadsheet/SortDialog.h b/src/kdefrontend/spreadsheet/SortDialog.h index 5cf6530cf..24136bef6 100644 --- a/src/kdefrontend/spreadsheet/SortDialog.h +++ b/src/kdefrontend/spreadsheet/SortDialog.h @@ -1,65 +1,65 @@ /*************************************************************************** File : SortDialog.h Project : LabPlot Description : Sorting options dialog -------------------------------------------------------------------- Copyright : (C) 2011 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 SORTDIALOG_H #define SORTDIALOG_H #include "backend/core/column/Column.h" #include class QPushButton; class QComboBox; class QLabel; class SortDialog : public QDialog { Q_OBJECT public: - explicit SortDialog( QWidget* parent = 0, Qt::WFlags fl = 0 ); + explicit SortDialog( QWidget* parent = 0 ); void setColumns(QVector); enum { Separately=0, Together=1 }; enum { Ascending=0, Descending=1 }; private slots: void sortColumns(); void changeType(int index); signals: void sort(Column*, QVector, bool ascending); private: QVector m_columns; QComboBox* m_cbOrdering; QLabel* m_lType; QComboBox* m_cbType; QLabel* m_lColumns; QComboBox* m_cbColumns; }; #endif diff --git a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp index c697531d9..31e46a604 100644 --- a/src/kdefrontend/spreadsheet/StatisticsDialog.cpp +++ b/src/kdefrontend/spreadsheet/StatisticsDialog.cpp @@ -1,238 +1,239 @@ /*************************************************************************** File : StatisticsDialog.cpp Project : LabPlot Description : Dialog showing statistics for column values -------------------------------------------------------------------- Copyright : (C) 2016-2017 by Fabian Kristof (fkristofszabolcs@gmail.com)) Copyright : (C) 2016-2017 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "StatisticsDialog.h" #include "backend/core/column/Column.h" #include #include #include -#include +#include #include #include #include #include #include +#include StatisticsDialog::StatisticsDialog(const QString& title, QWidget* parent) : QDialog(parent) { m_twStatistics = new QTabWidget; QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok); QPushButton* btnOk = btnBox->button(QDialogButtonBox::Ok); btnOk->setFocus(); connect(btnOk, &QPushButton::clicked, this, &StatisticsDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &StatisticsDialog::accept); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(m_twStatistics); layout->addWidget(btnBox); setLayout(layout); setWindowTitle(title); setAttribute(Qt::WA_DeleteOnClose); const QString htmlColor = (palette().color(QPalette::Base).lightness() < 128) ? QLatin1String("#5f5f5f") : QLatin1String("#D1D1D1"); m_htmlText = QString("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
" + i18n("Location measures")+ "
" + i18n("Minimum")+ "%1
" + i18n("Maximum")+ "%2
" + i18n("Arithmetic mean")+ "%3
" + i18n("Geometric mean")+ "%4
" + i18n("Harmonic mean")+ "%5
" + i18n("Contraharmonic mean")+ "%6
" + i18n("Median")+ "%7
" + i18n("Dispersion measures")+ "
" + i18n("Variance")+ "%8
" + i18n("Standard deviation")+ "%9
" + i18n("Mean absolute deviation around mean")+ "%10
" + i18n("Mean absolute deviation around median")+ "%11
" + i18n("Median absolute deviation")+ "%12
" + i18n("Shape measures")+ "
" + i18n("Skewness")+ "%13
" + i18n("Kurtosis")+ "%14
" + i18n("Entropy")+ "%15
"); connect(m_twStatistics, &QTabWidget::currentChanged, this, &StatisticsDialog::currentTabChanged); QTimer::singleShot(0, this, &StatisticsDialog::loadSettings); } void StatisticsDialog::loadSettings() { //restore saved settings if available QApplication::processEvents(QEventLoop::AllEvents, 0); KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize(QSize(490, 520)); } StatisticsDialog::~StatisticsDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "StatisticsDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); } void StatisticsDialog::setColumns(const QVector& columns) { if (!columns.size()) return; m_columns = columns; for (int i = 0; i < m_columns.size(); ++i) { QTextEdit* textEdit = new QTextEdit; textEdit->setReadOnly(true); m_twStatistics->addTab(textEdit, m_columns[i]->name()); } currentTabChanged(0); } const QString StatisticsDialog::isNanValue(const double value) { return (std::isnan(value) ? QLatin1String("-") : QString::number(value,'g', 10)); } void StatisticsDialog::currentTabChanged(int index) { WAIT_CURSOR; const Column::ColumnStatistics& statistics = m_columns[index]->statistics(); RESET_CURSOR; QTextEdit* const textEdit = static_cast(m_twStatistics->currentWidget()); textEdit->setHtml(m_htmlText.arg(isNanValue(statistics.minimum == INFINITY ? NAN : statistics.minimum), isNanValue(statistics.maximum == -INFINITY ? NAN : statistics.maximum), isNanValue(statistics.arithmeticMean), isNanValue(statistics.geometricMean), isNanValue(statistics.harmonicMean), isNanValue(statistics.contraharmonicMean), isNanValue(statistics.median), isNanValue(statistics.variance), isNanValue(statistics.standardDeviation)). arg(isNanValue(statistics.meanDeviation), isNanValue(statistics.meanDeviationAroundMedian), isNanValue(statistics.medianDeviation), isNanValue(statistics.skewness), isNanValue(statistics.kurtosis), isNanValue(statistics.entropy))); } diff --git a/src/kdefrontend/ui/datasources/importfilewidget.ui b/src/kdefrontend/ui/datasources/importfilewidget.ui index 97e4138cb..9bfac3d50 100644 --- a/src/kdefrontend/ui/datasources/importfilewidget.ui +++ b/src/kdefrontend/ui/datasources/importfilewidget.ui @@ -1,722 +1,757 @@ ImportFileWidget 0 0 679 - 1219 + 847 + + + 0 + 0 + + 0 0 0 0 0 0 Data source - - - - false - - - Save the current filter settings - - - - - - - Name - - - - - - - - - - false - - - - 0 - 0 - - - - - - - - Type - - - - - - - Host - - - - - - - Port - - - File or named pipe Network TCP socket Network UDP socket Local socket Serial port - - + + + + Source + + + + + + + false + + + Save the current filter settings + + + + + + + false + + + + 0 + 0 + + + false 0 0 Manage filters - - - - Specify the name of the file to import. + + + + Host - - true + + + + + + false + + + + + + + + + + Type 0 0 Select the file to import - - + + + + Name + + + + + Port - - - - false + + + + Specify the name of the file to import. + + + true - - + + - Source + Filter - + Qt::Vertical QSizePolicy::Fixed 20 10 - - + + + + + + + + + + false + + + + 0 + 0 + + + + Show file info + - Filter + Qt::Vertical QSizePolicy::Fixed 20 13 - - + + + + + - Baud rate + Port - - - - false - - - - 0 - 0 - - - - Show file info - + + - + Baud rate - - - - - - - + - - - 0 - 0 - - QAbstractItemView::NoEditTriggers false - - 150 - - + Field false - + 0 0 Format Options 0 0 0 0 0 + + + 0 + 0 + + Data format - - + + + 0 + 0 + + + + + + 0 + 0 + + + + + + + 0 + 0 + + + + + + 0 + 0 + + Preview 0 0 Number of rows to preview: 1 10000 100 Qt::Horizontal 23 20 Refresh true QTextEdit::NoWrap true + + + 0 + 0 + + Data portion to read - - - - Qt::Vertical - - - - 20 - 172 - - - - Start row: - - + + - Specify the start row for import + Specify the end row to import; -1 stands for the last row - 1 + -1 2147483647 + -1 + + + + + + + Start column: + + + + + + + Specify the start column for import + + 1 + + 2147483647 + - - + + Qt::Horizontal QSizePolicy::Fixed 40 20 - - - - Qt::Horizontal - - - - 108 - 20 - - - - - - - - End row: - - - - - + + - Specify the end row to import; -1 stands for the last row + Specify the start row for import - -1 + 1 2147483647 - -1 - - - - - - - Start column: + 1 - - - - Specify the start column for import - - - 1 + + + + Qt::Horizontal - - 2147483647 + + + 108 + 20 + - + - - + + Qt::Horizontal QSizePolicy::Fixed 40 20 End column: Specify the end column to import; -1 stands for the last column -1 2147483647 -1 Qt::Horizontal 108 20 + + + + End row: + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + Update Options 0 0 Periodically On new data If this option is checked, only the link to the file is stored in the project file but not it's content. Link the file true Read Keep last N values Update Sample rate 1 10000 5 Continuously fixed From end Till the end Update interval 0 0 5 60000 1000 ms KComboBox QComboBox
kcombobox.h
diff --git a/src/kdefrontend/widgets/DatapickerCurveWidget.cpp b/src/kdefrontend/widgets/DatapickerCurveWidget.cpp index b834137a0..035ebad41 100644 --- a/src/kdefrontend/widgets/DatapickerCurveWidget.cpp +++ b/src/kdefrontend/widgets/DatapickerCurveWidget.cpp @@ -1,523 +1,523 @@ /*************************************************************************** File : ImageWidget.cpp Project : LabPlot Description : widget for datapicker properties -------------------------------------------------------------------- Copyright : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com) Copyright : (C) 2015 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 "DatapickerCurveWidget.h" #include "backend/datapicker/DatapickerPoint.h" #include "backend/worksheet/Worksheet.h" #include "kdefrontend/GuiTools.h" #include -#include +#include #include DatapickerCurveWidget::DatapickerCurveWidget(QWidget* parent) : QWidget(parent), m_curve(nullptr), m_suppressTypeChange(false) { ui.setupUi(this); ui.cbXErrorType->addItem(i18n("No Error")); - ui.cbXErrorType->addItem(i18n("symmetric")); - ui.cbXErrorType->addItem(i18n("asymmetric")); + ui.cbXErrorType->addItem(i18n("Symmetric")); + ui.cbXErrorType->addItem(i18n("Asymmetric")); ui.cbYErrorType->addItem(i18n("No Error")); - ui.cbYErrorType->addItem(i18n("symmetric")); - ui.cbYErrorType->addItem(i18n("asymmetric")); + ui.cbYErrorType->addItem(i18n("Symmetric")); + ui.cbYErrorType->addItem(i18n("Asymmetric")); connect(ui.leName, &QLineEdit::textChanged, this, &DatapickerCurveWidget::nameChanged); connect(ui.leComment, &QLineEdit::textChanged, this, &DatapickerCurveWidget::commentChanged); connect( ui.cbXErrorType, SIGNAL(currentIndexChanged(int)), this, SLOT(xErrorTypeChanged(int)) ); connect( ui.cbYErrorType, SIGNAL(currentIndexChanged(int)), this, SLOT(yErrorTypeChanged(int)) ); //symbol connect( ui.cbStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(styleChanged(int)) ); connect( ui.sbSize, SIGNAL(valueChanged(double)), this, SLOT(sizeChanged(double)) ); connect( ui.sbRotation, SIGNAL(valueChanged(int)), this, SLOT(rotationChanged(int)) ); connect( ui.sbOpacity, SIGNAL(valueChanged(int)), this, SLOT(opacityChanged(int)) ); //Filling connect( ui.cbFillingStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(fillingStyleChanged(int)) ); connect( ui.kcbFillingColor, SIGNAL(changed(QColor)), this, SLOT(fillingColorChanged(QColor)) ); //border connect( ui.cbBorderStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(borderStyleChanged(int)) ); connect( ui.kcbBorderColor, SIGNAL(changed(QColor)), this, SLOT(borderColorChanged(QColor)) ); connect( ui.sbBorderWidth, SIGNAL(valueChanged(double)), this, SLOT(borderWidthChanged(double)) ); connect( ui.chbVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); //error bar connect( ui.cbErrorBarFillingStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(errorBarFillingStyleChanged(int)) ); connect( ui.kcbErrorBarFillingColor, SIGNAL(changed(QColor)), this, SLOT(errorBarFillingColorChanged(QColor)) ); connect( ui.sbErrorBarSize, SIGNAL(valueChanged(double)), this, SLOT(errorBarSizeChanged(double)) ); init(); hideErrorBarWidgets(true); } DatapickerCurveWidget::~DatapickerCurveWidget() { } void DatapickerCurveWidget::init() { m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, Qt::black); QPainter pa; int iconSize = 20; QPixmap pm(iconSize, iconSize); QPen pen(Qt::SolidPattern, 0); ui.cbStyle->setIconSize(QSize(iconSize, iconSize)); QTransform trafo; trafo.scale(15, 15); for (int i=1; i<19; ++i) { Symbol::Style style = (Symbol::Style)i; pm.fill(Qt::transparent); pa.begin(&pm); pa.setPen( pen ); pa.setRenderHint(QPainter::Antialiasing); pa.translate(iconSize/2,iconSize/2); pa.drawPath(trafo.map(Symbol::pathFromStyle(style))); pa.end(); ui.cbStyle->addItem(QIcon(pm), Symbol::nameFromStyle(style)); } GuiTools::updateBrushStyles(ui.cbFillingStyle, Qt::black); GuiTools::updateBrushStyles(ui.cbErrorBarFillingStyle, Qt::black); m_initializing = false; } void DatapickerCurveWidget::setCurves(QList list) { if (list.isEmpty()) return; m_curveList = list; m_curve = list.first(); if (list.size()==1) { ui.lName->setEnabled(true); ui.leName->setEnabled(true); ui.lComment->setEnabled(true); ui.leComment->setEnabled(true); ui.leName->setText(m_curve->name()); ui.leComment->setText(m_curve->comment()); } else { ui.lName->setEnabled(false); ui.leName->setEnabled(false); ui.lComment->setEnabled(false); ui.leComment->setEnabled(false); ui.leName->setText(""); ui.leComment->setText(""); } load(); initConnections(); updateSymbolWidgets(); } void DatapickerCurveWidget::initConnections() { connect( m_curve, SIGNAL(aspectDescriptionChanged(const AbstractAspect*)),this, SLOT(curveDescriptionChanged(const AbstractAspect*))); connect( m_curve, SIGNAL(aspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*)), this, SLOT(updateSymbolWidgets()) ); connect( m_curve, SIGNAL(aspectAdded(const AbstractAspect*)), this, SLOT(updateSymbolWidgets()) ); connect( m_curve, SIGNAL(curveErrorTypesChanged(DatapickerCurve::Errors)), this, SLOT(curveErrorsChanged(DatapickerCurve::Errors)) ); connect( m_curve, SIGNAL(pointStyleChanged(Symbol::Style)), this, SLOT(symbolStyleChanged(Symbol::Style))); connect( m_curve, SIGNAL(pointSizeChanged(qreal)), this, SLOT(symbolSizeChanged(qreal))); connect( m_curve, SIGNAL(pointRotationAngleChanged(qreal)), this, SLOT(symbolRotationAngleChanged(qreal))); connect( m_curve, SIGNAL(pointOpacityChanged(qreal)), this, SLOT(symbolOpacityChanged(qreal))); connect( m_curve, SIGNAL(pointBrushChanged(QBrush)), this, SLOT(symbolBrushChanged(QBrush)) ); connect( m_curve, SIGNAL(pointPenChanged(QPen)), this, SLOT(symbolPenChanged(QPen)) ); connect( m_curve, SIGNAL(pointVisibilityChanged(bool)), this, SLOT(symbolVisibleChanged(bool)) ); connect( m_curve, SIGNAL(pointErrorBarBrushChanged(QBrush)), this, SLOT(symbolErrorBarBrushChanged(QBrush)) ); connect( m_curve, SIGNAL(pointErrorBarSizeChanged(qreal)), this, SLOT(symbolErrorBarSizeChanged(qreal)) ); } void DatapickerCurveWidget::hideErrorBarWidgets(bool on) { ui.lErrorBar->setVisible(!on); ui.cbErrorBarFillingStyle->setVisible(!on); ui.kcbErrorBarFillingColor->setVisible(!on); ui.lErrorBarFillingColor->setVisible(!on); ui.lErrorBarFillingStyle->setVisible(!on); ui.sbErrorBarSize->setVisible(!on); ui.lErrorBarSize->setVisible(!on); } //************************************************************* //**** SLOTs for changes triggered in DatapickerCurveWidget *** //************************************************************* //"General"-tab void DatapickerCurveWidget::nameChanged() { if (m_initializing) return; m_curve->setName(ui.leName->text()); } void DatapickerCurveWidget::commentChanged() { if (m_initializing) return; m_curve->setComment(ui.leComment->text()); } void DatapickerCurveWidget::xErrorTypeChanged(int index) { if ( DatapickerCurve::ErrorType(index) != DatapickerCurve::NoError || m_curve->curveErrorTypes().y != DatapickerCurve::NoError ) hideErrorBarWidgets(false); else hideErrorBarWidgets(true); if (m_initializing || m_suppressTypeChange) return; DatapickerCurve::Errors errors = m_curve->curveErrorTypes(); errors.x = DatapickerCurve::ErrorType(index); for (auto* curve : m_curveList) curve->setCurveErrorTypes(errors); } void DatapickerCurveWidget::yErrorTypeChanged(int index) { if ( DatapickerCurve::ErrorType(index) != DatapickerCurve::NoError || m_curve->curveErrorTypes().x != DatapickerCurve::NoError ) hideErrorBarWidgets(false); else hideErrorBarWidgets(true); if (m_initializing || m_suppressTypeChange) return; DatapickerCurve::Errors errors = m_curve->curveErrorTypes(); errors.y = DatapickerCurve::ErrorType(index); for (auto* curve : m_curveList) curve->setCurveErrorTypes(errors); } void DatapickerCurveWidget::styleChanged(int index) { Symbol::Style style = Symbol::Style(index + 1); //enable/disable the filling options in the GUI depending on the currently selected points. if (style != Symbol::Line && style != Symbol::Cross) { ui.cbFillingStyle->setEnabled(true); bool noBrush = (Qt::BrushStyle(ui.cbFillingStyle->currentIndex())==Qt::NoBrush); ui.kcbFillingColor->setEnabled(!noBrush); } else { ui.kcbFillingColor->setEnabled(false); ui.cbFillingStyle->setEnabled(false); } bool noLine = (Qt::PenStyle(ui.cbBorderStyle->currentIndex())== Qt::NoPen); ui.kcbBorderColor->setEnabled(!noLine); ui.sbBorderWidth->setEnabled(!noLine); if (m_initializing) return; for (auto* curve : m_curveList) curve->setPointStyle(style); } void DatapickerCurveWidget::sizeChanged(double value) { if (m_initializing) return; for (auto* curve : m_curveList) curve->setPointSize( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void DatapickerCurveWidget::rotationChanged(int value) { if (m_initializing) return; for (auto* curve : m_curveList) curve->setPointRotationAngle(value); } void DatapickerCurveWidget::opacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* curve : m_curveList) curve->setPointOpacity(opacity); } void DatapickerCurveWidget::errorBarSizeChanged(double value) { if (m_initializing) return; for (auto* curve : m_curveList) curve->setPointErrorBarSize( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void DatapickerCurveWidget::fillingStyleChanged(int index) { Qt::BrushStyle brushStyle = Qt::BrushStyle(index); ui.kcbFillingColor->setEnabled(!(brushStyle==Qt::NoBrush)); if (m_initializing) return; QBrush brush; for (auto* curve : m_curveList) { brush = curve->pointBrush(); brush.setStyle(brushStyle); curve->setPointBrush(brush); } } void DatapickerCurveWidget::errorBarFillingStyleChanged(int index) { Qt::BrushStyle brushStyle = Qt::BrushStyle(index); ui.kcbErrorBarFillingColor->setEnabled(!(brushStyle==Qt::NoBrush)); if (m_initializing) return; QBrush brush; for (auto* curve : m_curveList) { brush = curve->pointBrush(); brush.setStyle(brushStyle); curve->setPointErrorBarBrush(brush); } } void DatapickerCurveWidget::fillingColorChanged(const QColor& color) { if (m_initializing) return; QBrush brush; for (auto* curve : m_curveList) { brush = curve->pointBrush(); brush.setColor(color); curve->setPointBrush(brush); } m_initializing = true; GuiTools::updateBrushStyles(ui.cbFillingStyle, color ); m_initializing = false; } void DatapickerCurveWidget::errorBarFillingColorChanged(const QColor& color) { if (m_initializing) return; QBrush brush; for (auto* curve : m_curveList) { brush = curve->pointErrorBarBrush(); brush.setColor(color); curve->setPointErrorBarBrush(brush); } m_initializing = true; GuiTools::updateBrushStyles(ui.cbErrorBarFillingStyle, color ); m_initializing = false; } void DatapickerCurveWidget::borderStyleChanged(int index) { Qt::PenStyle penStyle=Qt::PenStyle(index); if ( penStyle == Qt::NoPen ) { ui.kcbBorderColor->setEnabled(false); ui.sbBorderWidth->setEnabled(false); } else { ui.kcbBorderColor->setEnabled(true); ui.sbBorderWidth->setEnabled(true); } if (m_initializing) return; QPen pen; for (auto* curve : m_curveList) { pen = curve->pointPen(); pen.setStyle(penStyle); curve->setPointPen(pen); } } void DatapickerCurveWidget::borderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* curve : m_curveList) { pen = curve->pointPen(); pen.setColor(color); curve->setPointPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, color); m_initializing = false; } void DatapickerCurveWidget::borderWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* curve : m_curveList) { pen = curve->pointPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); curve->setPointPen(pen); } } void DatapickerCurveWidget::visibilityChanged(bool state) { if (m_initializing) return; for (auto* curve : m_curveList) curve->setPointVisibility(state); } void DatapickerCurveWidget::updateSymbolWidgets() { const QVector pointsList = m_curve->children(AbstractAspect::IncludeHidden); if (pointsList.isEmpty()) { ui.cbXErrorType->setEnabled(true); ui.cbYErrorType->setEnabled(true); ui.tSymbols->setEnabled(false); m_suppressTypeChange = false; } else { ui.cbXErrorType->setEnabled(false); ui.cbYErrorType->setEnabled(false); ui.tSymbols->setEnabled(true); m_suppressTypeChange = true; } } //************************************************************* //******** SLOTs for changes triggered in DatapickerCurve ***** //************************************************************* void DatapickerCurveWidget::curveDescriptionChanged(const AbstractAspect* aspect) { if (m_curve != aspect) return; m_initializing = true; if (aspect->name() != ui.leName->text()) { ui.leName->setText(aspect->name()); } else if (aspect->comment() != ui.leComment->text()) { ui.leComment->setText(aspect->comment()); } m_initializing = false; } void DatapickerCurveWidget::curveErrorsChanged(DatapickerCurve::Errors errors) { m_initializing = true; ui.cbXErrorType->setCurrentIndex((int) errors.x); ui.cbYErrorType->setCurrentIndex((int) errors.y); m_initializing = false; } void DatapickerCurveWidget::symbolStyleChanged(Symbol::Style style) { m_initializing = true; ui.cbStyle->setCurrentIndex((int)style - 1); m_initializing = false; } void DatapickerCurveWidget::symbolSizeChanged(qreal size) { m_initializing = true; ui.sbSize->setValue( Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } void DatapickerCurveWidget::symbolErrorBarSizeChanged(qreal size) { m_initializing = true; ui.sbErrorBarSize->setValue( Worksheet::convertFromSceneUnits(size, Worksheet::Point) ); m_initializing = false; } void DatapickerCurveWidget::symbolRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbRotation->setValue(round(angle)); m_initializing = false; } void DatapickerCurveWidget::symbolOpacityChanged(qreal opacity) { m_initializing = true; ui.sbOpacity->setValue( round(opacity*100.0) ); m_initializing = false; } void DatapickerCurveWidget::symbolBrushChanged(const QBrush& brush) { m_initializing = true; ui.cbFillingStyle->setCurrentIndex((int) brush.style()); ui.kcbFillingColor->setColor(brush.color()); GuiTools::updateBrushStyles(ui.cbFillingStyle, brush.color()); m_initializing = false; } void DatapickerCurveWidget::symbolErrorBarBrushChanged(const QBrush& brush) { m_initializing = true; ui.cbErrorBarFillingStyle->setCurrentIndex((int) brush.style()); ui.kcbErrorBarFillingColor->setColor(brush.color()); GuiTools::updateBrushStyles(ui.cbErrorBarFillingStyle, brush.color()); m_initializing = false; } void DatapickerCurveWidget::symbolPenChanged(const QPen& pen) { m_initializing = true; ui.cbBorderStyle->setCurrentIndex( (int) pen.style()); ui.kcbBorderColor->setColor( pen.color()); GuiTools::updatePenStyles(ui.cbBorderStyle, pen.color()); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Point)); m_initializing = false; } void DatapickerCurveWidget::symbolVisibleChanged(bool on) { m_initializing = true; ui.chbVisible->setChecked(on); m_initializing = false; } //********************************************************** //******************** SETTINGS **************************** //********************************************************** void DatapickerCurveWidget::load() { if(m_curve == NULL) return; m_initializing = true; ui.cbXErrorType->setCurrentIndex((int) m_curve->curveErrorTypes().x); ui.cbYErrorType->setCurrentIndex((int) m_curve->curveErrorTypes().y); ui.cbStyle->setCurrentIndex( (int)m_curve->pointStyle() - 1 ); ui.sbSize->setValue( Worksheet::convertFromSceneUnits(m_curve->pointSize(), Worksheet::Point) ); ui.sbRotation->setValue( m_curve->pointRotationAngle() ); ui.sbOpacity->setValue( round(m_curve->pointOpacity()*100.0) ); ui.cbFillingStyle->setCurrentIndex( (int) m_curve->pointBrush().style() ); ui.kcbFillingColor->setColor( m_curve->pointBrush().color() ); ui.cbBorderStyle->setCurrentIndex( (int) m_curve->pointPen().style() ); ui.kcbBorderColor->setColor( m_curve->pointPen().color() ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_curve->pointPen().widthF(), Worksheet::Point) ); ui.chbVisible->setChecked( m_curve->pointVisibility() ); ui.cbErrorBarFillingStyle->setCurrentIndex( (int) m_curve->pointErrorBarBrush().style() ); ui.kcbErrorBarFillingColor->setColor( m_curve->pointErrorBarBrush().color() ); ui.sbErrorBarSize->setValue( Worksheet::convertFromSceneUnits(m_curve->pointErrorBarSize(), Worksheet::Point) ); m_initializing = false; } diff --git a/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp index 9b41e87c7..4d349b8b3 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditAddUnitDialog.cpp @@ -1,69 +1,69 @@ /*************************************************************************** File : FITSHeaderEditAddUnitDialog.cpp Project : LabPlot Description : Widget for adding or modifying FITS header keyword units -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 "FITSHeaderEditAddUnitDialog.h" #include "backend/datasources/filters/FITSFilter.h" #include #include #include FITSHeaderEditAddUnitDialog::FITSHeaderEditAddUnitDialog(const QString& unit, QWidget* parent) : QDialog(parent) { ui.setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.horizontalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_okButton->setText(i18n("&Add")); - setWindowTitle(i18n("Add New Unit")); + setWindowTitle(i18nc("@title:window", "Add New Unit")); setWindowIcon(QIcon::fromTheme("document-new")); m_okButton->setEnabled(false); QCompleter* keyCompleter = new QCompleter(FITSFilter::units(), this); ui.leUnit->setCompleter(keyCompleter); ui.leUnit->setPlaceholderText(i18n("Enter unit name here")); connect(ui.leUnit, &QLineEdit::textChanged, this, &FITSHeaderEditAddUnitDialog::unitChanged); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FITSHeaderEditAddUnitDialog::close); connect(btnBox, &QDialogButtonBox::accepted, this, &FITSHeaderEditAddUnitDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &FITSHeaderEditAddUnitDialog::reject); ui.leUnit->setText(unit); } QString FITSHeaderEditAddUnitDialog::unit() const { QString unit = ui.leUnit->text(); if (unit.contains(QLatin1Char('('))) unit = unit.left(unit.indexOf(QLatin1Char('('))-1); return unit; } void FITSHeaderEditAddUnitDialog::unitChanged() { m_okButton->setEnabled(!ui.leUnit->text().isEmpty()); } diff --git a/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp index e8bd11f3a..e1c762efb 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditDialog.cpp @@ -1,108 +1,108 @@ /*************************************************************************** File : FITSHeaderEditDialog.h Project : LabPlot Description : Dialog for listing/editing FITS header keywords -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 "FITSHeaderEditDialog.h" #include #include #include #include #include /*! \class FITSHeaderEditDialog * \brief Dialog class for editing FITS header units. * \since 2.4.0 * \ingroup widgets */ FITSHeaderEditDialog::FITSHeaderEditDialog(QWidget* parent) : QDialog(parent), m_saved(false) { m_headerEditWidget = new FITSHeaderEditWidget(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(m_headerEditWidget); layout->addWidget(btnBox); setLayout(layout); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_okButton->setText(i18n("&Save")); m_okButton->setEnabled(false); connect(btnBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FITSHeaderEditDialog::reject); connect(btnBox, &QDialogButtonBox::accepted, this, &FITSHeaderEditDialog::accept); connect(btnBox, &QDialogButtonBox::rejected, this, &FITSHeaderEditDialog::reject); - setWindowTitle(i18n("FITS Metadata Editor")); + setWindowTitle(i18nc("@title:window", "FITS Metadata Editor")); setWindowIcon(QIcon::fromTheme("document-edit")); connect(m_okButton, &QPushButton::clicked, this, &FITSHeaderEditDialog::save); connect(m_headerEditWidget, &FITSHeaderEditWidget::changed, this, &FITSHeaderEditDialog::headersChanged); setAttribute(Qt::WA_DeleteOnClose); //restore saved settings if available KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditDialog"); if (conf.exists()) KWindowConfig::restoreWindowSize(windowHandle(), conf); else resize( QSize(400,0).expandedTo(minimumSize()) ); } /*! * \brief FITSHeaderEditDialog::~FITSHeaderEditDialog */ FITSHeaderEditDialog::~FITSHeaderEditDialog() { KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditDialog"); KWindowConfig::saveWindowSize(windowHandle(), conf); delete m_headerEditWidget; } void FITSHeaderEditDialog::headersChanged(bool changed) { if (changed) { - setWindowTitle(i18n("FITS Metadata Editor [Changed]")); + setWindowTitle(i18nc("@title:window", "FITS Metadata Editor [Changed]")); m_okButton->setEnabled(true); } else { - setWindowTitle(i18n("FITS Metadata Editor")); + setWindowTitle(i18nc("@title:window", "FITS Metadata Editor")); m_okButton->setEnabled(false); } } /*! * \brief This slot is triggered when the Save button was clicked in the ui. */ void FITSHeaderEditDialog::save() { m_saved = m_headerEditWidget->save(); } /*! * \brief Returns whether there were changes saved. * \return */ bool FITSHeaderEditDialog::saved() const { return m_saved; } diff --git a/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp b/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp index ec7a4872c..a917fdbcb 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditNewKeywordDialog.cpp @@ -1,114 +1,114 @@ /*************************************************************************** File : FITSHeaderEditNewKeywordDialog.cpp Project : LabPlot Description : Widget for adding new keyword in the FITS edit widget -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 "FITSHeaderEditNewKeywordDialog.h" #include #include #include #include #include #include #define FLEN_KEYWORD 75 /* max length of a keyword (HIERARCH convention) */ #define FLEN_VALUE 71 /* max length of a keyword value string */ #define FLEN_COMMENT 73 /* max length of a keyword comment string */ /*! \class FITSHeaderEditNewKeywordDialog * \brief Dialog class for adding new keywords to the FITSHeaderEditDialog's table. * \since 2.4.0 * \ingroup widgets */ FITSHeaderEditNewKeywordDialog::FITSHeaderEditNewKeywordDialog(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); ui.gridLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); - m_okButton->setText(i18n("&Add keyword")); + m_okButton->setText(i18n("&Add Keyword")); connect(btnBox, &QDialogButtonBox::clicked, this, &FITSHeaderEditNewKeywordDialog::slotButtonClicked); - setWindowTitle(i18n("Specify the new keyword")); + setWindowTitle(i18nc("@title:window", "Specify the New Keyword")); setWindowIcon(QIcon::fromTheme("document-new")); QCompleter* keyCompleter = new QCompleter(FITSFilter::standardKeywords(), this); keyCompleter->setCaseSensitivity(Qt::CaseInsensitive); ui.leKey->setCompleter(keyCompleter); ui.leKey->setPlaceholderText(i18n("Specify the name")); ui.leValue->setPlaceholderText(i18n("Specify the value")); ui.leComment->setPlaceholderText(i18n("Specify the comment")); ui.leKey->setMaxLength(FLEN_KEYWORD); ui.leValue->setMaxLength(FLEN_VALUE); ui.leComment->setMaxLength(FLEN_COMMENT); } /*! * \brief Decides whether the keyword can be used, messagebox pops up if the keywords key is empty. * \return Whether the keyword was "Ok" or not. */ int FITSHeaderEditNewKeywordDialog::okClicked() { if (!ui.leKey->text().isEmpty()) { m_newKeyword = FITSFilter::Keyword(ui.leKey->text(), ui.leValue->text(), ui.leComment->text()); return QMessageBox::Ok; } else { - const int yesNo = KMessageBox::warningYesNo(this, i18n("Can't add new keyword without key, would you like to try again?"), + const int yesNo = KMessageBox::warningYesNo(this, i18n("Cannot add new keyword without key, would you like to try again?"), i18n("Cannot add empty key")); if (yesNo == KMessageBox::No) return QMessageBox::Cancel; return yesNo; } } /*! * \brief Returns the new keyword. * \return The newly constructed keyword from the line edits. */ FITSFilter::Keyword FITSHeaderEditNewKeywordDialog::newKeyword() const { return m_newKeyword; } /*! * \brief Decides whether the dialog should move in an accepted state or canceled. * \param button the button clicked */ void FITSHeaderEditNewKeywordDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) { int okClickedBtn = okClicked(); if (okClickedBtn == QMessageBox::Ok) accept(); else if (okClickedBtn == QMessageBox::Cancel) reject(); } else if (button == m_cancelButton) reject(); } diff --git a/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp b/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp index 257f21eb8..61dd4fdae 100644 --- a/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp +++ b/src/kdefrontend/widgets/FITSHeaderEditWidget.cpp @@ -1,630 +1,630 @@ /*************************************************************************** File : FITSHeaderEditWidget.cpp Project : LabPlot Description : Widget for listing/editing FITS header keywords -------------------------------------------------------------------- Copyright : (C) 2016-2017 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 "FITSHeaderEditWidget.h" #include "ui_fitsheadereditwidget.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/lib/macros.h" #include "FITSHeaderEditNewKeywordDialog.h" #include "FITSHeaderEditAddUnitDialog.h" #include #include #include #include #include #include #include #include /*! \class FITSHeaderEditWidget * \brief Widget for listing/editing FITS header keywords * \since 2.4.0 * \ingroup kdefrontend/widgets */ FITSHeaderEditWidget::FITSHeaderEditWidget(QWidget* parent) : QWidget(parent), ui(new Ui::FITSHeaderEditWidget()), m_fitsFilter(new FITSFilter()), m_initializingTable(false) { ui->setupUi(this); initActions(); connectActions(); initContextMenus(); ui->bOpen->setIcon(QIcon::fromTheme("document-open")); ui->bAddKey->setIcon(QIcon::fromTheme("list-add")); ui->bAddKey->setEnabled(false); ui->bAddKey->setToolTip(i18n("Add new keyword")); ui->bRemoveKey->setIcon(QIcon::fromTheme("list-remove")); ui->bRemoveKey->setEnabled(false); ui->bRemoveKey->setToolTip(i18n("Remove selected keyword")); ui->bAddUnit->setIcon(QIcon::fromTheme("document-new")); ui->bAddUnit->setEnabled(false); ui->bAddUnit->setToolTip(i18n("Add unit to keyword")); ui->bClose->setIcon(QIcon::fromTheme("document-close")); ui->bClose->setEnabled(false); ui->bClose->setToolTip(i18n("Close file")); ui->twKeywordsTable->setColumnCount(3); ui->twExtensions->setSelectionMode(QAbstractItemView::SingleSelection); ui->twExtensions->headerItem()->setText(0, i18n("Content")); ui->twKeywordsTable->setHorizontalHeaderItem(0, new QTableWidgetItem(i18n("Key"))); ui->twKeywordsTable->setHorizontalHeaderItem(1, new QTableWidgetItem(i18n("Value"))); ui->twKeywordsTable->setHorizontalHeaderItem(2, new QTableWidgetItem(i18n("Comment"))); ui->twKeywordsTable->setAlternatingRowColors(true); ui->twKeywordsTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); ui->twKeywordsTable->horizontalHeader()->setStretchLastSection(true); ui->twKeywordsTable->installEventFilter(this); ui->twExtensions->installEventFilter(this); setAttribute(Qt::WA_DeleteOnClose); connect(ui->bAddUnit, &QPushButton::clicked, m_action_addmodify_unit, &QAction::triggered); connect(ui->bClose, &QPushButton::clicked, this, &FITSHeaderEditWidget::closeFile); connect(ui->bOpen, &QPushButton::clicked, this, &FITSHeaderEditWidget::openFile); connect(ui->bAddKey, &QPushButton::clicked, this, &FITSHeaderEditWidget::addKeyword); connect(ui->bRemoveKey, &QPushButton::clicked, this, &FITSHeaderEditWidget::removeKeyword); connect(ui->twKeywordsTable, &QTableWidget::itemClicked, this, &FITSHeaderEditWidget::enableButtonAddUnit); connect(ui->twKeywordsTable, &QTableWidget::itemChanged, this, &FITSHeaderEditWidget::updateKeyword); connect(ui->twExtensions, &QTreeWidget::itemClicked, this, &FITSHeaderEditWidget::fillTableSlot); connect(ui->twExtensions, &QTreeWidget::itemClicked, this, &FITSHeaderEditWidget::enableButtonCloseFile); } /*! * \brief Destructor */ FITSHeaderEditWidget::~FITSHeaderEditWidget() { delete m_fitsFilter; } /*! * \brief Fills the keywords tablewidget. * If the selected extension was not yet selected before, then the keywords are read from the file * and then the table is filled, otherwise the table is filled using the already existing keywords. */ void FITSHeaderEditWidget::fillTable() { m_initializingTable = true; if (!m_extensionDatas.contains(m_seletedExtension)) { m_extensionDatas[m_seletedExtension].keywords = m_fitsFilter->chduKeywords(m_seletedExtension); m_extensionDatas[m_seletedExtension].updates.updatedKeywords.reserve(m_extensionDatas[m_seletedExtension].keywords.size()); m_extensionDatas[m_seletedExtension].updates.updatedKeywords.resize(m_extensionDatas[m_seletedExtension].keywords.size()); m_fitsFilter->parseHeader(m_seletedExtension, ui->twKeywordsTable); } else { QList keywords = m_extensionDatas[m_seletedExtension].keywords; for (int i = 0; i < m_extensionDatas[m_seletedExtension].updates.updatedKeywords.size(); ++i) { FITSFilter::Keyword keyword = m_extensionDatas[m_seletedExtension].updates.updatedKeywords.at(i); if (!keyword.key.isEmpty()) keywords.operator [](i).key = keyword.key; if (!keyword.value.isEmpty()) keywords.operator [](i).value = keyword.value; if (!keyword.comment.isEmpty()) keywords.operator [](i).comment = keyword.comment; } for (const FITSFilter::Keyword& key : m_extensionDatas[m_seletedExtension].updates.newKeywords) keywords.append(key); m_fitsFilter->parseHeader(QString(), ui->twKeywordsTable, false, keywords); } m_initializingTable = false; } /*! * \brief Fills the tablewidget with the keywords of extension \a item * \param item the extension selected * \param col the column of the selected item */ void FITSHeaderEditWidget::fillTableSlot(QTreeWidgetItem *item, int col) { WAIT_CURSOR; const QString& itemText = item->text(col); QString selectedExtension; int extType = 0; if (itemText.contains(QLatin1String("IMAGE #")) || itemText.contains(QLatin1String("ASCII_TBL #")) || itemText.contains(QLatin1String("BINARY_TBL #"))) extType = 1; else if (!itemText.compare(QLatin1String("Primary header"))) extType = 2; if (extType == 0) { if (item->parent() != 0) { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(0) + '[' + item->text(col) + ']'; } } else if (extType == 1) { if (item->parent() != 0) { if (item->parent()->parent() != 0) { bool ok; int hduNum = itemText.rightRef(1).toInt(&ok); selectedExtension = item->parent()->parent()->text(0) + '[' + QString::number(hduNum-1) + ']'; } } } else { if (item->parent()->parent() != 0) selectedExtension = item->parent()->parent()->text(col); } if (!selectedExtension.isEmpty()) { if (!(m_seletedExtension == selectedExtension)) { m_seletedExtension = selectedExtension; fillTable(); } } RESET_CURSOR; } /*! * \brief Shows a dialog for opening a FITS file * If the returned file name is not empty (so a FITS file was selected) and it's not opened yet * then the file is parsed, so the treeview for the extensions is built and the table is filled. */ void FITSHeaderEditWidget::openFile() { KConfigGroup conf(KSharedConfig::openConfig(), "FITSHeaderEditWidget"); QString dir = conf.readEntry("LastDir", ""); QString fileName = QFileDialog::getOpenFileName(this, i18n("Open FITS file"), dir, i18n("FITS files (*.fits *.fit *.fts)")); if (fileName.isEmpty()) return; int pos = fileName.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = fileName.left(pos); if (newDir!=dir) conf.writeEntry("LastDir", newDir); } WAIT_CURSOR; QTreeWidgetItem* root = ui->twExtensions->invisibleRootItem(); const int childCount = root->childCount(); bool opened = false; for (int i = 0; i < childCount; ++i) { if(root->child(i)->text(0) == fileName) { opened = true; break; } } if (!opened) { for (QTreeWidgetItem* item : ui->twExtensions->selectedItems()) item->setSelected(false); m_fitsFilter->parseExtensions(fileName, ui->twExtensions); ui->twExtensions->resizeColumnToContents(0); if (ui->twExtensions->selectedItems().size() > 0) fillTableSlot(ui->twExtensions->selectedItems().at(0), 0); ui->bAddKey->setEnabled(true); ui->bRemoveKey->setEnabled(true); ui->bAddUnit->setEnabled(true); ui->bClose->setEnabled(false); } else { KMessageBox::information(this, i18n("Cannot open file, file already opened."), i18n("File already opened")); } enableButtonAddUnit(); RESET_CURSOR; } /*! * \brief Triggered when clicking the Save button * Saves the modifications (new keywords, new keyword units, keyword modifications, * deleted keywords, deleted extensions) to the FITS files. * \return \c true if there was something saved, otherwise false */ bool FITSHeaderEditWidget::save() { bool saved = false; for (const QString& fileName : m_extensionDatas.keys()) { if (m_extensionDatas[fileName].updates.newKeywords.size() > 0) { m_fitsFilter->addNewKeyword(fileName,m_extensionDatas[fileName].updates.newKeywords); if (!saved) saved = true; } if (m_extensionDatas[fileName].updates.removedKeywords.size() > 0) { m_fitsFilter->deleteKeyword(fileName, m_extensionDatas[fileName].updates.removedKeywords); if (!saved) saved = true; } if (!saved) { for (const FITSFilter::Keyword& key : m_extensionDatas[fileName].updates.updatedKeywords) { if (!key.isEmpty()) { saved = true; break; } } } m_fitsFilter->updateKeywords(fileName, m_extensionDatas[fileName].keywords, m_extensionDatas[fileName].updates.updatedKeywords); m_fitsFilter->addKeywordUnit(fileName, m_extensionDatas[fileName].keywords); m_fitsFilter->addKeywordUnit(fileName, m_extensionDatas[fileName].updates.newKeywords); } if (m_removedExtensions.size() > 0) { m_fitsFilter->removeExtensions(m_removedExtensions); if (!saved) saved = true; } if (saved) { //to reset the window title emit changed(false); } return saved; } /*! * \brief Initializes the context menu's actions. */ void FITSHeaderEditWidget::initActions() { - m_action_add_keyword = new QAction(QIcon::fromTheme("list-add"), i18n("Add new keyword"), this); - m_action_remove_keyword = new QAction(QIcon::fromTheme("list-remove"), i18n("Remove keyword"), this); + m_action_add_keyword = new QAction(QIcon::fromTheme("list-add"), i18n("Add New Keyword"), this); + m_action_remove_keyword = new QAction(QIcon::fromTheme("list-remove"), i18n("Remove Keyword"), this); m_action_remove_extension = new QAction(i18n("Delete"), this); - m_action_addmodify_unit = new QAction(i18n("Add unit"), this); + m_action_addmodify_unit = new QAction(i18n("Add Unit"), this); } /*! * \brief Connects signals of the actions to the appropriate slots. */ void FITSHeaderEditWidget::connectActions() { connect(m_action_add_keyword, SIGNAL(triggered()), this, SLOT(addKeyword())); connect(m_action_remove_keyword, SIGNAL(triggered()), this, SLOT(removeKeyword())); connect(m_action_remove_extension, SIGNAL(triggered()), this, SLOT(removeExtension())); connect(m_action_addmodify_unit, SIGNAL(triggered()), this, SLOT(addModifyKeywordUnit())); } /*! * \brief Initializes the context menus. */ void FITSHeaderEditWidget::initContextMenus() { m_KeywordActionsMenu = new QMenu(this); m_KeywordActionsMenu->addAction(m_action_add_keyword); m_KeywordActionsMenu->addAction(m_action_remove_keyword); m_KeywordActionsMenu->addSeparator(); m_KeywordActionsMenu->addAction(m_action_addmodify_unit); m_ExtensionActionsMenu = new QMenu(this); m_ExtensionActionsMenu->addAction(m_action_remove_extension); } /*! * \brief Shows a FITSHeaderEditNewKeywordDialog and decides whether the new keyword provided in the dialog * can be added to the new keywords or not. Updates the tablewidget if it's needed. */ void FITSHeaderEditWidget::addKeyword() { FITSHeaderEditNewKeywordDialog* newKeywordDialog = new FITSHeaderEditNewKeywordDialog; m_initializingTable = true; if (newKeywordDialog->exec() == QDialog::Accepted) { FITSFilter::Keyword newKeyWord = newKeywordDialog->newKeyword(); QList currentKeywords = m_extensionDatas[m_seletedExtension].keywords; for(const FITSFilter::Keyword& keyword : currentKeywords) { if (keyword.operator==(newKeyWord)) { - KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot add keyword")); + KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot Add Keyword")); return; } } for(const FITSFilter::Keyword& keyword : m_extensionDatas[m_seletedExtension].updates.newKeywords) { if (keyword.operator==(newKeyWord)) { - KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot add keyword")); + KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot Add Keyword")); return; } } for(const QString& keyword : mandatoryKeywords()) { if (!keyword.compare(newKeyWord.key)) { KMessageBox::information(this, i18n("Cannot add mandatory keyword, they are already present"), - i18n("Cannot add keyword")); + i18n("Cannot Add Keyword")); return; } } /* - Column related keyword (TFIELDS, TTYPEn,TFORMn, etc.) in an image - SIMPLE, EXTEND, or BLOCKED keyword in any extension - BSCALE, BZERO, BUNIT, BLANK, DATAMAX, DATAMIN keywords in a table - Keyword name contains illegal character */ m_extensionDatas[m_seletedExtension].updates.newKeywords.append(newKeyWord); const int lastRow = ui->twKeywordsTable->rowCount(); ui->twKeywordsTable->setRowCount(lastRow + 1); QTableWidgetItem* newKeyWordItem = new QTableWidgetItem(newKeyWord.key); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->twKeywordsTable->setItem(lastRow, 0, newKeyWordItem); newKeyWordItem = new QTableWidgetItem(newKeyWord.value); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->twKeywordsTable->setItem(lastRow, 1, newKeyWordItem); newKeyWordItem = new QTableWidgetItem(newKeyWord.comment); newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->twKeywordsTable->setItem(lastRow, 2, newKeyWordItem); emit changed(true); } m_initializingTable = false; delete newKeywordDialog; } /*! * \brief Shows a messagebox whether we want to remove the keyword or not. * Mandatory keywords cannot be deleted. */ void FITSHeaderEditWidget::removeKeyword() { const int row = ui->twKeywordsTable->currentRow(); if (row == -1) return; QString key = ui->twKeywordsTable->item(row, 0)->text(); - const int rc = KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete the keyword '%1'?").arg(key), - i18n("Confirm deletion")); + const int rc = KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete the keyword '%1'?", key), + i18n("Confirm Deletion")); if (rc == KMessageBox::Yes) { bool remove = true; for(const QString& k : mandatoryKeywords()) { if (!k.compare(key)) { remove = false; break; } } if (remove) { FITSFilter::Keyword toRemove = FITSFilter::Keyword(key, ui->twKeywordsTable->item(row, 1)->text(), ui->twKeywordsTable->item(row, 2)->text()); ui->twKeywordsTable->removeRow(row); m_extensionDatas[m_seletedExtension].keywords.removeAt(row); m_extensionDatas[m_seletedExtension].updates.removedKeywords.append(toRemove); emit changed(true); } else - KMessageBox::information(this, i18n("Cannot remove mandatory keyword."), i18n("Removing keyword")); + KMessageBox::information(this, i18n("Cannot remove mandatory keyword."), i18n("Removing Keyword")); } enableButtonAddUnit(); } /*! * \brief Trigggered when an item was updated by the user in the tablewidget * \param item the item which was updated */ void FITSHeaderEditWidget::updateKeyword(QTableWidgetItem *item) { if (!m_initializingTable) { const int row = item->row(); int idx; bool fromNewKeyword = false; if (row > m_extensionDatas[m_seletedExtension].keywords.size()-1) { idx = row - m_extensionDatas[m_seletedExtension].keywords.size(); fromNewKeyword = true; } else idx = row; if (item->column() == 0) { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).key = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.keyUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).key = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.keyUpdated = true; } } else if (item->column() == 1) { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).value = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.valueUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).value = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.valueUpdated = true; } } else { if (!fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.updatedKeywords.operator [](idx).comment = item->text(); m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.commentUpdated = true; } else { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).comment = item->text(); m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.commentUpdated = true; } } emit changed(true); } } /*! * \brief Shows a FITSHeaderEditAddUnitDialog on the selected keyword (provides the keyword's unit to the * dialog if it had one) and if the dialog was accepted then the new keyword unit is set and the tablewidget * is updated (filled with the modifications). */ void FITSHeaderEditWidget::addModifyKeywordUnit() { FITSHeaderEditAddUnitDialog* addUnitDialog; const int selectedRow = ui->twKeywordsTable->currentRow(); int idx; bool fromNewKeyword = false; if (selectedRow > m_extensionDatas[m_seletedExtension].keywords.size()-1) { idx = selectedRow - m_extensionDatas[m_seletedExtension].keywords.size(); fromNewKeyword = true; } else idx = selectedRow; QString unit; if (fromNewKeyword) { if (!m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) unit = m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit; } else { if (!m_extensionDatas[m_seletedExtension].keywords.at(idx).unit.isEmpty()) unit = m_extensionDatas[m_seletedExtension].keywords.at(idx).unit; } addUnitDialog = new FITSHeaderEditAddUnitDialog(unit); if (addUnitDialog->exec() == QDialog::Accepted) { if (fromNewKeyword) { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).unit = addUnitDialog->unit(); if (!m_extensionDatas[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) { m_extensionDatas[m_seletedExtension].updates.newKeywords.operator [](idx).updates.unitUpdated = true;; } } else { m_extensionDatas[m_seletedExtension].keywords.operator [](idx).unit = addUnitDialog->unit(); if (!m_extensionDatas[m_seletedExtension].keywords.at(idx).unit.isEmpty()) m_extensionDatas[m_seletedExtension].keywords.operator [](idx).updates.unitUpdated = true; } emit changed(true); fillTable(); } delete addUnitDialog; } /*! * \brief Removes the selected extension from the extensions treeview * If the last extension is removed from the tree, then the extension and the file will be removed too. */ void FITSHeaderEditWidget::removeExtension() { QTreeWidgetItem* current = ui->twExtensions->currentItem(); QTreeWidgetItem* newCurrent = ui->twExtensions->itemBelow(current); if (current->parent()) { if (current->parent()->childCount() < 2) delete current->parent(); else delete current; } const QStringList keys = m_extensionDatas.keys(); const int selectedidx = keys.indexOf(m_seletedExtension); if (selectedidx > 0) { const QString& ext = m_seletedExtension; m_extensionDatas.remove(ext); m_removedExtensions.append(ext); m_seletedExtension = keys.at(selectedidx-1); fillTable(); } ui->twExtensions->setCurrentItem(newCurrent); emit changed(true); } /*! * \brief Returns a list of mandatory keywords according to the currently selected extension. * If the currently selected extension is an image then it returns the mandatory keywords of an image, * otherwise the mandatory keywords of a table * \return a list of mandatory keywords */ QList FITSHeaderEditWidget::mandatoryKeywords() const { QList mandatoryKeywords; const QTreeWidgetItem* currentItem = ui->twExtensions->currentItem(); if (currentItem->parent()->text(0).compare(QLatin1String("Images"))) mandatoryKeywords = FITSFilter::mandatoryImageExtensionKeywords(); else mandatoryKeywords = FITSFilter::mandatoryTableExtensionKeywords(); return mandatoryKeywords; } /*! * \brief Manipulates the contextmenu event of the widget * \param watched the object on which the event occurred * \param event the event watched * \return */ bool FITSHeaderEditWidget::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::ContextMenu) { QContextMenuEvent *cm_event = static_cast(event); const QPoint& global_pos = cm_event->globalPos(); if (watched == ui->twKeywordsTable) { if (ui->twExtensions->selectedItems().size() != 0) m_KeywordActionsMenu->exec(global_pos); } else if (watched == ui->twExtensions) { if (ui->twExtensions->selectedItems().size() != 0) { QTreeWidgetItem* current = ui->twExtensions->currentItem(); int col = ui->twExtensions->currentColumn(); if (current->parent()) { if ((current->text(col) != QLatin1String("Images")) && (current->text(col) != QLatin1String("Tables"))) m_ExtensionActionsMenu->exec(global_pos); } } } else return QWidget::eventFilter(watched, event); return true; } else return QWidget::eventFilter(watched, event); } void FITSHeaderEditWidget::closeFile() { if (ui->twExtensions->currentItem()) { QTreeWidgetItem* current = ui->twExtensions->currentItem(); int idxOfCurrentAsTopLevel = -1; for (int i = 0; i < ui->twExtensions->topLevelItemCount(); ++i) { if (current == ui->twExtensions->topLevelItem(i)) { idxOfCurrentAsTopLevel = i; break; } } QTreeWidgetItem* newCurrent = (QTreeWidgetItem*)0; if (idxOfCurrentAsTopLevel == 0) { if (ui->twExtensions->topLevelItemCount() == 1) { //last file closed, deactivate action buttons, clear keywords table ui->twKeywordsTable->setRowCount(0); ui->bClose->setEnabled(false); ui->bAddUnit->setEnabled(false); ui->bAddKey->setEnabled(false); ui->bRemoveKey->setEnabled(false); } else newCurrent = ui->twExtensions->topLevelItem(idxOfCurrentAsTopLevel + 1); } else newCurrent = ui->twExtensions->topLevelItem(idxOfCurrentAsTopLevel - 1); if (newCurrent) { m_seletedExtension = newCurrent->text(0); fillTable(); } for(const QString& key : m_extensionDatas.keys()) { if (key.startsWith(current->text(0))) m_extensionDatas.remove(key); } delete current; enableButtonAddUnit(); emit changed(true); } } void FITSHeaderEditWidget::enableButtonAddUnit() { if (ui->twKeywordsTable->currentItem() != nullptr) ui->bAddUnit->setEnabled(true); else ui->bAddUnit->setEnabled(false); } void FITSHeaderEditWidget::enableButtonCloseFile(QTreeWidgetItem* item,int col) { Q_UNUSED(col) ui->bClose->setEnabled(item->parent() ? false : true); } diff --git a/src/kdefrontend/widgets/LabelWidget.cpp b/src/kdefrontend/widgets/LabelWidget.cpp index 5d2e5c940..9dafa97da 100644 --- a/src/kdefrontend/widgets/LabelWidget.cpp +++ b/src/kdefrontend/widgets/LabelWidget.cpp @@ -1,823 +1,823 @@ /*************************************************************************** File : LabelWidget.cc Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2017 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Description : label settings widget ***************************************************************************/ /*************************************************************************** * * * 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 "LabelWidget.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "tools/TeXRenderer.h" #include #include #include #include #include #include #include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include #include #include #endif /*! \class LabelWidget \brief Widget for editing the properties of a TextLabel object, mostly used in an appropriate dock widget. In order the properties of the label to be shown, \c loadConfig() has to be called with the correspondig KConfigGroup (settings for a label in *Plot, Axis etc. or for an independent label on the worksheet). \ingroup kdefrontend */ LabelWidget::LabelWidget(QWidget* parent) : QWidget(parent), m_label(0), m_initializing(false), m_dateTimeMenu(new QMenu(this)), m_teXEnabled(false) { ui.setupUi(this); m_dateTimeMenu->setSeparatorsCollapsible(false); //we don't want the first separator to be removed ui.kcbFontColor->setColor(Qt::black); // default color //Icons ui.tbFontBold->setIcon( QIcon::fromTheme(QLatin1String("format-text-bold")) ); ui.tbFontItalic->setIcon( QIcon::fromTheme(QLatin1String("format-text-italic")) ); ui.tbFontUnderline->setIcon( QIcon::fromTheme(QLatin1String("format-text-underline")) ); ui.tbFontStrikeOut->setIcon( QIcon::fromTheme(QLatin1String("format-text-strikethrough")) ); ui.tbFontSuperScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-superscript")) ); ui.tbFontSubScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-subscript")) ); ui.tbSymbols->setIcon( QIcon::fromTheme(QLatin1String("labplot-format-text-symbol")) ); ui.tbDateTime->setIcon( QIcon::fromTheme(QLatin1String("chronometer")) ); ui.tbTexUsed->setIcon( QIcon::fromTheme(QLatin1String("labplot-TeX-logo")) ); //Positioning and alignment - ui.cbPositionX->addItem(i18n("left")); - ui.cbPositionX->addItem(i18n("center")); - ui.cbPositionX->addItem(i18n("right")); - ui.cbPositionX->addItem(i18n("custom")); - - ui.cbPositionY->addItem(i18n("top")); - ui.cbPositionY->addItem(i18n("center")); - ui.cbPositionY->addItem(i18n("bottom")); - ui.cbPositionY->addItem(i18n("custom")); - - ui.cbHorizontalAlignment->addItem(i18n("left")); - ui.cbHorizontalAlignment->addItem(i18n("center")); - ui.cbHorizontalAlignment->addItem(i18n("right")); - - ui.cbVerticalAlignment->addItem(i18n("top")); - ui.cbVerticalAlignment->addItem(i18n("center")); - ui.cbVerticalAlignment->addItem(i18n("bottom")); + ui.cbPositionX->addItem(i18n("Left")); + ui.cbPositionX->addItem(i18n("Center")); + ui.cbPositionX->addItem(i18n("Right")); + ui.cbPositionX->addItem(i18n("Custom")); + + ui.cbPositionY->addItem(i18n("Top")); + ui.cbPositionY->addItem(i18n("Center")); + ui.cbPositionY->addItem(i18n("Bottom")); + ui.cbPositionY->addItem(i18n("Custom")); + + ui.cbHorizontalAlignment->addItem(i18n("Left")); + ui.cbHorizontalAlignment->addItem(i18n("Center")); + ui.cbHorizontalAlignment->addItem(i18n("Right")); + + ui.cbVerticalAlignment->addItem(i18n("Top")); + ui.cbVerticalAlignment->addItem(i18n("Center")); + ui.cbVerticalAlignment->addItem(i18n("Bottom")); //check whether the used latex compiler is available. //Following logic is implemented (s.a. LabelWidget::teXUsedChanged()): //1. in case latex was used to generate the text label in the stored project //and no latex is available on the target system, latex button is toggled and //the user still can switch to the non-latex mode. //2. in case the label was in the non-latex mode and no latex is available, //deactivate the latex button so the user cannot switch to this mode. m_teXEnabled = TeXRenderer::enabled(); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teLabel->document()); m_highlighter->setDefinition(m_repository.definitionForName(QLatin1String("LaTeX"))); m_highlighter->setTheme( (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme) ); #endif //SLOTS // text properties connect(ui.tbTexUsed, SIGNAL(clicked(bool)), this, SLOT(teXUsedChanged(bool)) ); connect(ui.teLabel, SIGNAL(textChanged()), this, SLOT(textChanged())); connect(ui.teLabel, SIGNAL(currentCharFormatChanged(QTextCharFormat)), this, SLOT(charFormatChanged(QTextCharFormat))); connect(ui.kcbFontColor, SIGNAL(changed(QColor)), this, SLOT(fontColorChanged(QColor))); connect(ui.kcbBackgroundColor, SIGNAL(changed(QColor)), this, SLOT(backgroundColorChanged(QColor))); connect(ui.tbFontBold, SIGNAL(clicked(bool)), this, SLOT(fontBoldChanged(bool))); connect(ui.tbFontItalic, SIGNAL(clicked(bool)), this, SLOT(fontItalicChanged(bool))); connect(ui.tbFontUnderline, SIGNAL(clicked(bool)), this, SLOT(fontUnderlineChanged(bool))); connect(ui.tbFontStrikeOut, SIGNAL(clicked(bool)), this, SLOT(fontStrikeOutChanged(bool))); connect(ui.tbFontSuperScript, SIGNAL(clicked(bool)), this, SLOT(fontSuperScriptChanged(bool))); connect(ui.tbFontSubScript, SIGNAL(clicked(bool)), this, SLOT(fontSubScriptChanged(bool))); connect(ui.tbSymbols, SIGNAL(clicked(bool)), this, SLOT(charMenu())); connect(ui.tbDateTime, SIGNAL(clicked(bool)), this, SLOT(dateTimeMenu())); connect(m_dateTimeMenu, SIGNAL(triggered(QAction*)), this, SLOT(insertDateTime(QAction*)) ); connect(ui.kfontRequester, SIGNAL(fontSelected(QFont)), this, SLOT(fontChanged(QFont))); connect(ui.kfontRequesterTeX, SIGNAL(fontSelected(QFont)), this, SLOT(teXFontChanged(QFont))); connect(ui.sbFontSize, SIGNAL(valueChanged(int)), this, SLOT(fontSizeChanged(int)) ); // geometry connect( ui.cbPositionX, SIGNAL(currentIndexChanged(int)), this, SLOT(positionXChanged(int)) ); connect( ui.cbPositionY, SIGNAL(currentIndexChanged(int)), this, SLOT(positionYChanged(int)) ); connect( ui.sbPositionX, SIGNAL(valueChanged(double)), this, SLOT(customPositionXChanged(double)) ); connect( ui.sbPositionY, SIGNAL(valueChanged(double)), this, SLOT(customPositionYChanged(double)) ); connect( ui.cbHorizontalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(horizontalAlignmentChanged(int)) ); connect( ui.cbVerticalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(verticalAlignmentChanged(int)) ); connect( ui.sbRotation, SIGNAL(valueChanged(int)), this, SLOT(rotationChanged(int)) ); connect( ui.sbOffsetX, SIGNAL(valueChanged(double)), this, SLOT(offsetXChanged(double)) ); connect( ui.sbOffsetY, SIGNAL(valueChanged(double)), this, SLOT(offsetYChanged(double)) ); connect( ui.chbVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 ui.tbFontUnderline->hide(); ui.tbFontStrikeOut->hide(); } void LabelWidget::setLabels(QList labels) { m_labelsList = labels; m_label = labels.first(); ui.lOffsetX->hide(); ui.lOffsetY->hide(); ui.sbOffsetX->hide(); ui.sbOffsetY->hide(); this->load(); initConnections(); } void LabelWidget::setAxes(QList axes) { m_labelsList.clear(); for (auto* axis : axes) { m_labelsList.append(axis->title()); connect(axis, SIGNAL(titleOffsetXChanged(qreal)), this, SLOT(labelOffsetxChanged(qreal)) ); connect(axis, SIGNAL(titleOffsetYChanged(qreal)), this, SLOT(labelOffsetyChanged(qreal)) ); connect(axis->title(), SIGNAL(rotationAngleChanged(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); } m_axesList = axes; m_label = m_labelsList.first(); this->load(); initConnections(); } void LabelWidget::initConnections() const { connect( m_label, SIGNAL(textWrapperChanged(TextLabel::TextWrapper)), this, SLOT(labelTextWrapperChanged(TextLabel::TextWrapper)) ); connect( m_label, SIGNAL(teXImageUpdated(bool)), this, SLOT(labelTeXImageUpdated(bool)) ); connect( m_label, SIGNAL(teXFontChanged(QFont)), this, SLOT(labelTeXFontChanged(QFont)) ); connect( m_label, SIGNAL(teXFontColorChanged(QColor)), this, SLOT(labelTeXFontColorChanged(QColor)) ); connect( m_label, SIGNAL(positionChanged(TextLabel::PositionWrapper)), this, SLOT(labelPositionChanged(TextLabel::PositionWrapper)) ); connect( m_label, SIGNAL(horizontalAlignmentChanged(TextLabel::HorizontalAlignment)), this, SLOT(labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment)) ); connect( m_label, SIGNAL(verticalAlignmentChanged(TextLabel::VerticalAlignment)), this, SLOT(labelVerticalAlignmentChanged(TextLabel::VerticalAlignment)) ); connect( m_label, SIGNAL(rotationAngleChanged(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); connect( m_label, SIGNAL(visibleChanged(bool)), this, SLOT(labelVisibleChanged(bool)) ); } /*! * enables/disables the "fixed label"-mode, used when displaying * the properties of axis' title label. * In this mode, in the "geometry"-part only the offset (offset to the axis) * and the rotation of the label are available. */ void LabelWidget::setFixedLabelMode(const bool b) { ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(b); ui.lOffsetY->setVisible(b); ui.sbOffsetX->setVisible(b); ui.sbOffsetY->setVisible(b); } /*! * enables/disables all geometry relevant widgets. * Used when displaying legend's title label. */ void LabelWidget::setNoGeometryMode(const bool b) { ui.lGeometry->setVisible(!b); ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(!b); ui.lOffsetY->setVisible(!b); ui.sbOffsetX->setVisible(!b); ui.sbOffsetY->setVisible(!b); ui.lRotation->setVisible(!b); ui.sbRotation->setVisible(!b); } //********************************************************** //****** SLOTs for changes triggered in LabelWidget ******** //********************************************************** // text formating slots void LabelWidget::textChanged() { if (m_initializing) return; if (ui.tbTexUsed->isChecked()) { QString text=ui.teLabel->toPlainText(); TextLabel::TextWrapper wrapper(text, true); for (auto* label : m_labelsList) label->setText(wrapper); } else { //save an empty string instead of a html-string with empty body, if no text available in QTextEdit QString text; if (ui.teLabel->toPlainText() == "") text = ""; else text = ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, false); for (auto* label : m_labelsList) label->setText(wrapper); } } void LabelWidget::charFormatChanged(const QTextCharFormat& format) { if(ui.tbTexUsed->isChecked()) return; // update button state ui.tbFontBold->setChecked(ui.teLabel->fontWeight()==QFont::Bold); ui.tbFontItalic->setChecked(ui.teLabel->fontItalic()); ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline()); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript); ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript); //font and colors ui.kcbFontColor->setColor(format.foreground().color()); ui.kcbBackgroundColor->setColor(format.background().color()); ui.kfontRequester->setFont(format.font()); } void LabelWidget::teXUsedChanged(bool checked) { //hide text editing elements if TeX-option is used ui.tbFontBold->setVisible(!checked); ui.tbFontItalic->setVisible(!checked); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 // ui.tbFontUnderline->setVisible(!checked); // ui.tbFontStrikeOut->setVisible(!checked); ui.tbFontSubScript->setVisible(!checked); ui.tbFontSuperScript->setVisible(!checked); ui.tbSymbols->setVisible(!checked); ui.lFont->setVisible(!checked); ui.kfontRequester->setVisible(!checked); if (checked) { //reset all applied formattings when switching from html to tex mode QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); ui.teLabel->selectAll(); QTextCharFormat format; ui.teLabel->setCurrentCharFormat(format); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(ui.teLabel->document()); #endif KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("Settings_Worksheet")); QString engine = conf.readEntry(QLatin1String("LaTeXEngine"), ""); if (engine == QLatin1String("xelatex") || engine == QLatin1String("lualatex")) { ui.lFontTeX->setVisible(true); ui.kfontRequesterTeX->setVisible(true); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); } else { ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(true); ui.sbFontSize->setVisible(true); } //update TeX colors ui.kcbFontColor->setColor(m_label->teXFontColor()); ui.kcbBackgroundColor->setColor(m_label->teXBackgroundColor()); } else { #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(0); #endif ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); //when switching to the text mode, set the background color to white just for the case the latex code provided by the user //in the TeX-mode is not valid and the background was set to red (s.a. LabelWidget::labelTeXImageUpdated()) ui.teLabel->setStyleSheet(QLatin1String("")); } //no latex is available and the user switched to the text mode, //deactivate the button since it shouldn't be possible anymore to switch to the TeX-mode if (!m_teXEnabled && !checked) { ui.tbTexUsed->setEnabled(false); ui.tbTexUsed->setToolTip(i18n("LaTeX typesetting not possible. Please check the settings.")); } else { ui.tbTexUsed->setEnabled(true); ui.tbTexUsed->setToolTip(QLatin1String("")); } if (m_initializing) return; QString text = checked ? ui.teLabel->toPlainText() : ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, checked); for (auto* label : m_labelsList) label->setText(wrapper); } void LabelWidget::fontColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextColor(color); for (auto* label : m_labelsList) label->setTeXFontColor(color); } void LabelWidget::backgroundColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextBackgroundColor(color); for (auto* label : m_labelsList) label->setTeXBackgroundColor(color); } void LabelWidget::fontSizeChanged(int value) { if (m_initializing) return; QFont font = m_label->teXFont(); font.setPointSize(value); for (auto* label : m_labelsList) label->setTeXFont(font); } void LabelWidget::fontBoldChanged(bool checked) { if (m_initializing) return; if (checked) ui.teLabel->setFontWeight(QFont::Bold); else ui.teLabel->setFontWeight(QFont::Normal); } void LabelWidget::fontItalicChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontItalic(checked); } void LabelWidget::fontUnderlineChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontUnderline(checked); } void LabelWidget::fontStrikeOutChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); format.setFontStrikeOut(checked); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSuperScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSuperScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSubScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSubScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontChanged(const QFont& font) { if (m_initializing) return; // underline and strike-out not included ui.teLabel->setFontFamily(font.family()); ui.teLabel->setFontPointSize(font.pointSize()); ui.teLabel->setFontItalic(font.italic()); ui.teLabel->setFontWeight(font.weight()); } void LabelWidget::teXFontChanged(const QFont& font) { if (m_initializing) return; for (auto* label : m_labelsList) label->setTeXFont(font); } void LabelWidget::charMenu() { QMenu menu; KCharSelect selection(this, 0, KCharSelect::SearchLine | KCharSelect::CharacterTable | KCharSelect::BlockCombos | KCharSelect::HistoryButtons); selection.setCurrentFont(ui.teLabel->currentFont()); connect(&selection, SIGNAL(charSelected(QChar)), this, SLOT(insertChar(QChar))); connect(&selection, SIGNAL(charSelected(QChar)), &menu, SLOT(close())); QWidgetAction *widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&selection); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbSymbols->width(),-menu.sizeHint().height()); menu.exec(ui.tbSymbols->mapToGlobal(pos)); } void LabelWidget::insertChar(QChar c) { ui.teLabel->insertPlainText(QString(c)); } void LabelWidget::dateTimeMenu() { m_dateTimeMenu->clear(); QDate date = QDate::currentDate(); m_dateTimeMenu->addSeparator()->setText(i18n("Date")); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleLongDate) ); QDateTime time = QDateTime::currentDateTime(); m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time")); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleLongDate) ); m_dateTimeMenu->exec( mapToGlobal(ui.tbDateTime->rect().bottomLeft())); } void LabelWidget::insertDateTime(QAction* action) { ui.teLabel->insertPlainText( action->text().remove('&') ); } // geometry slots /*! called when label's current horizontal position relative to its parent (left, center, right, custom ) is changed. */ void LabelWidget::positionXChanged(int index) { //Enable/disable the spinbox for the x- oordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionX->count()-1 ) ui.sbPositionX->setEnabled(true); else ui.sbPositionX->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.horizontalPosition = TextLabel::HorizontalPosition(index); for (auto* label : m_labelsList) label->setPosition(position); } /*! called when label's current horizontal position relative to its parent (top, center, bottom, custom ) is changed. */ void LabelWidget::positionYChanged(int index) { //Enable/disable the spinbox for the y-coordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionY->count()-1 ) ui.sbPositionY->setEnabled(true); else ui.sbPositionY->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.verticalPosition = TextLabel::VerticalPosition(index); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::customPositionXChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setX(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::customPositionYChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setY(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::horizontalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setHorizontalAlignment(TextLabel::HorizontalAlignment(index)); } void LabelWidget::verticalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVerticalAlignment(TextLabel::VerticalAlignment(index)); } void LabelWidget::rotationChanged(int value) { if (m_initializing) return; for (auto* label : m_labelsList) label->setRotationAngle(value); } void LabelWidget::offsetXChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetX( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::offsetYChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetY( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::visibilityChanged(bool state) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVisible(state); } //********************************************************* //****** SLOTs for changes triggered in TextLabel ********* //********************************************************* void LabelWidget::labelTextWrapperChanged(const TextLabel::TextWrapper& text) { m_initializing = true; //save and restore the current cursor position after changing the text QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); if (text.teXUsed) ui.teLabel->setText(text.text); else ui.teLabel->setHtml(text.text); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); ui.tbTexUsed->setChecked(text.teXUsed); this->teXUsedChanged(text.teXUsed); m_initializing = false; } /*! * \brief Highlights the text field red if wrong latex syntax was used (null image was produced) * or something else went wrong during rendering (\sa ExpressionTextEdit::validateExpression()) */ void LabelWidget::labelTeXImageUpdated(bool valid) { if (!valid) ui.teLabel->setStyleSheet(QLatin1String("QTextEdit{background: red;}")); else ui.teLabel->setStyleSheet(QLatin1String("")); } void LabelWidget::labelTeXFontChanged(const QFont& font) { m_initializing = true; ui.kfontRequesterTeX->setFont(font); ui.sbFontSize->setValue(font.pointSize()); m_initializing = false; } void LabelWidget::labelTeXFontColorChanged(const QColor color) { m_initializing = true; ui.kcbFontColor->setColor(color); m_initializing = false; } void LabelWidget::labelPositionChanged(const TextLabel::PositionWrapper& position) { m_initializing = true; ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(position.point.x(), Worksheet::Centimeter) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(position.point.y(), Worksheet::Centimeter) ); ui.cbPositionX->setCurrentIndex( position.horizontalPosition ); ui.cbPositionY->setCurrentIndex( position.verticalPosition ); m_initializing = false; } void LabelWidget::labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment index) { m_initializing = true; ui.cbHorizontalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelVerticalAlignmentChanged(TextLabel::VerticalAlignment index) { m_initializing = true; ui.cbVerticalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelOffsetxChanged(qreal offset) { m_initializing = true; ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelOffsetyChanged(qreal offset) { m_initializing = true; ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbRotation->setValue(angle); m_initializing = false; } void LabelWidget::labelVisibleChanged(bool on) { m_initializing = true; ui.chbVisible->setChecked(on); m_initializing = false; } //********************************************************** //******************** SETTINGS **************************** //********************************************************** void LabelWidget::load() { if(m_label == NULL) return; m_initializing = true; ui.chbVisible->setChecked(m_label->isVisible()); //Text/TeX ui.tbTexUsed->setChecked( (bool) m_label->text().teXUsed ); if (m_label->text().teXUsed) ui.teLabel->setText(m_label->text().text); else ui.teLabel->setHtml(m_label->text().text); this->teXUsedChanged(m_label->text().teXUsed); ui.kfontRequesterTeX->setFont(m_label->teXFont()); ui.sbFontSize->setValue( m_label->teXFont().pointSize() ); //move the cursor to the end and set the focus to the text editor QTextCursor cursor = ui.teLabel->textCursor(); cursor.movePosition(QTextCursor::End); ui.teLabel->setTextCursor(cursor); ui.teLabel->setFocus(); // Geometry ui.cbPositionX->setCurrentIndex( (int) m_label->position().horizontalPosition ); positionXChanged(ui.cbPositionX->currentIndex()); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.x(),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( (int) m_label->position().verticalPosition ); positionYChanged(ui.cbPositionY->currentIndex()); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.y(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetX(), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetY(), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( (int) m_label->horizontalAlignment() ); ui.cbVerticalAlignment->setCurrentIndex( (int) m_label->verticalAlignment() ); ui.sbRotation->setValue( m_label->rotationAngle() ); m_initializing = false; } void LabelWidget::loadConfig(KConfigGroup& group) { if(m_label == NULL) return; m_initializing = true; //TeX ui.tbTexUsed->setChecked(group.readEntry("TeXUsed", (bool) m_label->text().teXUsed)); this->teXUsedChanged(m_label->text().teXUsed); ui.sbFontSize->setValue( group.readEntry("TeXFontSize", m_label->teXFont().pointSize()) ); ui.kfontRequesterTeX->setFont(group.readEntry("TeXFont", m_label->teXFont())); // Geometry ui.cbPositionX->setCurrentIndex( group.readEntry("PositionX", (int) m_label->position().horizontalPosition ) ); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionXValue", m_label->position().point.x()),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( group.readEntry("PositionY", (int) m_label->position().verticalPosition ) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionYValue", m_label->position().point.y()),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetX", m_axesList.first()->titleOffsetX()), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetY", m_axesList.first()->titleOffsetY()), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( group.readEntry("HorizontalAlignment", (int) m_label->horizontalAlignment()) ); ui.cbVerticalAlignment->setCurrentIndex( group.readEntry("VerticalAlignment", (int) m_label->verticalAlignment()) ); ui.sbRotation->setValue( group.readEntry("Rotation", m_label->rotationAngle()) ); m_initializing = false; } void LabelWidget::saveConfig(KConfigGroup& group) { //TeX group.writeEntry("TeXUsed", ui.tbTexUsed->isChecked()); group.writeEntry("TeXFontColor", ui.kcbFontColor->color()); group.writeEntry("TeXBackgroundColor", ui.kcbBackgroundColor->color()); group.writeEntry("TeXFont", ui.kfontRequesterTeX->font()); // Geometry group.writeEntry("PositionX", ui.cbPositionX->currentIndex()); group.writeEntry("PositionXValue", Worksheet::convertToSceneUnits(ui.sbPositionX->value(),Worksheet::Centimeter) ); group.writeEntry("PositionY", ui.cbPositionY->currentIndex()); group.writeEntry("PositionYValue", Worksheet::convertToSceneUnits(ui.sbPositionY->value(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { group.writeEntry("OffsetX", Worksheet::convertToSceneUnits(ui.sbOffsetX->value(), Worksheet::Point) ); group.writeEntry("OffsetY", Worksheet::convertToSceneUnits(ui.sbOffsetY->value(), Worksheet::Point) ); } group.writeEntry("HorizontalAlignment", ui.cbHorizontalAlignment->currentIndex()); group.writeEntry("VerticalAlignment", ui.cbVerticalAlignment->currentIndex()); group.writeEntry("Rotation", ui.sbRotation->value()); } diff --git a/src/kdefrontend/widgets/ThemesWidget.cpp b/src/kdefrontend/widgets/ThemesWidget.cpp index 202c65a49..1384ed5ad 100644 --- a/src/kdefrontend/widgets/ThemesWidget.cpp +++ b/src/kdefrontend/widgets/ThemesWidget.cpp @@ -1,134 +1,134 @@ /*************************************************************************** File : ThemesWidget.cpp Project : LabPlot Description : widget for selecting themes -------------------------------------------------------------------- Copyright : (C) 2016 Prakriti Bhardwaj (p_bhardwaj14@informatik.uni-kl.de) Copyright : (C) 2016 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 "ThemesWidget.h" #include "kdefrontend/ThemeHandler.h" #include #include #include #include #include #include #include -#include +#include #include // #include /*! \class ThemesWidget \brief Widget for showing theme previews and for selecting a theme. \ingroup kdefrontend */ ThemesWidget::ThemesWidget(QWidget* parent) : QListView(parent) { setSelectionMode(QAbstractItemView::SingleSelection); setWordWrap(true); setViewMode(QListWidget::IconMode); setResizeMode(QListWidget::Adjust); //make the icon 3x3cm big and show two of them in the height int size = 3.0/2.54 * QApplication::desktop()->physicalDpiX(); setIconSize(QSize(size, size)); setMinimumSize(1.1*size, 2.1*size); //add some offset here to take care of potential scrollbars, etc. (not very precise...) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //show preview pixmaps QStandardItemModel* mContentItemModel = new QStandardItemModel(this); QStringList themeList = ThemeHandler::themes(); QStringList themeImgPathList = QStandardPaths::locateAll(QStandardPaths::DataLocation, "themes/screenshots/", QStandardPaths::LocateDirectory); if (themeImgPathList.isEmpty()) return; const QString& themeImgPath = themeImgPathList.first(); QString tempPath; for (int i = 0; i < themeList.size(); ++i) { QStandardItem* listItem = new QStandardItem(); tempPath = themeImgPath + themeList.at(i) + ".png"; if (!QFile::exists(tempPath)) tempPath = themeImgPath + "Unavailable.png"; listItem->setIcon(QIcon(QPixmap(tempPath))); listItem->setText(themeList.at(i)); mContentItemModel->appendRow(listItem); } //create and add the icon for "None" QPixmap pm(size, size); QPen pen(Qt::SolidPattern, 1); const QColor& color = (palette().color(QPalette::Base).lightness() < 128) ? Qt::white : Qt::black; pen.setColor(color); QPainter pa; pa.setPen(pen); pm.fill(Qt::transparent); pa.begin(&pm); pa.setPen(pen); pa.drawRect(5, 5, size-10, size-10); pa.end(); QStandardItem* listItem = new QStandardItem(); listItem->setIcon(pm); listItem->setText(i18n("None")); mContentItemModel->appendRow(listItem); //adding download themes option //TODO: activate this later // QStandardItem* listItem = new QStandardItem(); // listItem->setIcon(QIcon::fromTheme("get-hot-new-stuff")); // listItem->setText("Download Themes"); // listItem->setData("file_download_theme", Qt::UserRole); // mContentItemModel->appendRow(listItem); setModel(mContentItemModel); //SLOTS connect(this, &ThemesWidget::clicked, this, &ThemesWidget::applyClicked); } void ThemesWidget::applyClicked(const QModelIndex& index) { const QString& themeName = index.data(Qt::DisplayRole).toString(); //TODO: activate this later // if(themeName=="file_download_theme") // this->downloadThemes(); // else emit themeSelected(themeName); } //TODO: activate this later // void ThemesWidget::downloadThemes() { // KNS3::DownloadDialog dialog("labplot2_themes.knsrc", this); // dialog.exec(); // foreach (const KNS3::Entry& e, dialog.changedEntries()) { // kDebug() << "Changed Entry: " << e.name(); // } // } diff --git a/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp b/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp index 7998f802f..8e2488562 100644 --- a/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp +++ b/src/kdefrontend/worksheet/ExportWorksheetDialog.cpp @@ -1,270 +1,270 @@ /*************************************************************************** File : ExportWorksheetDialog.cpp Project : LabPlot Description : export worksheet dialog -------------------------------------------------------------------- Copyright : (C) 2011-2016 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "ExportWorksheetDialog.h" #include "ui_exportworksheetwidget.h" #include #include #include #include #include #include #include #include #include #include /*! \class ExportWorksheetDialog \brief Dialog for exporting a worksheet to a file. \ingroup kdefrontend */ ExportWorksheetDialog::ExportWorksheetDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ExportWorksheetWidget()), m_showOptions(true) { ui->setupUi(this); QDialogButtonBox* btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_showOptionsButton = new QPushButton; connect(btnBox, &QDialogButtonBox::clicked, this, &ExportWorksheetDialog::slotButtonClicked); btnBox->addButton(m_showOptionsButton, QDialogButtonBox::ActionRole); ui->verticalLayout->addWidget(btnBox); m_okButton = btnBox->button(QDialogButtonBox::Ok); m_cancelButton = btnBox->button(QDialogButtonBox::Cancel); QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui->leFileName->setCompleter(completer); ui->bOpen->setIcon(QIcon::fromTheme(QLatin1String("document-open"))); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("application-pdf")), QLatin1String("Portable Data Format (PDF)")); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-svg+xml")), QLatin1String("Scalable Vector Graphics (SVG)")); ui->cbFormat->insertSeparator(3); ui->cbFormat->addItem(QIcon::fromTheme(QLatin1String("image-x-generic")), QLatin1String("Portable Network Graphics (PNG)")); ui->cbExportArea->addItem(i18n("Object's bounding box")); ui->cbExportArea->addItem(i18n("Current selection")); ui->cbExportArea->addItem(i18n("Complete worksheet")); - ui->cbResolution->addItem(QString::number(QApplication::desktop()->physicalDpiX()) + " (" + i18n("desktop") + ')'); + ui->cbResolution->addItem(i18nc("%1 is the value of DPI of the current screen", "%1 (desktop)", QString::number(QApplication::desktop()->physicalDpiX()))); ui->cbResolution->addItem(QLatin1String("100")); ui->cbResolution->addItem(QLatin1String("150")); ui->cbResolution->addItem(QLatin1String("200")); ui->cbResolution->addItem(QLatin1String("300")); ui->cbResolution->addItem(QLatin1String("600")); ui->cbResolution->setValidator(new QIntValidator(ui->cbResolution)); connect(ui->cbFormat, static_cast(&KComboBox::currentIndexChanged), this, &ExportWorksheetDialog::formatChanged ); connect(ui->bOpen, &QPushButton::clicked, this, &ExportWorksheetDialog::selectFile); connect(ui->leFileName, &QLineEdit::textChanged, this, &ExportWorksheetDialog::fileNameChanged); connect(m_showOptionsButton, &QPushButton::clicked, this, &ExportWorksheetDialog::toggleOptions); ui->leFileName->setFocus(); - setWindowTitle(i18n("Export worksheet")); + setWindowTitle(i18nc("@title:window", "Export Worksheet")); setWindowIcon(QIcon::fromTheme(QLatin1String("document-export-database"))); QTimer::singleShot(0, this, &ExportWorksheetDialog::loadSettings); } void ExportWorksheetDialog::loadSettings() { //restore saved setting KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); KWindowConfig::restoreWindowSize(windowHandle(), conf); ui->cbFormat->setCurrentIndex(conf.readEntry("Format", 0)); ui->cbExportArea->setCurrentIndex(conf.readEntry("Area", 0)); ui->chkExportBackground->setChecked(conf.readEntry("Background", true)); ui->cbResolution->setCurrentIndex(conf.readEntry("Resolution", 0)); m_showOptions = conf.readEntry("ShowOptions", true); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); ui->gbOptions->setVisible(m_showOptions); } ExportWorksheetDialog::~ExportWorksheetDialog() { //save current settings KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Area", ui->cbExportArea->currentIndex()); conf.writeEntry("Background", ui->chkExportBackground->isChecked()); conf.writeEntry("Resolution", ui->cbResolution->currentIndex()); conf.writeEntry("ShowOptions", m_showOptions); KWindowConfig::saveWindowSize(windowHandle(), conf); } void ExportWorksheetDialog::setFileName(const QString& name) { KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); QString dir = conf.readEntry("LastDir", ""); if (dir.isEmpty()) dir = QDir::homePath(); ui->leFileName->setText(dir + QDir::separator() + name); this->formatChanged(ui->cbFormat->currentIndex()); } QString ExportWorksheetDialog::path() const { return ui->leFileName->text(); } WorksheetView::ExportFormat ExportWorksheetDialog::exportFormat() const { int index = ui->cbFormat->currentIndex(); //we have a separator in the format combobox at the 3th position -> skip it if (index > 2) index--; return WorksheetView::ExportFormat(index); } WorksheetView::ExportArea ExportWorksheetDialog::exportArea() const { return WorksheetView::ExportArea(ui->cbExportArea->currentIndex()); } bool ExportWorksheetDialog::exportBackground() const { return ui->chkExportBackground->isChecked(); } int ExportWorksheetDialog::exportResolution() const { if (ui->cbResolution->currentIndex() == 0) return QApplication::desktop()->physicalDpiX(); else return ui->cbResolution->currentText().toInt(); } void ExportWorksheetDialog::slotButtonClicked(QAbstractButton* button) { if (button == m_okButton) okClicked(); else if (button == m_cancelButton) { reject(); } } //SLOTS void ExportWorksheetDialog::okClicked() { if ( QFile::exists(ui->leFileName->text()) ) { int r = KMessageBox::questionYesNo(this, i18n("The file already exists. Do you really want to overwrite it?"), i18n("Export")); if (r == KMessageBox::No) return; } KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); conf.writeEntry("Format", ui->cbFormat->currentIndex()); conf.writeEntry("Area", ui->cbExportArea->currentIndex()); conf.writeEntry("Resolution", ui->cbResolution->currentIndex()); QString path = ui->leFileName->text(); if (!path.isEmpty()) { QString dir = conf.readEntry("LastDir", ""); ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { QString newDir = path.left(pos); if (newDir!=dir) conf.writeEntry("LastDir", newDir); } } accept(); } /*! Shows/hides the GroupBox with export options in this dialog. */ void ExportWorksheetDialog::toggleOptions() { m_showOptions = !m_showOptions; ui->gbOptions->setVisible(m_showOptions); m_showOptions ? m_showOptionsButton->setText(i18n("Hide Options")) : m_showOptionsButton->setText(i18n("Show Options")); //resize the dialog resize(layout()->minimumSize()); layout()->activate(); resize( QSize(this->width(),0).expandedTo(minimumSize()) ); } /*! opens a file dialog and lets the user select the file. */ void ExportWorksheetDialog::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ExportWorksheetDialog"); const QString dir = conf.readEntry("LastDir", ""); QString format; if (ui->cbFormat->currentIndex() == 0) format = i18n("Portable Data Format (PDF) (*.pdf *.PDF)"); else if (ui->cbFormat->currentIndex() == 1) format = i18n("Scalable Vector Graphics (SVG) (*.svg *.SVG)"); else format = i18n("Portable Network Graphics (PNG) (*.png *.PNG)"); const QString path = QFileDialog::getSaveFileName(this, i18n("Export to file"), dir, format); if (!path.isEmpty()) { ui->leFileName->setText(path); int pos = path.lastIndexOf(QDir::separator()); if (pos!=-1) { const QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } } } /*! called when the output format was changed. Adjusts the extension for the specified file. */ void ExportWorksheetDialog::formatChanged(int index) { //we have a separator in the format combobox at the 3rd posiiton -> skip it if (index > 2) index --; QStringList extensions; extensions << QLatin1String(".pdf") << QLatin1String(".svg") << QLatin1String(".png"); QString path = ui->leFileName->text(); int i = path.indexOf(QLatin1Char('.')); if (i == -1) path = path + extensions.at(index); else path = path.left(i) + extensions.at(index); ui->leFileName->setText(path); // show resolution option for png format ui->lResolution->setVisible(index == 3); ui->cbResolution->setVisible(index == 3); } void ExportWorksheetDialog::fileNameChanged(const QString& name) { m_okButton->setEnabled(!name.simplified().isEmpty()); } diff --git a/src/kdefrontend/worksheet/GridDialog.cpp b/src/kdefrontend/worksheet/GridDialog.cpp index cdc641401..7558495dd 100644 --- a/src/kdefrontend/worksheet/GridDialog.cpp +++ b/src/kdefrontend/worksheet/GridDialog.cpp @@ -1,118 +1,118 @@ /*************************************************************************** File : GridDialog.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2017 by Alexander Semke Email (use @ for *) : alexander.semke@web.de Description : dialog for editing the grid properties for the worksheet view ***************************************************************************/ /*************************************************************************** * * * 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 "GridDialog.h" #include #include #include #include #include -#include +#include #include //TODO: //1. improve the layout and move the UI-part to a ui-file //2. restore the dialog size //3. restore the currently active grid settings /** * @brief Provides a dialog for editing the grid properties for the worksheet view * \ingroup kdefrontend */ GridDialog::GridDialog(QWidget* parent) : QDialog(parent){ - setWindowTitle(i18n("Custom grid")); + setWindowTitle(i18nc("@title:window", "Custom Grid")); QWidget* widget = new QWidget; QGridLayout* layout = new QGridLayout(widget); QLabel* label = new QLabel(i18n("Style"), widget); layout->addWidget(label, 0, 0); cbStyle = new QComboBox(this); - cbStyle->addItem(i18n("lines")); - cbStyle->addItem(i18n("dots")); + cbStyle->addItem(i18n("Lines")); + cbStyle->addItem(i18n("Dots")); cbStyle->setCurrentIndex(0); layout->addWidget(cbStyle, 0, 1); label = new QLabel(i18n("Horizontal spacing"), widget); layout->addWidget(label, 1, 0); sbHorizontalSpacing = new QSpinBox(widget); sbHorizontalSpacing->setRange(1,100); sbHorizontalSpacing->setValue(10); layout->addWidget(sbHorizontalSpacing, 1, 1); label = new QLabel(i18n("Vertical spacing"), widget); layout->addWidget(label, 2, 0); sbVerticalSpacing = new QSpinBox(widget); sbVerticalSpacing->setRange(1,100); sbVerticalSpacing->setValue(10); layout->addWidget(sbVerticalSpacing, 2, 1); label = new QLabel(i18n("Color"), widget); layout->addWidget(label, 3, 0); kcbColor = new KColorButton(widget); kcbColor->setColor(Qt::gray); layout->addWidget(kcbColor , 3, 1); label = new QLabel(i18n("Opacity"), widget); layout->addWidget(label, 4, 0); sbOpacity = new QSpinBox(widget); sbOpacity->setRange(1,100); sbOpacity->setValue(100); layout->addWidget(sbOpacity, 4, 1); label = new QLabel("%", widget); layout->addWidget(label, 4, 2); QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout* vlayout = new QVBoxLayout(this); vlayout->addWidget(widget); vlayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); } void GridDialog::save(WorksheetView::GridSettings& settings){ if (cbStyle->currentIndex() == 0) settings.style = WorksheetView::LineGrid; else settings.style = WorksheetView::DotGrid; settings.horizontalSpacing = sbHorizontalSpacing->value(); settings.verticalSpacing = sbVerticalSpacing->value(); settings.color = kcbColor->color(); settings.opacity = (float)sbOpacity->value()/100; } diff --git a/src/kdefrontend/worksheet/SlidingPanel.cpp b/src/kdefrontend/worksheet/SlidingPanel.cpp index eb60f1920..c3aec7523 100644 --- a/src/kdefrontend/worksheet/SlidingPanel.cpp +++ b/src/kdefrontend/worksheet/SlidingPanel.cpp @@ -1,93 +1,93 @@ /*************************************************************************** File : SlidingPanel.cpp Project : LabPlot Description : Sliding panel shown in the presenter widget -------------------------------------------------------------------- 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 "SlidingPanel.h" #include #include #include #include #include -#include +#include SlidingPanel::SlidingPanel(QWidget *parent, const QString &worksheetName) : QFrame(parent) { setAttribute(Qt::WA_DeleteOnClose); m_worksheetName = new QLabel(worksheetName); QFont nameFont; nameFont.setPointSize(20); nameFont.setBold(true); m_worksheetName->setFont(nameFont); - m_quitPresentingMode = new QPushButton(i18n("Quit presentation")); + m_quitPresentingMode = new QPushButton(i18n("Quit Presentation")); m_quitPresentingMode->setIcon(QIcon::fromTheme(QLatin1String("window-close"))); QHBoxLayout* hlayout = new QHBoxLayout; hlayout->addWidget(m_worksheetName); QSpacerItem* spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum); hlayout->addItem(spacer); hlayout->addWidget(m_quitPresentingMode); setLayout(hlayout); QPalette pal(palette()); pal.setColor(QPalette::Background, Qt::gray); setAutoFillBackground(true); setPalette(pal); move(0, 0); raise(); show(); } SlidingPanel::~SlidingPanel() { delete m_worksheetName; delete m_quitPresentingMode; } void SlidingPanel::movePanel(qreal value) { move(0, -height() + static_cast(value * height()) ); raise(); } QPushButton* SlidingPanel::quitButton() const { return m_quitPresentingMode; } QSize SlidingPanel::sizeHint() const { QSize sh; QDesktopWidget* const dw = QApplication::desktop(); const int primaryScreenIdx = dw->primaryScreen(); const QRect& screenSize = dw->availableGeometry(primaryScreenIdx); sh.setWidth(screenSize.width()); //for the height use 1.5 times the height of the font used in the label (20 points) in pixels QFont font; font.setPointSize(20); const QFontMetrics fm(font); sh.setHeight(1.5*fm.ascent()); return sh; } diff --git a/tests/analysis/fit/CMakeLists.txt b/tests/analysis/fit/CMakeLists.txt index a97d5658b..7931ca21a 100644 --- a/tests/analysis/fit/CMakeLists.txt +++ b/tests/analysis/fit/CMakeLists.txt @@ -1,36 +1,36 @@ INCLUDE_DIRECTORIES(${GSL_INCLUDE_DIR}) add_executable (fittest FitTest.cpp) -target_link_libraries(fittest ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTSVG_LIBRARY} ${QT_QTSQL_LIBRARIES}) +target_link_libraries(fittest Qt5::Test) target_link_libraries(fittest KF5::Archive KF5::XmlGui ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) IF (Qt5SerialPort_FOUND) target_link_libraries(fittest Qt5::SerialPort) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries(fittest KF5::SyntaxHighlighting) ENDIF () #TODO: KF5::NewStuff IF (CANTOR_LIBS_FOUND) target_link_libraries(fittest ${CANTOR_LIBS}) ENDIF () IF (HDF5_FOUND) target_link_libraries(fittest ${HDF5_C_LIBRARIES}) ENDIF () IF (FFTW_FOUND) target_link_libraries(fittest ${FFTW_LIBRARIES}) ENDIF () IF (NETCDF_FOUND) target_link_libraries(fittest ${NETCDF_LIBRARY}) ENDIF () IF (CFITSIO_FOUND) target_link_libraries(fittest ${CFITSIO_LIBRARY}) ENDIF () IF (USE_LIBORIGIN) target_link_libraries(fittest liborigin-static) ENDIF () target_link_libraries(fittest labplot2lib) add_test(NAME fittest COMMAND fittest) diff --git a/tests/analysis/fit/FitTest.cpp b/tests/analysis/fit/FitTest.cpp index f077f3a9a..3dc21c092 100644 --- a/tests/analysis/fit/FitTest.cpp +++ b/tests/analysis/fit/FitTest.cpp @@ -1,2768 +1,2771 @@ /*************************************************************************** File : FitTest.cpp Project : LabPlot Description : Tests for data fitting -------------------------------------------------------------------- Copyright : (C) 2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2018 Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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 "FitTest.h" #include "backend/core/column/Column.h" #include "backend/worksheet/plots/cartesian/XYFitCurve.h" extern "C" { #include "backend/nsl/nsl_sf_stats.h" #include "backend/nsl/nsl_stats.h" } void FitTest::initTestCase() { // needed in order to have the signals triggered by SignallingUndoCommand, see LabPlot.cpp //TODO: redesign/remove this qRegisterMetaType("const AbstractAspect*"); qRegisterMetaType("const AbstractColumn*"); } //############################################################################## //################# linear regression with NIST datasets ###################### //############################################################################## void FitTest::testLinearNorris() { //NIST data for Norris dataset QVector xData = {0.2,337.4,118.2,884.6,10.1,226.5,666.3,996.3,448.6,777.0,558.2,0.4,0.6,775.5,666.9,338.0,447.5,11.6,556.0,228.1, 995.8,887.6,120.2,0.3,0.3,556.8,339.1,887.2,999.0,779.0,11.1,118.3,229.2,669.1,448.9,0.5}; QVector yData = {0.1,338.8,118.1,888.0,9.2,228.1,668.5,998.5,449.1,778.9,559.2,0.3,0.1,778.1,668.8,339.3,448.9,10.8,557.7,228.3, 998.0,888.8,119.6,0.3,0.6,557.6,339.3,888.0,998.5,778.9,10.2,117.6,228.9,668.4,449.2,0.2}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 1; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 2); QCOMPARE(fitResult.paramValues.at(0), -0.262323073774029); QCOMPARE(fitResult.errorValues.at(0), 0.232818234301152); QCOMPARE(fitResult.paramValues.at(1), 1.00211681802045); QCOMPARE(fitResult.errorValues.at(1), 0.429796848199937e-3); QCOMPARE(fitResult.rsd, 0.884796396144373); QCOMPARE(fitResult.rsquare, 0.999993745883712); QCOMPARE(fitResult.sse, 26.6173985294224); QCOMPARE(fitResult.rms, 0.782864662630069); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 5436419.54079774 FuzzyCompare(fitResult.fdist_F, 5436385.54079785, 1.e-5); } void FitTest::testLinearPontius() { //NIST data for Pontius dataset QVector xData = {150000,300000,450000,600000,750000,900000,1050000,1200000,1350000,1500000,1650000,1800000,1950000,2100000, 2250000,2400000,2550000,2700000,2850000,3000000,150000,300000,450000,600000,750000,900000,1050000,1200000,1350000,1500000, 1650000,1800000,1950000,2100000,2250000,2400000,2550000,2700000,2850000,3000000}; QVector yData = {.11019,.21956,.32949,.43899,.54803,.65694,.76562,.87487,.98292,1.09146,1.20001,1.30822,1.41599,1.52399, 1.63194,1.73947,1.84646,1.95392,2.06128,2.16844,.11052,.22018,.32939,.43886,.54798,.65739,.76596,.87474, .98300,1.09150,1.20004,1.30818,1.41613,1.52408,1.63159,1.73965,1.84696,1.95445,2.06177,2.16829}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 2; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 3); QCOMPARE(fitResult.paramValues.at(0), 0.673565789473684e-3); QCOMPARE(fitResult.errorValues.at(0), 0.107938612033077e-3); QCOMPARE(fitResult.paramValues.at(1), 0.732059160401003e-6); QCOMPARE(fitResult.errorValues.at(1), 0.157817399981659e-9); QCOMPARE(fitResult.paramValues.at(2), -0.316081871345029e-14); QCOMPARE(fitResult.errorValues.at(2), 0.486652849992036e-16); QCOMPARE(fitResult.rsd, 0.205177424076185e-3); QCOMPARE(fitResult.rsquare, 0.999999900178537); QCOMPARE(fitResult.sse, 0.155761768796992e-5); QCOMPARE(fitResult.rms, 0.420977753505385e-7); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 185330884.495776 FuzzyCompare(fitResult.fdist_F, 185330865.995752, 1.e-7); } void FitTest::testLinearNoInt1() { //NIST data for NoInt1 dataset QVector xData = {60,61,62,63,64,65,66,67,68,69,70}; QVector yData = {130,131,132,133,134,135,136,137,138,139,140}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*x"; fitData.paramNames << "b1"; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.; fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); //fitData.eps = 1.e-15; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 1); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 2.07438016513166 FuzzyCompare(fitResult.paramValues.at(0), 2.07438016528926, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0165289256079047 FuzzyCompare(fitResult.errorValues.at(0), 0.165289256198347e-1, 1.e-9); QCOMPARE(fitResult.rsd, 3.56753034006338); QCOMPARE(fitResult.sse, 127.272727272727); QCOMPARE(fitResult.rms, 12.7272727272727); QCOMPARE(fitResult.rsquare, 0.999365492298663); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 15760.25 FuzzyCompare(fitResult.fdist_F, 15750.25, 1.e-3); } void FitTest::testLinearNoInt1_2() { //NIST data for NoInt1 dataset QVector xData = {60,61,62,63,64,65,66,67,68,69,70}; QVector yData = {130,131,132,133,134,135,136,137,138,139,140}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 1; XYFitCurve::initFitData(fitData); fitData.paramStartValues[0] = 0; fitData.paramFixed[0] = true; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 2); QCOMPARE(fitResult.paramValues.at(0), 0.); QCOMPARE(fitResult.paramValues.at(1), 2.07438016528926); QCOMPARE(fitResult.errorValues.at(1), 0.165289256198347e-1); QCOMPARE(fitResult.rsd, 3.56753034006338); QCOMPARE(fitResult.sse, 127.272727272727); QCOMPARE(fitResult.rms, 12.7272727272727); QCOMPARE(fitResult.rsquare, 0.999365492298663); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 15760.25 FuzzyCompare(fitResult.fdist_F, 15750.25, 1.e-3); } void FitTest::testLinearNoInt2() { //NIST data for NoInt2 dataset QVector xData = {4,5,6}; QVector yData = {3,4,4}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "c * x"; fitData.paramNames << "c"; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.; fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); //fitData.eps = 1.e-15; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 1); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.727272727152573 FuzzyCompare(fitResult.paramValues.at(0), 0.727272727272727, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0420827316561797 FuzzyCompare(fitResult.errorValues.at(0), 0.420827318078432E-01, 1.e-8); QCOMPARE(fitResult.rsd, 0.369274472937998); QCOMPARE(fitResult.sse, 0.272727272727273); QCOMPARE(fitResult.rms, 0.136363636363636); // can not detect that intercept is zero for a custom linear model DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.590909090909091 // QCOMPARE(fitResult.rsquare, 0.993348115299335); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 4.88888888888889 // FuzzyCompare(fitResult.fdist_F, 298.666666666667, 1.); } void FitTest::testLinearNoInt2_2() { //NIST data for NoInt2 dataset QVector xData = {4,5,6}; QVector yData = {3,4,4}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 1; XYFitCurve::initFitData(fitData); fitData.paramStartValues[0] = 0; fitData.paramFixed[0] = true; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 2); QCOMPARE(fitResult.paramValues.at(0), 0.); QCOMPARE(fitResult.paramValues.at(1), 0.727272727272727); QCOMPARE(fitResult.errorValues.at(1), 0.420827318078432e-1); QCOMPARE(fitResult.rsd, 0.369274472937998); QCOMPARE(fitResult.sse, 0.272727272727273); QCOMPARE(fitResult.rms, 0.136363636363636); QCOMPARE(fitResult.rsquare, 0.993348115299335); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 300.666666666667 FuzzyCompare(fitResult.fdist_F, 298.666666666667, 1.e-2); } void FitTest::testLinearFilip() { //NIST data for Filip dataset QVector xData = {-6.860120914,-4.324130045,-4.358625055,-4.358426747,-6.955852379,-6.661145254,-6.355462942,-6.118102026, -7.115148017,-6.815308569,-6.519993057,-6.204119983,-5.853871964,-6.109523091,-5.79832982,-5.482672118,-5.171791386,-4.851705903, -4.517126416,-4.143573228,-3.709075441,-3.499489089,-6.300769497,-5.953504836,-5.642065153,-5.031376979,-4.680685696,-4.329846955, -3.928486195,-8.56735134,-8.363211311,-8.107682739,-7.823908741,-7.522878745,-7.218819279,-6.920818754,-6.628932138,-6.323946875, -5.991399828,-8.781464495,-8.663140179,-8.473531488,-8.247337057,-7.971428747,-7.676129393,-7.352812702,-7.072065318,-6.774174009, -6.478861916,-6.159517513,-6.835647144,-6.53165267,-6.224098421,-5.910094889,-5.598599459,-5.290645224,-4.974284616,-4.64454848, -4.290560426,-3.885055584,-3.408378962,-3.13200249,-8.726767166,-8.66695597,-8.511026475,-8.165388579,-7.886056648,-7.588043762, -7.283412422,-6.995678626,-6.691862621,-6.392544977,-6.067374056,-6.684029655,-6.378719832,-6.065855188,-5.752272167,-5.132414673, -4.811352704,-4.098269308,-3.66174277,-3.2644011}; QVector yData = {0.8116,0.9072,0.9052,0.9039,0.8053,0.8377,0.8667,0.8809,0.7975,0.8162,0.8515,0.8766,0.8885,0.8859,0.8959,0.8913, 0.8959,0.8971,0.9021,0.909,0.9139,0.9199,0.8692,0.8872,0.89,0.891,0.8977,0.9035,0.9078,0.7675,0.7705,0.7713,0.7736,0.7775,0.7841, 0.7971,0.8329,0.8641,0.8804,0.7668,0.7633,0.7678,0.7697,0.77,0.7749,0.7796,0.7897,0.8131,0.8498,0.8741,0.8061,0.846,0.8751,0.8856, 0.8919,0.8934,0.894,0.8957,0.9047,0.9129,0.9209,0.9219,0.7739,0.7681,0.7665,0.7703,0.7702,0.7761,0.7809,0.7961,0.8253,0.8602, 0.8809,0.8301,0.8664,0.8834,0.8898,0.8964,0.8963,0.9074,0.9119,0.9228}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 10; XYFitCurve::initFitData(fitData); const int np = fitData.paramNames.size(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 11); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: -1467.48962615175 FuzzyCompare(fitResult.paramValues.at(0), -1467.48961422980, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 298.084524514884 FuzzyCompare(fitResult.errorValues.at(0), 298.084530995537, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -2772.1796150428 FuzzyCompare(fitResult.paramValues.at(1), -2772.17959193342, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 559.779853249694 FuzzyCompare(fitResult.errorValues.at(1), 559.779865474950, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: -2316.37110148409 FuzzyCompare(fitResult.paramValues.at(2), -2316.37108160893, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 466.477561928144 FuzzyCompare(fitResult.errorValues.at(2), 466.477572127796, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: -1127.97395097195 FuzzyCompare(fitResult.paramValues.at(3), -1127.97394098372, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 227.204269523115 FuzzyCompare(fitResult.errorValues.at(3), 227.204274477751, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(4)); // result: -354.478236951913 FuzzyCompare(fitResult.paramValues.at(4), -354.478233703349, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(4)); // result: 71.6478645361214 FuzzyCompare(fitResult.errorValues.at(4), 71.6478660875927, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(5)); // result: -75.1242024539908 FuzzyCompare(fitResult.paramValues.at(5), -75.1242017393757, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(5)); // result: 15.289717547564 FuzzyCompare(fitResult.errorValues.at(5), 15.2897178747400, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(6)); // result: -10.875318143236 FuzzyCompare(fitResult.paramValues.at(6), -10.8753180355343, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(6)); // result: 2.23691155110776 FuzzyCompare(fitResult.errorValues.at(6), 2.23691159816033, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(7)); // result: -1.06221499687347 FuzzyCompare(fitResult.paramValues.at(7), -1.06221498588947, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(7)); // result: 0.221624317377432 FuzzyCompare(fitResult.errorValues.at(7), 0.221624321934227, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(8)); // result: -0.0670191161850038 FuzzyCompare(fitResult.paramValues.at(8), -0.670191154593408E-01, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(8)); // result: 0.0142363760310402 FuzzyCompare(fitResult.errorValues.at(8), 0.142363763154724E-01, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(9)); // result: -0.00246781081080665 FuzzyCompare(fitResult.paramValues.at(9), -0.246781078275479E-02, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(9)); // result: 0.000535617398555022 FuzzyCompare(fitResult.errorValues.at(9), 0.535617408889821E-03, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(10)); // result: -4.02962529900222e-05 FuzzyCompare(fitResult.paramValues.at(10), -0.402962525080404E-04, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(10)); // result: 8.96632820770946e-06 FuzzyCompare(fitResult.errorValues.at(10), 0.896632837373868E-05, 1.e-7); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.00334801050105949 FuzzyCompare(fitResult.rsd, 0.334801051324544E-02, 1.e-8); DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.996727416209443 FuzzyCompare(fitResult.rsquare, 0.996727416185620, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.00079585137637953 FuzzyCompare(fitResult.sse, 0.795851382172941E-03, 1.e-7); DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.12091743152047e-05 FuzzyCompare(fitResult.rms, 0.112091743968020E-04, 1.e-7); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 2169.53956090808 FuzzyCompare(fitResult.fdist_F, 2162.43954511489, 1.e-2); } void FitTest::testLinearWampler1() { //NIST data for Wampler1 dataset QVector xData = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; QVector yData = {1,6,63,364,1365,3906,9331,19608,37449,66430,111111, 177156,271453,402234,579195,813616,1118481,1508598,2000719,2613660,3368421}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 5; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 6); for (int i = 0; i < np; i++) { const double paramValue = fitResult.paramValues.at(i); const double errorValue = fitResult.errorValues.at(i); QCOMPARE(paramValue, 1.0); QCOMPARE(errorValue, 0.0); } QCOMPARE(fitResult.rsd, 0.0); QCOMPARE(fitResult.rsquare, 1.0); QCOMPARE(fitResult.sse, 0.0); QCOMPARE(fitResult.rms, 0.0); QVERIFY(std::isinf(fitResult.fdist_F)); } void FitTest::testLinearWampler2() { //NIST data for Wampler2 dataset QVector xData = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; QVector yData = {1.00000,1.11111,1.24992,1.42753,1.65984,1.96875,2.38336,2.94117,3.68928,4.68559, 6.00000,7.71561,9.92992,12.75603,16.32384,20.78125,26.29536,33.05367,41.26528,51.16209,63.00000}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 5; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results qDebug() << "STATUS " << fitResult.status; QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 6); QCOMPARE(fitResult.paramValues.at(0), 1.0); QCOMPARE(fitResult.paramValues.at(1), 0.1); QCOMPARE(fitResult.paramValues.at(2), 0.01); QCOMPARE(fitResult.paramValues.at(3), 0.001); QCOMPARE(fitResult.paramValues.at(4), 0.0001); QCOMPARE(fitResult.paramValues.at(5), 0.00001); for (int i = 0; i < np; i++) { const double errorValue = fitResult.errorValues.at(i); FuzzyCompare(errorValue, 0., 1.); } DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.32458538254974e-15 FuzzyCompare(fitResult.rsd, 0., 1.); QCOMPARE(fitResult.rsquare, 1.); DEBUG(std::setprecision(15) << fitResult.sse); // result: 8.1055458011459e-29 FuzzyCompare(fitResult.sse, 0., 1.); DEBUG(std::setprecision(15) << fitResult.rms); // result: 5.40369720076393e-30 FuzzyCompare(fitResult.rms, 0., 1.); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 2.44385217688297e+32 QVERIFY(fitResult.fdist_F > 2.e+32); } void FitTest::testLinearWampler3() { //NIST data for Wampler3 dataset QVector xData = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; QVector yData = {760.,-2042.,2111.,-1684.,3888.,1858.,11379.,17560.,39287.,64382.,113159., 175108.,273291.,400186.,581243.,811568.,1121004.,1506550.,2002767.,2611612.,3369180.}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 5; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 6); for (int i = 0; i < np; i++) { const double paramValue = fitResult.paramValues.at(i); QCOMPARE(paramValue, 1.0); } QCOMPARE(fitResult.errorValues.at(0), 2152.32624678170); QCOMPARE(fitResult.errorValues.at(1), 2363.55173469681); QCOMPARE(fitResult.errorValues.at(2), 779.343524331583); QCOMPARE(fitResult.errorValues.at(3), 101.475507550350); QCOMPARE(fitResult.errorValues.at(4), 5.64566512170752); QCOMPARE(fitResult.errorValues.at(5), 0.112324854679312); QCOMPARE(fitResult.rsd, 2360.14502379268); QCOMPARE(fitResult.rsquare, 0.999995559025820); QCOMPARE(fitResult.sse, 83554268.0000000); QCOMPARE(fitResult.rms, 5570284.53333333); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 675527.458240122 FuzzyCompare(fitResult.fdist_F, 675524.458240122, 1.e-5); } void FitTest::testLinearWampler4() { //NIST data for Wampler4 dataset QVector xData = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; QVector yData = {75901,-204794,204863,-204436,253665,-200894,214131,-185192,221249,-138370, 315911,-27644,455253,197434,783995,608816,1370781,1303798,2205519,2408860,3444321}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 5; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 6); - for (int i = 0; i < np; i++) { - const double paramValue = fitResult.paramValues.at(i); - QCOMPARE(paramValue, 1.0); - } + FuzzyCompare(fitResult.paramValues.at(0), 1.0, 3.e-9); // i386: 1.00000000223515 + FuzzyCompare(fitResult.paramValues.at(1), 1.0, 5.e-9); // i386: 0.999999995021441 + FuzzyCompare(fitResult.paramValues.at(2), 1.0, 2.e-9); // i386: 1.00000000188395 + FuzzyCompare(fitResult.paramValues.at(3), 1.0, 1.e-9); // i386: 0.999999999743725 + FuzzyCompare(fitResult.paramValues.at(4), 1.0, 2.e-11); // i386: 1.00000000001441 + FuzzyCompare(fitResult.paramValues.at(5), 1.0); // i386: 0.999999999999714 + QCOMPARE(fitResult.errorValues.at(0), 215232.624678170); QCOMPARE(fitResult.errorValues.at(1), 236355.173469681); QCOMPARE(fitResult.errorValues.at(2), 77934.3524331583); QCOMPARE(fitResult.errorValues.at(3), 10147.5507550350); QCOMPARE(fitResult.errorValues.at(4), 564.566512170752); QCOMPARE(fitResult.errorValues.at(5), 11.2324854679312); QCOMPARE(fitResult.rsd, 236014.502379268); QCOMPARE(fitResult.rsquare, 0.957478440825662); QCOMPARE(fitResult.sse, 835542680000.000); QCOMPARE(fitResult.rms, 55702845333.3333); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 70.5524458240122 FuzzyCompare(fitResult.fdist_F, 67.5524458240122, 5.e-2); } void FitTest::testLinearWampler5() { //NIST data for Wampler5 dataset QVector xData = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; QVector yData = {7590001,-20479994,20480063,-20479636,25231365,-20476094,20489331,-20460392,18417449,-20413570, 20591111,-20302844,18651453,-20077766,21059195,-19666384,26348481,-18971402,22480719,-17866340,10958421}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 5; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 6); for (int i = 0; i < np; i++) { const double paramValue = fitResult.paramValues.at(i); QCOMPARE(paramValue, 1.0); } QCOMPARE(fitResult.errorValues.at(0), 21523262.4678170); QCOMPARE(fitResult.errorValues.at(1), 23635517.3469681); QCOMPARE(fitResult.errorValues.at(2), 7793435.24331583); QCOMPARE(fitResult.errorValues.at(3), 1014755.07550350); QCOMPARE(fitResult.errorValues.at(4), 56456.6512170752); QCOMPARE(fitResult.errorValues.at(5), 1123.24854679312); QCOMPARE(fitResult.rsd, 23601450.2379268); QCOMPARE(fitResult.rsquare, 0.224668921574940E-02); QCOMPARE(fitResult.sse, 0.835542680000000E+16); QCOMPARE(fitResult.rms, 557028453333333.); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 3.0067552445824 //TODO FuzzyCompare(fitResult.fdist_F, 0.675524458240122E-02, 1.e-6); } // taken from https://en.wikipedia.org/wiki/Ordinary_least_squares void FitTest::testLinearWP_OLS() { //data from The World Almanac and Book of Facts, 1975 QVector xData = {1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83}; QVector yData = {52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 2; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 128.812803578436 FuzzyCompare(fitResult.paramValues.at(0), 128.8128, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3082821390367 FuzzyCompare(fitResult.errorValues.at(0), 16.3083, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(0)); // result: 7.89861264848368 FuzzyCompare(fitResult.tdist_tValues.at(0), 7.8986, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(0)); // result: 4.28330815316414e-06 FuzzyCompare(fitResult.tdist_pValues.at(0), 0.0, 1.); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -143.16202286476 FuzzyCompare(fitResult.paramValues.at(1), -143.1620, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 19.8331710430895 FuzzyCompare(fitResult.errorValues.at(1), 19.8332, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(1)); // result: -7.21831231897945 FuzzyCompare(fitResult.tdist_tValues.at(1), -7.2183, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(1)); // result: 1.05970640905074e-05 FuzzyCompare(fitResult.tdist_pValues.at(1), 0.0, 1.); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 61.9603254424724 FuzzyCompare(fitResult.paramValues.at(2), 61.9603, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 6.00842899301227 FuzzyCompare(fitResult.errorValues.at(2), 6.0084, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(2)); // result: 10.3122339490958 FuzzyCompare(fitResult.tdist_tValues.at(2), 10.3122, 1.e-5); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(2)); // result: 2.56647515320682e-07 FuzzyCompare(fitResult.tdist_pValues.at(2), 0.0, 1.); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.25158650082898 FuzzyCompare(fitResult.rsd, 0.2516, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.998904558436583 FuzzyCompare(fitResult.rsquare, 0.9989, 1.e-5); DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result: 0.99860580164656 FuzzyCompare(fitResult.rsquareAdj, 0.9987, 1.e-4); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.759549208792447 FuzzyCompare(fitResult.sse, 0.7595, 1.e-4); // QCOMPARE(fitResult.rms, ???); // result: 0.0632958 DEBUG(std::setprecision(15) << fitResult.chisq_p); // result: 0.999996987409119 // FuzzyCompare(fitResult.chisq_p, ???, 1.e-8); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 5477.24333307392 FuzzyCompare(fitResult.fdist_F, 5471.2, 2.e-3); QCOMPARE(fitResult.fdist_p, 0.0); DEBUG(std::setprecision(15) << fitResult.logLik); // result: 1.0890247702592 FuzzyCompare(fitResult.logLik, 1.0890, 3.e-5); DEBUG(std::setprecision(15) << fitResult.aic); // result: 5.82195045948161 // not reproducable // FuzzyCompare(fitResult.aic, 0.2548, 2.e-6); DEBUG(std::setprecision(15) << fitResult.bic); // result: 8.65415126389045 // not reproducable // FuzzyCompare(fitResult.bic, 0.3964, 2.e-6); } // from http://sia.webpopix.org/polynomialRegression1.html void FitTest::testLinearR_lm2() { QVector xData = {4,4,7,7,8,9,10,10,10,11,11,12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,16,16,17, 17,17,18,18,18,18,19,19,19,20,20,20,20,20,22,23,24,24,24,24,25}; QVector yData = {2,10,4,22,16,10,18,26,34,17,28,14,20,24,28,26,34,34,46,26,36,60,80,20,26,54,32,40,32,40,50,42,56,76,84,36,46, 68,32,48,52,56,64,66,54,70,92,93,120,85}; //data source columns Column xDataColumn("x", AbstractColumn::Integer); xDataColumn.replaceInteger(0, xData); Column yDataColumn("y", AbstractColumn::Integer); yDataColumn.replaceInteger(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 2; XYFitCurve::initFitData(fitData); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); const int np = fitData.paramNames.size(); QCOMPARE(np, 3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 2.47013778506623 FuzzyCompare(fitResult.paramValues.at(0), 2.47014, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 14.8171647250237 FuzzyCompare(fitResult.errorValues.at(0), 14.81716, 1.e-6); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(0)); // result: 0.16670785746848 FuzzyCompare(fitResult.tdist_tValues.at(0), 0.167, 2.e-3); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(0)); // result: 0.868315075848582 FuzzyCompare(fitResult.tdist_pValues.at(0), 0.868, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.913287614242592 FuzzyCompare(fitResult.paramValues.at(1), 0.91329, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.03422044231195 FuzzyCompare(fitResult.errorValues.at(1), 2.03422, 1.e-6); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(1)); // result: 0.448961968548804 FuzzyCompare(fitResult.tdist_tValues.at(1), 0.449, 1.e-4); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(1)); // result: 0.655522449402813 FuzzyCompare(fitResult.tdist_pValues.at(1), 0.656, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.0999593020698437 FuzzyCompare(fitResult.paramValues.at(2), 0.09996, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.0659682106823392 FuzzyCompare(fitResult.errorValues.at(2), 0.06597, 1.e-4); DEBUG(std::setprecision(15) << fitResult.tdist_tValues.at(2)); // result: 1.5152647166858 FuzzyCompare(fitResult.tdist_tValues.at(2), 1.515, 1.e-3); DEBUG(std::setprecision(15) << fitResult.tdist_pValues.at(2)); // result: 0.136402432803739 FuzzyCompare(fitResult.tdist_pValues.at(2), 0.136, 3.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 15.1760701243277 FuzzyCompare(fitResult.rsd, 15.18, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsquare); // result: 0.66733081652621 FuzzyCompare(fitResult.rsquare, 0.6673, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsquareAdj); // result: 0.645635000212702 DEBUG(std::setprecision(15) << 1.-(1.-fitResult.rsquare)*(50.-1.)/(50.-np)); // result: 0.65317468105924 // reference calulates 1-(1-R^2)(n-1)/(n-p) FuzzyCompare(1.-(1.-fitResult.rsquare)*(50.-1.)/(50.-np), 0.6532, 1.e-4); DEBUG(std::setprecision(15) << fitResult.sse); // result: 10824.71590767 FuzzyCompare(fitResult.sse, 10825, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rms); // result: 230.313104418511 // QCOMPARE(fitResult.rms, ???); DEBUG(std::setprecision(15) << fitResult.logLik); // result: -205.386034235309 FuzzyCompare(fitResult.logLik, -205.386, 1.e-6); DEBUG(std::setprecision(15) << fitResult.chisq_p); // result: // FuzzyCompare(fitResult.chisq_p, ???, 1.e-8); DEBUG(std::setprecision(15) << fitResult.fdist_F); // result: 70.6407481288434 // reference calculates sst/rms/np DEBUG(std::setprecision(15) << fitResult.sst/fitResult.rms/np); // result: 47.0938320858956 FuzzyCompare(fitResult.sst/fitResult.rms/np, 47.14, 1.e-3); DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0 QCOMPARE(fitResult.fdist_p, 0.); // exact: 5.852e-12 DEBUG(std::setprecision(15) << fitResult.aic); // result: 418.772068470618 FuzzyCompare(fitResult.aic, 418.7721, 1.e-7); DEBUG(std::setprecision(15) << fitResult.bic); // result: 426.42016049233 FuzzyCompare(fitResult.bic, 426.4202, 1.e-7); } //############################################################################## //############# non-linear regression with NIST datasets ##################### //############################################################################## void FitTest::testNonLinearMisra1a() { //NIST data for Misra1a dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-exp(-b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 500. << 0.0001; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573 FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243 FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367 FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06 FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394 FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316 FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9); } void FitTest::testNonLinearMisra1a_2() { //NIST data for Misra1a dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-exp(-b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 250. << 5.e-4; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573 FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243 FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367 FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06 FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394 FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316 FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9); } void FitTest::testNonLinearMisra1a_3() { //NIST data for Misra1a dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-exp(-b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 2.3894212918E+02 << 5.5015643181E-04; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 238.942305251573 FuzzyCompare(fitResult.paramValues.at(0), 2.3894212918E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 2.70651225218243 FuzzyCompare(fitResult.errorValues.at(0), 2.7070075241E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000550155958419367 FuzzyCompare(fitResult.paramValues.at(1), 5.5015643181E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 7.26565480949189e-06 FuzzyCompare(fitResult.errorValues.at(1), 7.2668688436E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.101878763320394 FuzzyCompare(fitResult.rsd, 1.0187876330E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.124551388988316 FuzzyCompare(fitResult.sse, 1.2455138894E-01, 1.e-9); } void FitTest::testNonLinearMisra1b() { //NIST data for Misra1b dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./(1.+b2*x/2)^2)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 500. << 0.0001; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098 FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192 FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039 FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06 FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488 FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881 FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9); } void FitTest::testNonLinearMisra1b_2() { //NIST data for Misra1b dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./(1.+b2*x/2)^2)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 300. << 2.e-4; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098 FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192 FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039 FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06 FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488 FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881 FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9); } void FitTest::testNonLinearMisra1b_3() { //NIST data for Misra1b dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./(1.+b2*x/2)^2)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 3.3799746163E+02 << 3.9039091287E-04; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 337.99775062098 FuzzyCompare(fitResult.paramValues.at(0), 3.3799746163E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.16358581006192 FuzzyCompare(fitResult.errorValues.at(0), 3.1643950207E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000390390523934039 FuzzyCompare(fitResult.paramValues.at(1), 3.9039091287E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 4.25373670682006e-06 FuzzyCompare(fitResult.errorValues.at(1), 4.2547321834E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0793014720259488 FuzzyCompare(fitResult.rsd, 7.9301471998E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0754646815857881 FuzzyCompare(fitResult.sse, 7.5464681533E-02, 1.e-9); } void FitTest::testNonLinearMisra1c() { //NIST data for Misra1c dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./sqrt(1.+2.*b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 500. << 0.0001; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969 FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875 FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746 FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06 FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661 FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468 FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8); } void FitTest::testNonLinearMisra1c_2() { //NIST data for Misra1c dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./sqrt(1.+2.*b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 600. << 2.e-4; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969 FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875 FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746 FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06 FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661 FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468 FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8); } void FitTest::testNonLinearMisra1c_3() { //NIST data for Misra1c dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(1.-1./sqrt(1.+2.*b2*x))"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 6.3642725809E+02 << 2.0813627256E-04; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 636.427904767969 FuzzyCompare(fitResult.paramValues.at(0), 6.3642725809E+02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.66168062054875 FuzzyCompare(fitResult.errorValues.at(0), 4.6638326572E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000208136026420746 FuzzyCompare(fitResult.paramValues.at(1), 2.0813627256E-04, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 1.77209416674174e-06 FuzzyCompare(fitResult.errorValues.at(1), 1.7728423155E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0584286153041661 FuzzyCompare(fitResult.rsd, 5.8428615257E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0409668370363468 FuzzyCompare(fitResult.sse, 4.0966836971E-02, 1.e-8); } void FitTest::testNonLinearMisra1d() { //NIST data for Misra1d dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*b2*x/(1.+b2*x)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 500. << 0.0001; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725 FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694 FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709 FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06 FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244 FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709 FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-9); } void FitTest::testNonLinearMisra1d_2() { //NIST data for Misra1d dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*b2*x/(1.+b2*x)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 450. << 3.e-4; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725 FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694 FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709 FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06 FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244 FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709 FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-8); } void FitTest::testNonLinearMisra1d_3() { //NIST data for Misra1d dataset QVector xData = {77.6E0,114.9E0,141.1E0,190.8E0,239.9E0,289.0E0,332.8E0,378.4E0,434.8E0,477.3E0,536.8E0,593.1E0,689.1E0,760.0E0}; QVector yData = {10.07E0,14.73E0,17.94E0,23.93E0,29.61E0,35.18E0,40.02E0,44.82E0,50.76E0,55.05E0,61.01E0,66.40E0,75.47E0,81.78E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*b2*x/(1.+b2*x)"; fitData.paramNames << "b1" << "b2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 4.3736970754E+02 << 3.0227324449E-04; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 437.370039987725 FuzzyCompare(fitResult.paramValues.at(0), 4.3736970754E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 3.64772833062694 FuzzyCompare(fitResult.errorValues.at(0), 3.6489174345E+00, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.000302272976784709 FuzzyCompare(fitResult.paramValues.at(1), 3.0227324449E-04, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.93256059733558e-06 FuzzyCompare(fitResult.errorValues.at(1), 2.9334354479E-06, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.068568272134244 FuzzyCompare(fitResult.rsd, 6.8568272111E-02, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.056419295321709 FuzzyCompare(fitResult.sse, 5.6419295283E-02, 1.e-9); } void FitTest::testNonLinearMGH09() { //NIST data for MGH09 dataset QVector xData = {4.000000E+00,2.000000E+00,1.000000E+00,5.000000E-01,2.500000E-01,1.670000E-01,1.250000E-01,1.000000E-01, 8.330000E-02,7.140000E-02,6.250000E-02}; QVector yData = {1.957000E-01,1.947000E-01,1.735000E-01,1.600000E-01,8.440000E-02,6.270000E-02,4.560000E-02,3.420000E-02, 3.230000E-02,2.350000E-02,2.460000E-02}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 2.5000000000E+01 << 3.9000000000E+01 << 4.1500000000E+01 << 3.9000000000E+01; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); // TODO: fit does not find global minimum /* DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-3); DEBUG(std::setprecision(15) << fitResult.rsd); // result: FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-9); */ } void FitTest::testNonLinearMGH09_2() { //NIST data for MGH09 dataset QVector xData = {4.000000E+00,2.000000E+00,1.000000E+00,5.000000E-01,2.500000E-01,1.670000E-01,1.250000E-01,1.000000E-01, 8.330000E-02,7.140000E-02,6.250000E-02}; QVector yData = {1.957000E-01,1.947000E-01,1.735000E-01,1.600000E-01,8.440000E-02,6.270000E-02,4.560000E-02,3.420000E-02, 3.230000E-02,2.350000E-02,2.460000E-02}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 2.5000000000E-01 << 3.9000000000E-01 << 4.1500000000E-01 << 3.9000000000E-01; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-3); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsd); // result: FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-8); DEBUG(std::setprecision(15) << fitResult.sse); // result: FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-8); } void FitTest::testNonLinearMGH09_3() { //NIST data for MGH09 dataset QVector xData = {4.000000E+00,2.000000E+00,1.000000E+00,5.000000E-01,2.500000E-01,1.670000E-01,1.250000E-01,1.000000E-01, 8.330000E-02,7.140000E-02,6.250000E-02}; QVector yData = {1.957000E-01,1.947000E-01,1.735000E-01,1.600000E-01,8.440000E-02,6.270000E-02,4.560000E-02,3.420000E-02, 3.230000E-02,2.350000E-02,2.460000E-02}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*(x^2 + b2*x)/(x^2 + x*b3 + b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.9280693458E-01 << 1.9128232873E-01 << 1.2305650693E-01 << 1.3606233068E-01; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: FuzzyCompare(fitResult.paramValues.at(0), 1.9280693458E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: FuzzyCompare(fitResult.errorValues.at(0), 1.1435312227E-02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: FuzzyCompare(fitResult.paramValues.at(1), 1.9128232873E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: FuzzyCompare(fitResult.errorValues.at(1), 1.9633220911E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: FuzzyCompare(fitResult.paramValues.at(2), 1.2305650693E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: FuzzyCompare(fitResult.errorValues.at(2), 8.0842031232E-02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: FuzzyCompare(fitResult.paramValues.at(3), 1.3606233068E-01, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: FuzzyCompare(fitResult.errorValues.at(3), 9.0025542308E-02, 1.e-5); DEBUG(std::setprecision(15) << fitResult.rsd); // result: FuzzyCompare(fitResult.rsd, 6.6279236551E-03, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: FuzzyCompare(fitResult.sse, 3.0750560385E-04, 1.e-9); } void FitTest::testNonLinearMGH10() { //NIST data for MGH10 dataset QVector xData = {5.000000E+01,5.500000E+01,6.000000E+01,6.500000E+01,7.000000E+01,7.500000E+01,8.000000E+01,8.500000E+01,9.000000E+01, 9.500000E+01,1.000000E+02,1.050000E+02,1.100000E+02,1.150000E+02,1.200000E+02,1.250000E+02}; QVector yData = {3.478000E+04,2.861000E+04,2.365000E+04,1.963000E+04,1.637000E+04,1.372000E+04,1.154000E+04,9.744000E+03,8.261000E+03, 7.030000E+03,6.005000E+03,5.147000E+03,4.427000E+03,3.820000E+03,3.307000E+03,2.872000E+03}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*exp(b2/(x+b3))"; fitData.paramNames << "b1" << "b2" << "b3"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 2.0000000000E+00 << 4.0000000000E+05 << 2.5000000000E+04; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205 (Windows: 0.00560964247264364) FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888682057687 FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191 (Windows: 6181.34545410281) FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.3105479190063 FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718 FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784915645388214 FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923 (Windows: 2.60097400662837) FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 1.e-10); DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321 (FreeBSD: 87.9458551726946, Windows: 87.9458551810338) FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-9); } void FitTest::testNonLinearMGH10_2() { //NIST data for MGH10 dataset QVector xData = {5.000000E+01,5.500000E+01,6.000000E+01,6.500000E+01,7.000000E+01,7.500000E+01,8.000000E+01,8.500000E+01,9.000000E+01, 9.500000E+01,1.000000E+02,1.050000E+02,1.100000E+02,1.150000E+02,1.200000E+02,1.250000E+02}; QVector yData = {3.478000E+04,2.861000E+04,2.365000E+04,1.963000E+04,1.637000E+04,1.372000E+04,1.154000E+04,9.744000E+03,8.261000E+03, 7.030000E+03,6.005000E+03,5.147000E+03,4.427000E+03,3.820000E+03,3.307000E+03,2.872000E+03}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*exp(b2/(x+b3))"; fitData.paramNames << "b1" << "b2" << "b3"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 2.0000000000E-02 << 4.0000000000E+03 << 2.5000000000E+02; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205 FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888586677272 FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191 FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.3105167849511 FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718 FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784914752553929 FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923 FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 1.e-10); DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321 FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-9); } void FitTest::testNonLinearMGH10_3() { //NIST data for MGH10 dataset QVector xData = {5.000000E+01,5.500000E+01,6.000000E+01,6.500000E+01,7.000000E+01,7.500000E+01,8.000000E+01,8.500000E+01,9.000000E+01, 9.500000E+01,1.000000E+02,1.050000E+02,1.100000E+02,1.150000E+02,1.200000E+02,1.250000E+02}; QVector yData = {3.478000E+04,2.861000E+04,2.365000E+04,1.963000E+04,1.637000E+04,1.372000E+04,1.154000E+04,9.744000E+03,8.261000E+03, 7.030000E+03,6.005000E+03,5.147000E+03,4.427000E+03,3.820000E+03,3.307000E+03,2.872000E+03}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1*exp(b2/(x+b3))"; fitData.paramNames << "b1" << "b2" << "b3"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5.6096364710E-03 << 6.1813463463E+03 << 3.4522363462E+02; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 3); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.00560963848336205 FuzzyCompare(fitResult.paramValues.at(0), 5.6096364710E-03, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.000156888348998521 FuzzyCompare(fitResult.errorValues.at(0), 1.5687892471E-04, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 6181.34604697191 FuzzyCompare(fitResult.paramValues.at(1), 6.1813463463E+03, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 23.310508065631 FuzzyCompare(fitResult.errorValues.at(1), 2.3309021107E+01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 345.223624540718 FuzzyCompare(fitResult.paramValues.at(2), 3.4522363462E+02, 1.e-7); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.784914424350028 FuzzyCompare(fitResult.errorValues.at(2), 7.8486103508E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 2.6009740064923 FuzzyCompare(fitResult.rsd, 2.6009740065E+00, 1.e-11); DEBUG(std::setprecision(15) << fitResult.sse); // result: 87.9458551718321 FuzzyCompare(fitResult.sse, 8.7945855171E+01, 1.e-11); } void FitTest::testNonLinearRat43() { //NIST data for Rat43 dataset QVector xData = {1.0E0,2.0E0,3.0E0,4.0E0,5.0E0,6.0E0,7.0E0,8.0E0,9.0E0,10.0E0,11.0E0,12.0E0,13.0E0,14.0E0,15.0E0}; QVector yData = {16.08E0,33.83E0,65.80E0,97.20E0,191.55E0,326.20E0,386.87E0,520.53E0,590.03E0,651.92E0,724.93E0,699.56E0,689.96E0,637.56E0,717.41E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1/pow(1. + exp(b2-b3*x), 1/b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.0000000000E+02 << 1.0000000000E+01 << 1.0000000000E+00 << 1.0000000000E+00; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193 FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3022524293302 FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844 FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08290034908325 FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637 FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195664372178938 FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867 FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687627478905195 FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284 FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-10); DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859 FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-10); } void FitTest::testNonLinearRat43_2() { //NIST data for Rat43 dataset QVector xData = {1.0E0,2.0E0,3.0E0,4.0E0,5.0E0,6.0E0,7.0E0,8.0E0,9.0E0,10.0E0,11.0E0,12.0E0,13.0E0,14.0E0,15.0E0}; QVector yData = {16.08E0,33.83E0,65.80E0,97.20E0,191.55E0,326.20E0,386.87E0,520.53E0,590.03E0,651.92E0,724.93E0,699.56E0,689.96E0,637.56E0,717.41E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1/pow(1. + exp(b2-b3*x), 1/b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 7.0000000000E+02 << 5.0000000000E+00 << 7.5000000000E-01 << 1.3000000000E+00; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193 FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-6); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3023134145464 (FreeBSD: 16.3023141645004) FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844 FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08288970703906 FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637 FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195662802156063 FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867 FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687623222160242 FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284 FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-10); DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859 FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-11); } void FitTest::testNonLinearRat43_3() { //NIST data for Rat43 dataset QVector xData = {1.0E0,2.0E0,3.0E0,4.0E0,5.0E0,6.0E0,7.0E0,8.0E0,9.0E0,10.0E0,11.0E0,12.0E0,13.0E0,14.0E0,15.0E0}; QVector yData = {16.08E0,33.83E0,65.80E0,97.20E0,191.55E0,326.20E0,386.87E0,520.53E0,590.03E0,651.92E0,724.93E0,699.56E0,689.96E0,637.56E0,717.41E0}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "b1/pow(1. + exp(b2-b3*x), 1/b4)"; fitData.paramNames << "b1" << "b2" << "b3" << "b4"; //fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 6.9964151270E+02 << 5.2771253025E+00 << 7.5962938329E-01 << 1.2792483859E+00; fitData.paramLowerLimits << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max() << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max() << std::numeric_limits::max(); fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 699.641340982193 FuzzyCompare(fitResult.paramValues.at(0), 6.9964151270E+02, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 16.3022905400761 FuzzyCompare(fitResult.errorValues.at(0), 1.6302297817E+01, 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 5.2771555758844 FuzzyCompare(fitResult.paramValues.at(1), 5.2771253025E+00, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 2.08289316520407 FuzzyCompare(fitResult.errorValues.at(1), 2.0828735829E+00, 1.e-5); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: 0.759632366113637 FuzzyCompare(fitResult.paramValues.at(2), 7.5962938329E-01, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: 0.195663312668779 FuzzyCompare(fitResult.errorValues.at(2), 1.9566123451E-01, 1.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: 1.27925748993867 FuzzyCompare(fitResult.paramValues.at(3), 1.2792483859E+00, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: 0.687624541478887 FuzzyCompare(fitResult.errorValues.at(3), 6.8761936385E-01, 1.e-5); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 28.2624146626284 FuzzyCompare(fitResult.rsd, 2.8262414662E+01, 1.e-11); DEBUG(std::setprecision(15) << fitResult.sse); // result: 8786.4049081859 FuzzyCompare(fitResult.sse, 8.7864049080E+03, 1.e-11); } // https://bugs.kde.org/show_bug.cgi?id=393213 void FitTest::testNonLinearMichaelis_Menten() { // generic data QVector xData = {0.0,0.2,0.4,0.6,0.8,1.0,1.2,1.4,1.6,1.8,2.0}; QVector yData = {0.0,0.6,0.65,0.7,0.75,0.75,0.8,0.9,0.85,0.95,0.9}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "Vm * x/(Km + x)"; fitData.paramNames << "Vm" << "Km"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.0 << 1.0 ; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 0.945561082955744 FuzzyCompare(fitResult.paramValues.at(0), 0.94556434933256, 1.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.0388724403232334 FuzzyCompare(fitResult.errorValues.at(0), 0.0388803714011844, 3.e-4); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 0.159396945453528 FuzzyCompare(fitResult.paramValues.at(1), 0.159400761666661, 3.e-5); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0388244491752518 FuzzyCompare(fitResult.errorValues.at(1), 0.0388429738447119, 5.e-4); DEBUG(std::setprecision(15) << fitResult.rms); // result: 0.00280486748619082 FuzzyCompare(fitResult.rms, 0.00280486748877263, 1.e-9); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.0529609996713697 FuzzyCompare(fitResult.rsd, 0.0529609996957444, 1.e-9); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.0252438073757174 FuzzyCompare(fitResult.sse, 0.0252438073989537, 1.e-9); } //############################################################################## //######################### Fits with weights ################################# //############################################################################## // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testNonLinearGP_lcdemo() { // data from https://github.com/gnuplot/gnuplot/blob/master/demo/lcdemo.dat QVector xData = {39.471,40.091,40.602,41.058,41.438,41.880,42.437,42.836,43.209,43.599,43.997,44.313,44.908,45.169,45.594,45.743,45.796,45.816, 45.841,45.876,45.908,45.959,46.008,46.040,46.060,46.096,46.126,46.149,46.372,46.625,46.945,47.326,47.708,48.095,48.540,48.927,49.314}; QVector yData = {1.03307,1.03246,1.03197,1.03153,1.03117,1.03074,1.03021,1.02982,1.02946,1.02907,1.02867,1.02833,1.02765,1.02735,1.02683, 1.02661,1.02650,1.02644,1.02634,1.02623,1.02611,1.02592,1.02561,1.02526,1.02506,1.02500,1.02496,1.02494,1.02474,1.02452,1.02425,1.02393, 1.02361,1.02329,1.02293,1.02262,1.02231}; QVector yError = {0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010,0.010, 0.010,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001,0.001}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); // x > Tc : d + mh(x-Tc) // x < Tc : d + ml(x-Tc) + b tanh(g(Tc-x)) fitData.model = "d + theta(x-Tc)*mh*(x-Tc) + theta(Tc-x)*(ml*(x-Tc)+b*tanh(g*(Tc-x)))"; fitData.paramNames << "d" << "Tc" << "mh" << "ml" << "b" << "g"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 1.02 << 45. << -0.0005 << -0.0005 << 0.01002 << 1.0; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitData.yWeightsType = nsl_fit_weight_instrumental; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 1.02499979307627 (Windows: 1.02561781433026) FuzzyCompare(fitResult.paramValues.at(0), 1.02499621370905, 1.e-3); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 4.81672854812941e-06 //TODO FuzzyCompare(fitResult.errorValues.at(0), 7.27819513635249e-06, 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: 46.0647953740441 (Windows: 45.4871250830364) FuzzyCompare(fitResult.paramValues.at(1), 46.0665367045608, 2.e-2); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 9.90288940612482 //TODO FuzzyCompare(fitResult.errorValues.at(1), 0.00159887430059728, 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(2)); // result: -0.000835023995828296 (Windows: -0.000883960665773456) FuzzyCompare(fitResult.paramValues.at(2), -0.0008340717673769, 1.e-1); DEBUG(std::setprecision(15) << fitResult.errorValues.at(2)); // result: //TODO FuzzyCompare(fitResult.errorValues.at(2), , 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(3)); // result: -0.000987547207638997 FuzzyCompare(fitResult.paramValues.at(3), -0.00103152542276233, 1.e-1); DEBUG(std::setprecision(15) << fitResult.errorValues.at(3)); // result: //TODO FuzzyCompare(fitResult.errorValues.at(3), , 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(4)); // result: 0.00158880319355268 FuzzyCompare(fitResult.paramValues.at(4), 0.00139548391000006, 1.5e-1); DEBUG(std::setprecision(15) << fitResult.errorValues.at(4)); // result: //TODO FuzzyCompare(fitResult.errorValues.at(4), , 1.e-6); DEBUG(std::setprecision(15) << fitResult.paramValues.at(5)); // result: 6.34254053273612 (Windows: 4.45482397068125) // FuzzyCompare(fitResult.paramValues.at(5), 6.92493866108287, 1.e-1); DEBUG(std::setprecision(15) << fitResult.errorValues.at(5)); // result: //TODO FuzzyCompare(fitResult.errorValues.at(5), , 1.e-6); DEBUG(std::setprecision(15) << fitResult.rms); // result: 98.0672185899393 // FuzzyCompare(fitResult.rms, 0.000188776, 1.e-11); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 9.90288940612482 // FuzzyCompare(fitResult.rsd, 0.0137395924378767, 1.e-11); DEBUG(std::setprecision(15) << fitResult.sse); // result: 3040.08377628812 // FuzzyCompare(fitResult.sse, 0.00585206841112775, 1.e-11); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_noerror() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; // QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; // QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); // Column yErrorColumn("yerr", AbstractColumn::Numeric); // yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); // fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "a1 + a2 * x"; fitData.paramNames << "a1" << "a2"; // fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } // fitData.yWeightsType = nsl_fit_weight_instrumental; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.76118518568804 FuzzyCompare(fitResult.paramValues.at(0), 5.76118519043878, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.189485192391863 FuzzyCompare(fitResult.errorValues.at(0), 0.189485195921141, 1.e-7); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.539577274076964 FuzzyCompare(fitResult.paramValues.at(1), -0.539577274983977, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0421265487265946 FuzzyCompare(fitResult.errorValues.at(1), 0.0421265483886995, 1.e-8); DEBUG(std::setprecision(15) << fitResult.rms); // result: 0.100082940279452 QCOMPARE(fitResult.rms, 0.100082940279452); DEBUG(std::setprecision(15) << fitResult.rsd); // result: 0.316358878932538 QCOMPARE(fitResult.rsd, 0.316358878932538); DEBUG(std::setprecision(15) << fitResult.sse); // result: 0.800663522235619 QCOMPARE(fitResult.sse, 0.800663522235619); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_yerror_polynomial() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; // QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 1; XYFitCurve::initFitData(fitData); // fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; fitData.yWeightsType = nsl_fit_weight_direct; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 6.10010931666575 FuzzyCompare(fitResult.paramValues.at(0), 6.10010931635002, 1.e-10); - DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.424059452104775 - FuzzyCompare(fitResult.errorValues.at(0), 0.424059452104785, 1.e-11); - DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.610812956583933 - FuzzyCompare(fitResult.paramValues.at(1), -0.610812956537254, 1.e-10); - DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0623409539388997 - FuzzyCompare(fitResult.errorValues.at(1), 0.0623409539389024, 1.e-11); + DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.424059452104775, i386: 0.424059429083679 + FuzzyCompare(fitResult.errorValues.at(0), 0.424059452104785, 1.e-10); + DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.610812956583933, i386: -0.610812954591566 + FuzzyCompare(fitResult.paramValues.at(1), -0.610812956537254, 1.e-9); + DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0623409539388997, i386: 0.0623409508171503 + FuzzyCompare(fitResult.errorValues.at(1), 0.0623409539389024, 1.e-10); QCOMPARE(fitResult.rms, 4.29315093729054); QCOMPARE(fitResult.rsd, 2.07199202153158); QCOMPARE(fitResult.sse, 34.3452074983243); DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.000101015996328551 //TODO QCOMPARE(fitResult.fdist_p, 3.51725605201025e-05); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_yerror_custom() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; // QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "a1 + a2 * x"; fitData.paramNames << "a1" << "a2"; // fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitData.yWeightsType = nsl_fit_weight_direct; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 6.10010932451396 FuzzyCompare(fitResult.paramValues.at(0), 6.10010931635002, 1.e-8); DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.424059453530443 FuzzyCompare(fitResult.errorValues.at(0), 0.424059452104785, 1.e-8); DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.610812957040808 FuzzyCompare(fitResult.paramValues.at(1), -0.610812956537254, 1.e-9); DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0623409543258892 FuzzyCompare(fitResult.errorValues.at(1), 0.0623409539389024, 1.e-8); QCOMPARE(fitResult.rms, 4.29315093729054); QCOMPARE(fitResult.rsd, 2.07199202153158); QCOMPARE(fitResult.sse, 34.3452074983243); DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.000101015996328551 //TODO QCOMPARE(fitResult.fdist_p, 3.51725605201025e-05); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_xyerror_polynomial() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column xErrorColumn("xerr", AbstractColumn::Numeric); xErrorColumn.replaceValues(0, xError); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setXErrorColumn(&xErrorColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_basic; fitData.modelType = nsl_fit_model_polynomial; fitData.degree = 1; XYFitCurve::initFitData(fitData); fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; fitData.xWeightsType = nsl_fit_weight_direct; fitData.yWeightsType = nsl_fit_weight_direct; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.3960522989993 FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458387983792 FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448925094435 FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627287153768 FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455608152396 FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252038082151 FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564486521917 FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031749154456 //TODO QCOMPARE(fitResult.fdist_p, 0.153296328355244); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_xyerror_custom() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column xErrorColumn("xerr", AbstractColumn::Numeric); xErrorColumn.replaceValues(0, xError); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setXErrorColumn(&xErrorColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "a1 + a2 * x"; fitData.paramNames << "a1" << "a2"; fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitData.xWeightsType = nsl_fit_weight_direct; fitData.yWeightsType = nsl_fit_weight_direct; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.39605223831693 FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458369696386 FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448913528306 FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627257036884 FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455596317092 FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252033241616 FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564477053674 FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031645455255 //TODO QCOMPARE(fitResult.fdist_p, 0.153296328355244); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_xyerror_custom_instrumental_weight() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; // w_i -> s_i for (int i = 0; i < xError.size(); i++) { xError[i] = 1./sqrt(xError[i]); yError[i] = 1./sqrt(yError[i]); } //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column xErrorColumn("xerr", AbstractColumn::Numeric); xErrorColumn.replaceValues(0, xError); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setXErrorColumn(&xErrorColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "a1 + a2 * x"; fitData.paramNames << "a1" << "a2"; // fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitData.xWeightsType = nsl_fit_weight_instrumental; fitData.yWeightsType = nsl_fit_weight_instrumental; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.3960521880058 FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458367406729 FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448904801834 FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627229337577 FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455610878403 FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252039197063 FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564488702722 FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031773039329 //TODO QCOMPARE(fitResult.fdist_p, 0.153296328355244); } // see http://gnuplot.sourceforge.net/demo_5.2/fit.html void FitTest::testLinearGP_PY_xyerror_custom_inverse_weight() { // Pearson's data and York's weights QVector xData = {0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4}; QVector yData = {5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5}; QVector xError = {1000.,1000.,500.,800.,200.,80.,60.,20.,1.8,1.0}; QVector yError = {1.0,1.8,4.,8.,20.,20.,70.,70.,100.,500.}; // w_i -> 1/w_i for (int i = 0; i < xError.size(); i++) { xError[i] = 1./xError[i]; yError[i] = 1./yError[i]; } //data source columns Column xDataColumn("x", AbstractColumn::Numeric); xDataColumn.replaceValues(0, xData); Column yDataColumn("y", AbstractColumn::Numeric); yDataColumn.replaceValues(0, yData); Column xErrorColumn("xerr", AbstractColumn::Numeric); xErrorColumn.replaceValues(0, xError); Column yErrorColumn("yerr", AbstractColumn::Numeric); yErrorColumn.replaceValues(0, yError); XYFitCurve fitCurve("fit"); fitCurve.setXDataColumn(&xDataColumn); fitCurve.setYDataColumn(&yDataColumn); fitCurve.setXErrorColumn(&xErrorColumn); fitCurve.setYErrorColumn(&yErrorColumn); //prepare the fit XYFitCurve::FitData fitData = fitCurve.fitData(); fitData.modelCategory = nsl_fit_model_custom; XYFitCurve::initFitData(fitData); fitData.model = "a1 + a2 * x"; fitData.paramNames << "a1" << "a2"; // fitData.eps = 1.e-12; const int np = fitData.paramNames.size(); fitData.paramStartValues << 5. << -0.5; for (int i = 0; i < np; i++) { fitData.paramLowerLimits << -std::numeric_limits::max(); fitData.paramUpperLimits << std::numeric_limits::max(); } fitData.xWeightsType = nsl_fit_weight_inverse; fitData.yWeightsType = nsl_fit_weight_inverse; fitCurve.setFitData(fitData); //perform the fit fitCurve.recalculate(); const XYFitCurve::FitResult& fitResult = fitCurve.fitResult(); //check the results QCOMPARE(fitResult.available, true); QCOMPARE(fitResult.valid, true); QCOMPARE(np, 2); DEBUG(std::setprecision(15) << fitResult.paramValues.at(0)); // result: 5.39605219384715 FuzzyCompare(fitResult.paramValues.at(0), 5.39749958415886, 3.e-4); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.errorValues.at(0)); // result: 0.361458367208767 FuzzyCompare(fitResult.errorValues.at(0), 0.361439886824914, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.paramValues.at(1)); // result: -0.463448906090293 FuzzyCompare(fitResult.paramValues.at(1), -0.463744669606207, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.errorValues.at(1)); // result: 0.0706627222052917 FuzzyCompare(fitResult.errorValues.at(1), 0.0706637562101211, 1.e-4); // -""- DEBUG(std::setprecision(15) << fitResult.rms); // result: 1.49455610679827 FuzzyCompare(fitResult.rms, 1.49417194665446, 1.e-3); // gnuplot result ("effective variance" method) DEBUG(std::setprecision(15) << fitResult.rsd); // result: 1.22252039115847 FuzzyCompare(fitResult.rsd, 1.22236326296828, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.sse); // result: 11.9564488543861 FuzzyCompare(fitResult.sse, 11.9533755732357, 1.e-3); // -""- DEBUG(std::setprecision(15) << fitResult.fdist_p); // result: 0.00441031771299435 //TODO QCOMPARE(fitResult.fdist_p, 0.153296328355244); } QTEST_MAIN(FitTest) diff --git a/tests/import_export/ASCII/CMakeLists.txt b/tests/import_export/ASCII/CMakeLists.txt index 9b8cedfa0..eef9f81de 100644 --- a/tests/import_export/ASCII/CMakeLists.txt +++ b/tests/import_export/ASCII/CMakeLists.txt @@ -1,35 +1,35 @@ add_executable (asciifiltertest AsciiFilterTest.cpp) -target_link_libraries(asciifiltertest ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTSVG_LIBRARY} ${QT_QTSQL_LIBRARIES}) +target_link_libraries(asciifiltertest Qt5::Test) target_link_libraries(asciifiltertest KF5::Archive KF5::XmlGui ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) IF (Qt5SerialPort_FOUND) target_link_libraries(asciifiltertest Qt5::SerialPort ) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries(asciifiltertest KF5::SyntaxHighlighting ) ENDIF () #TODO: KF5::NewStuff IF (CANTOR_LIBS_FOUND) target_link_libraries(asciifiltertest ${CANTOR_LIBS} ) ENDIF () IF (HDF5_FOUND) target_link_libraries(asciifiltertest ${HDF5_C_LIBRARIES} ) ENDIF () IF (FFTW_FOUND) target_link_libraries(asciifiltertest ${FFTW_LIBRARIES} ) ENDIF () IF (NETCDF_FOUND) target_link_libraries(asciifiltertest ${NETCDF_LIBRARY} ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries(asciifiltertest ${CFITSIO_LIBRARY} ) ENDIF () IF (USE_LIBORIGIN) target_link_libraries(asciifiltertest liborigin-static ) ENDIF () target_link_libraries(asciifiltertest labplot2lib) add_test(NAME asciifiltertest COMMAND asciifiltertest) diff --git a/tests/import_export/ASCII/scripts/client_cpu_udp_socket.py b/tests/import_export/ASCII/scripts/client_cpu_udp_socket.py new file mode 100755 index 000000000..3c8c7d6ad --- /dev/null +++ b/tests/import_export/ASCII/scripts/client_cpu_udp_socket.py @@ -0,0 +1,15 @@ +#!/usr/bin/python + +from socket import * +import psutil + +HOST = 'localhost' +PORT = 1027 +ADDR = (HOST,PORT) +client = socket(AF_INET, SOCK_DGRAM) +client.bind(ADDR) +while True: + data, addr = client.recvfrom(1024) + print 'read ' + data, addr + + diff --git a/tests/import_export/ASCII/scripts/cpu_usage_file.py b/tests/import_export/ASCII/scripts/cpu_usage_file.py index 869c785fd..d12b79229 100755 --- a/tests/import_export/ASCII/scripts/cpu_usage_file.py +++ b/tests/import_export/ASCII/scripts/cpu_usage_file.py @@ -1,15 +1,12 @@ #!/usr/bin/python -import socket import psutil -import time - while True: cpu_percents = str(psutil.cpu_percent(interval=0.5, percpu=True)) line = str(cpu_percents)[1:-1] print line - datafile = open("cpuData.txt","a") + datafile = open("cpuData.txt", "a") datafile.write(line + "\n") datafile.close() diff --git a/tests/import_export/ASCII/scripts/server_cpu_local_socket.py b/tests/import_export/ASCII/scripts/server_cpu_local_socket.py index 5834faab1..ae1036980 100755 --- a/tests/import_export/ASCII/scripts/server_cpu_local_socket.py +++ b/tests/import_export/ASCII/scripts/server_cpu_local_socket.py @@ -1,23 +1,24 @@ #!/usr/bin/python -import socket -import psutil +import socket, psutil, os ADDR = './local_socket' serv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -BUFSIZE = 4096 +if os.path.exists(ADDR): + print 'socket exists. Removing it' + os.remove(ADDR) serv.bind(ADDR) serv.listen(1) print 'listening ...' while True: conn, addr = serv.accept() print 'client connected ... ', addr cpu_percent = str(psutil.cpu_percent()) conn.send(cpu_percent) print 'written ' + cpu_percent conn.close() print 'client disconnected' diff --git a/tests/import_export/ASCII/scripts/server_cpu_tcp_socket.py b/tests/import_export/ASCII/scripts/server_cpu_tcp_socket.py index fef73b09f..9940d937b 100755 --- a/tests/import_export/ASCII/scripts/server_cpu_tcp_socket.py +++ b/tests/import_export/ASCII/scripts/server_cpu_tcp_socket.py @@ -1,26 +1,22 @@ #!/usr/bin/python -import socket -import psutil +import socket, psutil HOST = 'localhost' PORT = 1027 ADDR = (HOST,PORT) serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -BUFSIZE = 4096 - - serv.bind(ADDR) serv.listen(1) print 'listening ...' while True: conn, addr = serv.accept() print 'client connected ... ', addr cpu_percent = str(psutil.cpu_percent()) conn.send(cpu_percent) print 'written ' + cpu_percent conn.close() print 'client disconnected' diff --git a/tests/import_export/ASCII/scripts/server_cpu_udp_socket.py b/tests/import_export/ASCII/scripts/server_cpu_udp_socket.py index cf2d9117e..7282d428d 100755 --- a/tests/import_export/ASCII/scripts/server_cpu_udp_socket.py +++ b/tests/import_export/ASCII/scripts/server_cpu_udp_socket.py @@ -1,15 +1,15 @@ #!/usr/bin/python -import socket +from socket import * import psutil HOST = 'localhost' PORT = 1027 ADDR = (HOST,PORT) -serv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + +serv = socket(AF_INET, SOCK_DGRAM) +serv.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) while True: cpu_percent = str(psutil.cpu_percent(interval=0.5)) serv.sendto(cpu_percent, ADDR) - print 'written ' + cpu_percent - + print 'written ' + cpu_percent, ADDR diff --git a/tests/import_export/project/CMakeLists.txt b/tests/import_export/project/CMakeLists.txt index a62c33c55..d209073ea 100755 --- a/tests/import_export/project/CMakeLists.txt +++ b/tests/import_export/project/CMakeLists.txt @@ -1,35 +1,35 @@ add_executable (projectimporttest ProjectImportTest.cpp) -target_link_libraries(projectimporttest ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTSVG_LIBRARY} ${QT_QTSQL_LIBRARIES}) -target_link_libraries(projectimporttest KF5::KDELibs4Support KF5::Archive KF5::XmlGui ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) +target_link_libraries(projectimporttest Qt5::Test) +target_link_libraries(projectimporttest KF5::Archive KF5::XmlGui ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) IF (Qt5SerialPort_FOUND) target_link_libraries(projectimporttest Qt5::SerialPort ) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries(projectimporttest KF5::SyntaxHighlighting ) ENDIF () #TODO: KF5::NewStuff IF (CANTOR_LIBS_FOUND) target_link_libraries(projectimporttest ${CANTOR_LIBS} ) ENDIF () IF (HDF5_FOUND) target_link_libraries(projectimporttest ${HDF5_C_LIBRARIES} ) ENDIF () IF (FFTW_FOUND) target_link_libraries(projectimporttest ${FFTW_LIBRARIES} ) ENDIF () IF (NETCDF_FOUND) target_link_libraries(projectimporttest ${NETCDF_LIBRARY} ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries(projectimporttest ${CFITSIO_LIBRARY} ) ENDIF () IF (USE_LIBORIGIN) target_link_libraries(projectimporttest liborigin-static ) ENDIF () target_link_libraries(projectimporttest labplot2lib) add_test(NAME projectimporttest COMMAND projectimporttest)