How to use QStyle::standardIcon/standardPixmap with QStyle::StandardPixmap? - qt

In my code there is a bunch of calls which try to create QIcons from
QStyle standard pixmaps, like:
QIcon groupIcon;
groupIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirClosedIcon ),
QIcon::Normal, QIcon::Off );
groupIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirOpenIcon ),
QIcon::Normal, QIcon::On );
While this works correctly, in that using the icon for a model's
Qt::DecorationRole shows either an open or closed icon based on the item's
expanded state, it has two issues:
It's not hi-dpi friendly, and the icons are tiny
QStyle::standardPixmap is marked as obsolete, with QStyle::standardIcon being described as the preferred approach.
I'm unsure how to translate the above code to QStyle::standardIcon though.
QIcon groupIcon( style()->standardIcon( QStyle::SP_DirClosedIcon ) );
works nicely for closed items, and looks great on hidpi. But I can't see how I would add the SP_DirOpenIcon state. There's no equivalent method like "QIcon::addIcon" like there is QIcon::addPixmap.
What's the correct approach to take here, which is hi-dpi friendly and future proof?

how to translate the above code to QStyle::standardIcon
To be able to use QStyle::standardIcon instead of QStyle::standardPixmap, select the particular pixmap from the icon with QIcon::pixmap.
Here is an example I have prepared for you of how to change your code in order to accomplish that:
QIcon groupIcon;
QSize sz(16, 16);
groupIcon.addPixmap(style()->standardIcon(QStyle::SP_DirClosedIcon).pixmap(sz),
QIcon::Normal, QIcon::Off);
groupIcon.addPixmap(style()->standardIcon(QStyle::SP_DirOpenIcon).pixmap(sz),
QIcon::Normal, QIcon::On);
Here 16 is the requested size. Please note, that:
The pixmap might be smaller than requested, but never larger.
hence adjust this value accordingly.

Related

How do I tell if a QIcon lacks a valid pixmap?

If I create a QIcon with a pixmap as in the following code snippet:
from PyQt5 import QtGui
icon = QtGui.QIcon(':/images/view-refresh.png')
How do I tell if the icon doesn't have a valid pixmap, e.g. because ':/images/view-refresh.png' wasn't found?
My solution is to wrap the QIcon constructor in a factory function that creates an explicit QPixmap, which is probed to see if it is valid:
def create_icon(filename):
pixmap = QtGui.QPixmap(filename)
if pixmap.isNull():
raise ValueError('No icon with filename \'{}\' found'.format(filename))
return QtGui.QIcon(pixmap)
icon = create_icon(':/images/view-refresh.png')
Note that there is also a QIcon.isNull method, but according to the documentation it can be false even if the pixmap is invalid.
The documentation for Icon.isNull() specifically says it returns false only if it has neither a pixmap nor a filename.
Looking through the list of member functions, availableSizes() looks like a better candidate. Ive tested this.
So good test to see if an icon is valid is len(icon.availableSizes())>0.

Qt-Application is killing characters accidentally (drawText produces bug)

