Uhrzeit nach Fensterwechsel anzeigen

Fragen zu Tkinter.
Antworten
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

hallo,
ich versuche seit längerem schon die, am anfang vorhandene uhr- und datumsanzeige, nach (jedem) fensterwechsel wieder im startfenster anzeigen zu lassen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# from __future__ import absolute_import, division, print_function
import tkinter as tk
import os
import time
from threading import Thread, Event


class Clock:
    @staticmethod
    def update_clock():
        now = time.strftime("%H:%M:%S\n%d. %B %Y")
        return now


class Start_Page(Clock):
    def __init__(self, master):
        Clock.__init__(self)
        self.master = master
        self.master.state('zoomed')
        self.master.overrideredirect(True)
        self.master.resizable(False, False)
        self.master.columnconfigure(0, weight=2)
        self.master.iconbitmap(default='logo_piepenbrock.ico')

        ###########
        # FOOTER
        ###########
        self.footer = tk.Frame(self.master, height=35, bg="Gray")
        self.footer.pack_propagate(False)
        self.footer.pack(side=tk.BOTTOM, fill=tk.X)
        self.footer.bind("<Enter>", self.start_clock)

        # LABEL: CLOCK - FOOTER
        self.clock_var = tk.StringVar()
        self.label_clock = tk.Label(self.footer, textvariable=self.clock_var, font=("Arial", 9, 'bold'), bg="Grey")
        self.label_clock.pack(side=tk.RIGHT, padx=10)

        self.work = Thread(target=self.start_clock)
        self.work.start()

        ###########
        # MIDDLE
        ###########
        self.middle = tk.Frame(self.master)
        self.middle.pack(side=tk.BOTTOM)

        # BOTTOM: NEW_WINDOW - FOOTER
        self.new_window = tk.Button(self.middle, text='New Window', width=15, command=self.new_window)
        self.new_window.pack(pady=5)

        # BUTTON: CLOSE - FOOTER
        self.close = tk.Button(self.middle, text='Close', width=15)
        self.close.pack(pady=20)
        self.close.bind("<Button-1>", self.exit_program)

    def start_clock(self):
        while True:
            self.clock_var.set(self.update_clock())

    def frame_mapped(self, event):
        self.master.update_idletasks()
        self.master.overrideredirect(True)
        self.master.state('zoomed')

    def new_window(self):
        self.master.overrideredirect(False)
        self.master.iconify()
        new_window = tk.Toplevel(self.master)
        Page_1(new_window)

    def exit_program(self, event):
        os._exit(0)


class Page_1(Clock):
    def __init__(self, master):
        self.master = master
        self.master.state('zoomed')
        self.master.overrideredirect(True)
        self.master.resizable(False, False)

        # HEADER
        self.header = tk.Frame(self.master, height=35, bg="Gray")
        self.header.pack_propagate(False)
        self.header.pack(side=tk.TOP, fill=tk.X)

        # FOOTER
        self.footer = tk.Frame(self.master, height=35, bg="Gray")
        self.footer.pack_propagate(False)
        self.footer.pack(side=tk.BOTTOM, fill=tk.X)

        # BUTTONS
        self.frame = tk.Frame(self.master)
        self.frame.pack(side=tk.BOTTOM, pady=20)
        self.quitButton = tk.Button(self.frame, text='Back', width=15, command=self.close_windows)
        self.quitButton.pack(pady=20)

    def close_windows(self):
        self.master.destroy()
        second = tk.Tk()
        Start_Page(second)


def main():
    root = tk.Tk()
    Start_Page(root)
    root.mainloop()


if __name__ == '__main__':
    main()
