In Qt why cacheKey differs for same icon - c++

Take a form
Place two CommandLinkButton(s) on the form
Place a PushButton on the form
Right click PushButton-> Go to slot...
Choose clicked()
Write following code:
std::cout << ui->commandLinkButton->icon().cacheKey() << std::endl;
std::cout << ui->commandLinkButton_2->icon().cacheKey() << std::endl;
Why does that code print two different values for the cachekey?
We did not change the icon of any CommandLinkButton so both have the same default icon!
My problem is like this:
I have a push button with an icon on it, which is coming from resources.
I want to verify in test code that it has the same required icon on it.
To do that I compare the cachekey of image on the push button and cachekey of QPixmap object having same image like this:
const QPixmap image(":/MyProject/Images/Yellow_Icon_40x40.png");
if(image.cacheKey() == ui->PushButton->icon().cacheKey())
{
cout<<"OK";
}
But that test fails because cacheKey turns out to be different.
So what is the best way to do that check?
Basically like calculating hash of image and match that hash, which should always be same for any instance of same image.

That's because two icons are not cached in the same cache and/or not copied from the same object. Look at item 2 of this answer and cacheKey documentation.
To get the same cacheKey for different icons you should create an icon object in constructor of your window and call setIcon on it for both CommandLinkButtons.
Update:
To compare the icon with a bitmap from file by hash, you need some data-based hashing. Fortunately, Qt provides a bunch of qHash functions and qHashBits function for continuous memory blocks. You may compare your images hashes like this:
// assume image and icon object are declared as in your question
QImage imImage = image.toImage();
QImage imIcon = ui->PushButton->icon().pixmap(QSize(1024,1024)).toImage();
auto hbImage = qHashBits(imImage.constBits(), imImage.byteCount());
auto hbIcon = qHashBits(imIcon.constBits(), imIcon.byteCount());
if(hbImage == hbIcon) {.......}
Here we get image raw data in a very clumsy and unnatural way and then compute hashes of it. It's probably not what you want. Hashes returned by qHashBits are not the values returned by cacheKey. Furthermore, these hashes may differ as image is loaded directly from file, while QIcon may perform some processing on it's image during construction. qHashBits comes from Qt's hash table implementation, so it implies that minor changes of input data yield significant changes of returned hash. To compensate possible difference, you may construct QIcon object from image with options same as in the interface, and then obtain icon's data.
And it's not clear from your example, what for to take hashes before comparison? Hashing requires whole data traversal, so it will not be faster that just std::equals on data until you compare thousands of images to one image.

Related

Convert Mat to PvBuffer (from Pleora eBUS SDK lib.)

thank you in advance for reading this.
For better readability I'll list pieces of info. necessary in point form.
building an app based on eBUS player sample provided by Pleora.
Grabbing images in 16 bit gray in 50 fps.
my app is processing image data using openCV within the OnBufferDisplay function in DisplayThread
so far, I have successfully processed images suiting my needs and am trying to convert my Mat(processed already) data back to PvImage or PvBuffer s.t. the image can be displayed in the eBUS Player using the already existing display function(this way I can still use other features in the Player app without having to make too many modifications to the original app).
The difficulty arises from the fact that display functions and other important functions are hidden in dlls. I can only make modifications to what's exposed.
The processing procedure is basically:
void Display Thread::OnBufferDisplay(PvBuffer *aBuffer){
aBuffer->Attach(imageMat.data, 614400);
//where imageMat is init'd as
//imageMat = cv::Mat(cv::Size(imgWidth, imgHeight), CV_16U);
//process.. the data....
//at this point, I Have my Mat object ready to go.
//supposed to create or have another PvImage or PvBuffer with the processed datahere for displaying.
//Display
mDisplay->Display(*aBuffer);
}
What I have tried so far is:
memcpy(aBuffer->GetDataPointer(), imageMat.data 614400);
//and
aBuffer->free();
//and/or
aBuffer->Detach();
//then
aBuffer->Alloc(614400); //since 640 * 480 in 16 bit gray
memcpy( Mat.data to buffer->datapointer);
Please help me clarify the following:
I have discovered after using Attach(), the passed aBuffer does no longer properly display image. Does that mean this function not just copies the data from aBuffer to imageMat.data but erases the data in aBuffer and then copies it to imageMat.data?
I went for Attach() because memcpy(imageMat.data, aBuffer->GetDataPinter(), size); didn't work. It didn't copy the data properly like the way Attach() does. Anybody know why?
The description for Attach() provided by Pleora is "Attach this PvBuffer to an external memory buffer. To use an internal memory buffer, use Alloc.".
In summary,
How do you properly create a PvImage or a PvBuffer object that contains the data from my Mat object in order to pass to the Display() function?
Are there any detours to make this work without having to create such objects?

