From 88d4086d918ff086d2b443096229b72231dea50b Mon Sep 17 00:00:00 2001 From: bruno boutin Date: Tue, 16 Jan 2024 13:24:23 +0100 Subject: [PATCH] Add possibility to reset column types when changing the ordinal threshold (#5392) * Add possibility to reset column types when changing the ordinal threshold Fixes https://github.com/jasp-stats/jasp-issues/issues/2426 If you change the threshold between Categorical and Scale, it might be handy to reset the variable types with this new value. I have also redesigned a bit the Preferences/Data to have the same look as the other preferences memus. * Update preferencesmodel.cpp.in * Modify buttontext for clarity and add a tooltip --- .../components/JASP/Widgets/DataTableView.qml | 18 +- .../JASP/Widgets/FileMenu/PrefsData.qml | 154 +++++++++--------- Desktop/data/columnmodel.cpp | 1 + Desktop/data/columnsmodel.cpp | 2 +- Desktop/data/datasetpackage.cpp | 35 +++- Desktop/data/datasetpackage.h | 2 + Desktop/data/datasettablemodel.cpp | 2 +- Desktop/data/datasettablemodel.h | 2 +- Desktop/gui/preferencesmodel.cpp.in | 10 -- Desktop/gui/preferencesmodel.h | 5 - Desktop/mainwindow.cpp | 5 + Desktop/mainwindow.h | 1 + Desktop/utilities/settings.cpp | 1 - Desktop/utilities/settings.h | 1 - 14 files changed, 129 insertions(+), 110 deletions(-) diff --git a/Desktop/components/JASP/Widgets/DataTableView.qml b/Desktop/components/JASP/Widgets/DataTableView.qml index 6b6b484ca4..ecbc187177 100644 --- a/Desktop/components/JASP/Widgets/DataTableView.qml +++ b/Desktop/components/JASP/Widgets/DataTableView.qml @@ -515,6 +515,22 @@ FocusScope property real iconTextPadding: 10 readonly property int __iconDim: baseBlockDim * preferencesModel.uiScale + function getColumnTypeIcon(type) + { + return String(dataSetModel.getColumnTypesWithIcons()[type]) === "" ? "" : jaspTheme.iconPath + dataSetModel.getColumnTypesWithIcons()[type] + } + + Connections + { + target: dataSetModel + function onColumnTypeChanged(colName, colType) + { + if (headerText === colName) + colIcon.source = getColumnTypeIcon(colType) + } + } + + Keys.onPressed: (event) => { var controlPressed = Boolean(event.modifiers & Qt.ControlModifier) @@ -547,7 +563,7 @@ FocusScope anchors.margins: 4 - source: String(dataSetModel.getColumnTypesWithIcons()[columnType]) === "" ? "" : jaspTheme.iconPath + dataSetModel.getColumnTypesWithIcons()[columnType] + source: getColumnTypeIcon(columnType) width: source == "" ? 0 : headerRoot.__iconDim height: headerRoot.__iconDim diff --git a/Desktop/components/JASP/Widgets/FileMenu/PrefsData.qml b/Desktop/components/JASP/Widgets/FileMenu/PrefsData.qml index b816c8d119..b51fd78705 100644 --- a/Desktop/components/JASP/Widgets/FileMenu/PrefsData.qml +++ b/Desktop/components/JASP/Widgets/FileMenu/PrefsData.qml @@ -1,11 +1,10 @@ -import QtQuick 2.11 -import QtQuick.Controls 2.4 -import JASP.Widgets 1.0 +import QtQuick +import QtQuick.Controls as QTC +import JASP.Widgets +import JASP.Controls -import JASP.Controls 1.0 - -ScrollView +QTC.ScrollView { id: scrollPrefs focus: true @@ -30,14 +29,13 @@ ScrollView PrefsGroupRect { - spacing: jaspTheme.rowSpacing - implicitWidth: scrollPrefs.width - (jaspTheme.generalAnchorMargin * 2) + id: spreadSheetEditor + title: qsTr("External spreadsheet editor (for data synchronization)") - Item //Use default spreadsheet editor + Item { - - height: useDefaultEditor.height + (editCustomEditor.visible ? editCustomEditor.height : linuxInfo.visible ? linuxInfo.height : 0) - width: parent.width - jaspTheme.generalAnchorMargin + height: editCustomEditor.y + editCustomEditor.height + width: parent.width CheckBox { @@ -68,7 +66,6 @@ ScrollView Item { id: editCustomEditor - // visible: !LINUX && !preferencesModel.useDefaultEditor enabled: !LINUX && !preferencesModel.useDefaultEditor width: parent.width height: browseEditorButton.height @@ -110,7 +107,7 @@ ScrollView onTextChanged: preferencesModel.customEditor = text color: jaspTheme.textEnabled - KeyNavigation.tab: customThreshold + KeyNavigation.tab: thresholdScale anchors { @@ -129,55 +126,50 @@ ScrollView } } } + } + PrefsGroupRect + { + id: thresholdScaleSetting + title: qsTr("Import threshold between Categorical or Scale") - Item //Scale threshold + SpinBox { - height: customThreshold.height + thresholdScale.height - width: customThreshold.width - - CheckBox - { - id: customThreshold - label: qsTr("Import threshold between Categorical or Scale") + ":" - checked: preferencesModel.customThresholdScale - onCheckedChanged: preferencesModel.customThresholdScale = checked - ToolTip.delay: 500 - ToolTip.timeout: 6000 //Some longer to read carefully - toolTip: qsTr("Threshold number of unique integers before classifying a variable as 'scale'.\nYou need to reload your data to take effect! Check help for more info.") + id: thresholdScale + value: preferencesModel.thresholdScale + onValueChanged: preferencesModel.thresholdScale = value - KeyNavigation.tab: thresholdScale + KeyNavigation.tab: resetDataWithThresholdButton - } + toolTip: qsTr("If a variable has more distinct integer values than this it will be interpreted as scale.") - SpinBox + Button { - id: thresholdScale - value: preferencesModel.thresholdScale - onValueChanged: preferencesModel.thresholdScale = value - enabled: preferencesModel.customThresholdScale - - KeyNavigation.tab: missingValueDataLabelInput - + id: resetDataWithThresholdButton + label: qsTr("Reset types of loaded variables") + visible: mainWindow.dataAvailable + onClicked: mainWindow.resetVariableTypes() anchors { - top: customThreshold.bottom - left: customThreshold.left - leftMargin: jaspTheme.subOptionOffset + top: thresholdScale.top + left: thresholdScale.right + leftMargin: jaspTheme.generalAnchorMargin } + + KeyNavigation.tab: missingValueDataLabelInput } } + } + + PrefsGroupRect + { + id: missingValuesSettings + title: qsTr("Missing values setting") Item { id: missingValueDataLabelItem height: missingValueDataLabelInput.height - anchors - { - left: parent.left - right: parent.right - margins: jaspTheme.generalAnchorMargin - } Label { @@ -219,48 +211,48 @@ ScrollView showResetWorkspaceButton: true resetButtonLabel: qsTr("Reset with standard values") resetButtonTooltip: qsTr("Reset missing values with the standard JASP missing values") - KeyNavigation.tab: noBomNative + KeyNavigation.tab: WINDOWS ? noBomNative : useDefaultEditor } } + } - PrefsGroupRect + PrefsGroupRect + { + visible: WINDOWS + enabled: WINDOWS + title: qsTr("Windows workaround") + + CheckBox { - visible: WINDOWS - enabled: WINDOWS - title: qsTr("Windows workaround") - - CheckBox - { - id: noBomNative - label: qsTr("Assume CSV is the selected codepage, when no BOM is specified.") - checked: preferencesModel.windowsNoBomNative - onCheckedChanged: preferencesModel.windowsNoBomNative = checked - toolTip: qsTr("See documentation for more information ") + id: noBomNative + label: qsTr("Assume CSV is the selected codepage, when no BOM is specified.") + checked: preferencesModel.windowsNoBomNative + onCheckedChanged: preferencesModel.windowsNoBomNative = checked + toolTip: qsTr("See documentation for more information ") - KeyNavigation.tab: codePageSelection - } - - /*ErrorMessage - { - text: WINDOWS && windowsCodePagesHelper.error ? qsTr("Some problem occured loading the available codepages...") : "" - }*/ + KeyNavigation.tab: codePageSelection + } + /*ErrorMessage + { + text: WINDOWS && windowsCodePagesHelper.error ? qsTr("Some problem occured loading the available codepages...") : "" + }*/ - DropDown - { - id: codePageSelection - enabled: preferencesModel.windowsNoBomNative && WINDOWS //&& !windowsCodePagesHelper.error - toolTip: qsTr("See documentation for more information ") - values: WINDOWS ? windowsCodePagesHelper.codePageIDs : [] - addEmptyValue: true - showEmptyValueAsNormal: true - addLineAfterEmptyValue: true - placeholderText: qsTr("Choose codepage here") - startValue: WINDOWS ? windowsCodePagesHelper.codePageID : "" - onValueChanged: if(WINDOWS) windowsCodePagesHelper.codePageID = value - - KeyNavigation.tab: useDefaultEditor - } + + DropDown + { + id: codePageSelection + enabled: preferencesModel.windowsNoBomNative && WINDOWS //&& !windowsCodePagesHelper.error + toolTip: qsTr("See documentation for more information ") + values: WINDOWS ? windowsCodePagesHelper.codePageIDs : [] + addEmptyValue: true + showEmptyValueAsNormal: true + addLineAfterEmptyValue: true + placeholderText: qsTr("Choose codepage here") + startValue: WINDOWS ? windowsCodePagesHelper.codePageID : "" + onValueChanged: if(WINDOWS) windowsCodePagesHelper.codePageID = value + + KeyNavigation.tab: useDefaultEditor } } } diff --git a/Desktop/data/columnmodel.cpp b/Desktop/data/columnmodel.cpp index 6f5ffbabed..57937a363f 100644 --- a/Desktop/data/columnmodel.cpp +++ b/Desktop/data/columnmodel.cpp @@ -487,6 +487,7 @@ void ColumnModel::columnDataTypeChanged(const QString & colName) if(colIndex == chosenColumn()) { + emit columnTypeChanged(); // if(DataSetPackage::pkg()->dataSet()->column(colIndex)->type() == columnType::scale) // setChosenColumn(-1); emit tabsChanged(); diff --git a/Desktop/data/columnsmodel.cpp b/Desktop/data/columnsmodel.cpp index 3102328a31..39a74e350f 100644 --- a/Desktop/data/columnsmodel.cpp +++ b/Desktop/data/columnsmodel.cpp @@ -13,7 +13,7 @@ ColumnsModel::ColumnsModel(DataSetTableModel *tableModel) setSourceModel(tableModel); - connect(_tableModel, &DataSetTableModel::columnTypeChanged, this, &ColumnsModel::columnTypeChanged ); + connect(_tableModel, &DataSetTableModel::columnTypeChanged, this, [&](QString col, int) { emit columnTypeChanged(col); }); connect(_tableModel, &DataSetTableModel::labelChanged, this, [&](QString col, QString orgLabel, QString newLabel) { emit labelsChanged(col, {std::make_pair(orgLabel, newLabel) }); } ); connect(_tableModel, &DataSetTableModel::labelsReordered, this, &ColumnsModel::labelsReordered ); diff --git a/Desktop/data/datasetpackage.cpp b/Desktop/data/datasetpackage.cpp index 5e0fc2690b..f618ad024a 100644 --- a/Desktop/data/datasetpackage.cpp +++ b/Desktop/data/datasetpackage.cpp @@ -734,7 +734,7 @@ bool DataSetPackage::setData(const QModelIndex &index, const QVariant &value, in if(value.toInt() >= int(columnType::unknown) && value.toInt() <= int(columnType::scale)) { columnType converted = static_cast(value.toInt()); - if(setColumnType(index.column(), converted)) + if(converted != column->type() && setColumnType(index.column(), converted)) { aChange = true; emit columnDataTypeChanged(tq(column->name())); @@ -1129,16 +1129,17 @@ bool DataSetPackage::setColumnType(int columnIndex, columnType newColumnType) if (_dataSet == nullptr) return true; + Column *col = _dataSet->column(columnIndex); + + if (col->type() == newColumnType) + return true; + columnTypeChangeResult feedback; - feedback = _dataSet->column(columnIndex)->changeType(newColumnType); + feedback = col->changeType(newColumnType); if (feedback == columnTypeChangeResult::changed) //Everything went splendidly - { - beginResetModel(); - endResetModel(); emit columnDataTypeChanged(tq(_dataSet->column(columnIndex)->name())); - } else { QString informUser = tr("Something went wrong converting columntype, but it is unclear what."); @@ -1262,6 +1263,19 @@ void DataSetPackage::dbDelete() _dataSet->dbDelete(); } +void DataSetPackage::resetVariableTypes() +{ + for (int i = 0; i < _dataSet->columnCount(); i++) + { + Column* col = _dataSet->column(i); + columnType orgType = col->type(); + stringvec values = getColumnDataStrs(i); + initColumnWithStrings(i, col->name(), values); + if (col->type() != orgType) + emit columnDataTypeChanged(tq(col->name())); + } +} + void DataSetPackage::createDataSet() { JASPTIMER_SCOPE(DataSetPackage::createDataSet); @@ -1437,8 +1451,7 @@ void DataSetPackage::initColumnWithStrings(QVariant colId, const std::string & n int colIndex = getColIndex(colId); //If less unique integers than the thresholdScale then we think it must be ordinal: https://github.com/jasp-stats/INTERNAL-jasp/issues/270 - bool useCustomThreshold = Settings::value(Settings::USE_CUSTOM_THRESHOLD_SCALE).toBool(); - size_t thresholdScale = (useCustomThreshold ? Settings::value(Settings::THRESHOLD_SCALE) : Settings::defaultValue(Settings::THRESHOLD_SCALE)).toUInt(); + size_t thresholdScale = Settings::value(Settings::THRESHOLD_SCALE).toUInt(); JASPTIMER_RESUME(DataSetPackage::initColumnWithStrings preamble); bool valuesAreIntegers = convertVecToInt(colIndex, values, intValues, uniqueValues, emptyValuesMap); @@ -1904,6 +1917,12 @@ columnType DataSetPackage::getColumnType(size_t columnIndex) const return _dataSet && _dataSet->column(columnIndex) ? _dataSet->column(columnIndex)->type() : columnType::unknown; } +columnType DataSetPackage::getColumnType(const QString& name) const +{ + return _dataSet ? getColumnType(_dataSet->getColumnIndex(fq(name))) : columnType::unknown; +} + + std::string DataSetPackage::getColumnName(size_t columnIndex) const { return _dataSet && _dataSet->column(columnIndex) ? _dataSet->column(columnIndex)->name() : ""; diff --git a/Desktop/data/datasetpackage.h b/Desktop/data/datasetpackage.h index f12e93e248..d77e354894 100644 --- a/Desktop/data/datasetpackage.h +++ b/Desktop/data/datasetpackage.h @@ -242,6 +242,7 @@ class DataSetPackage : public QAbstractItemModel //Not QAbstractTableModel becau int getColumnIndex( const QString & name) const { return getColumnIndex(name.toStdString()); } Column* getColumn( const std::string & name) { return _dataSet->column(name); } enum columnType getColumnType( size_t columnIndex) const; + enum columnType getColumnType( const QString & name) const; std::string getColumnName( size_t columnIndex) const; intvec getColumnDataInts( size_t columnIndex); doublevec getColumnDataDbls( size_t columnIndex); @@ -285,6 +286,7 @@ class DataSetPackage : public QAbstractItemModel //Not QAbstractTableModel becau stringset columnsCreatedByAnalysis( Analysis * analysis); std::string freeNewColumnName(size_t startHere); void dbDelete(); + void resetVariableTypes(); signals: diff --git a/Desktop/data/datasettablemodel.cpp b/Desktop/data/datasettablemodel.cpp index 94f4a5edb6..cecd5be289 100644 --- a/Desktop/data/datasettablemodel.cpp +++ b/Desktop/data/datasettablemodel.cpp @@ -25,7 +25,7 @@ DataSetTableModel::DataSetTableModel(bool showInactive) { connect(DataSetPackage::pkg(), &DataSetPackage::columnsFilteredCountChanged, this, &DataSetTableModel::columnsFilteredCountChanged ); - connect(DataSetPackage::pkg(), &DataSetPackage::columnDataTypeChanged, this, &DataSetTableModel::columnTypeChanged ); + connect(DataSetPackage::pkg(), &DataSetPackage::columnDataTypeChanged, this, [&](QString colName) { emit columnTypeChanged(colName, int(DataSetPackage::pkg()->getColumnType(colName))); }); connect(DataSetPackage::pkg(), &DataSetPackage::labelChanged, this, &DataSetTableModel::labelChanged ); connect(DataSetPackage::pkg(), &DataSetPackage::labelsReordered, this, &DataSetTableModel::labelsReordered ); //connect(this, &DataSetTableModel::dataChanged, this, &DataSetTableModel::onDataChanged, Qt::QueuedConnection); diff --git a/Desktop/data/datasettablemodel.h b/Desktop/data/datasettablemodel.h index 612ace8bdc..3fbf0702e6 100644 --- a/Desktop/data/datasettablemodel.h +++ b/Desktop/data/datasettablemodel.h @@ -54,7 +54,7 @@ class DataSetTableModel : public DataSetTableProxy signals: void columnsFilteredCountChanged(); void showInactiveChanged(bool showInactive); - void columnTypeChanged(QString colName); + void columnTypeChanged(QString colName, int colType); void labelChanged(QString columnName, QString originalLabel, QString newLabel); void labelsReordered(QString columnName); diff --git a/Desktop/gui/preferencesmodel.cpp.in b/Desktop/gui/preferencesmodel.cpp.in index ae86b44648..8b934a142f 100644 --- a/Desktop/gui/preferencesmodel.cpp.in +++ b/Desktop/gui/preferencesmodel.cpp.in @@ -98,7 +98,6 @@ GET_PREF_FUNC_INT( customPPI, Settings::PPI_CUSTOM_VALUE ) GET_PREF_FUNC_WHT( whiteBackground, Settings::IMAGE_BACKGROUND ) GET_PREF_FUNC_STR( plotBackground, Settings::IMAGE_BACKGROUND ) GET_PREF_FUNC_BOOL( developerMode, Settings::DEVELOPER_MODE ) -GET_PREF_FUNC_BOOL( customThresholdScale, Settings::USE_CUSTOM_THRESHOLD_SCALE ) GET_PREF_FUNC_INT( thresholdScale, Settings::THRESHOLD_SCALE ) GET_PREF_FUNC_BOOL( logToFile, Settings::LOG_TO_FILE ) GET_PREF_FUNC_INT( logFilesMax, Settings::LOG_FILES_MAX ) @@ -407,15 +406,6 @@ void PreferencesModel::resetEmptyValues() _setEmptyValues(defaultValues); } -void PreferencesModel::setCustomThresholdScale(bool newCustomThresholdScale) -{ - if (customThresholdScale() == newCustomThresholdScale) - return; - - Settings::setValue(Settings::USE_CUSTOM_THRESHOLD_SCALE, newCustomThresholdScale); - emit customThresholdScaleChanged (newCustomThresholdScale); -} - void PreferencesModel::setThresholdScale(int newThresholdScale) { if (thresholdScale() == newThresholdScale) diff --git a/Desktop/gui/preferencesmodel.h b/Desktop/gui/preferencesmodel.h index 0278c9a5fb..3d9a19d251 100644 --- a/Desktop/gui/preferencesmodel.h +++ b/Desktop/gui/preferencesmodel.h @@ -3,7 +3,6 @@ #include #include -#include "utilities/qutils.h" #include "preferencesmodelbase.h" class JaspTheme; @@ -28,7 +27,6 @@ class PreferencesModel : public PreferencesModelBase Q_PROPERTY(int defaultPPI READ defaultPPI WRITE setDefaultPPI NOTIFY defaultPPIChanged ) Q_PROPERTY(bool developerMode READ developerMode WRITE setDeveloperMode NOTIFY developerModeChanged ) Q_PROPERTY(QString developerFolder READ developerFolder WRITE setDeveloperFolder NOTIFY developerFolderChanged ) - Q_PROPERTY(bool customThresholdScale READ customThresholdScale WRITE setCustomThresholdScale NOTIFY customThresholdScaleChanged ) Q_PROPERTY(int thresholdScale READ thresholdScale WRITE setThresholdScale NOTIFY thresholdScaleChanged ) Q_PROPERTY(bool logToFile READ logToFile WRITE setLogToFile NOTIFY logToFileChanged ) Q_PROPERTY(int logFilesMax READ logFilesMax WRITE setLogFilesMax NOTIFY logFilesMaxChanged ) @@ -91,7 +89,6 @@ class PreferencesModel : public PreferencesModelBase QString developerFolder() const; QString fixedDecimalsForJS() const; QStringList emptyValues() const; - bool customThresholdScale() const; int thresholdScale() const; bool logToFile() const; int logFilesMax() const; @@ -155,7 +152,6 @@ public slots: void removeEmptyValue( QString value); void addEmptyValue( QString value); void resetEmptyValues(); - void setCustomThresholdScale( bool customThresholdScale); void setThresholdScale( int thresholdScale); void setLogToFile( bool logToFile); void setLogFilesMax( int logFilesMax); @@ -209,7 +205,6 @@ public slots: void developerFolderChanged( QString developerFolder); void plotPPIChanged( int ppiForPlot, bool wasUserAction); void plotBackgroundChanged( QString plotBackground); - void customThresholdScaleChanged( bool customThresholdScale); void thresholdScaleChanged( int thresholdScale); void logToFileChanged( bool logToFile); void logFilesMaxChanged( int logFilesMax); diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index da102b8ab4..88ae5c250e 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -2112,3 +2112,8 @@ void MainWindow::setDefaultWorkspaceEmptyValues() { DataSetPackage::pkg()->setDefaultWorkspaceEmptyValues(); } + +void MainWindow::resetVariableTypes() +{ + DataSetPackage::pkg()->resetVariableTypes(); +} diff --git a/Desktop/mainwindow.h b/Desktop/mainwindow.h index 0962bba6be..82b6600a4c 100644 --- a/Desktop/mainwindow.h +++ b/Desktop/mainwindow.h @@ -137,6 +137,7 @@ public slots: void setContactVisible(bool newContactVisible); void setCommunityVisible(bool newCommunityVisible); void setDefaultWorkspaceEmptyValues(); + void resetVariableTypes(); void showRCommander(); diff --git a/Desktop/utilities/settings.cpp b/Desktop/utilities/settings.cpp index 4aa4bd1438..2d41112bfc 100644 --- a/Desktop/utilities/settings.cpp +++ b/Desktop/utilities/settings.cpp @@ -27,7 +27,6 @@ const Settings::Setting Settings::Values[] = { {"fixedDecimals", false}, {"developerMode", false}, {"developerFolder", ""}, - {"CustomThresholdScale", false}, {"ThresholdScale", 10}, {"logToFile", false}, //By default do not log to file and when running debug-mode log to stdout and in release to nowhere. {"logFilesMax", 15}, diff --git a/Desktop/utilities/settings.h b/Desktop/utilities/settings.h index 9f7f62a6a1..4bf3d5fa01 100644 --- a/Desktop/utilities/settings.h +++ b/Desktop/utilities/settings.h @@ -32,7 +32,6 @@ class Settings { FIXED_DECIMALS, DEVELOPER_MODE, DEVELOPER_FOLDER, - USE_CUSTOM_THRESHOLD_SCALE, THRESHOLD_SCALE, LOG_TO_FILE, LOG_FILES_MAX,