387 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
#*********************************#
 | 
						|
#*           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_()
 |