Qt Undo/Redo implementation, can’t connect to a button - c++

I post the question also in the Qt Forum, here
I am trying to implement an undo and redo commands in my application. I have a QTreeWidget and I’d like to let the user undo and redo an action (ex. change a value in a QTreeWidgetItem columns in the QTreeWidget and undo/redo it).
Here part of my code:
class A.h
class A : public QWidget
{
Q_OBJECT
public:
explicit A(...);
~A();
ChangeValueTreeCommand *commands;
QUndoStack *undoStack;
QPushButton *undoBtn;
QPushButton *redoBtn;
QString newValue;
void changeItem(QTreeWidgetItem* treeWidgetItemChanged, int col);
};
class A.cpp
A::A(...){
undoStack = new QUndoStack(this);
}
void A::changeItem(QTreeWidgetItem* treeWidgetItemChanged, int col){
....
commands = new ChangeValueTreeCommand(treeWidgetItemChanged, col, newValue);
connect(undoBtn, SIGNAL(clicked()), commands, SLOT(undo()));
undoStack->push(commands);
}
class Commands.h
#ifndef COMMANDS_H
#define COMMANDS_H
#include <QApplication>
#include <QUndoCommand>
#include <QTreeWidgetItem>
class ChangeValueTreeCommand : public QUndoCommand
{
public:
explicit ChangeValueTreeCommand(QTreeWidgetItem* treeWI = NULL, int c = 0, const QString changedV = "");
~ChangeValueTreeCommand();
QTreeWidgetItem* treeWItem;
const QString changedValue;
int col;
public slots:
void undo();
void redo();
};
#endif // COMMANDS_H
class Commands.cpp
#include "Commands.h"
ChangeValueTreeCommand::ChangeValueTreeCommand(QTreeWidgetItem* treeWI, int c, const QString changedV)
: treeWItem(treeWI), col(c), changedValue(changedV)
{}
ChangeValueTreeCommand::~ChangeValueTreeCommand(){}
void ChangeValueTreeCommand::undo()
{
const QString oldValue = treeWItem->text(col);
treeWItem->setText(col, oldValue);
}
void ChangeValueTreeCommand::redo()
{
treeWItem->setText(col, changedValue);
}
The problem is that when the user changes the value in the QTreeWidgetItem, it automatically appears the previous value. Moreover, I would like to connect the undo and redo functions to two buttons, but the compiler says that
1543: error: C2664: ‘QMetaObject::Connection QObject::connect(const QObject *,const char *,const QObject *,const char *,Qt::ConnectionType)‘ÿ: cannot convert parameter 3 from ‘ChangeValueTreeCommand *’ to ‘const QObject *’
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Can someone help me? Thx

Your undo and redo Buttons should call undoStack->undo() / undoStack->redo(). This will move the stack pointer and will call the undo/redo function of the current command.
See the Qt Documentation for a detailed Explanation: http://qt-project.org/doc/qt-4.8/qundostack.html#undo
Escpecially this part:
New commands are pushed on the stack using push(). Commands can be
undone and redone using undo() and redo(), or by triggering the
actions returned by createUndoAction() and createRedoAction().
QUndoStack keeps track of the current command. This is the command
which will be executed by the next call to redo(). The index of this
command is returned by index(). The state of the edited object can be
rolled forward or back using setIndex(). If the top-most command on
the stack has already been redone, index() is equal to count().

Related

Qt's slot(?) inserts redundant namespace into connect

