live changing as typing in QLineEdit - c++

I have a QlineEdit and a QTableView in a simple program.
I load a table (for example person) from SQLite to tableView.
I want an event or anything else that as I type in lineEdit the tableView change based on it.
For example if the the table person have a field name that filled by:
mehran
mehsa
mahid
naser
omid
I want when I press "m" all the name that started with "m", like mehran, mehsa, mahid show on the tableView. And when I press next key for example "e", just mehran and mehsa show on tableView, and so on.

You would need to do a connection like this based on this signal:
connect(lineEdit, &QLineEdit::textChanged, [=](const QString &string) {
QSqlQuery query(QString("SELECT %1 FROM ..").arg(string));
while (query.next()) {
QStringList stringList = query.value(0).toStringList();
updateTableView(stringList);
}
});
At this point in time, you will also need to add the following line in your qmake project file to get the new signal-slot syntax:
CONFIG += c++11

Related

How to use a custom validation function for a QCompleter in a QComboBox

I have a string matching function to be used for searching for names that is more advanced than QString::contains() (e. g. when you search for "mueller", it will match "Müller").
I'd like to use this function to search inside a QComboBox. The default completion almost does what I need: If I do
combobox->setEditable(true);
combobox->setInsertPolicy(QComboBox::NoInsert);
combobox->completer()->setCompletionMode(QCompleter::PopupCompletion);
and type some text in the QComboBox's lineedit, the popup pops up, only showing entries starting what has been typed.
This is what I want, but I would like the QCompleter to evaluate matches using my search function rather than the QString::startsWith() that is apparently used here (and setting the mode to Qt::MatchContains is better but still not sufficient).
Is there any way to customize the completer's search function?
Thanks for all help!
I ended up using an own QCompleter and set it for the QComboBox's QLineEdit. The completer does not use the combobox's model, but an own one, which is filled with data everytime the entered text changes.
Can be done as follows:
m_matchingNames = new QStringListModel(this);
m_nameCompleter = new QCompleter(m_matchingNames, this);
m_nameCompleter->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
m_playersSelect->setEditable(true);
m_playersSelect->setInsertPolicy(QComboBox::NoInsert);
m_playersSelect->setCompleter(0);
m_playersSelect->lineEdit()->setCompleter(m_nameCompleter);
connect(m_playersSelect->lineEdit(), &QLineEdit::textEdited, this, &ScorePage::nameSearchChanged);
and
void ScorePage::nameSearchChanged(const QString &text)
{
QStringList possibleNames;
for (const QString &name : m_availableNames) {
if (checkMatch(name, text)) {
possibleNames << name;
}
}
m_matchingNames->setStringList(possibleNames);
}
Most probably not the most prerformant solution, but it works :-)
One then can also connect to QCompleter::activated() to process what has been chosen from the list and e. g. do a QComboBox::setCurrentIndex() or such.

QSortFilterProxyModel how to handle QStandardItems correctly

I have QTreeView with some items and search QLineEdit with connected slot on textEdited signal.
With this code:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(messagesModel);
proxyModel->setFilterFixedString(text);
ui.treeView->setModel(proxyModel);
text filtering is ok, but when I clicked on QTreeView QStandardItems checkboxes (after proxy model assigned to QTreeView), I have the program crashes in slot, that connected to this QTreeView original model (before proxy was assigned).
What is the right way to processing item checkbox clicks? Need I use new connect/slot to processing model changes, or I can use the same code for original model with some changes? I just need to hide filtered items in QTreeView. In QTreeWidget is hide() method, does QTreeView has something like this, or QSortFilterProxyModel - is what I need? Thx!
UPD crashed in slot, connected to treeView:
auto item = messagesModel->itemFromIndex(index); // item is NULL because proxyModel is set for TreeView now
if(item->whatsThis().isEmpty()) return; // error below
#ifndef QT_NO_WHATSTHIS
inline QString whatsThis() const {
return qvariant_cast<QString>(data(Qt::WhatsThisRole));
}
inline void setWhatsThis(const QString &whatsThis);
#endif
because I set proxyModel to treeView, but messagesModel have whatsThis...
I changed my code with that:
QStandardItem* item;
if(ui.leFilter->text().isEmpty())
item = messagesModel->itemFromIndex(index);
else
item = messagesModel->itemFromIndex(proxyModel->mapToSource(index));
if(item->whatsThis().isEmpty()) return;
and it works. Is that correct way? Proxy model is member of my UI class ... not local.
UPD how can I update source model when checkbox checked in proxyModel?
UPD 2 I have load "original" model for QtreeView and show it. When I edit text in QListEdit, I use proxyModel (code from 1st post). When text edited, I have check checkboxes in QtreeView (now proxyModel is active) and at this step all is ok. But when I do some changes in UI, in QTreeView set the original model and it has no changes that was made for proxyModel. How can I notify and update items in source Model with new data from proxyModel?
UPD3 Yes, source model is also modified ... I have just clear it)

