Qt QGraphicsScene addItem() segmentation fault - c++

In Game::Game i call shoot() who create and add to scene NormalBullet object.
#include "game.h"
#include <QDebug>
#include "dirtblock.h"
#include "normalbullet.h"
#include <QTimer>
Game::Game(QGraphicsView *parent)
: QGraphicsView(parent)
{
//set scene
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 500, 500);
setFixedSize(500, 500);
setScene(scene);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//set Player
player = new PlayerTank();
scene->addItem(player);
//set dirtblock test
DirtBlock * db = new DirtBlock();
db ->setPos(100, 100);
scene->addItem(db);
NormalBullet *bl = new NormalBullet();
bl->shot(QPointF(300,300), 90);
}
I have segmentation fault (signal name: SIGSEGV) when i try add to scene object NormalBullet *bullet in NormalBullet::shoot. I create 'test' variable to see on debuger what scene() return and turn out its 0x0. When i try get acess to scene by game->scene (test2), i have also segmentation fault. Why scene not return proper pointer?
#include "normalbullet.h"
#include "game.h"
#include "QTimer"
#include "dirtblock.h"
#include <QGraphicsScene>
extern Game * game;
NormalBullet::NormalBullet(QGraphicsPixmapItem *parent)
{
setPixmap(QPixmap(":/images/NormalBullet.png"));
damage = 10;
speed = 10;
}
void NormalBullet::shot(QPointF coord, qreal angle)
{
//create new bullet, setPos, add to scene and connect to timer
NormalBullet * bullet = new NormalBullet();
bullet->setPos(coord);
bullet->setRotation(angle);
//scene()->addItem(bullet);
auto test = scene(); //return 0x0
//auto test2 = game->scene; //<= segmentation fault
QTimer *tim = new QTimer();
connect(tim, &QTimer::timeout, bullet, &NormalBullet::move);
tim->start(200);
scene()->addItem(bullet);
}
#ifndef GAME_H
#define GAME_H
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include "playertank.h"
#include<QObject>
class Game : public QGraphicsView
{
Q_OBJECT
public:
Game(QGraphicsView *parent = nullptr);
void keyPressEvent(QKeyEvent *event) override;
QGraphicsScene *scene;
PlayerTank *player;
private:
};
#endif // GAME_H
For some reason I dont have access to scene pointer in NormalBullet and otcher class, except Game class.

Related

how do I a QEvent::RequestSoftwareInputPanel event in my code

I am using an embedded platform to code using the Qt framework not Qt creator but it causes problems when I try to run my code I just set the size of my screen and it cause my player character to no longer take any inputs anymore I looked up the problem on Qt website and I found what I need to do use the QEvent::requestsoftwareinputpanel but I don't know how to implement it.
I tried to replace the original code that took inputs but it kept breaking when I tried it
here is my code
this is the main file
#include <QApplication>
#include <QGraphicsScene>
#include "Rect.h"
#include <QGraphicsView>
int main(int argc, char *argv[]){
QApplication a(argc,argv);
//how to make a screen
QGraphicsScene * scene1 = new QGraphicsScene();
// an item to put into the scene
Rect * player = new Rect();
player->setRect(0,0,100,100);
// focus on item
player->setFlag(QGraphicsItem::ItemIsFocusable);
player->setFocus();
//add item to the scene
scene1->addItem(player);
//how to actually see the scene
QGraphicsView * view = new QGraphicsView(scene1);
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// setting screensize
scene1->setSceneRect(0,0,800,600);
view->setBaseSize(800,600);
view->show();
//setting player pos
player->setPos(view->width()/2,view->height() - player->rect().height());
return a.exec();
}
the header file for the movment
//
// Created by krris on 2022-12-07.
//
#ifndef GALAGA_RECT_H
#define GALAGA_RECT_H
#include <QGraphicsRectItem>
#include <QEvent>
class Rect: public QGraphicsRectItem{
void keyPressEvent(QKeyEvent * event);
};
#endif //GALAGA_RECT_H
the file with the code for the movment
//
// Created by krris on 2022-12-07.
//
#include "Rect.h"
#include <QKeyEvent>
#include <QGraphicsScene>
#include "Bullet.h"
#include <QEvent>
void Rect::keyPressEvent(QKeyEvent * event){
if (event->key() == Qt::Key_Left){
QEvent::RequestSoftwareInputPanel;
setPos(x()-10,y());
}
else if (event->key() == Qt::Key_Right){
setPos(x()+10,y());
}
else if (event->key() == Qt::Key_Space){
// create a bullet
Bullet * bullet = new Bullet();
bullet->setPos(x(),y());
scene()->addItem(bullet);
}
}

