harupy / snipping-tool

Snipping tool in Python
84 stars 39 forks source link

The precision of the grab is off little bit. #7

Open dingusagar opened 3 years ago

dingusagar commented 3 years ago

First of all, nice work btw.

One issue I am facing is the precision of the image grab is not perfect, the image I captured from the rectangle is off by some amount.

To illustrate it, let's say I am trying to capture the center grey box from this image Screenshot from 2021-11-01 10-49-56

After carefully selecting the rectangle, the image captured looks like this. Looks like it's getting shifted up a little bit.

capture

fpollicelli commented 1 year ago

Hi! I know this issue is old, but today I came across the same problem while using PyQt6, and I hope the solution I found can help some fellow developers in the future. The offset issue you are experiencing is likely caused by a scaling that is not set to 100% (or at least that was the cause of my problem). image

In this case, you'll need to multiply each coordinate by the pixel ratio, using devicePixelRatio

scale_facto r = app.devicePixelRatio()
x1 *= scale_factor
y1 *= scale_factor
x2 *= scale_factor
y2 *= scale_factor
self.repaint()
QtWidgets.QApplication.processEvents()
img = ImageGrab.grab(bbox=(int(x1), int(y1), int(x2), int(y2)))

Have a good one! :)

tterebko commented 1 year ago

Hi @fpollicelli, @dingusagar, I have 100% scale and I still faced about dozen of pixels offset situation. My theory is that the tooling widget is located not in (0,0) relative to the screen. So we save mouseEvent::event.pos() that is position inside of a widget. And we submit it to PIL.ImageGrap() later on that uses these ones as global coordinates. I fixed my problem by introducing two extra variables for absolute position.

     def __init__(self, callback):
        . . . . .
        self.abs_begin = QtCore.QPoint()
        self.abs_end = QtCore.QPoint()

     def mousePressEvent(self, event):
        self.begin = event.pos()
        self.abs_begin = QtGui.QCursor.pos()
        . . . . .

    def mouseReleaseEvent(self, event):
       . . . . .
        self.abs_end = QtGui.QCursor.pos()
        x1 = min(self.abs_begin.x(), self.abs_end.x())
        y1 = min(self.abs_begin.y(), self.abs_end.y())
        x2 = max(self.abs_begin.x(), self.abs_end.x())
        y2 = max(self.abs_begin.y(), self.abs_end.y())

        self.repaint()
        QtWidgets.QApplication.processEvents()
        img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
dingusagar commented 1 year ago

@tterebko , @fpollicelli thanks for sharing these. I will try this out when I get some time.

Hierosme commented 9 months ago

I use the snipping tool for a helloSystem Grab.app project.

I have found a other fixe and repport here my fixe.

1) I use QRect from the selection inside the rePaint function. 2) I have create a function for got the True exact size of the screen (With global menu ) , then creat a screenshot and grop it with coordinate of the selection QRect

That fixe permit a pixel perfect selection

The min / Max permit selection to be done on each direction that a good thing but worng for pixel perfect. and Doing the min max inside mouseReleaseEvent is too late, the True coordinat is Store on the QRect of the selection, no need to compute anything, just use selection_rect.normalized() ....

link here: https://github.com/Hierosme/Utilities/blob/2ffdf443d127be9c15376de6031d36c77e9a552f/Under%20Construction/Grab.app/Resources/widget_snapping.py#L191 https://github.com/Hierosme/Utilities/blob/2ffdf443d127be9c15376de6031d36c77e9a552f/Under%20Construction/Grab.app/Resources/widget_snapping.py#L165

I can provide a Pull request if need

Why it have it bug: The Grabwindow(Object, x, y, w, h) is not pixel perfect because it cause a offset due to the Global Menu, i have fixe by use a Grabwindow(Object).copy(x, y, w, h) The second trouble is the screen, who is the screen ? by let it by default the screen is parent of the MainWindow, ans that is worng, The True screen is the parent of teh Desktop. Then i have create 2 functions on for fixe the self.screen value and a second for got a screenshoot of the entire Destop parent screen and get the size. Like that: self.screen.grabWindow(self.win_id).size() where self.win_id come from QApplication.desktop().winId().

in summary:

self.size(qApp.primaryScreen().grabWindow(QApplication.desktop().winId()).size())