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().
Related
I want to resize the app window proportionally 1:1. I tried to change it inside the ResizeEvent, but then I got the window flickering. Now my code looks like this, but it doesn't work.
filterobject.h:
class FilterObject:public QObject{
public:
QWidget *target = nullptr;//it holds a pointer to target object
int goalHeight=0;
FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};
widget.h:
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
virtual int heightForWidth ( int w ) const { return w*9/16;}
//virtual void resizeEvent(QResizeEvent *event) override;
~Widget();
private:
Ui::Widget *ui;
};
widget.cpp:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void QWidget::resizeEvent(QResizeEvent *event){
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
}
bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
if(watched != target){//checks for correct target object.
return false;
}
if(event->type() != QEvent::Resize){//and correct event
return false;
}
QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);
goalHeight = 9*resEvent->size().width()/16;
if(target->height()!=goalHeight){
target->setFixedHeight(goalHeight);
}
return true;
};
Perhaps this code will work, but my condition if(event->type() != QEvent::Resize) does not work .. Any ideas?
You have some problems in your code. First of all you should install event filter once e.g in your constructor. You create an object of event filter and install it every time resizeEvent is triggered which is wrong. Also you are installing event filter on the wrong object (a new QWidget). So remove the resizeEvent function and insert in the constructor of Widget:
FilterObject *filter = new FilterObject();
filter->target=this;
installEventFilter(filter);
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
HERE IS Qt PROJECT with minimal skeleton to show what is the problem (check console after you run that project)
http://uloz.to/xqxrXpdL/qtproject-zip
I try to call public slot from qml
Component.onCompleted: print(model.activate())
Still getting error:
TypeError: Property 'activate' of object QQmlDMObjectData(0x7fa35dd89eb0) is not a function
If i tried to call the method dynamically from C++, it works:
auto item = new TreeItem<MainMenuItem>(new MainMenuItem("kyklop"));
QMetaObject::invokeMethod(item, "activate");
If i try to access regular property of my TreeItemTemplateBackend class from qml (for instance level), it works, so problem is only if calling method. I am thinking it could be something with that subclass/template class.
Registering to qml:
qmlRegisterType<TreeItemTemplateBackend>("engine.ui", 1, 0, "TreeItemTemplateBackend");
qmlRegisterType<TreeItem<InspectorItem>>("engine.ui", 1, 0, "InspectorTreeItem");
qmlRegisterType<TreeItem<MainMenuItem>>("engine.ui", 1, 0, "MainMenuTreeItem");
TreeItem.h
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QObject>
#include "TreeItemTemplateBackend.h"
template <typename T>
class TreeItem : public TreeItemTemplateBackend
{
public:
explicit TreeItem(QObject * parent = NULL);
explicit TreeItem(T * data, QObject * parent = NULL);
explicit TreeItem(TreeItem<T> & other);
void addChild(TreeItem<T> * child);
~TreeItem() {}
};
#endif // TREEITEM_H
TreeItemTemplateBackend.h
#ifndef TREEITEMTEMPLATEBACKEND_H
#define TREEITEMTEMPLATEBACKEND_H
#include <QList>
#include <QQmlListProperty>
class TreeItemTemplateBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject * data READ data WRITE setData NOTIFY dataChanged)
Q_PROPERTY(QQmlListProperty<TreeItemTemplateBackend> childs READ childs NOTIFY childsChanged)
Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged)
public:
explicit TreeItemTemplateBackend(QObject * parent = NULL);
QObject * data() const;
void setData(QObject * data);
QQmlListProperty<TreeItemTemplateBackend> childs() const;
void addChild(TreeItemTemplateBackend * child);
int level() const;
void setLevel(int level);
void dump(QString propertyName) const;
~TreeItemTemplateBackend() {}
signals:
void activated();
void dataChanged();
void childsChanged();
void levelChanged();
public slots:
void activate(); // this is what i am trying to call
protected:
QObject * m_data;
QList<TreeItemTemplateBackend *> m_children;
int m_level;
static void append_function(QQmlListProperty<TreeItemTemplateBackend> * property, TreeItemTemplateBackend * item);
static TreeItemTemplateBackend * at_function(QQmlListProperty<TreeItemTemplateBackend> * property, int index);
static void clear_function(QQmlListProperty<TreeItemTemplateBackend> * property);
static int count_function(QQmlListProperty<TreeItemTemplateBackend> * property);
};
#endif // TREEITEMTEMPLATEBACKEND_H
QQmlDMObjectData is a wrapper for the actual object used in delegates. The original object is accessible via the property modelData, so model.modelData.activate() should work.
If you cal for it "method" and still you are use it as a method so convert it to the method:
...
public:
void Q_INVOKABLE activate();
...
I am following a simple OpenGL with Qt tutorial, but I have hit a snag. Here is the link to the tutorial:
Link
In the tutorial you add a container widget to the window and promote it to a class that is inherits QGLWidget. When I first ran the project I got an error at this line:
MyPanelOpenGL::MyPanelOpenGL(QObject *parent) :
QGLWidget(parent)
{
sides = 3;
radius = 1.0;
}
The error stated that it could convert QObject to a QWidget. This code is the constructor for the QGLWidget. I changed the argument to QWidget, but I got another error, this time in my ui.h file. The error is as follows:
/home/computer/build-OpenGLTest-Desktop_Qt_5_0_2_GCC_64bit-Debug/ui_mainwindow.h:64: error: undefined reference to `MyPanelOpenGL::MyPanelOpenGL(QObject*)'
I changed the declaration when I changed the implementation in the panel's.h file. Here is the MyPanelOpenGL.h file:
#include <QGLWidget>
class MyPanelOpenGL : public QGLWidget
{
Q_OBJECT
public:
explicit MyPanelOpenGL(QWidget *parent = 0);
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
int sides;
double radius;
public slots:
void changeSides(int s);
void changeRadius(double r);
};
Does anyone know what's going on? Thanks!
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();
}