QT - overriding QToolButton paint event - qt

I have own widget MyToolButton which inherit from QToolButton. In paintEvent I want draw only "background" from original QToolButton. I mean, I need draw only style (clicked, hover, etc) but without text and image. These things I want draw by my self (with custom formatting etc). When I call QToolButton::paintEvent(ev) then everything is painted. How to exclude image and text from painting? I supose that I should use drawControl(), drawPrimitive() and drawComplexControl() but can't understands all these states

Source code of void QToolButton::paintEvent(QPaintEvent *) is:
QStylePainter p(this);
QStyleOptionToolButton opt;
initStyleOption(&opt);
p.drawComplexControl(QStyle::CC_ToolButton, opt);
QStylePainter::drawComplexControl calls QStyle::drawComplexControl, which is a pure virtual function, and its implementation depends on a style, you are using. What you need is to go to a source code of concrete impementation (like QWindowsXPStyle or QGtkStyle), find drawComplexControl() implementation and use it in your paintEvent. For a simple control like QPushButton, you can easy rewrite its drawing quite style-independently. Redrawing QToolButton would be a bit more tricky task.
A good choice would be using source code of QCommonStyle.

Related

how to draw text in QQuickItem

I've been searching the internet how to draw or render text on QQuickItem but to no avail. I opt not to use QQuickPaintedItem which uses QPainter paint() function. Aside from that there is also a known issue of QQuickPaintedItem on iOS retina display devices where the display is blurred and edges were not sharp.
Please advise any possible work-around on this.
Since QtDeclarative has been deprecated already, I opt not to use
QQuickPaintedItem which uses QPainter paint() function
That statement doesn't make a lot of sense. QtDeclarative is QtQuick1, QQuickPaintedItem is part of the QtQuick2 module and has nothing to do with QtDeclarative. Furthermore, even though it uses QPainter it is still hardware accelerated using OpenGL.
Overloading a custom QQuickItem to draw text in it manually, without assistance from QPainter or any other similar class will be a very complex task.
A QQuickItem is basically the class behind QML's Item element. QML also has a Text element. QML has been designed for rapid UI development, it is entirely pointless to draw the text manually. You don't need any C++ for this, only QML:
Item {
Text {
text: "something"
}
}
Take a look at the Text element and its properties, you can specify font, color and whatnot. You can directly use the element as a source for graphics effects too.
You can use QPainter to draw on a QImage like a canvas. Then you can turn this into a QSGTexture that can be assigned to a QSGSimpleTextureNode.
Here is an excerpt of code I recently wrote to do this:
QColor color("steelblue");
QRect rect(0,0,aw, ah);
for(auto ch: m_charList){
QImage canvas(rect.width(), rect.height(), QImage::Format_RGBA8888);
canvas.fill(QColor("transparent"));
QPainter painter(&canvas);
QFont font = painter.font();
//font.setPixelSize(48);
font.setPixelSize(rect.width());
font.setBold(true);
painter.setFont(font);
painter.setPen(color);
QRect bounding = QRect(0, 0, rect.width(), rect.height());
painter.drawText(0, 0, rect.width(), rect.height(), Qt::AlignCenter, ch, &bounding);
QSGTexture *texture = this->window()->createTextureFromImage(canvas);
m_textureList[ch] = texture;
}
The repo with the full working code in context is here.
There are MANY reasons to want to render like this. You can do rotations in 3D space to QSGNodes for one.

How can I prevent a QWidget from painting, but still respond to events?

I have a QGLWidget that renders an OpenGL scene inside a Qt application. I would like to add some other translucent Qt widgets that are overlaid on top of the QGLWidget. This is more difficult than with standard widgets since the OpenGL drawing context is unknown to Qt's painter classes. So, if I just do the obvious thing and place a transparent toolbar on top of the QGLWidget for instance, the transparent part of the toolbar instead renders black (it doesn't have access to the OpenGL frame buffer when painting).
It seems that the recommended way for handling this sort of thing is to overpaint the 2D content after drawing the OpenGL scene. The linked example seems very straightforward, as long as you're just drawing simple shapes. Instead, what I would like to do is to defer the painting of some child QWidget objects to be done inside the paint event for the QGLWidget.
So, my problem boils down to this:
Prevent the overlay QWidgets from painting in the normal, 2D context.
In the paint event handler for the QGLWidget, paint the overlays after painting the 3D scene that makes up the background.
The second item on this list seems to be simple: I can use QWidget::render() inside the QGLWidget's paint event to draw the desired widgets on top of the viewport. This works just fine.
The first item is more tricky: I need a way to prevent the widgets from painting during the normal course of events and only paint them in the QGLWidget's paint handler. One obvious way to do this is to hide the overlays using QWidget::hide(). This does allow me to paint the widgets atop the OpenGL scene as I would like. However, since the widgets are hidden, they do not respond to mouse or keyboard events. As an example, I'm using a QToolBar with a few buttons on it, and the toolbar is painted properly, but is non-functional (none of the buttons respond to clicks). So, if going down this path, it seems that I would need a way to force the widget to still respond to events even though it is hidden.
Another approach that I've tried is to intercept the QToolBar's paint event using an event filter, with the goal of preventing the toolbar from painting itself. However, the toolbar is still rendered; I'm assuming that this is because the various buttons on the toolbar are child widgets that are still painted, even if I intercept the parent's paint event.
Any ideas on a way that I could accomplish my goal?
I don't understand your issue completely, but I'll try to answer the question stated in the title. You should use event filters. Install an event filter using widget->installEventFilter(object), where widget is a widget you want to block painting, and object is an object of any of your QObject-derived classes. Reimplement eventFilter of this class:
bool MyClass::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::Paint) { return true; }
return false;
}
When you return true from your eventFilter, the event is filtered and paint doesn't occur.
You can also try to use widget->setUpdatesEnabled(false) to temporarily disable painting. Don't forget to re-enable it when you're done.