QDialog on accept return custom class object

I'm using qt-creator to build a little QT application.
I have a main window where I have some controls like a "new contact" button.
Pressing the button a QDialog is shown, it contains 3 line edits: name, mobile and email.
The dialog is shown through the Signal/Slot system. It works fine but I want to create a Contact object when OK is clicked and I want to give back that Contact to my main window in order to put it in a QList created in the main window code.
The approach is:
QMainWindow -> new contact -> QDialog is shown
QDialog -> ok -> QMainWindow
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
Should I pass the QList from the main window to the QDialog as argument or there is a best way?
In my opinion, best would be a custom QDialog subclass with three QLabels and 3 QLineEdits.
The labels would get the following type of values:
Label 1: name
Label 2: mobileNumber
Label 3: email
You would use then QLabels and QLineEdits to display them with the input coming from the user.
Then, as your program probably already does, just handle the "accept" event respectively. You could use the following method to retrieve the text entered by the end user:
text : QString
This property holds the line edit's text.
Setting this property clears the selection, clears the undo/redo history, moves the cursor to the end of the line and resets the modified property to false. The text is not validated when inserted with setText().
The text is truncated to maxLength() length.
By default, this property contains an empty string.
Then, in the handler of the accepted signal, you could call three accessor methods, like:
QString name() const { return nameLineEdit->text(); }
QString mobileNumber() const { return mobileNumberLineEdit->text(); }
QString email() const { return emailLineEdit->text(); }
You could also store that in a dedicated structure depending on you build up your data representation, so the structure would be something like this:
struct Contact {
QString name;
QString mobileNumber;
QString email;
};
and then you would have the accessor for that as follows
Contact contact() const;
Make a subclass of QDialog. Call Yourclass::exec() to show the dialog (exec is a function in QDialog), then afterwards Yourclass::contactDetails() to get them. contactDetails is a perfectly ordinary member function that you have to write.

Qt - Set display text of non-editable QComboBox

