My app display a long scientific vertically-scrollable picture (1024 x 99999999... px) as a sequence of QPixmap 1024x128 blocks. This allows me to scroll a picture with minimal CPU-cost by picking needed blocks from a table: block_id = y_coord/128. Also, QPixmap is preferred "pixel container" for fast screen output.
But now I have a stream of new data coming to the application and need the new data to be added and displayed at the bottom of the long picture. Minimal portion: 1024x1 (a line). Also, I would like to display each new line as soon as possible (close to real-time). Each new portion of 128 lines will be "packed" to QPixmap, but until I received enough data I cannot build a whole block.
What approach should I consider for displaying the new data?
This video gives an idea of "adding new lines of data", except in my case the flow goes up: http://www.youtube.com/watch?v=Dy3zyQNK7jM
You can simply, directly, modify the bottom row of QPixmaps and update() the window (if the bottom row is in range).
You might find using a QImage is more efficient for half-baked rows, depending on how quickly you update/repaint.
On contemporary Qt, when using the raster backend, QPixmap offers no benefits compared to QImage. Everything is rendered to a big QImage backing buffer that then gets blitted to the screen. So just use QImage.
You can have a QImage that is 128 pixels high, but you only draw the part of it that was already filled with data. The part without data is either not drawn, or hangs below the visible area of the window, and is thus effectively invisible.
Here is a quick example I put together. I don't know if it is the most efficient, but it shows off the basic idea you are looking at:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QVector>
#include <QGraphicsScene>
#include <QTimerEvent>
#define TILE_HEIGHT 128
#define TILE_WIDTH 1024
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
QPixmap generateLine();
public slots:
void timerEvent(QTimerEvent *);
private:
QGraphicsView * m_view;
QGraphicsScene * m_scene;
QVector <QGraphicsPixmapItem *> m_tiles;
QVector <QGraphicsPixmapItem *> m_lineBuffer;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QPixmap>
#include <QtGlobal>
#include <QDateTime>
#include <QTimer>
#include <QPaintEngine>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->setFixedWidth(TILE_WIDTH);
this->setCentralWidget(m_view = new QGraphicsView());
m_scene = new QGraphicsScene;
m_view->setScene(m_scene);
QPixmap p(TILE_WIDTH, TILE_HEIGHT);
p.fill(Qt::black);
m_tiles.append(new QGraphicsPixmapItem(p));
m_tiles.last()->setPos(0,0);
m_scene->addItem(m_tiles.last());
qsrand(QDateTime::currentMSecsSinceEpoch());
this->startTimer(0);
}
MainWindow::~MainWindow()
{
}
void MainWindow::timerEvent(QTimerEvent *)
{
// if your generated data is on another thread, you may want to do some thread
// synchronization with a Mutex and a Mutex Locker so you don't stomp on your
// buffers
// static bool busy = false;
// static int skipCount = 0;
// if(busy)
// {
// skipCount++;
// qDebug() << "Skipped Line count =" << skipCount;
// return;
// }
// busy = true;
// grab a new line
QPixmap linePix = generateLine();
int y = m_tiles.size()*TILE_HEIGHT + m_lineBuffer.size()*1;
// append it to the line buffer
m_lineBuffer.append(new QGraphicsPixmapItem(linePix));
// add it to the scene
m_scene->addItem(m_lineBuffer.last());
m_lineBuffer.last()->setPos(0, y);
// scroll it into view
m_view->ensureVisible(m_lineBuffer.last());
if(m_lineBuffer.size() >= TILE_HEIGHT)
{
// when the line buffer is "full"
// or ready to be made into a tile
// compile all the qpixmaps into a single "tile"
static QRectF source(0,0, TILE_WIDTH, 1);
QPixmap tile(TILE_WIDTH, TILE_HEIGHT);
QPainter painter;
painter.begin(&tile);
for(int i = 0; i < m_lineBuffer.size(); i++)
{
painter.drawPixmap(QRectF(0, i, TILE_WIDTH, 1),
m_lineBuffer.at(i)->pixmap(),
source);
}
painter.end();
// add it into the tiles list
m_tiles.append(new QGraphicsPixmapItem(tile));
// add it to the scene
m_tiles.last()->setPos(0, (m_tiles.size() - 1)*TILE_HEIGHT);
m_scene->addItem(m_tiles.last());
// scroll it into view
m_view->ensureVisible(m_tiles.last());
// Clean up the line buffer
foreach(QGraphicsPixmapItem * pi, m_lineBuffer)
{
m_scene->removeItem(pi);
delete pi;
}
m_lineBuffer.clear();
}
// busy = false;
}
QPixmap MainWindow::generateLine()
{
// create a random pixmap of TILE_WIDTH x 1
static int img_width = TILE_WIDTH;
QImage img(img_width,1, QImage::Format_RGB16);
for(int i = 0; i< img_width; i++)
{
img.setPixel(i, 0, qrand()%65536);
}
return QPixmap::fromImage(img);
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.showMaximized();
return a.exec();
}
Related
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.
I'am stuck in one case. I have scene (using QGraphicsScene) and I fill that scene with squares (using QGraphicsRectItem). I want make every square color to black as I move mouse over squares with mouse button pressed. Can you please give me any idea how to make that happen ? I was trying to solve that using mousePressEvent, mouseMoveEvent, dragEnterEvent etc. and I think that this is a proper way to do that but I have no idea how to push that through. To put more light on my case I have added sample of my code. Thanks for help.
main.cpp
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include "square.h"
#include "background.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// create a scene
QGraphicsScene * scene = new QGraphicsScene(0,0,200,250);
Background * background = new Background();
background->fillBackgroundWithSquares(scene);
// add a view
QGraphicsView * view = new QGraphicsView(scene);
view->show();
return a.exec();
}
background.h
#ifndef BACKGROUND_H
#define BACKGROUND_H
#include <QGraphicsScene>
#include <square.h>
class Background
{
public:
Background();
void fillBackgroundWithSquares(QGraphicsScene *scene);
};
#endif // BACKGROUND_H
background.cpp
#include "background.h"
Background::Background()
{
}
void Background::fillBackgroundWithSquares(QGraphicsScene *scene)
{
// create an item to put into the scene
Square *squares[20][25];
// add squares to the scene
for (int i = 0; i < 20; i++)
for (int j = 0; j < 25; j++) {
squares[i][j] = new Square(i*10,j*10);
scene->addItem(squares[i][j]);
}
}
square.h (EDIT)
#ifndef SQUARE_H
#define SQUARE_H
#include <QGraphicsRectItem>
#include <QGraphicsView>
class Square : public QGraphicsRectItem
{
public:
Square(int x, int y);
private:
QPen pen;
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent * event);
};
#endif // SQUARE_H
square.cpp (EDIT)
#include "square.h"
Square::Square(int x, int y)
{
// draw a square
setRect(x,y,10,10);
pen.setBrush(Qt::NoBrush);
setPen(pen);
setBrush(Qt::cyan);
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
show();
}
void Square::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
if ( brush().color() != Qt::black && QApplication::mouseButtons() == Qt::LeftButton)
{
setBrush( Qt::black );
update();
}
}
Try calling:
square[j][j]->setAcceptedMouseButtons(...)
and
square[i][j]->show()
after creating it.
You can also reimplement hoverEnterEvent() and hoverLeaveEvent() if you want to change the color on the hover event.
If the mouse butten needs to be pressed when you hover: you store button state within mouse down/up event in a variable for ex. bool isMouseButtonDown and check this in your hover event handler.
You can also use: QApplication::mouseButtons() to check the buttons states.
As my work are going well on my long project of QT, I've tried to resolve my actual steps, but I think it is not working, so I have one question:
Draw points from data file on QGraphicsView
My code is writting actually at the end of the process a data file where the first line is the number of elements (size of the vector) and the other lines the values of the point with x and y separated by a blank.
res.dat
250
12 23
30 40
25 67
...
I would like to read this file (with the fstream) and to display each points of my vector on a QGraphicsView in order to have a graphic result, and ideally but not the most important result, to lie this point to have the desired form.
I have already tried this part of code on a main source file to test, but it is just doing a circle and I think it is not reading my file.
main.cpp
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPointF>
#include <QVector>
#include <QApplication>
#include <fstream>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int n;
std::ifstream ifs1("res.dat");
ifs1 >> n;
QVector <QPointF> points(n);
// Create a view, put a scene in it and add tiny circles
// in the scene
QGraphicsView * view = new QGraphicsView();
QGraphicsScene * scene = new QGraphicsScene();
view->setScene(scene);
for(int i = 1; i< n; i++)
scene->addEllipse(points[i].x(), points[i].y(), 512, 512);
// Show the view
view->show();
return a.exec();
}
Thank you in advance for reading, and for your response.
From your code it's completely missing the part where you read the actual coordinates of the points.
I have changed slightly your code to read the points into the vector but please remember:
a) This code is all but "solid" for example if your res.dat file is just a bit out of format it crashes.
b) With this loop you really do not need to know how many points you have in the file, right now it keeps reading until the end of file.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int n;
std::ifstream ifs1("res.dat");
ifs1 >> n;
QVector <QPointF> points;
qreal px,py;
while (ifs1 >> px) {
ifs1 >> py;
points.append(QPointF(px,py));
}
// Create a view, put a scene in it and add tiny circles
// in the scene
QGraphicsView * view = new QGraphicsView();
QGraphicsScene * scene = new QGraphicsScene();
view->setScene(scene);
foreach (QPointF point, points)
scene->addEllipse(point.x(), point.y(), 51, 51);
// Show the view
view->show();
return a.exec();
}
After you helped me, I've gone further in my code, and I have a problem I don't understand.
in the header file
//All includes
class ProjectWindow;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
// GraphicsView 1
QGraphicsView view1;
QGraphicsScene scene1;
QGraphicsPixmapItem item1;
QGraphicsPixmapItem item2;
QString fileName;
// GraphicsView 2
QGraphicsScene scene2;
QGraphicsView view2;
QVector <QPointF> points;
qreal px,py;
QPointF point;
QPainter painter;
QGraphicsEllipseItem *ellipse;
public slots:
void openBrowser();
void drawcirc();
void drawCircle();
void runSnakes();
void displayResult();
};
#endif // MAINWINDOW_H
in the .cpp file
void MainWindow::displayResult()
{
scene2.removeItem(&item2); //removing a picture
scene2.addItem(&item2); // adding a picture
scene2.setSceneRect(scene2.itemsBoundingRect());
// Reading res.dat file and plot the contour
int m;
std::ifstream ifs2("final_contour.dat");
ifs2 >> m;
std::cout << m;
while (ifs2 >> px)
{
ifs2 >> py;
points.append(QPointF(py,px));
}
foreach(point, points)
ellipse = scene2.addEllipse(QRectF(point.y(), width-point.x(),1,1),QPen(Qt::red));
// Saving the final contour into a picture
QImage pixmap(width, width, QImage::Format_ARGB32_Premultiplied);
painter.begin(&pixmap);
painter.setRenderHint(QPainter::Antialiasing, false);
scene2.render(&painter);
painter.end();
pixmap.save("finalcontour.bmp", "BMP");
}
I want to remove the ellipse I add to the scene with removeItem() (in order to see the differents results with changing parameters), so I need to create a QGraphicsEllipseItem and to tell to the scene, addItem() that I can remove later the item with removeItem().
the header file
//All includes
class ProjectWindow;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
// GraphicsView 1
QGraphicsView view1;
QGraphicsScene scene1;
QGraphicsPixmapItem item1;
QGraphicsPixmapItem item2;
QString fileName;
// GraphicsView 2
QGraphicsScene scene2;
QGraphicsView view2;
QVector <QPointF> points;
qreal px,py;
QPointF point;
QPainter painter;
QGraphicsEllipseItem ellipse; // The only line changed
public slots:
void openBrowser();
void drawcirc();
void drawCircle();
void runSnakes();
void displayResult();
};
#endif // MAINWINDOW_H
the cpp file
void MainWindow::displayResult()
{
scene2.removeItem(&item2); //removing a picture
scene2.addItem(&item2); // adding a picture
scene2.setSceneRect(scene2.itemsBoundingRect());
// Reading res.dat file and plot the contour
int m;
std::ifstream ifs2("final_contour.dat");
ifs2 >> m;
std::cout << m;
while (ifs2 >> px)
{
ifs2 >> py;
points.append(QPointF(py,px));
}
foreach(point, points)
ellipse.setRect(QRectF(point.y(), width-point.x(),1,1));
ellipse.setPen(QPen(Qt::red));
scene2.addItem(&ellipse);
// Saving the final contour into a picture
QImage pixmap(width, width, QImage::Format_ARGB32_Premultiplied);
painter.begin(&pixmap);
painter.setRenderHint(QPainter::Antialiasing, false);
scene2.render(&painter);
painter.end();
pixmap.save("finalcontour.bmp", "BMP");
}
But changing the way I want to add the ellipse, it is not displaying the whole points like this with the first way I define the ellipse:
But just one point and not the vector of points, like this:
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!
I want to animate small (100x20) image by changing the color of its pixels by the same value. For example, increase red-channel value by 1 every frame and then decrease back. The image has alpha channel, the animation speed is 30...100 fps (platform dependent; 30 is enough for linux, but windows requires ~70 to look smooth).
As i know, drawing is faster when done in QImage, but displaying is faster with QPixmap.
I like QGraphicsEffects, and QPropertyAnimations. White doesn't colorize, but black does.
#include <QLabel>
#include <QPixmap>
#include <QGraphicsColorizeEffect>
#include <QTimerEvent>
#include <QPropertyAnimation>
#include <QShowEvent>
#include <QDebug>
class Widget : public QLabel
{
Q_OBJECT
Q_PROPERTY(qreal redness READ getRedness WRITE setRedness)
public:
Widget(QWidget *parent = 0)
{
QPixmap p(300, 300);
p.fill(Qt::black);
this->setPixmap(p);
colorize = new QGraphicsColorizeEffect();
colorize->setColor(Qt::red);
redness = 0;
colorize->setStrength(redness);
this->setGraphicsEffect(colorize);
animation = new QPropertyAnimation(this,"redness");
animation->setDuration(2000);
animation->setLoopCount(10);
animation->setStartValue(0.0);
animation->setEndValue(1.0);
animation->setEasingCurve(QEasingCurve::CosineCurve);
animation->start();
}
~Widget(){}
qreal getRedness()
{
return redness;
}
void setRedness(qreal val)
{
redness = val;
colorize->setStrength(redness);
this->update();
// qDebug() << redness;
}
public slots:
void showEvent(QShowEvent *)
{
animation->start();
}
private:
qreal redness;
QGraphicsColorizeEffect * colorize;
QPropertyAnimation * animation;
};
and here is the main.cpp
#include <QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
Hope that helps.