How to set icon at left of text in a QLIstWidgetItem? - qt

I have a QListWidget which is used in iconMode as the viewMode. When I set an QIcon and the text for a QListWidgetItem, icon is showed on top of the text. If I use the QlistWidget in listMode as the viewMode, icon is shown at the left side of the text. How to show the icon at the left side of the text when the QListWidget is in iconMode ?
I tried setTextAlignment(Qt::AlignRight) for QLIstWidgetItems. But it didn't work.

The decorationPosition property of QStyleOptionViewItem determines the position of the icon, so the solution is to modify those properties:
Override viewOptions() method of QListWidget:
#include <QtWidgets>
class ListWidget: public QListWidget
{
public:
using QListWidget::QListWidget;
protected:
QStyleOptionViewItem viewOptions() const override{
QStyleOptionViewItem option = QListWidget::viewOptions();
option.decorationPosition = QStyleOptionViewItem::Left;
return option;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ListWidget w;
w.setViewMode(QListView::IconMode);
for (QStyle::StandardPixmap sp: {
QStyle::SP_ArrowBack,
QStyle::SP_ArrowDown,
QStyle::SP_ArrowForward,
QStyle::SP_ArrowLeft,
QStyle::SP_ArrowRight,
QStyle::SP_ArrowUp})
{
QIcon icon = QApplication::style()->standardPixmap(sp);
QListWidgetItem *it = new QListWidgetItem("foo");
it->setIcon(icon);
w.addItem(it);
}
w.show();
return a.exec();
}
Override initStyleOption() method of QStyledItemDelegate
#include <QtWidgets>
class StyledItemDelegate: public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
protected:
void initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const override
{
QStyledItemDelegate::initStyleOption(option, index);
option->decorationPosition = QStyleOptionViewItem::Left;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListWidget w;
w.setViewMode(QListView::IconMode);
StyledItemDelegate *delegate = new StyledItemDelegate(&w);
w.setItemDelegate(delegate);
for (QStyle::StandardPixmap sp: {
QStyle::SP_ArrowBack,
QStyle::SP_ArrowDown,
QStyle::SP_ArrowForward,
QStyle::SP_ArrowLeft,
QStyle::SP_ArrowRight,
QStyle::SP_ArrowUp})
{
QIcon icon = QApplication::style()->standardPixmap(sp);
QListWidgetItem *it = new QListWidgetItem("foo");
it->setIcon(icon);
w.addItem(it);
}
w.show();
return a.exec();
}

Related

Virtual keyboard or onscreen keyboard for QtWidgets applications?

I'm going to deploy the qtvirtualkeyboard in my widget-based application like so:
#include <QtWidgets>
int main(int argc, char *argv[]) {
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QApplication app(argc, argv);
QMainWindow window;
QLineEdit input(&window);
input.move(250, 250);
window.show();
return app.exec();
}
But the only issue is that the virtual keyboard input panel hides the underlying widgets and cover them!
How should I achieve this?
Is there any document or solution for widgets-based applications?
you just need to add this line in main.cpp
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
and will work Virtual Keyboard in Qtwidgets))
Finally got the solution!
You just need to call QGuiApplication::inputMethod() to get the application-wide Qt input method and then call QInputMethod::keyboardRectangle() and QInputMethod::isVisible() to get input method properties then remain a calculation based on your widget position and keyboard coordinate, here is a full-working sample to share:
lineedit.h:
class LineEdit :public QLineEdit {
Q_OBJECT
public:
LineEdit(QWidget *parent = nullptr);
LineEdit(const QString&, QWidget *parent = nullptr);
protected:
bool event(QEvent*) override;
private:
bool _moved = false;
int _lastDiff = 0;
};
lineedit.cpp:
LineEdit::LineEdit(QWidget *parent) :QLineEdit(parent) {
setAttribute(Qt::WA_InputMethodEnabled, true);
setInputMethodHints(inputMethodHints() | Qt::InputMethodHint::ImhDigitsOnly);
}
LineEdit::LineEdit(const QString& txt, QWidget *parent) : QLineEdit(txt, parent) {
setAttribute(Qt::WA_InputMethodEnabled, true);
setInputMethodHints(inputMethodHints() | Qt::InputMethodHint::ImhDigitsOnly);
}
bool LineEdit::event(QEvent* e) {
const auto keyboard_rect = QGuiApplication::inputMethod()->keyboardRectangle();
const auto keyboard_visible = QGuiApplication::inputMethod()->isVisible();
const auto global_y = QWidget::mapToGlobal(rect().topLeft()).y() + height();
const auto k_global_y = keyboard_rect.topLeft().y();
const auto diff = k_global_y - global_y;
const auto need_to_move = diff < 0;
/* move main widget */
if (keyboard_visible && !_moved && need_to_move) {
_moved = true;
_lastDiff = diff;
const auto g = parentWidget()->frameGeometry();
parentWidget()->move(g.x(), g.y() - qAbs(_lastDiff));
}
/* roll back */
if (!keyboard_visible && _moved) {
_moved = false;
const auto g = parentWidget()->frameGeometry();
parentWidget()->move(g.x(), g.y() + qAbs(_lastDiff));
}
return QLineEdit::event(e);
}
main.cpp:
#include <QtWidgets>
#define W 1024
#define H 768
int main(int argc, char *argv[]) {
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QApplication app(argc, argv);
QMainWindow window(nullptr, Qt::FramelessWindowHint);
LineEdit lineedit1(&window);
lineedit1.move(100, 450);
LineEdit lineedit2(&window);
lineedit2.move(100, 100);
window.resize(W, H);
window.show();
return app.exec();
}
results:

