erstmal vorweg, ich bin neuling was Python betrifft und hoffe ihr könnt mir helfen.
Mein Problem ganz kurz angesprochen, ich suche eine Möglichkeit meine Python Anwendung + Tkinter GUI mit einem unter Windows globalen Hotkey anzusprechen, egal ob die Anwedung den Focus hat oder nicht, oder auch versteckt ist oder nicht.
Soll heißen, auch wenn meine GUI versteckt ist soll sobald der Hotkey gedrückt wird (zb. "STRG+<"), die GUI wieder erscheinen und oder zu der Mausposition wandern. Warum das ganze? Das werde ich weiter unten ausführlicher erklären, da ich versuche ein von mir geschriebenes AutoIT Script in Python umzubauen.
Versucht hab ich schon vieles, wie beispielsweise hier: https://stackoverflow.com/questions/207 ... th-tkinter
Und noch unzählige weitere. Habe daher leider keinen Überblick mehr was ich probiert habe und was nicht.
Mein letzter Versuch war die Einbindung eines Python Moduls namens "keyboard" ( https://github.com/boppreh/keyboard ).
Problem hier war allerdings, dass dieses Modul seinen eigenen loop (keyboard.wait()) brauch um auf den Hotkey zu achten.
Tkinter benötigt auch seinen eigenen loop (mainloop()) soweit ich es verstanden habe, darum hab ich versucht beide loops per theading aktiv zu haben.
Überraschenderweise funktioniert das so halbwegs.
Meine GUI ist da und gleichzeitig reagiert mein Hotkey, egal ob die GUI im Focus ist oder nicht.
Auch wenn ich die GUI über das "X" schließe (egt verstecke ich meine GUI nur), funktioniert der Hotkey.
Leider funktioniert mygui.GUIShow() nicht, sobald ich den Hotkey gedrückt habe.
Kann ich diese Methode nicht ausführen, weil meine mygui.mainloop() über den anderen Thread gestartet wurde?
Damit ich nicht noch einen Roman schreibe und noch mehr verwirre, zeige ich euch einfach mal meinen Quellcode den ich bisher habe.
Weitere erklärung was ich letztendlich machen will, schreibe ich weiter unten.
Code: Alles auswählen
# coding: utf8
# Bibliotheken
import Tkinter as Tk
import keyboard # https://github.com/boppreh/keyboard
import time
from threading import Thread
##########################################################################################################
# --- MyGUI - Klasse -------------------------------------------------------------------------------
class MyGUI(object):
def __init__(self, title="", width=50, height=50, x=0, y=0):
""" Konstruktor der GUI """
# Erstellt GUI mit namen rootGUI
self.rootGUI = Tk.Tk()
# Überträgt übergebene Variablen
self.width = width
self.height = height
self.x = x
self.y = y
# Erstellt eine GUI ohne Windows Rahmen und bestimmt dessen Größe/Position
self.rootGUI.geometry("%dx%d+%d+%d" % (self.width, self.height, self.x, self.y))
self.rootGUI.wm_overrideredirect(True)
# Hintergrundfarbe der GUI
self.rootGUI.configure(background="#36393e")
# GUI Rahmen wird erstellt
self.gui_border = Tk.Canvas(self.rootGUI, width=self.width, height=25, bg = '#202225', highlightthickness=0) # Erstellt den Canvas Bereich
self.gui_border.place(x=0, y=0, width=self.width, height=25) # place() kümmert sich um die Plazierung des Rahmens
self.gui_border.create_text(300, 12, fill="#ffffff", font="Consolas 16 bold", text=title) # Erstellt Fenstertitel in dem Canvas für den Rahmen
self.gui_border.bind("<ButtonPress-1>", self.StartMove) # Sorgt für das Verschieben des Fensters
self.gui_border.bind("<ButtonRelease-1>", self.StopMove) # Sorgt für das Verschieben des Fensters
self.gui_border.bind("<B1-Motion>", self.OnMotion) # Sorgt für das Verschieben des Fensters
# Erstellt "X" zum schließen der GUI
self.gui_close_label = Tk.Label(self.rootGUI, text="X", font=("Helvetica", 12), fg="#a6a7a8", bg="#202225",)
self.gui_close_label.place(x=600, y=0)
self.gui_close_label.bind("<Button-1>", self.GUIHide)
# Erstellt ein Button zum schließen der GUI
self.button = Tk.Button(self.rootGUI, text='Quit', command=self.rootGUI.quit)
self.button.place(x=50, y=50, width=80, height=50)
# ------------------------------------------------------------------------------------------------------------------
def StartMove(self, event):
""" StartMove() Sorgt für das korrekte setzen der Startparameter """
self.x = event.x
self.y = event.y
def StopMove(self, event):
""" StopMove() Setzt beim stoppen die Klassenwerte für x/y auf None """
self.x = None
self.y = None
def OnMotion(self, event):
""" OnMotion() Kümmert sich um die Berechnung der neuen Fensterkoordinaten beim ziehen mit der Maus """
deltax = event.x - self.x # Zieht von der Event x Koordinate die GUI x Koordinate ab
deltay = event.y - self.y # Zieht von der Event y Koordinate die GUI y Koordinate ab
x = self.rootGUI.winfo_x() + deltax # Berechnet neue x Koordinate
y = self.rootGUI.winfo_y() + deltay # Berechnet neue y Koordinate
self.rootGUI.geometry("+%s+%s" % (x, y)) # Setzt neue GUI Koordinaten
def GUIHide(self, event):
""" GUIHide() versteckt die GUI """
self.rootGUI.withdraw()
print("GUI wurde versteckt.")
def GUIShow(self):
""" GUIShow() zeigt die GUI """
self.rootGUI.update()
self.rootGUI.deiconify()
print("GUI wird angezeigt.")
def run(self):
""" run() ist die Hauptschleife der GUI """
print("GUI Mainloop wird gestartet.")
self.rootGUI.mainloop()
# return self.rootGUI
# --- AutoFan GUI - Klasse -------------------------------------------------------------------------------
##########################################################################################################
def sleeper(i):
print "thread %d sleeps for 5 seconds" % i
time.sleep(5)
print "thread %d woke up" % i
def HotkeyPressed():
print("Hotkey pressed")
mygui.GUIShow()
# mygui.update()
# mygui.deiconify()
print("Hotkey pressed - exit")
def MyGUIHotkey():
print("Thread für Hotkey gestartet.")
keyboard.add_hotkey('ctrl+<', HotkeyPressed)
keyboard.wait()
def MyGUIThread():
print("Thread für MyGUI gestartet.")
mygui.run()
# Main Function
if __name__ == '__main__':
"""
# Thread test!
for i in range(10):
t = Thread(target=sleeper, args=(i,))
t.start()
"""
mygui = MyGUI("MyGUI - Python Edition", 620, 500, 100, 100)
# Erstelle Thread mit Hotkey Funktion
threadHotkey = Thread(target=MyGUIHotkey)
threadHotkey.start()
# Thread für GUI wird erstellt
threadGUI = Thread(target=MyGUIThread)
threadGUI.start()
# mygui = MyGUI("AutoFan v2 - Python Edition", 620, 500, 100, 100).run()
Sooo, hier möchte ich euch noch kurz schildern was ich wirklich vor habe, damit ihr euch was darunter vorstellen könnt.
Müsst aber nicht den ganzen Text lesen.
Ich habe ein AutoIT Script geschrieben, welches im Hintergrund bleibt, auf den Hotkey wartet und dann den User helfen soll, jenachdem was an Daten vorhanden sind, bzw gefunden wurden.
Der User markiert einen beliebigen Text, beispielsweise mit der Maus aus einer Excelartig aufgebauten Anwendung (ein Eventprogramm mit vielen Events drin). Sobald der Hotkey gedrückt wurde, macht das AutoIT Script intern ein "STRG+C" und liest das Clipboard ein.
Anschließend werten verschiedene Funktionen das Clipboard aus und prüfen was an Daten vorhanden sind, bzw gefunden wurden.
Beispielsweise ein Datum wird erkannt, Leitungsführungen werden erkannt, etc etc.
Das ganze habe ich in einzelne Module aufgegliedert.
Jenachdem welche Daten jetzt erkannt wurden im Clipboard, baut das AutoIT Script die verschiedenen Module auf, so damit in der AutoIT GUI "nur noch" die Module angezeigt werden, die mit den im Clipboard vorhandenen Daten etwas anfangen können, bzw weiter verwendet werden können (beispiel: wurde im Clipboard KEIN Datum erkannt, wird dieses Modul nicht angezeigt).
Wurden alle benötigten Module erkannt, baut sich die GUI auf (das heißt auch, das meine GUI nie die gleiche größe besitzt, sondern mal größer mal kleiner ausfallen kann). Zudem gibt es zwei Optionen die der User setzen kann, damit das AutoIT Script, bzw die GUI nach dem Hotkey an der Mausposition erscheint, oder eine feste Fensterposition bekommt (indem die aktuelle Fensterposition, bei setzen der Option gespeichert wird). Eine weitere Option die der User nutzen kann, ist ob das Fenster "on Top" bleibt, also immer über andere Fenster liegt und sich nicht in den Hintergrund schieben lässt.
Das AutoIT Script funktioniert sehr gut und ist mittlerweile sehr umfangreich geworden.
Darum bin ich auch an meine Grenzen gestoßen. Das AutoIT Script reicht von der performance her, aber würde gerne mehr rausholen.
Zudem möchte ich Threading nutzen und mehrere Arbeitsabläufe parallel laufen lassen. Bei AutoIT habe ich das mithilfe von forking versucht, was zwar gut klappt, aber nicht echtes threading ist und auch etwas umständlich in der Nutzung ist. Desweiteren würde ich gerne mit der objektorientierten Programmierung anfangen und AutoIT ist nicht objektorientiert, oder ich weiß nicht wie.
Ich hoffe ich konnte mein Vorhaben verständlich genug formulieren und hoffe ihr könnt mir bei meinem Problem weiterhelfen.
Vielen Dank schon mal im Voraus.
Grüße
borsTiHD
PS: Aus gewissen Gründen kann ich leider nur auf Python 2.7.x zurückgreifen.