QFileDialog component signals - qt

I am subclassing QFileDialog to try to get some custom behavior. I would like to connect to signals emitted by components of the dialog, e.g. the textEdited signal when the file name line edit is manually edited. I understand that QFileDialog emits some signals itself, but these do not cover the cases I would like to respond to.
I have two ways about this I can think of, but don't know how to implement. One is to somehow attain a reference to the component to connect to it's signal. The other would be something with event filters, but the event source is the dialog itself, so I don't know how to determine where mouse clicks or key presses occur.
Are either of these methods feasible? Or another way?

Here is one option (your first suggestion):
dialog = QFileDialog()
layout = dialog.layout()
# for i in range(layout.rowCount()):
# for j in range(layout.columnCount()):
# try:
# print i,j
# print layout.itemAtPosition(i,j).widget()
# except:
# pass
line_edit = layout.itemAtPosition(2,1).widget()
line_edit.setText('Hello Stack Overflow')
dialog.exec_()
This gives you access to the QLineEdit in the dialog, which has a bunch of signals you can connect to.
I've also included the code I used to find this widget. I just iterated over the widgets in the layout of the dialog and found the indices of the one I was after. So if you need access to anything else in the dialog, you should be able to find it pretty easily!
Downside to this method: If the layout changes in a future version of Qt, this will break. I suppose you could make the algorithm more robust by looking for widgets that are instances of QLineEdit, but there are always risks with hacky approaches like this!

Related

QT Creator: Trigger a Slot with Code?

I may have worked myself into a corner but this sounded to me like a good idea at the time.
I have been developing an interface that permits a user to modify settings of a robotic device, i.e. speed, directions, force, etc. with a very large series of options in the form of ComboBoxes. The problem is that there are about a thousand of these things, in sub categories. e.g. Speed category x1, x2, x3, Y1, y2, etc. So rather than create a thousand comboboxes in QT, I thought the good idea was to create one set of 50 (ish) and then provide a few button to switch between categories. So when the user selects speed QT, populates the comboboxes with the appropriate options, sets the style sheets and text for the labels etc. So it appears as though a dedicated page exists. Then if the user selects Direction, QT Writes the current index of each box to a dedicated array and then repopulates the boxes, labels etc with the appropriate content. I then do this over and over for the various needs of the system.
All of that seems to work fine. However I am now in a bind where the options provided to navigate to each page have grown. For instance I have forward / backward buttons (like you woudl expect in a set-up wizard), as well as action menus at the top to jump to a page. So now the code is becoming very repetitious. If you select the next button, I write the current values to array, then repopulate. If you jump to the page from anywhere, I look to see where I am, write it to array, and populate the boxes. Thus if I need to change anything I have to make the change in numerous places in the code.
I know that this is not optimal. What I woudl like to do is run a continuous loop as I woudl normally do with Micros in C. So the program can look at a variable in each pass and if it is then it does. I am not however skilled enough to figure this loop out in QT. So my new thought was...
Is it possible to trigger an action or slot with a variable. For example, if the user presses the Next button it triggers a slot for a button that does not exist, so that QT will execute a particular line of Code? Then I can have 1 dedicated section focused on reading and writing boxes, with a bunch of actions that will take me there.
You can make a signal that is triggered with an emit call in your code, so you'd hook up the next button signal of clicked to a slot that does some work and moves on, or directly calls another signal that you've created that triggers a slot elsewhere, or do some work in a lambda triggered by the button press.
I would first load all the ComboBoxes options in a QStringList array (or maybe an array of QList<QLatin1String> lists - for memory saving and code efficiency).
Then I would keep an array of a 1000 integers for current ComboBox indexes.
When the user changes a value in some ComboBox, the currentIndexChanged signal will trigger the corresponding slot (a single slot for all the ComboBoxes would be enough - sender()->objectName() to get the name of the ComboBox which had sent the signal):
void WindowWidget::on_ComboBox_currentIndexChanged(int index)
{
name = sender()->objectName();
/* here change the corresponding integer in the current
indexes array */
}
On Next/Back button push repopulate the ComboBoxes. Also, provide some 'Save' button for saving the ComboBoxes indexes (or trigger the Save slot on some action, i.e. on window close either even on a timer signal).

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.

QTabWidget: close tab button not working

