Showing a captured image onClicked - qt

I have a robot that has a camera, which I use to capture image and view it on QT Application. The camera output is three [40][40] arrays for RGB colors respectively. I have tried QPaintEvent but it cannot be used as a slot and I found it like inventing the wheel.
gui::EpuckImage image = this->epuck->cameraShot();
int r[40][40],g[40][40],b[40][40];
for(int i=0;i<40;i++){
for(int j=0;j<40;j++){
image.getRGB(i,j,r[i][j],g[i][j],b[i][j]);
}
}
So, I have 40 x 40 RGB Pixels.
void MainWindow::paintEvent(QPaintEvent *event, int r[40][40], int g[40][40], int b[40][40]){
QPainter painter(this);
painter.setBrush(Qt::DiagCrossPattern);
QPen pen;
QColor color(100,0,0, 255);
pen.setColor(color);
pen.setWidth(5);
for(int i=0;i<40;i++){
for(int j=0;j<40;j++){
QColor color(r[i][j],g[i][j],b[i][j], 255);
pen.setColor(color);
painter.setPen(pen);
painter.drawPoint(i,j);
}
}
}
I need to use this code to draw the pixels on click

Related

Superposing qimage with transparency

I try to superpose 3 QImage but I have some warnings, for example :
QImage::pixelColor: coordinate (31,30) out of range
And the result of the blending is a black image.
Here my code :
QBrush MainWindow::blendLayer(int x, int y){
QImage blend(m_layer1_data[x][y]->background().textureImage());
QImage l2(m_layer2_data[x][y]->background().textureImage());
QImage l3(m_layer3_data[x][y]->background().textureImage());
for(int a = 0; a < blend.width(); a++){
for(int b = 0; b < blend.height(); b++ ){
blend.setPixelColor(a,b,blendColor(blend.pixelColor(a,b),l2.pixelColor(a,b),l3.pixelColor(a,b)));
}
}
QBrush brush(blend);
return brush;
}
QColor MainWindow::blendColor(QColor c2, QColor c1){
QColor c3;
c3.setRed(((c1.red()+c2.red())/2)%256);
c3.setGreen(((c1.green()+c2.green())/2)%256);
c3.setBlue(((c1.blue()+c2.blue())/2)%256);
c3.setAlpha(((c1.alpha()+c2.alpha())/2)%256);
return c3;
}
QColor MainWindow::blendColor(QColor c1, QColor c2, QColor c3){
return blendColor(c3,blendColor(c1,c2));
}
Is there an easy way to superposing some QImage ?
Thanks for your help
Like Kuba Ober mentioned in the comments, the best way is to simply use QPainter in a way like this:
//[...]
QPainter p(&myWidgetOrSurface);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.drawRect(myWidgetOrSurface.size());
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawImage(image1);
p.end();
p.begin(image2);
p.drawImage(surface);
p.end();
//[...]
Here is the documentation for the different blend modes supported by Qt and QPainter.
Thanks a lot I've found a solution with QPainter
QBrush MainWindow::blendLayer(int x, int y){
QImage blend(m_layer1_data[x][y]->background().textureImage());
QImage l2(m_layer2_data[x][y]->background().textureImage());
QImage l3(m_layer3_data[x][y]->background().textureImage());
QImage im(QSize(32,32),QImage::Format_ARGB32);
QPainter painter(&im);
painter.drawImage(im.rect(),blend);
painter.drawImage(im.rect(),l2);
painter.drawImage(im.rect(),l3);
QBrush brush(im);
return brush;
}

Drawing thousands of rects quickly