I would like to set the text of a QComboBox to some custom text (that is not in the QComboBox's list), without adding this text as an item of the QComboBox.
This behaviour is achievable on an editable QComboBox with QComboBox::setEditText(const QString & text).
On a non-editable QComboBox, however, this function does nothing.
Is it possible to programmatically set the display/edit text of a non-editable QComboBox to something that is not in its list?
Or do I have to find another way (e.g. use a QPushButton with a popup menu)
EDIT: Consider an editable QComboBox with InsertPolicy QComboBox::NoInsert. If the user types in something and hits enter, the entered value will be used but not added to the list. What I want is this behaviour to change the 'current' text programmatically, but without allowing the user to type in some text himself. The user can choose something from the QComboBox, but some time later, I may want to override the 'current' text.
I had the same problem when I subclassed QComboBox to make a combo box of check boxes. I wrote a small function to programmatically change the text displayed in the combo box, but I didn't want to enable the user to edit that text. The solution was to set the combo box as editable:
this->setEditable(true);
and the QComboBox::lineEdit() to read only. Refer to the function:
void CheckedComboBox::setText(QString text)
{
QLineEdit *displayedText = this->lineEdit();
displayedText->setText(text);
displayedText->setReadOnly(true);
}
Reimplement paintEvent : https://github.com/qt/qtbase/blob/28d1d19a526148845107b631612520a3524b402b/src/widgets/widgets/qcombobox.cpp#L2995
and add this line : opt.currentText = QString(tr("My Custom Text"));
Example :
QCustomCheckComboBoxFilter.h
...
protected:
void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE;
...
QCustomCheckComboBoxFilter.cpp
...
void QCustomCheckComboBoxFilter::paintEvent(QPaintEvent *)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
opt.currentText = QString(tr("My Custom Text"));
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
...
I supposed that you want to have a combo box with "A", "B", "C" as actual data and "This is A"
, "This is B" and "This is c" as what is displayed in QComboBox. Here is the code:
box.addItems(QStringList () << "This is A"<< "This is B"<< "This is C");
box.setItemData(0, "A");
box.setItemData(1, "B");
box.setItemData(2, "C");
You can get the actual data with this code :
QString actual = box.itemData(0).toString();//actual will be = "A";
qDebug()<<actual;//"A"
Note: You can almost set every data types that you want for a combo box Item. Even more, you can set more that just one additional data for each item with the third parameter of setItemData.
I ended up using a QPushButton with a popup menu.
I added the items I had in the list of my QComboBox as QActions to the menu.
A menu can be set on a QPushButton with
QPushButton::setMenu(QMenu* menu)
.
The text on the button can easily be set with
QPushButton::setText(const QString &)
and is unrelated to the text in the popup menu, which is what I wanted.

QTreeWidget editItem fails with "edit: editing failed"

I have a QTreeWidgetItem added to a QTreeWidget:
QTreeWidgetItem* item = new QTreeWidgetItem(ui->trwPairs);
item->setFlags(item->flags() | Qt::ItemIsEditable);
If the item is edited, I want to do a few checks on the new value:
Pairs::Pairs(QWidget *parent) :
QWidget(parent),
ui(new Ui::Pairs)
{
ui->setupUi(this);
connect(this->ui->trwPairs, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(Validate(QTreeWidgetItem*,int)));
}
void Pairs::Validate(QTreeWidgetItem* item, int column)
{
if (item->text(column).toInt() < 1)
{
QMessageBox::critical(this, "Error", QString("Node ID ") + item->text(column) + " is invalid.");
ui->trwPairs->editItem(item, column);
}
}
Naturally, if it's less than 1, it catches it, and gives me the message box. However, printed to cerr is edit: editing failed and the item is not in edit mode. What am I missing?
Stepping through it in the debugger reveals the following:
In quabstractitemview.cpp line false is returned on line 3953. Somehow it looks like your item is still in editing state and you are trying to edit it again or something.
bool QAbstractItemViewPrivate::shouldEdit(QAbstractItemView::EditTrigger trigger,
const QModelIndex &index) const
{
// ..
if (state == QAbstractItemView::EditingState)
return false;
}
IIRC I had a similar problem with tables with multiple lines per cell. Check out the classes QAbstractItemDelegate views have item delegates which allow you to control which editor is used and how it behaves. I believe by default the QLineEdit is used. Editors like QLineEdit can have validators which control how the data is validated, in your case reject it if the numerical value is < 0. But I think you have to use the model / view classes and implement your own model for that. The Qt documentation for QTreeWidget::setItemWidget(..) says:
This function should only be used to display static content in the place of a tree widget item. If you want to display custom dynamic content or implement a custom editor widget, use QTreeView and subclass QItemDelegate instead.
I am not sure however if there is a simpler way to do this using the widget classes.
The problem could be, that you are setting the flags for your items in a very strange way.
Simply enable both item-selection, and edit:
item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
I had a similar issue where I was attempting to edit the subsequent column upon receiving the itemChanged signal. Based on Nils' analysis that the item was still in the edit state, I changed the signal connection type to QueuedConnection, which allowed the item to leave the state before re-entering it.
I had a similar problem where I'd get the 'edit: editing failed' error when invoking edit() via a shortcut key. I was passing currentIndex() to edit(), but I wasn't checking that the correct column of the selected row was current. I only had the first column editable, so if I had clicked the row (but in any other column) and then invoked my edit key I'd get the error.
I was able to solve my problem by passing the result of sibling(currentIndex().row(), 0) to edit() instead.