The problem I get is the following error message when calling connect, with MRC being the redundant namespace which I think Qt shouldn't have added - boundSubWindow isn't defined inside MRC. My understanding is the slot function isn't found, because MRC:: was prepended to it's name.
QObject::connect: No such slot MRC::boundSubWindow::myFunc(unsigned char *, int) in z:\mrc\mrc\mrc.h:23
QObject::connect: (receiver name: 'MRCClass')
The code is
---------- mrc.h ----------
#include <QtWidgets/QMainWindow>
#include "ui_MRC.h"
#include "myThread.h"
#include "boundsubwindow.h"
class MRC : public QMainWindow
{
Q_OBJECT
public:
MRC(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
ui.setupUi(this);
m_Thread = new myThread;
m_Subwindow = new boundSubWindow;
connect(MRC::m_Thread, SIGNAL(mySignal(char *, int)),
this, SLOT(boundSubWindow::myFunc(unsigned char *, int)));
}
static inline myThread *m_Thread;
boundSubWindow *m_Subwindow;
private:
Ui::MRCClass ui;
};
---------- boundsubwindow.h ----------
#include <QMdiSubWindow>
class boundSubWindow : public QMdiSubWindow
{
public:
boundSubWindow() {}
public slots:
void myFunc(unsigned char *, int);
};
---------- boundsubwindow.h ----------
void boundSubWindow::myFunc(unsigned char *, int) {}
---------- myThread.h ----------
#include <QThread>
class myThread : public QThread
{
Q_OBJECT
public:
myThread() {}
signals:
void mySignal(char *, int);
};
I've skipped an auto generated main.cpp for Qt GUI application, and the MRC.ui - auto generated then added a QMdiArea with Qt Creator. I'm using Visual Studio 2017 and Qt 5.13
This is wrong:
connect(MRC::m_Thread, SIGNAL(mySignal(char *, int)),
this, SLOT(boundSubWindow::myFunc(unsigned char *, int)));
boundSubWindow::myFunc is not slot of this. Also, specifying the namespace for m_Thread member variable seems quite redundant. You probably want this:
connect(m_Thread, SIGNAL(mySignal(char *, int)),
m_subWindow, SLOT(myFunc(unsigned char *, int)));
Additionally, add Q_OBJECT macro to boundSubWindw class and re-run qmake explicitly (you have to do that when adding these Qt macros to files which didn't previously have them).
Also, you should use the new connect syntax so you get compile time errors instead of run time errors:
connect(m_Thread, &myThread::mySignal,
m_subWindow, &boundSubWindow::myFunc);
Finally, it's common Qt convention to have class names start with capital, so consider renaming to BoundSubWindow and MyThread, if you want other Qt programmers to be able to easily read your code.

Cannot convert const object in Return

I'm not entirely sure what I'm doing incorrectly here. I have a class which contains a constant pointer to another class object. However I'm getting an error about not being able to convert the const (class object). What am I doing wrong? Is my code setup incorrect in the what I'm trying to do?
Error message: cannot convert 'const AppProfile' to 'AppProfile*' in return
I initially had this in my header file class AppProfile and i changed it to #include "appprofile.h" which helped remove another error.
I later will call run() which executes run on my AppProfile object.
header file
#ifndef APPITEM_H
#define APPITEM_H
#include <QObject>
#include <QUrl>
#include <QDir>
#include "appprofile.h"
class AppItem : public QObject
{
Q_OBJECT
public:
explicit AppItem(QObject *parent = nullptr);
explicit AppItem(const AppProfile &profile,
QObject *parent);
/// App Profile
AppProfile *profile() const;
signals:
public slots:
void run();
private:
const AppProfile m_profile;
};
#endif // APPITEM_H
cpp file
#include "appitem.h"
#include "appprofile.h"
AppItem::AppItem(QObject *parent) :
QObject(parent)
{
}
AppItem::AppItem(const AppProfile &profile,
QObject *parent) :
QObject(parent),
m_profile(profile)
{
}
QString AppItem::name() const
{
return m_name;
}
void AppItem::run()
{
AppProfile *profile = profile();
profile->run();
}
AppProfile *AppItem::profile() const
{
return m_profile;
}
UPDATE:
Follow up question reguarding the answer givens given...
To simply explain my intentions, I'm parsing a json file that contains data used to create the parent object AppItem. When this item is constructed, it takes in it's construct an AppProfile object. This object is only ever created once, at the time in which AppItem is created.
Knowing that, how would you suggest i move forward editing the original questions code relating to AppProfile. Assuming that's enough information. I appreciate you help. This is what the code looks like that I would use to create an AppItem
AppProfile *profile = new AppProfile();
AppItem *appItem = new AppItem(profile);
For starters either there is a typo in your code or the function is defined incorrectly
AppProfile *AppItem::profile() const
{
return m_profile;
}
Within the class the data member m_profile is not a pointer.
//...
private:
const AppProfile m_profile;
};
So if the declaration of the data member is valid then the function should look like
const AppProfile *AppItem::profile() const
{
return &m_profile;
}
Or if the data member declaration should look like
//...
private:
const AppProfile *m_profile;
};
then in any case the function shall return a pointer to constant data.
const AppProfile *AppItem::profile() const
{
return m_profile;
}
That is the error message implicitly says that there is a typo in your code
cannot convert 'const AppProfile' to 'AppProfile*' in return
And if you will update the typo in any case you may not discard the qualifier const for the pointer.