I have set ui->tabWidget->setTabsClosable(true); but QTabwidget only showing a cross on each tab that is not closing tab on click on this button. What else I have to do to make tabs closable? I tried to connect any slot (which would be proper for this work) of close to signal tabCloseRequested(int) but couldn't find any such slot in tabwidget. Please suggest the right way.
Create a slot e.g. closeMyTab(int) and connect the tab widget's tabCloseRequested(int) signal to this slot. In this slot call tab widget's removeTab method with the index received from the signal.
See this answer for more details.
For future stumblers upon this question looking for a PyQt5 solution, this can be condensed into a 1-liner:
tabs = QTabWidget()
tabs.tabCloseRequested.connect(lambda index: tabs.removeTab(index))
The tabCloseRequested signal emits an integer equal to the index of the tab that emitted it, so you can just connect it to a lambda function that takes the index as its argument.
The only problem I could see with this is that connecting a lambda function to the slot prevents the object from getting garbage collected when the tab is deleted (see here).
EDIT (9/7/21): The lambda function isn't actually necessary since QTabWidget.removeTab takes an integer index as its sole argument by default, so the following will suffice (and avoids the garbage-collection issue):
tabs.tabCloseRequested.connect(tabs.removeTab)
The best way to do it since we got new connection syntax (Qt 5) is:
QTabWidget* tabWidet = new QTabWidget();
connect(tabWidget->tabBar(), &QTabBar::tabCloseRequested, tabWidget->tabBar(), &QTabBar::removeTab);
You just need to tell the tabWidget itself to close the requested tab index (the param passed to the slot) as this:
ui->tabWidget->removeTab(index);

QDoubleSpinBox: Stop emitting intermediate values

I am subclassing QDoubleSpinBox to add some features (like incrementing the value based on the location of the cursor in the lineedit) and to change some features I find annoying. One of the latter is that intermediate values are emitted: e.g. if you want to enter the value 323 it will emit 3 then 32 then finally 323. I'd like to set it to only emit on entry (i.e. only actually change value on entry).
Anyway, I can't figure out how to capture these intermediate edits. I overrode setValue to see if I could stop it there somehow, but it apparently isn't called (or at least my override isn't). I'm not sure how the value is actually getting set while editing in line edit.
More generally, the logic of this box escapes me. Is there some documentation that explains e.g. "if you type a digit into the lineedit then this series of routines is called... while if you hit the up arrow, this series of routines is called?"
In case it matters, I'm using PyQt5
EDIT: Here is another case in which having access to this is important. Say I want to implement an undo/redo structure for the box. The only way I can think of to get access to the changed values is to connect to the valueChanged signal. But if I'm subclassing the box it seems a little convoluted to listen for a signal rather than just watch the value change 'internally' Or am I missing something here?
You could use the following signal:
void QAbstractSpinBox::editingFinished() [signal]
This signal is emitted editing is finished. This happens when the spinbox loses focus and when enter is pressed.
based on the documentation of QAbstractSpinBox:
http://qt-project.org/doc/qt-5.1/qtwidgets/qabstractspinbox.html#editingFinished
There is nothing that combines the arrow based changes and the editingFinished changes.
My use case was to let the user enter the value without getting the signal on each new digit, while still making ↑, ↓, Page Up, Page Down keys and arrow buttons work as usual, emitting the signal on each activation.
QAbstractSpinBox::editingFinished signal doesn't provide this functionality: it's only ever emitted when focus is lost or Return/Enter is pressed.
What does work exactly as I need is the keyboardTracking property of QAbstractSpinBox. If it's true (the default), the spinbox emits valueChanged on each digit typed. If you set it to false, it behaves exactly as I described in the beginning of this answer.

Can QWidget::find find widgets from a different process?

The documentation for QWidget::winId states (among other things) "If a widget is non-native (alien) and winId is invoked on it, that widget will be provided a native handle."
I'm not sure what 'alien' means in that context, but I'm choosing to ignore it for now. :)
So assuming that my widget now has a valid native handle associated with it, can I then pass that native handle to another process and into QWidget::find and get a valid QWidget object back within that second process?
I probably don't need to do too much else to the widget in the second process other than show/hide it and attach it to a parent widget. (It is guaranteed to not be attached to any parent widgets in the first process and never visible in the context of the first process).
If all the above works:
How much control will the second process have over that widget?
Will the first process receive user input events as if it were attached
to the first process's UI, and will the first process be able to update the widget as normal?
James
Let's take a look at Qt sources.
QWidget *QWidget::find(WId id)
{
return QWidgetPrivate::mapper ? QWidgetPrivate::mapper->value(id, 0) : 0;
}
find() can find a widget only if mapper contains it. The mapper is a static QHash<WId, QWidget *> variable. Items are inserted in this hash only in the QWidgetPrivate::setWinId method.
So, if a widget with a WId was created in another process, you can't find it using QWidget::find. This function doesn't use any native OS functions to find widgets.
Also see general description of alien widgets in Qt documentation:
Introduced in Qt 4.4, alien widgets are widgets unknown to the
windowing system. They do not have a native window handle associated
with them. This feature significantly speeds up widget painting,
resizing, and removes flicker.

Resources