How can I create text with an engraved effect? - c++

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.

Related

Resizing Qlabel image upon resizing window using resizeEvent

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.

Can't use QPainter in paintEvent of custom QWidget (Qt5)

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...

The right way to show a image in Qt

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);
}

Draw on custom widget after it's drawing

I have a custom widget, which inherits QWidget. It has own paintEvent and I cannot change it. So I want to use such a widget in my dialog object, but I need to draw some graphics on it after it draws its own graphics (that widget draws video frames on it, an I need to draw some lines over it). Can I draw every time after the paintEvent of that widget? I used installEventFilter and caught the event wuth type Qt::Paint, but I canoont see anything I've drown. Is there any other way?
You can derive from the custom widget class, reimplement paintEvent, and call the inherited paintEvent first, then do your drawing.
You can install an event filter on the widget and do the same: call the widget's paintEvent first, then do your drawing.
Hide the other widget. Create your own widget, and call the other widget's render method in your widget's paintEvent, then do your drawing. Since the other widget is presumably rendering video frames that change periodically over time, you might need to use a timer to update() your widget.
In neither case are you modifying the 3rd party custom widget.
In order to call other widget's protected paintEvent you need to be using a QWidget, even if just a dummy, invisible one.
This is a very simple code sample that draw inside a custom widget. It draws a blue rectangle inside of a QPushButton.
The method used is exactly what has been described in option 1 by #Kuba
So, you inherit from the custom widget class where you want to draw in, reimplement paintEvent, and call the inherited paintEvent first and the do your drawing.
Hope this helps
#include <QApplication>
#include <QPushButton>
#include <QPainter>
#include <QPaintEvent>
// inherit from the class over which you want to draw
class DrawOverButton : public QPushButton
{
Q_OBJECT
public:
DrawOverButton(const QString &text, QWidget *parent = 0) :
QPushButton(text, parent)
{
// enlarge the button so there is some space to draw in
setStyleSheet("QPushButton {min-height: 60px; "
"min-width: 120px; margin: 5px;}");
}
protected:
virtual void paintEvent(QPaintEvent *event) {
// call the base class paint event method
// this will draw the base class content
QPushButton::paintEvent(event);
// draw a blue border inside the button
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor("#3cf"), 4));
const int distance = 20;
painter.drawRoundedRect(QRect(distance, distance,
width() - 2 * distance, height() - 2 * distance),
10, 10);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DrawOverButton *button = new DrawOverButton("Button");
button->show();
return a.exec();
}
#include "main.moc"

QRubberBand like feature - Static selection area

I'm trying to draw a selection area on top of the desktop/opened windows, which works well by using QRubberBand, but seeing as it does not have a stylesheet command, how would I be able to change the color and width of the border and making the inside of it completely transparent?
Edit: Is there a similar method to use than QRubberBand in Qt? Changing the painter methods gives a lot of problems (border is one pixel larger on the left and top than right and bottom, the marked area seems not to be able to be completely transparent).
Edit2: The area it will cover is static, not something that is dragged by the user.
Edit3:
class CustomRubberBand : public QRubberBand
{
public:
CustomRubberBand(Shape s, QWidget * p = 0) : QRubberBand(s, p)
{
}
protected:
void paintEvent(QPaintEvent *pe)
{
Q_UNUSED(pe);
QPainter painter;
QPen pen(Qt::red, 6);
pen.setStyle(Qt::SolidLine);
painter.begin(this);
painter.setPen(pen);
painter.drawRect(pe->rect());
painter.end();
}
};
This gives me the border around it that I want, but I haven't found anything about removing the background (completely transparent) that works... Seems like there is a problem with Vista and Qt with this.
Any tips on how to remove the background? Right now with no painting method for it it is a semi-transparent white background instead of the default blue one.
Edit4:
This shows the problem: Visible background error notice how the background, with the border, is a semi transparent white. The paint method I'm using does not draw this but only the border. I want it to be completely invisible, and setting the opacity for the object will also make the border transparent, which it should not be.
You can use a transparent QPalette in the paintEvent function to achieve what you are trying to do.
class ScreenViewport : public QRubberBand
{
Q_OBJECT
public:
ScreenViewport(Shape shape, QWidget *parent = 0) : QRubberBand(shape,parent){
}
protected:
void paintEvent(QPaintEvent *pe){
pal = new QPalette(Qt::transparent);
setPalette(*pal);
Q_UNUSED(pe);
QPainter painter;
QPen pen(Qt::red, 6);
pen.setStyle(Qt::DashLine);
painter.begin(this);
painter.setPen(pen);
painter.drawRect(pe->rect());
painter.end();
}
private:
QPalette *pal;
};
QRubberBand inherits from QWidget which in turn supports setStyleSheet function, see the QRubberBand member functions listing.
If this is not working right for you just override ::paintEvent , see this example.