Undo stack remains empty in Qt - c++

I am trying to undo/redo in multi document interface. I have different entities. Each entity has its own class. I have used UndoGroup but when I unable to push them to undoStack dont know whats's wrong there. Can anyone help me to solve the issue.
cadgraphicscene.cpp
CadGraphicsView::CadGraphicsView()
{
undoStack = new QUndoStack(this);
}
QUndoStack *CadGraphicsView::m_undoStack() const
{
return undoStack;
}
void CadGraphicsView::showUndoStack()
{
undoView = 0;
// shows the undoStack window
if (undoView == 0)
{
undoView = new QUndoView(undoStack);
undoView->setWindowTitle("Undo Stack");
}
undoView->show();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_undoGroup = new QUndoGroup(this);
QAction *undoAction = m_undoGroup->createUndoAction(this);
undoAction->setShortcut(QKeySequence::Undo);
QAction *redoAction = m_undoGroup->createRedoAction(this);
redoAction->setShortcut(QKeySequence::Redo);
menuEdit->insertAction(menuEdit->actions().at(1), undoAction);
menuEdit->insertAction(undoAction, redoAction);
menuEdit->addAction(undoAction);
menuEdit->addAction(redoAction);
undoAction->setEnabled(true);
redoAction->setEnabled(true);
}
void MainWindow::updateActions()
{
CadGraphicsView *view = currentDocument();
m_undoGroup->setActiveStack(view == 0 ? 0 : view->m_undoStack());
}
void MainWindow::addDocument(CadGraphicsView *view)
{
m_undoGroup->addStack(view->m_undoStack());
connect(view->m_undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
connect(view->m_undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
setCurrentDocument(view);
}
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
}
I am confused with why I am not able to push entities to undoStack. Please help me to solve this issue

I think the problem is in these two functions (see the inline comments):
void MainWindow::setCurrentDocument(CadGraphicsView *view)
{
// The view argument is not used at all. You do not set anything here.
mdiArea->currentSubWindow();
}
CadGraphicsView *MainWindow::currentDocument() const
{
// mdiArea->parentWidget() returns the MainWindow, so this function
// always returns 0.
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
// You should write it as
// return qobject_cast<CadGraphicsView *>(mdiArea->activeSubWindow()->widget());
}

Related

QObject::connect: signal not found in promoted widget with Lambda syntax

I have an overloaded QTreeWidget class, with my SIGNALS: I have promoted it in my UI and when I listen promoted QTreeWidget object with a lambda syntax I have an error.
QObject::connect: signal not found in CustomTreeWidget.
MY CustomTreeWidget looks like:
.h
class CustomTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit CustomTreeWidget(QWidget *parent = 0);
~CustomTreeWidget() {
}
signals:
void currentNodeChanged(QSet<int> uids);
void deleteRequest(QVector<int> uids);
}
.cpp
CustomTreeWidget::CustomTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
setAnimated(true);
connect(this, &CustomTreeWidget::customContextMenuRequested, this, [=](const QPoint &pos) {
this->m_bCustomMenuOpen = true;
const auto &&item = this->itemAt(pos);
QMenu myMenu;
bool ok = !(item) ? false : true;
if (ok) {
//თუ topLevelItem -ია მხოლოდ დამატების action -ი უნდა იყოს ჩართული.
if (item == this->topLevelItem(0) || item == this->topLevelItem(0)->child(0)) {
ok = false;
}
}
QAction *Removecnt = myMenu.addAction(tr("&წაშლა"), this, SLOT(DeleteNode()));
Removecnt->setIcon(QIcon(":/global_res/delete.png"));
Removecnt->setEnabled(ok);
myMenu.exec(this->mapToGlobal(pos));
});
}
void CustomTreeWidget::BFS(QTreeWidgetItem *item, QSet<int> &out)
{
std::queue<QTreeWidgetItem *> Q;
Q.push(item);
while (!Q.empty()) {
QTreeWidgetItem *now = Q.front(); Q.pop();
out.insert(this->m_mapUids[now]);
for (int i = 0; i < now->childCount(); i++) {
Q.push(now->child(i));
}
}
}
QSet<int> CustomTreeWidget::GetCurrentNodeUids()
{
QSet<int> uids;
if (!this->currentItem())
return uids;
this->BFS(this->currentItem(), uids);
return uids;
}
void CustomTreeWidget::DeleteNode()
{
QSet<int> nodes = this->GetCurrentNodeUids();
QVector<int> uids;
for (auto it : nodes) {
uids.push_back(it);
}
emit deleteRequest(uids);
}
My lambda looks like:
connect(ui->productTree, &CustomTreeWidget::deleteRequest, this, [=](QVector<int> uids) {
//logic
});
But this signal works with old syntax.
connect(ui->productTree, SIGNAL(deleteRequest(QVector<int>)), this, SLOT(checkSlot(QVector<int>)));
And this slot is.
void ProductForm::checkSlot(QVector<int> uids)
{
qDebug() << uids.size();
}
So what is problem lambda syntax?
This smells like the violation of the one definition rule (ODR) - perhaps due to a stale build folder. The problem is that the address of deleteRequest passed to the connect method is not the same as the address of the method visible from moc_CustomTreeWidget.cpp. Remove the build folder and try again. If it still doesn't work, start reducing your problem:
Create a minimization branch in the repository (if you're not using version control, you won't go anywhere with minimization).
Copy-paste the contents of the ui_CustomTreeWidget.h file into the CustomTreeWidget.cpp file, remove the #include line.
Inspect the pasted contents for instantiation of the productTree object with the correct type.
If that's correct, then remove everything else from the code, step-by-step, rebuilding and committing to the repository at each step that still reproduces. You should end up with a test case that is 20-30 lines long at most. And it'll be either obvious what's wrong, or you can modify the question with the test case.

