Links (Tags) im Text Widget

Fragen zu Tkinter.
Markus12
User
Beiträge: 195
Registriert: Dienstag 6. März 2007, 19:32
Wohnort: Frankfurt am Main

Samstag 28. März 2009, 04:08

Damit man es versteht, denke ich, sollte ich einfach das gesamte Skript platzieren bzw. auslagern, obwohl ich nicht weiß, wo man sowas macht... Deshalb setze ich ihn hier hinein und wenn er euch zu viel ist, bitte auslagern! :)

Mein Code (209 Zeilen):

Code: Alles auswählen

from Tkinter import*
from ScrolledText import*
import time
import pickle

hauptseite= """Willkommen in der Dokumentation.

Auf der linken Seite finden Sie ein Eingabefeld, in welches Sie Wörter eingeben können, deren Definition suchen.

Bei jedem weiteren Buchstaben, den sie eingeben, wird bereits nach Themen gesucht, die ihr Schlüsselwort enthalten."""
start_tags = [("1.0", "1.32", {"font": ("Arial",12,"bold")})]

class Dokumentation(Tk):     
    def __init__(self):
        self.__titel = []
        self.datenbank = {}
        self.links = {}
        
        Tk.__init__(self)
        self.title("Nachschlagen von Wörtern")
        
        self.frame1 = Frame(self)
        self.frame1.pack(side="left", fill="y")

        self.l1 = Label(self.frame1, font=("Arial", 20, "bold"), text="Dokumentation")
        self.l1.pack(padx=5, pady=10)

        self.b = Label(self.frame1, font=("Arial", 12, "italic"), text="Schlüsselwort eingeben")
        self.b.pack()
        
        self.e = Entry(self.frame1, font=("Comic", 12, "bold"))
        self.e.pack(fill="x", padx=10, pady=5)
        self.e.bind("<Any-KeyRelease>", self._filtern)

        self.listbox = ScrolledBox(self.frame1, height=10, font=("Times", 12))
        self.listbox.pack(fill="both", padx=10, pady=10, expand=1)
        self.listbox.bind("<ButtonRelease-1>", self._handler_anzeigen)
        self.listbox.bind("<KeyRelease-Up>", self._handler_anzeigen)
        self.listbox.bind("<KeyRelease-Down>", self._handler_anzeigen)
        
        self.frame2 = Frame(self)
        self.frame2.pack(side="right", expand=1, fill="both")

        self.text = ScrolledText(self.frame2, width=30, height=14, bg="lightgrey", font=("Arial", 12), wrap="word", relief="ridg", cursor="hand1", padx=7, pady=7)
        self.text.pack(padx=10, pady=10, fill="both", expand=1)
        self.text.insert(END, hauptseite)
        self.text.config(state="disabled")
        for a,e,format in start_tags:
            self.text.tag_add("start", a, e)
            self.text.tag_config("start", format)
        
    def neuesThema(self, titel, beschreibung):
        self.listbox.delete(0, END)
        #self.titel.append(titel)
        self.datenbank[titel] = beschreibung
        #self.titel.sort()
        for i in self.titel:
            self.listbox.insert(END, i)

    def neuerLink(self, thema, index1, index2, verlinke_zu):
        assert verlinke_zu in self.titel
        tag = "link"+str(len(self.links)+1)        
        if thema in self.links:
            self.links[thema] = self.links[thema]+[(tag, index1, index2, verlinke_zu)]
        else:
            self.links[thema] = [(tag, index1, index2, verlinke_zu)]

    def anzeigen(self, thema):
        self.listbox.selection_clear(0, END)
        index = self.index(thema, self.listbox.get(0,END))
        if index!=None:   self.listbox.select_set(index)
        self.text.config(state="normal")
        self.text.delete("0.0", END)
        self.text.insert(END, self.datenbank[thema])
        self.text.config(state="disabled")
        if thema in self.links:
            for link in self.links[thema]:
                tag, i1, i2, zu = link
                self._linkanzeigen(tag, i1, i2)
                
    def _linkanzeigen(self, tagname, index1, index2):
        self.text.tag_add(tagname, index1, index2)
        self.text.tag_config(tagname, foreground="blue")
        self.text.tag_bind(tagname, "<Button-1>", self._handler)
        self.text.tag_bind(tagname, "<Enter>", self._handler)
        self.text.tag_bind(tagname, "<Leave>", self._handler)
        
    def _handler_anzeigen(self, e):
        if self.listbox.get(0, END):
            self.anzeigen(self.thema)
            
    def _handler(self, e):
        tag = self._linkwahl(self.thema)
        if e.type == "4": #Button-1
            e.type = "8"
            self._handler(e)
            self.text.tag_unbind(tag, "<Leave>")
            self.verlinken(tag)
        elif e.type == "7": #Enter
            self.text.config(cursor="hand2")
            self.text.tag_config(tag, underline=1)
        elif e.type == "8": #Leave
            self.text.config(cursor="hand1")
            self.text.tag_config(tag, underline=0)

    def _linkwahl(self, thema):
        links = self.links[thema]
        k = self.text.index(CURRENT)
        position = (int(k.split(".")[0]), int(k.split(".")[-1]))
        for tag, i1, i2, zu in links:
            tagposliste = [(int(i.split(".")[0]), int(i.split(".")[-1])) for i in (i1, i2)]
            wahlliste = tagposliste + [position]
            wahlliste.sort()
            if wahlliste[1] == position:
                break
        return tag

    def _filtern(self, event):
        wort = self.e.get()
        erg = [a for a in self.titel if wort in a]
        self.listbox.delete(0, END)
        for i in erg:
            self.listbox.insert(END, i)
        
    def verlinken(self, tag):
        links = self.links[self.thema]
        for tag2,i1,i2,zu in links:
            if tag2 == tag:
                break
        self.anzeigen(zu)

    def sichern(self, dateiname):
        f = file(dateiname+".db", "w")
        pickle.dump(self.datenbank, f)
        f.close()

    def laden(self, dateiname):
        f = file(dateiname+".db", "r")
        self.datenbank = pickle.load(f)
        f.close()
        self.titel = self.datenbank.keys()
        self.listbox.delete(0, END)
        for i in self.titel:
            self.listbox.insert(END, i)
            
    def index(self, thema, liste):
        erg = None
        for i in range(len(liste)):
            if liste[i] == thema:
                erg = i
                break
        return erg
    
    def _getThema(self):
        nummer = int(self.listbox.curselection()[0])
        return self.listbox.get(nummer)

    def _setThema(self, thema):
        self.anzeigen(thema)

    def _getTitel(self):
        keys = self.datenbank.keys()
        keys.sort()
        return keys
    
    thema = property(fget=_getThema, fset=_setThema)
    titel = property(fget=_getTitel)