Issue moving an QGraphicsItem in a custom QGraphicsView

I am having issues moving a QGraphicItem in a custom QGraphicView class. What I would like to be able to to do is select the item by a left mouse click and then move it to where I've done a right mouse click.
I stongly suspect that my problem is that QGraphicsItem::setPos() requires the coordinates to be in parent coordinates, and I'm unsure which for of QMouseEvent::*Pos() to use, and how to convert it to parent coordinates.
Screens shots of what is happening, versus what I what follow the code.
main.cpp: (simple main here, standard test harness)
#include "QtTest.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtTest w;
w.show();
return a.exec();
}
QtTest.h: (This defines the main application window)
#pragma once
#include <QtWidgets/QMainWindow>
class QGraphicsView;
class QGraphicsScene;
class QGraphicsItem;
class QMouseEvent;
class QtTest : public QMainWindow
{
Q_OBJECT
public:
QtTest(QWidget *parent = Q_NULLPTR);
private:
QGraphicsView* m_gv;
QGraphicsScene* m_pScene;
void setupUI();
};
QtTest.cpp: (implementation of the main application window)
#include "QtTest.h"
#include "testGV.h"
#include <QVariant>
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMainWindow>
#include <QMouseEvent>
#include <QWidget>
QtTest::QtTest(QWidget *parent): QMainWindow(parent)
{
setupUI();
}
void QtTest::setupUI()
{
QWidget *centralWidget;
if (objectName().isEmpty())
setObjectName("QtTestClass");
resize(600, 400);
centralWidget = new QWidget(this);
centralWidget->setObjectName("centralWidget");
m_gv = new testGV(centralWidget);
m_gv->setObjectName("graphicsView");
m_gv->setGeometry(QRect(100, 10, 441, 331));
setCentralWidget(centralWidget);
}
testGV.h: (definition of custom widget)
#pragma once
#include <QGraphicsView>
#include <QGraphicsItem>
class testGV : public QGraphicsView
{
Q_OBJECT
public:
testGV(QWidget* parent);
protected:
void mousePressEvent(QMouseEvent*);
private:
QGraphicsScene* m_pScene;
QGraphicsItem* m_pItem;
void createScene();
};
testGV.cpp: (implementation of custom widget)
#include "testGV.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMouseEvent>
testGV::testGV(QWidget* parent) : QGraphicsView(parent)
{
createScene();
}
void testGV::createScene()
{
m_pScene = new QGraphicsScene();
m_pScene->addRect(QRect(30, 30, 150, 150), QPen(Qt::black), QBrush(Qt::black, Qt::NoBrush));
QGraphicsEllipseItem* pTemp = m_pScene->addEllipse(QRect(0, 0, 15, 15), QPen(Qt::black), QBrush(Qt::red, Qt::SolidPattern));
pTemp->setFlag(QGraphicsItem::ItemIsMovable);
pTemp->setFlag(QGraphicsItem::ItemSendsGeometryChanges);
setScene(m_pScene);
}
void testGV::mousePressEvent(QMouseEvent* pEvent)
{
if (pEvent->button() == 1) // left button click
{
m_pItem = itemAt(pEvent->pos());
}
else if (pEvent->button() == 2) // right button click
{
m_pItem->setPos(pEvent->pos());
m_pScene->update();
}
}
The image on the left is the initial display, when I right click on the red dot and then click in the square at about where the black dot is I get the image on the right. What I'm after is the red dot moving to where I clicked.
The cause of the problem is that the QMouseEvent has the position information in the coordinates of the view but the item uses the coordinates of the scene. The solution in that case is to map the coordinates of the view to the coordinates of the scene:
void testGV::mousePressEvent(QMouseEvent* pEvent)
{
if (pEvent->button() == Qt::LeftButton)
m_pItem = itemAt(pEvent->pos());
else if (pEvent->button() == Qt::RightButton)
if(m_pItem)
m_pItem->setPos(mapToScene(pEvent->pos()));
}
But even so there is a problem, the position for your items is with respect to the topLeft so the displacement will have an error, if you want to avoid that deviation then you must consider the position of the left click:
void testGV::mousePressEvent(QMouseEvent* pEvent)
{
if (pEvent->button() == Qt::LeftButton){
m_pItem = itemAt(pEvent->pos());
m_pItem->setData(0, mapToScene(pEvent->pos()));
}
else if (pEvent->button() == Qt::RightButton)
if(m_pItem){
QPointF p = m_pItem->data(0).toPointF();
QPointF sp = mapToScene(pEvent->pos());
m_pItem->setPos(m_pItem->pos() + sp - p);
m_pItem->setData(0, sp);
}
}
Note: When a pointer is created it points to any memory location so that can cause problems so I recommend initializing it to nullptr and also checking if the pointer is valid:
testGV::testGV(QWidget* parent) : QGraphicsView(parent), m_pItem(nullptr)
{
createScene();
}