Checkbox change other checkbox statement

I need to connect some Checkboxes, so when I click one it becomes checked and other become unchecked. My code right now looks like this.
Connect in class constructor:
connect(cb_thickness1,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness1_isChecked()));
connect(cb_thickness2,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness2_isChecked()));
connect(cb_thickness3,SIGNAL(stateChanged(int)),this,SLOT(cb_thickness3_isChecked()));
and slots
void MainWind::cb_thickness1_isChecked()
{
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness3->isChecked())
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
if(cb_thickness1->isChecked())
cb_thickness1->setChecked(false);
if(cb_thickness2->isChecked())
cb_thickness2->setChecked(false);
}
Code doesn't work as expected. When I click to any ChBx first time, everything is OK, but when I click to other next time it only uncheck previous and does nothing with itself. Only on second click it become chekced.
Also I found one more bug, when I check to ChBox, and then uncheck it by clicking it againg, I can check 2 ChBxes. [pic 2]
Radio button is a great idea.
But if you really want to use check box, you can explicitly set cb_thickness1 checked in cb_thickness1_isChecked(), and do the same for other two check boxes.
void MainWind::cb_thickness1_isChecked()
{
cb_thickness1->setChecked(true);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness2_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(true);
cb_thickness3->setChecked(false);
}
void MainWind::cb_thickness3_isChecked()
{
cb_thickness1->setChecked(false);
cb_thickness2->setChecked(false);
cb_thickness3->setChecked(true);
}
I suggest to derive a class from QCheckBox (lets call it CustomCheckBox) and add a signal, private slot and public slot
signal:
void enabled();
private slot:
void CustomCheckBox::checkEnable(bool state)
{
if(state)
{
emit enabled();
}
}
public slot:
void CustomCheckBox::uncheck()
{
setChecked(false);
}
In the constructor add:
connect(this,SIGNAL(toggled(bool)),this,SLOT(checkEnable(bool)));
This way you can use simple connects.
CustomCheckBox *box1 = new CustomCheckBox();
CustomCheckBox *box2 = new CustomCheckBox();
connect(box1,SIGNAL(enabled()),box2,SLOT(uncheck()));
Feel free to improve this answer. :)
Verify that the state of the button is checked in the slot and then deactivate the other checkboxes like you already did. You can use the parameter of the stateChanged method by passing it to the slots.
Here is code that works:
Variant I:
connect(ui->checkBoxA, SIGNAL(stateChanged(int)), this, SLOT(checkBoxAChanged(int)));
connect(ui->checkBoxB, SIGNAL(stateChanged(int)), this, SLOT(checkBoxBChanged(int)));
connect(ui->checkBoxC, SIGNAL(stateChanged(int)), this, SLOT(checkBoxCChanged(int)));
void MainWindow::checkBoxAChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCChanged(int state)
{
if (state == Qt::Checked) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}
Variant II:
connect(ui->checkBoxA, SIGNAL(clicked(bool)), this, SLOT(checkBoxAClicked(bool)));
connect(ui->checkBoxB, SIGNAL(clicked(bool)), this, SLOT(checkBoxBClicked(bool)));
connect(ui->checkBoxC, SIGNAL(clicked(bool)), this, SLOT(checkBoxCClicked(bool)));
void MainWindow::checkBoxAClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxBClicked(bool val)
{
if (val == true) {
ui->checkBoxA->setChecked(false);
ui->checkBoxC->setChecked(false);
}
}
void MainWindow::checkBoxCClicked(bool val)
{
if (val == true) {
ui->checkBoxB->setChecked(false);
ui->checkBoxA->setChecked(false);
}
}

