Qt list of images - c++

I am looking for an efficient way to view a list of thumbnails of jpegs in Qt 5.8 (possibly several thousands).
My current approach is to use a QListWidget (see below, and this question). This turns out to be way too slow as it takes forever to assemble, even for a few images.
I am looking:
To understand why my current approach is so slow.
For a more efficient approach, which still requires only little code by relying as much as possible on Qt's features.
Current approach:
ui->listWidget->setViewMode (QListWidget::IconMode);
ui->listWidget->setIconSize (QSize(200,200) );
ui->listWidget->setResizeMode(QListWidget::Adjust );
for ( auto &i : files )
ui->listWidget->addItem(new QListWidgetItem(QIcon(i),i));
(whereby files is of the std::vector<QString> type)

A quick solution might be to create a simple class that inherits from QListWidgetItem and fixes up the thumbnails on demand. You could try something like (note: untested)...
class thumbnail_item: public QListWidgetItem {
using super = QListWidgetItem;
public:
explicit thumbnail_item (const QString &filename)
: super(filename)
{}
virtual QVariant data (int role) const override
{
if (role == Qt::DecorationRole) {
QIcon i = super::icon();
if (i.isNull()) {
/*
* The existing thumbnail (if any) is empty so create one
* from the file name and use it -- remembering to call
* setIcon so we don't need to go through all this again
* next time.
*/
i = QIcon(data(Qt::DisplayRole));
setIcon(i);
}
return(i);
}
return(super::data(role));
}
};
You still need the loop to create an item for each file but at least it defers the thumbnail construction.
A more elegant solution would probably involve using fully separated model and view objects with a custom proxy model performing the thumbnail generation and caching.

I have low rep to comment on G.M.'s answer, so here is my addition:
The initialisation may take long time due to 2 reasons:
The access to image files and their decoding can take up significant time. It is nice to have some buffering/precaching or defering, as G.M. suggested.
I believe, images are loaded "as is" (in high resolution), but resized to tiny resolution for thumbnail view inside QIcon. This can be optimised away by using QImageReader class for image reading, specifically its method setScaledSize(const QSize &size).

Related

Too deep hierarchy of signals-slots in Qt