Why does my attempt at connecting a pushbutton to a lambda fail?

I have some issues while trying to use lambda expression to make connections between a pushbutton and a function I want to call when I click the button.
I am using Qt 5.6, with the compiler MinGW 4.9.2 (the default one). My code is the following :
In mainwindow.cpp :
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
initBuildings();
initPage();
for (int i(0); i<buildings.size(); ++i) {
connect(static_cast<QAbstractButton*>(widgetlist.at(i).at(2)), &QAbstractButton::clicked, [this, i]() {
buildings.at(i).buy(amountMultiplier);});
}
}
void MainWindow::initBuildings()
{
Building b1 = Building("Building 1",100,1,200);
Building b2 = Building("Building 2",1000,10,2000);
buildings.append(b1);
buildings.append(b2);
}
void MainWindow::initPage()
{
for (int i(0); i<buildings.size(); i++) {
QList<QWidget *> buttons;
QLabel *namelabel = new QLabel(buildings.at(i).getName());
QLabel *amountlabel = new QLabel;
QPushButton *buybutton = new QPushButton(this);
QPushButton *upgradebutton = new QPushButton(this);
amountlabel->setFixedSize(50,40);
buybutton->setFixedSize(100,40);
upgradebutton->setFixedSize(100,40);
buttons.append(namelabel);
buttons.append(amountlabel);
buttons.append(buybutton);
buttons.append(upgradebutton);
widgetlist.append(buttons);
}
}
In mainwindow.h :
#include <QMainWindow>
#include <QScrollArea>
#include <QList>
#include <building.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void initBuildings();
void initPage();
Ui::MainWindow *ui;
int amountMultiplier;
QList<Building> buildings;
QList<QList<QWidget*>> widgetlist;
};
And "Building" is a class I have created which does not inherit from another class. The function I want to use is a public funtion of this class:
void buy(int amount) const;
It doesn't compile and I get several errors :
no matching function for call to 'MainWindow::connect(QAbstractButton*, void (QAbstractButton::*)(bool), MainWindow::MainWindow(QWidget*)::<lambda()>)
invalid use of incomplete type 'struct QtPrivate::QEnableIf< false, QMetaObject::Connection>
cannot convert '<lambda closure object>MainWindow::MainWindow(QWidget*)::< lambda()>{((MainWindow*)this), i}' (type 'MainWindow::MainWindow(QWidget*)::< lambda()>') to type 'const QObject*
I tried to change the lambda capture list, or to change the way I get the value in the lists but it doesn't change anything and I don't figure what is the problem. Maybe I am wrong in the use of the lambda ?
Two problems:
buildings.at() returns a const Building &, and the buy method is not const. You must index the buildings using [] instead.
The type returned from widgetlist.at(i).at(2) is definitely not QPushButton* - if it was, the code would compile. Even the error message indicates what the issue is:
no matching function for call to 'MainWindow::connect(QWidget* const&, void (QAbstractButton::*)(bool), [...])
This compiles:
// https://github.com/KubaO/stackoverflown/tree/master/questions/lambda-list-37615204
#include <QtWidgets>
struct Building {
void buy() {}
};
class Class : public QObject {
QList<Building> m_buildings;
QList<QList<QWidget*>> m_widgets;
public:
Class() {
for (int i = 0; i<m_buildings.size(); ++i)
connect(static_cast<QAbstractButton*>(m_widgets.at(i).at(2)), &QAbstractButton::clicked, [=] {
m_buildings[i].buy();
});
}
};
int main() {}
If you wish an extra measure of safety in face of programming errors on your part, replace the static_cast with a qobject_cast, it'll then abort if you cast a non-button instead of doing something possibly misleading.
According to the documentation, your lambda should accept a bool argument.

