Qt application crashes after call to removeItem() in QGrahicsScene - c++

I'm facing a problem I cannot explain yet. In the "Designer" GUI there is a QGraphicsView which I use as a placeholder for a custom GraphicsView. I have overridden the resizeEvent() method there and set the scene's rect.
The scene which is a custom class, too I'd like to use to display a graph.
The problem: Sometimes when starting the application it crashes and sometimes it doesn't. This behavior does not follow a rule, i.e. sometimes I start it ten times before crashing sometimes three times and so on.
I took a screenshot of the call stack but seems like I am not capable of understanding the problem. Hope someone of you does.
First things first, here is the code:
mainwindow.cpp
graph = new GraphScene(this);
ui->gv_graph->setScene(graph);
graph->addAxisLabels("P", "t");
graphicsview.cpp
GraphicsView::GraphicsView(QWidget *parent)
:QGraphicsView(parent)
{
}
void GraphicsView::resizeEvent(QResizeEvent *event)
{
if(GraphScene *s = dynamic_cast<GraphScene*>(scene()))
s->updateSceneRect(QRectF(QPointF(0,0), event->size()));
}
graphscene.cpp
const float GraphScene::margin = 20.0f;
GraphScene::GraphScene(QObject *parent)
:QGraphicsScene(parent)
{
}
void GraphScene::updateSceneRect(const QRectF &rect)
{
setSceneRect(rect);
if(_frameRect)
removeItem(_frameRect);
_frameRect = new QGraphicsRectItem;
_frame.setTopLeft(QPointF(margin, margin));
_frame.setWidth(rect.width()-2*margin);
_frame.setHeight(rect.height()-2*margin);
_frameRect->setRect(_frame);
addItem(_frameRect);
}
void GraphScene::addAxisLabels(const QString& xLabel, const QString& yLabel)
{
if(_xLabel)
removeItem(_xLabel);
if(_yLabel)
removeItem(_yLabel);
_xLabel = new QGraphicsSimpleTextItem(xLabel);
_xLabel->setX(margin+1);
_xLabel->setY(margin+1);
addItem(_xLabel);
//_yLabel = new QGraphicsSimpleTextItem;
}
So as what I see from the call stack the problem occurs when calling removeItem() in one of the both functions above.
The screenshot:
Environment information:
Can someone provide some help, hints or whatever. This would be very much appreciated. Thank you!

Related

Is there any better way to move a window?

I am working on an application with Qt Framework for desktop. Since I remove every window decoration I had to implement the main window to receive move event from when the user click on it and move the mouse around.
I tried the following code but I am not satisfied. I wonder if there is any better way to this with more elegance.
QPoint* mouseOffset; //global variable that hold distance of the cursor from
the top left corner of the window.
void ArianaApplication::mouseMoveEvent(QMouseEvent* event)
{
move(event->screenPos().x() - mouseOffset->x(),
event->screenPos().y() - mouseOffset->y());
}
void ArianaApplication::mousePressEvent(QMouseEvent*)
{
mouseOffset = new QPoint(QCursor::pos().x() - pos().x(),
QCursor::pos().y() - pos().y());
}
Would you suggest me something else?
The method is correct, but the implementation can be improved in the following points:
mouseOffset is not necessary to be a pointer, since you are creating dynamic memory unnecessarily and you have the responsibility to eliminate it.
It is not necessary to obtain each component, QPoint supports subtraction.
*.h
QPoint mouseOffset;
*.cpp
void ArianaApplication::mouseMoveEvent(QMouseEvent * event)
{
move(event->globalPos() - mouseOffset);
}
void ArianaApplication::mousePressEvent(QMouseEvent * event)
{
mouseOffset = event->globalPos() - frameGeometry().topLeft();
}

qt5 QGraphicsScene setBackgroundBrush() not always work when called frequently

I want to write an tool of objective annotation using qt5 MinGw32, which could annotate object in video file and diplay them during playing. So QGraphicsScene is inherited to implementation the function.
Something wrong happens when I change the QGraphicsScene's background frequently(e.g. 30 fps): most of time it works as expected while sometimes the background could not move.
Here is my code:
void MyGraphicsScene::UpdateFrame(QImage image)
{
QPixmap pixmap = QPixmap::fromImage(image);
//fix the view's size and scene's size
views().first()->setFixedSize(pixmap.size());
setSceneRect(0,0, pixmap.width(), pixmap.height());
setBackgroundBrush(QBrush(pixmap));
}
...
//In another thread
void Thread::run()
{
...
myScene.UpdateFrame(newImage);
...
}
I have search through the qt's document and found no answer.
However, there is something strange:
when wrong thing happens, I find the background continues to change, but it didn't show change on the screen unless I move the app to another screen (I have two screen). However, with the app moved, the QGraphicsScene's background just change once and becomes static afterwards.
I guess the background has been changed but doesn't repainted, so I used update(), but it didn't help.
BTW, I couldn't reproduce the occasion, sometiems it happens, somtimes not.
do I need to represented any methods? Or I called the methods in a wrong way? Or is there an alternative approach that would work?
Many thanks for your help in advance.
You should not change QtGui elements from a different thread by calling a method directly.
Use the Qt signal-slot concept.
Calling update() is not necessary.
class MyGraphicsScene{...
....
signals:
void singalFromThread(QImage image);
public:
//CTor
MyGraphicsScene()
{
connect(this, SIGNAL(singalFromThread(QImage)),this,SLOT(UpdateFrame(QImage)));
}
//Call this method from your thread
void updateForwarder(QImage image)
{
//Jump into gui thread
emit singalFromThread(image);
}
public slots:
void UpdateFrame(QImage image)
{
setBackgroundBrush(....);
}
};

