Case-insensitive auto-completion for QComboBox - c++

I have this simple program:
#include <QApplication>
#include <QComboBox>
#include <QLineEdit>
QStringList slNames={
"anna",
"andrew",
"ashley",
"brian",
"brianna",
"brandon",
"cameron",
"daniel",
"danna",
"devon",
"derek",
"emma"
};
int main(int argc,char *argv[]) {
QApplication appMain(argc,argv);
QComboBox cboNames;
cboNames.setEditable(true);
QObject::disconnect(
cboNames.lineEdit(),
&QLineEdit::editingFinished,
nullptr,
nullptr
); // Added (2023-01-25)
QObject::connect(
cboNames.lineEdit(),
&QLineEdit::textEdited,
[&](const QString &sText) {
//cboNames.lineEdit()->blockSignals(true); Removed (2023-01-25)
cboNames.clear();
cboNames.hidePopup();
if(sText.length()) {
for(const auto &n:slNames)
if(n.startsWith(sText,Qt::CaseInsensitive))
cboNames.addItem(n);
cboNames.showPopup();
cboNames.setEditText(sText);
cboNames.lineEdit()->grabKeyboard();
}
//cboNames.lineEdit()->blockSignals(false); Removed (2023-01-25)
}
);
cboNames.show();
return appMain.exec();
}
So, typing "d", for example, shows you 4 results, with autocompletion, etc.
But if you type "D" instead, the results are shown, but the autocompletion does not work anymore and just picks the full first result.
I know this has to do with QCompleter but I've not been able to make it work.
Any ideas?
EDIT (2023-01-25):
After struggling with QCompleter (works marvels for a standalone QLineEdit but could not find a way of making it work flawlessly for the QComboBox), I noticed that something was firing textEdited/textChanged, no matter the use of blockSignals().
So I used cboNames.lineEdit()->disconnect(); and that worked, but ultimately found that editingFinished was the culprit.
I've not found any disadvantages of this. It's a weird thing to do but it does the job in less lines of code.
Consider that this program is just a MRE. The actual list contents are dynamic.

Related

How do I pack a Gtk::Entry into a Gtk::HeaderBar so the entry completely fills the header bar?

I'm making a program in gtkmm-3.0 which has a Gtk::HeaderBar as the title bar.
I'm trying to pack a Gtk::Entry into it using this code:
Gtk::HeaderBar headerBar;
Gtk::Entry entry;
headerBar.set_hexpand();
headerBar.set_halign((Gtk::Align)GTK_ALIGN_FILL);
entry.set_hexpand();
entry.set_halign((Gtk::Align)GTK_ALIGN_FILL);
headerBar.pack_start(uriEntry);
headerBar.set_show_close_button();
The entry is correctly packed, but it only fills half the space of the header bar, which is very confusing. Using headerBar.add(entry) or headerBar.pack_end(entry) does not help the slightest (The entry still fills half the space it's supposed to take).
Also, using headerBar.pack_start() with a Gtk::Button before the headerBar.pack_start(entry) line will put the button in its place, but the entry will stop the expansion at the same point that it stopped before, being shorter than before.
How can I make the entry fill the whole header bar?
The problem is that Gtk::HeaderBar also has a "title" widget taking space. You could set a title, resulting in this:
An you see why only half the screen was given to the entry. One workaround is to define your own, custom, header bar. Here is an extremely minimal example:
#include <gtkmm.h>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow();
private:
Gtk::Box m_customHeaderBar;
Gtk::Entry m_entry;
};
MainWindow::MainWindow()
{
m_entry.set_hexpand_set(true);
m_entry.set_hexpand();
m_customHeaderBar.pack_start(m_entry);
set_titlebar(m_customHeaderBar);
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
MainWindow window;
window.show_all();
return app->run(window);
}
Which results in this:
Of course, you will have to add a close button and everything yourself (I would recommend making a class). I will leave this part to you.

Loading QStringList value received from signal slot

