iterate through mdiarea subwindows - qt

I have a mdiArea. I add subwindows to this mdiArea every time an image is opened. The widget I set for each subwindow is imageFileDialog which inherits from the QDialog. Within this dialog I have a spinbox. I want to be able to set the value of this spinbox for every dialog I have in the mdiArea after they have been created. I do not know how to iterate through the dialogs. I tried to think of ways to do this.
for (int j=0; j < ui->mdiArea->subWindowList().size(); j++)
{
imageFileDialog *ifd = ui->mdiArea->subWindowList()[j]->widget();
ifd->setSpinBox(0);
}
but I have an error because I cannot cast the widget as my imageFileDialog class that inherits from QDialog.
I though I might be able to set a connection upon creation of my imageFileDialog:
imageFileDialog *ifd = new imageFileDialog();
ifd->connect(this, SIGNAL(emitImageFileValue(double)), SLOT(ifd->setSpinBox(double)));
subWindow1->setWidget(ifd);
but this is unlike any connection I've tried to make before. Everything compiles fine, but the signal emitted does not reach my slot. I'm hoping someone has tried this before and has some suggestions! Thanks in advance.

Use qobject_cast for the first problem and for the second issue you need to pass only the slot name to the SLOT() macro (without ifd->), or pass the ifd pointer as separate parameter to connect, something like:
QObject::connect(this, SIGNAL(emitImageFileValue(double)), idf, SLOT(setSpinBox(double)));

Related

How to automatically update a QLineEdit with the value of another variable

I've got an std::string object called str;
I've also got an QLineEdit object called line_edit.
I need that str reflects whatever the user writes in line_edit, this I know how to do trough signals and slots.
But I also need that whenever str changes , QLineEdit automatically displays the new contents of str.
How can I do this?
Is it MVC what I need? I've been reading about it but I can't get it yet.
Also, examples I've read try to keep mutually updated subobjects of QWidget
which makes me think there is some magic happening in there.
How can I achieve that reactivity?
First of all it might be easier to use QString instead of std::string.
You can call line_edit->setText(str); to update line_edit from your code.
You can use the signal QLineEdit::textChanged to modify the content of str when writing to the QLineEdit.
There are multiple ways of handling signals, one way would be to use QObject::connect
It might look like this: QObject::connect(line_edit, &QLineEdit::textChanged, this, &YourClassName::setText);
And then you create setText(){ str = line_edit->text}

Can I pass "this" to a Q_Slot?

