QPushButton come out fended and I did not set that - c++

I'm working in ubuntu kde 20.04, qt5.15
I my purpose was to create a QPushButton that change his color when he got clicked, then I created a new derived class for doing this (Facelet), it works but the colors I got are not those I was assuming to get.
Facelet.h
#ifndef FACELET_H
#define FACELET_H
#include <QPushButton>
class Facelet : public QPushButton
{
Q_OBJECT
public:
explicit Facelet(QWidget * parent = nullptr);
explicit Facelet(int color, QWidget *pareny = nullptr);
void reset();
static QString colors[6];
private slots:
void __change_color();
private:
int _ColorIndex;
};
#endif // FACELET_H
Facelet.cpp
#include "Facelet.h"
QString Facelet::colors[6] = { "background-color: yellow;", "background-color: red;", "background-color: green;", "background-color: rgb(255, 130, 50);", "background-color: blue;", "background-color: white;" };
enum { rxyellow, rxred, rxgreen, rxorange, rxblue, rxwhite };
//public
Facelet::Facelet(QWidget *parent) :
QPushButton(parent),
_ColorIndex(-1)
{
this->resize(75, 75);
this->setStyleSheet("background-color: gray");
connect(this, SIGNAL(clicked()), SLOT(__change_color()));
}
Facelet::Facelet(int color, QWidget* parent) :
QPushButton(parent),
_ColorIndex(color)
{
this->resize(75, 75);
this->setStyleSheet(colors[_ColorIndex]);
connect(this, SIGNAL(clicked()), SLOT(__change_color()));
}
void Facelet::reset() {
this->setStyleSheet("background-color: gray");
}
//private
void Facelet::__change_color() {
_ColorIndex = (_ColorIndex >= 5 ) ? 0 : (_ColorIndex + 1);
this->setStyleSheet(colors[_ColorIndex]);
}
And here my main.cpp
#include <QApplication>
#include <QMainWindow>
#include "Facelet.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
Facelet f;
w.setCentralWidget(&f);
w.show();
return a.exec();
}
See? The screen is faded, I want a uniform strong color. What's wrong with my code?

