pystray windows

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ich benutze pystray um ein Icon in der Taskleiste anzeigen zu lassen. Mein Code:

Code: Alles auswählen

import pystray
from PIL import Image, ImageDraw
from iptool import IpInfo
import threading
import time

def after_click ():
	quit ()

def loop (ipinfo, icon):
	while True:
		ipinfo.get_info ()
		
		convert = Image.open(ipinfo.flag_path)
		convert.save (ipinfo.flag_path+'.bmp')
		
		icon.icon = Image.open(ipinfo.flag_path+'.bmp')
		time.sleep (1)
				
if __name__ == '__main__':
	ipinfo = IpInfo ()
	ipinfo.get_info ()

	convert = Image.open(ipinfo.flag_path)
	convert.save (ipinfo.flag_path+'.bmp')
	
	icon = pystray.Icon("flag", Image.open(ipinfo.flag_path+'.bmp'), "",
		menu=pystray.Menu(
		pystray.MenuItem("Exit", after_click)))
	

	thread = threading.Thread(target=loop, args=(ipinfo, icon,))
	thread.start()
	icon.run ()

Unter meinem Debian, wo ich auch drauf programmiere läuft das Script ohne Probleme und es wird mir mein Image angezeigt. Unter Windows bricht das Programm ab. Ich dachte zu erst, vielleicht liegt es da dran, das meine Imagedatei ein .png ist und Windows aber eine .bmp Datei erwartet. Aus dem Grund sieht man im Code ja auch, das ich aus Testzwecken einfach mal in .bmp umgewandelt habe. Bekomme aber leider die gleiche Fehlermeldung.

Code: Alles auswählen

Exception in thread Thread-2 (setup_handler):
Traceback (most recent call last):
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_base.py", line 396, in setup_handler
    self.visible = True
    ^^^^^^^^^^^^
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_base.py", line 183, in visible
    self._update_icon()
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_win32.py", line 72, in _update_icon
    self._assert_icon_handle()
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_win32.py", line 349, in _assert_icon_handle
    self._icon_handle = win32.LoadImage(
                        ^^^^^^^^^^^^^^^^
  File "C:\Users\u0\AppData\Local\Programs\Python\Python311\Lib\site-packages\pystray\_util\win32.py", line 203, in _err
    raise ctypes.WinError()
OSError: [WinError 0] Der Vorgang wurde erfolgreich beendet.

Kann da jemand helfen?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@The Hit-Man: Da wird GUI aus verschiedenen Threads aus verändert, das ist auch unter Linux nicht korrekt. Ich denke auch das schreiben von Bilddateien an einer Stelle wo die wahrscheinlich beim Installieren einer Bibliothek hinterlegt wurde ist nicht robust. Es muss ja nicht sein, dass man dort Schreibrechte hat. Ich sehe auch insgesamt nicht den Sinn das zu speichern wenn man es gleich danach wieder lädt‽
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

`ImageDraw` wird importiert, aber nirgends verwendet.

Das Hauptprogramm sollte in einer Funktion stecken, damit die ganzen Namen da nicht im Modul definiert werden.

`Icon` hat eine nicht-blockierende Methode, so dass man selbst gar keinen Thread benötigt.

`after_click()` ist einfach nur ein anderer Name für `quit()`. Da hätte man auch ``after_click = quit`` schreiben können. Allerdings gibt es `quit()` gar nicht. Das ist für die interaktive Python-Shell und nicht für Programme. Es ist auch nicht wirklich sauber irgendwo einfach das Programm hart abzubrechen, ohne Rücksicht auf anderen Code in anderen Threads und man nimmt sich damit auch die Möglichkeit am Ende noch Code auszuführen der am Ende der Hauptfunktion steht. Da der Wunsch für den Programmabbruch über Threads hinweg kommuniziert werden muss, bieten sich die Mittel aus `threading` an. Hier ein `Event`.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
from threading import Event

from iptool import IpInfo
from PIL import Image
from pystray import Icon, Menu, MenuItem


def main():
    ip_info = IpInfo()
    ip_info.get_info()
    stop_requested = Event()
    icon = Icon(
        "flag",
        Image.open(ip_info.flag_path),
        "",
        menu=Menu(MenuItem("Exit", stop_requested.set)),
    )
    icon.run_detached()

    while not stop_requested.wait(1):
        ip_info.get_info()
        icon.icon = Image.open(ip_info.flag_path)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ich sehe auch insgesamt nicht den Sinn das zu speichern wenn man es gleich danach wieder lädt‽
Ich fand im Netz kein anderes Beispiel. Mich hat es auch gewundert, das man mit PIL nicht einfach das Objekt umwandeln konnte/kann?
Zwischenstand (ungetestet):
Danke für den Code aber der funktioniert nicht, also es passiert einfach nichts. Es wird auch kein Image in der Taskleiste angezeigt.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Fehlt ein :

Code: Alles auswählen

icon.run()
Dann läuft es unter Linux ...
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Unter Windows läuft es dann wieder nicht ... Dieses Beispiel läuft aber unter Windows wie auch unter Linux:

Code: Alles auswählen

import pystray

from PIL import Image, ImageDraw


def create_image(width, height, color1, color2):
    # Generate an image and draw a pattern
    image = Image.new('RGB', (width, height), color1)
    dc = ImageDraw.Draw(image)
    dc.rectangle(
        (width // 2, 0, width, height // 2),
        fill=color2)
    dc.rectangle(
        (0, height // 2, width // 2, height),
        fill=color2)

    return image


# In order for the icon to be displayed, you must provide an icon
icon = pystray.Icon(
    'test name',
    icon=create_image(64, 64, 'black', 'white'))


# To finally show you icon, call run
icon.run()
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@The Hit-Man: Warum sucht man im Netz nach einem Beispiel für so etwas? Die API will ein `Image`-Objekt von `PIL` und `Image.open()` erzeugt so eines aus einer Datei. Simpler geht's ja kaum.

Da fehlt kein `icon.run()`, denn das blockiert ja wieder und nichts danach wird ausgeführt.

Letztlich scheint das dann eine Frage für die `pystray`-Entwickler zu sein. Entweder fehlt dort etwas in der Dokumentation oder der Code ist fehlerhaft.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

@The Hit-Man: Warum sucht man im Netz nach einem Beispiel für so etwas? Die API will ein `Image`-Objekt von `PIL` und `Image.open()` erzeugt so eines aus einer Datei. Simpler geht's ja kaum.
Bin schon ewig raus. Aus dem Grund, suche ich im Netz nach ner Lösung ...
Ich hatte mir per print halt ausgeben lassen, wie das Objekt aus dem Beispiel aussieht und dachte eben, bei meinem Objekt steht 'PNG', im Beispiel aber 'BMP'. Ich denke, vielleicht ist es besser wxPython oder so etwas zu nehmen. Allerdings kommt mir so etwas viel zu overkilled vor, nur um mal eben nen Icon/Image in der Taskleiste anzeigen zu lassen. Oder kennst du einen schnellen anderen Weg?
Antworten