A simple subclass of QWidget doesn't work as a QWidget - c++

I create a very simple subclass of QWidget like this:
class WorldView : public QWidget
{
Q_OBJECT
public:
explicit WorldView(QWidget *parent = 0);
signals:
public slots:
};
WorldView::WorldView(QWidget *parent) : QWidget(parent)
{
}
I create an instance of it in main window like this:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
virtual void resizeEvent(QResizeEvent* event) override;
private:
WorldView* _worldView;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
_worldView = new WorldView(this);
_worldView->setStyleSheet(QString("* {background-color : black}"));
}
MainWindow::~MainWindow()
{
delete _worldView;
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
_worldView->resize(size());
}
But the widget does not show as expected.
I have tried to call show(), but it still doesn't show.
The weird thing is that when I replace WorldView with QWidget, the widget shows.
I don't know why.

Because stylesheets don't work that way for custom QWidget subclasses.
From https://doc.qt.io/qt-5/stylesheet-reference.html :
QWidget
Supports only the background, background-clip and background-origin properties.
If you subclass from QWidget, you need to provide a paintEvent for your custom QWidget as below:
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
The above code is a no-operation if there is no stylesheet set.
Warning: Make sure you define the Q_OBJECT macro for your custom widget.
(And, in general, stop using stylesheets.)

Related

How to automatically update a statusbar message?

I am using Qt 5.15.1 and C++ to create a simple app which processes signals from hardware and displays images and driver status. I want to update the statusbar message when an int value defined in another class changes. I would like this to happen automatically, each time this value changes. I understand that I need signals and slots to achieve this. So far I have done the following:
signalprocessing.h
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
private:
int status;
public slots:
int GetStatus();
signals:
void StatusChanged();
}
signalprocessing.cpp
SignalProcessing::SignalProcessing(QObject *parent)
: QObject(parent)
{
}
int SignalProcessing::GetStatus()
{
emit(StatusChanged());
return status;
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
virtual ~MainWindow();
SignalProcessing *signalProcessing;
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
signalProcessing= new SignalProcessing(this);
ui->statusbar->showMessage(QString::number(signalProcessing->GetStatus()));
}
The problem is that the statusbar message is currently not updated automatically, but set to a given value.
How do I make sure it will always display the current status?
The idea is that you want to emit your StatusChanged signal when the value has actually changed, not when you call GetStatus(). So you need a SetStatus() function, and your SignalProcessing class will need to know when to call that. Then you want to connect that signal to a slot that then updates your status bar. It might look something like this:
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
int getStatus();
void setStatus(int value);
private:
int status;
signals:
void statusChanged(); // It's better to start signals with a lower case letter
}
signalprocessing.cpp
SignalProcessing::SignalProcessing(QObject *parent)
: QObject(parent)
{
}
int SignalProcessing::getStatus()
{
return status;
}
void SignalProcessing::setStatus(int value)
{
if (status != value)
{
status = value;
emit statusChanged();
}
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
virtual ~MainWindow();
SignalProcessing *signalProcessing;
public slots:
void updateStatus();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
signalProcessing= new SignalProcessing(this);
connect(signalProcessing, &SignalProcessing::statusChanged, this, &MainWindow::updateStatus);
updateStatus();
}
void MainWindow::updateStatus()
{
ui->statusbar->showMessage(QString::number(signalProcessing->getStatus()));
}
First change singal declartion so it will provide new value:
class SignalProcessing: public QObject
{
Q_OBJECT
public:
SignalProcessing(QObject *parent = nullptr);
private:
int status;
public slots:
int setStatus(int value);
signals:
void statusChanged(const QString& message);
}
void SignalProcessing::setStatus(int value)
{
if (status != value)
{
status = value;
emit statusChanged(tr("Status is %1").arg(status));
}
}
Then connect this signal to QStatusBar::showMessage slot and you done.

C++, QT. Function call for QShortcut does not exist

