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()) );
}
Related
I am creating a checkers board as part of a university assignment. There has been very little teaching around the GUI side of things. So I may have overcomplicated this project a bit and become stuck while trying to access the clicks from the board GUI.
I have read about lambda's in the documentation https://doc.qt.io/qt-5/qsignalmapper.html for signal mappers, but, I am not sure if it is right for the application I am coding. I have tried to implement the signal mapping so that when a particular button is pushed on the checker's board, the slot can identify it with its unique signal.
If there is a better way, or if lambdas are the best way, could you please explain how to implement this?
At the moment, my current code can't access the clicks made. I also get an error for the connect() function:
no known conversion from 'void QAbstractButton::* (bool)' to 'const char*' for the second argument.
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
However, this compiles but does not work:
connect(&checkers[i], SIGNAL(clicked), signalMapper, SLOT(map));
Below is more complete code for greater context.
Constructor and board set function:
// Constructor
GameWindow::GameWindow(QWidget *parent):
QDialog(parent), ui(new Ui::GameWindow)
{
ui->setupUi(this);
BoardSetup(4);
}
/* Sets up the initial gameboard display with the other game information displays also being
* initialised. The game board is an array of QpushButtons that have there Icon
* changed according to where the pieces are on the board.
* Called by the GameWindow Constructor*/
void GameWindow::BoardSetup(int size)
{
{
QSignalMapper *signalMapper = new QSignalMapper(this->checkers);
connect(signalMapper, &QSignalMapper::mappedInt, this, &GameWindow::SquareClicked );
bool col=1;
for (int j=0;j<size;j++)
{
col=!col;
for(int i=0; i<size; i++)
{
if(col == 1) // logically check to see what Color the square needs to be.
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: black");
}
else if(col==0)
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: white");
}
col=!col;
signalMapper->setMapping(&checkers[i], i);
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
}
}
Grid = new QGridLayout;
Grid->setGeometry(QRect(0,0,width,width));
for(int j=0; j<size; j++)
{
for(int i=0; i<size; i++)
{
Grid->addWidget(&checkers[i+j*size], j, i, Qt::AlignmentFlag::AlignCenter);
}
}
GridGroup = new QGroupBox();
GridGroup->setLayout(Grid);
ui->BoardGrid->addWidget(GridGroup);
ui->BoardGrid->setGeometry(QRect(0,0,width,width));
ui->BoardGrid->addWidget(GridGroup);
setWindowTitle("Checkers Game");
ui->P1PieceCount->setFontPointSize(40);
ui->P1PieceCount->setAlignment(Qt::AlignCenter);
ui->P2PieceCount->setFontPointSize(40);
ui->P2PieceCount->setAlignment(Qt::AlignCenter);
ui->TurnCount->setFontPointSize(40);
ui->TurnCount->setAlignment(Qt::AlignCenter);
ui->Player2Name->setAlignment(Qt::AlignCenter);
CheckersBoard(size);
SetInitialDisplay(size);
}
return;
}
How I am trying to access the clicks.
GetClicks() and WaitForClick() functions:
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::HumanMove */
void GameWindow::GetClicks()
{
connect(this, &GameWindow::SquareClicked, this, &GameWindow::WaitForClick);
}
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::GetClicks via signal SquareClicked */
void GameWindow::WaitForClick(int square)
{
firstclick = !firstclick;
unsigned int test=square;
if (firstclick ==0 && test != StartPos)
{
StartPos = square;
checkers[square].setChecked(1);
}
else if (firstclick == 1 && test == StartPos)
{
checkers[square].setChecked(0);
}
else if (firstclick==1 && test!= StartPos)
{
EndPos = square;
}
}
The StartPos and EndPos clicked are used in a possible move function to validate the move. It is then updated to the GUI.
Any help on how best to implement this signal and slot mechanism would be greatly appreciated.
Have a look at Qt calculator example?
https://doc.qt.io/qt-5/qtwidgets-widgets-calculator-example.html
you can connect multiple buttons to the same slot.
in slot, find out which button sent the signal using QObject::sender().
Once have the button, extract the position using MyButton::Pos().
(MyButton is the widget used for each of the board button)
I have several custom widget in my current project. I wish to apply stylesheets to them and when I do so inside Qt Creator, it appears to work. However, when executing the program, no stylesheet is used. The stylesheets for the Qt widgets are working normally.
Does anyone have any advice?
WidgetUnits.h
#ifndef WIDGETUNITS_H
#define WIDGETUNITS_H
#include <QList>
#include <QWidget>
#include <QPainter>
#include <Widgets/JECButton.h>
#include <Unit.h>
#include <Time.h>
namespace Ui
{
class WidgetUnits;
}
class WidgetUnits : public QWidget
{
Q_OBJECT
public:
explicit WidgetUnits(QWidget *parent = 0);
~WidgetUnits();
void setNumTimes(const int& numTimes);
public slots:
void updatePictures(const Time* time);
protected:
void paintEvent(QPaintEvent *event);
private:
void checkNewQueue(const QList<QList<Unit*>*>* units);
Ui::WidgetUnits *ui;
const int pictureWidth; // The width of the Unit pictures.
const int pictureHeight; // The height of the Unit pictures.
QList<QList<JECButton*>*> buttonPictures; // The Units' pictures. The outer QList stores the QList of pictures for a given tick.
// The inner QList stores the JECButtons for the specific tick.
};
WidgetUnits.cpp
#include "WidgetUnits.h"
#include "ui_WidgetUnits.h"
WidgetUnits::WidgetUnits(QWidget *parent):
QWidget(parent),
ui(new Ui::WidgetUnits),
pictureWidth(36),
pictureHeight(36)
{
ui->setupUi(this);
}
WidgetUnits::~WidgetUnits()
{
delete ui;
}
void WidgetUnits::updatePictures(const Time *time)
{
// Only showing units that started to get built this turn.
checkNewQueue(time->getUnits());
checkNewQueue(time->getBuildings());
checkNewQueue(time->getUpgrades());
// Updating the position of the remaining pictures (after some were removed).
// Checking the maximum number of Units made in one tick.
int maxNewQueue = 0;
for (int a = 0; a < buttonPictures.length(); ++a)
{
if (buttonPictures.at(a)->length() > maxNewQueue)
{
maxNewQueue = buttonPictures.at(a)->length();
}
}
if (buttonPictures.length() > 0)
{
this->setGeometry(0, 0, buttonPictures.length() * 130,
maxNewQueue * (pictureWidth + 10) + 20);
QList<JECButton*>* tickButtons = 0;
for (int a = 0; a < buttonPictures.length(); ++a)
{
tickButtons = buttonPictures.at(a);
for (int b = 0; b < tickButtons->length(); ++b)
{
tickButtons->at(b)->move(a * 130, b * (pictureHeight + 10));
}
}
}
update();
}
void WidgetUnits::checkNewQueue(const QList<QList<Unit *> *> *units)
{
if (units != 0)
{
const Unit* currentUnit = 0;
JECButton* currentButton = 0;
for (int a = 0; a < units->length(); ++a)
{
buttonPictures.append(new QList<JECButton*>());
for (int b = 0; b < units->at(a)->length(); ++b)
{
currentUnit = units->at(a)->at(b);
// Verifying that there is an item in the queue and the queue action was started this turn.
if (currentUnit->getQueue() != 0 && currentUnit->getAction()->getTimeStart() == currentUnit->getAction()->getTimeCurrent()
&& (currentUnit->getAction()->getType() == Action::BUILD || currentUnit->getAction()->getType() == Action::TRAIN ||
currentUnit->getAction()->getType() == Action::UPGRADE))
{
buttonPictures.last()->append(new JECButton(this));
currentButton = buttonPictures.last()->last();
QImage* image = new QImage(currentUnit->getQueue()->getUnitBase()->getImage().scaled(pictureWidth, pictureHeight));
currentButton->setImage(*image);
currentButton->setGeometry(0, 0, currentButton->getImage().width(),
currentButton->getImage().height());
currentButton->setColorHover(QColor(0, 0, 225));
currentButton->setColorPressed(QColor(120, 120, 120));
currentButton->setImageOwner(true);
currentButton->setVisible(true);
}
}
}
}
}
void WidgetUnits::setNumTimes(const int &numTimes)
{
// Appending new button lists for added ticks.
for (int a = buttonPictures.length(); a < numTimes; ++a)
{
buttonPictures.append(new QList<JECButton*>());
}
}
void WidgetUnits::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
}
The widget is visible- I set a tooltip which it showed me (It's just the same color of the QScrollArea it's sitting in).
I had a similar problem and it was solved using jecjackal's comment. As sjwarner said, it would be much more noticeable in the form of an answer. So I'll provide it. For the benefit of any future viewers. Again, it isn't my answer! Appreciate jecjackal for it!
As it is said in the Qt's stylesheets reference, applying CSS styles to custom widgets inherited from QWidget requires reimplementing paintEvent() in that way:
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
Without doing it your custom widgets will support only the background, background-clip and background-origin properties.
You can read about it here: Qt Stylesheets reference in the section "List of Stylable Widgets" -> QWidget.
There is an answer much easier than writing your own paintEvent: subclass QFrame instead of QWidget and it will work right away:
class WidgetUnits : public QFrame
{
Q_OBJECT
....
For completeness, the same problem is present in PyQt. You can apply a stylesheet to a subclassed QWidget by adding similar code:
def paintEvent(self, pe):
opt = QtGui.QStyleOption()
opt.init(self)
p = QtGui.QPainter(self)
s = self.style()
s.drawPrimitive(QtGui.QStyle.PE_Widget, opt, p, self)
I had same problem with pyside. I post my solution just for completeness.
It is almost like in PyQt as Pieter-Jan Busschaert proposed.
only difference is you need to call initFrom instead of init
def paintEvent(self, evt):
super(FreeDockWidget,self).paintEvent(evt)
opt = QtGui.QStyleOption()
opt.initFrom(self)
p = QtGui.QPainter(self)
s = self.style()
s.drawPrimitive(QtGui.QStyle.PE_Widget, opt, p, self)
One other thing you need to make sure is that you define your custom widget in your css file the following way:
FreeDockWidget{...}
and not like often recommended
QDockWidget#FreeDockWidget{...}
Calling setAttribute(Qt::WA_StyledBackground, true) for the custom widget worked for me.
Setting Qt::WA_StyledBackground to true only works if you remember to add Q_OBJECT to your class. With these two changes you don't need to reimplement paintEvent.
In my programm, I fill my interface with a lot of checkbox by this way :
void VGCCC::addMaterialToUI(QDomNodeList _materialNodeList, QWidget* _areaWidget, QLayout* _layout, QWidget* _layoutWidget, int _maTable)
{
for(int i=0; i< _materialNodeList.count();i++)
{
QDomElement materialElement = _materialNodeList.at(i).toElement();
QString elementFile = materialElement.attribute("file");
QString elementId = materialElement.attribute("id");
QString elementLabel = elementId;
elementLabel += " - ";
elementLabel += materialElement.attribute("label");
QCheckBox* checkbox = new QCheckBox(elementLabel);
_layout->addWidget(checkbox);
_layoutWidget->adjustSize();
_areaWidget->setMinimumHeight(_layoutWidget->height());
_areaWidget->setMinimumWidth(_layoutWidget->width());
configuration c;
c.path = (m_igmPath+elementFile).toStdString();
c.id = elementId.toInt();
c.name = elementLabel.toStdString();
if(_maTable==0)
{
m_materialSectionMap[checkbox] = c;
}
else
{
m_materialPostMap[checkbox] = c;
}
}
}
I would like to know how to retrieve these "abstract" checkbox. More exactly, if one of these checkbox is checked, I would like to call another function like this :
connect(anyCheckbox,SIGNAL(stateChanged(anyCheckbox)), this, SLOT(doSomethingFunctionIfCheckboxIsChecked()));
The difficulty is that in my UI, these checkbox didn't exist, so I can't connect them to my function. How can I solve it ?
You can e.g. collect pointers to your checkbox objects to a list so can access or "retrieve" them later.
You can connect each checkbox's stateChanged signal to a same slot which is then called when state of any of the checkboxes is changed. In the slot you can cast the sender() to a checkbox if you need to know which specific checkbox is in question. Another alternative is to use QSignalMapper.
In your class declaration:
private slots:
void checkboxStateChanged(int state)
private:
QList<QCheckBox*> m_checkboxes;
In your class definition:
void VGCCC::addMaterialToUI(QDomNodeList _materialNodeList, QWidget* _areaWidget, QLayout* _layout, QWidget* _layoutWidget, int _maTable)
{
...
QCheckBox* checkbox = new QCheckBox(elementLabel);
m_checkboxes.append(checkbox);
connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(checkboxStateChanged(int)));
...
}
void VGCCC::checkboxStateChanged(int state)
{
// Here your can e.g. call doSomethingFunctionIfCheckboxIsChecked()
QCheckBox* checkbox = qobject_cast<QCheckBox*>(sender());
if (checkbox)
{
// checkbox points to the object whose state changed
}
}
I have a function that should send data to a raspberry pi for a given period of time depending on the parameter.
//headerfiles
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QUdpSocket udpSocket;
// movement Timer
QTimer* movementTimer;
private slots:
void sendDatagram(); // Sends to the RaspberryPi
void processFrameAndUpdateGUI();
void turnLeft(double time);
void turnRight(double time);
void goStraight(double time);
void MovRobot();
};
// MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
movementTimer = new QTimer(this);
//send datagram sends data to tehe PI.
connect(movementTimer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
MovRobot(); // MovRobot() function
}
// controls robot to turn left for specified time
void TurnLeft(double time)
{
s = 1; // sets the value to be sent to the PI.
movementTimer->setInterval(time);
movementTimer->setSingleShot(true);
movementTimer->start();
}
//sendDatagram() slot
void MainWindow::sendDatagram() {
QString datagramOutput = "start," +
QString::number(w) + ',' + QString::number(a) + ',' +
QString::number(s) + ',' + QString::number(d) + ',' +
QString::number(ui->motorSpeedSlider->value()) + ',' +
QString::number(dispenserSignal);
datagramOutput += ",end";
QByteArray datagram;
QDataStream out(&datagram,QIODevice::WriteOnly);
out << datagramOutput;
udpSocket.writeDatagram(datagram,QHostAddress("192.168.0.104"),12345);
}
//MovRobot Function;
void MainWindow :: MovRobot() {
if (ui->pushButton_3->isChecked()) {
MapArea(); // This function maps the area....
// final_plan is a vector<Point2i> that stores positions on the map for
// the robot to move to
for (int i = 0; i < final_plan.size(); i++) {
for (int j = final_plan.size() - 1; j>=0; j--) {
do {
Mat src;
bool bsuccess = cap.read(src);
if (!bsuccess) {
ui->label_34->setText("Status: Can't read frame.");
ui->pushButton_3->setChecked(0);
}
GetRobotPosition(src);
// AngleToGoal calculates the angle of the robot relative
// to the final goal.
double tempAngle = AngletoGoal(final_plan[i][j]);
if (tempAngle>=0) {
turnLeft(TimeToTurn(tempAngle));
}
else {
turnRight(TimeToTurn(tempAngle));
}
double tempDistance = DistancetoGoal(final_plan[i][j]);
goStraight(tempDistance);
} while (DistancetoGoal(final_plan[i][j])<20);
}
}
}
}
The timer should only send the data over to the PI for the duration it in time sendDatagram is the function that sends the data over to the Pi. Is there anything I'm missing here. The timer doesn't start inside the TurnLeft() function and doesn't run at all. Am I going about this wrong?
EDIT: 13/03/2016:
My apologies for the late reply. I've been quite sick the past few days. I've added the relevant parts of the code. MovRobot() is the main function responsible for the movement and this is called in the constructor for MainWindow. I have debugged and stepped through the program and yes, TurnLeft() is called. However, the sendDatagram() slot doesn't actually send anything in the function. To confirm sendDatagram() was actually working, I used another timer to continuously send information to the PI on the robot to control the arm.
// Header File
QTimer* tmrTimer;
private slot:
void processFrameAndUpdateGUI();
// MainWindow Constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(tmrTimer, SIGNAL(timeout()), this, SLOT(processFrameAndUpdateGUI()));
tmrTimer->start(10);
}
void MainWindow::processFrameAndUpdateGUI() {
sendDatagram();
}
The sendDatagram() slot is pretty much the same with the exception of me changing what values are being sent to the PI and this seems to work perfectly.
However, my original problem is, I would like to send the data to the robot with for a specified amount of time as that makes the robot turn x degrees. This is why I've made movementTimer() single shot.
Stepping through my program, I know this line is called within my TurnLeft function.
movementTimer->start();
but the sendDatagram() slot itself doesn't actually send anything to the PI.
Based on your code. The program never called TurnLeft(). So the timer never started. It will be better to start the timer in constrator instead.
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MySlot()));
timer->start(1000);
I would try setting the timer to start in the constructor.
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());
}