Qt - QPushButton text formatting - c++

I have a QPushButton and on that I have a text and and icon. I want to make the text on the button to be bold and red. Looked at other forums, googled and lost my hope. Seems there is no way to do that if the button has an icon (of course if you don't create a new icon which is text+former icon). Is that the only way? Anyone has a better idea?

You really don't need to subclass to change the formatting of your button, rather use stylesheets e.g.
QPushButton {
font-size: 18pt;
font-weight: bold;
color: #ff0000;
}
Applying this to the button that you want to change will make the buttons text 18pt, bold and red. You can apply via widget->setStyleSheet()
Applying this to a widget in the hierarchy above will style all the buttons underneath, the QT stylesheet mechanism is very flexible and fairly well documented.
You can set stylesheets in the designer too, this will style the widget that you are editing immediately

you make the subclass of "QPushbutton", then override the paint event,
there you modify the text to your wish.
here it is,
class button : public QPushButton
{
Q_OBJECT
public:
button(QWidget *parent = 0)
{
}
~button()
{
}
void paintEvent(QPaintEvent *p2)
{
QPushButton::paintEvent(p2);
QPainter paint(this);
paint.save();
QFont sub(QApplication::font());
sub.setPointSize(sub.pointSize() + 7);
paint.setFont(sub);
paint.drawText(QPoint(300,300),"Hi");
paint.restore();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
button b1;
b1.showMaximized();
return a.exec();
}

Note:My answer is inspired by the idea from #Петър Петров and the comment from #mjwach.
You can subclass QPushButton and give it two private fields:
self.__lbl: A QLabel() instance to hold the rich text. Its background is made transparent and it doesn't catch mouse events.
self.__lyt: A QHBoxLayout() to hold the label. The layout margins are set to zero, such that the label's borders touch the button's borders. In other words: we ensure that the label has the exact same size as the button, and is positioned right on top of it.
Finally you have to override the setText() method, such that the text ends up in the label instead of the button.
class RichTextPushButton(QPushButton):
def __init__(self, parent=None, text=None):
if parent is not None:
super().__init__(parent)
else:
super().__init__()
self.__lbl = QLabel(self)
if text is not None:
self.__lbl.setText(text)
self.__lyt = QHBoxLayout()
self.__lyt.setContentsMargins(0, 0, 0, 0)
self.__lyt.setSpacing(0)
self.setLayout(self.__lyt)
self.__lbl.setAttribute(Qt.WA_TranslucentBackground)
self.__lbl.setAttribute(Qt.WA_TransparentForMouseEvents)
self.__lbl.setSizePolicy(
QSizePolicy.Expanding,
QSizePolicy.Expanding,
)
self.__lbl.setTextFormat(Qt.RichText)
self.__lyt.addWidget(self.__lbl)
return
def setText(self, text):
self.__lbl.setText(text)
self.updateGeometry()
return
def sizeHint(self):
s = QPushButton.sizeHint(self)
w = self.__lbl.sizeHint()
s.setWidth(w.width())
s.setHeight(w.height())
return s

You can subclass a QLabel and completely throw away all of its mouse events (so they reach the parent).
Then crate a QPushButton, set a layout on it and in this layout insert a QLabel with formatted text. You get a button that contains QLabel and is clickable. (Any Qt Widget can have layout set, including ones that you never ever expected they can.)

Related

Implementing a cell-based widget in Qt5

I'm trying to implement a cell-based widget similar to the one in Jupyter Notebook in Qt where multiple cells are stacked vertically. Each cell is made of a QTextEdit and multiple buttons and the cell's vertical size would grow by entering more text into the text edit (i.e. no vertical scrollbars in the text edit).
However I'm not sure what the best way would be to implement such a widget in Qt. I've tried a QListWidget with a custom widget (made of a QTextEdit and a couple of buttons) for each item, however the custom widget wouldn't grow vertically by entering more text and instead the QTextEdit scrollbars would show up. I also implemented a second version using QListView and QStyledItemDelegate however I encountered multiple issues including not being able to interact with the painted buttons (through the delegate).
Any suggestions/examples about how to implement such a cell-based widget?
You can subclass QTextEdit and listen for changes in the document size using document()->size() (not the size of the QTextEdit, mind). You can then resize the QTextEdit's parent to reflect the new size. To fill the parent with the widget, use a layout. I've used QVBoxLayout several times below for this purpose. (One, to hold multiple QTextEdit widgets in a vertical column; Two, to hold a single TextEdit).
#include <QtCore>
#include <QtWidgets>
class DyTextEdit : public QTextEdit
{
Q_OBJECT
public:
DyTextEdit(QWidget *parent = nullptr) :
QTextEdit(parent)
{
QTextDocument *doc = document();
currentSize = doc->size();
qDebug() << "[Init] Size:" << currentSize;
// listen to text/content changes
connect(this, &QTextEdit::textChanged, this, &DyTextEdit::on_textChanged);
// connect <> 💡
connect(this, &DyTextEdit::sizeChanged, this, &DyTextEdit::resizeParent);
doc->setTextWidth(-1);
emit sizeChanged(currentSize); // init
}
signals:
// emitted when document size changes
void sizeChanged(QSizeF size);
public slots:
void on_textChanged()
{
QSizeF newSize = document()->size();
qDebug() << "[TextChanged] Size:" << newSize;
// detect changes in the document size
if (newSize != currentSize)
emit sizeChanged(newSize); // emit signal💡
// update size
currentSize = newSize;
}
void resizeParent(QSizeF size)
{
// resize the parent's height, don't bother with the width
parentWidget()->setFixedHeight(size.height());
}
private:
QSizeF currentSize; // keeps track of current document size
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// holds all dytextedits
QWidget aggregateWidget;
QVBoxLayout aggregateLayout(&aggregateWidget);
// note: for scalability, use a QList (or QLists)
// first textbox
QWidget widget;
QVBoxLayout vbLayout(&widget);
vbLayout.setMargin(0);
DyTextEdit dytext(&widget);
vbLayout.addWidget(&dytext);
// first button row
QHBoxLayout hbLayout(&widget);
hbLayout.setMargin(0);
QPushButton pb1a("Push this.", &widget);
hbLayout.addWidget(&pb1a);
QPushButton pb1b("Push this.", &widget);
hbLayout.addWidget(&pb1b);
// second textbox
QWidget widget2;
QVBoxLayout vbLayout2(&widget2);
vbLayout2.setMargin(0);
DyTextEdit dytext2(&widget2);
vbLayout2.addWidget(&dytext2);
// second button row
QHBoxLayout hbLayout2(&widget2);
hbLayout2.setMargin(0);
QPushButton pb2a("Push this.", &widget2);
hbLayout2.addWidget(&pb2a);
QPushButton pb2b("Push this.", &widget2);
hbLayout2.addWidget(&pb2b);
// add widgets to layout
aggregateLayout.addWidget(&widget); // cell 1
aggregateLayout.addLayout(&hbLayout); // |
aggregateLayout.addWidget(&widget2); // cell 2
aggregateLayout.addLayout(&hbLayout2); // |
aggregateLayout.setSizeConstraint(QLayout::SetMinAndMaxSize);
aggregateWidget.show();
return a.exec();
}
#include "main.moc" // include if there are QObject classes in main.cpp
Note that above, I've used rows of buttons using QHBoxLayout and added them to the overall, aggregate layout detached from the textbox widgets. You can still use a say, QGridLayout if you wish to set your buttons in a different format. You can also combine the textedits and button layouts as children under another widget (to serve as an umbrella) covering both. That would more clearly form a cell of widget and may be easier to move around. (I've uploaded a workable example of this on Github.)
Demo (macOS):
This is a screenshot of after typing content and copy-pasting content.
Note that the aggregate widget will contract on removal of lines of text.

QTabWidget strange behavior

I have two tabs where placed QTableWidget with cell widget. See image.
QTabWidget *tab = new QTabWidget(this);
for (int i = 0; i < 2; ++i) {
QTableWidget *t = new QTableWidget(1, 1);
QPushButton *btn = new QPushButton("Click on me!");
t->setCellWidget(0, 0, btn);
connect(btn, &QPushButton::clicked, [=]() {
btn->hide();
});
tab->addTab(t, QString::number(i + 1));
}
setCentralWidget(tab);
The behavior you indicate is predictable, you must first know that if a widget becomes visible your children will also be visible.
Let's analyze the case of QTabWidget, this widget is essentially a QTabBar + QStackedWidget, the QStackedWidget manages the visibility of the widgets, and the latter internally has a QStackedLayout that when it establishes that a certain widget is wanted to show then it hides the current widget and shows the new one current widget. So every time you select tabbar the widget that is displayed will call the show method of that widget (in your case QTableWidget), and it will make your children visible even if they are hidden.
So if you want that if you have the need that works as you suppose a workaround should happen it is to save the status of the visibility in a property and in the showEvent method to apply the change if necessary.

Hide Icon from the label of QComboBox

I am trying to implement a QComboBox, which holds QIcon and QString, like this:
QComboBox.addItem(icon, label);
I want the icons to be visible in the drop-down list, but not in the toolbar. Only the string should be visible after item selection.
Is there an easy way to do this?
To solve this problem, it is enough to override the paintEvent method, taking the default implementation from the sources. Before drawing control QStyle::CE_ComboBoxLabel, it is necessary to set an invalid value of the QStyleOptionComboBox.currentIcon
This works well if the combobox is not editable, otherwise there is an empty space on the left side intended for drawing the icon. Looking at the source, I found out that the combobox changes the geometry of the QLineEdit. If the current element has an icon then geometrically QLineEdit will shift to the right. In order to prevent this in the same paintEvent, it is necessary to force the geometry of QLineEdit without taking into account the icon.
The following code takes this into account and works well in both modes:
class ComboBox : public QComboBox {
public:
ComboBox(QWidget *parent = nullptr)
: QComboBox(parent) { }
protected:
void paintEvent(QPaintEvent *) override {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
opt.currentIcon = QIcon();
if (auto le = lineEdit()) {
le->setGeometry(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this));
} else {
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
}
};
The best way is to set a delegate and to draw the items yourself.
Then you can choose when or not to draw the icon ( decorationRole ), you can choose not to draw the icon for the index which is the current index.
I could find a quick example on how to use a delegate on combobox:
http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate
But I am afraid it is not the most easist of the ways.
Good luck!

Remove QGraphicsEffect from child widget [duplicate]

This question already has answers here:
Blur effect over a QWidget in Qt
(3 answers)
Closed 6 years ago.
I'm making a game whith QGraphics framework.
When the game finish I want to blur the whole graphicsview and show a widget with some text on top of it.
My simplified code is :
In view :
void GraphicsTraxView::showGameResultWidget(bool res)
{
blurEffect_->setEnabled(true);
WigglyWidget *wigglyWidget = new WigglyWidget(this);
if(res)
wigglyWidget->setText("you win");
else
wigglyWidget->setText("you loose");
wigglyWidget->resize(150,150);
wigglyWidget->move(QPoint(
getScreenSize().width() / 2 -75, getScreenSize().height() / 2-75));
wigglyWidget->show();
}
In text widget :
void WigglyWidget::paintEvent(QPaintEvent * /* event */)
{
QColor backgroundColor = palette().light().color();
backgroundColor.setAlpha(220);
QPainter customPainter(this);
customPainter.fillRect(rect(), backgroundColor);
//...
But as you can see in image bellow text become blur and unreadable with view too.
How can I remove graphicsEffect from child widget and still keep background color of my widget semi transparent ?
You can embed your WigglyWidget in a dialog, or make itself a dialog.
You can then make it transparent by setting the window opacity with setWindowOpacity.
If you embedded your WigglyWidget into a dialog, you have to set the Qt::WA_TranslucentBackground attribute to the dialog to make the background transparent.
Then you have to set the Qt::FramelessWindowHint | Qt::Dialog flags to it to get rid off the title bar.
Heres an example:
QDialog *pop_up = new QDialog(this);
pop_up->resize(200, 200);
pop_up->setAttribute(Qt::WA_TranslucentBackground);
pop_up->setWindowOpacity(0.5);
pop_up->setStyleSheet("QDialog{background-color: transparent;}");
pop_up->setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
pop_up->setLayout(new QVBoxLayout);
QLabel *transparentLabel = new QLabel("TRANSPARENT");
transparentLabel->setAlignment(Qt::AlignCenter);
transparentLabel->setStyleSheet("QLabel{color: red; font: 20pt bold; background-color: #00baff;}");
pop_up->layout()->addWidget(transparentLabel);
pop_up->show();

Weird control boundaries detection on QGraphicsProxyWidget

Hello in my application i'm making a QGraphicsView and set the scene on it:
QGraphicsProxyWidget *rotateItemIcon;
HoverFilter *hv = new HoverFilter(); // my hover filter class
connect(hv,SIGNAL(SignalHover(QObject*)),this,SLOT(ObjectHover(QObject*)));
connect(hv,SIGNAL(SignalHoverLeave(QObject*)),this,SLOT(ObjectHoverLeave(QObject*)));
ui->TestIcon->installEventFilter(hv);
...
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 661, 255);
ui->TestIcon->setParent(NULL);
rotateItemIcon = scene->addWidget(ui->TestIcon); // here i add my control to the scene and receive QGraphicsProxyWidget object
rotateItemIcon->setTransformOriginPoint(ui->TestIcon->width()/2,
ui->TestIcon->height()/2);
ui->graphicsViewFive->setScene(scene); //QGraphicsView on my form
ui->graphicsViewFive->show();
my HoverFilter.cpp
#include "hoverfilter.h"
#include "QDebug"
HoverFilter::HoverFilter()
{
}
bool HoverFilter::eventFilter( QObject *dist, QEvent *event )
{
if( event->type() == QEvent::Enter )
{
emit SignalHover(dist);
return true;
}
if( event->type() == QEvent::Leave )
{
emit SignalHoverLeave(dist);
return true;
}
return false;
}
rotateItemIcon is my QGraphicsProxyWidget and the problem is that it has weird boundaries, i need to implement some animation on hover of my control TestIcon, (i done that using event filter) mouse enter and mouse leave fires when i drag my mouse on a random places, not only on my TestIcon control. If do not add my control to the QGraphicsScene hover detection works fine, so i assume this is a scene/proxywidget problem. Is there a way i can set size or boundaries to QGraphicsProxyWidget to stop that?
I'm not sure if I completely understand your question, but it sounds like you're have a widget placed in a scene via a QGraphicsProxyWidget and when you try to detect if your mouse is over the widget you want it to animate.
By 'weird boundaries' I assume you mean the extents to which the proxy widget is accepting the mouse as being over the widget. If so, I suggest either calling setGeometry on the QGraphicsProxyWidget to set its position and dimensions, or inherit from QGraphicsProxyWidget and implement the boundingRect function to define the area of the proxy widget.