ScrolledText mit Buttons

Fragen zu Tkinter.
Antworten
d.christopher
User
Beiträge: 5
Registriert: Samstag 25. Juli 2020, 11:03

hey zusammen,
ich möchte gerne Variablen in einen Text einfügen, hier in dem Beispiel erscheint ein Button im Text, wenn das Wort "Test" geschrieben wird, der Button bleibt aber leider nicht im Text ander Stelle wo er erschienen ist sondern wandert herum. Zudem kommt eine Fehlermeldung wenn man den Button mit dem "BackSpace" löscht. Ich würde den Button gern an einer festen Stelle im Text haben und diesen mit "BackSpace" wie einen Text löschen können.
Ich bin noch recht neu was Tkinter und GUI's angeht und freu mich über jeden hilfreichen Tipp ;)

LG
Christpher

Code: Alles auswählen

import tkinter as tk
from tkinter import *
from tkinter.scrolledtext import ScrolledText


def get_stringvar(event):
    SV.set(ST1.get("1.0", END))
    string = SV.get()
    if(string.find("Test")!= -1 and len(string)>0):
        ST1.window_create(END, window=btn)

def key_delete(event):
    print(event)


root = tk.Tk()

SV = StringVar()

ST1 = ScrolledText(root)
ST1.pack()
ST1.bind('<KeyRelease>', get_stringvar)

ST1.bind('<BackSpace>', key_delete)
btn = Button(root, text="Test")

root.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@d.christopher: Natürlich wandert der, denn wenn das Wort Test im Text steht, dann steht das da ja, das heisst bei jedem Tasteloslassen ist das Wort im Text und der Button wird am Ende des Textes hinzugefügt. Da das nur *ein* Button-Objekt ist, verschwindet der an der Stelle wo er vorher war. Und löschen des Buttons geht aus dem gleichen Grund nicht. Du kannst zwar den Button löschen, aber nachdem die Taste losgelassen wird, wird geprüft ob das Wort Test im Text vorkommt: Ja, kommt es, und zack wird ein Button am Ende angefügt.

Wenn Du das Wort "Test" aus dem Text zumindest ”kaputt” machst, dann wirst Du auch sehen, das a) der Button bleibt wo er ist, und b) der Button mit der Rücktaste oder der Entfernen-Taste, oder durch Markieren und Strg+X oder Strg+C entfernt werden kann.

Weitere Anmerkungen: Der Sternchen-Import muss da weg. Da `tkinter` ja schon als `tk` importiert wurde, macht das ja noch nicht mal Sinn das alles noch mal in den Modulnamensraum zu kippen statt konsequent über `tk` auf die Werte zuzugreifen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Da braucht man dann eventuell `functools.partial()` oder objektorientierte Programmierung, die man letztlich für jede nicht-triviale GUI benötigt.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Und Namen kürzt man nicht kryptisch ab.

Das `StringVar`-Objekt wird nicht wirklich verwendet, das kann weg.

``if`` ist keine Funktion, sollte also auch nicht so geschrieben werden als wäre es eine. Die Klammern gehören da nicht hin.

Das ``string.find("Test") != -1`` ist ``"Test" not in string`` bloss kryptisch formuliert. Der zweite Teil der Bedingung ist unsinnig, denn der wird nur geprüft wenn "Test" im `string` vorkommt, und dann wissen wir aber das der `string` nicht die Länge 0 haben kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
d.christopher
User
Beiträge: 5
Registriert: Samstag 25. Juli 2020, 11:03

@__blackjack__ Danke für deine Hilfreichen Tipps, ich habe den Code nun etwas angepasst und die Anmerkungen einfließen lassen.
Problematisch ist hier das Kaputtmachen von "Test", ich nutze replace, dadurch verschwindet der Button nun vom scrolledtext, gibt es eine andere Möglichkeit den Text zu bearbeiten ohne ihn jedesmal komplett zu ersetzen ?
Könnte man in der Methode, welche von dem <KeyRelease> Event getriggert wird das das Event <BackSpace> ausführen ?

Code: Alles auswählen

import tkinter as tk
import tkinter.scrolledtext as st
import re

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.scr_txt = st.ScrolledText(self)
        self.scr_txt.pack()
        self.scr_txt.bind('<KeyRelease>', self.get_stringvar)

    def get_stringvar(self,event):
        string = self.scr_txt.get('1.0',tk.END)
        if string.find("Test") > -1 and len(string)>0:
            string = re.sub("Test","",string)
            btn = tk.Button(self, text="Test")
            self.scr_txt.replace("0.0", tk.END, string)
            self.scr_txt.window_create(tk.END, window=btn)


if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@d.christopher: Du kannst den Text mit den entsprechenden Methoden verwenden. Neben `get()` und `replace()` gibt es ja beispielsweise `insert()` und `delete()`. Und ich würde da auch `search()` verwenden, denn ich vermute mal der Button soll eigentlich den Text "Test" ersetzen und nicht ans Ende angefügt werden. Das würde ja nur funktionieren wenn "Test" auch am Ende stand, und der Benutzer den Cursor nicht irgendwo in bereits getippten Text setzt und da "Test" schreibt.

Was ist eigentlich wenn der Benutzer nach dem "Test" noch weiterschreibt und das eigentlich gar nicht "Test" heissen sollte sondern "Testfall" oder "Testosteron" oder…
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
d.christopher
User
Beiträge: 5
Registriert: Samstag 25. Juli 2020, 11:03

@__blackjack__: So ich glaube mit dieser Lösung bin ich erst mal recht zufrieden.

Code: Alles auswählen

import tkinter as tk
import tkinter.scrolledtext as st

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.scr_txt = st.ScrolledText(self)
        self.scr_txt.pack()
        self.scr_txt.bind('<KeyRelease>', self.get_stringvar)

    def get_stringvar(self,event):
        idx = self.scr_txt.search('Test ','1.0', stopindex=tk.END,regexp=True)
        if idx:
            self.scr_txt.delete(idx, tk.END)
            btn = tk.Button(self, text='Test')
            self.scr_txt.window_create(tk.END, window=btn)


if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

ich habe jedoch ein paar allgemeine Fragen:
  • bedeutet dies auch, das ich alle Methoden dieser Module nutzen kann ?
Antworten