I am writing GUI applicaton in Qt. Currently I waste too much time on routine. It seems something wrong with my architecture. Please tell me how can I change my approach to improve code.
What I am doing:
My program can be decomposed as hierarchy of classes (not inheritance but composition). In example:
class D { /* something */ };
class C { /* something */ };
class B { C c1; C c2; D d1; };
class A { D d1; C c1; };
So, actually it is a tree hierarchy where leaf nodes (class C, class D) are "models" in Qt terminology which hold data. At the top of hierarchy MainWindow (class A) is placed, which holds first level of "views" (class D, i.e. subwidget) and leaf node with data (class C, i.e. text field).
To pass information down from main window to data I use function calls from mainwindow (pushbuttons) to leaf nodes. After that data changes and tells parents about it with signal slot mechanism. Parents continue to pass message up with signaling.
I am really bored by establishing all these communication. Now I have up to 5 levels, however it is not that much in usual code when using composition. Please tell me how can I change my approach. Due to complexity of these connections, development of the code extremely slow, almost stopped.
It is hard to give a concrete example, because there are a lot of code, but the idea of problem which is very difficult to solve is following:
There are two QTreeView, which differently shows data from own model inherited from QAbstractItemModel (tree inside model is not that tree in previous discussion, this tree is only single level of hierarchy). I want to select objects in one QTreeView and change by that selection in second QTreeView. There are total 2 QTreeView, 2 different QAbstractItemModel instances, 2 trees of own objects (for each QAbstractItemModel), and single data.
Sounds like you might have become a victim of going through too many examples. Examples tend to cram functionality where it doesn't belong, creating the possibility to develop bad programming habits.
In actual production things need to be more compartmentalized. The main window should not be the container of the "application logic", all it needs to concern itself with is holding together the main widgets.
But that doesn't seem to be your case, judging by the necessity to delegate things "from mainwindow (pushbuttons) to leaf nodes" as you put it.
On a grander scale, it is not advisable to mix application logic with UI at all, much less cram it all in the main window. The application logic should be its own layer, designed so that it can work without any GUI whatsoever, and then the GUI is another layer that simply hooks up to the logic core.
The logic core should not be monolith either, it should be made of individual components focusing on their particular task.
Your use case doesn't really require any crazy amount of connections, just some basic handlers for the UI elements, which should target the logic core API rather than GUI elements as you appear to be doing now.
Your clarification unfortunately makes absolutely no sense to me, it is still completely unclear what you exactly you want to do.
Let's assume your situation is something like this:
Tree 1 shows a folder structure.
Tree 2 shows the file content of the folder, selected in tree 1.
Data is an editor for the file, assuming a text file, selected in tree 2.
So, in pseudocode, presuming that app is your application core logic object:
Clicking an item in tree 1 says app.setFolder(tree1.selectedItem())
Clicking an item in tree 2 says app.setFile(tree2.selectedItem())
Clicking the editor "save" button says app.save(editorUI.dataField.text())
logic layer gui layer
app mainWindow
folder <-----------select----------- tree1
file <-----------select----------- tree2
save(newData) { editor
if (file) file.rewrite(newData) textField
} saveBtn: app.save(textField.text())
Since there is only a single data source, you could do the following:
Create a general model for that data source. The model should represent the data source generally, without consideration of what the views need.
Create two proxy viewmodels that adapt the general model to the needs of the views.
Couple the selection models of the views that display the viewmodels.
Given the selection models on top of the two proxy models that map to the same source, we can propagate the selection change between them. We leverage the selection mapping provided by the proxy. The QAbstractProxyModel has a functional implementation of mapSelectionxxxx.
void applySel(const QItemSelectionModel *src, const QItemSelection &sel,
const QItemSelection &desel, const QItemSelectionModel *dst) {
// Disallow reentrancy on the selection models
static QHash<QObject*> busySelectionModels;
if (busySelectionModels.contains(src) || busySelectionModels.contains(dst))
return;
busySelectionModels.insert(src);
busySelectionModels.insert(dst);
// The models must be proxies
auto *srcModel = qobject_cast<QAbstractProxyItemModel*>(src->model());
auto *dstModel = qobject_cast<QAbstractProxyItemModel*>(dst->model());
Q_ASSERT(srcModel && dstModel);
// The proxies must refer to the same source model
auto *srcSourceModel = srcModel->sourceModel();
auto *dstSourceModel = dstModel->sourceModel();
Q_ASSERT(srcSourceModel && (srcSourceModel == dstSourceModel));
// Convey the selection
auto const srcSel = srcModel->mapSelectionToSource(sel);
auto const srcDesel = srcModel->mapSelectionToSource(desel);
auto const dstSel = dstModel->mapSelectionFromSource(srcSel);
auto const dstDesel = dstModel->mapSelectionFromSource(srcDesel);
// we would re-enter in the select calls
dst->select(dstSel, QItemSelectionModel::Select);
dst->select(dstDesel, QItemSelectionModel::Deselect);
// Allow re-entrancy
busySelectionModels.remove(src);
busySelectionModels.remove(dst);
}
The above could be easily adapted for a list of destination item selection models, in case you had more than two views.
We can use this translation to couple the selection models of the views:
void coupleSelections(QAbstractItemView *view1, QAbstractItemView *view2) {
auto *sel1 = view1->selectionModel();
auto *sel2 = view2->selectionModel();
Q_ASSERT(sel1 && sel2);
connect(sel1, &QItemSelectionModel::selectionChanged,
[=](const QItemSelection &sel, const QItemSelection &desel){
applySel(sel1, sel, desel, sel2);
});
connect(sel2, &QItemSelectionModel::selectionChanged,
[=](const QItemSelection &sel, const QItemSelection &desel){
applySel(sel2, sel, desel, sel1);
});
}
The above is untested and written from memory, but hopefully will work without much ado.

QTableView slow scrolling when many cells are visible at once

