QToolTip Style Sheet not working if using QToolTip::showText - c++

I'm trying to use QSS to customize the look of the tool-tip, it works. However, if I try showing the tooltip using QToolTip::showText function, it won't work as intended which means that the QToolTip style sheet doesn't apply to it probably?
My purpose: When changing a slider value I want to show a rectangle somewhere near the slider, so I thought the tooltip was the easiest way to do it?
If you don't understand what I mean, I'm trying to make sliders in Qt feel like Windows 10 UWP so if you want to understand what I mean look for example any slider in Windows like go to Settings->System->Sound and see how their slider works. I've done everything they have except that rectangle which shows when value get changed.
What I'm trying to do:
In the style sheet
QToolTip
{
color: red;
}
In my custom Slider class (inherited from QSlider)
class FSlider : public QSlider
{
Q_OBJECT
public:
FSlider(QWidget *parent = 0) : QSlider(parent) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(notifyValueChanged(int))); }
protected:
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
bool event(QEvent* event);
private slots:
void notifyValueChanged(int Value);
};
void FSlider::notifyValueChanged(int Value)
{
QStyleOptionSlider opt;
initStyleOption(&opt);
QRect sliderHandle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle);
QToolTip::setFont(QFont("Segoe UI", 13, 400));
QToolTip::showText(mapToGlobal(QPoint(sliderHandle.x() - sliderHandle.width() - 2, sliderHandle.y() - sliderHandle.height() - 30)), QString::asprintf("%i", Value));
}
Thanks for reading !

I had the same issue at first with Qt 5.12.2. I was missing the third argument to QToolTip::showText. I had to add the parent widget. In that case, the CSS that I had applied to the tooltip in mainwindow.ui applied to the QToolTip . Without the parent, it is a separate window shown on the position specified and has no knowledge of the css.

Related

How to combine a QLineEdit with a QPushButton having no space in between?

I need to make an element like this:
It's a combination of a line edit and a button with the same height and no space between them.
I have tried in Qt Designer but the height of them is not the same and there is always a small space between these 2 elements.
How can I solve this problem?
Maybe you could customize a QComboBox, something like this:
#include <QComboBox>
#include <QLineEdit>
class CustomBox : public QComboBox
{
Q_OBJECT
public:
CustomBox(QWidget * parent = nullptr) : QComboBox(parent)
{
setEditable(true);
QString ss =
"QComboBox::drop-down {border: none;}"
"QComboBox::down-arrow { image: url(/data/whatever.png); }"
"QComboBox::down-arrow:pressed { image: url(/data/whatever-pressed.png); }";
setStyleSheet(ss);
connect(lineEdit(), &QLineEdit::editingFinished, [this](){
QString t = lineEdit()->text();
clear();
lineEdit()->setText(t);
});
}
void setText(const QString & t) { lineEdit()->setText(t); }
QString text() const { return lineEdit()->text(); }
protected:
void showPopup() override
{
emit buttonClicked();
}
signals:
void buttonClicked();
};
The combo box is set as editable, so you can use an underlying QLineEdit, which is, more or less, under full control through the protected lineEdit member function. As you can see, I connected its editingFinished signal to a lambda to avoid adding items to the combo each time (this happens when the user press Enter in an editable combo box).
The styling is quite simple, given that you have a couple of icons for the button.
I exposed the line edit text getter/setter, and added a signal for the button click, which gets emitted from the showPopup protected method (called when the user press the button).
Just an idea.

Qt set style for all objects but one

I've currently got this style for all push buttons:
this->setStyleSheet(".QPushButton { background-color: #416eb6; color: #ddd; }");
This is great because it allows me to keep a constant styling for all the QPushButton's without having to style them individually.
An issue arises when I need to style a QPushButton representing a color chooser. The button should represent the color that was chosen from the color chooser, but instead it just keeps the initial style that I set.
Things I have tried:
Giving an empty style sheet for the item:
this->setStyleSheet(#m_colorChooserButton { });
Setting the style to initial:
this->setStyleSheet(#m_colorChooserButton { background-color: initial});
Using a similar css selector :not:
this->setStyleSheet(".QPushButton:not(#m_colorChooserButton) { background-color: #416eb6; color: #ddd; }");
Is there any way to achieve this result?
I'd like to mimic the :not selector if possible since that's the most straightforward, but at this point, I'd do anything that works.
I'd like to avoid having to manually specify the style for each button I want it to show on, as there are well over 100 buttons and finding the object names of these buttons is very time consuming (large legacy code base).
Thanks
You have 2 choices: apply a stylesheet to just that button and invalidate/reapply it every time you change the selected colour or just subclass that button and reimplement the paintevent:
class ColorButton : public QPushButton{
Q_OBJECT
Q_DISABLE_COPY(ColorButton)
public:
ColorButton(QWidget* parent = Q_NULLPTR)
:QPushButton(parent)
,m_color(Qt::red)
{}
const QColor& color() const {return m_color;}
void setColor(const QColor& val){if(m_color==val) return; m_color=val; update();}
protected:
void paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);
option.palette.setColor(m_color,QPalette::Background);
p.drawControl(QStyle::CE_PushButton, option);
}
private:
QColor m_color;
};
P.S.
The KDE API already has that widget: https://api.kde.org/frameworks-api/frameworks-apidocs/frameworks/kwidgetsaddons/html/classKColorButton.html

