Dynamic Handling of Hundreds of UI elements
-
Ok so here is what I've got... doubleSpinBox - doubleSpinBox_312. I made them all read only, I only want them the to be edited from within 1 dialog. Ive made one of them clickable through an eventFilter:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); ui->doubleSpinBox->installEventFilter(this); QObjectList o_list = ui->doubleSpinBox->children(); for(int i = 0; i < o_list.length(); i++) { QLineEdit *cast = qobject_cast<QLineEdit*>(o_list[i]); if(cast) cast->installEventFilter(this); } }
and
bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if(event->type() == QEvent::MouseButtonPress) { Dialog mdialog; mdialog.setModal(true); mdialog.exec(); } return false; }
I am wondering if its possible to automate this part
ui->doubleSpinBox->installEventFilter(this); //~~~~~~~~^ QObjectList o_list = ui->doubleSpinBox->children(); // ~~~~~~~~^
and to make sure I can return a value to a function that corresponds to what spinbox has been selected so that the dialog can return the new value to the appropriate field.
I really like the parameters and the look that the spinbox with no arrows gives so that's how I arrived at this juncture. Perhaps this more a C++ question than a Qt question, in any event IDK cause I'm a n00b.
Qt 5
preemptive thank you
-
I can't really think of any way to do this unless you stored them all in a container so you could iterate over the container and apply the operations to all of them. I can't see a way of doing that without manually adding them to a container since you're using the UI designer, which defeats the point. If you created them in code, you could create them in a loop and store a pointer to them in a container or something.
Maybe there's a way to get a list of all child widgets that you can iterate over and test of they are the type of widget you're wanting. Not too sure about that. Maybe someone will have an answer.
-
@mrsurge
Hello,
For one you could make Qt do the casting for you:QList<QLineEdit *> list = ui->doubleSpinBox->children<QLineEdit *>(); foreach (QLineEdit * item, list) item->installEventFilter(this);
And then in your event filter you could connect the dialog's
accepted
signal to the line edit:bool MainWindow::eventFilter(QObject * obj, QEvent * event) { if(event->type() == QEvent::MouseButtonPress) { Dialog mdialog; QSignalMapper mapper(&mdialog); mapper.setMapping(qobject_cast<QWidget *>(obj)); QObject::connect(&mdialog, SIGNAL(accepted()), &mapper, SLOT(map())); QObject::connect(&mapper, SIGNAL(mapped(QWidget *)), this, SLOT(saveDataToLineEdit(QWidget *))); mdialog.exec(); } return false; } void MainWindow::saveDataToLineEdit(QWidget * widget) { QLineEdit * lineEdit = qobject_cast<QLineEdit *>(widget); Dialog * dialog = qobject_cast<Dialog *>(sender()->parent()); // ... Transfer data between the dialog and the line edit here ... }
Is this what you were asking for?
Kind regards.
EDIT:
I've moved the mapper to the stack, as it's not necessary to create it with new (since you're using a modal dialog). -
also be sure to check
http://www.qtcentre.org/threads/65146-Dynamic-Handling-of-Hundreds-of-UI-elements -
QList<QLineEdit *> list = ui->doubleSpinBox->children<QLineEdit *>(); foreach (QLineEdit * item, list) item->installEventFilter(this);
returns
**error: 'QLineEdit' does not refer to a value
QList<QLineEdit > list = ui->doubleSpinBox->children<QLineEdit >();in my compiler. Remember I may be doing something really stupid, I'm new at qt
-
@mrsurge
No, the problem is with my code, it happens sometimes when I don't double-check my examples. The proper function to use would beQObject::findChildren
. I usually use it similarly to this:QList<QLineEdit *> list = ui->doubleSpinBox->findChildren<QLineEdit *>(QString(), Qt::FindDirectChildrenOnly);
This should compile fine.
Kind regards.
-
Just apply the same principle:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QList<QSpinBox *> sbList = findChildren<QSpinBox *>(QString(), Qt::FindDirectChildrenOnly); foreach (QSpinBox * spinBox, sbList) { spinBox->installEventFilter(this); //< Do you need this?? QList<QLineEdit *> leList = spinBox->findChildren<QLineEdit *>(QString(), Qt::FindDirectChildrenOnly); foreach (QLineEdit * lineEdit, leList) lineEdit->installEventFilter(this); } }
-
@kshegunov
This worked :QList<QDoubleSpinBox *> sbList = ui->centralWidget->findChildren<QDoubleSpinBox *>(QString()); foreach (QDoubleSpinBox * spinBox, sbList) { //spinBox->installEventFilter(this); //< Do you need this??//<-no I don't QList<QLineEdit *> leList = spinBox->findChildren<QLineEdit *>(QString(), Qt::FindDirectChildrenOnly); foreach (QLineEdit * lineEdit, leList) lineEdit->installEventFilter(this); }
many thanks for steering me in the right direction.
now on to saving the item to the specific line item
-
@mrsurge
Yes, of course, I forgot we're in a main window. Anyway, I'm glad it worked. One note, though, if possible use theQObject::findChildren
withQt::FindDirectChildrenOnly
so Qt will not go about recursively looking for all. If your spin boxes are in layouts, but not inside other widgets, this should also work:QList<QDoubleSpinBox *> sbList = ui->centralWidget->findChildren<QDoubleSpinBox *>(QString(), Qt::FindDirectChildrenOnly);
Kind regards.
-
@mrsurge
I've provided some guidelines here.
If this is working,QList<QLineEdit *> leList = spinBox->findChildren<QLineEdit *>(QString(), Qt::FindDirectChildrenOnly);
then the line edit has a
QSpinBox
for a parent. You have the line edit that'd been clicked here:void MainWindow::saveDataToLineEdit(QWidget * widget) { QLineEdit * lineEdit = qobject_cast<QLineEdit *>(widget); // ... Then the line edit parent is ... QSpinBox * spinBox = qobject_cast<QSpinBox *>(lineEdit->parent()); // ... and so on ... }
-
@kshegunov said:
bool MainWindow::eventFilter(QObject * obj, QEvent * event)
{
if(event->type() == QEvent::MouseButtonPress)
{
Dialog mdialog;QSignalMapper mapper(&mdialog); mapper.setMapping(qobject_cast<QWidget *>(obj)); QObject::connect(&mdialog, SIGNAL(accepted()), &mapper, SLOT(map())); QObject::connect(&mapper, SIGNAL(mapped(QWidget *)), this, SLOT(saveDataToLineEdit(QWidget *))); mdialog.exec(); } return false;
}
my compiler says
mapper.setMapping(qobject_cast<QWidget *>(obj));
requires a second argument
mapper.setMapping(qobject_cast<QWidget *>(obj),0);
works but i have no idea what thats doing
-
@mrsurge
Yes, I forgot the sender. That should be:mapper.setMapping(&mdialog, qobject_cast<QWidget *>(obj));
QSignalMapper is a simple utility class to provide mapping of signals without parameters to signals/slots with a single parameter. This is done, because
QDialog::accepted
has no notion of theQLineEdit
that triggers the event filter, so what the line does is simply to remap the signal to a slot that accepts theQLineEdit
object as a parameter.Kind regards.
-
@mrsurge
Hello,Ok I have a separate form, cpp, and .h for my dialog, does all this still apply? do i need to add anything to my dialog.cpp or .h?
It shouldn't matter how you initialize the dialog, provided you call
QDialog::accept()
at the appropriate place, i.e. when a save/ok button is clicked.Kind regards.