QML OpenGL plugin not redrawing at 60Hz

The Situation
My company has a QML-based application which displays some content using a custom OpenGL-based render plugin (MyGame). This plugin has a few critical needs:
To be able to effect changes in the renderer in response to QML-based signals.
(e.g. change the position of an object rendered by the game)
To only process these changes at a specific spot in MyGame's redraw loop.
(This is very important; MyGame is very sensitive about when changes are allowed.)
To have the plugin redraw at 60Hz (at least).
The Problem
The code we have right now honors (1) and (2), but fails (3); the plugin does not get visually updated consistently. (The updates are erratic, at an estimated 5-10Hz.) I believe that the plugin we have created—based on QQuickFramebufferObject—is not taking proper advantage of how Qt/QML intended the scene graph to be updated.
How can I re-structure my plugin so that I get all three of the above?
The Code
Overview:
The plugin creates a QQuickFramebufferObject (MyPlugin) and a QQuickFramebufferObject::Renderer (MyRenderer).
When MyRenderer::render() is called it calls MyGame::Redraw() itself, and then calls update().
MyGame::Redraw() does what it needs to, and at the right spot where changes can be accepted, emits a timeToMakeChanges QML signal on MyPlugin.
QML listens for the onTimeToMakeChanges signal and invokes methods on the plugin that affect MyGame.
To workaround the problem of low-frequency visual updates, I've found that if I overlay a QML Canvas over my plugin and redraw the canvas frequently using a Timer, my plugin starts to get visually updated at what appears to be around 60Hz. Clearly this is a gross hack.
Following is a summary of the code setup. Please forgive missing/incorrect code; I'm trying to distill thousands of lines of glue code down to the essentials for this question.
MyPlugin.h
#include <QOpenGLFramebufferObject>
#include <QQuickFramebufferObject>
class MyPlugin : public QQuickFramebufferObject {
Q_OBJECT
public:
MyPlugin();
virtual ~MyPlugin();
virtual QQuickFramebufferObject::Renderer* createRenderer() const;
signals:
void timeToMakeChanges();
public slots:
void makeChanges(QVariant inValue);
void HandleWindowChanged(QQuickWindow *inWindow);
private:
MyGame* GetGame() { ... }
};
MyPlugin.cpp
#include "MyPlugin.h"
#include <MyGame.h>
// ******************************************************************
class MyRenderer:
public QObject,
public QQuickFramebufferObject::Renderer,
protected QOpenGLFunctions
{
Q_OBJECT
public:
virtual void render();
private:
static void RequestGameChanges();
};
void MyRenderer::render() {
if ( !m_Initialized ) {
QOpenGLFramebufferObject *theFbo = this->framebufferObject();
InitializeGl( theFbo ); // Not shown
m_MyGame = &MyGame::Create();
m_MyGame->RegisterCallback(
reinterpret_cast<qml_Function>(MyRenderer::RequestGameChanges)
);
m_Initialized = true;
}
m_MyGame->RestoreState();
m_MyGame->Redraw();
m_MyGame->SaveState();
m_PluginItem->window()->resetOpenGLState();
// Tell QML that we want to render again as soon as possible
update();
}
// This gets invoked in the middle of m_MyGame->Redraw()
void MyRenderer::RequestGameChanges() {
emit m_PluginItem->timeToMakeChanges();
}
// ******************************************************************
MyPlugin::MyPlugin() {
setMirrorVertically(true);
connect(
this, SIGNAL(windowChanged(QQuickWindow*)),
this, SLOT(HandleWindowChanged(QQuickWindow*))
);
}
void MyPlugin::HandleWindowChanged(QQuickWindow *inWindow) {
inWindow->setClearBeforeRendering(false);
}
void MyPlugin::makeChanges(QVariant inValue) {
MyGame *theGame = GetGame();
// Send the requested changes to theGame
}
QQuickFramebufferObject::Renderer* MyPlugin::createRenderer() const {
m_Renderer = new MyRenderer( *this );
}
MyApp.qml
import MyPlugin 1.0
Window {
MyPlugin {
property var queuedUpChanges: ([])
onSomeOtherSignal: queueUpChangesToMake();
onTimeToMakeChanges: makeChanges( queuedUpChanges );
}
Canvas { id:hack }
Timer {
interval:10; running:true; repeat:true
onTriggered: hack.changeWhatYouShow();
}
}
Bonus Points
The main question is "How do I modify my code so that I get 60Hz updates?" However, as seen in the QML, the setup above requires me to queue up all changes in QML so that they are able to be applied during the right spot in the MyGame::Render().
Ideally, I'd prefer to write QML without timeToMakeChanges, like:
MyPlugin {
onSomeOtherSignal: makeChanges( ... );
}
If there's a way to accomplish this (other than queuing up the changes in C++ instead)—perhaps something related to synchronize() I'd love to know about it.
I'd make a timer in QML that calls the makeChanges regularly. But store all the state in MyPlugin. Then, in Renderer::synchronize(), copy from MyPlugin to MyRenderer, so it can be used by the MyGame.
(although, I wouldn't do any gamelogic-related calculations in QML ever in the first place)

Qt paintEvent crashes

Im trying to draw simple board on my widget.
When I'm trying to automatize this, my paintEvent crashes. I think it is caused by for loop inside, am I right? How to paint it in other way?
void Widget::paintEvent(QPaintEvent *event)
{
QPixmap myPix( QSize(20,20) );
QPainter painter(this);
for(int i = 0; i < 100; i+5){
painter.drawLine(QPointF(i,0),QPointF(i,max));
}
this->setPixmap(myPix);
}
Your for loop is incorrect and causes the program crash (I'm sure that's not your fault here). It should be written like this:
for(int i = 0; i < 100; i+=5){
p.drawLine(QPointF(i,0),QPointF(i,max));
}
i.e. with an assignment of the increment. This way it will do the job and finish properly.
On a side note, I would suggest to use drawPixmap() instead of setPixmap(). But setPixmap() will not cause infinite recursion and for example next code works properly.
//...
this->setPixmap(QPixmap("G:/2/qt.jpg"));
QLabel::paintEvent(event);
Why? With this approach infinite recursion is never produced (see here):
If you call repaint() in a function which may itself be called from
paintEvent(), you may get infinite recursion. The update() function
never causes recursion.
Indeed setPixmap() calls update(), not repaint(). To prove that see source code:
setPixmap source:
void QLabel::setPixmap(const QPixmap &pixmap)
{
Q_D(QLabel);
//...
d->updateLabel();//what it does?
}
updateLabel source:
void QLabelPrivate::updateLabel()
{
Q_Q(QLabel);
//...
q->updateGeometry();
q->update(q->contentsRect());//not repaint
}
As I said it is not a mistake but I think that it will be better if you will do all what you need with QPainter.

Reimplemented resizeEvent() is not called

Please check the following implementation:
BoardView.h
#include <QGraphicsView>
class BoardView : public QGraphicsView
{
protected:
void resizeEvent(QResizeEvent* event);
public:
BoardView();
};
BoardView.cpp
BoardView::BoardView()
{
}
void BoardView::resizeEvent(QResizeEvent* event)
{
QGraphicsView::resizeEvent(event);
double wd = width();
double ht = height();
double min_wh = min(wd, ht);
qDebug() << min_wh;
QTransform transform;
transform.scale(min_wh / Options::getMainBoardMinSize(), min_wh / Options::getMainBoardMinSize());
setTransform(transform);
}
The above two files are directly copied from an existing project, say P1. The purpose of this code is to handle resizeEvent() and scale the view according to widget size.
The problem is, this works exactly as it should in project P1. Everything is scaled as expected. But resizeEvent() is not called from the new project at all, when the view is resized by resizing main window, on which it is a widget.
The BoardView object does everything else as expected. It shows the scene. It captures and passes mouse events to the scene etc. Only the resizeEvent() is not called.
What am I doing wrong?
I'm using Qt Creator 2.0.1, Qt 4.7 32 bit on Windows 7 Ultimate 32 bit.
Hmm... Are you missing some code here?
Or your class declaration is incorrect - you should change it to
class BoardView : public QGraphicsView
{
Q_OBJECT
...
public:
BoardView(QWidget* parent);
};
...
BoardView::BoardView(QWidget* parent):QGraphicsView(parent)
{
}
Sorry if this is omitted by you because of triviality, but I don't know your QT experience level. This might be why QT events works incorrectly with your class...
Why a BoardView::resizeEvent() is not called, yet the QGraphicsView was showing the QGraphicsScene correctly? The reason is a silly mistake, instead of
BoardView _board_view;
I wrote
QGraphicsView _board_view;
Sorry for posting such a silly question!