"bold" und "unbold" läuft nicht rund (bei App mit 2 Text-Widgets)

Fragen zu Tkinter.
Antworten
marlon_germany
User
Beiträge: 33
Registriert: Samstag 30. April 2022, 23:32

Hi Allerseits,

Ich habe eine kleine App mit 2 Text Widgets und 2 Buttons (bold + unbold) erstellt.
Nun schreibe ich etwas in Text-Feld 1 und in Text-Feld 2 hinein.

(1.Runde)
Wenn ich nun:
- den Text von Text-Feld 1 markiere, auf bold drücke und im Anschluss...
- den Text von Text-Feld 2 markiere und ebenfalls auf bold drücke, funktioniert es soweit.

(2.Runde)
Wenn ich aber dann im Anschluss:
- wieder den Text von Text-Feld 1 markiere, und auf unbold drücke, werden beide Texte gleichzeitig auf unbold gesetzt
- und wenn nun z.B. die Texte von beiden Text-Felder auf bold gestellt sind und ich einmal in das Text-Feld 1 klicke, dann den Text von Text-Feld 2 markiere und auf unbold drücke, passiert gar nichts.


Ich wäre über Eure Hilfe sehr erfreut!

Was muss verändert werden, damit die Texte in den Textfeldern
unabhängig voneinander auf "bold" oder "unbold" gesetzt werden können?

Hier der Code:

Code: Alles auswählen

import tkinter as tk
from tkinter import *
import tkinter as tk
from tkinter import font

app = tk.Tk()
app.geometry("500x500")
app.title("Bold and Unbold") 

###Text-Field Frames###
TextField1 = tk.Text(app, width=20, height=2, wrap=tk.WORD,font=("Levetica",12))
TextField2 = tk.Text(app, width=20, height=2, wrap=tk.WORD,font=("Levetica",12))



def make_bold():
    
    TextField1.tag_config("bt", font=("Levetica", "12", "bold")) 
    TextField2.tag_config("bt", font=("Levetica", "12", "bold"))

    bold_font = font.Font(TextField1, TextField1.cget("font"))
    bold_font.configure(weight="bold")
        
    if TextField1.tag_add("bt", "sel.first", "sel.last"):
        TextField2.tag_config("bt", font=("Levetica", "12", "normal"))
            
        TextField1.tag_config("bt", font=("Levetica", "12", "bold"))
        
    if TextField2.tag_add("bt", "sel.first", "sel.last"):
        TextField1.tag_config("bt", font=("Levetica", "12", "normal"))
            
        TextField2.tag_config("bt", font=("Levetica", "12", "bold"))       
       

def make_unbold():

    bold_font = font.Font(TextField1, TextField1.cget("font"))
    bold_font.configure(weight="normal") 

    TextField1.tag_config("bt", font=("Levetica", "12", "bold")) 
    TextField2.tag_config("bt", font=("Levetica", "12", "bold"))
      
    if TextField1.tag_remove("bt", "sel.first", "sel.last"): 
        TextField2.tag_config("bt", font=("Levetica", "12", "normal")) 
            
        TextField1.tag_config("bt", font=("Levetica", "12", "normal"))

    if TextField2.tag_remove("bt", "sel.first", "sel.last"):
        TextField1.tag_config("bt", font=("Levetica", "12", "normal"))
            
        TextField2.tag_config("bt", font=("Levetica", "12", "normal"))
              
def main():
    
    ###place Text-Fields###
    TextField1.place(x=29,
                        y=50,
                        width=220,
                        height=200)
                    
    TextField2.place(x=29,
                        y=280,
                        width=220,
                        height=200)

    ###Button-Frames###
    ButtonMakeBold = Button(app, text="bold", font=("Levetica", "9", "bold"),command=make_bold)
    ButtonMakeBold.place(x=300, y=210, width=40, height=40)

    ButtonMakeUnbold = Button(app, text="unbold", font=("Levetica", "9", "bold"), command=make_unbold)
    ButtonMakeUnbold.place(x=300, y=260, width=40, height=40)

  


    app.mainloop()


if __name__ == '__main__':
    main()
    exit(0) 



Liebe Grüße,
Marlon
marlon_germany
User
Beiträge: 33
Registriert: Samstag 30. April 2022, 23:32

PS: Wenn ich z.B. für jedes Textfeld einen eigenen Bold und Unbold Button erstelle, funktioniert es.
Denn dann ist klar definiert, welcher Button für welches Text-Widget zuständig ist.

Aber wie macht man einem einzigen Bold und Unbold Button klar,
dass er immer nur bei einem Text-Widget zur Zeit wirken darf ???
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

Hallo marlon_germany,