How do you change a QGraphicsScene from another function?

My program has a QGraphicsView called SimulatorGV and it has a QGraphicsScene called Simulation. In the main function I have drawn a few rectangles and ellipses. I want to be able to draw on the same scene in another function too.
In essence, how do you add shapes to a scene from another function? (Which is called when a button on the user interface is pressed)
So far I've tried to pass the QGraphicsView to the other function? Below is the main ui function (irrelevant statements cut out):
{
ui->setupUi(this);
Simulation = new QGraphicsScene(this);
ui->SimulatorGV->setScene(Simulation);
Simulation->addRect(0,415,20,50,noPen,greyBrush);
Simulation->addRect(425,230,10,420,noPen,goldBrush);
Simulation->addEllipse(80,90,700,700,greyPen,noBrush);
Simulation->addRect(72,215,90,450,noPen,blackBrush);
}
In the header file I declared this function in the private slots:
void DrawingSimulation(QGraphicsView *SimulationGV);
Should it be like this instead?
void DrawingSimulation(QGraphicsScene *Simulation);
I called the function in another function like this:
DrawingSimulation(ui->SimulatorGV);
Should it be like this instead?
DrawingSimulation(ui->SimulatorGV->Simulation);
or?
DrawingSimulation(ui->Simulation);
This is the function I want to be able to draw on the scene from:
void RASP::DrawingSimulation(QGraphicsView *SimulationGV)
{
for (int i = 0; i < DisplayAlphaParticleNumber; i++)
{
if (ParticleLocation[i*6+3] != 0 || ParticleLocation[i*6+6] != 0)
{
ui->SimulatorGV->setScene(Simulation);
Simulation->addEllipse(ParticleLocation[i*6+3],ParticleLocation[i*6+4],10,10);
}
}
}
SimulatorGV is the name of the QGraphicsView in my ui form. RASP is the name of the project. ParticleLocation[i*6+3] is the x coordinate and [i*6+4] is the y coordinate.
Was I right to pass the QGraphicsView onto the varibale instead of the QGraphicsScene Simulation?
Did I pass it correctly?
In the DrawingSimulation function did I add the ellipse correctly?
Edit:
In essence, how do you add shapes to a scene from another function? (Which is called when a button on the user interface is pressed)
When a button is pressed this function is called:
void RASP::on_Button1_clicked()
{
//some values set
//some other functions called then the main one that leads to the drawingsimulation
mainfunction();
}
Then inside the mainfunction():
void RASP::mainfunction()
{
DrawingSimulation(); //is called
}
Now the function DrawingSimulation() which I would like to draw on the original scene in the RASP::RASP() (MyClass::MyClass) is called.
My previous attempt was to have a boolean function that is set true by the button then the addEllipse:
MyClass::MyClass()
{
ui->setupUi(this);
Simulation = new QGraphicsScene(this);
ui->SimulatorGV->setScene(Simulation);
Simulation->addRect(0,415,20,50,noPen,greyBrush);
Simulation->addRect(425,230,10,420,noPen,goldBrush);
Simulation->addEllipse(80,90,700,700,greyPen,noBrush);
Simulation->addRect(72,215,90,450,noPen,blackBrush);
if (SimulationRun == true)
{
for (int i = 0; i < DisplayAlphaParticleNumber; i++)
{
if (ParticleLocation[i*6+3] != 0 || ParticleLocation[i*6+6] != 0)
{
ui->SimulatorGV->setScene(Simulation);
Simulation->addEllipse(ParticleLocation[i*6+3],ParticleLocation[i*6+4],10,10);
}
}
}
}
and then in the button clicked function setting SimulationRun = to true.
Let's keep it simple.
If you have:
MyClass::MyClass() : ui(new Ui::MainWindow)
{
ui->setupUi(this);
Simulation = new QGraphicsScene(this);
ui->SimulatorGV->setScene(Simulation);
Simulation->addRect(0,415,20,50,noPen,greyBrush);
Simulation->addRect(425,230,10,420,noPen,goldBrush);
Simulation->addEllipse(80,90,700,700,greyPen,noBrush);
Simulation->addRect(72,215,90,450,noPen,blackBrush);
}
If this works, then, this will work too:
MyClass::MyClass() : ui(new Ui::MainWindow)
{
ui->setupUi(this);
Simulation = new QGraphicsScene(this); // supposing MyClass has a Simulation attribute of type QGraphicsScene
ui->SimulatorGV->setScene(Simulation);
addItems(); // calling function below
}
void MyClass::addItems()
{
// declared "void addItems();" in header file
addItems( Simulation ); // calling function below that could be static
}
void MyClass::addItems( QGraphicsScene* simu )
{
// declared "static void addItems(QGraphicsScene* simu);" in header file
simu->addRect(0,415,20,50,noPen,greyBrush);
simu->addRect(425,230,10,420,noPen,goldBrush);
simu->addEllipse(80,90,700,700,greyPen,noBrush);
simu->addRect(72,215,90,450,noPen,blackBrush);
}
Then, if this works, you now know how to modify the secene from "another function".
Finally, you should also have:
void MyClass::DrawingSimulation()
{
// declared "void DrawingSimulation();" in header file
DrawingSimulation( Simulation );
}
void MyClass::DrawingSimulation(QGraphicsScene *simu)
{
// declared "void DrawingSimulation(QGraphicsScene *simu);" in header file
for (int i = 0; i < DisplayAlphaParticleNumber; i++)
{
if (ParticleLocation[i*6+3] != 0 || ParticleLocation[i*6+6] != 0)
{
simu->addEllipse(ParticleLocation[i*6+3],ParticleLocation[i*6+4],10,10);
}
}
}
Note that DrawingSimulation() could also be a slot (declare it using public slots: in your header file. Then, if you connect it to the clicked() signal of a QPushButton of your GUI (for instance), it will be called when the button is clicked and ellipse will be added.
Like this:
MyClass::MyClass()
{
...
connect( ui->pushButton, SIGNAL(clicked()), this, SLOT(DrawingSimulation()) );
}

How to check two QLineEdit If they are not empty

I have three controls, two QTextLine and one QPushButton.
When startup the program, the add button will be disabled, and must be two QTextLine not empty for enable the add button.
I have the following code, but it does not works fine:
void Question_Answer::on_newQuestion_txt_textChanged(const QString &arg1)
{
if(arg1.isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
void Question_Answer::on_newAnswer_txt_textChanged(const QString &arg1)
{
if(ui->newAnswer_txt->text().isEmpty())
{
ui->addNewQuestion_btn->setEnabled(false);
}
else
{
ui->addNewQuestion_btn->setEnabled(true);
}
}
Now, How to check if the two QTextLine are not empty, and also if any of them is empty, how to disable the add button.
Just connect a single slot to handle textChanged signals of both LineEdits
void Question_Answer::onTextChanged(const QString &arg1){
if(ui->newAnswer_txt->text().isEmpty() || ui->newQuestion_txt->text().isEmpty()){
ui->addNewQuestion_btn->setEnabled(false);
}else{
ui->addNewQuestion_btn->setEnabled(true);
}
}
In the header class:
// ...
private slots:
void onTextChanged();
// ...
In the source file:
// Setup the connections in the constructor.
void Question_Answer::onTextChanged()
{
const bool editable1 = ui->newAnswer_txt->text().size() > 0;
const bool editable2 = ui->newQuestion_txt->text().size() > 0;
ui->addNewQuestion_btn->setEnabled( editable1 && editable2 );
}

Error while trying Undo Redo

I am trying to do undo/redo on multidocument interface but facing following error:
no matching function for call to 'qobject_cast(QMdiSubWindow*&)'
return qobject_cast<CadGraphicsView *>(activeSubWindow);
My code to above function is as follows:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
m_undoGroup = new QUndoGroup(this);
QAction *undoAction = m_undoGroup->createUndoAction(this);
undoAction->setShortcut(QKeySequence::Undo);
QAction *redoAction = m_undoGroup->createRedoAction(this);
redoAction->setShortcut(QKeySequence::Redo);
menuEdit->insertAction(menuEdit->actions().at(0), undoAction);
menuEdit->insertAction(undoAction, redoAction);
menuEdit->addAction(undoAction);
menuEdit->addAction(redoAction);
updateActions();
}
void MainWindow::setCurrentDocument()
{
mdiArea->currentSubWindow();
}
void MainWindow::addnewFile()
{
m_undoGroup->addStack(view->scene->undoStack());
connect(view->scene->undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
connect(view->scene->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
}
void MainWindow::updateActions()
{
CadGraphicsScene *scene = currentDocument();
m_undoGroup->setActiveStack(scene == 0 ? 0 : scene->undoStack());
}
CadGraphicsView *MainWindow::currentDocument() const
{
if (QMdiSubWindow *activeSubWindow = mdiArea->currentSubWindow())
return qobject_cast<CadGraphicsView *>(activeSubWindow);
return 0;
}
void MainWindow::newFile()
{
// creates a new file
createMdiView();
view->newFile();
addnewFile();
curFileName = tr("Document %1").arg(++fileNumber);
view->setWindowTitle(curFileName);
view->scene->installEventFilter(this);
view->show();
isEntitySelected = false;
}
CadGraphicsView *MainWindow::createMdiView()
{
// creates a graphicsView and add it to the MDI window
view = new CadGraphicsView;
QMdiSubWindow *w = mdiArea->addSubWindow(view);
mdiArea->setActiveSubWindow(w);
windowViewList.append(qMakePair(w, view));
return view;
}
Can you please help me to solve the error. I have added the basic idea how it is working.
Got the solution to my problem. I need to change the currentDocument function as follows:
CadGraphicsView *MainWindow::currentDocument() const
{
return qobject_cast<CadGraphicsView *>(mdiArea->parentWidget());
}