How to remove QTreeView indentation

I want to have a QTreeView without an indentation on the left side increasing at each nesting level. I tried setting QTreeView::setIndentation(0). It removes the indentations just as I want, however it also hides the tree arrows.
Default behavior:
With indentations ✗
With arrows ✔
After setIndentation(0):
Without indentations ✔
Without arrows ✗
Desired behavior:
Without indentations ✔
With arrows ✔
So how can I achieve the result shown in the third example? Is there any standard way of doing it, or I will have to reimplement the QTreeView::paintEvent(), QTreeView::drawBranches(), etc.?
To solve the problem I used a delegate to translate the paint of the items, and paint the arrows.
#include <QtWidgets>
class BranchDelegate: public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override{
QStyleOptionViewItem opt(option);
if(index.column() == 0)
opt.rect.adjust(opt.rect.height(), 0, 0, 0);
QStyledItemDelegate::paint(painter, opt, index);
if(index.column() == 0){
QStyleOptionViewItem branch;
branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());
branch.state = option.state;
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_IndicatorBranch, &branch, painter, widget);
}
}
};
class TreeView: public QTreeView
{
public:
TreeView(QWidget *parent=nullptr):QTreeView(parent)
{
BranchDelegate *delegate = new BranchDelegate(this);
setItemDelegate(delegate);
setIndentation(0);
}
protected:
void mousePressEvent(QMouseEvent *event) override{
QModelIndex index = indexAt(event->pos());
bool last_state = isExpanded(index);
QTreeView::mousePressEvent(event);
if(index.isValid() && last_state == isExpanded(index))
setExpanded(index, !last_state);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TreeView w;
QFileSystemModel model;
model.setRootPath(QDir::rootPath());
w.setModel(&model);
w.setRootIndex(model.index(QDir::homePath()));
/*for (int i = 1; i< model.columnCount() ; ++i) {
w.hideColumn(i);
}*/
w.expandAll();
w.resize(640, 480);
w.show();
return a.exec();
}
eyllanesc's falls appart if there is horizontal scrolling. Also usually the view only expands/collapses when clicking the branch-indicator, not the index.
My Solution: Only change the Rect of indices which have a parent but no children. Also dont set the indentation to 0. No need to subclass QTreeView.
#include <QtWidgets>
class BranchDelegate: public QStyledItemDelegate
{
public:
mIndent = 50;
using QStyledItemDelegate::QStyledItemDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt(option);
if(index.parent().isValid && (!index.model() || !index.model()->index(0, 0, index).isValid()))
{
opt.rect.adjust(-mIndent, 0, 0, 0);
}
QStyledItemDelegate::paint(painter, opt, index);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTreeView apView;
BranchDelegate* const apDelegate = new BranchDelegate(apView);
apDelegate->mIndent = 50;
apView->setIndentation(apDelegate->mIndent);
apView->setItemDelegateForColumn(0, apDelegate);
QFileSystemModel model;
model.setRootPath(QDir::rootPath());
apView.setModel(&model);
apView.setRootIndex(model.index(QDir::homePath()));
/*for (int i = 1; i< model.columnCount() ; ++i) {
apView.hideColumn(i);
}*/
apView.expandAll();
apView.resize(640, 480);
apView.show();
return a.exec();
}
ellyanesc's answer works, but is incorrect by one small detail in the line:
branch.rect = QRect(0, opt.rect.y(), opt.rect.height(), opt.rect.height());
The reason is because when the view is horizontally scrolled, option.rect.x() becomes negative. If branch.rect.x() is 0 (as in ellyanesc's answer), the branch indicator will always be shown, which also causes artifacts during scroll:
https://i.stack.imgur.com/O518A.png
https://i.stack.imgur.com/qYJMj.png
To solve this, replace the above line with:
branch.rect = QRect(option.rect.x(), opt.rect.y(), opt.rect.height(), opt.rect.height());
(I would have just pointed that out as a comment in ellyanesc's answer, but I don't have enough reputation for that.)

how to clickable a QGraphicsTextItem?

I have a QDialog for beginning of my game.in this class I have a QGraphicsTextItem. I want to it is clickable. When user clicked play game start. I do this but not work.
class Mydialog_start:public QDialog
{
Q_OBJECT
public:
explicit Mydialog_start(QWidget *parent = 0);
signals:
public slots:
void on_play_clicked();
void on_exit_clicked();
private:
QGraphicsScene* scene;
QGraphicsView* view;
QPixmap image;
QBrush brush;
QGraphicsTextItem* text;
QFont font;
const int x_size;
const int y_size;
};
Mydialog_start::Mydialog_start(QWidget *parent) :
QDialog(parent),x_size(400),y_size(400)
{
scene=new QGraphicsScene(this);
view=new QGraphicsView(this);
view->setScene(scene);
scene->setSceneRect(0,0,x_size,y_size);
image.load(":picture/image/background.jpg");
image=image.scaled(x_size,y_size);
brush.setTexture(image);
scene->setBackgroundBrush(brush);
font.setBold(true);
font.setPointSize(40);
font.setItalic(true);
text=scene->addText("play",font);
text->setDefaultTextColor(QColor("red"));
text->setPos(100,300);
this->setFixedSize(400,400);
connect(text,SIGNAL(linkActivated(QString("play"))),this,SLOT(on_play_clicked()));
}
void Mydialog_start::on_play_clicked()
{
accept();
}
void Mydialog_start::on_exit_clicked()
{
reject();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
Mydialog_start dialog;
dialog.exec();
if( dialog.exec()==QDialog::Accepted)
{
w.show();
}
else
{
w.close();
}
}
Not quite sure whether you needed your text item to be "editable" - see Mitch's comment...
It seems that you need your item to be "clickable" - then all you need are some flags:
text->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);

Qt: How to draw a dummy line edit control

I have a QPainter, and a rectangle.
i'd like to draw a QLineEdit control, empty. Just to draw it, not to have a live control. How do I do that? I have tried QStyle::drawPrimitive to no avail. nothing gets drawn.
QStyleOption option1;
option1.init(contactsView); // contactView is the parent QListView
option1.rect = option.rect; // option.rect is the rectangle to be drawn on.
contactsView->style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option1, painter, contactsView);
Naturally, i'd like the drawn dummy to look native in Windows and OSX.
Your code is pretty close, but you would have to initialize the style from a fake QLineEdit. The following is based on QLineEdit::paintEvent and QLineEdit::initStyleOption.
#include <QtGui>
class FakeLineEditWidget : public QWidget {
public:
explicit FakeLineEditWidget(QWidget *parent = NULL) : QWidget(parent) {}
protected:
void paintEvent(QPaintEvent *) {
QPainter painter(this);
QLineEdit dummy;
QStyleOptionFrameV2 panel;
panel.initFrom(&dummy);
panel.rect = QRect(10, 10, 100, 30); // QFontMetric could provide height.
panel.lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
&panel,
&dummy);
panel.midLineWidth = 0;
panel.state |= QStyle::State_Sunken;
panel.features = QStyleOptionFrameV2::None;
style()->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, &painter, this);
}
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
FakeLineEditWidget w;
w.setFixedSize(300, 100);
w.show();
return app.exec();
}

