qt passing object to different class - c++

EDIT
I am creating all objects initially when the program is started in my dialog.cpp and storing all QPixmaps in an array then picking a random one from them all. That random QPixmap I want to pass to my maintargets class and draw in the scene (which is also created in the dialog.cpp).
// dialog.cpp
#include "dialog.h"
#include "scene.h"
#include "ui_dialog.h"
#include "instructions.h"
#include "settings.h"
#include "highscore.h"
#include "maintargets.h"
#include <stdlib.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//Store targets in array and random generator
index = 0;
main_targets[0] = QPixmap(":images/darkbluelogo.jpg)");
main_targets[1] = QPixmap(":images/graylogo.jpg");
main_targets[2] = QPixmap(":images/lightbluelogo.jpg");
main_targets[3] = QPixmap(":images/limE.jpg");
main_targets[4] = QPixmap(":images/pink.jpg");
main_targets[5] = QPixmap(":images/purple.jpg");
main_targets[6] = QPixmap(":images/redlogo.jpg");
main_targets[7] = QPixmap(":images/yellow.jpg");
main_targets[8] = QPixmap(":images/brown.jpg");
index = qrand((index % 9) + 1);
//scene->addItem(main_targets[index]);
//Timer for scene advancement
QTimer *timer = new QTimer();
QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
timer->start(100);
}
Dialog::~Dialog()
{
delete ui;
}
//maintargets.h
#ifndef MAINTARGETS_H
#define MAINTARGETS_H
#include "dialog.h"
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QPainter>
#include <QRect>
class MainTargets : public QGraphicsScene
{
public:
MainTargets();
QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
protected:
void advance(int step);
private:
qreal dx, dy;
qreal x, y;
qreal w, h;
};
#endif // MAINTARGETS_H
//maintargets.cpp
#include "maintargets.h"
MainTargets::MainTargets()
{
dx = -0.005;
dy = 0.0;
x = 1.5;
y = 0.0;
w = 100.0;
h = 70.0;
}
QRectF MainTargets::boundingRect() const
{
qreal shift = 1;
return QRectF(-w/2 -shift, - h/2
- shift, w + shift, h + shift);
}
QPainterPath MainTargets::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void MainTargets::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
painter->drawPixmap(-w/2, -h/2, main_targets[index]);
}
void MainTargets::advance(int step)
{
if(step == 0) return;
x = x + dx;
y = y + dy;
setPos(mapToParent(x, y));
}
After it is drawn it moves in x-direction.

Your question is very broad unfortunately, and the exact solution depends on your use case. I will mention a few different solutions for your issue, and then you can take your peek, but please read the documentation about how to ask questions on Stack Overflow because your question is very low-quality at the moment.
1) If your operation is supposed to build the other class, you can pass it as a constructor argument if it is not against your design for the construction of this class.
2) You can use a void setPixmap(QPixmap); setter if it is possible to extend your class this way, and you have access to an instance of the object in that method.
3) You can use a proxy class dealing with all this, if that is all you have access in your operation getting the random index.
4) You can set a static variable in the same source file if the other class needs this in the same source file. This is not a good idea in general, but I saw this happening, too.
5) You can set a global variable that the other class method is using. Again, this is a very bad practice.
6) You can just pass this QPixmap as an argument to the drawing function directly in the other class.
7) You can pass this to a proxy class object that will pass it to the drawing method of the other class.
8) If the other class is in a different process, you have get at least another many ways to pass it through.
As I said, it really depends on your scenario, and there is loads of ways to do it. That being said, I will remove this answer later because this question is too broad, but I wished to show you how broad what you are asking is.

