Initial commit
This commit is contained in:
		
							parent
							
								
									96d841d4f3
								
							
						
					
					
						commit
						52ae16df3b
					
				|  | @ -0,0 +1,37 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| # Form implementation generated from reading ui file 'C:\Users\lmahler\Desktop\Dump\02_Python\Grail\fillscreen.ui' | ||||
| # | ||||
| # Created by: PyQt4 UI code generator 4.11.4 | ||||
| # | ||||
| # WARNING! All changes made in this file will be lost! | ||||
| 
 | ||||
| from PyQt4 import QtCore, QtGui | ||||
| 
 | ||||
| try: | ||||
|     _fromUtf8 = QtCore.QString.fromUtf8 | ||||
| except AttributeError: | ||||
|     def _fromUtf8(s): | ||||
|         return s | ||||
| 
 | ||||
| try: | ||||
|     _encoding = QtGui.QApplication.UnicodeUTF8 | ||||
|     def _translate(context, text, disambig): | ||||
|         return QtGui.QApplication.translate(context, text, disambig, _encoding) | ||||
| except AttributeError: | ||||
|     def _translate(context, text, disambig): | ||||
|         return QtGui.QApplication.translate(context, text, disambig) | ||||
| 
 | ||||
| class Ui_View(object): | ||||
|     def setupUi(self, View): | ||||
|         View.setObjectName(_fromUtf8("View")) | ||||
|         View.resize(294, 166) | ||||
|         View.setWindowOpacity(0.3) | ||||
|         View.setStyleSheet(_fromUtf8("")) | ||||
| 
 | ||||
|         self.retranslateUi(View) | ||||
|         QtCore.QMetaObject.connectSlotsByName(View) | ||||
| 
 | ||||
|     def retranslateUi(self, View): | ||||
|         View.setWindowTitle(_translate("View", "Grail.exe", None)) | ||||
| 
 | ||||
|  | @ -0,0 +1,25 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>View</class> | ||||
|  <widget class="QWidget" name="View"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>294</width> | ||||
|     <height>166</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Grail.exe</string> | ||||
|   </property> | ||||
|   <property name="windowOpacity"> | ||||
|    <double>0.300000000000000</double> | ||||
|   </property> | ||||
|   <property name="styleSheet"> | ||||
|    <string notr="true"/> | ||||
|   </property> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
|  | @ -0,0 +1,386 @@ | |||
| """ | ||||
| #*********************************# | ||||
| #*           Grail               *# | ||||
| #*********************************# | ||||
| #*   Author: Lukas Mahler        *# | ||||
| #*   Copyright: (C)2018-2020     *# | ||||
| #*   Version: 0.5.0              *# | ||||
| #*   Date: 25.12.2020            *# | ||||
| #*********************************# | ||||
| # Instant Upload Screenshot-Tool  # | ||||
| # based on the idea of Gyazoo.    # | ||||
| # -> MS Windows only!             # | ||||
| #*********************************# | ||||
| """ | ||||
| 
 | ||||
| __author__ = "Lukas Mahler" | ||||
| __copyright__ = "Copyright 2018-2021" | ||||
| __version__ = "0.5.1" | ||||
| __date__ = "16.01.2021" | ||||
| __email__ = "lm@ankerlab.de" | ||||
| __status__ = "Development" | ||||
| 
 | ||||
| 
 | ||||
| try: | ||||
|     # Defaults | ||||
|     import os | ||||
|     import sys | ||||
|     import webbrowser | ||||
|     from io import BytesIO | ||||
|     from shutil import rmtree | ||||
|     from getpass import getpass | ||||
|     from tempfile import gettempdir | ||||
|     from socket import create_connection | ||||
|     from configparser import ConfigParser | ||||
|     from time import gmtime, strftime, sleep | ||||
| 
 | ||||
|     # GUI | ||||
|     from PyQt4 import QtGui, QtCore, Qt | ||||
|     from PyQt4.QtCore import QProcess, QPoint | ||||
|     from fillscreen import Ui_View | ||||
| 
 | ||||
|     # Customs | ||||
|     import win32gui | ||||
|     import win32clipboard | ||||
|     import paramiko | ||||
|     from desktopmagic.screengrab_win32 import getRectAsImage | ||||
| 
 | ||||