To correct that I found two solution via reimplementation od paintEvent method.
first
void Facelet::paintEvent(QPaintEvent*) {
QPainter p(this);
p.setOpacity(1.0);
}
the first one is pretty easy, reading some topic I found out that a QWidget to have a setStyleSheet(QString) methods working properly need the following line in the paintEvent(QPaintEvent*) method as the official documentation says
void Facelet::paintEvent(QPaintEvent *) {
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

Related

Turn QPolygon into a QPushbutton

I would like to paint a QPolygon on my Window and be able to use it as a QPushbutton.
Is there any way to do this?
(most preferably without using QMousePressEvent to check the position of the mouse with the position of the polygon)
After the advice of Ton:
MainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
qv_point = {QPoint(10,20), QPoint(20,30), QPoint(50,30)};
ui->pushButton = new QPolygonPushButton(qv_point);
ui->setupUi(this);
ui->pushButton->update();
}
MainWindow::~MainWindow()
{
delete ui;
}
qpolygonpusbutton.cpp:
#include "qpolygonpushbutton.h"
QPolygonPushButton::QPolygonPushButton(QVector<QPoint> qv_points)
{
this->polygon << qv_points;
}
void QPolygonPushButton::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
painter.setViewport(e->rect());
painter.setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter.drawPolygon(this->polygon);
}
This can be done by declaring your own push button type, something like QPolygonPushButton that derives from QPushButton, for which you then reimplement its paintEvent member function.
Something along the following lines (not tested):
class QPolygonPushButton : public QPushButton
{
public:
using QPushButton::QPushButton;
private:
void paintEvent(QPaintEvent* e) override
{
QPainter painter(this);
painter.setViewport(e->rect());
painter.drawPolygon(...);
}
};
Update; fully working example. It uses a rectangle instead of a polygon but other than that you will get the idea. The button initially is a red rectangle, clicking it will change its color to blue.
#include <QtCore/QObject>
#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QPainter>
#include <QtGui/QPushButton>
namespace
{
QColor buttonColor{Qt::red};
}
class QRectButton : public QPushButton
{
public:
using QPushButton::QPushButton;
void paintEvent(QPaintEvent* e) override
{
QPainter painter(this);
painter.setPen(buttonColor);
painter.setBrush(buttonColor);
painter.drawRect(painter.window());
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow() : QMainWindow(nullptr)
{
QPushButton* button{new QRectButton(this)};
QObject::connect(button, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
private slots:
void onButtonClicked()
{
buttonColor = Qt::blue;
update();
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
#include "moc_polygon_button.cpp"

Layout mismanagement issue in Qt Application

I have an application which shows some images in a grid and rest of the widgets in their respective positions. Something like this
Drawing inspiration from this i managed to solve keeping the aspect ratio of an image while resizing.
What are the issues:
The QLabel widgets(image widgets) overlap if QMainWindow is shrinked. Check the image below
The whole application is apparently not suited for different screens. Running this application on a laptop, layout is completely messed up.
What have i done:
Here is the MVCE code i have created
//ImageWidget.h
#ifndef IMAGEWIDGET_H
#define IMAGEWIDGET_H
#include <QLabel>
#include <QResizeEvent>
#include <QWidget>
class ImageWidget : public QLabel
{
Q_OBJECT
public:
explicit ImageWidget(QWidget* parent = nullptr);
virtual QSize sizeHint() const;
QPixmap scaledPixmap() const;
virtual int widthForHeight(int height) const;
public slots:
void setPixmap ( const QPixmap& p);
void resizeEvent(QResizeEvent* ev);
private:
QPixmap pix;
};
#endif // IMAGEWIDGET_H
ImageWidget.cpp
#include "imagewidget.h"
ImageWidget::ImageWidget(QWidget* parent) :
QLabel(parent)
{
setStyleSheet("QLabel{margin-left: 10px; border-radius: 25px; background: white; color: #4A0C46;}");
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setWidthForHeight(true);
setSizePolicy(sizePolicy);
setMinimumSize(sizeHint());
}
void ImageWidget::setPixmap (const QPixmap& p)
{
pix = p;
QLabel::setPixmap(scaledPixmap());
}
/* virtual */ int ImageWidget::widthForHeight(int height) const
{
return pix.isNull() ? height * pix.height() / pix.width() : height;
}
QSize ImageWidget::sizeHint() const
{
if(pix.width() != 0)
{
int h = this->height();
return QSize(widthForHeight(h), h);
}
else
{
return QSize(300, 300);
}
}
QPixmap ImageWidget::scaledPixmap() const
{
auto scaled = pix.scaled(this->size() * devicePixelRatioF(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
scaled.setDevicePixelRatio(devicePixelRatioF());
return scaled;
}
void ImageWidget::resizeEvent(QResizeEvent* )
{
if (!pix.isNull())
{
QLabel::setPixmap(scaledPixmap());
}
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QTableWidget>
#include "imagewidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
virtual void resizeEvent(QResizeEvent* event) override;
private:
Ui::MainWindow *ui;
ImageWidget* lbl1;
ImageWidget* lbl2;
ImageWidget* lbl3;
ImageWidget* lbl4;
QPushButton* btn1;
QPushButton* btn2;
QPushButton* btn3;
QPushButton* btn4;
QTableWidget* tableWidget;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QTabWidget>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QTabBar>
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVBoxLayout* mainLayout = new QVBoxLayout;
QVBoxLayout* tabLay = new QVBoxLayout;
QHBoxLayout* buttonLay = new QHBoxLayout;
QGridLayout* gridLay = new QGridLayout;
QHBoxLayout* dockLay = new QHBoxLayout;
btn1 = new QPushButton(this);
btn1->setText("Button1");
btn2 = new QPushButton(this);
btn2->setText("Button2");
btn3 = new QPushButton(this);
btn3->setText("Button3");
btn4 = new QPushButton(this);
btn4->setText("Button4");
QTabWidget* tabView = new QTabWidget(this);
tabView->addTab(new QWidget(), "Table");
tabView->setMinimumSize(500, 300);
tabView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
tableWidget = new QTableWidget(this);
tableWidget->setFixedHeight(200);
lbl1 = new ImageWidget(this);
lbl2 = new ImageWidget(this);
lbl3 = new ImageWidget(this);
lbl4 = new ImageWidget(this);
QPixmap lbl1Pix("1.png");
QPixmap lbl2Pix("2.png");
QPixmap lbl3Pix("3.png");
QPixmap lbl4Pix("4.png");
lbl1->setPixmap(lbl1Pix);
lbl1->show();
lbl2->setPixmap(lbl2Pix);
lbl2->show();
lbl3->setPixmap(lbl3Pix);
lbl3->show();
lbl4->setPixmap(lbl4Pix);
lbl4->show();
buttonLay->addWidget(btn1);
buttonLay->addWidget(btn2);
buttonLay->addWidget(btn3);
buttonLay->addWidget(btn4);
tabLay->addWidget(tabView);
gridLay->addWidget(lbl1, 0, 0);
gridLay->addWidget(lbl2, 0, 1);
gridLay->addWidget(lbl3, 1, 0);
gridLay->addWidget(lbl4, 1, 1);
dockLay->addLayout(gridLay);
dockLay->addLayout(tabLay);
mainLayout->addLayout(dockLay);
mainLayout->addLayout(buttonLay);
mainLayout->addWidget(tableWidget);
centralWidget()->setLayout(mainLayout);
setMinimumSize(200,200);
show();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::resizeEvent(QResizeEvent * /*event*/)
{
// get label dimensions
int h = lbl1->height();
int w = lbl1->widthForHeight(h);
lbl1->setFixedWidth(w);
lbl2->setFixedWidth(w);
lbl3->setFixedWidth(w);
lbl4->setFixedWidth(w);
}
Main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
What do i want:
Set a minimum size to the whole application so that the imagewidgets do not overlap.
Make the whole app work on any PC or laptop screen with different resolutions.
The imagewidgets should take up available space while keeping the aspect ratio of the images and the QTabWidget on the right should have a fixed size.
Maybe there is an easy solution but i am bit confused with Qt Layout management system.
EDIT1: Added the image with overlapping widgets
Here is what I think is happening. When you set the pixmap like here:
QLabel::setPixmap(scaledPixmap());
The label will set the size of the images as the minimum size. And from that point on the label can not be resized any smaller.
The solution I have found around this is to set the following resize flags for the QLabel in the constructor:
QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
setSizePolicy(sizePolicy);
This way the QLabel will be resizable at all times. Or you might have to adapt the minimum width.
I am not sure if that is even it, but maybe it is a good start. Let me know what you make of this.

Qt Drawing Without Erasing Components

I am using Qt 5.11.1 and Qt Creator to create a project. My code draws several ellipses in the paintEvent function that I have overriden. But because of the paintEvent function's working style the buttons that I have under the ellipses are being erased. I want to have a window that has ellipses at the top and buttons at the bottom of the window. It will roughly seem like this:
Is there any way to do this. Right now, the buttons are being erased and I only have the ellipses. I would be really glad if someone could guide me.
Thanks in advance.
Note: My ellipses are green and my background is black but I have tried by changing the background to white or changing the stylesheet of the buttons, it didn't work.
This is my .h file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
protected:
void paintEvent(QPaintEvent *e);
void setBackGroundColorToBlack();
};
#endif // MAINWINDOW_H
This is my .cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtGui>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setBackGroundColorToBlack();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *event) {
setUpdatesEnabled(false);
QPainter painterObj;
painterObj.begin(this);
painterObj.setPen(QPen(Qt::green, 2, Qt::SolidLine, Qt::RoundCap));
painterObj.drawEllipse(0, 0, 318, 390);//456
painterObj.drawEllipse(53, 65, 212, 260);//304
painterObj.drawEllipse(106, 130, 106, 130);//152
painterObj.end();
}
void MainWindow::setBackGroundColorToBlack() {
QPalette pal = palette();
// set black background
pal.setColor(QPalette::Background, Qt::black);
this->setAutoFillBackground(true); // This enables the qt to fill the background before the paint event.
this->setPalette(pal);
//update();
}
This is what I get:
My ui file is like this:
In general - don't use a QMainWindows class. It's for "big" desktop applications that have a menu, a status bar, etc. All you need is to derive from QDialog or even just QWidget. And you can paint the background yourself, so no need to mess with the pallete. The minimal example that should work is below. If the Bar button doesn't show up, then your Qt for the target is broken: the default platform style doesn't work.
// https://github.com/KubaO/stackoverflown/tree/master/questions/painted-with-children-51498155
// This project is compatible with Qt 4 and Qt 5
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
QRectF scaled(const QRectF &rect, qreal scale) {
auto const center = rect.center();
auto const w = rect.width()*scale;
auto const h = rect.height()*scale;
return {center.x() - w/2.0, center.y() - h/2.0, w, h};
}
QRectF marginAdded(const QRectF &rect, qreal margin) {
return rect.adjusted(margin, margin, -margin, -margin);
}
const char buttonQSS[] =
"* { background-color: white; border-width: 2px; border-style:solid; border-color: red;"
" border-radius: 3px; padding: 3px; }"
"*:pressed { padding-left: 5px; padding-top: 5px; background-color: lightGray; }";
class MyWindow : public QWidget {
Q_OBJECT
QPushButton m_restoreButton{"Restore Size"};
QPushButton m_otherButton{"Bar"};
QGridLayout m_layout{this};
Q_SLOT void onRestore() { resize(sizeHint()); }
public:
explicit MyWindow(QWidget *parent = {}) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
m_layout.addItem(new QSpacerItem(0, 100, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), 0, 0);
m_layout.addWidget(&m_restoreButton, 1, 0);
m_layout.addWidget(&m_otherButton, 1, 1);
m_restoreButton.setStyleSheet(buttonQSS);
connect(&m_restoreButton, SIGNAL(clicked(bool)), SLOT(onRestore()));
}
protected:
void paintEvent(QPaintEvent *) override {
qreal const penWidth = 2.0;
// Cover the area above all the children
QRectF const area = marginAdded(QRect(0, 0, width(), childrenRect().top() - 10), penWidth);
QPainter p(this);
p.fillRect(rect(), Qt::black);
p.setPen({Qt::green, penWidth});
p.drawEllipse(scaled(area, 3./3.));
p.drawEllipse(scaled(area, 2./3.));
p.drawEllipse(scaled(area, 1./3.));
}
QSize sizeHint() const override { return {320, 568}; /* iPhone 5 */ }
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWindow w;
w.show();
return a.exec();
}
#include "main.moc"

Qt QFrame StyleSheet not working in MainWindow

I am struggling with a basic StyleSheet question. I am trying to apply a stylesheet to a QFrame within a MainWindow, and for some reason, the stylesheet is being ignored. This seems related to Qt Stylesheet for custom widget and Enable own widget for stylesheet, but I've tried adding a paintEvent and that did not help. Its gotta be something simple, but I can't see it. Can anyone help?
main.cpp:
#include <QApplication>
#include <QDebug>
#include "mainwindow.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MainWindowDef mainWindow;
mainWindow.show();
return app.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindowDef : public QMainWindow {
Q_OBJECT
public:
MainWindowDef(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);
};
#endif
mainwindow.cpp:
#include <QDebug>
#include "mainwindow.h"
MainWindowDef::MainWindowDef(QWidget *parent) : QMainWindow(parent) {
QFrame *frame = new QFrame;
QVBoxLayout *layout = new QVBoxLayout;
QPushButton *button = new QPushButton("Press");
layout->addWidget(button);
QString myStyle = QString( "QFrame {"
"border: 2px solid green;"
"border-radius: 4px;"
"padding: 2px"
"background-color: red;"
"color: blue;"
"}");
frame->setStyleSheet(myStyle);
frame->setLayout(layout);
frame->setAutoFillBackground(true);
setCentralWidget(frame);
}
void MainWindowDef::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
I finally figured this out. It was missing a semicolon.
"padding: 2px"
should say:
"padding: 2px;"
Sigh.

Qt C++ - Aligning QProgressBar inside a QSplashScreen

I am putting a QProgressBar inside a QSplashScreen by subclassing QSplashScreen. It overrides the drawContents() method.
I thought I had set the geometry correctly, but it renders at both the top and bottom of the screen. I don't know why. Perhaps there's another way to align it. The numbers are correct, as the image is 380x284, so a 19 height progress bar should be 265 pixels down.
Sorry for crappy picture, splash screen wasn't showing up with print screen button. It's just a 1 color white splash screen at the moment, but as you can see, progress bar at top and bottom (they're both the same colors, its the lighting from the camera).
http://i.imgur.com/p1LoJ.jpg
Another issue will be the showMessage() method of QSplashScreen. I want the message to appear above the progress bar, right-aligned... if anyone has any ideas how to do that.
splashscreen.cpp
#include "splashscreen.h"
SplashScreen::SplashScreen(QApplication *app, QWidget *parent) :
QSplashScreen(parent)
{
this->app = app;
this->setPixmap(QPixmap(":/images/splashscreen.png"));
this->setCursor(Qt::BusyCursor);
// if I dont make it a child, it *only* renders at the top
progress = new QProgressBar(this);
progress->setGeometry(0, 265, 380, 19); // puts it at bottom
progress->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
progress->setValue(0);
progress->setMaximum(100);
progress->setEnabled(true);
this->showMessage("Hello", Qt::AlignBottom);
connect(progress, SIGNAL(valueChanged(int)), this, SLOT(progressBarUpdated(int)));
}
void SplashScreen::drawContents(QPainter *painter)
{
QSplashScreen::drawContents(painter);
this->progress->render(painter);
}
void SplashScreen::progressBarUpdated(int value)
{
this->repaint();
this->app->processEvents();
}
splashscreen.h
#ifndef SPLASHSCREEN_H
#define SPLASHSCREEN_H
#include <QSplashScreen>
#include <QProgressBar>
#include <QApplication>
class SplashScreen : public QSplashScreen
{
Q_OBJECT
public:
explicit SplashScreen(QApplication *app, QWidget *parent = 0);
QProgressBar *progress;
QWidget *spacer;
QApplication *app;
public slots:
void progressBarUpdated(int value);
protected:
void drawContents(QPainter *painter);
};
#endif // SPLASHSCREEN_H
main.cpp
#include <QtGui/QApplication>
#include <time.h>
#include "splashscreen.h"
#include "mainwindow.h"
int main(int argc, char *argv[])
{
srand(time(0));
QApplication a(argc, argv);
SplashScreen *splash = new SplashScreen(&a);
splash->show();
// snip.. loading a ton of stuff into memory at startup
// if you're testing this you might have to sleep/timer here iono
MainWindow w;
splash->finish(&w);
w.show();
return app.exec();
}
You can paint progress directly, without creating QProgressBar. For example:
sp.h:
#ifndef SPLASHSCREEN_H
#define SPLASHSCREEN_H
#include <QSplashScreen>
#include <QApplication>
class SplashScreen : public QSplashScreen
{
Q_OBJECT
public:
explicit SplashScreen(QApplication *app, QWidget *parent = 0);
int m_progress;
QApplication *app;
public slots:
void setProgress(int value)
{
m_progress = value;
if (m_progress > 100)
m_progress = 100;
if (m_progress < 0)
m_progress = 0;
update();
}
protected:
void drawContents(QPainter *painter);
};
#endif // SPLASHSCREEN_H
sp.cpp
#include "sp.h"
SplashScreen::SplashScreen(QApplication *aApp, QWidget *parent) :
QSplashScreen(parent), app(aApp), m_progress(0)
{
this->setPixmap(QPixmap(":/images/splashscreen.png"));
this->setCursor(Qt::BusyCursor);
this->showMessage("Hello", Qt::AlignBottom);
}
void SplashScreen::drawContents(QPainter *painter)
{
QSplashScreen::drawContents(painter);
// Set style for progressbar...
QStyleOptionProgressBarV2 pbstyle;
pbstyle.initFrom(this);
pbstyle.state = QStyle::State_Enabled;
pbstyle.textVisible = false;
pbstyle.minimum = 0;
pbstyle.maximum = 100;
pbstyle.progress = m_progress;
pbstyle.invertedAppearance = false;
pbstyle.rect = QRect(0, 265, 380, 19); // Where is it.
// Draw it...
style()->drawControl(QStyle::CE_ProgressBar, &pbstyle, painter, this);
}
May be this helps you.