Simple pass by reference was what I was lost on. This explains the process of what was needed to get the QPixmap variable in the maintargets class.
//dialog.h
private:
Ui::Dialog *ui;
//add a pointer
MainTargets* pmap;
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "maintargets.h"
#include <stdlib.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//Store targets in array and random generator
index = 1;
main_targets[0] = QPixmap(":images/darkbluelogo.jpg)");
main_targets[1] = QPixmap(":images/graylogo.jpg");
main_targets[2] = QPixmap(":images/lightbluelogo.jpg");
main_targets[3] = QPixmap(":images/lime.jpg");
main_targets[4] = QPixmap(":images/pink.jpg");
main_targets[5] = QPixmap(":images/purple.jpg");
main_targets[6] = QPixmap(":images/redlogo.jpg");
main_targets[7] = QPixmap(":images/yellow.jpg");
main_targets[8] = QPixmap(":images/brown.jpg");
//Timer for scene advancement
QTimer *timer = new QTimer();
QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
timer->start(10);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_startButton_clicked()
{
ui->settingsButton->hide();
ui->titlescreen->hide();
ui->highscoreButton->hide();
ui->instructionButton->hide();
ui->startButton->hide();
QGraphicsTextItem *FirstP;
QString P1 = "Player1";
FirstP = scene->addText(P1);
FirstP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
FirstP->setDefaultTextColor(Qt::white);
FirstP->setPos(-300, -220);
QGraphicsTextItem *SecondP;
QString P2 = "Player2";
SecondP = scene->addText(P2);
SecondP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
SecondP->setDefaultTextColor(Qt::white);
SecondP->setPos(230, -220);
//Initializes function
setPixmaps();
}
void Dialog::setPixmaps()
{
index = (qrand() % (9));
//Add a new MainTarget and set equal to new pointer created in header file
pmap = new MainTargets(main_targets[index]);
pmap->setPos(355,0);
scene->addItem(pmap);
}
//maintargets.h
private:
//Add a new QPixmap to header
QPixmap p;
//maintargets.cpp
Reference in QPixmap variable in constructor and set equal to newly created pointer
#include "maintargets.h"
MainTargets::MainTargets(QPixmap& nomTargets)
{
dx = -0.005;
dy = 0.0;
x = 0.0;
y = 0.0;
w = 100.0;
h = 70.0;
p = nomTargets;
}
QRectF MainTargets::boundingRect() const
{
qreal shift = 1;
return QRectF(-w/2 -shift, - h/2
- shift, w + shift, h + shift);
}
QPainterPath MainTargets::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void MainTargets::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
//Set that pointer variable as the source for the
//given drawPixmap memeber
painter->drawPixmap(-w/2, -h/2, p);

Related

How to get Image pixel position loaded in QGraphicsView - Strange MapToScene() behaviour