I have a class, MainWindow with a pointer to a Canvas, mCanvas...
mainwindow.h...
#include "canvas.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
//snip
private:
Canvas* mCanvas;
};
canvas.h...
class MainWindow;
class Canvas
{
public:
Canvas(MainWindow* parent);
//snip
public slots:
void doSomething();
private:
MainWindow* mParent;
};
Canvas.cpp...
Canvas::Canvas(MainWindow* parent)
{
mParent = parent;
}
void Canvas::doSomething()
{
//snip
}
In MainWindow.cpp, within the MainWindow constructor, I point mCanvas to an Canvas(this). I then attempt to create a new QShortcut with the action Canvas::doSomething().
MainWindow.cpp...
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
new QShortcut(QKeySequence(Qt::Key_BracketLeft),
mCanvas, SLOT(doSomething()) );
However, when I attempt to run the program, I recieve the error NO MATCHING FUNCTION CALL for doSomething(), meaning the compiler does not think that doSomething() exists. In the new QShortcut, I have written mCanvas as mCanvas, *mCanvas, &mCanvas; nothing works.
What is going wrong?
To use signals and slots in Canvas you need to inherit it from QObject (from QWidget in your case) and use Q_OBJECT macro. Also your mCanvas is not initialized before using in new QShortcut ....
Or do something like this:
auto shortcut = new QShortcut(QKeySequence(Qt::Key_BracketLeft), this);
QObject::connect(shortcut, &QShortcut::activated, this, [ this ] { mCanvas->doSomething(); } );

Signals and slots not working in qt

in my Qt program I need the dialog to send a signal to a slot in the mainwindow. I have set the connection up correctly to the best of my knowledge and it does not give me any errors during compile or run time but for some reason it just doesn't do anything when the button that is supposed to activate the signal is clicked. Why is this happening?
beastiary.h (mainwindow header)
namespace Ui {
class Beastiary;
}
class Beastiary : public QMainWindow
{
Q_OBJECT
public:
explicit Beastiary(QWidget *parent = 0);
Ui::Beastiary *ui;
QStringList MyList;
~Beastiary();
public slots:
void refresh();
private slots:
void on_pushButton_clicked();
void on_listWidget_itemClicked(QListWidgetItem);
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
beastiary.cpp (mainwindow cpp file)
Beastiary::Beastiary(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Beastiary)
{
ui->setupUi(this);
Dialog dialog;
connect(&dialog, SIGNAL(gorefresh()),this, SLOT(refresh())) ;
void Beastiary::refresh()
{
qDebug () << "recieved";
ui->listWidget->clear();
QString path = "C:/Program Files/Bargest/bin";
QDir myPath(path);
myPath.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
MyList = myPath.entryList();
ui->listWidget->addItems(MyList);
}
dialog.h
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
signals:
void gorefresh();
private slots:
void on_pushButton_done_clicked();
void on_pushButton_cancel_clicked();
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
dialog.cpp
void Dialog::on_pushButton_done_clicked()
{
emit gorefresh();
}
I did leave out large parts of the code that just have nothing to do with the actual signals and slots mechanism at play here.
You're only connecting the dialog instance you created locally in the Bestiary's constructor, which goes out of scope as the constructor finishes.
connect is connecting instances, not classes. That means you need to connect each created dialog:
void Beastiary::on_pushButton_clicked() {
Dialog* ad = new Dialog(this);
connect(ad, SIGNAL(gorefresh()), this, SLOT(refresh()));
ad->show();
}
While at it, you should seriously consider using the type-safe connect syntax that came with Qt 5:
void Beastiary::on_pushButton_clicked() {
Dialog* ad = new Dialog(this);
connect(ad, &Dialog::gorefresh, this, &Bestiary::refresh));
ad->show();
}

Qt, OpenGL Widget, no such slot

I wanted to create a simple application where there's triangle generated using OpenGL and three push buttons changing that triangle color. The triangle is generated but unfortunately buttons don't work and I get errors saying:
QObject::connect: No such slot MainWindow::redSlot(OGL) in
..\buttonwidget\mainwindow.cpp:17 QObject::connect: No such slot
MainWindow::greenSlot(OGL) in ..\buttonwidget\mainwindow.cpp:20
QObject::connect: No such slot MainWindow::blueSlot(OGL) in
..\buttonwidget\mainwindow.cpp:23
I have slots definitions:
void MainWindow::redSlot(Widget* w)
{
w->setColor(red);
}
void MainWindow::greenSlot(Widget* w)
{
w->setColor(green);
}
void MainWindow::blueSlot(Widget* w)
{
w->setColor(blue);
}
They are changing variable declared in class Widget that changes color of a generated triangle. Here's class Widget:
class Widget : public QGLWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
QSize minimumSizeHint() const;
QSize sizeHint() const;
enum color c;
void setColor(enum color color1);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
};
And then I connect slots to buttons in class MainWindow constructor:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
layout = new QVBoxLayout();
QWidget *w = new QWidget();
setCentralWidget(w);
w->setLayout(layout);
Widget *OGL = new Widget();
//OGL->c=green; - it was a test whether changing value of enum type variable c works
//it works, changing it changes the color of a generated triangle
redButton = new QPushButton(tr("Red"));
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot(OGL)));
greenButton = new QPushButton(tr("Green"));
connect(greenButton, SIGNAL(clicked()), this, SLOT(greenSlot(OGL)));
blueButton = new QPushButton(tr("Blue"));
connect(blueButton, SIGNAL(clicked()), this, SLOT(blueSlot(OGL)));
layout->addWidget(OGL);
layout->addWidget(redButton);
layout->addWidget(greenButton);
layout->addWidget(blueButton);
}
Here's slot declaration in header:
class MainWindow : public QMainWindow
{
Q_OBJECT
QVBoxLayout *layout;
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QPushButton *redButton;
QPushButton *greenButton;
QPushButton *blueButton;
public slots:
void redSlot(Widget*);
void greenSlot(Widget*);
void blueSlot(Widget*);
};
How should I make them work?
Connects in QT are string-based, that means:
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot(OGL)));
will not work, since you havent defined a slot "redSlot(OGL)", instead you would have to use
...SLOT(redSlot(Widget*)));
Moreover, it is possible to define a SLOT with less parameter but not with more, therefore your slot/connect has to look like
void redSlot();
connect(redButton, SIGNAL(clicked()), this, SLOT(redSlot()));
if you need a pointer to the "Widget" you have to retrieve it from somewhere else.
Example: Define slot in "Widget"-class and change connect to:
connect(redButton, SIGNAL(clicked()), OGL, SLOT(redSlot()));

