I have a matrix with boolean values that I'm trying to plot with QImage. I have imported the image from a uchar vector with 0 and 255 values for false and true respectively. I would like to have black and white squares representing the different values, but instead I get blur between the pixels as shown in the picture here. Could you please let me know how to get rid of the blur and display larger pixels?
The code is as follows:
for(int i=0; i<world.size(); ++i) {
if(world[i] == true)
world_uchar[i] = 255;
else
world_uchar[i] = 0;
}
QImage img(world_uchar, cols, rows, QImage::Format_Indexed8);
QLabel myLabel;
myLabel.setPixmap(QPixmap::fromImage(img));
myLabel.setScaledContents(true);
myLabel.show();
Instead of using setScaledContents I draw a resized image every times that the window size changes, or on the image change. See an example below (on that example I've used QMainWindow):
class MainWindow : public QMainWindow
{
Q_OBJECT
...
private:
QPixmap pixmap;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->label->setAlignment(Qt::AlignCenter);
//SetMinminumSize to ensure that the QLabel
//can be resized
ui->label->setMinimumSize(10, 10);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QBitArray world(256, false);
uchar *world_uchar = new uchar[world.size()];
int cols, rows;
cols = rows = (int)qSqrt(world.size());
for (int i = 0; i < world.size(); i += 2)
world.setBit(i, (qrand() % ((1 + 1) - 0) + 0));
for (int i = 0; i < world.size(); i++)
{
if (world[i])
world_uchar[i] = 255;
else
world_uchar[i] = 0;
}
QImage img(world_uchar, cols, rows, QImage::Format_Indexed8);
pixmap = QPixmap::fromImage(img);
OnResize();
}
void MainWindow::resizeEvent(QResizeEvent *)
{
OnResize();
}
void MainWindow::OnResize()
{
if (!pixmap.isNull())
ui->label->setPixmap(pixmap.scaled(ui->label->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
}
Related
I made an Image Editor in Qt / OpenCV where you can load the Image from the File explorer and grayscale/adaptive threshold/resize it afterwards.
Bug 1: When I resize the Loaded Image to (for example) 600x600 Pixels using my ImageProcessor::Resize(int, int) method, it works fine. But when I change it to like 546x750 Pixels, the Image has a weird grayscale.
Bug 2: When I want to resize my Grayscaled/Thresholded Image, it always gets a weird grayscale similiar to Bug 1.
Codes:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "resizer.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::Display(cv::Mat inputImage)
{
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_RGB888);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
void MainWindow::on_actionOpen_triggered()
{
QString file = QFileDialog::getOpenFileName(this, "Open", "", "Images (*.jpg *.png)");
std::string filename = file.toStdString();
inputImage = cv::imread(filename);
Display(inputImage);
imgProc = new ImageProcessor(inputImage);
}
void MainWindow::on_pushButton_clicked() // Grayscale
{
scene->clear();
imgProc->mode = 1;
inputImage = imgProc->Grayscale();
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_Grayscale8);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
void MainWindow::on_pushButton_2_clicked() // ADT
{
scene->clear();
imgProc->mode = 2;
inputImage = imgProc->AdaptiveThreshold();
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_Grayscale8);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
void MainWindow::on_pushButton_3_clicked() // Resize
{
scene->clear();
Resizer resizer;
resizer.exec();
int newWidth = resizer.GetWidth();
int newHeight = resizer.GetHeight();
inputImage = imgProc->Resize(newWidth, newHeight);
if(imgProc->mode == 1 || imgProc->mode == 2)
{
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_Grayscale8);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
else
{
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_RGB888);
scene->addPixmap(QPixmap::fromImage(image));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
}
}
imageprocessor.cpp
#include "imageprocessor.h"
ImageProcessor::ImageProcessor(cv::Mat inputImage)
{
this->inputImage = inputImage;
}
cv::Mat ImageProcessor::Resize(int width, int height)
{
cv::Mat resized;
cv::resize(inputImage, resized, cv::Size(width, height), cv::INTER_LINEAR);
return resized;
}
cv::Mat ImageProcessor::Grayscale()
{
cv::Mat grayscaled;
cv::cvtColor(inputImage, grayscaled, cv::COLOR_RGB2GRAY);
return grayscaled;
}
cv::Mat ImageProcessor::AdaptiveThreshold()
{
cv::Mat binarized, grayscaled;
cv::cvtColor(inputImage, grayscaled, cv::COLOR_RGB2GRAY);
cv::adaptiveThreshold(grayscaled, binarized, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 15, 11);
return binarized;
}
QImage::Format_RGB888 is the format type you defined means that:
The image is stored using a 24-bit RGB format (8-8-8).
If your image has 3 channels then your way is correct to continue, except adding this:
QImage image = QImage(inputImage.data, inputImage.cols, inputImage.rows, QImage::Format_RGB888).rgbSwapped();
You need to add at the end rgbSwapped() because Qt reads it in RGB order as OpenCV gives BGR.
If you want to send a gray scale image to GUI then you need to use QImage::Format_Grayscale8 format type, which means:
The image is stored using an 8-bit grayscale format.
Here is the clear cocumentation for the formats.
Note: How do you resize your image, by using OpenCV function ? Share resizer.h , I will update the answer accordingly.
I am trying to make something like network on Qt but while running the program I faced a weird error all the drawings disappear when I click anywhere outside the running program or open any other application.
here is the code
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setFixedSize(210*6+10,this->height());
Lobes["Frontal Lobe"] = {"Higher Mental \n function",
"Broca's area",
"Motor Function\n(Eye Movement)"};
Lobes["Partial Lobe"] = {"Motor Function\n(Muscles)",
"Somatosensory \n association\narea",
"Sensory Area"};
Lobes["Temporal Lobe"] = {"Association area",
"Auditory area",
"Wernicke's area"};
Lobes["Occiptal Lobe"] = {"Visual area"};
SizeCalculations();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::red);
it = Lobes.begin();
int space = 10;
for(int x = 0; x < 6; x++)
{
if(x < 6)
{
Node *Rec = new Node(CognitveSpace,50,CogitiveFunctions[x],0,szCogntive);
painter.drawRect(Rec->getRectangle());
painter.drawText(Rec->getRectangle(), Qt::AlignCenter, Rec->getName());
// painter.fillRect(Rec->getRectangle(),Qt::red);
CognitveSpace += szCogntive.width()+10;
}
if(x < 4)
{
Node *Rec = new Node(LobesSpace,250,it->first,0,szLobes);
painter.drawRect(Rec->getRectangle());
painter.drawText(Rec->getRectangle(), Qt::AlignCenter, Rec->getName());
// delete Rec;
for(size_t index = 0; index < it->second.size();index++)
{
Node *Rec = new Node(AreasSpace,450,it->second[index],0,szAreas);
painter.drawRect(Rec->getRectangle());
painter.drawText(Rec->getRectangle(), Qt::AlignCenter, Rec->getName());
space += 180;
//delete Rec;
AreasSpace += szAreas.width()+10;
}
it++;
LobesSpace += szLobes.width()+10;
AreasSpace = LobesSpace +15;
}
}
}
void Widget::SizeCalculations()
{
szCogntive.setWidth((this->width() / 6) - CognitveSpace);
szLobes.setWidth((this->width() / 4) - LobesSpace);
szAreas.setWidth(szLobes.width()/3 - 15);
}
void Widget::on_pushButton_clicked()
{
}
It's a simple problem: the paint event should be, effectively, const. You're modifying various sizes repeatedly, on each repaint. But we can have the compiler help you out - let's modify the code to make your object invariant in painting:
void Widget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
paintEvent(event, painter);
}
void Widget::paintEvent(QPaintEvent *, QPainter &painter) const { // const is important!
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::red);
// etc - rest of the code follows
}
// Declaration in the class
void paintEvent(QPaintEvent *, QPainter &) const; // const!!
Now compile - the compiler will flag as errors all the locations where you modify the object. You'll need to move those computations elsewhere, so that painting doesn't modify the object. Then it'll work.
I'm having a bit of a problem with Qt. I'm trying to create a 2D drawing of cells, with QRect, by overloading paintEvent for a custom class which inherits QWidget and which is placed inside a QScrollArea. The problem is, paintEvent does not trigger at all (not on resize events, not when I call repaint() or update(), nor when I launch my program). Here is where I overload paintEvent, in GOL.cpp:
void GOL::paintEvent(QPaintEvent *) {
QPainter painter(this);
//painter.setPen(Qt::black);
int x1Rect = rectPaint.x();
int y1Rect = rectPaint.y();
int x2Rect = x1Rect + rectPaint.width();
int y2Rect = y1Rect + rectPaint.height();
int xCell;
int yCell = 0;
for (int i = 0; i < rows; i++) {
xCell = 0;
for (int j = 0; j < cols; j++) {
if (xCell <= x2Rect && yCell <= y2Rect && xCell + cellSize >= x1Rect &&
yCell + cellSize >= y1Rect) {
if (principalMatrix->get(i,j)) {
painter.fillRect(xCell, yCell, cellSize - 1, cellSize - 1, cellColourAlive);
}
else {
painter.fillRect(xCell, yCell, cellSize - 1, cellSize - 1, cellColourDead);
}
}
xCell += cellSize;
}
yCell += cellSize;
}
}
And my layout is as follows, in DisplayGame.cpp:
DisplayGame::DisplayGame(QWidget *parent, int threads_no, int generations, char* file_in, char* file_out) :
QWidget(parent) {
gol = new GOL(threads_no, generations, file_in, file_out);
QHBoxLayout *title = setupTitle();
QHBoxLayout *buttons = setupButtons();
QVBoxLayout *layout = new QVBoxLayout();
scrlArea = new QScrollArea;
scrlArea->setWidget(gol);
layout->addLayout(title);
layout->addWidget(scrlArea);
layout->addLayout(buttons);
setLayout(layout);
}
I honestly have no idea why it does not draw anything. Any ideas?
I've fixed it by modifying as follows:
DisplayGame::DisplayGame(QWidget *parent, int threads_no, int generations, char* file_in, char* file_out) :
QWidget(parent) {
gol = new GOL(this, threads_no, generations, file_in, file_out);
QSize *adjustSize = new QSize(gol->cellSize, gol->cellSize); //QSize object that is as big as my QRect matrix
adjustSize->setWidth(gol->cellSize * gol->rows);
adjustSize->setHeight(gol->cellSize * gol->cols);
gol->setMinimumSize(*adjustSize);
QVBoxLayout *layout = new QVBoxLayout;
QHBoxLayout *title = setupTitle();
layout->addLayout(title);
QHBoxLayout *buttons = setupButtons();
layout->addLayout(buttons);
QPalette pal(palette()); //Setting the background black, so the white spaces between QRect items cannot be seen (though I could have modified the margins?)
pal.setColor(QPalette::Background, Qt::black);
scrlArea = new QScrollArea(this);
scrlArea->setAutoFillBackground(true);
scrlArea->setPalette(pal);
scrlArea->setWidget(gol);
layout->addWidget(scrlArea);
setLayout(layout);
}
And I've left the paintEvent as it was. It was, ultimately, a size problem, as AlexanderVX said.
i want to draw some rhombuses with random colors in a Qwidget. The widget should be repainted only when the window is resized .The problem is that when the widget was obscured and has now been uncovered , it is repainted. How can i avoid calling paintEvent() in this case? Thanks in advance.
void Dialog::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
QRect background(0,0,this->geometry().width(),this->geometry().height());
painter.setBrush( QBrush( Qt::white ) );
painter.setPen( Qt::NoPen );
// QBrush bbrush(Qt::black,Qt::SolidPattern);
painter.drawRect(background);
int width = this->geometry().width();
int height = this->geometry().height();
int rec_size=64;
int rows=floor((double)height/(double)rec_size);
int cols=floor((double)width/(double)rec_size);
QPointF points[4];
for (int i=0;i<floor(rows);i++)
{
for (int j=0;j<floor(cols);j++)
{
painter.setBrush( QBrush( colors[rand() % color_size] ) );
points[0] = QPointF(rec_size*(j),rec_size*(i+0.5));
points[1] = QPointF(rec_size*(j+0.5),rec_size*(i));
points[2] = QPointF(rec_size*(j+1),rec_size*(i+0.5));
points[3] = QPointF(rec_size*(j+0.5),rec_size*(i+1));
painter.drawPolygon(points, 4);
}
}
}
You are presupposing a solution, where in fact you should be focusing on the problem instead. Your problem isn't about when paintEvent is called. Qt's semantics are such that the paintEvent can be called at any time in the main thread. You must cope with it.
What you need to do, instead, is store the randomized colors in a container, to re-use them when painting. In the example below, the colors are generated on demand, and are stored in a dynamically growing list indexed by row and column. This way when you resize the widget, the colors of the existing items don't have to change. You can then regenerate the colors at any time by simply clearing the container and forcing an update.
The example below allows you to select whether to preserve the colors during resizing.
The code uses Qt 5 and C++11. Note that the use of rand() % range is discouraged in modern code - it doesn't preserve the uniformity of the distribution.
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QCheckBox>
#include <QGridLayout>
#include <QPainter>
#include <random>
std::default_random_engine rng;
class Dialog : public QWidget {
Q_OBJECT
Q_PROPERTY(bool recolorOnResize READ recolorOnResize WRITE setRecolorOnResize)
QList<QColor> m_palette;
QList<QList<QColor>> m_chosenColors;
bool m_recolorOnResize;
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.fillRect(rect(), Qt::white);
p.setRenderHint(QPainter::Antialiasing);
int rec_size=64;
int rows=height()/rec_size;
int cols=width()/rec_size;
std::uniform_int_distribution<int> dist(0, m_palette.size()-1);
while (m_chosenColors.size() < rows) m_chosenColors << QList<QColor>();
for (QList<QColor> & colors : m_chosenColors)
while (colors.size() < cols)
colors << m_palette.at(dist(rng));
QPointF points[4];
for (int i=0; i<rows; i++) {
for (int j=0; j<cols; j++) {
points[0] = QPointF(rec_size*(j),rec_size*(i+0.5));
points[1] = QPointF(rec_size*(j+0.5),rec_size*(i));
points[2] = QPointF(rec_size*(j+1),rec_size*(i+0.5));
points[3] = QPointF(rec_size*(j+0.5),rec_size*(i+1));
p.setBrush(m_chosenColors[i][j]);
p.drawPolygon(points, 4);
}
}
}
void resizeEvent(QResizeEvent *) {
if (m_recolorOnResize) m_chosenColors.clear();
}
public:
Dialog(QWidget * parent = 0) : QWidget(parent), m_recolorOnResize(false) {
m_palette << "#E2C42D" << "#E5D796" << "#BEDA2C" << "#D1DD91" << "#E2992D" << "#E5C596";
setAttribute(Qt::WA_OpaquePaintEvent);
}
Q_SLOT void randomize() {
m_chosenColors.clear();
update();
}
bool recolorOnResize() const { return m_recolorOnResize; }
void setRecolorOnResize(bool recolor) {
m_recolorOnResize = recolor;
setAttribute(Qt::WA_StaticContents, !m_recolorOnResize);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QGridLayout l(&w);
Dialog d;
QCheckBox recolor("Recolor on Resize");
QPushButton update("Repaint"), randomize("Randomize");
d.setMinimumSize(256, 128);
l.addWidget(&d, 0, 0, 1, 2);
l.addWidget(&recolor, 1, 0, 1, 2);
l.addWidget(&update, 2, 0);
l.addWidget(&randomize, 2, 1);
recolor.setChecked(d.recolorOnResize());
QObject::connect(&recolor, &QAbstractButton::toggled, [&d](bool checked){
d.setRecolorOnResize(checked);}
);
QObject::connect(&update, &QAbstractButton::clicked, &d, static_cast<void(QWidget::*)()>(&QWidget::update));
QObject::connect(&randomize, &QAbstractButton::clicked, &d, &Dialog::randomize);
w.show();
return a.exec();
}
#include "main.moc"
I have created a QImage of format QImage::Format_Mono. When I try and display the image to a QGraphicsView through a QGraphicsScene the view is unchanged. The QImage is loaded to the scene using a QPixmap produced through the QPixmap::fromImage() function. I also tried saving the QPixmap as a PNG/JPG/BMP using the save function as well to no avail. The basic code structure is as follows:
QGraphicsView *view = new QGraphicsView();
QGraphicsScene *scene = new QGraphicsScene();
view.setScene(scene);
QImage img(size,QImage::Format_Mono);
QVector<QRgb> v;
v.append(Qt::color0); // I have tried using black and white
v.append(Qt::color1); // instead of color0 and 1 as well.
img.setColorTable(v);
// Do some stuff to populate the image using img.setPixel(c,r,bw)
// where bw is an index either 0 or 1 and c and r are within bounds
QPixmap p = QPixmap::fromImage(img);
p.save("mono.png");
scene->addPixmap(p);
// Code to display the view
If I instead make the image of QImage::Format_RGB888 and fill the pixels with either black or white the PNG/View displays appropriately.
How can I update my code to display the QImage in a QGraphicsView?
The error is that the Qt::GlobalColors (such as Qt::white or Qt::color0) are of type QColor, and not QRgb as expected. (QRgb is a typedef for unsigned int)
You can convert a QColor to a QRgb by using the method QColor::rgb(), or directly create a QRgb using the global method qRgb(r,g,b). Following is a complete working example to illustrate, that displays (and saves as PNG) the very exact image whether mono is true or false.
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QGraphicsView *view = new QGraphicsView();
QGraphicsScene *scene = new QGraphicsScene();
view->setScene(scene);
int W = 100;
int H = 100;
QImage img;
uint color0 = qRgb(255,0,0);
uint color1 = Qt::green.rgb();
bool mono = true;
if(mono)
{
img = QImage(QSize(W,H),QImage::Format_Mono);
QVector<QRgb> v; v << color0 << color1;
img.setColorTable(v);
for(int i=0; i<W; i++)
for(int j=0; j<H; j++)
{
uint index;
if(j-(j/10)*10 > 5)
index = 0;
else
index = 1;
img.setPixel(i,j,index);
}
}
else
{
img = QImage(QSize(W,H),QImage::Format_RGB888);
for(int i=0; i<W; i++)
for(int j=0; j<H; j++)
{
uint color;
if(j-(j/10)*10 > 5)
color = color0;
else
color = color1;
img.setPixel(i,j,color);
}
}
QPixmap p = QPixmap::fromImage(img);
p.save("mono.png");
scene->addPixmap(p);
view->show();
return app.exec();
}