I have a little problem with the Qt class QGraphicsScene:
To detect the current mouse coordinates I made a new class QGraphicsScenePlus with QGraphicsScene as the base class. I have already redefined the slot function mouseMoveEvent(QGraphicsSceneMouseEvent* event) and the received coordinates seem to be correct. Now I want to notify the parent QMainWindow class, where the QGraphicsScenePlus object is stored, whenever the mouse coordinates change. What is the best way to do this? I already tried to define signals and slots, but it didn't work. The slot function wasn't found during the execution of the program.
Here is the code so far:
qgraphicssceneplus.h
#ifndef QGRAPHICSSCENEPLUS_H
#define QGRAPHICSSCENEPLUS_H
#include <QObject>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
class QGraphicsScenePlus : public QGraphicsScene {
public:
QGraphicsScenePlus(QObject* parent = 0);
public slots:
void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
public:
int mx = 0;
int my = 0;
};
#endif // QGRAPHICSSCENEPLUS_H
qgraphicssceneplus.cpp
#include "qgraphicssceneplus.h"
QGraphicsScenePlus::QGraphicsScenePlus(QObject* parent) : QGraphicsScene(parent) {
}
void QGraphicsScenePlus::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
mx = mouseEvent->scenePos().x();
my = mouseEvent->scenePos().y();
this->update();
}
Comment
I am not sure how you made the above code compiled.
1. Even though you subclass from a QObject, you still need the Q_OBJECT macro to keep meta-object compiler informed:
class QGraphicsScenePlus : public QGraphicsScene {
Q_OBJECT // <--- You miss this
public:
QGraphicsScenePlus(QObject* parent = 0);
2. It's not allowed to assign primitive value in C++ class definition, do it in the constructor instead:
public:
int mx /*= 0*/;
int my /*= 0*/;
};
Solution
As for your question:
What is the best way to do this? I already tried to define signals and slots, but it didn't work.
The best way is still Signals & Slots.
Code
qgraphicssceneplus.h
class QGraphicsScenePlus : public QGraphicsScene {
Q_OBJECT
public:
QGraphicsScenePlus(QObject* parent = 0);
public slots:
void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
signals:
void sendCoord(int,int); // for sending the information of coordinates
public:
int mx;
int my;
};
qgraphicssceneplus.cpp
QGraphicsScenePlus::QGraphicsScenePlus(QObject* parent) : QGraphicsScene(parent) {
mx = 0;
my = 0;
}
void QGraphicsScenePlus::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
mx = mouseEvent->scenePos().x();
my = mouseEvent->scenePos().y();
emit sendCoord(mx, my); // emit the signal
this->update();
}
To catch the signal, define the slot in QMainWindow. For example:
public slots:
void receiveCoord(int x, int y);
and connect it to the signal of your graphic scene.
Demo
Related
I have two simple classes(class A and class B).
In a.h, I just declared a QPushButton:
QPushButton *testBtn = new QPushButton(this);
In b.h:
class B : public QMainWindow
{
Q_OBJECT
public:
explicit B(QWidget *parent = nullptr);
A testingA;
public slots:
void testing();
};
and b.cpp:
B::B(QWidget *parent) : QMainWindow(parent)
{
connect(testingA.testBtn, &QPushButton::clicked, this, &B::testing);
}
void B::testing()
{
qDebug() << "testing";
}
I am trying to connect the signal in class A to the slot in class B, but from the code I provide, it's not working.
So what is the right way to do it? Thanks
Edit:
According to PRIME's answer, I made a few changes.
In A's constructor, added:
connect(testBtn, &QPushButton::clicked, [this](){OnButtonClicked();});
to emit the own defined OnButtonClicked() signal;
and in B's construtor, changed to this:
connect(&testingA, &A::OnButtonClicked, this, &B::testing);
But when I clicked the button, the testing slot still not triggered.
Edit 2:
After doing some researches and trying a few times, I found that if I created B's object in A's constructor, and then connect A's signal to B's slot in A, it will work.
But I really can not figure out why I can not connect A's signal to B's slot in B.
This is what's in the main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
B b;
A w;
w.show();
return a.exec();
}
Is that because of some reasons that A's object is out of scope in B?
Can someone tell me where I did wrong? Thanks so much.
Don't do it like this, hide your button in the class A, emit your own defined signal from class A lets call it OnButtonClicked.
Cascading code(inside A's c'tor):
connect(testBtn , &QPushButton::clicked, [this](){OnButtonClicked();});
You will also have to declare this new signal in class A now:
So class A must have folowing besides whatever it has right now:
class A
{
Q_OBJECT
signals:
void OnButtonClicked();
};
No special slot is needed since you are using a Lambda as a slot for the signal OnButtonClicked.
Connection in class B( do it in the c'tor ):
connect(testingA, &A::OnButtonClicked, this, &B::testing);
You can connect signal-to-signal in your sender object, for example widget containing the button:
class MyWidget : public QWidget
{
Q_OBJECT
QPushButton *pushButton;
public:
explicit MyWidget(QWidget *parent = nullptr) : QWidget(parent), pushButton(new QPushButton(this)) {
connect(pushButton, &QPushButton::click, this, &MyWidget::buttonClicked);
}
signals:
void buttonClicked();
public slots:
};
By the way you would normally send signals by using emit keyword, e.g.:
emit buttonClicked();
Then the consumer:
class TestObject : public QObject
{
Q_OBJECT
public:
explicit TestObject(QObject *parent = nullptr) : QObject(parent) { }
public slots:
void onButtonClicked() {
qDebug() << "clicked";
}
};
And connect both instances:
MyWidget widget;
TestObject to;
QObject::connect(&widget, &MyWidget::buttonClicked, &to, &TestObject::onButtonClicked);
in your class A you should use the signal testing of the class B, if you clicked on your button the OnButtonClicked function will be activated
A:
public slots:
void OnButtonClicked();
void A::OnButtonClicked()
{
...
emit testing(1);
}
B:
signals:
void testing(int level);
then to connect both you can do this
connect(startButton, &QPushButton::clicked, board, &A::OnButtonClicked);
Can you explain a methods for storing program data with global access ?
I found these keywords:
- using static class to store data
- pass QList by value
- pass Qlist by reference
- use 'friend' keyword
but I cannot find any real example of storing global QList, as they say, it is a bad design to use global variables. Also there is a mention that using pointers on a QList is a bad idea because of implicit sharing (?).
So where should I store my Qlist for accessing it from a different class in an other .cpp ? So I have:
mainwindow.h
QList <SceneCard> sceneList;
QString mTitle;
public slots:
QString setValue()
{
return mTitle;
}
mainwindow.cpp
MainWindow::AddScene()
{
sceneCard = new SceneCard(sNumber);
sceneList.append(sceneCard);
mTitle = "Nejat is right!"
}
void MainWindow::showSceneCard()
{
SceneDialog D;
connect(D,SIGNAL(getValue()),this,SLOT(setValue()));
D.exec();
}
scenedialog.h
#ifndef SCENEDIALOG_H
#define SCENEDIALOG_H
#include <QDialog>
#include <QList>
namespace Ui {
class SceneDialog;
}
class SceneDialog : public QDialog
{
Q_OBJECT
public:
SceneDialog(QWidget *parent = 0);
~SceneDialog();
signals:
QString getValue();
private:
Ui::SceneDialog *ui;
QString myText;
};
scenedialog.cpp
#include "scenedialog.h"
#include "ui_scenedialog.h"
#include <QDebug>
SceneDialog::SceneDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SceneDialog)
{
ui->setupUi(this);
myText = getValue();
qDebug() << myText; // myText is empty!!
}
You can put your list as a class member and use Qt's Signal/slot mechanism to access the list from other classes. Just make a signal in the target class, connect it to a slot in the class containing the list and make a connection between two objects of the classes. This way you can access any data member of other classes by connecting a signal to a slot returning that value and just emitting the signal and getting the return value.
For example if you have two classes like :
class A: public QObject
{
Q_OBJECT
public:
A(QObject *parent = 0);
~A();
signals:
int getValue();
private:
void someFunction()
{
int val = getValue();
}
};
class B
{
Q_OBJECT
public:
B(QObject *parent = 0);
~B();
public slots:
int getValue()
{
return someValue;
}
};
And connect the signal from an object of A to the slot in an object of B :
connect(a, SIGNAL(getValue()), b, SLOT(getValue()));
In class A you can access the value returned from getValue slot in B by just calling the signal and using the returned value.
Note that the two objects should be in the same thread for this to work. If they are in different threads then the connection type should be of type Qt::BlockingQueuedConnection :
connect(a, SIGNAL(getValue()), b, SLOT(getValue()), Qt::BlockingQueuedConnection);
Another way is two use static class members but it is not recommended unless you have a good reason to do it. If you have two classes like :
class A {
public:
static QList<int> list;
};
class B {
public:
void do_something();
};
You can access A's static data member from B like this:
void B::do_something()
{
int val = A::list[0];
...
};
I have the following snippet:
class A : public QWidget
{
Q_OBJECT
public:
A(QWidget *parent = 0);
void
setGeometry(int x, int y, int w, int h);
protected:
virtual void
resizeEvent(QResizeEvent *event);
}
class B : public A
{
Q_OBJECT
public:
B(A*parent = 0);
void
setGeometry(int x, int y, int w, int h);
protected:
virtual void
resizeEvent(QResizeEvent *event);
}
void
A::setGeometry(int x, int y, int w, int h)
{
QWidget::setGeometry(x, y, w, h);
}
void
A::resizeEvent( QResizeEvent * event)
{
QWidget::resizeEvent(event);
// common A and B stuff
}
void
B::setGeometry(int x, int y, int w, int h)
{
A::setGeometry(x, y, w, h);
}
void
B::resizeEvent( QResizeEvent * event)
{
A::resizeEvent(event);
}
Calling setGeometry on an instance of A will fire resizeEvent() . Invoke setGeometry on an instance of B will not fire resizeEvent(). Is there anything wrong with this?
EDIT:
I could do the same calculation I need inside setGeometry successfully. Now, mine is only curiosity.
There are a few problems with the snippet above, so I've altered it in a few places... the code below is the minimum necessary to produce the behaviour you want.
Header:
class TestA : public QWidget
{
Q_OBJECT
public:
explicit TestA(QWidget *Parent = 0) : QWidget(Parent) {}
~TestA() {}
protected:
virtual void resizeEvent(QResizeEvent *);
};
class TestB : public TestA
{
Q_OBJECT
public:
explicit TestB(QWidget *Parent = 0) : TestA(Parent) {}
~TestB() {}
protected:
virtual void resizeEvent(QResizeEvent *);
};
Implementation:
void TestA::resizeEvent(QResizeEvent *)
{
qDebug() << "TestA Resize";
}
void TestB::resizeEvent(QResizeEvent *)
{
qDebug() << "TestB Resize";
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
TestA* A = new TestA(this);
TestB* B = new TestB(this);
A->setGeometry(0,0,100,100);
B->setGeometry(200,200,100,100);
}
Changes:
Addition of Q_OBJECT macros to class definition:
Tells the compiler to add Qt meta-object code for the class (not strictly necessary to ensure that resizeEvent()s are called, but should be included for objects which inherit QObject as a matter of course).
Addition of constructors which allow the passing in of a parent object and invoke the base class constructor with this parent object AND passing a parent object into the constructors of the two objects when they are created:
From the docs:
When changing the geometry, the widget, if visible, receives a move event (moveEvent()) and/or a resize event (resizeEvent()) immediately. If the widget is not currently visible, it is guaranteed to receive appropriate events before it is shown.
If your widgets don't have parents setting the geometry is half meaningless, as the x and y parts refer to its position relative to a parent. On top of that, since the widgets have no parents they can't be visible as part of the application so Qt doesn't bother to call the appropriate resizeEvent().
Sorry if I'm missing something obvious, but I can't seem to find an answer to my question. Any help would be appreciated.
I am trying to use a QSlider to manipulate data in a class I created.
In the main window constructor I have the following:
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), this, SLOT(setValue(int)));
With a slot defined in the same class:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
AFrame *frame;
Ui::MainWindow *ui;
public slots:
void setValue(int val)
{
frame->setMphValue(val);
}
};
My frame class is a promoted widget to allow for drawing over the image I have set and is defined as follows:
class AFrame : public QLabel
{
Q_OBJECT
public:
AFrame( QWidget *parent );
void setMphValue(int val) { m_mph = val; }
protected:
void paintEvent( QPaintEvent *event );
private:
int m_mph;
};
The problem is that when I try assigning the m_mph value in the paintEvent function of the AFrame class, the integer value is lost.
Is there something obvious that I'm missing? Is there a better way to approach this problem?
And my paintEvent code:
void AFrame::paintEvent(QPaintEvent *event)
{
QLabel::paintEvent(event);
QPainter painter(this);
QPen pen("#FF0099");
pen.setWidth(8);
painter.setPen(pen);
//rpm
painter.drawLine(250,275,165,165);
//oil
painter.drawLine(450,100,400,75);
//fuel
painter.drawLine(650,95,600,65);
//mph
QRect rec(0,0,125,3);
int velocity = m_mph;
int rpmStartVal = -225;
float mph = velocity * 1.68;
painter.translate(870,275);
painter.rotate(rpmStartVal + mph);
painter.drawRect(rec);
}
The integer value is not being lost. The widget has no magical insight into the fact that it should repaint when the mph value is updated. Your setMphValue should look like below. That's all there's to it.
void setMphValue(int val) {
m_mph = val;
update();
}
To add to Kuba's reply:
You should also check whether val is the same value as previous, in order to avoid avoid repainting when it's not actually necessary - and side-effect infinite loops should anything called by update() later touch setMphValue().
In full:
void setMphValue(int val) {
if (val == m_mph) return;
m_mph = val;
update();
}
I'm having some problem with understanding usage of parent pointer in QT4.
class firstClass : public QWidget
{
Q_OBJECT
public:
firstClass(QWidget *parent = 0);
~firstClass();
void doSomething();
private:
secondClass * myClass;
};
class secondClass : public QWidget
{
Q_OBJECT
public:
secondClass(QWidget *parent = 0);
void doSomethingElse();
};
I want to call doSomething() method while running doSomethingElse(). Is there any way to do it using parent pointer?
I tried parent->doSomething() but it doesn't work. It seems that Qt Creator is suggesting only methods from QObject class after parent->.
On the other hand I can't write it like secondClass(firstClass *parent = 0); - compilator returns error:
Thanks for any suggestions.
If you are positive that the parent of secondClass is always going to be firstClass then you can do this:
static_cast<firstClass *>(parent)->doSomething();
Alternatively you can use qobject_cast and check to make sure that parent is actually an instance of firstClass:
firstClass *myParent = qobject_cast<firstClass *>(parent);
if(myParent){
myParent->doSomething();
}
The more Qt-ish way to do this would be to use signals and slots, instead of trying to directly call a different function.
class firstClass : public QWidget
{
Q_OBJECT
public:
firstClass(QWidget *parent = 0);
~firstClass();
public slot:
void doSomething();
private:
secondClass * myClass;
};
class secondClass : public QWidget
{
Q_OBJECT
public:
secondClass(QWidget *parent = 0);
void doSomethingElse()
{
// ...
emit ( triggerDoSomething() );
// ...
}
signal:
void triggerDoSomething();
};
firstClass::firstClass(QWidget *parent) :
QWidget(parent), myClass(new secondClass(this))
{
// ...
bool connected = connect(myClass, SIGNAL(triggerDoSomething()),
SLOT(doSomething()));
assert( connected );
}