In my qt c++ application a QStringList is sent from one cpp file(MainWIndow) to another cpp file(Dialog) via signal and slots mechanism! I want to display the elements in the qtringList on a combo box in the Dialog.ui when the interface gets loaded(no button click)!
following is my code
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
for(int i=0;i<subMessages.size();i++){
ui->comboBox->addItem(subMessages[i]);
}
}
//slot which is used to get the qstringList
void Dialog::receiveSubMessages(QStringList List){
subMessages=List;
}
The QStringList is received successfully through the slot(already verified).Though I used a for loop and tried display (as in the code) nothing was displayed on the combo box! How can I fix this issue?
In order to get a working code you need to place your for llop inside you slot:
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
//slot which is used to get the qstringList
void Dialog::receiveSubMessages(QStringList List){
ui->comboBox->addItems (List);
}
If you want to fill the comboBox with the contents of some QStringList upon Dialog construction then you should either pass this list as constructor argument:
Dialog::Dialog(QStringList List, QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->comboBox->addItems (List);
}
or call Dialog::receiveSubMessages() right after Dialog object construction:
// somewhere in code, where you construct Dialog object
// ...
auto parent = SomeQWidgetDerivedClass (...);
QStringList someStringList {
"string 1",
"string 2"
};
// ...
auto dialog {new Dialog ()};
dialog->receiveSubMessages (someStringList);
// ...
The code that you have provided will never allow you to achieve the desired result because your for loop that is supposed to fill the QComboBox is executed on your Dialog object creation. At that point your subMessages is empty. The slot that you have created is not called before the constructor - the slot is just a member function that can only be called after the object is created. You can only call a member function without an object if the function itself is static, in which case it is definitely not a slot.
I did this answer rather to show you how to solve your problem. (I've the feeling that I even didn't understand what your actual problem is.)
When asking a question the chances to get a helpful answer increase if an MCVE is provided. (Please, follow this link. It teachs you really basic skills every S/W developer shouldmust have. I would also recommend to follow-up to How to debug small programs.)
As I did understand your problem I made such an MCVE. This is the code testQComboBox:
#include <QtWidgets>
int main(int argc, char **argv)
{
// build appl.
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// a QStringList with items
QStringList items;
items
<< QString::fromUtf8("first item")
<< QString::fromUtf8("second item")
<< QString::fromUtf8("third item");
// build GUI
QDialog dlg;
QVBoxLayout vBox;
QComboBox cmbBox;
cmbBox.addItems(items);
vBox.addWidget(&cmbBox);
dlg.setLayout(&vBox);
dlg.show();
app.exec();
// done
return 0;
}
I compiled it in VS2013 with Qt 5.9.2 on Windows 10 (64 bit). This is how it looks:
As you see, the usage of combobox is rather easy – no secret trap doors to use it. The actual code which is directly related to QComboBox is exactly 4 lines of code:
QVBoxLayout vBox;
QComboBox cmbBox;
cmbBox.addItems(items);
vBox.addWidget(&cmbBox);
And there is exactly one line of code where items are added to the QComboBox:
cmbBox.addItems(items);
Note:
I used QComboBox::addItems() instead of QComboBox::addItem() as the former has already a loop built-in to add a complete QStringList. It doesn't make any difference to the loop you used in your code Dialog::Dialog().
So, finally I dare to do the following statement:
If your combobox doesn't show items then:
You added items from an empty list.
Or, you forgot to add the items from list.
Or, something very weird happend.
I would always bet for 1. or 2. reason – the 3. reason is for real emergency cases only (e.g. broken Qt installation).
Concerning 3. reason:
I saw many questions where some lines of innocent looking code were presented which looked exactly as they should but were claimed to fail. And finally almost everytimes it showed that these lines worked fine when isolated in an MCVE but they didn't in the original code. How can this happen? Either there is some context which changes the behavior of the code in your original program or there is UB – undefined behavior. Something else does bad things but instead of crashing your process immediately (which would mean you're lucky) it goes on for a while corrupting the data more and more until finally everything breaks completely. Looking into the core-dump doesn't help at all. Therefore my recommendation of How to debug small programs.

Qt GUI Easiest way to access MainWindow from another class

