Checking then Adding items to QCompleter model - qt

I am currently working on a code editor written in Qt,
I have managed to implement most of the features which I desire, i.e. auto completion and syntax highlighting but there is one problem which I can't figure out.
I have created a model for which the QCompleter uses, which is fine for things like html tags and c++ keywords such as if else etc.
But I would like to add variables to the completer as they are entered by the user.
So I created an event on the QTextEdit which will get the word (I know I need to check to make sure that it is a variable etc but I just want to get it working for now).
void TextEdit::checkWord()
{
//going to get the previous word and try to do something with it
QTextCursor tc = textCursor();
tc.movePosition(QTextCursor::PreviousWord);
tc.select(QTextCursor::WordUnderCursor);
QString word = tc.selectedText();
//check to see it is in the model
}
But now I want to work out how to check to see if that word is already in the QCompleters model and if it isn't how do I add it?
I have tried the following:
QAbstractItemModel *m = completer->model();
//dont know what to do with it now :(

You can check if word is in your QCompleter really by using
QAbstractItemModel *m = completer->model();
as you can see, method model() returns const pointer.
That is good for checking procedure, you can check like this:
bool matched = false;
QString etalon("second");
QStringListModel *strModel = qobject_cast<QStringListModel*>(completer.model());
if (strModel!=NULL)
foreach (QString str, strModel->stringList()) {
if (str == etalon)
{
matched = true;
break;
}
}
qDebug()<<matched;
But for your purposes, I recommend you to declare QStringListModel, and connect it to your completer, and then, all of operations you'll must do thru your model, according to Qt's principles of MVC programming (http://doc.qt.digia.com/qt/model-view-programming.html).
Your code can be like this:
// declaration
QCompleter completer;
QStringListModel completerModel;
// initialization
completer.setModel(&completerModel);
QStringList stringListForCompleter;
stringListForCompleter << "first" << "second" << "third";
completerModel.setStringList(stringListForCompleter);
// adding new word to your completer list
completerModel.setStringList(completerModel.stringList() << "New Word");
Good luck!

Related

Update QMessageBox continuously within specific function in Qt

I have a program which when it runs, at first the user is asked to initialize the system. In that question form, there are 3 checkboxes that the user can check them for specific person or every persons and the system initializes the items related to that checkbox for the person(s).
When a checkbox is selected, a specific function and subsequently the specific class is called and initialization is done.
In the mainwindow.cpp I have:
InitializeDialog *id=new InitializeDialog;
connect(id,&InitializeDialog::initializeSignal,this,&MainWindow::initializeSlot);
id->exec();
id is the question form which has 3 checkboxes in it. And:
void MainWindow::initializeSlot(QStringList persons, bool interests, bool plots, bool graphs)
{
initializeMBox->setWindowTitle(tr("Initializing System")+"...");
initializeMBox->setText(tr("Please wait until initialization has been done") + ".<br>");
initializeMBox->show();
initializeMBox->setStandardButtons(0);
if (interests)//checkbox 1 is checked
initializeInterests(persons);
if (plots)//checkbox 2 is checked
initializePlots(persons);
if(graphs)//checkbox 3 is checked
initializeGraphs(persons);
initializeMBox->setStandardButtons(QMessageBox::Ok);
}
And again:
void MainWindow::initializeInterests(QStringList persons)
{
for(int p=0;p<persons_comboBox_->count();p++)
{
persons_comboBox_->setCurrentIndex(p);
if (persons.contains(persons_comboBox_->currentText()))
{
//..
//create a specific class object and some specific functions
//..
//*
initializeMBox->setText(initializeMBox->text() + "<div><img src=\":/tickIcon.png\" height=\"10\" width=\"10\">" + " " + tr("Interests analyzed for the persons") + ": " + persons_comboBox_->currentText() + ".</div>");
}
}
}
initializePlots and initializeGraphs are similiar to initializeInterests.
The problem starts from here:
I want to show a message after initialization for every person (as I mentioned by star in initializeInterests) but my initializeMBox (is a QMessageBox) does not show the message continuously and when all persons are initialized, all messages are shown suddenly. It should be noted that I see my initializeMBox is getting bigger but it seems that my QMessageBox is Freezed.
I can't use QtConcurrent::run because my QMessageBox is updated from mainwindow (and so from the base thread) by the line that I mentioned by star.
How can I have a QMessageBox which be updated continuously?
Don't reenter the event loop. Replace id->exec() with id->show(). Manage the dialog's lifetime - perhaps it shouldn't be dynamically created at all.
Don't block in initializeInterests. Instead of changing the combo box, get its data, send it out to an async job, set everything up there, then send the results back.
Pass containers by const reference, not value.
Don't create strings by concatenation.
If the input persons list is long, sort it to speed up look-ups.
For example:
class StringSignal : public QObject {
Q_OBJECT
public:
Q_SIGNAL void signal(const QString &);
};
void MainWindow::initializeInterests(const QStringList &personsIn) {
auto in = personsIn;
std::sort(in.begin(), in.end());
QStringList persons;
persons.reserve(in.size());
for (int i = 0; i < persons_comboBox_->count(); ++i) {
auto const combo = persons_comboBox->itemText(i);
if (std::binary_search(in.begin(), in.end(), combo))
persons << combo;
}
QtConcurrent::run([persons = std::move(persons), w = this](){
StringSignal source;
connect(&source, &StringSignal::signal, w, [w](const QString & person){
w->initalizeMBox->setText(
QStringLiteral("%1 <div><img src=...> %2: %3.</div>")
.arg(w->initalizeMBox->text())
.arg(tr("Interests analyzed for the person"))
.arg(person)
);
});
for (auto &person : persons) { // persons is const
// create a specific object etc.
QThread::sleep(1); // let's pretend we work hard here
source.signal(person);
}
});
}
The creation of the "specific objects" you allude to should not access anything in the gui thread. If it doesn't - pass a copy of the required data, or access it in a thread-safe manner. Sometimes it makes sense, instead of copying the data, move it into the worker, and then when the worker is done - move it back into the gui, by the way of a lambda.

How do I wait for a keypress in a Qt GUI application

I am writing a math practice program for my grandson. I first wrote it as a console app as below and everything worked fine.
for(int i = 0; i <= 9; i++)
{
n2Digit = GetNewDigit(iProblemsWorkedArray);
int validAnswer = add(n1Digit, n2Digit);
bool noKey = true;
while(noKey)
{
char KeyPressed = getch();
int KeyAscii = KeyPressed;
if((KeyAscii >= 48) && (KeyAscii <= 57) && (validAnswer < 10))
{
studentAnswer = KeyAscii - 48;
noKey = false;
}
yada yada...
}
}
Now I want to write the same thing using the Qt GUI, but found that the
char KeyPressed = getch();
within the while loop no longer works in GUI mode.
I have searched for days and come to the conclusion that I must be phrasing the search wrong. Would someone please help ?
My guess is that you're creating a list of questions with lettered or numbered answers, and you want the user (grandson) to press a key to answer it, rather than enter the answer into an edit field.
Assuming that's correct, you can do one of two things:
Subclass a widget where you display the questions, and reimplement the keyPressEvent method. Qt calls this method for the widget that gets the keyboard focus. It passes a QKeyEvent objects, which contains the key that was pressed. You can then examine the key to see if it's correct or not, and provide feedback. See the QWidget::keyPressEvent documentation for more information.
Without subclassing, you can create an event filter and install it on a standard widget. The event filter receives the events sent to the target widget, and you can intercept QKeyEvent events, inspect them for a correct or incorrect answer and respond accordingly. See the QObject::installEventFilter documentation for more information.

Qt - Dynamically create, read from and destroy widgets (QLineEdit)

I have the following situation:
I have QSpinBox where the user of my application can select how many instances of an item he wants to create. In a next step, he has to designate a name for each item. I wanted to solve this problem by dynamically creating a number of QLabels and QLineEdits corresponding to the number the user selected in the SpinBox. So, when the number is rising, I want to add new LineEdits, when the number falls, I want to remove the now obsolete LineEdits.
Well, guess what - this turns out much more difficult than I expected. I've searched the web, but the results were more than disappointing. There seems to be no easy way to dynamically create, maintain (maybe in a list?) and destroy those widgets. Can anybody point me in the right direction how to do this?
Take a while and check QListWidget, it does what you exactly want for you by using QListWidgetItem.
An little example: this function adds a new element to a QListWidgetwith a defined QWidget as view and return the current index:
QModelIndex MainWindow::addNewItem(QWidget* widget) {
QListWidgetItem* item = new QListWidgetItem;
ui->listWidget->addItem(item1);
ui->listWidget->setItemWidget(item, widget);
return ui->listWidget->indexFromItem(item);
}
Now, if your user selects X items, you should iterate to create X widgets and you could save all the widgets in a QList:
listWidget.clear();
for (int i=0; i<X; i++) {
QTextEdit* edit = new QTextEdit();
const QModelIndex& index = addNetItem(edit);
qDebug() << "New element: " << index;
listWidget.append(edit);
// Handle edit text event
connect(edit, SIGNAL(textChanged()), this, SLOT(yourCustomHandler()));
}
Now, just show the list with all the edit fields.

Is it possible to get primary key of selected text from QCompleter

I am using a QCompleter on line edit to get some text. The completer functionality is as such working fine.
The QCompleter is fetching data from Sql Table.
completer = new QCompleter(this);
model = new QSqlRelationalTableModel(this, db);
model->setTable("product");
model->select();
completer->setModel(model);
completer->setCompletionColumn(1); // points to "name" in product table
ui->line_edit->setCompleter(completer);
now on line_edit_returnPressed(), I am able to get the selected text. Is it further possible to get the primary key / row index in Sql Table for the currect selection made from "QCompleter" ?
I see that ui->line_edit->completer()->currentRow(); always return 0.
I am just trying to save one SQL query thats all.
I have to acknowledge #Pavel Strakhov comments, thanks. Had it been put up as answer, I would have accepted it.
The whole time I was using QCompleter::currentIndex with the sql table model I had set with QCompleter::setModel(). I dont know how QCompleter works but I believe it internally derives a list model from the input table model.
From documentation -
QAbstractItemModel* QCompleter::completionModel()
Returns the completion model. The completion model is a read-only list model that contains all the possible matches for the current completion prefix. The completion model is auto-updated to reflect the current completions.
So now my SLOT looks like this -
void MainWindow::on_line_edit_returnPressed()
{
QModelIndex index = ui->le_filter->completer()->currentIndex();
if (index.isValid()) {
int row = index.row();
int key = completer->completionModel()->index(row, 0).data().toInt();
qDebug() << key;
}
}

QCompleter and QListWidget as custom popup issue

I have a QCompleter using a QStringListModel for my QPlainTextEdit (check this example):
QStringListModel* model = new QStringListModel(names);
QCompleter* completer = new QCompleter(model);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setModelSorting(QCompleter::UnsortedModel);
It works fine. Now I need some Icon, Tooltips for each suggestion I'm trying to use a QListWidget as custom popup:
QListWidget* w = new QListWidget();
foreach(name, names) {
QListWidgetItem* i = new QListWidgetItem(name);
i->setIcon(/*my Icon*/);
i->setToolTip("");
w->addItem(i);
}
completer->setPopup(w);
The popup ok, just like I need, but the completion no more work. I cannot type the text to make it filter the suggestion, just Up/Down key.
I have try:
completer->setModel(w->model());
but no help!
What is my misstake or just QStringListModel give me the ability to filter the suggestions? What do you suggest?
Thanks you!
I mostly deal with PyQt, but same deal. My syntax may be off, but you should use a QStandardItemModel vs. a QStringListModel. From there, you can leave it as the standard popup (QListView)
Something like:
QStandardItemModel* model = new QStandardItemModel();
// initialize the model
int rows = names.count(); // assuming this is a QStringList
model->setRowCount(rows);
model->setColumnCount(1);
// load the items
int row = 0;
foreach(name, names) {
QStandardItem* item = new QStandardItem(name);
item->setIcon(QIcon(":some/icon.png");
item->setToolTip("some tool tip");
model->setItem(row, 0, item);
row++;
}
completer->setModel(model);
completer->popup()->setModel(model); // may or may not be needed

Resources