After: Wo hakt es?

Fragen zu Tkinter.
Antworten
PePe
User
Beiträge: 3
Registriert: Samstag 22. Mai 2021, 13:31

Hallo liebes Forum,

ich möchte ein kleines Tkinter-Programm schreiben, das mir Eingabefelder automatisch mit Inhalten aus der Zwischenablage in einer vorgegebenen Reihenfolge füllt. Möchte mir damit die Datenerfassung vereinfachen, da ich mir nach Strg+C das manuelle Navigieren ins Eingabefeld und dortige Einfügen spare. So sieht mein bisheriger Ansatz aus:

Code: Alles auswählen

import tkinter as tk


class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.current_entry = 0
        self.max_entries = 3
        self.clipboard_clear()
        self.clipboard_append("")
        for n in range(self.max_entries):
            ClipboardEntry(self, index=n).pack(side=tk.TOP)


class ClipboardEntry(tk.Entry):
    def __init__(self, parent, index, *args, **kwargs):
        tk.Entry.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.index = index
        self.entry_var = tk.StringVar()
        self.entry_var.set("")
        self["textvariable"] = self.entry_var
        self.refresh_entry()

    def refresh_entry(self):
        if (
            self.entry_var.get() == ""
            and self.clipboard_get() != ""
            and self.parent.current_entry == self.index
        ):
            self.entry_var.set(self.clipboard_get())
            self.clipboard_clear()
            self.clipboard_append("")
            self.parent.current_entry += 1
        self.after(250, self.refresh_entry)


if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack()
    root.mainloop()
An sich scheint das auch zu funktionieren, aber nur sehr unregelmäßig. Das Programm soll alle 250 ms nachsehen, ob was Neues in der Zwischenablage liegt. Wenn ich das Programm ausführe klappt das manchmal direkt, manchmal dauert es mehrere Sekunden, bis der Inhalt eingefügt wird. Fehlermeldungen werden keine ausgegeben. Wo könnte das Problem liegen? Kommen sich mehrfache Aufrufe von after vielleicht in irgendwie in die Quere?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ist aus Gruenden von Klarheit und Robustheit kein besonders gutes Vorgehen, eine Reihe von Entries diese Frage jeweils immer selbst beantworten zu lassen. Das sollte also einfach von aussen in der MainApplication passiern. Denn dann kannst du dir im Grunde die gesamte zweite Klasse sparen, und auch die Verschraenkung der beiden Klassen ineinander.

Aber so richtig einen Grund dafuer, dass das ein Problem darstellt, sehe ich erstmal so nicht. Denn die ganzen Timer-Callbacks sollten seriell abgearbeitet werden, und dadurch eigentlich nichts schiefgehen.

Als ersten Debugging-Schritt koenntest du mal probieren, refresh_entry auf die MainApplication zu ziehen, und einfach nur zu reporten, was da passiert.

Dinge, die mir noch einfallen: weil das Clipboard ja ein System-Resource ist, die geteilt wird, und ggf. irgendwelchen komplexen Regeln unterliegt - kann es sein, dass deine Beobachtung zB damit zusammenhaengt, ob dein Programm in den Vordergrund gebracht wurde, bevor da was passiert?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: ich habe das mal leicht umgebaut, und geschaut, ob und wie das bei mir funktioniert:

Code: Alles auswählen

import tkinter as tk
from itertools import cycle


class MainApplication(tk.Frame):
    def __init__(self, parent, entries=3, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.current_entry = 0
        self.clipboard_clear()
        self.clipboard_append("")
        self._entries = []
        for n in range(entries):
            var = tk.StringVar()
            entry = tk.Entry(self, textvariable=var)
            entry.pack(side=tk.TOP)
            self._entries.append((entry, var))

        self._entry_it = cycle(self._entries)
        self._current_entry = next(self._entry_it)
        self.refresh_entry()

    def refresh_entry(self):
        if self.clipboard_get() != "":
            self._current_entry[1].set(self.clipboard_get())
            self.clipboard_clear()
            self.clipboard_append("")
            self._current_entry = next(self._entry_it)
        self.after(250, self.refresh_entry)


if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack()
    root.mainloop()
Unter Ubuntu Linux funktioniert es ganz wunderbar. So gut, dass es mir sogar Schwierigkeiten bereitet hat, dieses Posting zu erstellen, weil der kopierte Quelltext immer aus dem Clipboard gefischt wurde... wenn ich des persoenlich benutzen wollen wuerde, dann wuerde ich das mit einem expliziten globalen Hotkey probieren zu verheiraten, damit sowas nicht passiert.

Aber unabhaengig davon: geht alles wie gedacht.
PePe
User
Beiträge: 3
Registriert: Samstag 22. Mai 2021, 13:31

Danke für den Hinweis, die refresh-Methode ins Hauptprogramm zu holen hat schon geholfen. Habe es nun so angepasst:

Code: Alles auswählen

import tkinter as tk


class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.max_entries = 3
        self.clipboard_clear()
        self.clipboard_append("")

        self.entry_vars, self.entries = {}, {}
        for n in range(self.max_entries):
            self.entry_vars[n] = tk.StringVar()
            self.entries[n] = tk.Entry(self, textvariable=self.entry_vars[n])
            self.entries[n].pack()

        self.current_entry = 0

        self.refresh()

    def refresh(self):
        if self.current_entry == self.max_entries:
            pass
        elif (
            self.entry_vars[self.current_entry].get() == ""
            and self.clipboard_get() != ""
        ):
            self.entry_vars[self.current_entry].set(self.clipboard_get())
            self.current_entry += 1
            self.clipboard_clear()
            self.clipboard_append("")
        self.after(250, self.refresh)


if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack()
    root.mainloop()
Der Code macht jetzt was er soll.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hm, dann war das wohl doch irgendwie ein Seiteneffekt. Wirklich erklaerlich ist mir das nicht, aber wenn es geht, schoen.

Codekritik: wenn man etwas durchnummerieren will, dann benutzt man eine Liste. Nicht ein Woerterbuch mit Zahlen als Schluesseln.
PePe
User
Beiträge: 3
Registriert: Samstag 22. Mai 2021, 13:31

Stimmt, hätte ich eigentlich auch schon wissen sollen :roll:
Antworten