I am doing a blackjack program and I am keeping track of the cards in the players hand in another class ("hand.h") than the main window class.
In the hand class, for every card that I collect, I am also creating a QLabel that grabs the proper card image for the card and also sets the coordinates for where the card should appear on the main window.
The problem is that I am not able to create the QLabel based on the MainWindows object that is originally created at the main function. Is there any easy way that I am able to get that information fairly easily? Thanks for your help!
I have tried using QGuiApplication::topLevelWindows(), but haven't came to luck with using that. Here is my function that I am using.
#include <QRect>
#include <QApplication>
#include <iostream>
#include <QLabel>
#include "mainwindow.h"
#include <QMainWindow>
#include <QWindowList>
#include <QWidgetList>
#include "ui_mainwindow.h"
void Test() {
QList<QWindow*> Main_Window = QGuiApplication::topLevelWindows();
for (int i = 0; i < Main_Window.size(); ++i) {
if(Main_Window.objectName() == "mainWindow") // name is OK
break;
}
QMainWindow* mainWindow = static_cast<QMainWindow*>(Main_Window);
QLabel* temp;
temp = new QLabel(Main_Window);
temp->setPixmap(QString("Ten of Clubs.png"));
temp->setGeometry(290, 300, 350, 390);
temp->show();
}
Here is the main.cpp file that creates the mainwindow
int main(int argc, char *argv[])
{
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I found the iterating code online and have been having issues from it.
I am having issues while trying to iterate through the list, but I have no idea how to identify the list and the error says that there is no objectName() function. Also, in the static cast line, there is an error that says that I cannot convert an QList to type QMainWindow. Any help would be greatly appreciated.
No way in general, because some applications may have several (toplevel) QMainWindow-s (and their list could change with time). So for that case you'll better pass the pointer to it (the particular QMainWindow you want to deal with) explicitly....
A possible way might be to have your specific subclass of QApplication (which is a singleton class, see QCoreApplication::instance to get its sole instance) and in your application subclass put, as fields, the explicit windows you want to deal with (maybe you even want to add some new signal or slot to your application class).
However, you could use QGuiApplication::topLevelWindows() or QGuiApplication::allWindows() to get the list of all such windows. Notice that a QWindowList is just a QList<QWindow *>. So see QList for how to traverse or iterate on that list.
Once you have found which QMainWindow you want, adding a QLabel into it is usual practice (but again, signals & slots could be helpful).
BTW, each (displayed) widget has its window, see QWidget::window()
About your code:
Your Main_Window is really poorly named (and the name is so confusing that I cannot use that). It is a list not a window. So code first:
QMainWindow* mainWindow = nullptr;
{
QList<QWindow*> topwinlist = QGuiApplication::topLevelWindows();
int nbtopwin = topwinlist.size();
for (int ix=0; ix<nbtopwin; ix++) {
QWindow*curwin = topwinlist.at(ix);
if (curwin->objectName() == "mainWindow")
mainWindow = dynamic_cast<QMainWindow*>(curwin);
}
}
I did not test the above code and I am not sure it is correct or even can compile. But why don't you just have a global pointer to your main window:
MainWindow*mymainwinp = nullptr;
and initialize it appropriately in your main body:
int main(int argc, char *argv[]) {
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
mymainwinp = &w;
w.show();
int r = a.exec();
mymainwinp = nullptr;
return r;
}
then use mymainwinp elsewhere (e.g. in your Test)? If you want more elegant code, define your own subclass of QApplication and have mymainwinp be a field in it.

Divide Gtkmm program into functions (part 1)

I know that this will seem a very basic question, but actually it is not something obvious because of the use of pointers, scopes and GTK especific types of variables and others. I really was not able to find an answer.
I have got to divide the GUI related part of a Gtkmm program into functions, but something seems to be wrong.
To make it clear, here is an example, There is the WORKING code in CODE1.cpp, and it must be divided into something similar to CODE2.cpp (not yet working).
The first one is a window containing only a label, the second is the same, but the label is created inside a function.
Where is the error? What is missing? Any tip or help would be appreciated.
Codes mentioned are the following:
CODE1.cpp:
#include <gtkmm.h>
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
Gtk::Label labela;
labela.set_text("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}
CODE2.cpp:
#include <gtkmm.h>
Gtk::Label etiqueta (string x)
{
Gtk::Label labela;
labela.set_text(x);
return ( labela );
}
int main (int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "Ejemplo");
Gtk::Window ventana;
etiqueta("perrito");
ventana.add (labela);
ventana.show_all ();
return app->run(ventana);
}
I guess your problem is that the Gtk::Label does not appear. That's because:
a) You are creating it as a local variable in the scope of the function and it is then released at the end of the function. Maybe you mean to use new (with Gtk::manage()) and return a Gtk::Label* instead of a Gtk::Label.
b) You don't use the return value from your function. There is no labela variable in your main() function.
I don't mean to sound harsh, but you need to read your compiler warnings and you need to read a beginner's C++ book. It's hard to learn C++ just by guessing or by hoping that it's like other languages such as Java.

Is it possible for QTestLib to display the GUI it is testing as it runs?

The use case is, I have a Qt app, and I would like to automate user-style testing of it; that is, I'd like to use keyClicks(), mouseClick(), and so on, but I would like for the Qt application window to actually be displayed while this is happening.
The issue I'm having right now is that using QTestLib involves using the QTEST_MAIN macro instead of defining main myself, so I never get an opportunity to show() the widgets being tested. So, another way to word this question is, is there a way to use QTestLib on an application that is using its main function?
I know Squish and probably Testability Driver are capable of this, but if it is possible to get this functionality without using extra tools, then that would be ideal.
Figured out how to do this. Makes Squish totally unnecessary, but it requires source code access.
In your test class, store a pointer to the QApplication and any widgets you want to test. For ease of use, store a pointer to your application's QMainWindow. Then, either instantiate your test class with pointers to the widgets you plan on testing, or use window->findChild() to grab any element you need. Keep in mind that you will need to call app->processEvents() after everything. Call it after showing the widget so that all of the child widgets appear. Call it after interacting with a widget so that the interaction is actually processed on the GUI. If you need things to be slow enough to watch, use QTest::qSleep().
#KelvinS. This is my snippet of code following #VGambit method, which tries to test adding a log to itemview.
#include <QApplication>
#include <QWidget>
#include <QtTest/QtTest>
#include "guimain.h"`
#include "xlogview.h"`
class TestLogView:public QObject
{
Q_OBJECT
public:
void set_mainwindow(QWidget * qw);
public slots:
void startTest();
private:
QWidget * m_qw ;
private slots:
void addItem();
};
void TestLogView::startTest()
{
QTest::qExec(this);
}
void TestLogView::set_mainwindow(QWidget * qw)
{
m_qw = qw;
}
void TestLogView::addItem()
{
XLogView * test_logview= m_qw->findChild<XLogView*>();
bool ret = test_logview->addLog("new log");
QVERIFY (ret == true);
}
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
GUIMain window;
window.show();
app.processEvents();
TestLogView test;
test.set_mainwindow(&window);
QTimer::singleShot(1000, &test, SLOT(startTest()));
return app.exec();
}