I am new to Qt and almost every tutorial I find says to add the image to a QLabel using setPixmap(). Maybe this is the right way, but it doesn't feels to be, since using a label for a image seems to go beyond the purpose of a label.
Could anyone tell me if there is a "right way" or a special Class for this, or if the "Label way" is the right one, not just the simple one.
Using QLabel is the usual way to display an image in a QtWidgets-based UI. That might indeed feel a bit awkward, as QLabel's API is mostly concerned with text rendering. However, it does the job and there's no other class dedicated to only painting images. One could think of writing his own class (taking a QPixmap, reimplementing paintEvent(), sizeHint()), but that would only make sense to me if functionality is needed that QLabel lacks.
There are of course many other ways to paint an image, depending on the context, like images on buttons (QToolButton, QPushButton, ...), images in graphics scenes (QGraphicsScene/View) etc., but they all serve either more specialized or more complex use cases.
easiest way is to use a QLabel.in ImageViewer example
http://qt-project.org/doc/qt-4.8/widgets-imageviewer.html they use QLabel ..
another way
QGraphicsView view(&scene);
QGraphicsPixmapItem item(QPixmap("c:\\test.png"));
scene.addItem(&item);
Here is a simple class that isn't Label based. I suppose it depends on what you personally feel is the proper way to do it and what you need to do with it. I prefer implementing my own class so you can add on to it later(maybe you want to manipulate the image).
imagewidget.h
#ifndef IMAGEWIDGET_H
#define IMAGEWIDGET_H
#include <QPainter>
#include <QImage>
#include <QWidget>
QT_BEGIN_NAMESPACE
class QPainter;
class QImage;
QT_END_NAMESPACE
class ImageWidget : public QWidget
{
Q_OBJECT
public:
ImageWidget(const QString &filename, QWidget* parent = 0);
~ImageWidget();
bool load(const QString &fileName);
bool save(const QString &fileName);
protected:
void paintEvent(QPaintEvent* event);
private:
QImage img;
};
#endif
imagewidget.cpp
#include <QDebug>
#include "imagewidget.h"
ImageWidget::ImageWidget(const QString &filename, QWidget* parent) : QWidget(parent)
{
img.load(filename);
setMinimumWidth(img.width());
setMinimumHeight(img.height());
setMaximumWidth(img.width());
setMaximumHeight(img.height());
this->show();
}
bool ImageWidget::load(const QString &fileName)
{
img.load(fileName);
return true;
}
bool ImageWidget::save(const QString &fileName)
{
img.save(fileName, "PNG");
return true;
}
ImageWidget::~ImageWidget()
{
}
void ImageWidget::paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setViewport(0, 0, width(), height());
painter.setWindow(0, 0, width(), height());
painter.drawImage(0, 0, img);
}
Related
I want to use ResizeEvent to receive the size of the window and set it to the size of a QLabel, in order to make the image stretched and adapted to the window's dimensions.by a left click of the mouse I can resize my window and the image takes a new size.
You must have the following considerations:
It is not necessary to store QPixmap by means of a pointer since when passing it to the QLabel it is copied by value.
Therefore, if you change the size of the QPixmap p will not be reflected in the QLabel since the QPixmap that has the QLabel is a copy of the one you established at the beginning.
It is not necessary to use a layout for this task since it will create an infinite loop since this also intervenes in the resizeEvent of the widget where it has been established so if you change the size of the QLabel, it will change the size of the QWidget, and this again I will try to change the QLabel, and so on.
It is not advisable to modify the original QPixmap since changing its size modifies the pixels and you will get an unexpected effect.
Using the above we obtain the following code:
*.h
#ifndef TESTSIZE_H
#define TESTSIZE_H
#include <QWidget>
class QLabel;
class testsize : public QWidget
{
Q_OBJECT
public:
explicit testsize(QWidget *parent = 0);
~testsize();
private:
QLabel *image;
QPixmap original_px;
protected:
void resizeEvent(QResizeEvent *event);
};
#endif // TESTSIZE_H
*.cpp
#include "testsize.h"
#include <QLabel>
#include <QResizeEvent>
testsize::testsize(QWidget *parent) :
QWidget(parent)
{
image = new QLabel(this);
original_px = QPixmap(":/wallpaper.jpg");
image->setPixmap(original_px);
resize(640, 480);
}
testsize::~testsize()
{
}
void testsize::resizeEvent(QResizeEvent *event)
{
QPixmap px = original_px.scaled(event->size());
image->setPixmap(px);
image->resize(event->size());
QWidget::resizeEvent(event);
}
You can find the complete example in the following link.
The error in question is as follows:
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setPen: Painter not active
QPainter::setFont: Painter not active
After looking at a ton of online forum posts that all came down to people making the same mistake of trying to paint on their widgets outside of paintEvent(), I've been unable to have any luck. I tried drawing directly on this custom QWidget subclass, I've tried making a child QWidget and drawing on that. Can someone please show me what thing I'm (probably obviously to someone else) doing wrong?
Thanks in advance.
Header:
#ifndef TEXTDISPLAY_H
#define TEXTDISPLAY_H
#include <QWidget>
class TextDisplay : public QWidget
{
Q_OBJECT
public:
TextDisplay(QString text, QString fontFamily = "Helvetica", int fontSize = 20,
int fontColor = Qt::black, QWidget* parent = 0);
protected:
void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE;
void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE;
private:
QString text;
QString fontFamily;
int fontSize;
int fontColor;
};
#endif // TEXTDISPLAY_H
Cpp:
#include "textdisplay.h"
#include <QPainter>
TextDisplay::TextDisplay(QString text, QString fontFamily, int fontSize,
int fontColor, QWidget* parent)
: QWidget(parent), text(text), fontFamily(fontFamily),
fontSize(fontSize), fontColor(fontColor)
{
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
}
void TextDisplay::resizeEvent(QResizeEvent*) {
paintEvent(NULL);
}
void TextDisplay::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.setPen(fontColor);
painter.setFont(QFont(fontFamily, fontSize));
QRect rect(QPoint(0, 0), this->size());
QRect bound;
QTextOption options;
options.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.drawText(rect, Qt::TextWordWrap | Qt::TextWrapAnywhere, text, &bound);
this->setMinimumHeight(bound.size().height());
}
Edit with solution:
Thanks to both responders - I needed to do two things to get it working:
a) Get rid of paintEvent(NULL). I got rid of the resizeEvent override as well, it was unnecessary as suggested.
b) Set a minimum size for the widget. Without this, the paintEvent was never called by Qt.
If you want to schedule a redraw for your widget, just call update().
If you need an immediate repaint (which you almost never need) you can call repaint() instead.
Calling paintEvent() directly will not work -- Qt needs to prepare the backing store to handle the painting, so you can't bypass the update mechanism. Instead, call the methods above, which will result in a call to paintEvent() (if the widget is visibile, not occluded, etc.etc.etc.).
Note also that you should not need to reimplement resizeEvent() just to update your widget. That should already be done for you by Qt...
I am streaming from a webcam using OpenCV. Each captured frame (type is cv::Mat) is transported via custom signal to slots in a node-based QGraphicsScene. Since I have a single type of node that actually requires to display the cv::Mat while the rest is just for processing the data I have created a viewer widget that converts the cv::Mat data to QImage data and then using paintEvent() draws it on its surface:
Here is the code for the widget itself:
qcvn_node_viewer.hpp
#ifndef QCVN_NODE_VIEWER_HPP
#define QCVN_NODE_VIEWER_HPP
#include <QWidget>
#include <QImage>
#include <QSize>
#include <QPaintEvent>
#include <opencv2/core.hpp>
class QCVN_Node_Viewer : public QWidget
{
Q_OBJECT
QImage output;
public:
explicit QCVN_Node_Viewer(QWidget *parent = 0);
~QCVN_Node_Viewer();
QSize sizeHint() const;
QSize minimumSizeHint() const;
// Source: http://stackoverflow.com/a/17137998/1559401
static QImage Mat2QImage(cv::Mat const& frame);
static cv::Mat QImage2Mat(QImage const& src);
protected:
void paintEvent(QPaintEvent *event);
signals:
// Nodes connect to sendFrame(cv::Mat) signal
void sendFrame(cv::Mat);
public slots:
void receiveFrame(cv::Mat frame);
};
#endif // QCVN_NODE_VIEWER_HPP
qcvn_node_viewer.cpp
#include "qcvn_node_viewer.hpp"
#include <opencv2/imgproc.hpp>
#include <QPainter>
QCVN_Node_Viewer::QCVN_Node_Viewer(QWidget *parent) :
QWidget(parent)
{
qRegisterMetaType<cv::Mat>("cv::Mat");
}
QCVN_Node_Viewer::~QCVN_Node_Viewer()
{
}
QSize QCVN_Node_Viewer::sizeHint() const
{
return output.size();
}
QSize QCVN_Node_Viewer::minimumSizeHint() const
{
return output.size();
}
QImage QCVN_Node_Viewer::Mat2QImage(cv::Mat const& frame)
{
cv::Mat temp;
cvtColor(frame, temp,CV_BGR2RGB);
QImage dest((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
dest.bits(); // enforce deep copy
return dest;
}
cv::Mat QCVN_Node_Viewer::QImage2Mat(QImage const& frame)
{
cv::Mat tmp(frame.height(),frame.width(),CV_8UC3,(uchar*)frame.bits(),frame.bytesPerLine());
cv::Mat result; // deep copy just in case (my lack of knowledge with open cv)
cvtColor(tmp, result,CV_BGR2RGB);
return result;
}
void QCVN_Node_Viewer::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawImage(QPoint(0,0), output);
painter.end();
}
void QCVN_Node_Viewer::receiveFrame(cv::Mat frame)
{
if(frame.empty()) return;
// Pass along the received frame
emit sendFrame(frame);
// Generate
// http://stackoverflow.com/questions/17127762/cvmat-to-qimage-and-back
// Applications crashes sometimes! Conversion seems to be incorrect
output = Mat2QImage(frame);
output.bits();
setFixedSize(output.width(), output.height());
repaint();
}
As you can see from the screenshot above the QWidget is embedded in a QGraphicsScene using QGraphicsProxyWidget along with a QGraphicsRectItem that I use for selecting/moving the node around in the scene.
Using QPainter is considered to be more efficient than using QLabel and settings its QPixmap to the captured and converted frame. Using an OpenGL viewport inside a QGraphicsScene as far as I know is not an option. It seems that processing the paint events in the scene also handles the local ones inside the QGraphicsItems and QGraphicsProxyWidgets. The frame that the viewer paints on its surface changes only when the scene's paint event handler is called (hovering some of my nodes, moving them around etc.).
Is there a way to fix this? I'm about to go for the QLabel+QPixmap option (even though I'm not sure if it won't suffer from the same or different problem) but first I'd like to make sure there is nothing I can do to keep my current way of doing things.
EDIT: Okay, so I have changed my viewer to use QLabel instead of the widget's paint event and it works like a charm. The question is still opened though since I'd like to know how to use local paint events.
I have a QLabel, and I want the text inside of it to appear engraved, similar to the text-shadow approach in CSS. Is there a way to do this in Qt?
Much more easier than overriding the paintEvent is using a QGraphicsEffect, precisely QGraphicsDropShadowEffect.
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(5);
effect->setXOffset(5);
effect->setYOffset(5);
label->setGraphicsEffect(effect);
and the result is something like this:
If you want to color the shadow you can easily achieve this through QGraphicsDropShadowEffect::setColor member function.
Hope this helps.
This can be accomplished by overriding the label's paint event in a subclass. Example:
#include <QRect>
#include <QLabel>
#include <QPainter>
class QEngravedLabel : public QLabel
{
public:
explicit QEngravedLabel(QWidget *parent=0, Qt::WindowFlags f=0)
: QLabel(parent, f){};
explicit QEngravedLabel(const QString &text, QWidget *parent=0, Qt::WindowFlags f=0)
: QLabel(text,parent,f){};
protected:
virtual void paintEvent(QPaintEvent *pe) override
{
QRect toPaint(pe->rect());
QPainter painter(this);
toPaint.translate(0,1);
painter.setPen(QColor("#CCC")); // light shadow on bottom
painter.drawText(toPaint, this->alignment() ,this->text());
toPaint.translate(0,-2);
painter.setPen(QColor("#333")); // dark shadow on top
painter.drawText(toPaint, this->alignment() ,this->text());
toPaint.translate(0,1);
painter.setPen(QColor("#000000")); // text
painter.drawText(toPaint, this->alignment() ,this->text());
}
};
These shadow colors are tailored for a light gray background.
I've been working on my first gui application. I need to store some values for everything to function so I decided to use QSettings and learn how to use windows registry. I have found an example for the size and position so I have do have a slight grasp on what is going on here, but for some reason I cannot get it to work when I try on my own. I have been struggling with this for a few weeks now and just cant find any good references on how to add my own strings to the registry. Below is all the pertinent code needed for this to happen. Any help or point in the right direction will be greatly appreciated. Also I have the fundamentals of Qt 4 or something like that but it was not that much help to me in this instance, so if anyone has any quick references to some quality related articles or more comprehensive works on the more advanced QT topics i would be interested to hear them, not to get to far off the topic at hand though, I'm sure there's tons of good books out there.
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
createActions();
createMenus();
createMainWidget();
createIcons();
createDocks();
createStatusBar();
setMinimumSize(950,600);
setWindowTitle(tr("Black Ops Toolbox: Vrs. 0.0.0.12.01"));
QString defaultDirPath = "";
readSettings();
}
void MainWindow::readSettings()
{
QSettings settings;
QPoint pos = settings.value("pos", QPoint(25,25)).toPoint();
move(pos);
QSize size = settings.value("size",QSize(1200,900)).toSize();
resize(size);
QString defaultDirPath = settings.value("defaultDirPath", QString("c:/programfiles/")).toString();
}
void MainWindow::writeSettings()
{
QSettings settings;
settings.setValue("pos",pos());
settings.setValue("size",size());
settings.setValue("defaultDirPath", QVariant(QString *defaultDirPath).toString());
}
void MainWindow::closeEvent(QCloseEvent *event)
{
writeSettings();
}
MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
protected:
void closeEvent(QCloseEvent *event);
private:
void createActions();
void createActionEvents();
void createMenus();
void createMainWidget();
void createIcons();
void createDocks();
void createStatusBar();
void readSettings();
void writeSettings();
QString *defaultDirPath;
};
I really did not use QSettings yet, but from documentation it seems that in your code some information misses.
In particular, you must give (from Basic usage section), organization name and application name, using the QSettings constructor, like
QSettings settings("MySoft", "Star Runner");
or using QCoreApplication settings and then the default QSettings constructor
QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");
...
QSettings settings;
Have you tried one of these methods to create your QSettings?