Qt3D Crashing when removing frame graph

I'm trying to create an application using Qt3D that where I can create multiple view windows on the same scene. I started with the code from the Qt3DWindow and the Simple C++ Example and started moving things around. What I figured is that each view window would define its own frame graph (just using a simple QForwardRenderer for now) and camera and then I would add each window's frame graph to the main frame graph in my scene.
Everything appears to be working fine as I create multiple windows, but when I close the windows and start removing frame graphs, the application crashes. It's crashing on a background thread somewhere down in the Qt3DCore or Qt3DRender module and I can't get to the source code. As I understand it I should be able to modify the frame graph dynamically at run time, but is that not thread safe? Are you expected to wholesale replace one frame graph with another as opposed to modifying the active frame graph like I'm doing?
--- Edit ---
I did a little more testing and if I delay destroying the QWindow (i.e., the surface that it's trying to render to) a bit after removing its frame graph from the parent frame graph, I don't get the crash. I do however get some warnings on the console that say:
Qt3D.Renderer.Backend: bool __cdecl Qt3DRender::Render::GraphicsContext::makeCurrent(class QSurface *) makeCurrent failed
My guess is it's a threading issue, that the backend is still trying to use the QSurface to render to after it has been destroyed on the main thread. I don't really like my solution (I just used a single shot timer to delay destroying the window by 1 second), but it's better than crashing.
RenderWindow.h
#ifndef RENDERWINDOW_H
#define RENDERWINDOW_H
#include <QWindow>
#include <Qt3DCore>
#include <Qt3DRender>
#include <Qt3DInput>
#include <Qt3DExtras/QForwardRenderer>
class RenderWindow : public QWindow
{
public:
RenderWindow(QScreen* screen = nullptr);
~RenderWindow();
Qt3DRender::QCamera* camera() const;
Qt3DRender::QFrameGraphNode* frameGraph() const;
protected:
void resizeEvent(QResizeEvent *) override;
private:
// Rendering
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QCamera* mpCamera;
static bool msFormatDefined;
};
#endif // RENDERWINDOW_H
RenderWindow.cpp
#include "renderwindow.h"
#include <QDebug>
bool RenderWindow::msFormatDefined = false;
namespace
{
// Different clear colors so that it's obvious each window is using a
// different camera and frame graph.
static QColor sClearColors[] = {
Qt::darkBlue,
Qt::blue,
Qt::darkCyan,
Qt::cyan
};
static int sViewCount = 0;
}
RenderWindow::RenderWindow(QScreen* screen)
: QWindow(screen)
, mpFrameGraph(nullptr)
, mpCamera(new Qt3DRender::QCamera)
{
setSurfaceType(QWindow::OpenGLSurface);
// Set the default surface format once
if (!msFormatDefined)
{
QSurfaceFormat format;
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setDepthBufferSize(24);
format.setSamples(4);
format.setStencilBufferSize(8);
setFormat(format);
QSurfaceFormat::setDefaultFormat(format);
msFormatDefined = true;
}
// Camera
mpCamera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
mpCamera->setPosition(QVector3D(0, 0, 40.0f));
mpCamera->setViewCenter(QVector3D(0, 0, 0));
// Frame Graph (using forward renderer for now)
Qt3DExtras::QForwardRenderer* renderer = new Qt3DExtras::QForwardRenderer;
renderer->setCamera(mpCamera);
renderer->setSurface(this);
renderer->setClearColor(sClearColors[sViewCount++ % 4]);
mpFrameGraph = renderer;
}
RenderWindow::~RenderWindow()
{
qDebug() << "start ~RenderWindow";
// Unparent objects. Probably not necessary but it makes me feel
// good inside.
mpFrameGraph->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
mpCamera->setParent(static_cast<Qt3DCore::QNode*>(nullptr));
delete mpFrameGraph;
delete mpCamera;
qDebug() << "end ~RenderWindow";
}
Qt3DRender::QCamera* RenderWindow::camera() const
{
return mpCamera;
}
Qt3DRender::QFrameGraphNode* RenderWindow::frameGraph() const
{
return mpFrameGraph;
}
void RenderWindow::resizeEvent(QResizeEvent *)
{
mpCamera->setAspectRatio((float)width()/(float)height());
}
Scene.h
#ifndef SCENE_H
#define SCENE_H
#include <Qt3DCore/QEntity>
#include <Qt3DInput/QInputAspect>
#include <Qt3DRender/QFrameGraphNode>
#include <Qt3DRender/QRenderAspect>
#include <Qt3DRender/QRenderSettings>
class RenderWindow;
class Scene
{
public:
Scene();
~Scene();
Qt3DCore::QEntityPtr rootNode() const;
void addView(RenderWindow* window);
private:
void setupScene();
private:
Qt3DCore::QEntityPtr mpRoot;
// Frame Graph
Qt3DRender::QFrameGraphNode* mpFrameGraph;
Qt3DRender::QRenderSettings* mpRenderSettings;
// Aspects
Qt3DCore::QAspectEngine* mpEngine;
Qt3DRender::QRenderAspect* mpRenderAspect;
Qt3DInput::QInputAspect* mpInputAspect;
};
#endif // SCENE_H
Scene.cpp
#include "scene.h"
#include <QDebug>
#include <QPropertyAnimation>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QClearBuffers>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QCylinderMesh>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QTorusMesh>
#include "orbittransformcontroller.h"
#include "RenderWindow.h"
Scene::Scene()
: mpRoot(nullptr)
, mpFrameGraph(new Qt3DRender::QFrameGraphNode)
, mpRenderSettings(new Qt3DRender::QRenderSettings)
, mpEngine(new Qt3DCore::QAspectEngine)
, mpRenderAspect(new Qt3DRender::QRenderAspect)
, mpInputAspect(new Qt3DInput::QInputAspect)
{
mpEngine->registerAspect(mpRenderAspect);
mpRenderSettings->setActiveFrameGraph(mpFrameGraph);
setupScene();
mpRoot->addComponent(mpRenderSettings);
mpEngine->setRootEntity(mpRoot);
}
Scene::~Scene()
{
qDebug() << "start ~Scene";
mpEngine->setRootEntity(Qt3DCore::QEntityPtr());
mpRoot.clear();
delete mpEngine;
// mpRenderSettings and mpFrameGraph are children of the
// root node and are automatically destroyed when it is.
qDebug() << "end ~Scene";
}
Qt3DCore::QEntityPtr Scene::rootNode() const
{
return mpRoot;
}
void Scene::addView(RenderWindow* window)
{
// Add the window's frame graph to the main frame graph
if (window->frameGraph())
{
window->frameGraph()->setParent(mpFrameGraph);
}
}
void Scene::setupScene()
{
mpRoot.reset(new Qt3DCore::QEntity);
Qt3DCore::QEntity* entity = new Qt3DCore::QEntity;
entity->setParent(mpRoot.data());
// Create the material
Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(entity);
material->setAmbient(Qt::black);
material->setDiffuse(QColor(196, 196, 32));
material->setSpecular(Qt::white);
// Torrus
Qt3DCore::QEntity *torusEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QTorusMesh *torusMesh = new Qt3DExtras::QTorusMesh;
torusMesh->setRadius(5);
torusMesh->setMinorRadius(1);
torusMesh->setRings(100);
torusMesh->setSlices(20);
Qt3DCore::QTransform *torusTransform = new Qt3DCore::QTransform;
torusTransform->setScale3D(QVector3D(1.5, 1, 0.5));
torusTransform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), -45.0f));
torusEntity->addComponent(torusMesh);
torusEntity->addComponent(torusTransform);
torusEntity->addComponent(material);
// Sphere
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(entity);
Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh;
sphereMesh->setRadius(3);
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform;
/*OrbitTransformController *controller = new OrbitTransformController(sphereTransform);
controller->setTarget(sphereTransform);
controller->setRadius(20.0f);
QPropertyAnimation *sphereRotateTransformAnimation = new QPropertyAnimation(sphereTransform);
sphereRotateTransformAnimation->setTargetObject(controller);
sphereRotateTransformAnimation->setPropertyName("angle");
sphereRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
sphereRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
sphereRotateTransformAnimation->setDuration(10000);
sphereRotateTransformAnimation->setLoopCount(-1);
sphereRotateTransformAnimation->start();*/
sphereEntity->addComponent(sphereMesh);
sphereEntity->addComponent(sphereTransform);
sphereEntity->addComponent(material);
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "scene.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void createWindow();
private:
Ui::MainWindow *ui;
Scene* scene;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include "ui_mainwindow.h"
#include "renderwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
scene(new Scene())
{
ui->setupUi(this);
connect(ui->createButton, &QPushButton::clicked, this, &MainWindow::createWindow);
}
MainWindow::~MainWindow()
{
qDebug() << "~MainWindow";
delete scene;
delete ui;
}
void MainWindow::createWindow()
{
RenderWindow* window = new RenderWindow();
scene->addView(window);
window->resize(640, 480);
window->show();
QVector3D pos[] = {
QVector3D(0, 0, 40),
QVector3D(0, 25, -30),
QVector3D(-20, -20, -20),
QVector3D(40, 0, 0)
};
static int count = 0;
window->camera()->setPosition(pos[count++%4]);
window->camera()->setViewCenter(QVector3D(0, 0, 0));
// Delete the window when it is closed.
connect(window, &QWindow::visibilityChanged, this, [=](bool on)
{
if (!on)
window->deleteLater();
});
}
I've thoroughly tested your example and draw the same conclusions. When you destroy the window too quickly, the application crashes, probably because Qt3D still tries to issue some OpenGL commands to the underlying QSurface. I think this is a bug that should be reported.
A 'cleaner' work-around of this problem could be to track the generated 3d windows in the main window. You could maintain a list of pointers to all windows that where generated (and probably closed by the user at the some point). The windows are finally destroyed in the destructor of the main window.
I had exactly the same problem. I was creating a class derived from Qt3DWindow in a dialog box so the user could preview the effects of the choices made, and the program crashed when the dialog exited. In fact on Windows this crash causes the debugger and Qt Creator to crash too!
I tried working around this in a variety of ways and some helped because it turns out that it is a threading issue that was fixed on the 23rd October:
https://github.com/qt/qt3d/commit/3314694004b825263c9b9f2d69bd85da806ccbbc
The fix is now to apply the patch, and recompile Qt. 5.11.3 (or perhaps 5.12) will be out quite soon I expect but this bug is a killer if you are using Qt3D in dialogs.