What is the proper way to draw thousands of rects in QT (around 100,000 or more)?
I tried:
Simple with paintEvent() of QWidget.
Drawing objects to QImage then this image to QWidget.
Using QGraphicsScene (maybe I didn't use it properly, I just added rects to scene)
Every time drawing was really slow and I don't have more ideas on how to do this (maybe with opengl/directx but this doesn't sound like a good idea). I know that there exist applications that do that so there should be some way.
EDIT:
I wonder how drawRects() work? Is there a chance that filling some uchar* array and passing it to QImage will be better?
The first trick is to do the drawing in a separate thread onto a QImage, then pass that into the main thread. This won't make it quicker, but it'll make it not block the GUI thread.
// https://github.com/KubaO/stackoverflown/tree/master/questions/threaded-paint-36748972
#include <QtWidgets>
#include <QtConcurrent>
class Widget : public QWidget {
Q_OBJECT
QImage m_image;
bool m_pendingRender { false };
Q_SIGNAL void hasNewRender(const QImage &);
// Must be thread-safe, we can't access the widget directly!
void paint() {
QImage image{2048, 2048, QImage::Format_ARGB32_Premultiplied};
image.fill(Qt::white);
QPainter p(&image);
for (int i = 0; i < 100000; ++i) {
QColor c{rand() % 256, rand() % 256, rand() % 256};
p.setBrush(c);
p.setPen(c);
p.drawRect(rand() % 2048, rand() % 2048, rand() % 100, rand() % 100);
}
emit hasNewRender(image);
}
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.drawImage(0, 0, m_image);
}
public:
Widget(QWidget * parent = 0) : QWidget(parent) {
this->setAttribute(Qt::WA_OpaquePaintEvent);
setMinimumSize(200, 200);
connect(this, &Widget::hasNewRender, this, [this](const QImage & img) {
m_image = img;
m_pendingRender = false;
update();
});
refresh();
}
Q_SLOT void refresh() {
if (!m_pendingRender) {
m_pendingRender = true;
QtConcurrent::run([this] { paint(); });
}
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Widget w;
QPushButton button{"Refresh", &w};
button.connect(&button, &QPushButton::clicked, &w, [&w]{ w.refresh(); });
w.show();
return app.exec();
}
#include "main.moc"
As a separate concern, you can then split the drawing across multiple parallel jobs, by clipping each job's painter to a sub-area of the shared image, and noting that fully clipped rectangle draws are no-ops, and partially clipped ones will only fill the pixels they affect.
Solution which I found:
Create array of uint32_t which can contain all pixels of widget, fill it using memcpy(). Create QImage with this array and use drawImage() to show it.
It can have some optimization like (for profiler) merging rects that are continues ( start time second is equal to end of first ). Don't draw rects that are out of time bounds. Maybe skip too small rects.
For drawing things like text, tool tips you can still use Qt functions.
For alpha blending in simplest case you can just take existing values, blend them in loop and write blended values or maybe use SIMD for this.
Of course for more complex shapes it will get harder to draw but still, I think, it will be faster than using Qt functions.

QPainter::drawLine and QPainter::drawText with different color issue in Qt