I have an application with many windows (QWidgets).
I didn't save a list of open windows, though, so everytime I want to close a window, I have to retrieve it.
Particularly, each of these windows is called here SubWindow.
Every SubWindow class contains a layout with a MultiEditor *sEditors, which has a menu with an action that closes the current window.
Every SubWindow is created within the MainWindow.
I have two plans.
1) destroying the SubWindow from within itself, by adding in the SubWindow constructor
connect(sEditors, SIGNAL(closeWindow()),
this, closeWindow()));
or
2) destroying the SubWindow from within the MainWindow class, by adding in the SubWindow constructor
connect(sEditors, SIGNAL(closeWindow()),
main, SLOT(closeWindow(this)));
About 1), I don't understand how I can close and destroy a QWidget from within itself (delete this; didn't seem to work, but I can try again).
About 2) my SLOT(closeWindow(this)) doesn't seem to be triggered, so I am wondering if I can pass "this" as an argument.
Ad 1) You can use QObject::deleteLater(). This will destroy the object in the next event loop cycle, and is specifically create for situations like this
Ad 2) You cannot pass actual arguments as parameters in signal-slot connections.
You can however find out who has emitted the signal by using the sender() function in the slot. In your case, that would be the sEditors object.
Other options:
3) You can use a QSignalMapper to map signals from your editors to the Subwindows.
4) (Using Qt5 / C++11) You can use a lambda connection in your Subwindows:
connect(sEditors, SIGNAL(closeWindow()), [this] () {this->closeWindow();});
Can I pass this to a Qt slot?
A slot is a non-static method, so it already has access to this. The this you refer to is the third argument to QObject::connect. In Qt 4 syntax, you're free to omit the third argument - it defaults to this. In Qt 5 syntax, you must be explicit about it, though.
I don't understand how I can close and destroy a QWidget from within itself
To delete any QObject from within itself, use QObject::deleteLater(). Recall that a QWidget is-a QObject in terms of LSP.
my SLOT(closeWindow(this)) doesn't seem to be triggered
There's no such slot (give us a link to its documentation: you can't), and your slot signature is also invalid because the only things within the parentheses in the slot signature can be types, and this is not a type: SLOT(slotName(TYPE_LIST_HERE)), e.g. SLOT(mySlot(int,QString)).
To close a widget, use its close() slot:
connect(sEditors, SIGNAL(closeWindow()), this, SLOT(close());
Yet, by using Qt 4 connect syntax, you're leaving coding mistakes to be detected at runtime - and then if you don't pay attention to the debug output at runtime, you'll miss it. It's thus much better to use the new (Qt 5) connect syntax, and let the compiler detect errors for you:
connect(sEditors, &MultiEditor::closeWindow, this, &QWidget::close);
Alas, there's no need to tightly couple the object that sends closeWindow to SubWindow - at least not within the SubWindow::SubWindow(). Instead, you can connect at the place where you create the editor.
To delete a widget when it gets closed, simply set the Qt::WA_DeleteOnClose attribute on it and let Qt do it for you. No need to explicitly call deleteLater etc.
You might factor all of it into a factory method:
template <class T> T* SubWindow::makeEditor() {
auto sub = new T{this};
sub->setAttribute(Qt::WA_DeleteOnClose);
connect(sEditor, &MultiEditor::closeWindow, sub, &QWidget::close);
return sub;
}
MainWindow::MainWindow(/*...*/) : /*...*/ {
makeEditor<EditorType1>();
makeEditor<EditorType2>();
/*...*/
}

QT: How to close multiple windows of the same widget?

I'm a beginner in programming in C++/Qt.
I created a widget called wexample in Qt. When displayed, there is an button event that will open another window of the same widget wexample, and so on. My question is how to close all the windows of that widget?
I open my first widget as follows:
wexample *w = new wexample;
w-> show();
Inside the widget, I also have these events:
void wexample::on_pushButton1_clicked()
{
wexample *w = new wexample;
w -> show();
}
void wexample::on_pushButton2_clicked()
{
QWidget::close();
}
So when button 1 is pressed, it will open a new window and so on. When button 2 is pressed, it will close the window where the button is. Is there a way to close all of the windows from that widget all at once? Or even better, is there a way to close specific windows (for example, all the windows after the 3rd one)?
I have tried using signal and slot but I can't connect them since they are all of the same name. I would have to declare all of the widgets beforehand for it to work but I cannot know how many of them the user will need.
I'm sorry if my question isn't clear. I am really a beginner and have been searching for a while but couldn't find an answer. Maybe the structure as a whole doesn't make sense. Please help. Thanks.
You should add your newly created wexample's to a list and then iterate through them when you'd like to close them:
class wexample : public QDialog
{
...
private Q_SLOTS:
void on_pushButton1_clicked() {
wexample *w = new wexample(this);
m_wexamples.append(w);
w->show();
}
void wexample::on_pushButton2_clicked() {
foreach (wexample *w, m_wexamples)
w->close();
}
private:
QList<wexample*> m_wexamples;
};
A few extra points here:
Notice that I added (this) to the wexample constructor above. This ensures that the dialog is properly parented (and therefore will be cleaned up if you don't manually delete the object yourself). Previously, you would have been leaking memory every time you showed the dialog
The slot for the second button simply iterates through the list of dialogs and closes them. This does NOT mean that the dialogs have been deleted (and in fact are still in the list after closing them). If you'd prefer to fully remove them, then use of the "take" methods of QList, removing the dialog from the list and then call dialog->close(); dialog->deleteLater(); on it
This is a somewhat contrived example, you probably won't be opening the dialog from within the dialog's implementation in practice

Qt - signal for when QListWidget row is edited?

I am working in Qt4.7, and I have a QListWidget in my dialog. I have a QString that needs to match the current text in the row of this widget (the individual rows are editable). Looking at the signals associated with QListWidget, there seem to be signals for when a different index is selected but none for when the text of a the currently selected row changes. I thought currentTextChanged(QString) would do it, but it didn't. I also thought to try to connect each individual row to something, but QListWidgetItem doesn't have any built-in signals. Does anyone know of a way to do this? Thanks!
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, changing colors, checking boxes, and anything else that "changes" the item! Predelnik pointed that out in his answer. Some people have tried to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
I guess you need to look into the following signal:
void QListWidget::itemChanged(QListWidgetItem * item)
But be careful because it's being sent every time some property of item changed, not only text. I remember when we ran into the problem once when we changed item colors and got tons of false positive slots called because of that. If you need more fine tuning I guess it's better to write model/view classes yourself and not rely on QListWidget.

Is there a tidier way to connect many Qt widgets of different types to the same slot?

I am trying to make an options dialog that saves as much time as possible when applying the settings.
The widgets used are spread across 4 tabs and are a selection of group boxes, check boxes, radio buttons, text entry fields, spin counters and combo-boxes amongst possible others but these are most common.
I've got a boolean flag in each tab that I want changed to true if any single widget on it changed in some way. this would mean that when the apply method is invoked the dialog can check the each tab's flag to see if the tab has been altered and ignore it if it's unchanged.
A sample of my current solution follows, setModified() is the function that sets the flag:
connect(chkShowFormula, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(chkShowAxis, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(cmbAxisType, SIGNAL(currentIndexChanged(int)), this, SLOT(setModified()));
connect(cmbAxisType, SIGNAL(editTextChanged(QString)), this, SLOT(setModified()));
connect(cmbFormat, SIGNAL(currentIndexChanged(int)), this, SLOT(setModified()));
connect(grpShowLabels, SIGNAL(clicked(bool)), this, SLOT(setModified()));
connect(btnAxesFont, SIGNAL(clicked()), this, SLOT(setModified()));
connect(btnLabelFont, SIGNAL(clicked()), this, SLOT(setModified()));
Is there a tidier way to tie all these signals to the same slot? as this is but a fraction of the amount of signals I'm dealing with.
My other concern is that this method would fire almost constantly, so i'm also looking for another solution if possible.
For editable controls, the value that is edited (checkbox status, list item index, etc.) is called the user property. It is possible to extract the notification signal of such property programmatically, and thus to connect it to a slot:
QMetaMethod slot = this->metaObject()->method(this->metaObject()->indexOfSlot("setModified()"));
QList<QWidget*> widgets;
foreach (QWidget * widget, widgets) {
QMetaProperty prop = widget->metaObject()->userProperty();
if (!prop.isValid() || !prop.hasNotifySignal())
continue;
connect(widget, prop.notifySignal(), this, slot);
}

Resources