I've got a really simple application for adding watermarks to pictures. So you can drop your pictures in a QListWidget which shows you a thumbnail and the path, adjust some things like the text, the transparency, the output format and so on.. and after pressing start it saves the copyrighted picture in a destination of your choice. This works with a QPainter which paints the logo and text on the picture.
Everything is able to work fine. But here's the misterious bug:
The application kills random letters. It's really strange, because I can't reproduce it. With every execution and combination of options it's different. For example:
Sometimes I can't write some letters in the QLineEdit of my interface (like E, 4 and 0 doesnt exist, or he changes the letters so some special signs).
The text of the items in the QListWidget aren't completly displayed, sometimes completley missing. But I can extract the text normally and use the path.
While execution I have a QTextBrowser as a log to display some interesting things like the font size. Although, the font is shown normaly on the resulting picture, it says " 4" or "6" instead of much higher and correct sizes. Betimes strange white blocks appear between some letters
When drawing text on the picture with a QPainter, there also letters missing. Sometimes, all the letters are printed over each other. It seems like this bug appears more often when using small pixelsizes (like 12):
//Text//
int fontSize = (watermarkHeight-(4*frame));
int fontX = 2*frame;
int fontY = (result.height()-(watermarkHeight-2*frame));
int fontWidth = watermarkWidth;
QRect place(fontX,fontY,fontWidth,fontSize);
QFont font("Helvetica Neue", QFont::Light);
font.setPixelSize(fontSize);
emit log(QString::number(fontSize));
pixPaint.setFont(font);
pixPaint.setPen(QColor(255,255,255,textOpacity));
pixPaint.drawText(place,text);
Not all of these bugs appear at once! Sometimes I haven't got any bugs...
Perhaps someone had a similar bug before. Unfortunately I didn't found something like this in the internet. I didn't post a lot of code snippets because I think (and hope) that this is a gerneral problem. If you need something specific to help me, please let me know =)
I've added a example picture:
In the lineEdit I simply wrote ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 (look what he has done with the 7 and 9)
This small square in the lower corner of the picture should be the "ABC..." thing
The "62" looks very strange in the textBrowser
I'm using Qt 5.0.1 on a Windows 7 64Bit computer.
EDIT: Everytime after adding the first picture to the list, he's finding these warnings:
QFontEngine::loadEngine: GetTextMetrics failed ()
QWindowsFontEngine: GetTextMetrics failed ()
But when I change the height (and with it the pointSize of the font) its not emitted anymore, even with the start-parameters.
EDIT 2: Thank you for your help! I corrected my code so that he only uses correct fonts and correct sizes, but it still doesn't work. When I remove the QPainter::drawText() function it works fine (without the text). But as soon as I am adding text everything is bugged. I have something like this now:
//Text//
QList<int> smoothSizes = fontDatabase->smoothSizes("Verdana","Standard");
int fontSize = (watermarkHeight-(4*frame))*0.75;
emit log("Requested: "+QString::number(fontSize));
if(!smoothSizes.contains(fontSize)){
for(int i = 0; i<smoothSizes.length(); i++){
if(smoothSizes.at(i) > fontSize && i>0){
fontSize = smoothSizes.at(i-1);
break;
}
}
}
int fontX = 2*frame;
int fontY = (result.height()-(watermarkHeight/2)+frame);
QFont font = fontDatabase->font("Verdana","Standard",fontSize);
QFontInfo info(font);
emit log("Corrected: "+QString::number(fontSize));
emit log("Okay?: "+QString::number(info.exactMatch()));
pixPaint.setFont(font);
const QFontMetrics fontMetrics = pixPaint.fontMetrics();
if(info.exactMatch()){
pixPaint.setPen(QColor(255,255,255,textOpacity));
pixPaint.drawText(fontX,fontY+(fontMetrics.height()-fontMetrics.ascent()),text);
}
It almost sounds like you are corrupting random memory in your process, or you have a badly broken Windows install. Possibly your font request is matched by a very poorly chosen system font.
Whatever is set on a QFont is merely a request. To obtain the parameters of the actual font that was selected, you must create a QFontInfo, and get your information from there.
Imagine that you request a QFont that doesn't exist on a system, or that can't be scaled to a particular size. At some point the font object would need to morph to reflect what really happened - this would be very confusing. Thus, the QFontInfo provides the information about the font that was actually used. Think of QFontInfo as a response, and QFont as a request.
I finally found a solution: I simply updated Qt from 5.0.1 to 5.2.1, now it works. Perhaps someone has a similar bug and this post helps him. Thank you for your help!

Qt-GUI Testing with Squish or other stuff

Is there a way to find out the filename of a QPixmap? My intention was to test which pixmap is currently set.
I've tried Squish to find out any properties, but I've get only access to some stuff like width, height etc
I assume that while you writing a test you already know which image you want to see. You can convert QPixmap to QImage and check if it contains the same image as expected:
bool same = QImage(filename) == pixmap.toImage();
It's not efficient but should be ok for testing purposes.

QPlainTextEdit truncate history linewise

