A clean and intuitive way to implement a list of clickable strings to invoke functions? - qt

For the Qt App I'm writing, I'd like to have a list of clickable functions, which, when clicked will allow the user to supply required input arguments.
What I'm specifically looking for is a selection of widgets which provide a clean and intuitive interface for the following tasks:
User scrolls through a list of functions for performing computations (in my case, from glm).
Once a function is found, the user clicks on the item; a popup window opens, which specifies the required input arguments (e.g., vec3, vec4, etc.).
The idea here is that the functions themselves already exist: they just need an interface, which in a nutshell, provides a pseudo interpreter to process and output their results to a GLWidget, which will update the data passed accordingly by sending it to its corresponding shader.
I've looked at QListView, and its Widget variant, but it appears to be more suited towards filesystem data, such as images or text files, though I'm not quite sure. So far, it seems to be the only thing which could be considered as realistically usable in this scenario.
Is there a recommended way to do this? I'm fairly new to Qt in general, thus my knowledge is pretty limited.

The view isn't really important in your case. You need to create/reuse a adapted model.
This model have to contain the relation between what your view displays and the action that you want to launch.
For example, if your commands are text like bash commands, you can create a view that displays "list files", "Copy files" and a model that contains the data ("list files" = 'ls -l'), ("copy files" = 'ls -l'), etc.
You can store different data (using QVariant) in a same item with different roles: Qt::DisplayRole corresponds to the data that the view displays and Qt::UserRole what you want.
So, if you only have to store a command line associated to a name, you can store the name in the item with the Qt::DisplayRole and the command line as a QString (or other) using Qt::UserRole.
See QAbstractItemModel::data() and QAbstractItemModel::setData(), for more information.

Related

Why it uses d->eventFilters.prepend(obj) not append(obj) in function(QObject::installEventFilter)

Why it uses d->eventFilters.prepend(obj) not append(obj) in function(QObject::installEventFilter),i want to know why design it in such way.I just curious about it.
void QObject::installEventFilter(QObject *obj)
{
Q_D(QObject);
if (!obj)
return;
if (d->threadData != obj->d_func()->threadData) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}
// clean up unused items in the list
d->eventFilters.removeAll((QObject*)0);
d->eventFilters.removeAll(obj);
d->eventFilters.prepend(obj);
}
It's done that way because the most recently installed event filter is to be processed first, i.e. it needs to be at the beginning of the filter list. The filters are invoked by traversing the list in sequential order from begin() to end().
The most recently installed filter is to be processed first because the only two simple choices are to either process it first or last. And the second choice is not useful: when you filter events, you want to decide what happens before anyone else does. Well, but then some new user's filter will go before yours, so how that can be? As follows: event filters are used to amend functionality - functionality that already exists. If you added a filter somewhere inside the existing functionality, you'd effectively be interfacing to a partially defined system, with unknown behavior. After all, even Qt's implementation uses event filters. They provide the documented behavior. By inserting your event filter last, you couldn't be sure at all what events it will see - it'd all depend on implementation details of every layer of functionality above your filter.
A system with some event filter installed is like a layer of skin on the onion - the user of that system only sees the skin, not what's inside, not the implementation. But they can add their own skin on top if they wish so, and implement new functionality that way. They can't dig into the onion, because they don't know what's in it. Of course that's a generalization: they don't know because it doesn't form an API, a contract between them and the implementation of the system. They are free to read the source code and/or reverse engineer the system, and then insert the event filter anywhere in the list they wish. After all, once you get access to QObjectPrivate, you can modify the event filter list as you wish. But then you're responsible for the behavior of not only what you added on top of the public API, but of many of the underlying layers too - and your responsibility broadens. Updating the toolkit becomes next to impossible, because you'd have to audit the code and/or verify test coverage to make sure that something somewhere in the internals didn't get broken.

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).

Can connectSlotsByName connect to selection model changes?

In my main window (QMainWindow) I have a QTableView (named commandsTableView). Now I want to react on its selection changes.
I made a slot and connected it manually to ui.commandsTableView->selectionModel(). All works fine.
But then I thought: why not use auto-connection (especially that there will be more connections to be done)? At least it will add more force to consistent naming rules.
Yet I wasn't able to find proper name syntax. I tried:
on_commandsTableView_selectionModel_selectionChanged,
on_commandsTableViewSelectionModel_selectionChanged,
on_commandsTableView_selectionChanged,
on_commandsTableView___selectionChanged
but neither worked. In all cases there is there is a message on output when running the app (with corresponding slot name, here only first given as an example):
QMetaObject::connectSlotsByName: No matching signal for on_commandsTableView_selectionModel_selectionChanged(QItemSelection,QItemSelection)
(Why there are no assertions in response for connection errors - that I cannot understand. I lost much time wondering what is wrong before I spotted those - and alike - messages on output.)
The object returned by ui.commandsTableView->selectionModel() has an empty name. But setting it to selectionModel prior to making a call to connectSlotsByName doesn't help either.
According to the documentation connectSlotsByName() only supports signatures like
void on_<object name>_<signal name>(<signal parameters>);
According to the sources that's the only form it checks (watch how it collects a list of children, then matches parent's method names against names of the children).
Hence, to be able to use auto-connection you would have needed a named selection model, which would continue existing from the call to connectSlotsByName() onwards. Each time you change the selection model (not likely) or the model (likely) you'd have to name the selection model and auto-connect again. But alas connectSlotsByName() will duplicate all other connections as it doesn't seem to check if connections are unique, so we have to connect signals to such dynamic children as models, scenes etc manually.
I think it's
on_selectionModel_selectionChanged(const QItemSelection & selected, const QItemSelection & deselected)