Compiler errors on setter and getter functions in QT

In the midst of making a test program I've come across a problem that has me boggled. I have googled and looked on here and haven't found an answer to the problem so thought I might just ask.
The issue: I get compiler errors on a getter and a setter function I made in QT. Basically I need to be able to set and get a QList object.
What am I overlooking? I think I might be doing something wrong with QList but I can't see what.
SliderArray.h
#ifndef SLIDERARRAY_H
#define SLIDERARRAY_H
#include <QWidget>
#include <QList>
#include <QSlider>
class SliderArray : public QWidget
{
Q_OBJECT
public:
explicit SliderArray(int sliders, QWidget *parent = 0);
~SliderArray();
//getter function for slider data
QList<int> GetSliderData();
//setter function for slider data
void SetSliderData(QList<int>);
private:
QList<int> integerList1; //Qlist Object to hold data
SliderArray.cpp
SliderArray::GetSliderData()
{
return integerList1;
}
SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
these are the errors I get when I try to compile:
...\sliderarray.cpp:24: error: prototype for 'intSliderArray::GetSliderData()' does not match any in class 'SliderArray'
SliderArray::GetSliderData()
^
...\sliderarray.h:16: error: candidate is: QList<int> SliderArray::GetSliderData()
QList<int> GetSliderData();
^
...\sliderarray.cpp:29: error: prototype for 'int SliderArray::SetSliderData(QList<int>)' does not match any in class 'SliderArray'
SliderArray::SetSliderData(QList<int> datalist)
^
...\sliderarray.h:19: error: candidate is: void SliderArray::SetSliderData(QList<int>)
void SetSliderData(QList<int>);
^
In your cpp file you should have:
QList<int> SliderArray::GetSliderData()
{
return integerList1;
}
void SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
This has nothing to do with Qt, you're just missing the return types.

How to pass QList from QML to C++/Qt?

I'm trying to pass QList of integer from QML to C++ code, but somehow my approach is not working. With below approach am getting following error:
left of '->setParentItem' must point to class/struct/union/generic type
type is 'int *'
Any inputs to trouble shoot the issue is highly appreciated
Below is my code snippet
Header file
Q_PROPERTY(QDeclarativeListProperty<int> enableKey READ enableKey)
QDeclarativeListProperty<int> enableKey(); //function declaration
QList<int> m_enableKeys;
cpp file
QDeclarativeListProperty<int> KeyboardContainer::enableKey()
{
return QDeclarativeListProperty<int>(this, 0, &KeyboardContainer::append_list);
}
void KeyboardContainer::append_list(QDeclarativeListProperty<int> *list, int *key)
{
int *ptrKey = qobject_cast<int *>(list->object);
if (ptrKey) {
key->setParentItem(ptrKey);
ptrKey->m_enableKeys.append(key);
}
}
You CAN'T use QDeclarativeListProperty (or QQmlListProperty in Qt5) with any other type than QObject derived ones. So int or QString will NEVER work.
If you need to exchange a QStringList or a QList or anything that is an array of one of the basic types supported by QML, the easiest way to do it is to use QVariant on the C++ side, like this :
#include <QObject>
#include <QList>
#include <QVariant>
class KeyboardContainer : public QObject {
Q_OBJECT
Q_PROPERTY(QVariant enableKey READ enableKey
WRITE setEnableKey
NOTIFY enableKeyChanged)
public:
// Your getter method must match the same return type :
QVariant enableKey() const {
return QVariant::fromValue(m_enableKey);
}
public slots:
// Your setter must put back the data from the QVariant to the QList<int>
void setEnableKey (QVariant arg) {
m_enableKey.clear();
foreach (QVariant item, arg.toList()) {
bool ok = false;
int key = item.toInt(&ok);
if (ok) {
m_enableKey.append(key);
}
}
emit enableKeyChanged ();
}
signals:
// you must have a signal named <property>Changed
void enableKeyChanged();
private:
// the private member can be QList<int> for convenience
QList<int> m_enableKey;
};
On the QML side, simply affect a JS array of Number, the QML engine will automatically convert it to QVariant to make it comprehensible to Qt :
KeyboardContainer.enableKeys = [12,48,26,49,10,3];
That's all !