Tooltips for QDockWidget close button & float button?

Is there a way to set a tool tip text for the close button & float button in a QDockWidget ?
As ixSci mentioned, using setTitleBarWidget() is a potential way of solving this problem. Having said that I was looking for a much simpler solution, ideally using QSS (Qt Style Sheets). So after digging into the source code of QDockWidget I found an alternative way which suits my requirement better.
I wanted to find the place these float and close buttons are created. That is inside QDockWidgetPrivate::init() method in QDockWidget.cpp.
As for an example, the float button is created like this:
QAbstractButton *button = new QDockWidgetTitleButton(q);
button->setObjectName(QLatin1String("qt_dockwidget_floatbutton"));
QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
Now all I need is to use the flexibility of Qt Style Sheets, for that I need only the Object Name, in this case it's "qt_dockwidget_floatbutton"
So all you need to do, to set tooltips for Close and Float buttons of a QDockWidget, is to add following two lines of styles in your application style sheet
QAbstractButton#qt_dockwidget_closebutton{qproperty-toolTip: "Close";}
QAbstractButton#qt_dockwidget_floatbutton{qproperty-toolTip: "Restore";}
You can implement whatever title widget you want and set it with setTitleBarWidget(). In that widget you can add whatever buttons with tooltips you need.

How to draw a header in my own widget in Qt?

Does anyone know how to draw a header (and other simple elements), just like in, for example, QTreeWidget, in my own widget?
I would like to use style and call something like:
drawElement(CE_Header, rect, painter);
to draw standard header in specified rect.
QStyle::drawControl can't do it, because it draws control over whole widget.
Qt documentation doesn't say much about it.
Subclass QHeaderView and reimplement the paint method, inside there you can use the QStyleOption data. Then use QTreeWidget::setHeader(QHeaderView* header) to set your header in the widget.

How can you know when a specific part of an item drawn with a QStyledItemDelegate is hovered?

I have a custom QAbstractItemModel used to display information in a QTreeWidget. However, individual indices are drawn using a QStyledItemDelegate. One item that is drawn using the delegate is a pixmap. When the user hovers the mouse over the pixmap (either help event style or hover enter style is fine) I need to do something, what it is isn't important.
So my question is, how can I know when the mouse has hovered over a specific item inside a QTreeWidget, when that item is drawn using a delegate?
In other situations, I could just subclass QLabel, set the pixmap on it, and then do whatever I need in the event() function, but in this case there is no object behind the pixmap, it is just painted onto the screen, so it doesn't actually receive events. Is it possible to use the delegate to paint an actual widget where I want it to so that widget can receive events, or do I have to work around this some other way?
You could subclass QTreeWidget and reimplement the mouseMoveEvent. In the event you can use the itemAt function in order to check if a valid item is at the mouse position and then do what you want.
void MyTreeWidget::mouseMoveEvent(QMouseEvent * event)
{
QTreeWidget::mouseMoveEvent(event);
QTreeWidgetItem* treeItem = itemAt(event->pos());
if (treeItem != NULL)
doSomething(treeItem);
}
You could avoid subclassing QTreeWidget and implement it in the parent widget/main window. Notice however that the itemAt function expects coordinates in the widget's viewport so you should transform the coordinates to tree widget's coordinates. IMHO it is more elegant to subclass it and just implement the mouseMoveEvent function.
EDIT
If you need to detect the position of an icon within the widget item, it is a bit more advanced but you can check my answer to an older question for more details:
Position of icon in QTreeWidgetItem
There is no Qt built-in solution to this problem. The problem can be solved, however, by saving the geometry of the individual items that are painted using the delegate as they are painted. The paint() function, however, has a const modifier so the data structure you use to save the geometry must be mutable. In this case, I don't think this constitutes a breach of the principles of OOP, but is rather a prime example of why the mutable keyword exists and when it should be used.
You then need to subclass QTreeWidget so you can re-implement the mouseMoveEvent() function as webclectic said. Inside that function you can compare the position of the mouse to the geometry of the item that you painted earlier. If the mouse is inside the item, then it is being hovered.

Resources