QPainter::begin: Paint device returned engine == 0, type: 3

Errors:
QPainter::begin: Paint device returned engine == 0, type: 3
QPainter::setCompositionMode: Painter not active
QPainter::end: Painter not active, aborted
I made a small example that reproduces this error, but it doesn't reproduce the exact nature of the error. In the original code I have these QGraphicsSimpleTextItems (MyCallout) act as attachments to a QGraphicsItem that never moves, and never changes position; instead it has an image that I redraw that looks like a loading bar. The text item is meant to follow and move up or down according to the percentage of that bar. If I scroll my view down so the text item is no longer visible and then scroll back up, I see artefacts. Specifically, I see that my QGraphicsLineItem used in MyCallout has an additional copy slightly above/below the original, and the text portion of MyCallout has disappeared. When I scroll left or right and then back is when I see these QPainter errors. This small example has these errors as soon as they are created and added to the scene and when scrolling up or down, but the artefact issue is much less pronounced.
Source for small compilable example:
main.cpp
#include <QApplication>
#include "mainwindowtest.h"
#include <stdio.h>
int main(int argc, char * argv[]){
QApplication app(argc,argv);
MainWindow mainWin;
mainWin.show();
int rc = app.exec();
printf("%d\n",rc);
return 0;
}
mainwindowtest.cpp
#include <QtGui>
#include "mainwindowtest.h"
#include "myviewtest.h"
#include "myscenetest.h"
MainWindow::MainWindow(){
scene = new MyScene(this);
QSize size2=QSize(100000,6000);
view = new MyView(scene,size2);
setCentralWidget(view);
}
mainwindowtest.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTableView>
#include <QListView>
#include <QSlider>
#include <QLineEdit>
#include <QLayout>
class MyView;
class MyScene;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
protected:
private slots:
signals:
private:
MyView *view;
QWidget * window;
MyScene * scene;
};
#endif
mycallouttest.cpp
#include <QtGui>
#include "mycallouttest.h"
#include "myscenetest.h"
MyCallout::MyCallout (QGraphicsItem* parent) : QGraphicsSimpleTextItem (parent) {
setZValue(100);
setText(QString("0:"));
setBrush(QColor("violet"));
_shadow = new QGraphicsDropShadowEffect;
_shadow->setOffset(-boundingRect().x()+1.05,-boundingRect().y()+1.05);
_shadow->setBlurRadius(0);
_shadow->setColor("black");
setGraphicsEffect(_shadow);
_line.setParentItem(this);
_line.setLine(4,boundingRect().height()+18,14,boundingRect().height()-3);
_line.setZValue(90);
_line.setPen(QColor("violet"));
hide();
}
void MyCallout::setTextTo(const QString & text){
QString a=this->text();
int i = this->text().indexOf(":");
if(i>0){
a.remove(i+1,a.size());
a.append(text);
setText(a);
}else{
printf("warning: callout text with no ':'\n");
}
}
void MyCallout::position(int col,int value){
printf("position\n");
QString label;
QString a=text();
int i = text().indexOf(":");
if(i>0){
a.remove(0,i);
a.prepend(QString(QString("%1").arg(value)));
}else{
a=QString(QString("%1:").arg(value));
}
setText(a);
setPos((col+1)*7, (MAX_ROW-value-3)*7);
prepareGeometryChange();
show();
update();
}
QRectF MyCallout::boundingRect() const{
return QGraphicsSimpleTextItem::boundingRect();
}
mycallouttest.h
#ifndef MY_CALLOUT_H
#define MY_CALLOUT_H
#include <QGraphicsSimpleTextItem>
class QGraphicsLineItem;
class QGraphicsDropShadowEffect;
class MyCallout :public QGraphicsSimpleTextItem {
//Q_OBJECT
public:
MyCallout(QGraphicsItem * parent =0);
void position(int col,int value);
void setTextTo(const QString & text);
QRectF boundingRect() const;
protected:
QGraphicsLineItem _line;
QGraphicsDropShadowEffect * _shadow;
};
#endif
myscenetest.cpp
#include <QtGui>
#include "myscenetest.h"
#include "mainwindowtest.h"
#include "mycallouttest.cpp"
MyScene::MyScene(QObject *parent) : QGraphicsScene(parent){
setSceneRect(0,0,MAX_COL*11,MAX_ROW*11);
QGraphicsScene::setBackgroundBrush (QColor(173,216,230));
MyCallout *c;
for(int i=1;i<=MAX_COL;++i){
c=new MyCallout;
c->position(i,50);
addItem(c);
}
}
myscenetest.h
#ifndef MY_SCENE_H
#define MY_SCENE_H
#include <QGraphicsScene>
#include <QList>
#include "myviewtest.h"
#define MAX_ROW 90
#define MAX_COL 360
class MyView;
class MyScene : public QGraphicsScene
{
Q_OBJECT
public:
MyScene(QObject * parent =0);
public slots:
signals:
private:
};
#endif
myviewtest.cpp
#include <QtGui>
#include "myviewtest.h"
MyView::MyView(QGraphicsScene * scene,QSize &size,QWidget *parent) : QGraphicsView(scene,parent){
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
setDragMode(RubberBandDrag);
setMouseTracking(true);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);
_zoom_level=0;
_size=size;
}
const QSize MyView::sizeHint(){
return _size;
}
void MyView::wheelEvent(QWheelEvent * event){
if(event->modifiers() & Qt::ControlModifier){
if(event->delta() > 0){
zoomIn();
centerOn(mapToScene(event->pos()));
_zoom_level--;
emit updateZoom();
} else {
zoomOut();
centerOn(mapToScene(event->pos()));
_zoom_level++;
emit updateZoom();
}
}else{
QGraphicsView::wheelEvent(event);
}
}
myviewtest.h
#ifndef MY_VIEW_H
#define MY_VIEW_H
#include <QGraphicsView>
class MyView : public QGraphicsView {
Q_OBJECT
public:
MyView(QGraphicsScene * scene,QSize &size,QWidget *parent = NULL);
virtual const QSize sizeHint();
public slots:
void zoomIn(){ scale(1.1,1.1);};
void zoomOut(){ scale(1/1.1,1/1.1);};
void wheelEvent(QWheelEvent * event);
signals:
void updateZoom();
protected:
int _zoom_level;
QSize _size;
};
#endif

