Windows Small System Icon Height Incorrect - icons

I'm running on Windows 10, but using Delphi 7 (yes, I know it's quite old).
I want to use the system icons in Windows and have gone about this by defining a TImageList called SystemIcons which I initialize as follows:
var
fileInfo: TSHFileInfo;
begin
SystemIcons.Handle := ShGetFileInfo('', 0, fileInfo, SizeOf(fileInfo),
SHGFI_ICON or SHGFI_SMALLICON or SHGFI_SYSICONINDEX);
...
I have SystemIcons properties set statically as a TImageList component with width and height set to 16.
Elsewhere, I wish to retrieve an icon from this image list given a valid shell object's image index. Because these are "small system icons", I expect them to be 16x16. The result of calling GetSystemMetrics(SM_CYSMICON) yields 16. Oddly, the dimensions depend upon whether I retrieve them as a bitmap or an icon.
...
var
icon: TIcon;
bm: TBitmap;
begin
...
icon := TIcon.Create;
SystemIcons.GetIcon(imgIndex, icon);
bm := TBitmap.Create;
SystemIcons.GetBitmap(imgIndex, bm);
The imgIndex is correct and the same in both cases. The image retrieved is the same in each case, as expected. The dimensions of the bitmap (bm.Width and bm.Height) are also as expected: 16x16. However, the dimensions of the icon (icon.Width and icon.Height) are not. They are 32x32.
When I paint the icon on a canvas it appears as 16x16. So it's only its Height and Width values that appear incorrect. Very odd.
Why are these different?

The images are likely actually 32x32 to begin with.
Internally, TImageList.GetIcon() simply retrieves an HICON for the chosen image directly from the underlying Win32 ImageList API, using ImageList_GetIcon(), and assigns that to the TIcon.Handle property.
TImageList.GetBitmap(), on the other hand, is a bit different. It sizes the TBitmap to the dimensions of the TImageList (16x16), and then stretch draws the chosen image onto the TBitmap.Canvas using TImageList.Draw(), which in turn uses ImageList_DrawEx().

Related

Use Qicon disabled mode for off state

I have a bunch of checkable QToolbuttons, and I'd like the icons to be 'greyed out' in the unchecked state.
I can accomplish this by setting different files for the on/off states in the QIcon. like this:
tb = QToolButton()
tb.setCheckable(True)
ico = QIcon()
ico.addFile('color.jpg', QSize(16, 16), QIcon.Normal, QIcon.On)
ico.addFile('grey.jpg', QSize(16, 16), QIcon.Normal, QIcon.Off)
tb.setIcon(ico)
But since a QIcon can create a 'greyed out' version of itself that is used in disabled mode, I'd prefer to use the disabled mode icon over creating the grey version of all the icons myself. Is this possible?
You can get the grayed icon with QIcon.pixmap() and using the Disabled state, then set it again for the desired mode.
Since you want it for the Off state (the default) you have to first set the pixmap for the On state, get the grayed out pixmap and then set it with the other state:
original = QtGui.QPixmap('icon.png')
icon = QtGui.QIcon()
icon.addPixmap(original, QtGui.QIcon.Normal, QtGui.QIcon.On)
grayed = icon.pixmap(original.size(), QtGui.QIcon.Disabled, QtGui.QIcon.On)
icon.addPixmap(grayed, QtGui.QIcon.Normal, QtGui.QIcon.Off)
Note that while the common behavior of Qt is to gray out images, it is not guaranteed that it will happen on all platforms and styles.
Since we're talking about icons, we can assume that they are very small, so we can use a helper function to get a grayed out pixmap (while still respecting the alpha channel):
def getGrayed(src):
if isinstance(src, QtGui.QPixmap):
src = src.toImage()
dest = QtGui.QImage(src.size(), QtGui.QImage.Format_ARGB32)
widthRange = range(src.width())
for y in range(src.height()):
for x in widthRange:
pixel = src.pixelColor(x, y)
alpha = pixel.alpha()
if alpha < 255:
alpha //= 3
gray = QtGui.qGray(src.pixel(x, y))
pixel.setRgb(gray, gray, gray, alpha)
dest.setPixelColor(x, y, pixel)
return QtGui.QPixmap.fromImage(dest)
Then, do something similar to the above:
original = QtGui.QPixmap('iconalpha.png')
icon = QtGui.QIcon(getGrayed(original))
icon.addPixmap(original, QtGui.QIcon.Normal, QtGui.QIcon.On)
Obviously, this can be very demanding if there are many source icons and their size is somehow "big" (256x256 or more).
If you're worried about performance, the above getGrayed() function can be converted to a simple script that automatically creates grayed out icons.
Note that if that feature is required a lot of times in your code, you might consider creating your own QIconEngine subclass and create a custom static function to get the preferred icon (with modes already set) based on your needs.

Split screens with ALV Grid and Tabstrip control

