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