würdest Du schauen, was dein Interpreter meldet, hättest Du wahrscheinlich die Fehler entdeckt.
Dein Programm weiss nicht, in welchem Feld Du den Text markierst. Markierst Du in Feld 1
und willst auf bold setzen, wirft der Interpreter eine Ausnahme für das Feld 2, weil in diesem Feld ja nichts ausgewählt wurde. Diese Ausnahmen soll man abfangen.
Für Gui Anwendungen wird objektorientierte Programmierung empfohlen, also Klassen.
Ich hab gar nicht erst probiert, dein Problem ohne Klasse zu lösen.
Das Text Widget bekommt schon von zu Hause den Event <<Selection>> mit. Mittels dieses Events, kann man sich merken, welches Textfeld markiert wurde. Ich merke mir das Feld in einer kleinen Liste.
Und bitte halte dich an die empfohlene Schreibweise, Variablennamen werden klein geschrieben. Sonst kann es sein, dass Du erst aufgefordert wirst dich an die Regeln zu halten, bevor dir geholfen wird.
Es macht echt keinen Spass die Schreibweise zu korrigieren.
Die Bezeichnung bt für den tag ist schlecht, wähle doch direkt bold. Soll bt bold_text bedeuten?
Place ist keine gute Wahl für den Windowmanager. Grid oder pack sind besser.

Diese Code Zeilen funktionieren, sind aber unlogisch. Erst wird der tag bt entfernt, dann werden die Zeichen mit dem tag bt umkonfiguriert.

Code: Alles auswählen

if TextField1.tag_remove("bt", "sel.first", "sel.last"): 
        TextField2.tag_config("bt", font=("Levetica", "12", "normal"))             
        TextField1.tag_config("bt", font=("Levetica", "12", "normal"))
Besser ist, auf den ausgewählten Zeichen das bold Tag zu entfernen und das
normal Tag anzuheften und umgekehrt. Hier mein Vorschlag:

Code: Alles auswählen

import tkinter as tk
from tkinter import font
              
class Gui():
    def __init__(self, root):        
        root.geometry("500x500")
        root.title("Bold and Unbold")
        self.in_field_selected_list = [0, 0]
        ###Text-Field Frames###
        self.textfield_1 = tk.Text(root, width=20, height=2, wrap=tk.WORD,
                             font=("Levetica", 12, 'normal'))
        self.textfield_1.bind('<<Selection>>', self.selected_field_1)
        
        self.textfield_2 = tk.Text(root, width=20, height=2, wrap=tk.WORD,
                             font=("Levetica",12))
        self.textfield_2.bind('<<Selection>>', self.selected_field_2)
        ###Button-Frames###
        tk.Button(root, text="bold", font=("Levetica", "9", "bold"),
                  command=self.determine_field_to_make_bold).place(x=300,
                                        y=210, width=80, height=40)
        tk.Button(root, text="unbold", font=("Levetica", "9", "bold"),
                  command=self.determine_field_to_make_unbold).place(x=300,
                                        y=260, width=80, height=40)
        ###place Text-Fields###
        self.textfield_1.place(x=29, y=50, width=220, height=200)              
        self.textfield_2.place(x=29, y=280, width=220, height=200)

    def determine_field_to_make_bold(self):
        self.check_selection()        
        if self.in_field_selected_list[0] == 1:
            field = self.textfield_1
            self.set_bold(field)
        if self.in_field_selected_list[1] == 1:
            field = self.textfield_2
            self.set_bold(field)

    def set_bold(self, field):
        bold_font = font.Font(self.textfield_1, self.textfield_1.cget("font"))
        bold_font.configure(weight="bold")
        field.tag_remove("normal", "sel.first", "sel.last")
        field.tag_add("bold", "sel.first", "sel.last")
        field.tag_config("bold", font=("Levetica", 12, "bold"))        
        self.in_field_selected_list == [0, 0]

    def determine_field_to_make_unbold(self):
        self.check_selection()        
        if self.in_field_selected_list[0] == 1:
            field = self.textfield_1
            self.set_unbold(field)
        if self.in_field_selected_list[1] == 1:
            field = self.textfield_2
            self.set_unbold(field)
            
    def set_unbold(self, field):
        bold_font = font.Font(self.textfield_1, self.textfield_1.cget("font"))
        bold_font.configure(weight="normal")
        field.tag_remove("bold", "sel.first", "sel.last")
        field.tag_add("normal", "sel.first", "sel.last")   
        field.tag_config("normal", font=("Levetica", "12", "normal"))       
        self.in_field_selected_list == [0, 0]

    def check_selection(self):
        if self.in_field_selected_list[0] == 1:
            try:
                self.textfield_1.selection_get()
            except tk.TclError:
                self.in_field_selected_list = [0, 0]
        if self.in_field_selected_list[1] == 1:
            try:
                self.textfield_2.selection_get()
            except tk.TclError:
                self.in_field_selected_list = [0, 0]

    def selected_field_1(self, event):
        self.in_field_selected_list = [1, 0]        

    def selected_field_2(self, event):
        self.in_field_selected_list = [0, 1]
       