Background: I'm developing application using Qt 5.5.1, compiling with msvc2013. In this app I use my own implementation of QTableView, along with custom QStyledItemDelegate (needed custom cell editing) and QAbstractTableModel. I intend this view to work with massive amount of data that I wrap inside mentioned model. I allow the user few data editing options, custom sorting, 'invalid' rows windup etc.
The problem: scrolling speed of my QTableView subclass is slow - it gets slower the more table is shown (by resizing window), e.g. ~250 cells shown (in fullscreen) = slow, ~70 cells shown (small window) = fast.
Whad did I try so far:
First was to check if my model is slowing things down - I have measured times (using QTime::elapsed()) reading 10k samples and it shown 0 or 1ms. Then I have simply altered QTableView::data method to always return predefined string and not acquire any real data.
QVariant DataSet_TableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::ItemDataRole::DisplayRole) {
return QVariant("aRatherLongString"); //results in slow scrolling
//return QVariant("a"); // this instead results in fast scrolling
}
else return QVariant();
}
As you can see, the speed seems to be affected by number of characters vieved per cell, and not by underlying connections to data source.
In my custom implementation of QStyledItemDelegate I have tried same 'trick' as above - this time overriging displayText method:
QString DataSet_TableModel_StyledItemDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return "a" //fast
// return "aRatherLongString"; //slow
// return QStyledItemDelegate::displayText(value, locale); //default
}
After some thought with a friend we concluded that maybe we could disable drawing/painting/updating of cells until whole scroll action is done. It might cause some flickering, but it's worth a try. Unfortunately we dont really know how to aproach this. We have everriden QTableView methods: scrollContentsBy(int dx, int dy) and verticalScrollbarAction(int action) - we have captured scroll action properly (either method intercepts it) and tried to somehow disable repainting like this:
void DataSet_TableView::verticalScrollbarAction(int action) {
this->setUpdatesEnabled(false);
QTableView::verticalScrollbarAction(action);
this->setUpdatesEnabled(true);
}
...but it did not have any visible effect.
How should we approach it? Do we need to use setUpdatesEnabled() on items that are put inside cells directly? (not sure what those are - widgets?)
Here are screenshots taken as part of testing this problem:
Predefined text, no calls to underlying data structure - slow scrolling, 'full screen'
Predefined text, no calls to underlying data structure - fast scrolling, windowed
Request: Could you kindly help me pinpoint the cause of this and suggest solution if possible? Is it limitation of the classes that I use?
First of all, you should also run your application in release mode to check your perfomance, in my experience, the performance decreases greatly when using debug mode.
Secondly, you need to be aware that the model data method and delegates methods are called every time you resize, scroll, focus out, right click etc. These actions trigger those methods to be called for each displayed cell, therefore you would need to make sure that you don't do any unnecessary processing.
The items inside cells are delegates that call their own methods (eg: paint).
Some C++ specific optimisations would be helpful in the implementation of these methods, like using a switch instead of an if statement, see explanation here and here. The usage of Conditional (Ternary) Operators might also speed up the things, more information here, here and some information about expensive checkings here.
Also, QVariant handles text in different ways, as exemplified below, you should try both ways and check if there is any difference in speed. Some conversions are more expensive than others.
v = QVariant("hello"); // The variant now contains a QByteArray
v = QVariant(tr("hello")); // The variant now contains a QString

Simple acces of C++ object data from QML