I am originally loading image in QGraphicsView and using this method for basic zoom out and zoom in functionality.
However, I am unable to retrieve actual image pixel position using mapToScene functionality in eventFilter function of Graphics_view_zoom class. The below code produces behaviour exactly as windows photo viewer zooming only selected region.
MapToScene() returns same Point as mouse event position.
Here is the class which deals with zooming.
#include "Graphics_view_zoom.h"
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qmath.h>
Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
: QObject(view), _view(view)
{
_view->viewport()->installEventFilter(this);
_view->setMouseTracking(true);
_modifiers = Qt::ControlModifier;
_zoom_factor_base = 1.0015;
}
void Graphics_view_zoom::gentle_zoom(double factor) {
_view->scale(factor, factor);
_view->centerOn(target_scene_pos);
QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
_view->viewport()->height() / 2.0);
QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
_view->centerOn(_view->mapToScene(viewport_center.toPoint()));
emit zoomed();
}
void Graphics_view_zoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
_modifiers = modifiers;
}
void Graphics_view_zoom::set_zoom_factor_base(double value) {
_zoom_factor_base = value;
}
bool Graphics_view_zoom::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::MouseMove) {
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
QPointF delta = target_viewport_pos - mouse_event->pos();
// Here I want to get absolute image coordinates
if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
target_viewport_pos = mouse_event->pos();
target_scene_pos = _view->mapToScene(mouse_event->pos());
}
} else if (event->type() == QEvent::Wheel) {
QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
if (QApplication::keyboardModifiers() == _modifiers) {
if (wheel_event->orientation() == Qt::Vertical) {
double angle = wheel_event->angleDelta().y();
double factor = qPow(_zoom_factor_base, angle);
gentle_zoom(factor);
return true;
}
}
}
Q_UNUSED(object)
return false;
In mainwindow.cpp,
I am creating object of this class and loading an image as below:
m_GraphicsScene = new QGraphicsScene();
pixmapItem = new QGraphicsPixmapItem();
m_GraphicsScene->addItem(multiview[i].pixmapItem);
view_wrapper = new Graphics_view_zoom(ui->GraphicsView);
ui->GraphicsView->setScene(multiview[i].m_GraphicsScene);
pixmapItem->setPixmap(QPixmap::fromImage("img.jpg"));
multiview[view].m_GraphicsView->fitInView(QRectF(0,0,640,320),Qt::KeepAspectRatio);
Can anyone help with how do I achieve this ?
Keep in mind that the scaling you use only scales the scene, not the items. Given this, the position of the pixel can be obtained, so the algorithm is:
Obtain the mouse position with respect to the QGraphicsView
Transform that position with respect to the scene using mapToScene
Convert the coordinate with respect to the scene in relation to the item using mapFromScene of the QGraphicsItem.
Considering the above, I have implemented the following example:
#include <QtWidgets>
#include <random>
static QPixmap create_image(const QSize & size){
QImage image(size, QImage::Format_ARGB32);
image.fill(Qt::blue);
std::random_device rd;
std::mt19937_64 rng(rd());
std::uniform_int_distribution<int> uni(0, 255);
for(int i=0; i< image.width(); ++i)
for(int j=0; j < image.height(); ++j)
image.setPixelColor(QPoint(i, j), QColor(uni(rng), uni(rng), uni(rng)));
return QPixmap::fromImage(image);
}
class GraphicsView : public QGraphicsView
{
Q_OBJECT
Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers WRITE setModifiers)
public:
GraphicsView(QWidget *parent=nullptr): QGraphicsView(parent){
setScene(new QGraphicsScene);
setModifiers(Qt::ControlModifier);
auto item = scene()->addPixmap(create_image(QSize(100, 100)));
item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
item->setPos(40, 40);
fitInView(QRectF(0, 0, 640, 320),Qt::KeepAspectRatio);
resize(640, 480);
}
void setModifiers(const Qt::KeyboardModifiers &modifiers){
m_modifiers = modifiers;
}
Qt::KeyboardModifiers modifiers() const{
return m_modifiers;
}
signals:
void pixelChanged(const QPoint &);
protected:
void mousePressEvent(QMouseEvent *event) override{
if(QGraphicsPixmapItem *item = qgraphicsitem_cast<QGraphicsPixmapItem *>(itemAt(event->pos()))){
QPointF p = item->mapFromScene(mapToScene(event->pos()));
QPoint pixel_pos = p.toPoint();
emit pixelChanged(pixel_pos);
}
QGraphicsView::mousePressEvent(event);
}
void wheelEvent(QWheelEvent *event) override{
if(event->modifiers() == m_modifiers){
double angle = event->orientation() == Qt::Vertical ? event->angleDelta().y(): event->angleDelta().x();
double factor = qPow(base, angle);
applyZoom(factor, event->pos());
}
}
private:
void applyZoom(double factor, const QPoint & fixedViewPos)
{
QPointF fixedScenePos = mapToScene(fixedViewPos);
centerOn(fixedScenePos);
scale(factor, factor);
QPointF delta = mapToScene(fixedViewPos) - mapToScene(viewport()->rect().center());
centerOn(fixedScenePos - delta);
}
Qt::KeyboardModifiers m_modifiers;
const double base = 1.0015;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsView *view = new GraphicsView;
QLabel *label = new QLabel;
QObject::connect(view, &GraphicsView::pixelChanged, label, [label](const QPoint & p){
label->setText(QString("(%1, %2)").arg(p.x()).arg(p.y()));
});
label->setAlignment(Qt::AlignCenter);
QWidget w;
QVBoxLayout *lay = new QVBoxLayout(&w);
lay->addWidget(view);
lay->addWidget(label);
w.show();
return a.exec();
}
#include "main.moc"
It may be better to use a custom graphics scene subclassed from QGraphicsScene as this makes extracting the necessary coordinates much simpler. The only snag is you have to have the QGraphicsPixmapItem::pos available in the custom QGraphicsScene class - I have included a full working example which uses Graphics_view_zoom.h and Graphics_view_zoom.cpp from the linked question. The position of the QGraphicsPixmapItem is passed to a member of the QGraphicsScene subclass, Frame, in order to make the necessary correction.
#include <QPixmap>
#include <QGraphicsPixmapItem>
#include <QGraphicsTextItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <qfont.h>
#include "Graphics_view_zoom.h"
class Frame : public QGraphicsScene {
Q_OBJECT
public:
QGraphicsTextItem * coords;
QPointF pic_tl;
Frame::Frame(QWidget* parent)
: QGraphicsScene(parent) {
coords = new QGraphicsTextItem();
coords->setZValue(1);
coords->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
addItem(coords);
}
void Frame::tl(QPointF p) {
pic_tl = p;
}
protected:
void Frame::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
QPointF pos = event->scenePos();
coords->setPlainText("(" + QString("%1").arg(int(pos.x() - pic_tl.x())) + ", "
+ QString("%1").arg(int(pos.y() - pic_tl.y())) + ")");
coords->setPos(pos);
coords->adjustSize();
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QMainWindow* main = new QMainWindow();
QGraphicsView* GraphicsView = new QGraphicsView(main);
Graphics_view_zoom* view_wrapper = new Graphics_view_zoom(GraphicsView);
Frame* frame = new Frame(main);
QGraphicsPixmapItem* pixmapItem = new QGraphicsPixmapItem();
frame->addItem(pixmapItem);
GraphicsView->setScene(frame);
// Loads a 497x326 pixel test image
pixmapItem->setPixmap(QPixmap(":/StackOverflow/test"));
// small offset to ensure it works for pictures which are not at
// (0,0). Larger offsets produce the same result but require manual
// adjustments of the view, I have neglected those for brevity as
// they are not in the scope of the question.
pixmapItem->setPos(-20, 20);
frame->tl(pixmapItem->pos());
GraphicsView->fitInView(QRectF(0, 0, 640, 320), Qt::KeepAspectRatio);
GraphicsView->centerOn(pixmapItem->pos());
main->resize(1920, 1080);
main->show();
GraphicsView->resize(main->width(), main->height());
return a.exec();
}
This will display the image coordinates of the pixel under the mouse relative to (0,0) at the top left corner.
The mouse is not visible in these screenshots but in the first it is in exactly the upper left corner and the second it is in exactly the lower right corner. If these coordinates are needed inside the Graphics_view_zoom object then you simply have to scope the Frame instance appropriately, or pass the value as needed.
Note - the exact coordinates displayed may not precisely represent the position of the mouse in this example since they are cast to ints for demonstration, but the floating point values can be easily accessed since QGraphicsSceneMoveEvent::scenePos() returns a QPointF. Additionally, note that in running this demonstration there may be some (hopefully very small) variation on where the mouse appears to be relative to it's 'actual' position - I recommend using Qt::CrossCursor to allay this. For example on my system the default cursor is off by about a pixel for certain areas on my smaller display, this is also affected by the zoom level - higher zoom will produce more accurate results, less zoom will be less accurate.