def main():
    root = tk.Tk()
    Gui(root)
    root.mainloop()
    
if __name__ == '__main__':
    main()
    exit(0) 
Gruss Peter
Sirius3
User
Beiträge: 18051
Registriert: Sonntag 21. Oktober 2012, 17:20

@marlon_germany: da ist so viel unnützer Code dabei, dass man kaum die eigentliche Funktionalität erkennen kann. Man benutzt keine *-Importe, keine globalen Variablen und kein `exit` wenn es nicht nötig ist. Man benutzt keine `place` und keine fixen Fenstergrößen.

@peterpy: Du hast sehr viel kopierten Code, weil Du immer Fallunterscheidungen zwischen den beiden Textfeldern hast. Das kann man sich alles sparen, wenn man sich einfach das Textfeld, das gerade selektiert wurde, merkt:

Code: Alles auswählen

import tkinter as tk
from tkinter import font
              
class Gui():
    def __init__(self, root):        
        root.geometry("500x500")
        root.title("Bold and Unbold")

        self.selected_field = None
        self.textfield_1 = tk.Text(root, width=20, height=10, wrap=tk.WORD,
                             font=("Levetica", 12, 'normal'))
        self.textfield_1.bind('<<Selection>>', self.select_field)
        
        self.textfield_1.grid(row=0, column=0)
        self.textfield_2 = tk.Text(root, width=20, height=10, wrap=tk.WORD,
                             font=("Levetica",12))
        self.textfield_2.bind('<<Selection>>', self.select_field)
        self.textfield_2.grid(row=1, column=0)

        tk.Button(root, text="bold", font=("Levetica", "9", "bold"),
                  command=self.make_bold).grid(row=0, column=1)
        tk.Button(root, text="unbold", font=("Levetica", "9", "bold"),
                  command=self.make_unbold).grid(row=1, column=1)


    def make_bold(self):
        field = self.selected_field
        field.tag_remove("normal", "sel.first", "sel.last")
        field.tag_add("bold", "sel.first", "sel.last")
        field.tag_config("bold", font=("Levetica", 12, "bold"))

    def make_unbold(self):
        field = self.selected_field
        field.tag_remove("bold", "sel.first", "sel.last")
        field.tag_add("normal", "sel.first", "sel.last")   
        field.tag_config("normal", font=("Levetica", "12", "normal"))       

    def select_field(self, event):
        self.selected_field = event.widget
       

def main():
    root = tk.Tk()
    Gui(root)
    root.mainloop()
    
if __name__ == '__main__':
    main()
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

Hallo sirius3,
wird nichts, oder eine leere Zeile ausgewählt, soltte die Ausnahme doch noch abgefangen werden.

Code: Alles auswählen

import tkinter as tk
from tkinter import font
              
class Gui():
    def __init__(self, root):        
        root.geometry("500x500")
        root.title("Bold and Unbold")

        self.selected_field = None
        self.textfield_1 = tk.Text(root, width=20, height=10, wrap=tk.WORD,
                             font=("Levetica", 12, 'normal'))
        self.textfield_1.bind('<<Selection>>', self.select_field)
        
        self.textfield_1.grid(row=0, column=0)
        self.textfield_2 = tk.Text(root, width=20, height=10, wrap=tk.WORD,
                             font=("Levetica",12))
        self.textfield_2.bind('<<Selection>>', self.select_field)
        self.textfield_2.grid(row=1, column=0)

        tk.Button(root, text="bold", font=("Levetica", "9", "bold"),
                  command=self.make_bold).grid(row=0, column=1)
        tk.Button(root, text="unbold", font=("Levetica", "9", "bold"),
                  command=self.make_unbold).grid(row=1, column=1)
        self.chek_selection = False


    def make_bold(self):
        field = self.selected_field
        if self.chek_selection == True:
            field.tag_remove("normal", "sel.first", "sel.last")
            field.tag_add("bold", "sel.first", "sel.last")
            field.tag_config("bold", font=("Levetica", 12, "bold"))

    def make_unbold(self):
        field = self.selected_field
        if self.chek_selection == True:
            field.tag_remove("bold", "sel.first", "sel.last")
            field.tag_add("normal", "sel.first", "sel.last")   
            field.tag_config("normal", font=("Levetica", "12", "normal"))       

    def select_field(self, event):
        self.selected_field = event.widget
        try:
            self.selected_field.get("sel.first", "sel.last")
            self.chek_selection = True
        except tk.TclError:
            self.chek_selection = False
       

def main():
    root = tk.Tk()
    Gui(root)
    root.mainloop()
    
if __name__ == '__main__':
    main()
