I would like to have a QDialogButtonBox with three buttons in this particular order:
Ok | Apply | Cancel
Is it possible to reorder the buttons to put Apply in the center?
The button layout is platform specific.
Windows - Ok | Cancel | Apply
OS X - Apply | Cancel | Ok
KDE - Ok | Apply | Cancel
GNOME - Apply | Cancel | Ok
There is two way to force use non standard layout.
You can subclass QProxyStyle and reimplement styleHint method, to provide custom style for QStyle::SH_DialogButtonLayout styleHint.
class KdeStyle : public QProxyStyle
{
public:
virtual int styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidget *widget, QStyleHintReturn *returnData) const override
{
if (stylehint == SH_DialogButtonLayout)
return QDialogButtonBox::KdeLayout;
return QProxyStyle::styleHint(stylehint, opt, widget, returnData);
}
};
Then apply custom style to application.
qApp->setStyle(new KdeStyle());
Another way to do it, is using stylesheets. button-layout property specify the layout of buttons in a QDialogButtonBox or a QMessageBox. The possible values are 0 (WinLayout), 1 (MacLayout), 2 (KdeLayout), and 3 (GnomeLayout).
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel);
buttonBox->setStyleSheet("* { button-layout: 2 }");
Related
I subclassed a QTreeView and I have two columns where there are checkboxes. I would like to set two different images: one for the first column, and another one for the second column. I know I can change the image in the stylesheet with:
QTreeView::indicator:checked{
image: url(:/checked);
}
QTreeView::indicator:unchecked{
image: url(:/unchecked);
}
but it will change all the checkboxes in the tree view. Is there a way to do it with the stylesheets, or do I need to use a delegate?
Short answer: Stylesheets can't do that (as far as I know). They are a pretty immature feature in Qt, and there seems to be no development on them either.
What you can do:
Stylesheets
You cannot assign properties to a column or an item and you cannot access the columns by index.
But you can use some of the pseudo-states selectors like :first, :middle and :last:
QTreeView::indicator:first:checked{
background: red;
}
QTreeView::indicator:middle:checked{
background: blue;
}
QTreeView::indicator:unchecked{
background: lightgray;
}
I used colors instead of images for the sake of simplicity.
Note however, that these pseudo-states are actual currently visible states, so if the user is allowed to reorder columns, the style of the column might change. For example if the user drags one of the :middlecolumns and drops it at the end, the box will not be blue anymore.
DecorationRole
You can fake it using Qt::DecorationRole.
To do so, you have to receive the mousePressEvent either by subclassing QTreeView or by installing an event filter. You can then change the icon (via Qt::DecorationRole + emit dataChanged()) when the user clicks in the area of the icon.
This does not work with the keyboard of course.
Custom ItemDelegate
Subclass QStyledItemDelegate and override paint(), just as you suggested.
Custom Style
If you are creating a heavily styled application, you probably have to create a custom Style sooner or later. Stylesheets just don't support some features.
To do so, subclass QProxyStyle, override drawPrimitive and handle the drawing if QStyle::PE_IndicatorViewItemCheck is passed. You will also receive a QStyleOptionViewItem of which has some useful properties like checkState, features (contains QStyleOptionViewItem::HasCheckIndicator if there is a checkbox), and of course index so you can determine what kind of checkbox you want to draw.
Edit: Appendix
Example Using a Custom QStyledItemDelegate
void MyItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyledItemDelegate::paint(painter, option, index);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
drawCheckBox(painter, checkRect, opt.checkState, index);
}
void MyItemDelegate::drawCheckBox(QPainter * painter, const QRect & checkRect, Qt::CheckState checkState, const QModelIndex & index) const
{
if (checkState == Qt::Checked)
{
switch (index.column())
{
case 0:
painter->fillRect(checkRect, Qt::red);
break;
default:
painter->fillRect(checkRect, Qt::blue);
}
}
else
{
painter->fillRect(checkRect, Qt::lightGray);
}
}
This example is quick and easy. Simply paint over the checkbox drawn by QStyledItemDelegate. Requires you to fill the whole box however, otherwise the original will be visible.
You can try to use QStyledItemDelegate to draw anything but the checkbox, and draw the checkbox afterwards, but that is a little harder and will leave you with some minor drawing artifacts if you don't want to spend too much time on it.
Example Using a Custom QProxyStyle
void MyStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * w) const
{
if (pe == QStyle::PE_IndicatorViewItemCheck)
{
const QStyleOptionViewItem * o = static_cast<const QStyleOptionViewItem *>(opt);
drawCheckBox(p, opt->rect, o->checkState, o->index);
return;
}
QProxyStyle::drawPrimitive(pe, opt, p, w);
}
The drawCheckBox() function is the same as in the first example.
As you can see, this way is much simpler, cleaner and has none of the drawbacks. You can apply the style globally, or only for a single widget.
i want to achieve following layout behavior:
| A | B | | A | |
1) |---- -----| 2) |---- D |
| C | D | | C | |
Align A,B,C,D with each other and allow D to take available space if B is hidden.
I can achieve layout behaviors 1 or 2 with multiple ways. But i cannot seem to find solution to satisfy both of these conditions without removing widgets from layouts and re-adding later when B hides/shows with qt default layouts.
I tried so far:
1) grid layout - when B is hidden, D stays in place. Can align like i want if i will start to track hide-show status of widget B.
2) combinations of hbox and vbox layouts - D will get all space, after B is hidden, but when B is shown - A and B are never aligned. Once again i'll need to rearrange all widgets to achieve behavior i want.
I suppose Grid layout best suited for my purposes, but row span is set in stone when I add widgets.
Is there simple solution i miss?
You could try the following code:
#include <QMainWindow>
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow, public Ui::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setupUi(this);
QWidget *left = new QWidget;
QWidget *right = new QWidget;
QHBoxLayout *hbox = new QHBoxLayout;
QVBoxLayout *vboxLeft = new QVBoxLayout;
QVBoxLayout *vboxRight = new QVBoxLayout;
vboxLeft->addWidget(new QLabel("A"));
vboxLeft->addWidget(new QLabel("C"));
auto b = new QLabel("B");
vboxRight->addWidget(b);
vboxRight->addWidget(new QLabel("D"));
left->setLayout(vboxLeft);
right->setLayout(vboxRight);
hbox->addWidget(left);
hbox->addWidget(right);
centralWidget()->setLayout(hbox);
QAction *toggleB = new QAction("Toggle B");
toggleB->setCheckable(true);
connect(toggleB, &QAction::toggled, this, [=](bool toggled) {
b->setHidden(toggled);
});
mainToolBar->addAction(toggleB);
}
You can toggle as many times as you want, it will keep the layout clean:
I am developing an android game with webview layout. I would like to hide the navigation bar while the user is playing the game. I have found some solutions but when I touch the screen the navigation bar shows up.
What am I doing wrong? Here is my non-working solution:
int mUIFlag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
getWindow().getDecorView().setSystemUiVisibility(mUIFlag);
you need to make use of following piece of code to reset the flags again :
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
(new View.OnSystemUiVisibilityChangeListener() {
#Override
public void onSystemUiVisibilityChange(int visibility) {
// Note that system bars will only be "visible" if none of the
// LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
decorView.setSystemUiVisibility(mUIFlag);
} else {
// TODO: The system bars are NOT visible. Make any desired
// adjustments to your UI, such as hiding the action bar or
// other navigational controls.
}
}
});
also, read through this page to know how to handle your scenario
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...
I am using a QTreeWidget to display a tree of parent nodes with their leave
nodes. Each parent can have various leaf nodes but leaf nodes should have no
childs. The user should be able to move leaves between parents by dragging them
to the new position. To avoid leaves from being dropped on other leaves, I have
only set ItemIsDragEnabled on leaves while having ItemIsDropEnabled on
parent nodes. This works fine if the QTreeWidget is set to "SingleSelection".
However, if the SelectionMode is set to ExtendedSelection you are able to
select a leave and a parent node together and drop them both on a leaf: http://i.stack.imgur.com/Kil3y.jpg (Screenshot)
Here is the sample code:
QTreeWidget *tree = this->ui->treeWidget;
QTreeWidgetItem *item;
QTreeWidgetItem *child;
tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
tree->setDefaultDropAction(Qt::MoveAction);
tree->setDragEnabled(true);
tree->setAcceptDrops(true);
tree->setDropIndicatorShown(true);
// disable dropping of leaves as top level items
tree->invisibleRootItem()->setFlags( Qt::ItemIsSelectable |
Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
for (int i = 0; i < 2; i++) {
// create top level item
item = new QTreeWidgetItem();
item->setText(0, "parent");
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable
| Qt::ItemIsDropEnabled | Qt::ItemIsEnabled );
// add 3 child items
for (int j = 0; j < 3; j++) {
child = new QTreeWidgetItem();
child->setText(0, "child");
child->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable
| Qt::ItemIsDragEnabled | Qt::ItemIsEnabled );
item->addChild(child);
}
// add item to tree
tree->addTopLevelItem(item);
}
I googled quite a lot but could not come up with a solution. How can I keep child and parent nodes on their respective levels while using ExtendedSelection?
Do I have to
subclass QTreeWidget and override insertRows()? Is there any way to intercept
drag'n'drop actions on QTreeWidget so I could check if the action is ok? (If
there is a way to get this to work with QStandardItemModel/QTreeView I would be
happy too)
One easy workaround is to connect a little function to the signal itemSelectionChanged that removes all items from the selection that are not of the same type as the last selected item. Works just perfect for me (and for other programs like the Warcraft 3 trigger editor)