I have a window with many buttons. Each one triggers a sub-program (written using the Opencv API). Each sub-program displays images and stuff on windows.
The problem is, when I close these windows (via the little red cross), all the buttons become unclickable. So if I want to launch another program, I'll have to exit the main window and run it again.
In other words, I want to be able to run all the sub-programs without having to start over every time.
Here's the GUI's code :
.cpp
#include "fenprincipale.h"
#include "ui_fenprincipale.h"
#include<highgui.h>
#include<cv.h>
#include <moyenetmedian.h>
#include<morpho.h>
#include<tracking.h>
#include<contour.h>
#include<QApplication>
FenPrincipale::FenPrincipale(QWidget *parent) :
QWidget(parent),
ui(new Ui::FenPrincipale)
{
ui->setupUi(this);
MoyenEtMedian *moyenEtMedian = new MoyenEtMedian;
morpho * mor = new morpho;
tracking * tra= new tracking;
contour * cont= new contour;
QObject::connect(ui->bMoyMed, SIGNAL( clicked() ), moyenEtMedian, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bMorph, SIGNAL( clicked() ), mor, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bTrack, SIGNAL( clicked() ), tra, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bCont, SIGNAL( clicked() ), cont, SLOT( exec() ), Qt::AutoConnection );
}
FenPrincipale::~FenPrincipale()
{
delete ui;
}
.h :
#ifndef FENPRINCIPALE_H
#define FENPRINCIPALE_H
#include <QWidget>
#include <QApplication>
namespace Ui {
class FenPrincipale;
}
class FenPrincipale : public QWidget
{
Q_OBJECT
public:
explicit FenPrincipale(QWidget *parent = 0);
void switch_callback(int);
void execMoyMed (void);
~FenPrincipale();
private:
Ui::FenPrincipale *ui;
};
#endif // FENPRINCIPALE_H
the main class :
#include <QCoreApplication>
#include <QApplication>
#include <QtGui>
#include <QWidget>
#include "fenprincipale.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FenPrincipale fenetre;
fenetre.show();
return a.exec();
}
Slot implementation for "moyenetmedian" :
void MoyenEtMedian::exec(void)
{
const char* name = "Filtres";
IplImage* img = cvLoadImage( "C:/Users/XELTINFO/ProjetVision/image.png" );
IplImage* out = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 3 );
cvNamedWindow( name, 1 );
cvShowImage(name, out);
// Create trackbar
cvCreateTrackbar2( "Filtre", name, &g_switch_value, 1, &MoyenEtMedian::switch_callback, this );
while( 1 ) {
switch( filterInt ){
case 0:
cvSmooth( img, out, CV_BLUR, 7, 7 );
break;
case 1:
cvSmooth( img, out, CV_MEDIAN, 7, 7 );
break;
}
if(filterInt != lastfilterInt){
cvShowImage(name, out);
lastfilterInt = filterInt;
}
if( cvWaitKey( 15 ) == 27 )
break;
}
cvReleaseImage( &img );
cvReleaseImage( &out );
cvDestroyWindow( name );
}
The class declaration :
#ifndef MOYENETMEDIAN_H
#define MOYENETMEDIAN_H
#include "ui_fenprincipale.h"
#include<QObject>
class MoyenEtMedian : public QObject
{
Q_OBJECT
public:
MoyenEtMedian();
static void switch_callback(int position, void*);
public slots :
void exec(void);
};
#endif // MOYENETMEDIAN_H
The class delcarations and slots implementations are very similar for all classes. I'll add the rest if this isn't enough.
You are blocking the event loop in your exec() slot, since it doesn't return immediately. You should instead subclass QWidget and override keyPressEvent() to get keyboard input from Qt's event loop instead of doing the busy-loop you currently have.
So when using Qt with OpenCV, I would setup the polling using Qt's timers instead of a while loop.
There is a pretty good tutorial of using QTimers to interact with OpenCV objects here:
http://www.youtube.com/watch?v=0ONxIy8itRA
Jump to 35 or 38 minutes into it to see how he writes his classes.
Basically, you let Qt do the waiting and timing, instead of having a while loop with a wait call doing the timing.
And if possible, let Qt create the windows, and nest the OpenCV windows into Qt's windows so that Qt can manage the events on the windows.
Hope that helps.
Related
Let's say that you've created a new thread that then calls a static function after it has been started. Within that static function you need to create and display a custom qdialog. How can you create it so that it has no parent and is located in the proper thread?
The constructor sets the parent to 0 but it still reports an error about being unable to create children for a parent in a different thread. Since it's a static function I can't use the "this" object and without "this" I can't retrieve the current thread or thread id. I thought I might be able to call myCustomDialog->moveToThread() but I have no idea how to determine the correct thread from a static function.
If I use one of the QMessageBox static functions everything works fine. For example, calling QMessageBox::information(0, tr("Title"), tr("Message")) doesn't report any errors. How can I code my custom qdialog to function similar to the qmessagebox static function?
Is there any way to retrieve a list of all running threads from the qApp object? Any other suggestions?
static int myFunction();
int myObject::myFunction()
{
myCustomDialog *mcd = new myCustomDialog();
// as soon as I call exec() it reports an error and crashes
mcd->exec();
// QObject: Cannot create children for a parent that is in a different thread.
// can't call mcd->moveToThread() without knowing the current thread
// how can I determine the current thread from this static function?
// if parent = 0 then why is this error occurring?
return 0;
}
myCustomDialog(QWidget *parent = 0);
myCustomDialog::myCustomDialog(QWidget *parent) : QDialog(parent)
{
// create widgets and layout here
}
Don't. Creating QWidget objects from another thread is a bad idea. The Documentation states:
In GUI applications, the main thread is also called the GUI thread
because it's the only thread that is allowed to perform GUI-related
operations.
The way I normally get around this is to emit a signal from my object in the non-GUI thread that's connected to an object living in the main thread (often MainWindow for me). The receiving object then creates the dialog on the main thread.
As mentioned in the other answer, this connection can block your worker thread if needed by establishing the connection type as Qt::BlockingQueuedConnection.
For more information on invoking functions from other threads, see this post.
Sample... You may adapt it to make calls from static functions, but I don't think that it is necesary. It is good practice to work with threads in next way: You should create your dialog in GUI thread and connect it's exec() slot to your signal with Qt::BlockingQueuedConnection
Worker.h
#include <QObject>
class Worker
: public QObject
{
Q_OBJECT
signals:
void showDialog();
public:
Worker( QObject *parent = NULL );
~Worker();
public slots:
void doLongWork();
private:
};
Worker.cpp
#include <QThread>
#include <QMessageBox>
#include <QDebug>
#include <QCoreApplication>
#include <Windows.h>
Worker::Worker( QObject *parent )
: QObject( parent )
{
}
Worker::~Worker()
{
}
void Worker::doLongWork() // You may override QThread::run with same effect, but it is bad practice
{
qDebug() << "Worker thread id = " << QThread::currentThreadId();
::MessageBoxA( NULL, "Click to show dialog in 3 seconds", NULL, 0 );
::Sleep( 3000 );
emit showDialog(); // "Showing" dialog from non-GUI thread. And wait for close
::MessageBoxA( NULL, "Dialog closed!", NULL, 0 );
qApp->quit();
}
main.cpp
#include <QApplication>
#include <QDialog>
#include <QThread>
#include <QDebug>
#include "Worker.h"
int main(int argc, char *argv[])
{
QApplication a( argc, argv );
a.setQuitOnLastWindowClosed( false );
QDialog dlg;
QThread workerThread;
Worker worker;
qDebug() << "Main thread id = " << QThread::currentThreadId();
QObject::connect( &workerThread, SIGNAL( started() ), &worker, SLOT( doLongWork() ) );
QObject::connect( &worker, SIGNAL( showDialog() ), &dlg, SLOT( exec() ), Qt::BlockingQueuedConnection ); // !!!See connection type!!!
worker.moveToThread( &workerThread );
workerThread.start();
return a.exec();
}
Note: WinAPI MessageBoxes are used only to visualize that thread is really waiting for close of dialog.
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)
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.
//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"
I need to grab each QML (QtQuick 2) drawing frame and sent it over the network.
At the moment I have used method listed below, but this method has two big disadvantage
1) Due to Qt5 documentation grabWindow() function has performance issues
2) It can't work with hidden QML window
Is it possible to get OpenGL render buffer right after QQuickWindow::afterRendering ?
Using FBOs ? Shared opengl context ?
class Grab: public QObject
{
public:
Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
public slots:
void Grabme()
{
QImage image = wnd_->grabWindow();
}
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
&grab, &Grab::Grabme, Qt::DirectConnection );
return app.exec();
}
Example bellow can grab any qml content to FBO and then sent it as Image via signal.
Only one problem of this approach is visibility, grab window must be visible for successful grabbing. If anybody knows how to prevent this you can help me and provide more advanced approach.
// main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GrabWindow grab;
grab.setResizeMode( QQuickView::SizeViewToRootObject );
grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
grab.setFlags( Qt::Popup );
grab.show();
return app.exec();
}
// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
Q_OBJECT
signals:
void changeImage( const QImage &image );
public:
GrabWindow( QWindow * parent = 0 );
private slots:
void afterRendering();
void beforeRendering();
private:
QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
QQuickView( parent )
{
setClearBeforeRendering( false );
setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
connect( this, SIGNAL( afterRendering() ), SLOT( afterRendering() ), Qt::DirectConnection );
connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
if( !fbo_.isNull() )
{
emit changeImage( fbo_->toImage() );
}
}
void GrabWindow::beforeRendering()
{
if (!fbo_)
{
fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
setRenderTarget(fbo_.data());
}
}
I managed to find a trick to make grabWindow() work when the Window is "not visible". The trick is to set the window's visibility: Window.Minimized and the flags: Qt.Tool. The window is not displayed to the user, but to the Qt's internals it appears to be visible and the grabWindow() method call works as expected. Remember to call that method only once the scene has been initialised.
The only problem with this solution (that I have come across) is that if the window's color property is set to transparent, the captured content has black background.
With later versions of Qt 5.X you can also use the software render backend.
The following renders any scene in the background without any visible window or OpenGL tricks:
// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
const char *source = "qrc:/main.qml";
if (argc > 1) source = argv[1];
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app{argc, argv};
QQuickRenderControl renderControl;
QQuickWindow window{&renderControl};
QQmlEngine engine;
QQmlComponent component{
&engine,
QUrl{QString::fromUtf8(source)}
};
QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
window.contentItem()->setSize(rootItem->size());
rootItem->setParentItem(window.contentItem());
window.resize(rootItem->size().width(), rootItem->size().height());
QImage image = renderControl.grab();
image.save("output.png");
return 0;
}
I've got some problem with signal for a button in Qt 4.8.0. I am using vs 2010 with Qt Designer. I have created a button in Designer with playButton name. But after that I tried to connect clicked() signal (in vs) with my function from CRenderArea (to start a timer), but it seems that it doesn't work (the function start() works when I put it in the constructor, so it's not the problem of the code itself). The code is compiling, the program is executing, but after clicking a button - nothing happens (it should move a line).
I would be really thankful for some help, just started a fun with Qt.
The codes are here (I hope the number of files won't scare you, these are the simplest codes ever :) ):
main.cpp
#include "ts_simulator.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TS_simulator w;
w.show();
return a.exec();
}
ts_simulator.cpp:
TS_simulator::TS_simulator(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
p_map = new CRenderArea();
ui.setupUi( this );
p_map->setParent( ui.renderArea );
// this doesn't work, why?
connect( ui.playButton, SIGNAL( clicked() ), this, SLOT( p_map->start() ) );
}
CRenderArea.h
#pragma once
#include <QtGui>
class CRenderArea : public QWidget {
Q_OBJECT // I think it's necessary?
int x;
QBasicTimer* timer;
public:
CRenderArea();
public slots: // this is necessary too, right?
void start();
private:
void timerEvent( QTimerEvent* );
void paintEvent( QPaintEvent* );
};
and CRenderArea.cpp:
#include "CRenderArea.h"
CRenderArea::CRenderArea() : x( 0 ) {
setBackgroundRole( QPalette::Base );
setMinimumSize( 591, 561 );
setAutoFillBackground( true );
timer = new QBasicTimer();
}
void CRenderArea::timerEvent( QTimerEvent* e ) {
++x;
update();
}
void CRenderArea::paintEvent( QPaintEvent* p ) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::darkGray);
painter.drawLine(2+x/10, 8, 60, 300);
}
void CRenderArea::start() {
timer->start( 0, this );
}
Greets.
The problem is here:
connect( ui.playButton, SIGNAL( clicked() ), this, SLOT( p_map->start() ) );
if p_map is the receiver of the signal, and it has Q_OBJECT, it should be written as:
connect( ui.playButton, SIGNAL( clicked() ), p_map, SLOT(start()) );
I think I have some fundamental misunderstanding of how the Qt signal/slot mechanism works.
I have worked through example programs and they make sense but when I've tried to take those and modify them I have been getting results I do not understand. I have attach a code sample below that is a boiled-down version of what I was trying to do that certainly does not do what I want it to. Am I misusing the signal/slot mechanism and or the QString class? Is the way I am using the signal/slot to modify things in a coupled fashion creating an infinite loop? Any help greatly appreciated.
// test.cpp
#include <QApplication>
#include <QDialog>
#include <QLineEdit>
#include <QString>
#include <QVBoxLayout>
class myDialog : public QDialog
{
Q_OBJECT
public:
myDialog() : a_( new QLineEdit ), b_( new QLineEdit )
{
QVBoxLayout* layout( new QVBoxLayout( this ) );
layout->addWidget( a_ );
layout->addWidget( b_ );
connect( a_, SIGNAL( textChanged( const QString& ) ),
this, SLOT( aChanged( const QString& ) ) );
connect( b_, SIGNAL( textChanged( const QString& ) ),
this, SLOT( bChanged( const QString& ) ) );
}
private:
QLineEdit* a_;
QLineEdit* b_;
private slots:
void aChanged( const QString& qs );
void bChanged( const QString& qs );
};
#include "test.moc"
void myDialog::aChanged( const QString& qs )
{
b_->setText( QString::number( 2.0 * qs.toDouble() ) );
}
void myDialog::bChanged( const QString& qs )
{
a_->setText( QString::number( 3.3 * qs.toDouble() ) );
}
int main( int argc, char** argv )
{
QApplication a( argc, argv );
myDialog d;
d.show();
return a.exec();
}
Because In aChanged, you edit the b QLineEdit, it triggers the textChanged() signal for b...causing it to call bChanged, changing a..... etc. etc.
I think that is your problem here.
You might want to use textEdited() in stead.
Not so critical in this case, but please pay attention to delete a_ and b_ in the destructor since they dont have any parent.