| except ImportError as e: | ||||
|     print("ERROR: Missing Module |||", e) | ||||
|     sleep(10) | ||||
|     os.system("PAUSE>NUL") | ||||
|     sys.exit() | ||||
| 
 | ||||
| 
 | ||||
| ################################################################################################### | ||||
| ################################################################################################### | ||||
| 
 | ||||
| 
 | ||||
| class Fillscreen(QtGui.QWidget, Ui_View): | ||||
| 
 | ||||
|     def __init__(self, parent=None): | ||||
|         QtGui.QWidget.__init__(self, parent) | ||||
| 
 | ||||
|         ############################################################################################### | ||||
|         ############################################################################################### | ||||
| 
 | ||||
|         # Bastelstüble wegen Transparentem Window welches aber den Input auf unterliegendes blockieren soll | ||||
| 
 | ||||
|         # self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True) | ||||
|         # self.setBackgroundRole(QtGui.QPalette.Base) | ||||
|         # self.setAttribute(Qt.Qt.WA_NoSystemBackground, True) | ||||
|         self.setWindowFlags( | ||||
|             QtCore.Qt.WindowStaysOnTopHint | | ||||
|             QtCore.Qt.FramelessWindowHint | | ||||
|             QtCore.Qt.X11BypassWindowManagerHint | ||||
|         ) | ||||
|         # self.istransparent = True | ||||
| 
 | ||||
|         self.setupUi(self) | ||||
| 
 | ||||
|         # self.setWindowOpacity(0.3) | ||||
|         # Qcolor = "rgba(255, 255, 255, 0)" | ||||
|         # self.setStyleSheet("background-color:{0}".format(Qcolor)) | ||||
| 
 | ||||
|         ############################################################################################### | ||||
|         ############################################################################################### | ||||
| 
 | ||||
|         # Change Cursor to select tool on Startup | ||||
|         self.change_cursor() | ||||
|         self.begin = QtCore.QPoint() | ||||
|         self.end = QtCore.QPoint() | ||||
|         self.startingpoint = QtCore.QPoint() | ||||
|         self.endpoint = QtCore.QPoint() | ||||
| 
 | ||||
|     def showFullscreen(self): | ||||
|         self.showMaximized() | ||||
| 
 | ||||
|     # https://stackoverflow.com/a/44468898/5593051 | ||||
|     def paintEvent(self, event): | ||||
|         qp = QtGui.QPainter(self) | ||||
|         # br = QtGui.QBrush(QtGui.QColor(100, 10, 10, 40)) | ||||
|         qp.setPen(QtGui.QPen(QtCore.Qt.red, 3)) | ||||
|         qp.drawRect(QtCore.QRect(self.begin, self.end)) | ||||
| 
 | ||||
|     def mousePressEvent(self, event): | ||||
|         if event.button() == QtCore.Qt.RightButton: | ||||
|             print("DEBUG: Exiting on Right Click Request") | ||||
|             app.quit() | ||||
|         else: | ||||
|             self.begin = event.pos() | ||||
|             self.end = event.pos() | ||||
|             # self.startingpoint = event.pos() | ||||
|             self.startingpoint = get_real_pos() | ||||
|             # print("Startpoint:",self.startingpoint) | ||||
|             self.update() | ||||
| 
 | ||||
|     def mouseMoveEvent(self, event): | ||||
|         self.end = event.pos() | ||||
|         self.update() | ||||
| 
 | ||||
|     def mouseReleaseEvent(self, event): | ||||
|         self.begin = event.pos() | ||||
|         self.end = event.pos() | ||||
|         self.endpoint = event.pos() | ||||
|         self.endpoint = get_real_pos() | ||||
|         # print("Endpoint:",self.endpoint) | ||||
|         self.update() | ||||
|         do_screen(self.startingpoint, self.endpoint) | ||||
|         app.quit() | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def change_cursor(): | ||||
|         # http://doc.qt.io/archives/qt-4.8/qcursor.html | ||||
|         QtGui.QApplication.setOverrideCursor(QtCore.Qt.CrossCursor) | ||||
|     # QtGui.QApplication.restoreOverrideCursor() | ||||
| 
 | ||||
| 
 | ||||