Can't refesh pixmap of QLabel

I have been stuck on this issue for 2 days now. I'm using the Qt plugin for Visual Studio 2013 on Window 7-64 bit.
I have been trying to display a pair of images in QLabels. I need to manipulate the pixel data regularly, so I store them in QImages, and every time I want to refresh the display I set the QPixmap of a QLabel. The problem is, it only seems to refresh if I change/move the window in some way.
This problems goes away if I just make the QLabels children of my QWidget, but never set a layout. If I then add repaint() or update(), the problem comes back.
(this is a very similar post to one I posted using QGraphicsScene, but the problem seems to be more fundamental than that, so I am reposting)
Here is my code. First the .h
#ifndef DISPLAYWIDGET_H
#define DISPLAYWIDGET_H
#include <QtWidgets/QWidget>
#include <QPixmap>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
#include <QLabel>
#define FULLSCALE 255
#define IM_X_MIN -5.0
#define IM_X_MAX 5.0
#define IM_Z_MIN 0.0
#define IM_Z_MAX 15.0
#define IM_PIXEL_WIDTH 200
#define IM_PIXEL_HEIGHT IM_PIXEL_WIDTH * (IM_Z_MAX-IM_Z_MIN)/(IM_X_MAX - IM_X_MIN)
#define BORDER_WIDTH 10
#define RAND_SEED 7
class DisplayWidget : public QWidget
{
Q_OBJECT
public:
DisplayWidget(int width, int height, QWidget *parent = 0);
~DisplayWidget();
private:
QLabel* bimageLabel;
QLabel* dimageLabel;
QImage* bImage;
QImage* dImage;
QTimer* frameGrab;
QPushButton* debugButton;
void CreateWidgets();
void SetupGui();
int w, h;
public slots:
void GenerateNewData();
};
#endif // DISPLAYWIDGET_H
and the .cpp.
#include "displaywidget.h"
DisplayWidget::DisplayWidget(int width, int height, QWidget *parent): QWidget(parent)
{
//ui.setupUi(this);
w = width;
h = height;
CreateWidgets();
SetupGui();
// seed the random number generator
srand(RAND_SEED);
GenerateNewData();
}
DisplayWidget::~DisplayWidget()
{
}
void DisplayWidget::CreateWidgets()
{
bImage = new QImage(w, h, QImage::Format_ARGB32);
dImage = new QImage(w, h, QImage::Format_ARGB32);
bimageLabel = new QLabel(this);
dimageLabel = new QLabel(this);
debugButton = new QPushButton("DEBUG", this);
bimageLabel->setStyleSheet("QLabel {background-color: black};");
dimageLabel->setStyleSheet("QLabel {background-color: white};");
frameGrab = new QTimer(this);
}
void DisplayWidget::SetupGui()
{
QHBoxLayout * layout = new QHBoxLayout();
setLayout(layout); // commenting this line out makes it refresh
layout->addWidget(bimageLabel);
layout->addWidget(dimageLabel);
layout->addWidget(debugButton);
connect(frameGrab, SIGNAL(timeout()),this, SLOT(GenerateNewData()));
connect(debugButton, SIGNAL(clicked()), this, SLOT(GenerateNewData()));
frameGrab->start(50);
}
void DisplayWidget::GenerateNewData()
{
QRgb * bImageData = (QRgb *)bImage->scanLine(0);
QRgb * dImageData = (QRgb *)dImage->scanLine(0);
for (int i; i < w * h; i++)
{
bImageData[i] = qRgba(rand() % FULLSCALE, 0, 0, FULLSCALE);
dImageData[i] = qRgba(0, 0, rand() % FULLSCALE, FULLSCALE);
}
bimageLabel->setPixmap(QPixmap::fromImage(*bImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
dimageLabel->setPixmap(QPixmap::fromImage(*dImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
//this->update(); // this breaks it again
}
Losing my mind here. I have very limited experience with Qt, but I believe I have the right approach.
Please help!
I've tested your code on Ubuntu. I can unfortunately not comment on windows. I've modified the code slightly (See comments marked by //#w:):
#include "displaywidget.h"
DisplayWidget::DisplayWidget(int width, int height, QWidget *parent): QWidget(parent)
{
w = width;
h = height;
CreateWidgets();
SetupGui();
// seed the random number generator
srand(RAND_SEED);
GenerateNewData();
}
DisplayWidget::~DisplayWidget()
{
}
void DisplayWidget::CreateWidgets()
{
bImage = new QImage(w, h, QImage::Format_ARGB32);
dImage = new QImage(w, h, QImage::Format_ARGB32);
bimageLabel = new QLabel(this);
bimageLabel->setScaledContents(true);
dimageLabel = new QLabel(this);
dimageLabel->setScaledContents(true);
debugButton = new QPushButton("DEBUG", this);
bimageLabel->setStyleSheet("QLabel {background-color: black};");
dimageLabel->setStyleSheet("QLabel {background-color: grey};");
frameGrab = new QTimer(this);
}
void DisplayWidget::SetupGui()
{
//#w: Adding vertical layout as button below seems like better usage of space...
QVBoxLayout * vlay = new QVBoxLayout();
QHBoxLayout * hlay = new QHBoxLayout();
hlay->addWidget(bimageLabel);
hlay->addWidget(dimageLabel);
vlay->addLayout(hlay);
vlay->addWidget(debugButton);
//#w: Removing size constraints on top layout allows me to resize window and see effect.
vlay->setSizeConstraint(QLayout::SetNoConstraint);
setLayout(vlay); // commenting this line out makes it refresh
connect(frameGrab, SIGNAL(timeout()),this, SLOT(GenerateNewData()));
//#w: I suppose we can chuck the button.... Currently it serves no purpose
connect(debugButton, SIGNAL(clicked()), this, SLOT(GenerateNewData()));
//#w: Timer slower initially, then increase to see where performance degrades.
frameGrab->start(200);
}
void DisplayWidget::GenerateNewData()
{
QRgb * bImageData = (QRgb *)bImage->scanLine(0);
QRgb * dImageData = (QRgb *)dImage->scanLine(0);
//#w: This code just varies the contents by having a b and d selector that
// alternates colour... Simple stuff...
int bSelect = rand() % 3,
dSelect = (bSelect==2) ? 0 : bSelect+1;
for (int i = 0; i < w * h; i++)
{
//#w: 3 colours, only two being selected - can be improved, I suppose.
QRgb rgb[3] =
{
(bSelect == 0) || (dSelect==0) ? qRgba(rand() % FULLSCALE, 0, 0, FULLSCALE) : 0,
(bSelect == 1) || (dSelect==1) ? qRgba(0, rand() % FULLSCALE, 0, FULLSCALE) : 0,
(bSelect == 2) || (dSelect==2) ? qRgba(0, 0, rand() % FULLSCALE, FULLSCALE) : 0,
};
bImageData[i] = rgb[bSelect];
dImageData[i] = rgb[dSelect];
}
//#w: Removed scaling, as it depends on layout...
bimageLabel->setPixmap(QPixmap::fromImage(*bImage));
dimageLabel->setPixmap(QPixmap::fromImage(*dImage));
}
Initially the labels take its size from the pixmaps. Thereafter the labels adhere to the layout, which adheres to resizing of the form (main/parent widget)
QtForum solved this one for me, and boy is it embarassing.
I forgot to initialize the loop counter. Fixed everything.

How to make QGraphicsView update after changing a QGraphicsPixmapItem

I'm quite a n00b with Qt, but I have seen a lot of posts where people can't get a QGraphicsView to update as expected. I am trying to make very simple widget that will display two arrays of data as images overlapped with some alpha blending. From what I gather the best way to do this is to create a QImage to hold the image data, and each time I want to update the overlay display, you convert the QImage into a QPixmap, and then use that to update the pixmap of a QGraphicsPixmapItem (which is in a QGraphicsScene, which is in a QGraphicsView).
As a minimum working example, I have it setup to generate a random red image and random green image, and with random blending between them. It's setup on a timer to generate new data and, ideally, update the view. No matter how many updates/repaints I drop around, I can't seem to get it to update properly. The timer seems to be working, and the random data generation seems to be working as well, but the scene only updates if I physically change the size of the window.
Here is my code, in a few blocks. First DisplayWidget.h
#include <QtWidgets/QWidget>
#include "ui_displaywidget.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include "math.h"
#include <QVBoxLayout>
#include <qsize.h>
#include <qtimer.h>
#define FULLSCALE 255
#define IM_X_MIN -5.0
#define IM_X_MAX 5.0
#define IM_Z_MIN 0.0
#define IM_Z_MAX 15.0
#define IM_PIXEL_WIDTH 200
#define IM_PIXEL_HEIGHT IM_PIXEL_WIDTH * (IM_Z_MAX-IM_Z_MIN)/(IM_X_MAX - IM_X_MIN)
#define BORDER_WIDTH 10
#define RAND_SEED 7
class DisplayWidget : public QWidget
{
Q_OBJECT
public:
DisplayWidget(int width, int height, QWidget *parent = 0);
~DisplayWidget();
void SetData(float * data, float minVal, float maxVal);
void SetTransparency(float * alpha, float minVal, float maxVal);
private:
//Ui::DisplayWidgetClass ui;
QGraphicsView * view;
QGraphicsScene * scene;
QGraphicsPixmapItem * bModeItem;
QGraphicsPixmapItem * dModeItem;
QImage * bImage;
QImage * dImage;
QTimer * frameGrab;
void CreateWidgets();
void SetupGui();
int w, h;
public slots:
void GenerateNewData();
};
And here's the relevant parts of my .cpp. GenerateNewData() is the function that doesn't seem to produce updates to the scene. This is where I have tried view/scene/item updates/repaints.
DisplayWidget::DisplayWidget(int width, int height, QWidget *parent): QWidget(parent)
{
w = width;
h = height;
CreateWidgets();
SetupGui();
// seed the random number generator
srand(RAND_SEED);
GenerateNewData();
}
void DisplayWidget::CreateWidgets()
{
view = new QGraphicsView(this);
scene = new QGraphicsScene(this);
bModeItem = scene->addPixmap(QPixmap());
dModeItem = scene->addPixmap(QPixmap());
bImage = new QImage(w, h, QImage::Format_ARGB32);
dImage = new QImage(w, h, QImage::Format_ARGB32);
frameGrab = new QTimer(this);
}
void DisplayWidget::SetupGui()
{
QVBoxLayout * layout = new QVBoxLayout(this);
layout->addWidget(view);
setLayout(layout);
scene->setSceneRect(0, 0, IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT);
view->setGeometry(0, 0, IM_PIXEL_WIDTH + 2*BORDER_WIDTH, IM_PIXEL_HEIGHT + 2*BORDER_WIDTH);
view->setScene(scene);
bModeItem->setPixmap(QPixmap::fromImage(*bImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
dModeItem->setPixmap(QPixmap::fromImage(*dImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
connect(frameGrab, SIGNAL(timeout()),this, SLOT(GenerateNewData()));
frameGrab->start(500);
}
void DisplayWidget::GenerateNewData()
{
QRgb * bImageData = (QRgb *)bImage->scanLine(0);
QRgb * dImageData = (QRgb *)dImage->scanLine(0);
for (int i; i < w * h; i++)
{
bImageData[i] = qRgba(rand() % FULLSCALE, 0, 0, FULLSCALE);
dImageData[i] = qRgba(0, 255,0, rand() % FULLSCALE);
}
bModeItem->setPixmap(QPixmap::fromImage(*bImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
dModeItem->setPixmap(QPixmap::fromImage(*dImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
}
And here's my main.
#include "displaywidget.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DisplayWidget w(25,25);
w.show();
return a.exec();
}
I would appreciate any help!

QTimer won't advance screen

I create my scene in my dialog.cpp and draw some QGraphicsItem's in my scene.cpp. When I add my QTimer to my dialog.cpp it makes whenever I move the cursor over the scene to crash.
dialog.cpp
#include "dialog.h"
#include "scene.h"
#include "ui_dialog.h"
#include "instructions.h"
#include "settings.h"
#include "highscore.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// Create and configure scene
scene = new Scene;
scene->setBackgroundBrush(Qt::black);
scene->setItemIndexMethod(QGraphicsScene::NoIndex);
ui->graphicsView->setScene(scene);
scene->setSceneRect(-200, -150, 400, 300);
ui->graphicsView->setMouseTracking(true);
QPixmap tankbase1(":/images/tankbase.jpg");
ui->tankbaseplay1->setPixmap(tankbase1);
//\/\/\/This is my problem. And not sure why\/\/\/\/\/\/
// timer = new QTimer(this);
// QObject::connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
// timer->start(10);
}
Dialog::~Dialog()
{
delete ui;
}
//void Dialog::shoot()
//{
//}
void Dialog::on_startButton_clicked()
{
ui->settingsButton->hide();
ui->titlescreen->hide();
ui->highscoreButton->hide();
ui->instructionButton->hide();
ui->startButton->hide();
QGraphicsTextItem *FirstP;
QString P1 = "Player1";
FirstP = scene->addText(P1);
FirstP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
FirstP->setDefaultTextColor(Qt::white);
FirstP->setPos(-300, -220);
QGraphicsTextItem *SecondP;
QString P2 = "Player2";
SecondP = scene->addText(P2);
SecondP->setFont(QFont("Nimbus Mono L", 12,QFont::Bold));
SecondP->setDefaultTextColor(Qt::white);
SecondP->setPos(230, -220);
}
void Dialog::on_instructionButton_clicked()
{
Instructions intDialog;
intDialog.setModal(true);
intDialog.exec();
}
void Dialog::on_settingsButton_clicked()
{
settings intDialog;
intDialog.setModal(true);
intDialog.exec();
}
void Dialog::on_highscoreButton_clicked()
{
highscore intDialog;
intDialog.setModal(true);
intDialog.exec();
}
scene.cpp
#include "scene.h"
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
#include <QGraphicsSceneMouseEvent>
#include <QTimer>
#include "qmath.h"
#include <math.h>
class GraphicsCircle : public QGraphicsEllipseItem
// class for the fire bullets
{
public:
GraphicsCircle(qreal dirx, qreal diry)
: m_Speed(3)
, m_DirX(dirx)
, m_DirY(diry)
{
setRect(-3.0,-3.0,8.0,8.0);
setPos(-195, 130);
QRadialGradient rGrad( 0.0, 0.0, 20.0, 0.0, 0.0);
rGrad.setColorAt(0.0, QColor(255,255,255));
rGrad.setColorAt(0.7, QColor(255,255,225));
rGrad.setColorAt(1.0, QColor(255,0,0,0));
setBrush(QBrush(rGrad) );
setPen(QPen(Qt::NoPen));
}
virtual ~GraphicsCircle() {}
void advance(int phase)
{
if(!phase) return;
setPos(x()+m_Speed*m_DirX, y()+m_Speed*m_DirY);
}
private:
qreal m_Speed;
qreal m_DirX;
qreal m_DirY;
};
Scene::Scene() : QGraphicsScene()
{
// added the lines below to setup an item, pointing in the positive x direction
int x1 = 0;
int y1 = 0;
cannon = new QGraphicsLineItem(x1, y1, x1 + 50, y1);
cannon->setPen(QPen(Qt::white, 6));
this->addItem(cannon);
cannon->setPos(-195, 130);
//Create bullets
m_FireTimer= new QTimer();
QObject::connect(m_FireTimer, SIGNAL(timeout()), this, SLOT(fire()));
}
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
m_FireTarget = e->scenePos();
m_FireTimer->start();
QGraphicsScene::mousePressEvent(e);
}
void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *e)
{
// emit mouseMoving(e->scenePos());
// FirstPlayer->setPos(e->scenePos());
// qAtan2(cannon->pos(), e->scenePos());
m_FireTarget = e->scenePos();
QGraphicsScene::mouseMoveEvent(e);
QLineF arm(cannon->pos(), e->scenePos());
cannon->setRotation(360 - arm.angle());
}
void Scene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * e )
{
m_FireTimer->stop();
QGraphicsScene::mouseReleaseEvent(e);
}
void Scene::fire()
// creates a fire bullet
// the bullet will move in the direction of the mouse cursor
// the trajectory is sligthly perturbated by a random small angle
{
qreal dirx = m_FireTarget.x()-195;
qreal diry = m_FireTarget.y()-195;
qreal length = sqrt(dirx*dirx+diry*diry);
if (length!=0)
{
// normalized direction vector
qreal invLength= 1.0/length;
dirx *= invLength;
diry *= invLength;
// creating an angle perturbation of +/- 3°
qreal alphaPerturbation = static_cast<qreal>(qrand()%6-3) * M_PI / 180.0;
qreal xPerturbation = cos(alphaPerturbation);
qreal yPerturbation = sin(alphaPerturbation);
dirx = dirx*xPerturbation - diry*yPerturbation;
diry = diry*xPerturbation + dirx*yPerturbation;
GraphicsCircle * circle = new GraphicsCircle(dirx, diry);
addItem(circle);
}
}
void Scene::advance()
{
// first remove the pellet out of the sceneRect
for (int i=0; i<items().count(); ++i)
{
QGraphicsItem * item = items().at(i);
qreal x= item->x();
qreal y= item->y();
qreal sx=sceneRect().width();
qreal sy= sceneRect().height();
if ( (x < 0.0) || (y < 0.0) || (x > sx) || (y > sy))
{
removeItem(item);
delete item;
}
}
QGraphicsScene::advance();
}
When I run the code without the QTimer code in dialog.cpp it runs and my QGraphicsItems are displayed and move accordingly. When I add the QTimer the QGraphicsItem disappears. Completely lost to what the issue is.
Also I have taken the scene code and ran it separately and it works. The only difference is the scene and QTimer is created in main.cpp.
Help greatly needed!!!!!
You are iterating through a list of items while you are deleting items in the list. That sounds like trouble.
http://qt-project.org/doc/qt-5/qrect.html#intersects
http://qt-project.org/doc/qt-5/qgraphicsitem.html#boundingRect
http://qt-project.org/doc/qt-5/qrectf.html#contains
I think this might be a little cleaner in your advance() function.
QList <QGraphicsItem *> itemsToRemove;
foreach( QGraphicsItem * item, this->items() )
{
if( !this->sceneRect().intersects(item->boundingRect()) )
{
// The item is no longer in the scene rect, get ready to delete it
itemsToRemove.append(item);
}
}
foreach( QGraphicsItem * item, itemsToRemove )
{
this->removeItem(item);
delete(item);
}
Also reading the description of QGraphicsScene,
http://qt-project.org/doc/qt-5/qgraphicsscene.html#details
there a number of helper methods that can make finding items in an area or one item colliding with another, much easier.
Hope that helps.

How to animate two values using QTimeLine

I have a QGraphicsItem for which I want to animate a size change. Because of this, I need to vary both the height and width of the object over time. I have used the QTimeLine in the past for single-variable animations and I would like to use it here for two-variable if possible. However, I don't see a QTimeLine::setFrameRange() that works for two variables.
How can I accomplish this? Is there a better Qt class for this?
Since animations have to be attached to objects, your graphics item will be a QGraphicsObject, so you can expose the relevant properties to the Qt property system.
Since you're animating what amounts to a QSizeF, the simplest way would be to expose this as a single property and use a QPropertyAnimation.
In general, if you have to animate multiple properties in parallel, set them up as individual QPropertyAnimations. Then run those in parallel using a QParallelAnimationGroup.
It's of course possible to use a QTimeLine, but then you have to interpolate between the endpoints manually, basing on the frame number, for example. Why bother.
Below is a complete example that shows how to animate three properties at once: pos, size and rotation.
main.cpp
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QSequentialAnimationGroup>
#include <QGraphicsObject>
#include <QPainter>
#include <QApplication>
class Item : public QGraphicsObject {
Q_OBJECT
qreal m_pen;
QSizeF m_size;
Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY newSize)
Q_SIGNAL void newSize();
qreal width() const { return m_size.width() + m_pen; }
qreal height() const { return m_size.height() + m_pen; }
public:
explicit Item(QGraphicsItem *parent = 0) :
QGraphicsObject(parent), m_pen(5.0), m_size(50.0, 50.0) {}
QSizeF size() const { return m_size; }
void setSize(const QSizeF & size) {
if (m_size != size) {
m_size = size;
update();
emit newSize();
}
}
QRectF boundingRect() const {
return QRectF(-width()/2, -height()/2, width(), height());
}
void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) {
p->setPen(QPen(Qt::black, m_pen));
p->drawEllipse(QPointF(0,0), width()/2, height()/2);
p->setPen(QPen(Qt::red, m_pen));
p->drawLine(0, 0, 0, height()/2);
}
};
void animate(QObject * obj)
{
QParallelAnimationGroup * group = new QParallelAnimationGroup(obj);
QPropertyAnimation * pos = new QPropertyAnimation(obj, "pos", obj);
QPropertyAnimation * size = new QPropertyAnimation(obj, "size", obj);
QPropertyAnimation * rot= new QPropertyAnimation(obj, "rotation", obj);
pos->setDuration(3000);
pos->setLoopCount(-1);
pos->setEasingCurve(QEasingCurve::InOutCubic);
pos->setStartValue(QPointF(-50, -50));
pos->setEndValue(QPointF(50, 50));
size->setDuration(1500);
size->setLoopCount(-1);
size->setEasingCurve(QEasingCurve::InOutElastic);
size->setStartValue(QSizeF(100, 100));
size->setEndValue(QSizeF(100, 30));
rot->setDuration(1000);
rot->setLoopCount(-1);
rot->setStartValue(0.0);
rot->setEndValue(360.0);
group->addAnimation(pos);
group->addAnimation(size);
group->addAnimation(rot);
group->start();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene s;
QGraphicsView v(&s);
Item * item = new Item;
s.addItem(item);
v.setRenderHint(QPainter::Antialiasing);
v.setSceneRect(-125, -125, 300, 300);
v.show();
animate(item);
return a.exec();
}
#include "main.moc"