class ScrolledBox(Listbox):
    def __init__(self, master, **kw):
        self.frame = Frame(master)
        self.frame.pack()
        Listbox.__init__(self, self.frame, kw)
        Listbox.pack(self, fill="both", expand=1, side="left")
        
        self.scrollbar = Scrollbar(self.frame, command=self.yview)
        self.scrollbar.pack(fill="y", side="right")
        self.config(yscrollcommand=self.scrollbar.set)

        methoden = Pack.__dict__.keys()
        methoden += Grid.__dict__.keys()
        methoden += Place.__dict__.keys()
        for i in methoden:
            if i[0]!="_" and i!="config" and i!="configure":
                setattr(self, i, getattr(self.frame, i))



def main():
    global d
    daten = {"Hallo": "Begrüßung auf Deutsch.",
             "Computer": "Ein anderes Wort für pc, was personal computer bedeutet.",
             "Dokumentation": "Eine Erklärung für Dummies.",
             "Hilfe": "Wenn man nicht weiter weiß, benötigt man die Hilfe.",
             "Markus": "Ersteller dieses Tools.",
             "Programm": "Ein Skript, das Befehle ausführt.",
             "Python": "Eine Computersprache, in der dieses Programm geschrieben wurde und welche einfach ist.",
             "Willkommen": "Eine andere Begrüßung in deutsch."}

    d = Dokumentation()
    for i in daten.keys():
        d.neuesThema(i, daten[i])

    d.neuerLink("Python", "1.36", "1.44", "Programm")
    d.neuerLink("Markus", "1.17", "1.22", "Programm")
    d.neuerLink("Hallo", "1.0", "1.9", "Willkommen")

    
if __name__=="__main__":
    main()
Viele Grüße Markus :)
Benutzeravatar
wuf
User
Beiträge: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Samstag 28. März 2009, 11:36

Hallo Markus12

Danke für deine Anwort. Werde es einmal anschauen.

Vorab betreffs auslagern deines Skriptes. Die musst einfach folgende Web-Adresse aufrufen:

http://paste.pocoo.org/

Auf dieser Seite links unten musst du das Textformat aus der Listbox auswählen. In diesem Fall währe es 'Python'.

Als nächstes kopierst du deinen Skriptext in das Textfenster. Der Text erscheint als normaler Plaintext.

Dann musst du die Schaltfäche 'Paste' aktivieren. Nun wird der Text ins Python highlighted Format umgewandelt.