| ################################################################################################### | ||||
| ###################################################################################################	 | ||||
| 
 | ||||
| 
 | ||||
| def read_ini(rootdir): | ||||
|     myini = {} | ||||
|     config = ConfigParser() | ||||
|     try: | ||||
|         config.read_file(open(rootdir+'settings.ini')) | ||||
|     except Exception as e: | ||||
|         print("ERROR: Couldn't locate a 'settings.ini' |||", e) | ||||
|         # Creating new blank ini | ||||
|         with open(rootdir+'settings.ini', 'w') as f: | ||||
|             txt = "[MAIN]\n" \ | ||||
|                   "# MODE EITHER ONLINE OR OFFLINE\nMODE = OFFLINE\n\n\n" \ | ||||
|                   "# IF MODE IS ONLINE PROVIDE THE SFTP INFO BELOW\n" \ | ||||
|                   "[ONLINE]\n\n" \ | ||||
|                   "# URL EITHER IS AN IP OR A FQDN\nURL = \n\n" \ | ||||
|                   "# PORT IS THE SSH PORT\nPORT = 22\n\n" \ | ||||
|                   "# THE TIME IN SECONDS TO TRY TO CONNECT\nTIMEOUT = 5\n\n" \ | ||||
|                   "# YOUR SSH USER, THIS CAN BE BLANK TO GET ASKED INTERACTIVELY\nUSR = \nPASS = \n\n" \ | ||||
|                   "# THE LOCAL SERVER PATH WHERE TO PUT THE SCREENSHOT\nPTH = ./Screens/\n\n" \ | ||||
|                   "# THIS SHOULD BE THE FULL ONLINE URL\nWHERE = \n\n\n" \ | ||||
|                   "# IF MODE IS OFFLINE YOU MAY CHANGE THE SETTINGS BELOW\n" \ | ||||
|                   "[OFFLINE]\n\n" \ | ||||
|                   "WHERE = " | ||||
|             f.write(txt) | ||||
| 
 | ||||
|     mode = config.get('MAIN', 'MODE') | ||||
|     if mode == "ONLINE": | ||||
|         url = config.get('ONLINE', 'URL') | ||||
|         port = int(config.get('ONLINE', 'PORT')) | ||||
|         timeout = int(config.get('ONLINE', 'TIMEOUT')) | ||||
|         usr = config.get('ONLINE', 'USR') | ||||
|         passw = config.get('ONLINE', 'PASS') | ||||
|         pth = config.get('ONLINE', 'PTH') | ||||
|         where = config.get('ONLINE', 'WHERE') | ||||
|         myini.update( | ||||
|             mode    = mode, | ||||
|             URL     = url, | ||||
|             PORT    = port, | ||||
|             TIMEOUT = timeout, | ||||
|             USR     = usr, | ||||
|             PASS    = passw, | ||||
|             PTH     = pth, | ||||
|             WHERE   = where | ||||
|         ) | ||||
|     elif mode == "OFFLINE": | ||||
|         where = config.get('OFFLINE', 'WHERE') | ||||
|         myini.update( | ||||
|             mode	= mode, | ||||
|             WHERE	= where | ||||
|         ) | ||||
|     else: | ||||
|         print("ERROR: you seem to be having a corrupt or broken 'settings.ini' please recheck!") | ||||
| 
 | ||||
|     return myini | ||||
| 
 | ||||
| 
 | ||||
| def get_real_pos(): | ||||
|     flags, hcursor, (x, y) = win32gui.GetCursorInfo() | ||||
|     return {"x": x, "y": y} | ||||
| 
 | ||||
| 
 | ||||
| def calc_frame(startinspot, endspot): | ||||
|     # If dragging began from top | ||||
|     if startinspot['x'] < endspot['x']: | ||||
|         left = startinspot['x'] | ||||
|         right = endspot['x'] | ||||
|         top = startinspot['y'] | ||||
|         bottom = endspot['y'] | ||||
|     # If dragging began from bottom | ||||
|     else: | ||||
|         left = endspot['x'] | ||||
|         right = startinspot['x'] | ||||
|         top = endspot['y'] | ||||
|         bottom = startinspot['y'] | ||||
| 
 | ||||