Setting focus on QLineEdit while showing the QListView view

In Qt, there is a QCompleter class which provides auto-complete funtionanity.
I want to use QListView to finish the same thing. In the following code, When the QListView shows, QLineEdit will lose focus. How could I keep QLineEdit's focus?
1) mdict.h:
#include <QtGui/QWidget>
class QLineEdit;
class QListView;
class QModelIndex;
class mdict : public QWidget
{
Q_OBJECT
public:
mdict(QWidget *parent = 0);
~mdict() {}
private slots:
void on_textChanged(const QString &);
void completeText(const QModelIndex &);
private:
QLineEdit *mLineEdit;
QListView *mView;
};
2) mdict.cpp
#include <cassert>
#include <QtGui>
#include "mdict.h"
mdict::mdict(QWidget *parent) : QWidget(parent), mLineEdit(0), mView(0)
{
mLineEdit = new QLineEdit(this);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(mLineEdit);
layout->addStretch(100);
setLayout(layout);
QStringList stringList;
stringList << "m0" << "m1" << "m2";
QStringListModel *model = new QStringListModel(stringList);
mView = new QListView(this);
mView->setModel(model);
mView->setEditTriggers(QAbstractItemView::NoEditTriggers);
mView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mView->setSelectionBehavior(QAbstractItemView::SelectRows);
mView->setSelectionMode(QAbstractItemView::SingleSelection);
mView->setParent(0, Qt::Popup);
mView->setFocusPolicy(Qt::NoFocus);
mView->setFocusProxy(mLineEdit);
connect(mLineEdit, SIGNAL(textChanged(const QString&)),
this, SLOT(on_textChanged(const QString &)));
connect(mView, SIGNAL(activated(const QModelIndex &)),
this, SLOT(completeText(const QModelIndex &)));
connect(mView, SIGNAL(clicked(const QModelIndex &)),
this, SLOT(completeText(const QModelIndex &)));
}
void mdict::on_textChanged(const QString &text)
{
int lineEidtWidth = mLineEdit->width();
mView->setMinimumWidth(lineEidtWidth);
mView->setMaximumWidth(lineEidtWidth);
mView->setMaximumHeight(60);
QPoint p(0, mLineEdit->height());
int x = mLineEdit->mapToGlobal(p).x();
int y = mLineEdit->mapToGlobal(p).y();
mView->move(x, y);
mView->show();
}
void mdict::completeText(const QModelIndex &index)
{
mLineEdit->setText(index.data().toString());
mView->hide();
}
3) main.cpp
#include "mdict.h"
#include <QtGui>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
mdict w;
w.show();
return a.exec();
}
Use Qt::ToolTip instead of Qt::Popup, in this way:
mView->setParent(0, Qt::ToolTip);

Resources