Das Adressfeld im Browser ändert sich wie folgt. Die Nummer 109979 hier nur ein Beispiel wird automatisch fortlaufend mit jedem umgewandelten Text generiert z.B:

http://paste.pocoo.org/show/109979/

Diese Adresse musst du in deinen Beitrag im Forum kopieren. Sie erscheint dann in blauer Farbe.

Dann sollte beim anklicken dieser Adresse automatisch dein ausgelagerter Text unter der Webseite http://paste.pocoo.org erscheinen.

Das musst du einmal ausprobieren. Das ist so wie ich es bis jetzt gemacht habe. Ist vielleicht nicht der schlauste Weg aber er funktioniert.

Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
wuf
User
Beiträge: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Samstag 28. März 2009, 16:05

Hallo Markus12

Hier ein möglicher Lösungsansatz für das erste Problem. Direkte Selektion des ersten Listbox-Elementes beim Start des Skriptes:

http://paste.pocoo.org/show/110010/

Im Skript sind folgende Ergänzungen eingebettet:

Code: Alles auswählen

#~~ Neu wuf: Wird aufgerufen, wenn die Listbox den Fokus bekommt
self.listbox.bind("<FocusIn>", self._handler_anzeigen)

Code: Alles auswählen

#~~ Neu wuf: Selektiert das erste Element in der Listbox
d.listbox.focus_set()
d.listbox.selection_set(0)
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
wuf
User
Beiträge: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Samstag 28. März 2009, 19:38

Hallo Markus12

Das zweite Problem mit dem Doppel-Klick und Dreifach-Klick kann mit folgenden Anweisungen behoben werden (nur unter Linux ausgetestet):

Code: Alles auswählen

self.text.bind_class('Text', '<Double-Button-1>', lambda e: None)
self.text.bind_class('Text', '<Triple-Button-1>', lambda e: None)
Unmittelbar hinter der Text-Widget-Konfiguration einfügen.

Es gibt noch ein drittes Problem zu beheben. Wenn mit der Maus Text im Text-Widget selektiert wird verliert die Listbox die Selektion.
Nicht die Textselektion mit einem Doppel-Klick sondern mit folgender Selektionprozedur:
a) Maus auf Text im Text-Widget führen.
b) Linke Maustaste drücken und gedrückt halten
c) Maus über den Text ziehen
d) Linke Maustaste loslassen
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
wuf
User
Beiträge: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Samstag 28. März 2009, 20:26

Hallo Markus12

Hier ein Lösungsversuch des dritten Problems:

http://paste.pocoo.org/show/110042/

Gruss wuf :wink:
Take it easy Mates!
Markus12
User
Beiträge: 195
Registriert: Dienstag 6. März 2007, 19:32
Wohnort: Frankfurt am Main

Montag 30. März 2009, 03:43

Hallo wuf,
Vielen Dank für die großartige Hilfe!
Jap, Problem Nummer drei hatte ich bemerkt, aber vergessen zu posten... Mein Fehler :)

Habe dein Skript ausprobiert, und schon mal gibt es keine Exception mehr! Das finde ich toll. Hätte an deine Lösungsansätze wahrscheinlich niemals gedacht, weshalb es mich freut wieder etwas gelernt zu haben.

Die Listbox hat nun allerdings den Fokus, sodass sie umrandet ist und der aktuelle Eintrag unterstrichen ist. Ich würde es bevorzugen, wenn es nicht so wäre, aber wenn es nicht anders geht, würde ich es so natürlich machen...
Klicke ich aber auf einen Link im Text-Widget, wird man verlinkt, klappt perfekt, und ein neuer Eintrag wird angewählt... Allerdings bleibt der alte Eintrag unterstrichen...? Das ist komsch^^ :D

Vielen Dank schon mal für die Hilfe!
Grüße Markus :)

PS: Danke für's Erklären wie man es auslagert ;-)
Benutzeravatar
wuf
User
Beiträge: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Montag 30. März 2009, 08:50

Hallo Markus12

Den Fokusrand bei der ScrolledBox kannst du mit der relativ langen Optionsbezeichung highlightthickness=0 wegschaffen.

Code: Alles auswählen

self.listbox = ScrolledBox(self.frame1, height=10, font=("Times", 12),
    highlightthickness=0)
Bei mir hat der selektierte Eintrag eine hellblaue Hintergrundfarbe und ist mit einem gepunkten Rähmchen umgeben. Unter welchem OS arbeitest du? (Linux, Windows oder Mac)

Gruss wuf :wink:
Take it easy Mates!
Antworten