|     # print("DEBUG:  Start:",left,"x",top) | ||||
|     # print("DEBUG:  End  :",right,"x",bottom) | ||||
| 
 | ||||
|     if abs(left-right) <= 10 and abs(top-bottom) <= 10: | ||||
|         coordinates = None | ||||
|     else: | ||||
|         coordinates = (left, top, right+1, bottom+1) | ||||
| 
 | ||||
|     return coordinates | ||||
| 
 | ||||
| 
 | ||||
| def do_screen(start, end): | ||||
|     """ This Function somehow is the Brain now """ | ||||
|     # QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId()).save('screenshot.jpg', 'jpg') | ||||
|     # Hide all windows to take a Screenshot of the underlying interface | ||||
|     for window in windows: | ||||
|         window.hide() | ||||
|     frame = calc_frame(start, end) | ||||
|     print("Took Snapshot on Coordinates:", frame) | ||||
|     # https://github.com/python-pillow/Pillow/issues/1547 | ||||
|     # https://github.com/ludios/Desktopmagic/blob/master/desktopmagic/screengrab_win32.py | ||||
|     # im = ImageGrab.grab(bbox=frame) | ||||
| 
 | ||||
|     if not frame: | ||||
|         print("ERROR: Screenshots below 5x5 px not allowed, exiting...") | ||||
|         sleep(5) | ||||
|         return | ||||
| 
 | ||||
|     im = getRectAsImage(frame) | ||||
|     send_to_clipboard(im) | ||||
|     timestamp = strftime("%Y-%m-%d_%H.%M.%S") | ||||
| 
 | ||||
|     tempdir = gettempdir()+"\\Screens" | ||||
|     # print("DEBUG:",tempdir) | ||||
|     if not os.path.exists(tempdir): | ||||
|         os.makedirs(tempdir) | ||||
|     tempsave = '{0}\\{1}.png'.format(tempdir, 'Screen_'+timestamp) | ||||
|     im.save(tempsave) | ||||
| 
 | ||||
|     if settings['MODE'] == "ONLINE": | ||||
|         upload_to_url(tempsave) | ||||
|     else: | ||||
|         save_to_local(tempsave) | ||||
| 
 | ||||
|     clean_temp(tempdir) | ||||
| 
 | ||||
| 
 | ||||
| # https://stackoverflow.com/questions/34322132/copy-image-to-clipboard-in-python3 | ||||
| def send_to_clipboard(im=None): | ||||
|     if im: | ||||
|         output = BytesIO() | ||||
|         im.convert("RGB").save(output, "BMP") | ||||
|         data = output.getvalue()[14:] | ||||
|         output.close() | ||||
|         win32clipboard.OpenClipboard() | ||||
|         win32clipboard.EmptyClipboard() | ||||
|         win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data) | ||||
|         win32clipboard.CloseClipboard() | ||||
|         print("Copied to Clipboard") | ||||
|     else: | ||||
|         win32clipboard.EmptyClipboard() | ||||
|         print("Cleared Clipboard") | ||||
| 
 | ||||
| 
 | ||||
| # https://stackoverflow.com/questions/432385/sftp-in-python-platform-independent | ||||
| def upload_to_url(tempsave): | ||||
| 
 | ||||
|     # SFTP INFO | ||||
|     url 	= settings['URL'] | ||||
|     port 	= settings['PORT'] | ||||
|     timeout = settings['TIMEOUT'] | ||||
|     usr 	= settings['USR'] | ||||
|     passw 	= settings['PASS'] | ||||
|     pth		= settings['PTH'] + os.path.basename(tempsave) | ||||
|     where	= settings['WHERE'] + os.path.basename(tempsave) | ||||
| 
 | ||||
|     # Test Connection first or go into fallback | ||||
|     try: | ||||
|         s = create_connection((url, port), timeout) | ||||
|         s.close() | ||||
|     except Exception as e: | ||||
|         print("ERROR: Connection failed. |||", e) | ||||
|         print("\nFalling back to local save...") | ||||
|         save_to_local(tempsave, fallback=1) | ||||
|         return | ||||
| 
 | ||||
|     # If not provided ask for Username & Password | ||||
|     if not usr: | ||||
|         usr = input("Please provide your Username: ") | ||||
| 
 | ||||
