Qt: Visibility state of the QWidget when used stylesheet - qt

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

Related

Can you hide a QGroupBox frame but preserve it's content visible?

I have a QGroupBox. Depending on the context, it's title may be redundent (displayed in another place of the GUI), so I then need to make as if the QGroupBox was not here....but I must preserve it's content visible (so I don't want to call QGroupBox::hide())!
I need to do this dynamically at runtime and would like to avoid creating/destroying the QGroupBox + reparenting it's content....there must be an easier way to do this.
What I tried so far:
QGroupBox visible:
QGroupBox::setTitle("") removes the text.
QGroupBox::setFlat(true) makes the frame be a single line.
I end up with this:
Not too bad...but a line remains....is there a way to completely hide the QGroupBox frame but preserve it's content visible?
My option:
QGroupBox theBox;
theBox.setFlat(true);
//This removes the border from a QGroupBox named "theBox".
theBox.setStyleSheet("QGroupBox#theBox {border:0;}");
//This removes the border from the group box and all of its children
theBox.setStyleSheet("border:0;");
You can derive your own Group Box from the QGroupBox and reimplement the paintEvent() method. It should be very simple. Original QGroupBox::paintEvent() looks like this:
void QGroupBox::paintEvent(QPaintEvent *)
{
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
What you need to do is just to modify the style option right before the widget is painted:
void CMyGroupBox::paintEvent(QPaintEvent *)
{
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
// This should disable frame painting.
option.features = QStyleOptionFrame::None;
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
You can use QFrame + QGridLayout (or some more complex combination of layouts) + QSS instead of a QGroupBox.
Considering a QGroupBox only, a trivial solution via QSS could be:
static const char kSavedTitle[] = "_savedTitle";
void hideBoxFrame(QGroupBox * box) {
box->setProperty(kSavedTitle, box->title());
box->setTitle(QString());
box->setStyleSheet("border:none");
}
void showBoxFrame(QGroupBox * box) {
box->setTitle(box->property(kSavedTitle).toString());
box->setStyleSheet(QString());
}
Here's an example that does it by swapping the widgets and reparenting the children. It works for any widget that has direct children, not only QGroupBox. It would require special case handling for widgets such as QScrollArea and QMainWindow that wrap children in a special sub-widget.
See this question for a related discussion of programmatically promoting widgets.
// https://github.com/KubaO/stackoverflown/tree/master/questions/group-reparent-36603051
#include <QtWidgets>
/// Replaces the visible widget with a hidden widget, preserving the layout of the
/// children, and making the new widget visible.
void swapWidgets(QWidget * a, QWidget * b)
{
auto src = a->isVisible() ? a : b;
auto dst = a->isVisible() ? b : a;
Q_ASSERT(dst->isHidden());
/// Move the children to the destination
dst->setLayout(src->layout());
/// Replace source with destination in the parent
auto layout = src->parentWidget()->layout();
delete layout->replaceWidget(src, dst);
/// Unparent the source, otherwise it won't be reinsertable into the parent.
src->setParent(nullptr);
/// Only the destination should be seen.
src->hide();
dst->show();
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
QGridLayout wLayout{&w};
QPushButton swapBtn{"Swap"};
wLayout.addWidget(&swapBtn);
QWidget noBox;
QGroupBox box{"Group"};
wLayout.addWidget(&box);
QGridLayout boxLayout{&box};
for (int i = 0; i < 16; ++i)
boxLayout.addWidget(new QLabel(QString("Tr%1").arg(i)), i/8, i%8);
swapBtn.connect(&swapBtn, &QPushButton::clicked, [&] { swapWidgets(&box, &noBox); });
w.show();
return app.exec();
}
Yes there is a alternative that you can Try.
You can morph into a QFrame which will keep the behavior But make the container boundaryless
You can simply right click on the Group Box in the QDesigner and Select the 'Morph Into' option to select from

How to get minimize/restore/close rectangle?

I want to draw some text in the end of menu bar like this
But when the child window is maximized in the MDI project it looks like this
I need to fix this output.
I want to check if the active child is maximized and if it is I want to get minimize/restore/close rectangle to get them total width.
How can I get active child window and how can I get it's rectangle of buttons?
// in yourMainWindow.cpp
...
auto child = mdiArea->addSubWindow(yourWidget);
connect(child, &QMdiSubWindow::windowStateChanged, this, &yourMainWindow::yourSlot);
...
void yourMainWindow::yourSlot(Qt::WindowStates oldState, Qt::WindowStates newState)
{
if (newState.testFlag(Qt::WindowMaximized)) {
auto child = qobject_cast<QMdiSubWindow *>(sender());
if (!child)
return;
QStyleOptionComplex opt;
opt.initFrom(child);
auto size = child->style()->sizeFromContents(QStyle::CT_MdiControls, &opt, QSize(100, 20));
qDebug() << size;
}
}

Hide QLineEdit blinking cursor

I am working on QT v5.2
I need to hide the blinking cursor (caret) of QLineEdit permanently.
But at the same time, I want the QLineEdit to be editable (so readOnly and/or setting editable false is not an option for me).
I am already changing the Background color of the QLineEdit when it is in focus, so I will know which QLineEdit widget is getting edited.
For my requirement, cursor (the blinking text cursor) display should not be there.
I have tried styleSheets, but I can't get the cursor hidden ( {color:transparent; text-shadow:0px 0px 0px black;} )
Can someone please let me know how can I achieve this?
There is no standard way to do that, but you can use setReadOnly method which hides the cursor. When you call this method it disables processing of keys so you'll need to force it.
Inherit from QLineEdit and reimplement keyPressEvent.
LineEdit::LineEdit(QWidget* parent)
: QLineEdit(parent)
{
setReadOnly(true);
}
void LineEdit::keyPressEvent(QKeyEvent* e)
{
setReadOnly(false);
__super::keyPressEvent(e);
setReadOnly(true);
}
As a workaround you can create a single line QTextEdit and set the width of the cursor to zero by setCursorWidth.
For a single line QTextEdit you should subclass QTextEdit and do the following:
Disable word wrap.
Disable the scroll bars (AlwaysOff).
setTabChangesFocus(true).
Set the sizePolicy to (QSizePolicy::Expanding, QSizePolicy::Fixed)
Reimplement keyPressEvent() to ignore the event when Enter/Return is hit
Reimplement sizeHint to return size depending on the font.
The implementation is :
#include <QTextEdit>
#include <QKeyEvent>
#include <QStyleOption>
#include <QApplication>
class TextEdit : public QTextEdit
{
public:
TextEdit()
{
setTabChangesFocus(true);
setWordWrapMode(QTextOption::NoWrap);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setFixedHeight(sizeHint().height());
}
void keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
event->ignore();
else
QTextEdit::keyPressEvent(event);
}
QSize sizeHint() const
{
QFontMetrics fm(font());
int h = qMax(fm.height(), 14) + 4;
int w = fm.width(QLatin1Char('x')) * 17 + 4;
QStyleOptionFrameV2 opt;
opt.initFrom(this);
return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
expandedTo(QApplication::globalStrut()), this));
}
};
Now you can create an instance of TextEdit and set the cursor width to zero :
textEdit->setCursorWidth(0);
Most straight forward thing I found was stolen from this github repo:
https://github.com/igogo/qt5noblink/blob/master/qt5noblink.cpp
Basically you just want to disable the internal "blink timer" Qt thinks is somehow good UX (hint blinking cursors never were good UX and never will be - maybe try color or highlighting there eh design peeps).
So the code is pretty simple:
from PyQt5 import QtGui
app = QtGui.QApplication.instance()
app.setCursorFlashTime(0)
voilĂ .
Solution in python:
# somelibraries
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.setFocus() # this is what you need!!!
container = QWidget()
container.setLayout(self.layout)
# Set the central widget of the Window.
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
I ran into the same problem but setReadOnly is not a viable option because it alters the UI behavior in other places too.
Somewhere in a Qt-forum I found the following solution that actually solves the problem exactly where it occurs without having impact on other parts.
In the first step you need to derive from QProxyStyle and overwrite the pixelMetric member function:
class CustomLineEditProxyStyle : public QProxyStyle
{
public:
virtual int pixelMetric(PixelMetric metric, const QStyleOption* option = 0, const QWidget* widget = 0) const
{
if (metric == QStyle::PM_TextCursorWidth)
return 0;
return QProxyStyle::pixelMetric(metric, option, widget);
}
};
The custom function simply handles QStyle::PM_TextCursorWidth and forwards otherwise.
In your custom LineEdit class constructor you can then use the new Style like this:
m_pCustomLineEditStyle = new CustomLineEditProxyStyle();
setStyle(m_pCustomLineEditStyle);
And don't forget to delete it in the destructor since the ownership of the style is not transferred (see documentation). You can, of course, hand the style form outside to your LineEdit instance if you wish.
Don't get complicated:
In QtDesigner ,
1.Go the the lineEdit 's property tab
2.Change focusPolicy to ClickFocus
That's it...

