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.
Related
I'm running the next animation on two widgets:
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
qDebug() << QString("Animation Start %1: ").arg(widgets[i]->objectName()) << widgets[i]->pos() << " g " << widgets[i]->geometry();
//
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
So far so good, animation moves both widgets as expected, but as soon as I resize the window, those widgets get automatically moved back to their original position. The current layout holding this both widgets is a QHBoxLayout, and has the following items (same order):
QLabel0 | QSpacerItem0 | QToolTipButton | QSpacerItem1 | QLabel1
after the animation they become:
QLabel1 | QSpacerItem0 | QToolTipButton | QSpacerItem1 | QLabel0
But whenever I change the size of the window the position on the widgets resets to the original layout. I haven't implemented the resizeEvent from my window.
I'm using Windows 10 with Visual Studio | Qt Visual Studio Tools 2.3.1 and Qt 12.0, building with msvc2017_64 version and c++17 enabled.
Complete Test Code (New project):
SwapItemsTest.cpp
#include "SwapItemsTest.h"
#include <array>
#include <QHBoxLayout>
#include <QToolButton>
#include <QSpacerItem>
#include <QLabel>
#include <QDebug>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QTimer>
SwapItemsTest::SwapItemsTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto layout = new QHBoxLayout;
auto toolButton = new QToolButton;
auto left = new QLabel("Left");
left->setFixedSize(50, size().height());
left->setFrameShape(QFrame::Box);
left->setFrameShadow(QFrame::Plain);
left->setLineWidth(2);
left->setAlignment(Qt::AlignCenter);
auto right = new QLabel("Right");
right->setFixedSize(50, size().height());
right->setAlignment(Qt::AlignCenter);
right->setFrameShape(QFrame::Box);
right->setFrameShadow(QFrame::Plain);
right->setLineWidth(2);
layout->addWidget(left);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(toolButton);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(right);
ui.centralWidget->setLayout(layout);
auto widgets = std::array{left, right};
connect(toolButton, &QToolButton::clicked, [widgets]() {
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
});
}
SwapItemsTest.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_SwapItemsTest.h"
class SwapItemsTest : public QMainWindow
{
Q_OBJECT
public:
SwapItemsTest(QWidget *parent = Q_NULLPTR);
private:
Ui::SwapItemsTestClass ui;
};
The problem is that you place the widgets in a layout but then try to manage their geometry yourself. One workaround would be to update the layout once the animation group has finished by swapping the two widgets. Your code then becomes...
SwapItemsTest::SwapItemsTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto layout = new QHBoxLayout;
auto toolButton = new QToolButton;
auto left = new QLabel("Left");
left->setFixedSize(50, size().height());
left->setFrameShape(QFrame::Box);
left->setFrameShadow(QFrame::Plain);
left->setLineWidth(2);
left->setAlignment(Qt::AlignCenter);
auto right = new QLabel("Right");
right->setFixedSize(50, size().height());
right->setAlignment(Qt::AlignCenter);
right->setFrameShape(QFrame::Box);
right->setFrameShadow(QFrame::Plain);
right->setLineWidth(2);
layout->addWidget(left);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(toolButton);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(right);
ui.centralWidget->setLayout(layout);
auto widgets = std::array{left, right};
connect(toolButton, &QToolButton::clicked,
/*
* Note that layout has been added to the capture list for this lambda.
*/
[layout, widgets]()
{
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
/*
* This is the lambda that will be invoked when the
* QPropertyAnimation::finished signal is emitted. It simply
* swaps the positions of the two widgets in the layout.
*/
connect(animGroup, &QPropertyAnimation::finished,
[layout, widgets]()
{
auto index0 = layout->indexOf(widgets[0]);
auto index1 = layout->indexOf(widgets[1]);
if (index0 < index1) {
layout->takeAt(index1);
layout->takeAt(index0);
layout->insertWidget(index0, widgets[1]);
layout->insertWidget(index1, widgets[0]);
} else {
layout->takeAt(index0);
layout->takeAt(index1);
layout->insertWidget(index1, widgets[0]);
layout->insertWidget(index0, widgets[1]);
}
});
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
});
}
Note that you might still see some strange behaviour if the parent widget is resized during the animation.
I'm building a multi-window app and so far creates and shows MainWidget with 8 buttons. My next step is to make each button open a new window which in Qt terms is a child of QWidget. I keep all my buttons and new windows (which should be opened upon a button is clicked) in QVectors. It all compiles with no warnings or errors, however, when I click a button, the corresponding QWidget window is not shown.
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QPushButton>
#include <QGridLayout>
#include <QSignalMapper>
#include "examwindow.h"
namespace Ui { class MainWidget; }
class MainWidget : public QWidget {
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
~MainWidget();
private:
Ui::MainWidget *ui;
int nExams;
QVector<QString> titles;
QVector<QPushButton*> examButtons;
QGridLayout* mainWidgetLayout;
QVector<ExamWindow*> examWindows;
public slots:
void clickedExamW ();
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QDesktopWidget>
#include <iostream>
MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget)
{
ui->setupUi(this);
/**
* #brief Resize the main window size in proportion to the desktop size
*/
double ratio = 0.7;
resize(QDesktopWidget().availableGeometry(this).size() * ratio);
/**
* #brief Set the main window position in the desktop center
*/
QDesktopWidget dw;
int width = this->frameGeometry().width();
int height = this->frameGeometry().height();
int screenWidth = dw.screen() -> width();
int screenHeight = dw.screen() -> height();
this->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height);
/**
* Set the button titles
*/
titles.push_back("FCE - 2008");
titles.push_back("CAE - 2008");
titles.push_back("CPE - 2008");
titles.push_back("ЕГЭ");
titles.push_back("FCE - 2015");
titles.push_back("CAE - 2015");
titles.push_back("CPE - 2015");
titles.push_back("User's Format");
/**
* Create buttons
*/
nExams = 8; // Number of exams
examButtons.resize(nExams);
for(int i = 0; i < nExams; i++) {
examButtons[i] = new QPushButton(titles[i]);
examButtons[i]->setMinimumSize(QSize(150, 150));
examButtons[i]->setMaximumSize(QSize(500, 500));
examButtons[i]->setObjectName(titles[i]);
connect(examButtons[i], SIGNAL(clicked()), this, SLOT(clickedExamW()));
}
/**
* Add exam buttons to the main widget layout
*/
mainWidgetLayout = new QGridLayout(this);
for(int i = 0; i < nExams; i++)
if (i < nExams / 2)
mainWidgetLayout -> addWidget(examButtons[i], i, 0);
else
mainWidgetLayout -> addWidget(examButtons[i], i - nExams / 2, 1);
/**
* Create exam windows
*/
examWindows.resize(nExams);
for(int i = 0; i < nExams; i++) {
examWindows[i] = new ExamWindow(this);
examWindows[i]->setWindowTitle(titles[i]);
}
}
void MainWidget::clickedExamW() {
QObject *senderObj = sender();
QString senderObjName = senderObj->objectName();
for(int i = 0; i < nExams; i++)
if (senderObjName == titles[i]) {
this->setWindowTitle(titles[i]); // WORKS - it changes the title
examWindows[i]->show(); // DOES NOT WORK - no win shown
}
}
MainWidget::~MainWidget()
{
delete ui;
}
examwindow.h
#ifndef EXAMWINDOW_H
#define EXAMWINDOW_H
#include <QWidget>
class ExamWindow : public QWidget
{
Q_OBJECT
public:
explicit ExamWindow(QWidget *parent = 0);
signals:
public slots:
};
#endif // EXAMWINDOW_H
examwindow.cpp
#include "examwindow.h"
ExamWindow::ExamWindow(QWidget *parent) : QWidget(parent)
{
}
The way, how do you create widgets is ok (examWindows[i] = new ExamWindow(this);). But:
ExamWindow::ExamWindow(QWidget *parent)
: QWidget(parent, Qt::Window)
{}
You need to directly specify a flag that you need a "window" widget.
Or, if you don't want to set parent widget, you may set Qt::WA_DeleteOnClose attribute for automatic releasing memory. Note, that in this case you will have an invalid pointer inside your examWindows vector. It may be resolved if you will use next declaration: QVector<QPointer<ExamWindow>> examWindows;
ANSWER: it turned out that the problem was in this line:
examWindows[i] = new ExamWindow(this);
If I remove 'this', making my windows parentless, it works as intended. I'm puzzled why and I guess I have to delete them now manually.
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!
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);
So I'm trying to make a gui for a guitar effects program. One goal is to allow others to design svg "skins" to customize the look. I made widgets that inherited each widget (i.e. qdial) and in the initializer load the svg file specified by the skin into a qGraphicsSvgItem. This is put into a scene and a view and I overloaded the resize and repaint appropriately. This works.
When I load several of these custom svg widgets (5 dials, a button, and an led) into a parent widget the cpu rails and my program freezes. Am I going about this entirely the wrong way? Should I even be using QT? It seemed easier than trying to do everything with a stylesheet (especially I couldn't figure out how to change the dial appearance). Would it improve things to leave the widgets as qGraphicsSvgItems and put them all into the parent widget's scene and view?
This is meant to be a real time signal processing program so I don't want to burn up a lot of cpu in the GUI. I tried to do some research on it but couldn't find a lot for svg. I also don't see any performance checker in qtCreator or I'd try to see what the bottleneck is. Anyway I'd really appreciate any advice you could offer before I spend more time trying to do something the wrong way.
Thanks so much!
_ssj71
p.s.
Here is some of the code (hopefully enough)
an svg widget:
#ifndef QSVGDIAL_H
#define QSVGDIAL_H
#include <QWidget>
#include <QDial>
#include <QtSvg/QSvgRenderer>
#include <QtSvg/QGraphicsSvgItem>
#include <QGraphicsView>
#include <QGraphicsScene>
class qSVGDial : public QDial
{
Q_OBJECT
public:
explicit qSVGDial(QWidget *parent = 0);
explicit qSVGDial(QString knobFile = "defaultKnob.svg", QString needleFile = "defaultNeedle.svg", QWidget *parent = 0);
~qSVGDial();
private:
void paintEvent(QPaintEvent *pe);
void resizeEvent(QResizeEvent *re);
float degPerPos;
float middle;
float mysize;
QGraphicsView view;
QGraphicsScene scene;
QGraphicsSvgItem *knob;
QGraphicsSvgItem *needle;
QSize k,n;
};
#endif // QSVGDIAL_H
the cpp:
#include "qsvgdial.h"
#include "math.h"
qSVGDial::qSVGDial(QWidget *parent) :
QDial(parent)
{
knob = new QGraphicsSvgItem("defaultKnob.svg");
needle = new QGraphicsSvgItem("defaultNeedle.svg");
view.setStyleSheet("background: transparent; border: none");
k = knob->renderer()->defaultSize();
n = needle->renderer()->defaultSize();
needle->setTransformOriginPoint(n.width()/2,n.height()/2);
knob->setTransformOriginPoint(k.width()/2,k.height()/2);
degPerPos = 340/(this->maximum() - this->minimum());
middle = (this->maximum() - this->minimum())/2;
mysize = k.width();
if (mysize<n.width()) mysize = n.width();
if (mysize<k.height()) mysize = k.height();
if (mysize<n.height()) mysize = n.height();
mysize = sqrt(2)*mysize;
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(knob);
scene.addItem(needle);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
qSVGDial::qSVGDial(QString knobFile, QString needleFile, QWidget *parent) :
QDial(parent)
{
knob = new QGraphicsSvgItem(knobFile);
needle = new QGraphicsSvgItem(needleFile);
view.setStyleSheet("background: transparent; border: none");
k = knob->renderer()->defaultSize();
n = needle->renderer()->defaultSize();
needle->setTransformOriginPoint(n.width()/2,n.height()/2);
knob->setTransformOriginPoint(k.width()/2,k.height()/2);
if (k!=n)
needle->setPos((k.width()-n.width())/2,(k.height()-n.height())/2);
degPerPos = 340/(this->maximum() - this->minimum());
middle = (this->maximum() - this->minimum())/2;
mysize = k.width();
if (mysize<n.width()) mysize = n.width();
if (mysize<k.height()) mysize = k.height();
if (mysize<n.height()) mysize = n.height();
mysize = sqrt(2)*mysize;
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(knob);
scene.addItem(needle);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
qSVGDial::~qSVGDial()
{
//delete ui;
}
void qSVGDial::paintEvent(QPaintEvent *pe)
{
needle->setRotation((this->sliderPosition() - middle)*degPerPos);
}
void qSVGDial::resizeEvent(QResizeEvent *re)
{
if (this->width()>this->height())
{
view.setFixedSize(this->height(),this->height());
view.move((this->width()-this->height())/2,0);
knob->setScale(this->height()/mysize);
needle->setScale(this->height()/mysize);
view.centerOn(knob);
}
else
{
view.setFixedSize(this->width(),this->width());
view.move(0,(this->height()-this->width())/2);
knob->setScale(this->width()/mysize);
needle->setScale(this->width()/mysize);
view.centerOn(knob);
}
QDial::resizeEvent(re);
}
The parent header:
#ifndef PEDAL_H
#define PEDAL_H
#include <QtGui/QWidget>
#include <QString>
#include <QtSvg/QSvgRenderer>
#include <QtSvg/QGraphicsSvgItem>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <skin.h>
#include <qsvgdial.h>
#include <qsvgbutton.h>
#include <qsvgled.h>
#include <qsvgslider.h>
class Pedal : public QWidget
{
Q_OBJECT
public:
explicit Pedal(QWidget *parent = 0);
explicit Pedal(QString boxFile, QWidget *parent = 0);
~Pedal();
int LoadSkin(skin skinfiles);
QWidget* AddControl(QString type, QString param, int x, int y, int w, int h, QString file1, QString file2, QString file3, QString file4);
private:
void resizeEvent(QResizeEvent *re);
QRect PedalPosition();
float myheight;
float mywidth;
float scale;
int effectNumber;
QGraphicsView view;
QGraphicsScene scene;
QGraphicsSvgItem *box;
QSize p;
QWidget* controls[20];
QRect ctrlPos[20];
int numControls;
};
#endif // PEDAL_H
parent cpp
#include "pedal.h"
#include "math.h"
Pedal::Pedal(QWidget *parent)
: QWidget(parent)
{
numControls = 0;
box = new QGraphicsSvgItem("stompbox.svg");
view.setStyleSheet("background: transparent; border: none");
p = box->renderer()->defaultSize();
box->setTransformOriginPoint(p.width()/2,p.height()/2);
myheight = p.height();
mywidth = p.width();
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(box);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
Pedal::Pedal(QString boxFile, QWidget *parent) :
QWidget(parent)
{
numControls = 0;
box = new QGraphicsSvgItem(boxFile);
view.setStyleSheet("background: transparent; border: none");
p = box->renderer()->defaultSize();
box->setTransformOriginPoint(p.width()/2,p.height()/2);
myheight = p.height();
mywidth = p.width();
view.setDisabled(true);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scene.addItem(box);
view.setScene(&scene);
view.setParent(this,Qt::FramelessWindowHint);
}
Pedal::~Pedal()
{
}
void Pedal::resizeEvent(QResizeEvent *re)
{
view.setFixedSize(this->width(),this->height());
//view.move((this->width()-this->height())/2,(this->width()-this->height())/2);
if (this->width()/mywidth>this->height()/myheight)
{
scale = this->height()/myheight;
}
else
{
scale = this->width()/mywidth;
}
box->setScale(scale);
view.centerOn(box);
//QWidget::resizeEvent(re);
QRect v = PedalPosition();
QRect cpos;
for(int i = 0; i<numControls; i++)
{
cpos = ctrlPos[i];
controls[i]->setGeometry(v.x()+cpos.x()*scale,v.y()+cpos.y()*scale,cpos.width()*scale,cpos.height()*scale);
}
}
QWidget* Pedal::AddControl(QString type, QString param, int x, int y, int w, int h, QString file1, QString file2, QString file3, QString file4)
{
QWidget* control;
if (type.toLower() == "dial")
{
if (!file2.isEmpty())
control = new qSVGDial(file1,file2,this);
else
control = new qSVGDial(this);
}
else if (type.toLower() == "button")
{
if (!file2.isEmpty())
control = new qSVGButton(file1,file2,this);
else if (!file1.isEmpty())
control = new qSVGButton(file1,this);
else
control = new qSVGButton(this);
}
else if (type.toLower() == "slider")
{
if (!file2.isEmpty())
control = new qSVGSlider(file1,file2,this);
else if (!file1.isEmpty())
control = new qSVGSlider(file1,this);
else
control = new qSVGSlider(this);
}
else if (type.toLower() == "led")
{
if (!file2.isEmpty())
control = new qSVGLED(file1,file2,this);
else
control = new qSVGLED(this);
}
control->setToolTip(param);
ctrlPos[numControls] = QRect(x,360-y-h,w,h);
controls[numControls] = control;
numControls++;
return control;
}
QRect Pedal::PedalPosition()
{
QRect mypos;
mypos.setWidth(mywidth*scale);
mypos.setHeight(myheight*scale);
mypos.setX((this->width()-mypos.width())/2);
mypos.setY((this->height()-mypos.height())/2);
return mypos;
}
finally the test main
#include <QtGui/QApplication>
#include "pedal.h"
#include <string.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Pedal w("skins/default/stompbox.svg",0);
w.AddControl("Dial" , "Level", 133, 295, 47, 47, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Button", "On", 20, 21, 182, 111, "skins/default/blackButton.svg","","","");
w.AddControl("LED", "On", 106, 328, 11, 11, "skins/default/redLEDOff.svg", "skins/default/redLEDOn.svg","","");
w.AddControl("Dial", "Gain", 44, 295, 47, 47, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "Low", 36, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "Mid", 98, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.AddControl("Dial", "High", 160, 244, 31, 31, "skins/default/blackKnob.svg", "skins/default/whiteNeedle.svg","","");
w.show();
return a.exec();
}
hopefully this helps. As you can see I have layers of QGraphicsViews each with one widget. I suspect now this might be the worst way to do it so I wonder if anyone has more experience before I move forward in a bad direction. Also after playing some more with it, the problem seems to occur when I have 2 instances of the qSVGDial. If I load other combinations of widgets it works alright. Thanks again everyone!
void qSVGDial::paintEvent(QPaintEvent *pe)
{
needle->setRotation((this->sliderPosition() - middle)*degPerPos);
}
This looks very suspicious to me. Doing anything that could result in a redraw from the paintEvent method is dangerous, could lead to infinite update loops.
You should connect the dial's sliderMoved signal to a slot in your class and rotate the needle in that slot, and remove the paintEvent handler altogether. If that doesn't trigger an update, call update() in that slot too, but I don't think this should be necessary.
(To check if this is the problem in the first place, try printing something to the console inside the paintEvent handler. If it prints like crazy, that's your problem.)