|     if not passw: | ||||
|         passw = getpass("Please provide the password for {0}: ".format(usr)) | ||||
| 
 | ||||
|     try: | ||||
|         transport = paramiko.Transport((url, port)) | ||||
|         transport.connect(username=usr, password=passw) | ||||
|     except Exception as e: | ||||
|         print("ERROR: SSH2 negotiation or authentication failed. |||", e) | ||||
|         print("\nFalling back to local save...") | ||||
|         save_to_local(tempsave, fallback=1) | ||||
|         return | ||||
| 
 | ||||
|     print("Starting to Upload...") | ||||
|     sftp = paramiko.SFTPClient.from_transport(transport) | ||||
|     sftp.put(tempsave, pth) | ||||
|     sftp.close() | ||||
|     transport.close() | ||||
| 
 | ||||
|     print("Screen was Uploaded: URL: " + where) | ||||
|     open_url(where) | ||||
| 
 | ||||
| 
 | ||||
| def save_to_local(tempsave, fallback=None): | ||||
| 
 | ||||
|     if not fallback: | ||||
|         where = settings['WHERE'] | ||||
|     else: | ||||
|         print("    -> Did fallback!") | ||||
|         config = ConfigParser() | ||||
|         config.read_file(open(root+'_settings.ini')) | ||||
|         where = config.get('OFFLINE', 'WHERE') | ||||
| 
 | ||||
|     if not where: | ||||
|         print("No local Screens folder provided, creating one...") | ||||
|         where = os.path.dirname(os.path.abspath(__file__))+"\\Screens" | ||||
| 
 | ||||
|     print("OFFLINE MODE: Screenshot Folder:", where) | ||||
| 
 | ||||
|     if not os.path.exists(where): | ||||
|         os.makedirs(where) | ||||
| 
 | ||||
|     src = tempsave | ||||
|     dst = where + "\\" + os.path.basename(tempsave) | ||||
|     # print("Source:",src,"\nDestination:",dst) | ||||
|     os.rename(src, dst) | ||||
| 
 | ||||
| 
 | ||||
| def open_url(where): | ||||
|     webbrowser.open(where, new=0, autoraise=True) | ||||
| 
 | ||||
| 
 | ||||
| def clean_temp(tempdir): | ||||
|     if os.path.exists(tempdir): | ||||
|         print("Cleaning up the tempdir [", tempdir, "]...") | ||||
|         rmtree(tempdir, ignore_errors=True) | ||||
| 
 | ||||
| 
 | ||||
| ################################################################################################### | ||||
| ################################################################################################### | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
| 
 | ||||
|     root = os.path.dirname(os.path.abspath(__file__))+"\\" | ||||
|     settings = read_ini(root) | ||||
|     if os.path.exists(root+"qt.conf"): | ||||
|         try: | ||||
|             os.remove(root+"qt.conf") | ||||
|         except Exception as e: | ||||
|             print("DEBUG:", e) | ||||
|     app = QtGui.QApplication(sys.argv) | ||||
|     windows = [] | ||||
| 
 | ||||
|     # https://stackoverflow.com/questions/51058236/create-qt-windows-based-on-number-of-list-items | ||||
|     for i in range(QtGui.QApplication.desktop().screenCount()): | ||||
|         topLeft = QtGui.QApplication.desktop().screenGeometry(i).topLeft() | ||||
|         window = Fillscreen() | ||||
|         window.move(topLeft) | ||||
|         window.showFullscreen() | ||||
|         windows.append(window) | ||||
|     app.exec_() | ||||
|  | @ -0,0 +1,11 @@ | |||
| [ship] | ||||
| param = --onefile --windowed | ||||
| icon = C:\Users\lukas\Desktop\Dump\08_Resources\camera.ico | ||||
| 
 | ||||
| [debug] | ||||
| param = --onefile | ||||
| icon = C:\Users\lukas\Desktop\Dump\08_Resources\camera.ico | ||||
| 
 | ||||
| [test] | ||||
| param = --onedir --noupx --add-binary msvcp140.dll;. | ||||
| icon = C:\Users\lukas\Desktop\Dump\08_Resources\camera.ico | ||||
|  | @ -0,0 +1,105 @@ | |||
| """ | ||||
| This subfile to Grail handles window settings and calculations. | ||||
| -> Only works on MS Windows! | ||||
| """ | ||||
| 
 | ||||
