I have this simple example and this program crashed when I tried to call context()->moveToThread(render_thread_);. Can anyone help?
class FrameRenderer: public QThread
{
Q_OBJECT
public:
FrameRenderer(QGLCanvas *parent):m_parent(parent), QThread(){}
void run() Q_DECL_OVERRIDE;
QGLCanvas *m_parent;
};
class QGLCanvas : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
QGLCanvas(QWidget* parent = NULL);
~QGLCanvas();
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
void DrawThreadEntry();
FrameRenderer* render_thread_;
};
void FrameRenderer::run()
{
m_parent->DrawThreadEntry();
}
QGLCanvas::QGLCanvas(QWidget* parent)
: QOpenGLWidget(parent)
{
render_thread_ = new FrameRenderer(this);
doneCurrent();
context()->moveToThread(render_thread_);
render_thread_->start();
}
void QGLCanvas::DrawThreadEntry()
{
while(true)
{
makeCurrent();
QOpenGLFunctions f;
f.initializeOpenGLFunctions();
f.glClearColor(1.0, 1.0, 1.0, 1.0);
f.glFinish();
doneCurrent();
emit update();
}
}
I don't know if it's the solution but i got a similar problem on iOS, the context was available but after a short period of time.
You can try to add a timmer and delay your call to OpenGL
.h
slots:
void update();
.cpp
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
If it's working maybe you should try look if you can do your move ToThread inside void QOpenGLWidget::initializeGL() which is responsible to set up the context
Related
I'm trying to figure out how I could work with Qt signals in a little bit different way.
I have a working example of such:
Game::Game(QObject *parent)
: QObject{parent}
, m_timer{nullptr}
{
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &Game::run);
m_timer->setinterval(300);
m_timer->start();
}
Above signaling works fine and calls the Game::run regularly as desired. Now I am trying to replicate this with this bypass (for the sake of learning and it could be useful for more complicated moments especially in unit testing)
Abstract class:
class ISignalHandler
{
public:
virtual void onTimeout() = 0;
protected:
virtual ~ISignalHandler() = default;
};
and another class MyTimer.cpp:
MyTimer::MyTimer(QTimer *parent)
: QTimer{parent}
{
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &MyTimer::onTimeout);
}
MyTimer::~MyTimer()
{
delete m_timer;
m_timer = nullptr;
}
void MyTimer::registerTimerSignal(ISignalHandler* callback)
{
m_callback = callback;
}
void MyTimer::onTimeout()
{
if (m_callback != nullptr)
{
m_callback->onTimeout();
}
}
and finally Game class:
//header:
class Game : public QObject, public ISignalHandler
{
Q_OBJECT
public:
explicit Game(QObject *parent = nullptr);
~Game();
//cpp:
Game::Game(QObject *parent)
: QObject{parent}
, m_timer{nullptr}
{
m_timer = new MyTimer();
m_timer->registerTimerSignal(this);
m_timer->setinterval(300);
m_timer->start();
}
void Game::onTimeout()
{
run();
}
I expected that the MyTimer class will repeat within itself, and the signal will be handled to Game class so ::run will be continuously called. Where is a mistake here?
I have followed this turorial Qt Creator + OpenGL and everything is going well.
I have added a QPlainTextEdit on top of the Quit button and I would like to have GLWidget send info back to MainWindow.
How can I make GLWidget call a function of the MainWindow?
Here is some code:
// GLWidget.h
class GLWidget: public QGLWidget
{
Q_OBJECT // <-----
public:
GLWidget(QWidget* parent = 0);
QWidget* parentObject;
void writeText( QString );
void (*p)(QString) = NULL;
protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int w, int h);
private:
QTimer timer;
signals:
//void writeText( QString );
void dataReady(QString data); // <------------------
};
// GLWidget.cpp
GLWidget::GLWidget(QWidget* parent) : QGLWidget() /7 <---- new error
{
connect(&timer, SIGNAL(timeout()), this, SLOT(updateGL()));
timer.start(16);
QMessageBox msgBox;
msgBox.setText(typeid(parent).name());
msgBox.exec();
//p = parent->
//parentObject->
//parent->
//parent->
//this->parentObject = parent;
}
void GLWidget::initializeGL(){
//emit GLWidget::dataReady("myData"); //<--------- error
emit this->dataReady("Joe");//<--------- same as above
glClearColor(0.2, 0.2, 0.2, 1);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
}
// MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void print(QString text);
private slots:
void on_plainTextEdit_textChanged();
void foo(QString data); // <----------------------
}
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->widget->p = &MainWindow::print; // error, how to asign pointer?
}
void MainWindow::print(QString text){
ui->plainTextEdit->setPlainText(text);
}
void MainWindow::foo(QString data) { // <---------
QMessageBox msgBox;
msgBox.setText("hi, there");
msgBox.exec();
// Do something with the data
ui->plainTextEdit->setPlainText(data);
}
error:
undefined reference to `vtable for GLWidget'
undefined reference to `GLWidget::dataReady(QString)'
Here is the answer to your immediate question, How to make a lower class exploit a function in it's parent?:
All QObject subclasses hold a reference to their parent object, accessible through the parent() method.
Include the header of the parent's class, e.g Foo.h
Call the parent() methodd to obtain a pointer to the object's parent
Check if the pointer is valid (not a nullptr)
Cast the pointer to the parent's class, e.g. Foo
Call the desired method, e.g. foo
In code it looks like this:
#include "Foo.h"
...
if (parent())
static_cast<Foo *>(parent())->foo();
That being said, this creates a tight coupling between the parent and the child and I strongly advise to avoid doing it if possible. In your case would be much better to use signals and slots to communicate the data from the child to the parent:
Define a signal in the child, e.g. dataReady(DataType data)
In the parent, when you create the child, connect this signal to the method, which will use the data, e.g. foo
In the child, emit the signal, whenever you feel like calling foo
In code it looks like this:
Child.h
...
signals:
void dataReady(DataType data);
...
Child.cpp
...
// I want to call foo
emt dataReady(myData);
...
Parent.h
...
private slots:
void foo(DataType data);
...
Parent.cpp
...
//somewhere
auto *child = new Child(...);
connect(child, &Child::dataReady, this, &Parent::foo);
...
void Parent::foo(DataType data) {
// Do something with the data
...
}
...
This requires more work initially, but it is much better in a long-term.
when i executing this Qtimer it says "invalid use of 'this' in non-member function"
QTimer *timerStart( )
{
QTimer* timer = new QTimer( );
Ball *b = new Ball();
QObject::connect(timer,SIGNAL(timeout()),b,SLOT(move()));
//timer->start( timeMillisecond );
timer->start(15);
return timer;
}
my ball.h file
class Ball: public QObject, public QGraphicsRectItem{
Q_OBJECT
public:
// constructors
Ball(QGraphicsItem* parent=NULL);
// public methods
double getCenterX();
public slots:
// public slots
void move();
private:
// private attributes
double xVelocity;
double yVelocity;
int counter = 0;
QTimer timerStart( );
// private methods
void stop();
void resetState();
void reverseVelocityIfOutOfBounds();
void handlePaddleCollision();
void handleBlockCollision();
};
#endif // BALL_H
the move() function is in the same class. what i want to do is stop the returned timer upon a if condition is satisfied.
when i issue this code in Ball::Ball constructor in Cpp it works fine. the ball is moving.
QTimer* timer = new QTimer();
timer->setInterval(4000);
connect(timer,SIGNAL(timeout()),this,SLOT(move()));
timer->start(15);
but when i add Qtimer *timerStart beyond the Ball::Ball constructor, iT doesnt work
Declare the QTimer as a member in you class
h file:
class Ball: public QObject, public QGraphicsRectItem{
{
Q_OBJECT
public:
// constructor
Ball(QGraphicsItem* parent=Q_NULLPTR);
// control your timer
void start();
void stop();
...
private:
QTimer * m_poTimer;
}
Initiate the timer object in your constractor
cpp file:
Ball::Ball(QGraphicsItem* parent) :
QGraphicsItem(parent)
{
m_poTimer = new QTimer(this); // add this as a parent to control the timer resource
m_poTimer->setInterval(4000);
connect(m_poTimer,SIGNAL(timeout()),this,SLOT(move()));
}
void Ball::start()
{
// start timer
m_poTimer->start();
}
void Ball::stop()
{
// stop timer
m_poTimer->stop();
}
I'm facing a problem using QTimer. The program closes with a segmentation fault in run time and when I exclude the "timer" from code, it runs properly. Here's the code:
Class A : public QObject, public QGraphicsPixmapItem{
Q_OBJECT
public:
A(QPixmap pic){
this->setPixmap(pic);
}
void start(){
timer = new QTimer();
timer->start(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(moveObject()));
}
public slots:
void moveObject(){
moveTimer = new QTimer();
moveTimer->start(20);
connect(moveTimer, SIGNAL(timeout()), this, SLOT(changePosition()));
}
void changePosition(){
//a couple of things are done here
}
private:
QTimer *timer;
QTimer *moveTimer;
}
Class B : QGraphicsView{
Q_OBJECT
public:
B(QWidget *parent = 0) : QGraphicsView(parent){
a = new A(QPixmap("a.png"));
void go(){
a->start();
}
private:
A *a;
}
P.S. I delete moveTimer in changePosition as long as the object stops moving and recall moveObject so that it can move towards the next target.
I am trying to implement Signal and Slot system between the main gui and another object moved to another thread...the following is how the class design looks like...unfortunately cannot implement it...
MainWindow.h
signals:
void StopDisplayWidget();
void StartDisplayWidget();
void signalFromGUI();
private slots:
void on_pushButton_start_display_clicked();
void on_pushButton_stop_display_clicked();
void on_pushButton_check_clicked();
private:
Ui::MainWindow *ui;
displaythread *threadforDisplay;
display *displayWidget;
QThread *WorkerDisplay;
MainWindow.cpp
{
threadforDisplay = new displaythread;
threadforDisplay->setptr2display(displayWidget);
WorkerDisplay = new QThread;
QObject::connect(WorkerDisplay,SIGNAL(started()),threadforDisplay,SLOT(Process()));
QObject::connect(this,SIGNAL(StartDisplayWidget()),threadforDisplay,SLOT(StartDisplay()));
QObject::connect(this,SIGNAL(StopDisplayWidget()),threadforDisplay,SLOT(StopDisplay()));
QObject::connect(this,SIGNAL(signalFromGUI()),threadforDisplay,SLOT(Check()));
threadforDisplay->moveToThread(WorkerDisplay);
}
void MainWindow::on_pushButton_start_display_clicked()
{
if(!threadforDisplay->IsDisplayActive())
emit this->StartDisplayWidget();
if(!WorkerDisplay->isRunning())
WorkerDisplay->start();
}
void MainWindow::on_pushButton_stop_display_clicked()
{
if(threadforDisplay->IsDisplayActive())
{
emit this->StopDisplayWidget();
}
}
void MainWindow::on_pushButton_check_clicked()
{
std::cout<<"CHECKING SIGNAL SLOT"<<std::endl;
emit this->signalFromGUI();
}
threadforDisplay is a pointer to displaythread class which looks like
displaythread.h
#include <QObject>
#include <QWaitCondition>
#include <QMutex>
#include "display.h"
class displaythread : public QObject
{
Q_OBJECT
public:
explicit displaythread(QObject *parent = 0);
bool IsDisplayActive() const;
void setptr2display(display *);
signals:
public slots:
void Process();
void StartDisplay();
void StopDisplay();
void Check();
private:
void SleepThread();
volatile bool stopped,running;
QMutex mutex;
QWaitCondition waitcondition;
display *displayinGUI;
displaythread.cpp
void displaythread::setptr2display(display *ptr)
{
displayinGUI = ptr;
}
void displaythread::Process()
{
std::cout<<"RECEIVED START PROCESS SIGNAL"<<std::endl;
running = true;
while(true)
{
if(!stopped)
{
displayinGUI->update();
this->SleepThread();
}
}
}
void displaythread::SleepThread()
{
mutex.lock();
waitcondition.wait(&mutex,20);
mutex.unlock();
}
void displaythread::StartDisplay()
{
std::cout<<"RECEIVED START SIGNAL"<<std::endl;
stopped = false;
running = true;
}
void displaythread::StopDisplay()
{
std::cout<<"RECEIVED STOP SIGNAL"<<std::endl;
stopped = true;
running = false;
}
bool displaythread::IsDisplayActive() const
{
return running;
}
void displaythread::Check()
{
std::cout<<"SIGNAL FROM GUI RECEIVED"<<std::endl;
}
display.h
class display : public QWidget
{
Q_OBJECT
public:
explicit display(QWidget *parent = 0);
~display();
signals:
public slots:
private:
void paintEvent(QPaintEvent *);
IplImage *image_opencvBGR,*image_opencvRGB;
QImage image;
CvCapture *webcam;
display.cpp
display::display(QWidget *parent) :
QWidget(parent)
{
image_opencvRGB = cvCreateImage(cvSize(640,480),8,3);
webcam = cvCaptureFromCAM(-1);
}
display::~display()
{
cvReleaseCapture(&webcam);
}
void display::paintEvent(QPaintEvent *)
{
//std::cout<<"IN PAINT LOOP"<<std::endl;
image_opencvBGR = cvQueryFrame(webcam);
cvCvtColor(image_opencvBGR,image_opencvRGB,CV_BGR2RGB);
image = QImage((const unsigned char*)image_opencvRGB->imageData,image_opencvRGB->width,image_opencvRGB->height,QImage::Format_RGB888);
QRectF target(0.0,0.0,image.width(),image.height());
QRectF source(0.0,0.0,image.width(),image.height());
QPainter painter(this);
painter.drawImage(target,image,source);
}
OUTPUT :
RECEIVED START PROCESS SIGNAL
However except the Process slot no other slot is working when signals are emitted from the main gui i.e. MainWindow..is it due to movetoThread command? Donno where i am going wrong..
The answer is simple : Qwidgets don't work outside the main thread. So you cannot execute GUI code using displaythread.
Furthermore your while loop may cause issues (I know the variable is volatile but I dont have the time to analyze properly if it is correct)
See the documentation for more information.
ps: It seems you are overdoing things. Rework your whole design. GUI operations are in the main thread. Use threads for computations only. If the communication between thread and access to their variables use signal and slots only, you dont need locking mechanisms.