I'm making a board game in Qt/C++ using qml. All the important game data is represented in a single class. My intention is to have the qml sheets access this one object and draw the game board depending on the data.
What is the simplest approach to exposing the C++ data members to QML?
Now I know the question has been asked, I've seen the answers and the Qt Documentation. I am, however, not satisfied. What I've seen, the way to do this seems to be to make Q_PROPERTY out of every single variable I want to access from QML. This looks to me tedious and unnecessary, not to mention it will stretch the code to 3x it's original length, making it significantly worse to read. Also, in most cases I won't need write function to the data members, for example.
And why bother with Q_PROPERTY overhead when I could just write Q_INVOKABLE getters for just the situations I need?
Here's an example of how simple I hoped it would be when I read in the Qt Project documentation: "This enables C++ data and functions to be accessible directly from QML, often with little or no modification."
class game : public QObject
{
Q_OBJECT
public:
explicit game(QObject *parent = 0);
colors NPC[3]; // colors being an enum declared elsewhere
player players[4]; // player is a non-QObject class containing player stats
}
...
game gMain;
QDeclarativeContext *context = viewer.rootContext();
context->setContextProperty("Game",&gMain);
QML in my ideal world:
Image {
id : Image1
source: { if (Game.NPC[0] === 0) {
if (Game.players[1].LifeCount > 0) {
return "pics/FigG.png"
}
else {
return "pics/StoneG.png"
}
}
Now how close to that can I actually get with QML and how do I go about it?
I'm especially interested in handling simple C++ style arrays and enums (have a lot of those in the game) - would I need to write helper functions, e.g. int Game.GetNPCAt(int i) instead of using just Game.NPC[i] ?
I realize that the way I DON'T want to do it is the tried and trusted, and for good reason... however in my situation (small one-man project) it seems like using a cannon to kill a fly (although the GUI building part in qml is amazingly simple and quite a joy to use) - also having to wrap around every data member including the simplest like an int seems... ridiculously excessive.
Maybe I have missed something somewhere, In which case I humbly apologize. Thank you for any thoughts on the matter.
In order:
Q_PROPERTY: When you look at the page that you quoted, they discuss using the Q_PROPERTY method to expose properties to QML. If you don't use Q_PROPERTY, it is my understanding that your variables won't be registered by QMLViewer (or what have you). The Q_PROPERTY needs a Q_INVOKABLE to get/set your variables. If you don't use Q_PROPERTY, though, your class properties will not appear in QML.
Setting the image source: If you may remember, QML is a forge between CSS and JavaScript. If you're just looking to make the image's source change depending on a condition outside of your Image element, you can just create a JavaScript function to achieve what you have quoted:
Image {
id: Image1
function getImage()
{
if (Game.NPC[0] === 0)
{
if (Game.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
However, the function won't run by itself: you'll have to associate it with a signal, which I would create in your C++ class (put the function under a label named signals: (NOT within public -- see here on how to write signals)). Based on your example, I'm guessing that your C++ object is called Game.
Game {
id: gameKeeper //or whatever you want to name it
onUpdate: Image1.getImage() //replace onUpdate with your signal
}
Image {
id: Image1
function getImage()
{
if (gameKeeper.NPC[0] === 0)
{
if (gameKeeper.players[1].LifeCount > 0) {
Image1.source="pics/FigG.png";
}
else {
Image1.source="pics/StoneG.png";
}
}
}
In theory, you should be able to reference arrays this way with JavaScript (I'm not all that familiar with JS myself).
Array handling: On the C++ side, it looks like the best way to do it is through a QList. Fortunately, a QList iterates very similar to a normal array. I found this, which should help -- just ignore the second dimension.
Hope this helps.

Good design practice concerning MVC view interface

I don't often have to create GUI's but today I do so I was hoping for some design input.
Basically I have a backend which I intend to add a GUI too using the MVC pattern. The issue is I feel whatever class encapsulates the main GUI window is going to have A LOT of state (all of the sub elements); and on top of that it's going to have a lot of setters, and possibly getter, clear, colour, size, position and refresh functions too.
One option is to march ahead with this idea and have a very large public interface which deals with the types the GUI uses (std::string, std::vector<std::string>...) the more control I want over the UI the more public member function I am going to need.
The other option would be to pass the program state to the GUI and have it decide how it display it, I fear doing this would mean it would give me less fine detail control and would break down the separation of concerns and would mean any changes to the representation of the program state would require changes in the GUI too.
Any input on the matter would be of great help.
If it makes any difference this is a C++ gui using an ncurses abstraction.
It sounds like to me you've thought alot about the M and the V, but not much about the C. The pattern should really be called MCV because the whole idea is that the controller IS the bridge between your model (data) and view (GUI). It sounds like you need a controller with most of the functionality you've mentioned.
Simply put though, your model obviously should know nothing about display and your display (view) should not know how to access the model. You need a controller that reads the data (model) and gives instructions to the display (view). If you have user interaction within the view, the controller can interpret that and modify the model as necessary.
The idea is that you never have to change all 3, but if you change the model or the view, you almost always have to update the controller.
Hope that helps...
There is at least one alternative to the giant interface. Instead of having a function that handles each thing (size, font, color, what-to-display, etc...) have a singular function that accepts a "role" and data that represents the role. This requires some sort of wrapper that can contain multiple data types.
QT's QAbstractItemModel Class Reference has a good example:
QVariant QAbstractItemModel::data ( const QModelIndex & index, int
role = Qt::DisplayRole ) const [pure virtual]
What that function will do is return the QVariant that represents the role indicated at the index provided.
The downside of this approach, is you have to know what roles exist, and what they do. QT's default roles are shown here.
I like to have parts of the model able to instrument themselves:
class Model {
private:
int value;
public:
void instrument(Instrumenter& instrumenter);
};
The Instrumenter manages the creation of controls. The model will tell it how it can be controlled and give it access to the data.
void Model::instrument(Instrumenter& instrumenter) {
instrumenter.addRangeController(0, 100, 5, value);
}
Then for different input devices (e.g keyboard, touchscreen) you can create appropriate controls:
class KeyboardInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create 3 widgets, up arrow, down arrow, and value
}
};
class TouchscreenInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create slider with min, max and increments
}
};
Instead of passing in the int directly we could have wrapped it in a controlling object, to aid encapsulation.

Custom text (code) areas in QTextEdit

I am interested in creating a text object type (inheriting QTextObjectInterface) that behaves like a code area:
distinctive background
border
fixed-width font
editable content
the instances need to be identifiable to code, so that content inside them may be extracted (separate code from surrounding content)
saving / loading (from regular html files)
syntax highlighting would be a plus, but is not really required
The other areas of the document would need to behave the usual way (font properties editable, colors editable, etc).
Qt provides an example for implementing custom text objects with QTextEdit. This looks like the hard way, since new text object can't make use of the existing infrastructure inside QTextEdit / QTextDocument.
QTextObject is
a base class for different kinds of objects that can group parts of a QTextDocument together
so inheriting it may be a choice, but neither its source files in Qt SDK package nor Google searches revealed helpful information.
QTextFrame inherits QTextObject so, again, it may be a feasible base class if some hints about this path are to be found.
In an simple HTML file all this (except syntax highlighting) would be easy. QTextEdit takes html as input and is able to export html, but the structure is lost in the process.
<code class="code-sample">
int i = 0;
</code>
QWebView is read-only, by the way. It advertises that:
Parts of HTML documents can be editable for example through the contenteditable attribute on HTML elements.
There may be other platforms where this is readily available, but the text editor needs to be used inside Qt Creator as a plug-in, so using Qt framework makes sense.
Bottom line: how does one implement code areas in a QTextEdit widget?
Later edits:
using Qt sdk from trunk (identifies itself as 4.8.4)
Qt Creator from trunk (Qt Creator 2.6.81)
I have found out that implementing this is possible using QTextEdit / QTextDocument.The most simple implementation that I can think of is presented in this answer for the reference of future seeker.
Notice that saving/loading needs to be customised as regular .toHtml() will not preserve the information needed.
Inserting a code block is simple:
QTextFrame * frame;
frame = cursor.insertFrame( code_block_format_ );
connect( frame, SIGNAL( destroyed() ),
this, SLOT( codeBlockDeleted() ) );
code_blocks_.append( frame );
notice the two variables that you can save in the class:
QTextFrameFormat code_block_format_;
QList<const QTextFrame*> code_blocks_;
We need the format for frame to be consistent and distinctive. It mat be initialised in constructor to something like:
code_block_format_.setBackground( QBrush( Qt::yellow ) );
code_block_format_.setBorder( 1 );
code_block_format_.setBorderStyle( QTextFrameFormat::BorderStyle_Inset);
code_block_format_.setMargin( 10 );
code_block_format_.setPadding( 4 );
We need the list so we can tell if a certain frame is a code box or not. Since all objects inheriting QTextObject need to be created by QTextDocument::createObject() we can't simply subclass the QTextFrame (actually I think we can, but not sure yet).
Now separating the code content from the rest may be done the usual way:
for ( it = frame->begin(); !(it.atEnd()); ++it ) {
child_frame = it.currentFrame();
child_block = it.currentBlock();
if ( child_frame != NULL )
{
if ( code_blocks_.contains( frame ) )
{
/* ... */
}
}
} /* for ( it = frame->begin(); !(it.atEnd()); ++it ) */
but notice that this is over-simplified for the sake of brevity. One needs to take into account nested frames.
If you are interested in a full implementation check out the git repository (work in progress, November 2012).