I am facing problem for drawing line and text with different color using QPainter. I am using the following piece of code to achieve this but it's not working. Both lines and texts are drawn using the color set for drawing Text.
void MyWidget::drawHorLinesWithText(QPainter & painter, const QRect & rect)
{
for(int i=0; i < 5; i++)
{
QPen penHLines(QColor("#0e5a77"), 1, Qt::DotLine, Qt::FlatCap, Qt::RoundJoin);
painter.setPen(penHLines);
painter.drawLine(10, 50 - (5*(i+1)), 200, 50 - (5*(i+1)));
QString strNumber = QString::number((2)*(i+1));
painter.setFont(QFont("Arial", 8, QFont::Bold));
//QBrush brush(QColor("#00e0fc"));
//painter.setBrush(brush);
QPen penHText(QColor("#00e0fc"));//Here lines are also drawn using this color
painter.setPen(penHText);
painter.drawText(5, 50 - (5*(i+1)) - 10), 20, 30, Qt::AlignHCenter | Qt::AlignVCenter,
strNumber);
}
}
How would I set different colors for drawing lines and Texts. Any suggestions. Thanks.
This works for me with Qt 5.3; perhaps it was a bug in the version you were using?
#include <QtWidgets>
class Widget : public QWidget
{
public:
Widget() {
resize(200, 200);
}
void paintEvent(QPaintEvent *) {
QPainter painter(this);
for(int i=0; i < 5; i++)
{
QPen penHLines(QColor("#0e5a77"), 10, Qt::DotLine, Qt::FlatCap, Qt::RoundJoin);
painter.setPen(penHLines);
painter.drawLine(10, 50 - (5*(i+1)), 200, 50 - (5*(i+1)));
QString strNumber = QString::number((2)*(i+1));
painter.setFont(QFont("Arial", 8, QFont::Bold));
QPen penHText(QColor("#00e0fc"));
painter.setPen(penHText);
painter.drawText(5, 50 - (5*(i+1)) - 10, 20, 30, Qt::AlignHCenter | Qt::AlignVCenter, strNumber);
}
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
I increased the line width to 10 to see what's going on:
QPainter draw text using QBrush, not QPen. Text is rendered with glyph strokes then filled with current brush. Current pen only controls lines and strokes.

Set a cross in QImage at the position, where mousepressed

I have a graph shown in a QImage and want to set a cross (+) in yellow colour for measurement, if right mouse button is pressed.
void foo::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
QPoint pos = event->pos();
int x = pos.x();
int y = pos.y();
QLine line(x-5,y,x+5,y);
QLine line(x,y-5,x,y+5);
QPainter painter(&my_image);
painter.setPen( Qt::red );
painter.setBrush( Qt::yellow );
/*
QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
*/
painter.drawLine(line); //no effect
}
}
if I do it in Paintevent(...), I destroy the original pic. how can I do it.
additional Information:
the imag is indexed.
my_image.setColorCount(33);
for(int i = 0;i<33;i++)
{
my_image.setColor(i,qRgb((unsigned char)palette[i*3], (unsigned char)palette[i*3+1], (unsigned char)palette[i*3+2]));
}
my_imag has a black-Background and I want to draw a cross in white color --> (this is the index 32)
int color = 32;//_index_value_of_cross_color;
for (int ix=x-5;ix<x+5;ix++) {
my_image.setPixel(ix,y,color);
}
for (int iy=y-5;iy<y+5;iy++) {
my_imag.setPixel(x,iy,color);
}
but I see no effect !
From your comments, you cannot paint on a QImage with Format_Indexed8.
From the QImage docs:
Warning: Painting on a QImage with the format QImage::Format_Indexed8 is not supported.
Choose a different format like QImage::Format_ARGB32_Premultiplied and things should work.
Another quick and dirty alternative is to simply set the values in the image data.
You will have to do a little more work - because there is no line command, see setpixel
int x = pos.x();
int y = pos.y();
int color = _index_value_of_cross_color;
for (int ix=x-5;ix<x+5;ix++) {
my_image.setPixel(ix,y,color);
}
for (int iy=y-5;iy<y+5;iy++) {
my_image.setPixel(x,iy,color);
}

Animated QGraphicsItem with dashed pen

I'd like to create a rotating circle drawn with a Qt:DotLine pen, using the Graphics View Framework. Using QGraphicsItemAnimation, I can rotate other shapes but not the circle. The program below demonstrates the problem: instead of the rectangle and the circle rotating together, the circle jerks around while the rectangle rotates gracefully.
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsItem>
#include <QTimeLine>
#include <QGraphicsItemAnimation>
QRectF rect (int r)
{
return QRectF (-r, -r, r * 2, r * 2);
}
void setupRot (QTimeLine *timeline, QGraphicsItem *item)
{
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation;
animation->setItem(item);
animation->setTimeLine(timeline);
animation->setRotationAt (1, 360);
QObject::connect (timeline, SIGNAL(finished()), animation, SLOT(deleteLater()));
}
int main(int argc, char *argv[])
{
QApplication app (argc, argv);
QGraphicsScene scene;
QTimeLine *timeline = new QTimeLine;
timeline->setDuration (3000);
timeline->setCurveShape (QTimeLine::LinearCurve);
QObject::connect (timeline, SIGNAL(finished()), timeline, SLOT(deleteLater()));
setupRot (timeline, scene.addEllipse (rect (50), QPen (QBrush (QColor ("blue")), 8, Qt::DotLine)));
setupRot (timeline, scene.addRect (rect (60)));
scene.addEllipse (rect (40), QPen (QBrush (QColor ("red")), 8));
scene.setSceneRect (-100, -100, 200, 200);
QGraphicsView view (&scene);
view.show ();
timeline->setLoopCount (0);
timeline->start();
return app.exec ();
}
p.s.: I've found some sample code on the web where people are creating intermediate animation steps manually, like this:
const int steps = 100;
for (int i = 0; i < steps; ++i)
animation->setRotationAt (i / (float)steps, 360 / (float)steps * i);
Is this just a sign of people not understanding the concept of interpolation, or is there some advantage of setting (seemingly superfluous) control points?
Which version/platform? If I run your code as is (or slowed down 2x), the dotted circle rotation looks as good as the rectangle in Windows with Qt 4.7.

Resources