Adding a child widget to another widget in Qt

Although there are similar questions to mine posted on stackoverflow, none of their solutions actually respond to my problem. I have 2 independent widgets that I would like to combine (insert one widget into the other as a child): one is a UI created only with Qt Creator (drag-and-drop), and the other one an animation done in Qt with OpenGL. I am trying to add the animation in the UI and here is the code:
glwidget.h (animation):
class GLWidget : public QGLWidget
{
public:
GLWidget(QWidget *parent);
~GLWidget();
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
void drawCube(int i, GLfloat z, GLfloat ri, GLfloat jmp, GLfloat amp);
QGLFramebufferObject *fbo;
};
and glwidget.cpp:
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
makeCurrent();
fbo = new QGLFramebufferObject(512, 512);
timerId = startTimer(20);
}
GLWidget::~GLWidget()
{
glDeleteLists(pbufferList, 1);
delete fbo;
}
void GLWidget::initializeGL()
{....
As for the UI, I have the header file:
class ClaraTeCourseSimulator : public QMainWindow
{
Q_OBJECT
public:
explicit ClaraTeCourseSimulator(QWidget *parent = 0);
~ClaraTeCourseSimulator();
private:
Ui::ClaraTeCourseSimulator *ui;
GLWidget *defaultAnim;
protected:
void setupActions();
protected slots:
void addAnimWidget();
};
and the .cpp file:
ClaraTeCourseSimulator::ClaraTeCourseSimulator(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ClaraTeCourseSimulator)
{
ui->setupUi(this);
defaultAnim = new GLWidget(ui->centralWidget);
}
void ClaraTeCourseSimulator::setupActions()
{
connect(ui->actionCar_Modelling, SIGNAL(triggered(bool)), ui->centralWidget,
SLOT(addAnimWidget()));
}
void ClaraTeCourseSimulator::addAnimWidget()
{
ui->centralWidget->layout()->addWidget(defaultAnim);
}
ClaraTeCourseSimulator::~ClaraTeCourseSimulator()
{
delete ui;
}
But when I try to run it I get about 24 of these errors: undefined reference to `imp_ZN9QGLFormatD1Ev all pointing to the constructor and destructor in glwidget.cpp.
What am I doing wrong? How can I fix this problem?
Are you trying to change the central widget to the GL one? Because specifying the parent for the GL widget does not change them. If you'd like to change a widget to another (using the Designer), I recommend the "promote to" feature, with which you can change a widget's actual class in the designer. So add a QWidget on the UI, and than promote it to your class (GLWidget).
It seems like the problem is with the GLFormat constructor call in the GLWidget constructor.