leider gelingt es mir nicht, so daß ich mal hier nachfragen wollte, bevor ich den rechner noch kaputt hau :)
habe zu letzt versucht, das irgendwie mit der .bind-methode zu lösen (self.footer.bind("<Enter>", self.start_clock)),
leider funktioniert bisher keine meiner ideen :(
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DMD-OS: Das funktioniert so nicht. `Thread` sollte da gar nicht verwendet werden, weil GUIs in der Regel nicht thread-sicher sind. Die GUI darf nur aus dem Thread heraus manipuliert werden in dem die GUI-Hauptschleife läuft.

Wenn man in GUIs etwas nach einer bestimmten Zeit oder regelmässig tun will, dann gibt es in der Regel vom GUI-Rahmenwerk eine Möglichkeit das in die Hauptschleife zu integrieren. Bei Tk ist das die `after()`-Methode. Damit belastet Du dann auch gleich die CPU deutlich weniger als mit der ständig auf Hochtouren laufenden ``while``-Schleife in Deinem Code, die ja eigentlich nur einmal in der Sekunde etwas tun müsste.

Zudem beendest Du Deine Threads nirgends explizit. Ich vermute mal deshalb ist da ein `os._exit()`-Aufruf im Code. Man sollte allgemein ja schon `sys.exit()` nicht verwenden, aber `os._exit()` schon gar nicht. Das dürfte, abgesehen von einem tatsächlichen harten Programmabsturz, die rabiateste Art sein ein Python-Programm zu beenden, ohne das danach noch irgendwelcher Code mit Aufräumarbeiten eine Chance hätte abzulaufen.

Die Uhr startet ja automatisch, da sollte es nicht noch eine zweite Möglichkeit über die Enter-Taste geben das parallel noch mal zu starten. Zumal das bei der Endlos-``while``-Schleife dann sowieso die GUI blockiert hätte.

Es darf immer nur ein einziges `Tk`-Exemplar geben. Das ist *das* Hauptfenster und da hängt der Tcl/Tk-Interpreter dran. Wenn davon zwei laufen, gibt es Chaos und es können komische Dinge passieren. Zusätzliche Fenster immer mit `Toplevel` erstellen, auch wenn die den gleichen Inhalt wie das Hauptfenster haben sollen.

Dass das Python 3-Code ist, sollte man in der ersten Zeile klar machen, damit das nicht mit einem falschen Interpreter ausgeführt wird.

Die Klasse `Clock` macht keinen Sinn. Das ist eine ziemlich umständliche Art eine Funktion zu definieren, und die dann extrem undurchsichtig über Vererbung in Klassen zu schmuggeln. Der Methodenname ist zudem irreführend, weil da nichts aktualisiert wird. Die `start_clock()`-Methode wäre ein besserer Kandidat für den Namen. In `Page_1.__init__()` hätte dann übrigens auch die Initialisierung der ”Basisklasse” gefehlt.

Unterstriche haben in Klassennamen nichts zu suchen. Durchnummerieren ist auch keine gute Idee.

Der Name `master` macht keinen Sinn. Das jeweilige Objekt ist kein ”Herr”, sondern im Gegenteil eher Befehlsempfänger. Wenn das der Master wäre, dürfte ein Objekt das tiefer in der Objekthierarchie steht, dem sicher nicht sagen wie er sich darzustellen hat. Nach dem Methoden die auf Objekten mit diesen Namen aufgerufen werden, wäre `window` ein passender Name.

Du bindest zu viel an die Objekte. Insbesondere irgendwelche `Frame`\s die nur für das Layout in der `__init__()` benötigt werden, sollte man einfach ganz normal als lokale Namen haben. Besonders `new_window` ist problematisch: Du hast das einmal als Attribut und einmal als Methode, es kann aber immer nur eines von beiden geben. Das ist nur Glück das Du das in der ”richtigen Reihenfolge” verwendest, aber für Leser ist das verwirrend.

Bei `Button`\s benutzt man `command` für die Rückruffunktion und bindet nicht manuell Maus-Ereignisse an das Widget. Das verhält sich nicht wie Benutzer das gewohnt sind!

Die `frame_mapped()`-Methode wird nirgends verwendet? Hat auch einen komischen Namen. Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt, die sie durchführen.

Ungetesteter Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import time
import tkinter as tk


def get_formatted_timestamp():
    return time.strftime('%H:%M:%S\n%d. %B %Y')


class StartPage:
    
    def __init__(self, window):
        self.window = window
        self.window.state('zoomed')
        self.window.overrideredirect(True)
        self.window.resizable(False, False)
        self.window.columnconfigure(0, weight=2)
        self.window.iconbitmap(default='logo_piepenbrock.ico')

        ###########
        # FOOTER
        ###########
        footer = tk.Frame(self.window, height=35, bg='Gray')
        footer.pack_propagate(False)
        footer.pack(side=tk.BOTTOM, fill=tk.X)

        # LABEL: CLOCK - FOOTER
        self.clock_var = tk.StringVar()
        tk.Label(
            footer,
            textvariable=self.clock_var,
            font=('Arial', 9, 'bold'),
            bg='Grey',
        ).pack(side=tk.RIGHT, padx=10)

        self.update_clock()

        ###########
        # MIDDLE
        ###########
        middle = tk.Frame(self.window)
        middle.pack(side=tk.BOTTOM)

        # BOTTOM: NEW_WINDOW - FOOTER
        tk.Button(
            middle, text='New Window', width=15, command=self.new_window
        ).pack(pady=5)

        # BUTTON: CLOSE - FOOTER
        tk.Button(
            middle, text='Close', width=15, command=self.window.quit
        ).pack(pady=20)

    def update_clock(self):
        self.clock_var.set(get_formatted_timestamp())
        self.window.after(1000, self.update_clock)

    def frame_mapped(self, _event):
        self.window.update_idletasks()
        self.window.overrideredirect(True)
        self.window.state('zoomed')

    def new_window(self):
        self.window.overrideredirect(False)
        self.window.iconify()
        Page1(tk.Toplevel(self.window))


class Page1:
    
    def __init__(self, window):
        self.window = window
        self.window.state('zoomed')
        self.window.overrideredirect(True)
        self.window.resizable(False, False)

        # HEADER
        self.header = tk.Frame(self.window, height=35, bg="Gray")
        self.header.pack_propagate(False)
        self.header.pack(side=tk.TOP, fill=tk.X)

        # FOOTER
        footer = tk.Frame(self.window, height=35, bg="Gray")
        footer.pack_propagate(False)
        footer.pack(side=tk.BOTTOM, fill=tk.X)

        # BUTTONS
        self.frame = tk.Frame(self.window)
        self.frame.pack(side=tk.BOTTOM, pady=20)
        self.quit_button = tk.Button(
            self.frame, text='Back', width=15, command=self.close_window
        )
        self.quit_button.pack(pady=20)

    def close_window(self):
        self.window.destroy()
        # 
        # XXX This looks odd or even wrong!
        # 
        StartPage(tk.Toplevel())


def main():
    root = tk.Tk()
    StartPage(root)
    root.mainloop()


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

danke für die ganzen infos, mühe und zeit. so langsam verstehe ich die kritikpunkte (hatte vorher immer probleme mit dem verständis, da
ich oft nicht genau wußte, was gemeint war :)
die `after()`-Methode habe ich lange versucht, da aber immer ein mainloop-problem aufgetreten ist, habe ich irgendwann aufgegeben damit bzw.
irgendwann eher gedacht, dass diese methode falsch und die 'while'-schleife besser ist. habe die `after()`-Methode nie in die hauptschleife bekommen :(
werde mir das zu herzen nehmen. VIELEN DANK :)
Antworten