3

A experienced the following bug in Qt 4.8.5, under Ubuntu 13.04 (and I'm nem to Qt) I have have an application with the following structure:

Mainwondow
-CentralWidget
--VerticalLayout
---TabWidget
---QLabel (created with code, and added to the layout)
---StatusBar

In fullscreen mode I hide the TabWidget, and the Statusbar, then the QLabel stops refreshing. (i have a thread to do the refresh) The strange thing is, when i restore the TabWidget or the StatusBar it works fine. It also works good, if i add a 1x1 pixel label to the VerticalLayout.

The slot responsible for the gui change;

void Mainview::onToggleFullScreen()
{
    if (this->isFullScreen())
    {
        this->showNormal();
        this->statusbar->show();
        this->tabWidget->show();
    }
    else
    {
        this->showFullScreen();
        this->statusbar->hide();
        this->tabWidget->hide();
    }
}

But the thing I cant understand if I put a QLabel near the image, it works, and if I add this single line to the MainWindow constructor, it stops refreshing:

label_10->hide(); //this is the label

Any idea what is the problem?

(Thanks in advance)

András Kovács
  • 847
  • 1
  • 10
  • 29

1 Answers1

0

You're probably doing it in some wrong way, but you don't show the code, so how can we know?

Below is a safe SSCCE of how one might do it. Works under both Qt 4.8 and 5.1.

Nitpick: The status bar should not be a part of the centralWidget()! QMainWindow provides a statusBar() for you.

The only safe way of passing images between threads is via QImage. You can not use QPixmap anywhere but in the GUI thread. End of story right there.

In the example below, all of the important stuff happens behind the scenes. The DrawThing QObject lives in another thread. This QThread's default implementation of the run() method spins a message loop. That's why the timer can fire, you need a spinning message loop for that.

Every time the new image is generated, it is transmitted to the GUI thread by implicitly posting a message to MainWindow. The message is received by Qt event loop code and re-synthesized into a slot call. This is done since the two ends of a connection (DrawThing and MainWindow instances) live in different threads.

That the beauty of Qt's "code less, create more" approach to design :) The more you leverage what Qt does for you, the less you need to worry about the boilerplate.

Screenshot

//main.cpp
#include <QMainWindow>
#include <QVBoxLayout>
#include <QStatusBar>
#include <QLabel>
#include <QThread>
#include <QPainter>
#include <QImage>
#include <QApplication>
#include <QBasicTimer>
#include <QPushButton>

class DrawThing : public QObject {
    Q_OBJECT
    int m_ctr;
    QBasicTimer t;
    void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() != t.timerId()) return;
        QImage img(128, 128, QImage::Format_RGB32);
        QPainter p(&img);
        p.translate(img.size().width()/2, img.size().height()/2);
        p.scale(img.size().width()/2, img.size().height()/2);
        p.eraseRect(-1, -1, 2, 2);
        p.setBrush(Qt::NoBrush);
        p.setPen(QPen(Qt::black, 0.05));
        p.drawEllipse(QPointF(), 0.9, 0.9);
        p.rotate(m_ctr*360/12);
        p.setPen(QPen(Qt::red, 0.1));
        p.drawLine(0, 0, 0, 1);
        m_ctr = (m_ctr + 1) % 12;
        emit newImage(img);
    }
public:
    explicit DrawThing(QObject *parent = 0) : QObject(parent), m_ctr(0) { t.start(1000, this); }
    Q_SIGNAL void newImage(const QImage &);
};

class MainWindow : public QMainWindow {
    Q_OBJECT
    QLabel *m_label;
public:
    explicit MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0) : QMainWindow(parent, flags) {
        QWidget * cw = new QWidget;
        QTabWidget * tw = new QTabWidget();
        QVBoxLayout * l = new QVBoxLayout(cw);
        l->addWidget(tw);
        l->addWidget(m_label = new QLabel("Label"));
        setCentralWidget(cw);
        QPushButton * pb = new QPushButton("Toggle Status Bar");
        tw->addTab(pb, "Tab 1");
        connect(pb, SIGNAL(clicked()), SLOT(toggleStatusBar()));
        statusBar()->showMessage("The Status Bar");
    }
    Q_SLOT void setImage(const QImage & img) {
        m_label->setPixmap(QPixmap::fromImage(img));
    }
    Q_SLOT void toggleStatusBar() {
        statusBar()->setHidden(!statusBar()->isHidden());
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QThread t;
    DrawThing thing;
    MainWindow w;
    thing.moveToThread(&t);
    t.start();
    w.connect(&thing, SIGNAL(newImage(QImage)), SLOT(setImage(QImage)));
    w.show();
    t.connect(&a, SIGNAL(aboutToQuit()), SLOT(quit()));
    int rc = a.exec();
    t.wait();
    return rc;
}

#include "main.moc"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • The code is way to big to be posted here, but i try to reproduce the error with minimal size. – András Kovács Aug 28 '13 at 13:55
  • That's the thing: once one starts working to minimize the code, one finds what the error is. Quickly, I might add. In every case. This saves everyone time, since one doesn't have to wait for someone else to figure it out :) – Kuba hasn't forgotten Monica Aug 28 '13 at 17:31
  • You need one file for the C++ code, and then if you use QML, you need one or more QML files. That's all. Don't bother with .h files or anything else. The code above requires, pretty much, putting it into a folder, running `qmake -project` followed by `qmake` and `make`. Can't get any simpler than that! – Kuba hasn't forgotten Monica Aug 28 '13 at 17:36