QPaintDevice::height() returns the height of the application window, not the height of monitor screen.
Ex: Assume a monitor screen that is 1920 pixels wide and 1024 high. If an app window is resized to 800 x 600, QPaintDevice::height() returns 600, not 1024.
What function must I call to get the 1024 value?
In Windows GDI, this would be GetDeviceCaps(hdc, VERTRES)
Related
The devicePixelRatio is used to translate the device independent pixels into the actual physical pixels on the device. Therefore taking the example from the Qt Summit 2015 if I had a 300 x 200 QWidget with a devicePixelRatio of 2, it will render a 600 x 400 QWidget to the screen.
I have beneath me some code that I created, however I feel as though the opposite behaviour is happening. When I set self.image.setDevicePixelRatio(2) my QImage actually halves in size, however with a devicePixelRatio of 2, I interpret this as, for each device independent pixel, now occupy 2 pixels on my physical device, therefore doubling the size of my QImage (or atleast doubling when comparing to the before devicePixelRatio value of 1). Could someone explain why it is instead halving the size?
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QImage, QPainter
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
DEFAULT_WIDTH = DEFAULT_HEIGHT = 250
class QPainterWidget(QWidget):
def __init__(self):
super().__init__()
self.image = QImage(DEFAULT_WIDTH, DEFAULT_HEIGHT, QImage.Format_RGB32)
self.image.fill(Qt.green)
self.image.setDevicePixelRatio(2)
painter = QPainter(self.image)
painter.end()
def paintEvent(self, event):
painter = QPainter(self)
painter.drawImage(0, 0, self.image)
painter.end()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT)
self.setCentralWidget(QPainterWidget())
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
This is very logical but a bit complicated at the first sight from the library users' perspective. And very complicated considering all implementation details in Qt library. But you do not need to care about most of the details, fortunately.
With widgets it is easy: you specify the logical size and the physical size on screen is derived from the logical size multiplied by the application DPR.
widget_physical_size_on_screen = widget_logical_size * application_dpr
But with images (and also icons and pixmaps) you need to distinguish between the image physical resolution and the physical size on screen. They can differ. And you also need to distinguish between the application DPR and the image DPR. They can also differ. The final physical size on screen is calculated using logical size as an intermediary step.
So with images you first specify the physical resolution in the constructor QImage(width, height), the parameters width and height represent physical pixels of your image resolution. Then you can specify image DPR (as you did in your code). Then your image logical pixel size is derived from its physical resolution by dividing with the image DPR. And when the image is displayed in application, it is then resized to physical size by multiplying with the application DPR (not the image DPR).
image_logical_size = physical_image_resolution / image_dpr
image_physical_size_on_screen = image_logical_size * application_dpr
Therefore if you display image with physical resolution 600x400 with image DPR 2 (then it has logical size 300x200) in application which has DPR 1, then it will have physical size 300x200 pixels, because in application with DPR 1, one logical pixel translates to one physical pixel on screen. This is exactly what hapenned in your case - increasing DPR to 2 leads to smaller image on screen because your application still has DPR 1. E.g. image with resolution 600x400 with image DPR 2 appears as 300x200 on screen.
If you display the same image in application with application DPR 2, then the image will have 600x400 physical pixels. Because in application with DPR 2, one logical pixel translates to 2 physical pixels. I.e. your image will have its original size if and only if your application has the same DPR as is the image DPR.
If you display this image in application with DPR 3 (e.g. if you have really large 8k HighDPI display), then the image will have 900x600 physical pixels. And it will scale up badly (if you get a closer look on your fancy 8k display, the image will be slightly pixelated) because the original image does not have such a big resolution.
Actually setting DPR for images is not strictly needed, you can leave it with default value, which is DPR 1. But then you will need to do some more size recalculations and image resizings yourself. So if you want image with logical pixel size 300x200 then for application with DPR 1 you would need to create image with resolution 300x200, for application with DPR 2 you would need image with resolution 600x400 etc. Otherwise if you display image with physical resolution 300x200 and DPR 1 (i.e. it has logical size 300x200) in application with DPR 2, the image will be scaled-up to physical size 600x400 but it will be pixelated because the actual physical resolution of the image is still just 300x200.
So keeping images with DPR 1 can actually save some memory in some cases since you will allocate smaller images, only as big as you really need. But the drawback is doing more calculations in code yourself. It may be tempting to do these calculations yourself at the first sight. But I should warn you, in the text above I left out one important detail. In general case there is not a single application-wide DPR. In multiscreen set up the application can occupy more screens, each with different DPR. So it is not the application which specifies the DPR but DPR is specified for each widget individually. And widgets can move from screen to screen if you move your window and hence their DPR can change. And believe, then you do not want to recalculate the size of your images yourself in your code.
But if you are 100 % sure you do not want to support multiscreen mixed DPR scenario, then you can leave your images with DPR 1 (the default) and resize your images yourself. It can save you save memory and processing power in some cases because you can avoid allocating unneccessarily big images and avoid scaling down if the big resoltion is not needed. But I would probably not recommend going this way.
And one last very important note: always test your application in low-DPI scenarion with DPR == 1 and high-DPI scenario with DPR = 2. And ideally test your application also with fractional DPR, i.e. when DPR is a floating point number such as 1.25 or 1.5. Your application should work well in all these cases. If not, then fix it.
I've been making a website with react and I've been using css with classNames. I also was following a figma document where the figma screens were a certain size lets say x pixels by y pixels. But, on other computers, it might have a larger or smaller pixel dimension (a pixels by b pixels). I've used the x by y pixel for div dimensions as well as button sizes and more. So, when I open my application on other computers with a larger pixel dimension, a portion of the screen is just white, and it still displays the website as if it were on a smaller screen. Is there a way to fix this so that even though I used pixel values my app will adjust to different sized screens? Or, do I have to go back and change everything to percentages?
You need to think about proportion when building your UI.
1- Use percentage(%) for width and aspectRatio for height, or vice versa.
container: {
width: "100%",
aspectRatio: 10 / 3, //height will be "30%" of your width
}
2- Use flex for the jobs percentage can't do. For example, if you have arbitrary size of items in a list and you want them to share equal sizes. Assign each of them with flex: 1
3- Use rem from EStyleSheet instead of pixels. rem is a scale factor. For example, if your rem is 2 and your “11rem” will become “11*2” = “22”. If we make rem proportion to the screen sizes, your UI will scale with any screen sizes.
//we define rem equals to the entireScreenWidth / 380
const entireScreenWidth = Dimensions.get('window').width;
EStyleSheet.build({$rem: entireScreenWidth / 380});
enter code here
//how to use rem
container: {
width: "100%",
aspectRatio: 10 / 3, //height will be "30%"
padding: "8rem", //it'll scale depend on the screen sizes.
}
4- Use scrollView for contents that could potentially scale out of the boxes. For example, a TextView
5- Every time you think about using pixels, consider use rem in method 3.
I am trying to use the Qt Virtual keyboard in my application and one of the things that would be nice to have is to have the keyboard come from the right side of the screen and not basically align itself to use the full available width but say half the width.
Now looking at the InputPanel documentation it says:
The keyboard size is automatically calculated from the available
width; that is, the keyboard maintains the aspect ratio specified by
the current style. Therefore the application should only set the width
and y coordinates of the InputPanel, and not the height.
So, in my application I would like to set the width (say to half the screen width) but that means that the height is calculated by default and the keyboard is too small. What I would like to do is to set the width and ask the keyboard to ignore the aspect ratio settings and basically set the height to the parent height. This is, of course, at odds with the documentation quoted above and I wonder if someone knows of a workaround for this?
My InputPanel looks like:
InputPanel {
id: inputPanel
z: 89
y: active ? parent.height - height : parent.height + 100
width: 300
anchors.right: parent.right
}
With my current code (with qt numeric keypad enabled) it looks like the attached image:
What I would like is to stretch the keyboard in the vertical direction to use the height of the parent window. Currently, it calculates the height based on the current aspect ratio which makes the input panel too small for my needs.
So I have a <canvas> element in which I am drawing things. Vectory things, not pixel-pushing. I would like this to display in the highest quality with the least resources under the largest range of viewing conditions. Specifically, I want to ensure that there is a 1:1 mapping between canvas pixels and device pixels, to eliminate blurry, time-consuming pixel scaling operations.
There are a
few things
I've seen about "Retina" displays (conclusion: devicePixelRatio is your friend), but they all assume viewing at 100% zoom. Equally I've seen things about detecting zoom level, but it seems messy and hacky and imprecise
(not to mention prone to breakage). In particular, imprecision is a problem as the entire point is to get a ratio of precisely 1:1. (Plus I don't actually care about the zoom level itself, only about addressing native pixels.)
So my questions, from pragmatic to philosophical:
What is the preferred way of getting a 1:1 canvas in modern (== released in the past 6 months) browsers? Do I really have to detect the device pixel ratio and the zoom (including checking for zoom changes) and calculate things from there?
If I don't care about resource consumption, is a 2:1 canvas (native res on "Retina" displays, upto 200% zoom elsewhere) worth considering? I'm assuming downscaling is less bad than upscaling.
What are browser vendors' take on this? Are there better solutions being worked on? Or is zoom a detail they think web developers should have no need to worry about? [Answered partially here: many consider it important. There seems some indecision on whether devicePixelRatio should change with zoom level or whether a new property that allows user-specified zoom detection as well as device-specific DPI detection is the way forward.]
How many people regularly browse the web at !100% zoom? How many of them are using high DPI displays? Are there stats? [Answered here: about 10% of gmail users.]
What i found (for Chrome and Internet Explorer) is the very tricky, subtle, differences between
canvas.width, and
canvas.style.width
They are not interchangeable. If you want to manipulate them you must do so very carefully to achieve what you want. Some values are measured in physical pixels. Others are measured in logical pixels (the pixels if the screen was still zoomed to 96 dpi).
In my case i want the Canvas to take up the entire window.
First get the size of the window in logical pixels (which i call "CSS pixels"):
//get size in CSS pixels (i.e. 96 dpi)
var styleWidth = window.innerWidth; //e.g. 420. Not document.body.clientWidth
var styleHeight = window.innerHeight; //e.g. 224. Not document.body.clientHeight
These values are in "logical pixels". When my browser is zoomed in, the amount of "logical pixels" decreases:
Zoom window.innerWidth x windows.innerHeight
==== ======================================
100% 1680 x 859
200% 840 x 448
400% 420 x 224
75% 2240 x 1193
What you then have to do is figure out the "real" size of the window; applying a zoom correction factor. For the moment we'll abstract the function that can figure out the current zoom level (using the TypeScript syntax):
function GetZoomLevel(): number
{
}
With the GetZoomLevel() function, we can calculate the real size of the window, in "physical pixels". When we need to set the width and height of the canvas to the size of the window in physical pixels:
//set canvas resolution to that of real pixels
var newZoomLevel = GetZoomLevel();
myCanvas.width = styleWidth * newZoomLevel; //e.g. 1680. Not myCanvas.clientWidth
myCanvas.height = styleHeight * newZoomLevel; //e.g. 859. Not myCanvas.clientHeight
The critical trick is that internally the canvas will render to a surface that is width by height pixels in size. After that rendering is complete, CSS can come along and shrink or stretch your canvas:
blurring it if CSS makes it larger
throwing away pixel data if CSS makes it smaller.
The final piece is to resize the canvas so that it takes up the entire client area window, using CSS length String:
myCanvas.style.width = styleWidth + "px"; //e.g. "420px"
myCanvas.style.height = styleHeight + "px"; //e.g. "224px"
So the canvas will be positioned and sized correctly in your browser window, yet internally is using full "real" pixel resolution.
GetZoomLevel()
Yes, you need the missing piece, GetZoomLevel. Unfortunately only IE supplies the information. Again, using TypeScript notation:
function GetZoomLevel(): number {
/*
Windows 7, running at 131 dpi (136% zoom = 131 / 96)
Internet Explorer's "default" zoom (pressing Ctrl+0) is 136%; matching the system DPI
screen.deviceYDPI: 130
screen.systemYDPI: 131
screen.logicalYDPI: 96
Set IE Zoom to 150%
screen.deviceYDPI: 144
screen.systemYDPI: 131
screen.logicalYDPI: 96
So a user running their system at 131 dpi, with IE at "normal" zoom,
and a user running their system at 96 dpi, with IE at 136% zoom,
both need to see the same thing; everything zoomed in from our default, assumed, 96dpi.
http://msdn.microsoft.com/en-us/library/cc849094(v=vs.85).aspx
Also note that the onresize event is triggered upon change of zoom factor, therefore you should make sure
that any code that takes into account DPI is executed when the onresize event is triggered, as well.
http://htmldoodads.appspot.com/dimensions.html
*/
var zoomLevel: number;
//If the browser supports the corrent API, then use that
if (screen && screen.deviceXDPI && screen.logicalXDPI)
{
//IE6 and above
zoomLevel = (screen.deviceYDPI / screen.logicalYDPI);
else
{
//Chrome (see http://htmldoodads.appspot.com/dimensions.html)
zoomLevel = window.outerWidth / window.innerWidth; //e.g. 1680 / 420
}
return zoomLevel;
}
Unfortunately no browsers besides IE support telling you:
device dpi
logical dpi
My full TS code is:
function OnWindowResize() {
if (drawingTool == null)
return;
//Zoom changes trigger window resize event
//get size in CSS pixels (i.e. 96 dpi)
var styleWidth = window.innerWidth; //myCanvas.clientWidth;
var styleHeight = window.innerHeight; //myCanvas.clientHeight;
var newZoomLevel = GetZoomLevel();
// myCanvas.width = document.body.clientWidth;
// myCanvas.height = document.body.clientHeight;
//set canvas resolution to that of real pixels
myCanvas.width = styleWidth * newZoomLevel;
myCanvas.height = styleHeight * newZoomLevel;
//myCanvas.clientWidth = styleWidth;
//myCanvas.clientHeight = styleHeight;
myCanvas.style.width = styleWidth + "px"; //styleWidth.toString() + "px";
myCanvas.style.height = styleHeight + "px"; //styleHeight.toString() + "px";
drawingTool.Width = myCanvas.width;
drawingTool.Height = myCanvas.height;
// Only take action if zoom factor has changed (won't be triggered by actual resize of window)
if (newZoomLevel != lastZoomLevel) {
lastZoomLevel = newZoomLevel;
drawingTool.CurrentScaleFactor = newZoomLevel;
}
}
It's also important to tell your drawing code the user's current zoom level, in case you try to hard-code any lengths. E.g. attempting to draw
a 32px box at (50,50)
is wrong. You need to draw a
128 px box at (200,200)
when the zoom factor is 4.
Note: Any code is released into the public domain. No attribution required.
You basically cannot detect zoom level on modern browser versions of Firefox and Chrome:
On Firefox 18+ Mozilla changes the devicePixelRatio value on manual zoom (cmd/ctrl +/-), making it impossible to know whether the browser is in zoom mode or is it a retina device, ignoring what the word DEVICE represents.
On Chrome 27 (Meaning WebKit and Blink) webkitTextSizeAdjust was deprecated on desktops versions of the browser. This was the only bullet proof way to detect zoom in desktop chrome that I am aware of.
There are couple of other ways, but they don't cover all the bases - one uses SVG but is not working in iFrames, the other uses window.inner/outerWidth and is not working when there is a sidebar or the DevTools are open on the side.
Source
How can I make an application with the same ratio width/height? I have an application with width = 800 height = 600. Its width = 1.33*height and when I change the width to 1024 I want the height to change to 768 automatically.
Do you mean your swf change it´s size?
you would need swffit to do it.
if you want to change your content on your movie, you have to calculate the ratio, more or less like this:
ratio = width/height;
newHeight = newWidth/ratio;
and vice-versa for the width…