I'm experiencing a problem with hover functionality on QLabel.
I've implemented this as event filter:
HoverLabel::HoverLabel(QWidget *parent) : QWidget(parent)
{
installEventFilter(this);
label = (QLabel *)parent;
show();
}
bool HoverLabel::eventFilter(QObject *object, QEvent *event)
{
if (object == this)
{
if (event->type() == QEvent::Enter)
{
label->setText("Howering");
return true;
}
else if (event->type() == QEvent::Leave)
{
label->setText("Not howering");
return true;
}
}
return false;
}
Calling while main constructor is running as:
hoverLabels[0] = new HoverLabel(ui->hoverLabel_1);
Now everything works fine except that the area where I ger QEvent::Enter is just too small and what's more, constant - you can double the size of label and area that generates event stays the same.
I've marked the hover area on picture with blue rectangle, as the mouse wasn't captured for whatever reason. Beyond that, it say's "Not howering". I've tried various text content, various text sizes, checking all the boxes around, setting different size policies but the area is still the same.
In short, the class had to override eventFilter. In my case class is called PlayerLabel.
See these 2 files for how to:
header file:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/inc/gui/mainwindow.h#L40
source file:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/src/gui/mainwindow.cpp#L908
Also see how to create this object afterwards:
https://github.com/metthal/ICP-Projekt/blob/1f0a0f6e919d132bbec6bacd1cff91a3a596460d/src/gui/mainwindow.cpp#L358
Related
I am trying to filter out any horizontal wheel/touchpad scrolling in a widget (ProjectTreeView) that is based on a QTreeView. The idea is to let the vertical movement pass through, supporting horizontal scrolling only via the scrollbar.
I've tried to implement this via a ProjectTreeView::wheelEvent() event handler (below), but that function somehow has the opposite effect. Am I overlooking something (too) obvious or doing something wrong altogether?
void ProjectTreeView::wheelEvent(QWheelEvent *e)
{
if ((e->pixelDelta().x() != 0 || e->angleDelta().x() !=0)
#ifdef Q_OS_MACOS
// allow horizontal scrolling controlled by a physical mouse wheel
&& e->source() != Qt::MouseEventNotSynthesized
#endif
){
QPoint pixelDelta(e->pixelDelta()), angleDelta(e->angleDelta());
// disable horizontal wheel scrolling
pixelDelta.setX(0);
angleDelta.setX(0);
// discard the original event
e->accept();
QWheelEvent filtered(e->posF(), e->globalPosF(), pixelDelta, angleDelta,
e->delta(), e->orientation(), e->buttons(),
e->modifiers(), e->phase(), e->source(), e->inverted());
QCoreApplication::sendEvent(this, &filtered);
} else {
QTreeView::wheelEvent(e);
}
}
When I try to filter via ProjectTreeView::event(QEvent*) I observe that
1) I hardly receive any wheel events at all, at least not when I'm trying to scroll (some come in when I release the touchpad)
2) the events that do come in don't have the required delta information (both components are 0).
This reminds me of remarks I've seen about the Qt4 implementation about how the events are actually treated by a different widget.
thanks!
You can do it easily by installing and event filter function.
bool eventFilter( QObject * o, QEvent * e ) {
if (e->type() == QEvent::Wheel
&& qobject_cast<ProjectTreeView*>( o ) ) {
// Then do what you want, per example: ignore it.
e->ignore();
return true;
}
return QWidget::eventFilter( o, e );
}
If you want the user to use the scroll by clicking in the scrollbar then you can modify the focus polizy of the widget. It should fix your problem:
setFocusPolicy(Qt::ClickFocus);
In the end, this works:
void ProjectTreeView::wheelEvent(QWheelEvent *e)
{
if ((e->pixelDelta().x() !=0 || e->angleDelta().x()!= 0
|| e->orientation() == Qt::Orientation::Horizontal)
#ifdef Q_OS_MACOS
// Cocoa: allow horizontal scrolling controlled by a physical mouse wheel
&& (!isCocoa || e->source() != Qt::MouseEventNotSynthesized)
#endif
){
QPoint pixelDelta(e->pixelDelta()), angleDelta(e->angleDelta());
pixelDelta.setX(0);
angleDelta.setX(0);
// discard the original event
e->ignore();
if (!pixelDelta.isNull() || !angleDelta.isNull()) {
QWheelEvent filtered(e->posF(), e->globalPosF(), pixelDelta, angleDelta,
e->delta(), Qt::Orientation::Vertical, e->buttons(),
e->modifiers(), e->phase(), Qt::MouseEventSynthesizedByApplication, e->inverted());
QCoreApplication::sendEvent(this, &filtered);
}
}
QTreeView::wheelEvent(e);
}
The key was realising that some events only had the orientation set to horizontal but no non-zero delta information.
The isCocoa member is to detect when I'm running my tweaked XCB QPA plugin (= using Qt under XQuartz).
I have customized QWidget named "MainWidget" and a customized QWidget named "ChildWidget".
MainWidget is the parent of ChildWidget and these child widgets are created dynamically and inserted into the MainWidget's stack of widgets.
I have used style sheet to set the background color of the both MainWidget and ChildWidget in their respective .ui (form) file.
Select the widget ->Right Click on Widget-> Change stylesheet -> select background color-> apply -> save the .ui file
"background-color: ;"
Each ChidlWidget has its own unique size (width & height) such that when they have stacked on MainWidget's stack of widgets, there may be chances of one widget overlapping other widget, some widgets are completely obscured and some widgets are partially visible.
I am trying to find the visibility state of all types of widgets ( obscured, completely visible , partially visible) .
I am using following code snippet.
void MainWidget::printVisibilityState()
{
QList<ChildWidget *> childWidgetsList = findChildren<ChildWidget *>();
for (register int i = 0; i < childWidgetsList.size(); i++)
{
ChildWidget* pWidget = childWidgetsList.at(i);
QRegion visibleRegion = pWidget->visibleRegion();
QRect visibleRegionBoundingRect = visibleRegion.boundingRect();
int visibleRegionRectsCount = visibleRegion.rects().count();
QRect widgetRect = pWidget->rect();
if (visibleRegion.isEmpty()) {
qDebug() <<pWidget->getName()<< "is OBSCURED";
}
else {
if (
(visibleRegionBoundingRect == widgetRect) &&
(1 == visibleRegionRectsCount))
{
qDebug() <<pWidget->getName()<< "is VISIBLE";
}
else
{
qDebug() <<pWidget->getName()<< "is PARTIALLY VISIBLE";
}
}
}
}
and I have implemented painEvent in both MainWidget and ChildWidget with the following code snippet.
void ChildWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void MainWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
This code prints the visibility state for all child widgets as VISIBLE always.
Note: Instead of setting the background-color using style sheet, if I use the setAutoFillBackground(true) for both MainWidget and ChildWidget then I am getting the proper visibility state.
Could you please guide me why it doesn't get me the proper visibility state when used style sheet?
Am I missing something in calculating the visibility state?
Are there any other way to get the visibility state info?
The QWidget document (http://qt-project.org/doc/qt-4.8/qwidget.html#visible-prop) says that, "A widget that happens to be obscured by other windows on the screen is considered to be visible."
If this is the case How do we determine the visibility state for the partially visible widgets? ( For example an error popup on other window, so error popup is completely visible and window is partially visible)
Please help me
Regards
SRaju
I would like to write a custom QLabel subclass with some more features for responsive design. In thisexample, I want to write a QLabel which scales the text based on the useable space. This is quite easy but also has some problems because of Qt-intern stuff. (I have to scale the text to 0.9 of the useable space, otherwise resizing the window / widget gets buggy)
Now I wan't to add a way to hide the label completely when the font size is bellow a specific threshold. However, this seems to be quite a complex task.
Here is what I have sofar in the classes resizeEvent(QResizeEvent *event) function.
Right now, my function only sets the text to "" when the size would be bellow the threshold.
void CustomLabel::resizeEvent (QResizeEvent * event ) {
if(autoFontResize) {
this->setSilentText(labelText); // just the normal setText function, I overwrote it for the subclass
QFont f = this->font();
int flags = Qt::TextDontClip|Qt::TextWordWrap;
QRect fontBoundRect = this->fontMetrics().boundingRect(this->rect(), flags, this->text());
float xFactor = (float)event->size().width() / (float)fontBoundRect.width();
float yFactor = (float)event->size().height() / (float)fontBoundRect.height();
float factor = xFactor < yFactor ? xFactor : yFactor;
f.setPointSizeF(f.pointSize()*factor*0.9); //
if(minimumFontSize != 0) { // 0 = no minimum Size for the font
if(f.pointSize() < minimumFontSize) {
if(hideFontOnMinimum) { // either Hide or set to the limit size
this->setSilentText(""); //replace text
} else {
f.setPointSizeF(minimumFontSize);
}
}
}
this->setFont(f);
}
QLabel::resizeEvent(event);
}
By the way, some parts of the code are found on stackoverflow, not mine. ;)
What I would like to do is to completely hide() the label. However the label doesn't know when It can show() again since the resizeEvent doesn't seem to be called after that.
Any ideas?
Thanks!
As you've noticed, if you call hide() on the widget it fails to receive a resize event. Since you're customising the class anyway, rather than calling hide(), you could just set a class variable to note that it's hidden and overload the paintEvent function, not to draw the widget if the variable is set: -
void CustomLabel::paintEvent(QPaintEvent * event)
{
if(m_hideOnMinimum)
return;
QLabel::paintEvent(event);
}
Note that by not painting the label, it will be hidden, but the user may still be able to interact with it, so you will need to disable it or overload keyboard / mouse events too.
I copied the question description below from other asked but not answered question, because this is the exactly the same one I wanna ask.
I have a QMenu with a translucent background and rounded edges (border-radius). Unfortunately, Windows 7 draws a drop shadow for this menu, which does not fit to the rounded edges. Its the shadow that would be drawn for normal rectangular menus.
Is there either - a way to completely disable drawing drop shadows for QMenu or - a way to make the shadow fit to the rounded edges ?
Here is a minimalistic example where it occurs:
QPushButton b("press me");
QMenu m;
m.addAction("hello"); m.addAction("world");
m.setWindowFlags(m.windowFlags() | Qt::FramelessWindowHint);
m.setAttribute(Qt::WA_TranslucentBackground);
m.setStyleSheet("background:rgba(255,0,0,50%); border-radius:5px;");
b.setMenu(&m);
b.show();
Right now I have to turn off the menu shadow in Windows Control panel manually to get rid of that shadow.
Actually what I want to achieve is a menu like Qt's pie menu or a menu like this one:
http://upload.wikimedia.org/wikipedia/commons/8/85/Blender_2.36_Screenshot.jpg
I tried the popup widget, but it gets the shadow artifact described above.
Could anyone help this out?
On Windows Vista and higher, I wanted a menu with normal window shadow. So I had to do two things:
Remove CS_DROPSHADOW from the menu HWND's WNDCLASS that Qt is adding deep down in the core.
Add shadow using DWM APIs.
The trick is to capture QEvent::WinIdChange to get the HWND handle to the menu window, and then to use GetClassLong / SetClassLong to remove CS_DROPSHADOW flag. I'm doing this only once (by using a static bool), as theWNDCLASS is always the same for all menus. This might lead into a problem if part of your app wants to show the menu shadows and other does not.
I have subclassed the QMenu and I'm always using my overriden class when creating menus
Menu * my_menu = new Menu(tr("&File"));
mainMenu->addMenu(my_menu);
Here's the whole code, enjoy:
menu.h
#ifndef MENU_H
#define MENU_H
#include <QMenu>
class Menu : public QMenu
{
Q_OBJECT
public:
explicit Menu(QWidget *parent = 0);
explicit Menu(const QString & title);
protected:
virtual bool event(QEvent *event);
signals:
public slots:
};
#endif // MENU_H
menu.cpp
#include "menu.h"
#pragma comment( lib, "dwmapi.lib" )
#include "dwmapi.h"
Menu::Menu(QWidget *parent) :
QMenu(parent)
{
}
Menu::Menu(const QString &title) :
QMenu(title)
{
}
bool Menu::event(QEvent *event)
{
static bool class_amended = false;
if (event->type() == QEvent::WinIdChange)
{
HWND hwnd = reinterpret_cast<HWND>(winId());
if (class_amended == false)
{
class_amended = true;
DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);
class_style &= ~CS_DROPSHADOW;
::SetClassLong(hwnd, GCL_STYLE, class_style);
}
DWMNCRENDERINGPOLICY val = DWMNCRP_ENABLED;
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &val, sizeof(DWMNCRENDERINGPOLICY));
// This will turn OFF the shadow
// MARGINS m = {0};
// This will turn ON the shadow
MARGINS m = {-1};
HRESULT hr = ::DwmExtendFrameIntoClientArea(hwnd, &m);
if( SUCCEEDED(hr) )
{
//do more things
}
}
return QWidget::event(event);
}
I just remove the Qt::popup flag to get rid of the shadow.
And I have to add close codes to any other background UI. These have been more extra work, but I got what I want :)
Maybe someone can help me with the following problem:
I want to draw the content of a QImage in a QGLWidget, but the widget is painted in black.
class QGLCanvas {
public:
QGLCanvas(QWidget* parent) : QGLWidget(parent) {
}
void setImage(const QImage* image) {
img = image;
}
void paintEvent(QPaintEvent*) {
// From Painter Documentation Qt
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), *img);
p.end();
}
public slots:
void rgb_data(const void *data) {
memcpy((void *)img->bits(), data, img->byteCount()); // data will be copied (sizes are known)
// img.save("text.png"); // saves right image
this->update(); // calls repaint, but does not draw the image.
}
private:
QImage *img;
}
The Bug:
When the slot is called, the memory is copied to the image. If the image is saved, the content is correct. But the repaint method just draws black content to the widget.
The Fix:
If the memcpy line is implemented outside the slot, the image content is drawn to the widget. This fix increased code complexity a lot. Thus, i've got the following question:
The Question:
Why does the memcpy not work within the slot? Is this a general problem with Qt?
There's nothing special about a slot which would stop your code from working.
What's probably the issue is that when you call update(), a repaint is scheduled but happens asynchronously. From the code you've provided the most likely cause is img being modified between the calls to rbg_data and paintEvent
You want to be sure about the format of the QImage. When you call bits and are expecting it to be RGB, you need to check the format.
if( img->format() != QImage::Format_RGB888 )
{
// convert the image format to RGB888
*img = img->convertToFormat(QImage::Format_RGB888);
}
This way, Qt will know the image format when trying to paint it. If you fill it with RGB data but the QImage is "formatted" as ARGB, you will get some painting errors.