I would like to ask about proper way to split following screens in ALV:
1st screen with type CL_GUI_ALV_GRID
2nd screen is subscreen with Tab strip control
Using docker there are issues with resizing of screen during the runtime. I am not able to provide ratio for both screen.
Is there a way to use CL_GUI_SPLITTER_CONTAINER also for the screen with Tab strip control ?
Thank you !
The following code reacts to a change of the window height. It does not react to a window width, that's a limit of Dynpro, so most of time it will react to Windows buttons minimize and restore, unless the window is the exact half left or half right of the monitor (combined keys Windows+Left and Windows+Right). SY-SCOLS and SY-SROWS are the only way I know to get the window size when a dynpro screen is displayed, but probably there are other ways.
DATA go_docking TYPE REF TO cl_gui_docking_container.
DATA ok_code TYPE syucomm.
DATA ratio TYPE i VALUE 70.
DATA pixels_by_sy_scol TYPE p DECIMALS 2.
CALL SCREEN 100.
MODULE pbo OUTPUT.
IF go_docking IS INITIAL.
CREATE OBJECT go_docking
EXPORTING
repid = sy-repid
dynnr = sy-dynnr
side = cl_gui_docking_container=>dock_at_left
ratio = ratio.
go_docking->get_extension( IMPORTING extension = DATA(extension) ).
cl_gui_cfw=>flush( ). " to calculate the extension (by default in pixels)
pixels_by_sy_scol = extension * 100 / ratio / sy-scols.
ELSE.
go_docking->set_extension( sy-scols * pixels_by_sy_scol * ratio / 100 ).
ENDIF.
ENDMODULE.

Load and Save opaque 8 bit PNG Files using ImageSharp

I am trying to Load -> Manipulate byte array directly -> Save an 8 bit png image.
I would like to use ImageSharp to compare its speeds to my current library, however in their code example they require the pixel type to be defined (they use Rgba32):
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
// Image.Load(string path) is a shortcut for our default type.
// Other pixel formats use Image.Load<TPixel>(string path))
using (Image<Rgba32> image = Image.Load("foo.jpg"))
{
image.Mutate(x => x
.Resize(image.Width / 2, image.Height / 2)
.Grayscale());
image.Save("bar.jpg"); // Automatic encoder selected based on extension.
}
I looked through the pixel types: https://github.com/SixLabors/ImageSharp/tree/master/src/ImageSharp/PixelFormats
But there is no grayscale 8 bit pixel type.
As of 1.0.0-beta0005 There's no Gray8 pixel format because we couldn't decide what color model to use when converting from Rgb (We need that internally). ITU-R Recommendation BT.709 seems like the sensible solution because that is what png supports and what we use when saving an image as an 8bit grayscale png so it's on my TODO list.
https://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
So... currently you need to use either Rgb24 or Rgba32 when decoding the images.
Update.
As of 1.0.0-dev002094 this is now possible! We have two new pixel formats. Gray8 and Gray16 that carry only the luminance component of a pixel.
using (Image<Gray8> image = Image.Load<Gray8>("foo.png"))
{
image.Mutate(x => x
.Resize(image.Width / 2, image.Height / 2));
image.Save("bar.png");
}
Note. The png encoder by default will save the image in the input color type and bit depth. If you want to encode the image in a different color type you will need to new up an PngEncoder instance with the ColorType and BitDepth properties set.

Get Geometry of widgets with variable size

Situation
I have a pop_up widget (say a textbox), which I can place arbitrarily on the screen by setting the properties x and y accordingly.
On the other hand I use the prompt, which is located in the default wibar.
I would like to place the pop_up widget directly below the prompt
Problem
I was not yet able to gather any useful information about the geometry of the prompt. With geometry I mean its x and y values together with its height and width. I solved the y-positioning by using the height of the wibar itself.
But I am stuck with x-positioning.
Is there a way to get the width of the widgets within the toolbar?
Notice
I read something about forced_width, but in this situation it sounds like a hack to me. So I would prefer to avoid forcing any widths.
I'm currently running awesome WM 4.2 on Fedora 26
Part of a problem is that "a" widget does not have a position and size since awesome allows widgets to be shown in multiple places at once. However, if we just ignore this problem, something like the following could work (to be honest: I did not test this):
function find_widget_in_wibox(wb, widget)
local function find_widget_in_hierarchy(h, widget)
if h:get_widget() == widget then
return h
end
local result
for _, ch in ipairs(h:get_children()) do
result = result or find_widget_in_hierarchy(ch, widget)
end
return result
end
local h = wb._drawable._widget_hierarchy
return h and find_widget_in_hierarchy(h, widget)
end
However, I have to warn you that the above could break in newer versions of awesome since it access non-public API (the part with wb._drawable._widget_hierarchy). There is a way to work with just the public API using :find_widgets(), but I am too lazy for that for now.
The above function gets the wibox.hierarchy instance representing a widget which allows to get the geometry of the prompt via something like the following (in the default config of awesome 4.2):
local s = screen.primary -- Pick a screen to work with
local h = find_widget_in_wibox(s.mywibox, s.mypromptbox)
local x, y, width, height = h:get_matrix_to_device()
:transform_rectangle(0, 0, h:get_size())
local geo = s.mywibox:geometry()
x, y = x + geo.x, y + geo.y
print(string.format("The widget is inside of the rectangle (%d, %d, %d, %d) on the screen", x, y, width, height)
Finally, note that the widget hierarchy is only updated during repaints. So, during startup the code above will fail to find the widget at all and right after something changed (e.g. you entered another character into the promptbox), the above will still "figure out" the old geometry.

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!

Resources