in Qt Gui how to make a button randomly appear every click

Alright so is there any way to make this program randomly change the variables x and y every time the button is clicked i am new to programming...
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QtGUI>
#include <QWidget>
#include <cstdlib>
#include <ctime>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *window = new QWidget;
srand(time(0));
int x = 1+(rand()%900);
int y = 1+(rand()%400);
QPushButton *MainInter = new QPushButton("Push me!",window);
QPropertyAnimation *animation = new QPropertyAnimation(MainInter, "pos");
animation->setDuration(0);
animation->setEndValue(QPoint(x,y));
Object::connect(MainInter,SIGNAL(released()),animation,SLOT(start()));
window->resize(900,500);
window->show();
return a.exec();
}
What you can do is, instead of connecting the released() signal of your button directly to your animations start() SLOT, you would create your own custom SLOT. Then you connect the button to it, handle the action, and call the animation.
First read up on how to create a custom QWidget, instead of creating top level object in your main(). Simple example here
A custom widget might look like this:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class QPushButton;
class QPropertyAnimation;
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
private:
QPushButton *button;
QPropertyAnimation *animation;
public slots:
void randomizeAnim();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QPushButton>
#include <QPropertyAnimation>
#include <ctime>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
button = new QPushButton("Push me!", this);
animation = new QPropertyAnimation(button, "pos");
animation->setDuration(0);
QObject::connect(button, SIGNAL(released()), this, SLOT(randomizeAnim()));
}
void MyWidget::randomizeAnim()
{
srand(time(0));
int x = 1+(rand()%900);
int y = 1+(rand()%400);
animation->setEndValue(QPoint(x,y));
animation->start();
}
And now your main.cpp can be reduced to the boilerplate code:
#include <QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *window = new MyWidget;
window->resize(900,500);
window->show();
return a.exec();
}
Every time you click, your custom slot will handle the action and do the animation.