QPixmap How to compare if two pixmap(pictures) are same or different

Hi i have this QUI project its based match 3 game. I have problem with checking if in row there is 3 same type pic. i try to check if the next label in row is same picture and add it to new vector adding at the moment i cant find any way i could compare two pictures. I have tried chacheKey and few other methods but don't make them work. If they are same i could use any help to this project. Thank you.
QPixmap pic_value = labels[y][x]->pixmap(Qt::ReturnByValue);
QPixmap pic_value2 = labels[y][x+1]->pixmap(Qt::ReturnByValue);
if (pic_value.cacheKey() == pic_value2.cacheKey())
{
match_set.append(labels[y][x+1]);
}
Try the following code:
if (pic_value.toImage() == pic_value2.toImage())
Reference: https://www.qtcentre.org/threads/13537-do-we-can-t-compare-QIcon-or-QPixmap
First, unfortunately you can't compare QPixmap directly with the == operator so you will compare it by transferring it to an image then compare it.
if (mLogo.toImage() == logo.toImage())
return;
Secondaly, based on the QImage comparison from this documentation
The comparison can be slow, unless there is some obvious difference (e.g. different size or format), in which case the function will return quickly.
So, the operation of setting the mLogo to logo is easier than the checking for comparison.

Qt list of images

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

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

QT QIcon properties for custom widget in designer

I have been working for a little while now on creating a QT custom designer widget for GUI menus. The idea being that you simply drag it into the designer, select the number of frames you'd like, how many buttons per frame, etc. and it generates and sizes everything for you.
The way the widget is structured there are properties to configure each button for the frame you are in. For example, you would use the button0Text field to enter text under Button0 while editing in frame 0, then use it again to edit Button0 which is in frame 1. Both buttons would retain the individual changes for each frame.
The Problem
Normally when I switch frames all of my properties are updated to reflect the status of the frame. The exception being QIcon. The correct icon is retained in the actual graphical representation and builds correctly, however the file path in the property list is always of the last edited for that property. I think this will be extremely confusing to an end user and I have found no way to fix it. So for example, if I set text and icons in frame 0 then switch to frame 1 the text in the property list will update to reflect the state of frame 1 but the icon path names will still show my last edit in frame 0 and not the actual icon in frame 1.
I have tried things as simple as:
setProperty("button0Icon", getButton0Icon());
That code works on properties like text, but not for the icon. I try executing it immediately after changing frames.
I have also tried:
#ifndef Q_WS_QWS
QDesignerFormWindowInterface *form = QDesignerFormWindowInterface::findFormWindow(this);
if(form){
QDesignerFormEditorInterface *editor = form->core();
QExtensionManager *manager = editor->extensionManager();
QDesignerPropertySheetExtension *sheet;
sheet = qt_extension<QDesignerPropertySheetExtension*>(manager, this);
int propertyIndex = sheet->indexOf("button0Icon");
sheet->setChanged(propertyIndex, true);
sheet->setProperty(propertyIndex, getButton0Icon());
}
#endif
And:
int propertyIndex = this->metaObject()->indexOfProperty("button0Icon");
QMetaProperty property = this->metaObject()->property(propertyIndex);
property.write(this, QIcon());
Nothing seems to update the property list in the designer.
I have all properties, including all QIcon properties properly declared in the header file with Q_PROPERTY and assigned getter and setter functions.
To be clear, the icon values are indeed retained through each frame when compiled. So it is functioning, just unclear for most users.
If anyone has any experience with this or any ideas please let me know. Thanks.
I have discovered that QIcon does not store file names/paths. The file names are only used for the creation of the QIcon. I think this is most likely the reason I do not get any feedback in the Property Browser for my QIcon properties.
Instead I have chosen to hide this property in the designer and add three new ones. Three QUrl properties, each of which is used to supply an image file. I use three because I want to construct a QIcon that contains Modes/States for normal, disabled, and pressed operations.
I take each of these QUrls and save them in QStringLists behind the scenes so their values are stored. I then construct my QIcon using the file names provided from the QUrls.
I would much prefer to be using the native QIcon in the designer for this, any thoughts or feedback are appreciated.