I have a GUI application whose main part is a QPlainTextEdit. It is used to display a log of the application, and as such the associated text grows line by line ad infinitum.
As the application is intended to run very long, I need to limit the memory that will be allocated for this log. Therefore I want to have some maxNumLines or maxNumCharacters parameter that will make sure the history will be truncated when reached, i.e. the head lines will be removed as new lines are appended (a.k.a. log rotation).
To achieve this I found the functions
// get the associated text
QString toPlainText () const
// set the associated text
void setPlainText ( const QString & text )
Therefore something like this untested code would probably do the trick:
QString &tmp = pte.toPlainText();
while (tmp.size() > maxNumCharacters) {
// remove lines from the head of the string until the desired size is reached
// removes nothing if "\n" could not be found
tmp.remove(0, tmp.indexOf("\n")+1);
}
pte.setPlainText( tmp );
Is this the way to go to remove the first line(s) from the QPlainTextEdit? Are there probably other Qt Text GUI elements that would better fit to this task (set a maximum number of lines and truncate at the head of the list), e.g. somehow display a QStringList in which I could store the lines (s.t. I could easily erase(0))?
Or does the QPlainTextEdit eventually implement such upper bound for the size of the associated QString after all?
Apparantly the property maximumBlockCount is exactly what I need:
If you want to limit the total number of paragraphs in a QPlainTextEdit, as it is for example useful in a log viewer, then you can use the maximumBlockCount property. The combination of setMaximumBlockCount() and appendPlainText() turns QPlainTextEdit into an efficient viewer for log text.
For reference:
http://doc.qt.io/qt-5/qplaintextedit.html#maximumBlockCount-prop
I had exactly the same problem a months back, and I ended up using a QListView. Although using the model/view/delegate architecture is a bit more fiddly, it scales much better in the long run. For example once the basic architecture is in place, adding a filter that displays only error or warning entries becomes trivial, or creating a delegate so that the background of error entries are painted red is also straightforward.

QTableWidget: How can I get tighter lines with less vertical spacing padding?

The QTableWdiget is fabulous for simple grid displays. Changing colors, fonts, etc is straightforward.
However, I did not manage to give the grid a 'tighter' look with less vertical whitespace. I see that the Qt documentation talks (eg here) about
margin
border
padding
around widgets, but when I set these I only get changes around the entire grid widget rather than inside.
How can I set this (with a style sheet, or hard-coded options) directly to make the QTableWidget display tighter?
The code getting 'h' might be unsound. It was just an example. Copy & paste the following rather rudimentary code. Change the value in "setDefaultSectionSize()", recompile, and run. You should see the difference. Setting this to 10 or 50 yields visible results. In the code above, it is possible QFontMetrics or QFont is messing something up.
You can use whatever you want to get the height, but font size makes the most sense.
#include <QtGui>
int main( int argc, char* argv[] )
{
QApplication app( argc, argv );
QDialog* my_dialog = new QDialog();
QHBoxLayout* layout = new QHBoxLayout();
QTableWidget* my_table_widget = new QTableWidget( my_dialog );
my_table_widget->setRowCount( 10 );
my_table_widget->setColumnCount( 10 );
my_table_widget->verticalHeader()->setDefaultSectionSize( 15 );
layout->addWidget( my_table_widget );
my_dialog->setLayout( layout );
my_dialog->resize( 500, 200 );
my_dialog->show();
return app.exec();
}
EDIT: I don't know how to format a block of code here... forgive me. :)
Edit 2: I fixed that, and the following simple tighterTable.pro file
helps along.
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
SOURCES += tighterTable.cpp # if that is the filename
Thanks a big fat bunch for this. BTW: Editing as code is just indenting by four spaces, and/or hitting the button with the little '101010' in the formatting row.
What you're looking for has a very silly solution IMHO, but it works. You need to set the headers' defaultSectionSize() members. Accessed via verticalHeader() & horizontalHeader(). I never really set the column width's w/ this b/c most of my projects involve me adding rows, not columns, and I just call resizeColumnsToContents or do a manual resizing. However, the rows is irksome. I generally get the height of the font using QFontMetrics, and add 2. Any subsequent row added should have this height, and viola: tighter look.
Hope that helps.
EDIT:
Untested code:
QFontMetrics fm( my_font );
int h = fm.height() + 2;
my_table->verticalHeader()->setDefaultSectionSize( h );
QTableWidget is a convenience model and view. Typically, QAbstractItemModel's data() method provides a SizeHintRole that is used to tell the view what size each cell should be.
Since you're using QTableWidget, I don't think there's anything that you can do to change the size hint being returned by its internal model. Even the Qt style sheet documentation mentions nothing in that area.
QTableWidget -> verticalHeader -> setMinimumSectionSize() is the right way, and it can be set in ui.
I tried all the answers here without success. What worked for me though was setting both the following.
table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents)
table->verticalHeader()->setMaximumSectionSize(10)

Resources