Gruss Peter
marlon_germany
User
Beiträge: 33
Registriert: Samstag 30. April 2022, 23:32

@peterpy: Vielen Dank ersteinmal für die Aufklärung worin das Problem lag und dein Code-Beispiel wie man es richtig macht!
Da hätte ich noch lange im Dunkeln herum getappt. Jetzt im Nachhinein ergibt es natürlich Sinn.
Mit der richtigen Schreibweise der Variablennamen merke ich mir.


@Sirius3:
Vielen Dank auch dir. Für die verkürzte Code-Version und um es noch einfacher zu halten.
Die Sache mit den globalen Variablen habe ich mir eingeprägt und werde von nun an darauf achten
es zu vermeiden. Ich habe mir euren richtigen Code-Aufbau genau unter die Lupe genommen um die Denkweise dahinter zu verstehen und auch OOP dadurch besser zu verstehen. Seitdem arbeite ich auch mit OOP. Hatte mich bisher immer davor gedrückt, da die Abschnitte dazu in den Büchern zu trocken und schwer verständlich waren.

Aber warum ratet ihr mir von der 'place' Layout Methode ab? Bietet sie nicht am meisten Positionierungs-Freiheit?
Pack hat zumindest für detailliertere Zwecke meiner Meinung nach zu wenig Auswahl.
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@marlon_germany: `place()` funktioniert schlicht nicht. Woher weisst Du denn wie die Pixelpositionen sein müssen? Wenn Du nicht weisst wie viele Pixel Schrift gross ist? Was für eine Auflösung verwendet wird? Welche Schriftart(en) verwendet werden? Das mag auf dem Rechner wo Du das platzierst alles nett aussehen, aber auf dem nächsten System vielleicht sogar unbenutzbar sein, wenn Widgets andere Widgets verdecken.

So sieht die GUI aus Deinem ersten Beitrag bei mir aus: Bild
Der zweite Button ist offensichtlich nicht breit genug. Und egal wie breit Du den machst, es besteht immer die Gefahr, das irgendwer eine Auflösungs-/Schriftgrössen-Kombination hat, bei der auch das nicht ausreicht. Oder es ist umgekehrt und es gibt riesige Leerflächen. Das passiert mit `pack()` und `grid()` nicht. Da ist alles so gross, das der Inhalt komplett dargestellt wird, egal welche Einstellungen auf dem Zielsystem sind.

Was für Freiheiten denkst Du denn zu brauchen? Die gezeigte GUI liesse sich problemlos mit einem Grid-Layout beschreiben. Oder mit Pack und Frames.

Ausserdem ist es einfacher in eine GUI die Layouts verwendet etwas einzufügen oder zu löschen, weil alles andere automatisch entsprechend verschoben wird um Platz zu machen oder freien Platz zu füllen. Bei einer GUI mit `place()` ist in solchen Fällen dann viel Handarbeit beim anpassen von allen betroffenen Elementen fällig.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
marlon_germany
User
Beiträge: 33
Registriert: Samstag 30. April 2022, 23:32

Ok ergibt Sinn. Danke für Info. Dann benutze ich ab jetzt die grid-Methode.
__blackjack__ hat geschrieben: Mittwoch 20. Juli 2022, 00:05 @marlon_germany: `place()` funktioniert schlicht nicht. Woher weisst Du denn wie die Pixelpositionen sein müssen? Wenn Du nicht weisst wie viele Pixel Schrift gross ist? Was für eine Auflösung verwendet wird? Welche Schriftart(en) verwendet werden? Das mag auf dem Rechner wo Du das platzierst alles nett aussehen, aber auf dem nächsten System vielleicht sogar unbenutzbar sein, wenn Widgets andere Widgets verdecken.

So sieht die GUI aus Deinem ersten Beitrag bei mir aus: Bild
Der zweite Button ist offensichtlich nicht breit genug. Und egal wie breit Du den machst, es besteht immer die Gefahr, das irgendwer eine Auflösungs-/Schriftgrössen-Kombination hat, bei der auch das nicht ausreicht. Oder es ist umgekehrt und es gibt riesige Leerflächen. Das passiert mit `pack()` und `grid()` nicht. Da ist alles so gross, das der Inhalt komplett dargestellt wird, egal welche Einstellungen auf dem Zielsystem sind.

Was für Freiheiten denkst Du denn zu brauchen? Die gezeigte GUI liesse sich problemlos mit einem Grid-Layout beschreiben. Oder mit Pack und Frames.

Ausserdem ist es einfacher in eine GUI die Layouts verwendet etwas einzufügen oder zu löschen, weil alles andere automatisch entsprechend verschoben wird um Platz zu machen oder freien Platz zu füllen. Bei einer GUI mit `place()` ist in solchen Fällen dann viel Handarbeit beim anpassen von allen betroffenen Elementen fällig.
Antworten