How to expand tabs in QTabWidget Qt

I have a QTabWidget like this one:
But I want to expand the tabs to "fill" the entire widget width, like this:
How can I do that?
I am using Qt 5.3.2 and Qt Creator 3.2.1
Update:
I tried to use the setExpanding function:
ui->myTabWidget->tabBar()->setExpanding(true);
But it didn't work.
I found that QTabBar has a setExpanding method, which appears to do exactly what you want, but I tried it (on Windows), and it doesn't work. This is the code:
ui->tabWidget->tabBar()->setExpanding (true);
Then I found the following post:
https://forum.qt.io/topic/47404/qtabbar-will-not-expand-its-tabs
I find the answer provided in the above post to be debatable. He says it's respecting the operating system style whether or not the expanding property is set to true and that it's a feature, not a bug, and that you have to subclass QTabBar to get the desired behavior. If I write code to do a specific thing, I feel like my instructions should override the OS style. If I just wanted the OS style, I would leave that special code out. However much I disagree with the implementation, that appears to be what we're stuck with.
So if it's important to you to have this look, then you'll need to subclass QTabBar, override the tabSizeHint--I suspect that will take some experimentation--and use QTabWidget::setTabBar to replace the default with your own. According to the documentation, you have to do that before adding any tabs, so this mechanism is not workable if you want to create your tab widget in Qt Designer. (Another argument in favor of implementing setExpanding as an override to the OS style rather than the way it's been done.)
Just set both expanding and document mode to true.
ui->tabWidget->tabBar()->setDocumentMode(true);
ui->tabWidget->tabBar()->setExpanding(true);
Works for me :)
As mostefa answered here, I can set a fixed width for the tabs using styleSheet.
I am calculating the width based on the QTabWidget width.
To get the QTabWidget width correctly I need to get it in the showEvent function:
void LogListForm::showEvent(QShowEvent *ev)
{
/*
* Divide by 2 because we have 2 tabs.
* I need to decrease 24 pixels to fill the width correctly.
*/
int tabWidth = (ui->myTabWidget->width()/2)-24;
/*
* Then, I set this tabWidth to the styleSheet.
* Note: I need to set the previously styleSheet to not lose it
*/
ui->myTabWidget->setStyleSheet( ui->myTabWidget->styleSheet() +
"QTabBar::tab {"
"width: " + QString::number(tabWidth) + "px; }" );
}
This works as well:
QTabWidget::tab-bar
{
min-width:1000;
}
// Qt 5.12.2
// just use TabWidget in place of QTabWidget, nothing else
class TabWidget : public QTabWidget
{
class TabBar : public QTabBar
{
QSize _size;
public:
TabBar(QWidget* a_parent) : QTabBar(a_parent)
{
setWidth(size().width());
}
QSize tabSizeHint(int index) const
{
return QSize(_size.width()/(count()?count():1), size().height());
}
void setWidth(int a_width)
{
_size = QSize(a_width, size().height());
QTabBar::resize(_size);
}
};
TabBar* _tabBar = new TabBar(this);
public:
TabWidget(QWidget* a_parent) : QTabWidget(a_parent)
{
setTabBar(_tabBar);
}
void resizeEvent(QResizeEvent *e) override
{
_tabBar->setWidth(size().width());
QTabWidget::resizeEvent(e);
}
};
this worked for me:
ui->myTabWidget->tabBar()->setDocumentMode(true);
Suggestion from Mitak worked. Assuming the tabwiget is in the centralwidget of the mainwindow, one can replace a tabwidget created with the designer as follow in the code :
QTabWidget* newTabWidget = new GeneralTabWidget(ui->tabWidget_ToReplace->parentWidget());
ui->centralWidget->layout()->replaceWidget(ui->tabWidget_ToReplace, newTabWidget );
delete ui->tabWidget_ToReplace;
ui->tabWidget_ToReplace= newTabWidget ;
if the tabwidget is situated in another location or in a dialog, one need to replace it in the appropriate layout.
Not pretty, but what worked for me was to forced the value setExpending in the minimumSizeHint override.
class A: public QTabWidget
{
A(QWidget *p = nullptr): QTabWidget(p)
{
setDocumentMode(true);
}
virtual QSize minimumSizeHint() const override
{
tabBar()->setExpanding(true);
return QTabWidget::minimumSizeHint();
}
};