| __author__ = "Lukas Mahler" | ||||
| __copyright__ = "Copyright 2018-2020" | ||||
| __version__ = "1.0.0" | ||||
| __date__ = "25.11.2020" | ||||
| __email__ = "lm@ankerlab.de" | ||||
| __status__ = "Development" | ||||
| 
 | ||||
| 
 | ||||
| import win32api | ||||
| import win32gui | ||||
| 
 | ||||
| 
 | ||||
| def getCursorPos(): | ||||
|     flags, hcursor, (x, y) = win32gui.GetCursorInfo() | ||||
|     return {"x": x, "y": y} | ||||
| 
 | ||||
| 
 | ||||
| def getMonitors(): | ||||
|     monitors = [] | ||||
|     for hMonitor, hdcMonitor, pyRect in win32api.EnumDisplayMonitors(): | ||||
|         monitors.append(pyRect) | ||||
|     #print(monitors) | ||||
|     return monitors | ||||
| 
 | ||||
| 
 | ||||
| def getResolution(monitors): | ||||
|     resolutions = {} | ||||
|     index = 0 | ||||
|     for monitor in monitors: | ||||
|         x_from	= monitor[0] | ||||
|         x_to	= monitor[2] | ||||
|         y_from	= monitor[1] | ||||
|         y_to	= monitor[3] | ||||
|         res_x = x_to - x_from | ||||
|         res_y = y_to - y_from | ||||
|         res = (res_x, res_y) | ||||
|         resolutions['Monitor'+str(index)] = res | ||||
|         index += 1 | ||||
| 
 | ||||
|     return resolutions | ||||
| 
 | ||||
| 
 | ||||
| def getScreenInfo(): | ||||
|     return {"x": win32api.GetSystemMetrics(0), "y": win32api.GetSystemMetrics(1)} | ||||
| 
 | ||||
| 
 | ||||
| def getStartingpoint(): | ||||
|     return getCursorPos() | ||||
| 
 | ||||
| 
 | ||||
| def getEndpoint(): | ||||
|     return getCursorPos() | ||||
| 
 | ||||
| 
 | ||||
| def getRectangle(): | ||||
| 
 | ||||
|     # Source: https://stackoverflow.com/a/41930485/5593051 | ||||
|     state_left = win32api.GetKeyState(0x01)  # Left button down = 0 or 1. Button up = -127 or -128 | ||||
|     state_right = win32api.GetKeyState(0x02)  # Right button down = 0 or 1. Button up = -127 or -128 | ||||
| 
 | ||||
|     while True: | ||||
|         a = win32api.GetKeyState(0x01) | ||||
|         b = win32api.GetKeyState(0x02) | ||||
| 
 | ||||
|         if a != state_left:  # Button state changed | ||||
|             state_left = a | ||||
|             if a < 0: | ||||
|                 print('Left Button Pressed') | ||||
|                 pos_start = getStartingpoint() | ||||
|             else: | ||||
|                 print('Left Button Released') | ||||
|                 pos_end = getEndpoint() | ||||
|                 break | ||||
| 
 | ||||
|     print(pos_start) | ||||
|     print(pos_end) | ||||
| 
 | ||||
|     calcScreenshotFrame(pos_start, pos_end) | ||||
| 
 | ||||
| 
 | ||||
| def calcScreenshotFrame(pos_start, pos_end): | ||||
|     # If dragging began from top | ||||
|     if pos_start['x'] < pos_end['x']: | ||||
|         left = pos_start['x'] | ||||
|         right = pos_end['x'] | ||||
|         top = pos_start['y'] | ||||
|         bottom = pos_end['y'] | ||||
|     # If dragging began from bottom | ||||
|     else: | ||||
|         left = pos_end['x'] | ||||
|         right = pos_start['x'] | ||||
|         top = pos_end['y'] | ||||
|         bottom = pos_start['y'] | ||||
| 
 | ||||
|     print("Screenshot needs to be taken from: x:", left, "->", right, "and y:", top, "->", bottom) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     print("This is a Subfile from grail, it can't be executed on its own.") | ||||
|     exit() | ||||
		Loading…
	
		Reference in New Issue