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);
Related
I'm using a custom QTableView with a custom QAbstractTableModel and a QItemDelegate. I'd need to access the contents of the delegate's editor while the user is editing it, and after several attempts, I couldn't find anything satisfying.
Indeed, I've tried several things.
First: trying to access the delegate's current input (created through createEditor) through a property defined in QItemDelegate but... it seems that none exists. That's why I tried to add a QWidget* editor property and setting it in the createEditor.
Unfortunately, QItemDelegate's createEditor is supposed to be const, which makes me unable to set my property there (and since I don't control what calls createEditor, I can't do it before or after).
I don't really know what to do here. Actually, I also needed to know when the user started (or stopped) editing the cell content, which I eventually achieved by creating two const signals (editingStarted and editingStopped). I could probably create a const editorOpened(QWidget*) signal but it just feels bad and ugly...
I can't believe nothing "official" exists to achieve what I'm trying to do, hence this question. If I have everything wrong from the beginning, I'd be glad to know. If you have any other ideas, please suggest.
EDIT: Here is a minimal working example
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include <QTableView>
#include "mytableview.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
auto tableView = new MyTableView(this);
setCentralWidget(tableView);
}
MainWindow::~MainWindow()
{
}
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QItemDelegate>
#include <QLineEdit>
#include <QStandardItemModel>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual void onCloseEditor();
virtual ~MyItemDelegate() = default;
signals:
// Const signals trick
void editingStarted() const;
void editingFinished() const;
void editorOpened(const QWidget*) const;
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
MyItemDelegate::MyItemDelegate(QObject* parent) : QItemDelegate(parent)
{
connect(this, &QItemDelegate::closeEditor, this, &MyItemDelegate::onCloseEditor);
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
emit editingStarted();
emit editorOpened(lineEdit);
return lineEdit;
}
void MyItemDelegate::onCloseEditor()
{
emit editingFinished();
}
MyTableView.h
#ifndef MYTABLEVIEW_H
#define MYTABLEVIEW_H
#include <QTableView>
#include <QDebug>
#include "myitemdelegate.h"
class MyTableView : public QTableView
{
Q_OBJECT
public:
explicit MyTableView(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // MYTABLEVIEW_H
MyTableView.cpp
#include "mytableview.h"
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::editingStarted, []() {
qDebug() << "Editing started";
});
connect(delegate, &MyItemDelegate::editingFinished, []() {
qDebug() << "Editing finished";
});
connect(delegate, &MyItemDelegate::editorOpened, [](const QWidget* editor) {
auto lineEdit = qobject_cast<const QLineEdit*>(editor);
connect(lineEdit, &QLineEdit::textChanged, [](const QString& text) {
qDebug() << text;
});
});
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
The following solution maybe fits your needs. I just defined a new signal inside the delegate and connected to it inside the class owning the delegate.
MyItemDelegate.h
#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H
#include <QStyledItemDelegate>
class MyItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
MyItemDelegate(QObject* parent);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
virtual ~MyItemDelegate() = default;
signals:
void valueChanged(const QString&);
};
#endif // MYITEMDELEGATE_H
MyItemDelegate.cpp
#include "myitemdelegate.h"
#include <QLineEdit>
MyItemDelegate::MyItemDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}
QWidget* MyItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto lineEdit = new QLineEdit(parent);
connect(lineEdit, &QLineEdit::textChanged, this, &MyItemDelegate::valueChanged);
return lineEdit;
}
MyTableView.cpp
#include "mytableview.h"
#include <QStandardItemModel>
MyTableView::MyTableView(QWidget *parent) : QTableView(parent)
{
MyItemDelegate* delegate = new MyItemDelegate(this);
QStandardItemModel* model = new QStandardItemModel(this);
setItemDelegate(delegate);
setModel(model);
QList<QList<QStandardItem*>> items;
for(int i = 0; i < 10; i++)
{
items << QList<QStandardItem*>();
for (int j = 'A'; j < 'E'; j++)
items[i] << new QStandardItem(QString("%1,%2").arg(i).arg(static_cast<char>(j)));
}
for (const auto& row : items)
model->appendRow(row);
connect(delegate, &MyItemDelegate::valueChanged, [](auto v) { qDebug() << v; });
}
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();
}
I am trying to implement auto-completion for QLineEdit.
Here see my code:
#ifndef COMPLETER_H
#define COMPLETER_H
#include <QCompleter>
#include <QString>
#include <QStringList>
#include <QLineEdit>
class Completer:public QCompleter
{
Q_OBJECT
public:
explicit Completer(QStringList stringList, QObject *parent=0);
virtual QString pathFromIndex(const QModelIndex &index)const;
virtual QStringList splitPath(const QString&)const;
public slots:
void onLineEditTextChanged() const;
private:
mutable int cursorPos_;
};
class ExpressionLineEdit: public QLineEdit
{
Q_OBJECT
public:
explicit ExpressionLineEdit(QWidget *parent=0);
private:
QStringList stringList;
Completer *completer_;
};
#endif // COMPLETER_H
#include <completer.h>
#include <QDebug>
Completer::Completer(QStringList stringList, QObject *parent)
: QCompleter(stringList,parent)
, cursorPos_(-1)
{
}
ExpressionLineEdit::ExpressionLineEdit(QWidget* parent)
: QLineEdit(parent)
{
stringList << "minRoute" << "minPitch" << "minSpacing";
completer_ = new Completer(stringList, this);
setCompleter(completer_);
QObject::connect(this, SIGNAL(textChanged(const QString&)),
completer_, SLOT(onLineEditTextChanged()));
QObject::connect(this, SIGNAL(cursorPositionChanged(int, int)),
completer_, SLOT(onLineEditTextChanged()));
}
QString Completer::pathFromIndex(const QModelIndex &index) const
{
QString newStr = index.data(Qt::EditRole).toString();
ExpressionLineEdit *lineEdit = qobject_cast<ExpressionLineEdit*>(parent());
QString str = lineEdit->text();
int prevSpacePos = str.mid(0, lineEdit->cursorPosition()).lastIndexOf(' ');
int curPos = lineEdit->cursorPosition();
int nextSpacePos = str.indexOf(' ', curPos);
if (nextSpacePos == -1) {
nextSpacePos = str.size();
}
QString part1 = str.mid(0, prevSpacePos + 1);
QString pre = str.mid(prevSpacePos + 1, curPos - prevSpacePos - 1);
QString post = str.mid(curPos, nextSpacePos - curPos);
QString part2 = str.mid(nextSpacePos);
onLineEditTextChanged();
cursorPos_ = curPos + newStr.size() - pre.size();
return part1 + newStr + part2;
}
void Completer::onLineEditTextChanged() const
{
qDebug() << "Completer::onLineEditTextChanged()" << cursorPos_;
if (cursorPos_ != -1) {
ExpressionLineEdit *lineEdit = qobject_cast<ExpressionLineEdit*>(parent());
lineEdit->setCursorPosition(cursorPos_);
cursorPos_ = -1;
}
}
QStringList Completer::splitPath(const QString &path) const
{
cursorPos_ = -1;
ExpressionLineEdit *lineEdit = qobject_cast<ExpressionLineEdit*>(parent());
QString text = lineEdit->text();
QStringList stringList;
QString str;
int index = text.mid(0,lineEdit->cursorPosition()).lastIndexOf(' ');
str = text.mid(index, lineEdit->cursorPosition()-index);
str.trimmed();
str.replace(" ", "");
stringList << str;
onLineEditTextChanged();
return stringList;
}
#include <completer.h>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ExpressionLineEdit le;
le.show();
return a.exec();
}
For this code ExpressionLineEdit and Completer work properly (as expected).
But when I try to set the to QTreeView and QSyledItemDelegate, the the behavior is changed.
I tried to reimplement the eventFilter function of QSyledItemDelegate, but it does not word.
I tried to set ExpressionLineEdit to other QWidget and set the QWidget to QTreeView (for QSyledItemDelegate not to work with ExpressionLineEdit directly), but again it did not help.
Here is the code with QTreeView and QSyledItemDelegate.
#include <QTreeView>
#include <QVBoxLayout>
#include <QStandardItemModel>
#include <QStyledItemDelegate>
#include <Completer.h>
class QEvent;
class Delegate:public QStyledItemDelegate
{
public:
Delegate(QObject* parent = 0)
:QStyledItemDelegate(parent){}
bool eventFilter(QObject* editor,QEvent* event){return false;}
virtual bool editorEvent ( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex& index){return false;}
QWidget* createEditor(QWidget* parent,const QStyleOptionViewItem &option,const QModelindex &index) const
{
if(index.column() != 1) return 0;
QWidget* w = new QWidget(parent);
ExpressionLineEdit *le = new ExpressionLineEdit(w);
QVBoxLayout* lay = new QVBoxLayout(w);
lay->setContentsMargins(0, 0, 0, 0);
lay->addWidget(le);
w->setLayout(lay);
return w;
}
};
class Tree:public QTreeView
{
public:
Tree(QWidget* parent = 0)
:QTreeView(parent)
{
QStandardItemModel *model = new QStandardItemModel(this);
model->setRowCount(1);
model->setColumnCount(2);
QStandardItem *item = new QStandardItem("Item");
model->setItem(0,0,item);
setModel(model);
Delegate *d = new Delegate(this);
setItemDelegate(d);
}
void keyPressEvent ( QKeyEvent * event ){}
};
#include <QApplication>
#include <Completer.h>
#include <Tree.h>
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
Tree t;
t.show();
return app.exec();
}
Please help to understand why same code works differently for this 2 cases.
Please help to fix the behavior of Completer.
I'm new with Qt and having some playing-around with it.
I picked a sample code from "C GUI programming with Qt 4" and cannot find anything incomprehensive about the code but it doesn't run correctly:
** projectfile.pro
QT += core gui
TARGET = CustomDialog
TEMPLATE = app
SOURCES += main.cpp \
finddialog.cpp
HEADERS += \
finddialog.h
** dialog header:
#ifndef FINDDIALOG_H
#define FINDDIALOG_H
#include <QDialog>
class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;
class FindDialog : public QDialog
{
Q_OBJECT
public:
FindDialog(QWidget *parent = 0);
signals:
void findNext(const QString &str, Qt::CaseSensitivity cs);
void findPrevious(const QString &str, Qt::CaseSensitivity cs);
private slots:
void findClicked();
void enableFindButton(const QString &text);
private:
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
};
#endif // FINDDIALOG_H
** dialog cpp:
#include <QtGui>
#include "finddialog.h"
FindDialog::FindDialog(QWidget *parent)
: QDialog(parent)
{
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
caseCheckBox = new QCheckBox(tr("Match &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
findButton = new QPushButton(tr("&Find"));
findButton->setDefault(true);
connect(lineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(enableFindButton(const QString &)));
connect(findButton, SIGNAL(clicked()),
this, SLOT(findClicked()));
connect(closeButton, SIGNAL(clicked()),
this, SLOT(close()));
QHBoxLayout *topLeftLayout = new QHBoxLayout;
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox);
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
setWindowTitle(tr("Find"));
setFixedHeight(sizeHint().height());
}
void FindDialog::findClicked()
{
QString text = lineEdit->text();
Qt::CaseSensitivity cs =
caseCheckBox->isChecked() ? Qt::CaseSensitive
: Qt::CaseInsensitive;
if (backwardCheckBox->isChecked()) {
emit findPrevious(text, cs);
} else {
emit findNext(text, cs);
}
}
void FindDialog::enableFindButton(const QString &text)
{
findButton->setEnabled(!text.isEmpty());
}
** main.cpp:
#include <QtGui/QApplication>
#include "finddialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FindDialog w;
w.show();
return a.exec();
}
what's wrong here??
when I click run, no dialog is shown but this error:
You have not initialized closeButton. Add
closeButton = new QPushButton(tr("&Close"));
to your constructor (before connecting its signal).
I create a QTableview with a QStandardItemModel, after editing the QStandardItem
the type changed from unsigned int to int.
This behavior just happen to unsigned int and just while the user is editing it, other datatypes stay.
window.cpp
#include "window.h"
#include "ui_window.h"
#include <QTableView>
#include <QStandardItem>
#include <QDebug>
Window::Window(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Window)
{
ui->setupUi(this);
QTableView *tblview = new QTableView(this);
model = new QStandardItemModel(0,0);
tblview->setModel(model);
QStandardItem *data=new QStandardItem;
data->setEditable(true);
data->setData(QVariant((uint)1), Qt::DisplayRole);
model->setItem(0, 0, data);
tblview->show();
QModelIndex index = model->index( 0, 0, QModelIndex() );
tblview->setGeometry(0,0,200,200);
//result QVariant(uint, 1)
qDebug() << model->data(index);
connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(dataChanged(QStandardItem*)));
}
Window::~Window()
{
delete ui;
}
void Window::dataChanged(QStandardItem* stditem)
{
//result
//QVariant(int, 3)
//expected result
//QVariant(uint, 3)
qDebug() << model->data(stditem->index());
}
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QMainWindow>
#include <QStandardItem>
namespace Ui {
class Window;
}
class Window : public QMainWindow
{
Q_OBJECT
public:
explicit Window(QWidget *parent = 0);
~Window();
private:
Ui::Window *ui;
QStandardItemModel* model;
private slots:
void dataChanged(QStandardItem*);
};
#endif // WINDOW_H
The second qDebug() does not print nothing because you do not define the role. This will work:
qDebug() << stditem->data(Qt::DisplayRole);
Now concerning the conversion from an uint QVariant to an int after the edit. This is natural and can be explained as follows:
First you have a QVariant that is uint
QVariant v = QVariant((uint) 5)); // It is uint now...
After the edit, the model changes its value with the int value that is entered
v = QVariant(10); // Now v is not uint anymore but int
In order to avoid it you should subclass the QStandardItemModel, and reimplement the setData function. There you should explicitly cast the new value to uint.