Why do I not see the drop indicator in a QTableView?

I use drag and drop in my QTableView (works). However, I do not see any drop indicator. I should see a line where the drop is supposed to be inserted, shouldn't I? At least here they say so.
My init is pretty much standard.
// see model for implementing logic of drag
this->viewport()->setAcceptDrops(allowDrop);
this->setDragEnabled(allowDrag);
this->setDropIndicatorShown(true);
this->m_model->allowDrop(allowDrop);
I have no idea why I do not see the indicator. A style sheet is used with the views, could that be the reason. However, I have disabled the stylesheet and still do not see it.
The view uses entire rows for selection, not sure if this causes an issue. So any hint is appreciated.
-- Edit --
As of the comment below, tried all selection modes: single, multi or extended, no visual effect. Also tried cell instead of row selection, again no improvement.
-- Edit 2 --
Currently evaluating another style proxy example, similar to the one below, originally referenced here
-- Related --
QTreeView draw drop indicator
How to highlight the entire row on mouse hover in QTableWidget: Qt5
https://forum.qt.io/topic/12794/mousehover-entire-row-selection-in-qtableview/7
https://stackoverflow.com/a/23111484/356726
I faced the same problem, I tried two options which both worked for me. IIRC, the help came from an answer on SO.
if you are subclassing QTreeView, you can override its paintEvent() method. It is calling by default the drawTree() method and the paintDropIndicator() one (the latter being part of QAbstractItemView private class).
You can call drawTree() from your paintEvent(), and it should override the default drag and drop indicator as well :
class MyTreeView : public QTreeView
{
public:
explicit MyTreeView(QWidget* parent = 0) : QTreeView(parent) {}
void paintEvent(QPaintEvent * event)
{
QPainter painter(viewport());
drawTree(&painter, event->region());
}
};
the other method is to subclass QProxyStyle and overriding the drawPrimitive() method. When you get the element QStyle::PE_IndicatorItemViewItemDrop as a parameter, you can paint it your own way.
The code will look like this:
class MyOwnStyle : public QProxyStyle
{
public:
MyOwnStyle(QStyle* style = 0) : QProxyStyle(style) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
//custom paint here, you can do nothing as well
QColor c(Qt::white);
QPen pen(c);
pen.setWidth(1);
painter->setPen(pen);
if (!option->rect.isNull())
painter->drawLine(option->rect.topLeft(), option->rect.topRight());
}
else
{
// the default style is applied
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};

Paint over top of label, not behind it in Qt

I am creating a simple gauge in Qt 4.7.4, and everything is working wonderfully. Except for the fact that, for the life of me, I cannot get the dial shape to paint over the text labels when it passes over them. It always paints it behind the label. I am just using a simple drawpolygon() method.
I'm thinking this has something to do about paint events? I am drawing everything inside a QFrame inside a MainWindow. I am using QFrame's paintEvent.
Edit:
The QLabels are created on start up with new QLabel(this). They are only created once, and never touched again ( Similar to manually adding them on the Ui with Designer). The drawpolygon() is in the QFrame's Paint event.
"myclass.h"
class gauge : public QFrame
{
Q_OBJECT
public:
explicit gauge(QWidget *parent = 0);
~gauge();
void setValues(int req, int Limit, bool extra=false);
private:
void drawDial();
protected:
void paintEvent(QPaintEvent *e);
};
"myclass.cpp"
void gauge::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
drawDial();
return;
}
void gauge::drawDial()
{
QPainter Needle(this);
Needle.save();
Needle.setRenderHint(Needle.Antialiasing, true); // Needle was Staggered looking, This will make it smooth
Needle.translate(centrePt); // Center of Widget
Needle.drawEllipse(QPoint(0,0),10,10);
Needle.restore();
Needle.end();
}
If the gauge widget and the QLabels are siblings, then you can move the gauge widget to the front by calling its raise() method.
If the QLabels are children of the gauge widget, on the other hand, then they will always display in front of it. In that case you can either reorganize your widget hierarchy so that they are siblings instead, or you can get rid of the QLabels and simply call drawText() from your paintEvent() method instead (after drawDial() returns)