QGraphicsItem doesn't detect its own boundingRect change

// QT 4.8
I've implemented a resizable QGraphicsItem, which works pretty fine.
However, now, I need to include child items in this resizable item.
Child items size/position will obviously depend on parent one.
My problem is to notify childs when parent one is resized.
But so far, the parent itself doesn't detect its own size change (whereas on the scene, everything behave as expected!)
In the parent item (= the resizable one) item I've tried to override
QVariant itemChange(QGraphicsItemChange, QVariant)
in order to dump change events. I can see all events, but itemChange method never get called when item is (being) resized.
Any help greatly appreciated.
NB:
In my resizable QGraphicsItem constructor, I've enabled the flag QGraphicsItem::ItemSendGeometryChange
ResizableGraphicsItem::ResizableGraphicsItem(QGraphicsItem *p_parent,
qreal p_initialWidth,
qreal p_initialHeight)
: QGraphicsItem(p_parent), _width(p_initialWidth),
_height(p_initialHeight), _resized(true)
{
setFlag(ItemSendsGeometryChanges);
...
}
The resize method looks like the following:
void ResizableGraphicsItem::setSize(qreal p_width, qreal p_height)
{
prepareGeometryChange();
if ((_width != p_width) ||
(_height != p_height))
{
_width = p_width;
_height = p_height;
_resized = true;
}
}
with:
QRectF ResizableGraphicsItem::boundingRect() const
{
return QRectF(0, 0, _width, _height);
}

Qt menu without shadow?

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 :)

Resources