In Qt, create a table with an blank editable row

This is a Qt-specific question.
It's convenient to be able to add new data to a table by typing content into a blank row at the bottom of a table. When the data is committed, a new blank row is added to the table.
Has anyone found a way of implementing this in a generic way, that fits into Qt's model-view programming architecture? My closest attempt involves creating a proxy model, such that the rowCount() returned from the model is always one greater than the source model.
QAbstractTableModel* sourceModel ; // Data is stored here
QBlankRowModel* model ; // Proxy model that adds one to rowCount()
QTableView* view ; // View
view->setModel( model ) ;
model->setSourceModel( sourceModel ) ;
Any suggestions are welcome. Thanks.
From a design-perspective, this should be part of the view, not the model. Therefore, I suggest implementing a view with the functionality and leave the model unchanged. KOffice Kexi does just this with kexitableview (screenshot, documentation). Maybe you want to use some of their code.
BTW, you might still be able to use your hack and combine it with my suggestion by putting it inside a new table view implementation YourTableView:
QBlankRowModel re-implements the
QAbstractTableModel
interface. It returns sourceModel.rowCount()+1 as the QBlankRowModel::rowCount().
It returns a QVariant() if the n+1th row is requested in QBlankRowModel::data().
All the rest within QBlankRowModel is forwarded to the sourceModel (with editing
the n+1th row in QBlankRowModel buffered and replaced with inserting into the
sourceModel when finished).
The new YourTableView inherits from
QTableView and wraps the sourceModel within
YourTableView::setModel(), calling
QTableView::setModel(QBlankRowModel(sourceModel)).
Thereby, your hack is localized at one point.
Your solutions seems a little hackish. Your problem is not only additions, it's also editions. What happens when your user edits a row, the typed data goes directly to your "data layer" even before the user commits his edition?
A better solution would be to restrict the role of your sourceModel. Rather than being a "direct" representation of your data, it should be a "buffered" representation of it. When the sourceModel is created, you make a copy of your data in some kind of Row() instances. The sourceModel, having its own copy of the data can then freely play around, perform editions and additions, and only commit the data to your model layer when the user commits his edits.
If you want a PyQt example of such a table, you can look at the source of a project of mine:
http://hg.hardcoded.net/moneyguru/
You might have to dig around to actually find the "buffering" logic because it's not in the PyQt code itself, but rather the "cross-platform" part of the code:
http://hg.hardcoded.net/moneyguru/src/tip/core/gui/table.py
This logic is then used in my QAbstractItemModel subclass:
http://hg.hardcoded.net/moneyguru/src/tip/qt/controller/table.py
Sounds like a reasonable solution, as it should work for any model that you might want as the actual table model, ie. SqlTableModel or just a plain one. As long as you add the row when the user is done editing and take care not to add the row when the user did not add any data.

How to get a LPITEMIDLIST pointer according to the path of a folder?

I want to get the system icon of a specified folder, but maybe the only way to retrieve the icon is to use SHGetFileInfo() method. The first parameter of SHGetFileInfo() method is a pointer of LPITEMIDLIST.
If I only have the absolute path of the folder, how can I get the pointer according to the path?
SHParseDisplayName().
Welcome to the wonderful world of PIDLs.
You can read more at Introduction to the Shell Namespace, but basically a PIDL is a Pointer to an item ID List. You can think of it as a linked list in contiguous memory, but instead of each node having a pointer to the next node, you instead have the cb member which is the Count of Bytes that are contained the item, so you can add that to the base address to get the next item. IDLists are terminated with an item with { cb = 0, abID = NULL }.
So, what's in these magic lits? Basically you don't care and can't know. Any IShellFolder implementation can create a new type of ID to represent its type of item in the shell namespace. The basic file system view that the Shell implements just stores the parts of the path in these lists, so you have something like "c:\" in the first one "Users\" in the next one, etc. In reality they are serialized structs (or classes) that may contain more data. But they can also represent printers, network shares, database searches (for search folders, stacks, etc).
All you really need to know is you can ask IShellFolders to give you a PIDL that represents the items they contain, and later on you can give that PIDL back to them, and other various Shell functions and interfaces, and they know how to deal with them. What SHParseDisplayName() basically does (I think) is go through the registry looking for all registered IShellFolder implementations and asks them if they know what to do with the string you pass in, and the first one to handle it makes the PIDL and gives it back.

Resources