Workshop: Gui Entwicklung in tkinter

Fragen zu Tkinter.
BlackJack

@stefanxfg: Wenn die Dateien aus dem GUI-Designer kommen, dann muss der dafür sorgen das die Kodierung stimmt. Und dort müsste es dann auch eine Einstellung für die Kodierung geben wenn man das beeinflussen können möchte.

Edit: ANSI gibt's nicht wirklich als Kodierung, das ist so eine komische Windowseigenart das so zu nennen. Ist in der Regel dann tatsächlich cp1252.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

stefanxfg hat geschrieben:Das mag ja sein. Aber ich benötige die Lösung auch für den GUIDesigner, da ich ihn durchaus benutzen möchte und aber bisher die Dateien auf ANSI-Format exportiert werden. Das liegt nicht an Notepad++. Das Format muss irgendwo anders enzustellen gehen.
Du scheinst nicht zu verstehen, was ANSI bedeutet. Es ist eine Teilmenge von so ziemlich jedem Zeichenvorrat und was als ANSI abgespeichert wird, kannst Du sowohl mit UTF-8 wie auch mit ISO weiterverarbeiten.
BlackJack

@Melewo: Ich denke Du verwechselst gerade ANSI und ASCII. ;-)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@stefanxfg: Wenn die Dateien aus dem GUI-Designer kommen, dann muss der dafür sorgen das die Kodierung stimmt. Und dort müsste es dann auch eine Einstellung für die Kodierung geben wenn man das beeinflussen können möchte.
Also, was aus dem GuiDesigner kommt, stimmt. Das ist UTF-8 und dieser Kommentar wird seit vielleicht einer oder zwei Wochen mit generiert.

Dass der GuiDesigner alle möglichen ANSI Codierungen encodieren soll, das muss wohl nicht sein. Wenn jemand eine andere Codierung haben will - worum auch immer - kann er mit IDLE eine andere Kommentarzeile einfügen und dann neu abspeichern.

Man sollte eben nur einen passenden Editor nehmen, der die Codierung richtig erkennt und nicht statt ä dann irgendwie \#ef\#58
keine Ahnung, wie das aussieht, drin stehen hat.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

BlackJack hat geschrieben:@Melewo: Ich denke Du verwechselst gerade ANSI und ASCII. ;-)
Eigentlich nicht, denke ich mir.
Bei dem ANSI-Zeichencode handelt es sich um eine Erweiterung des ASCII-Codes.
https://de.wikipedia.org/wiki/ANSI-Zeichencode
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich meine, man sollte heutzutage utf-8 nehmen ubnd nicht veraltete ISO Normen für nationale Zeichensätze für ASII Werte von 128 bis 255.

Andere Schriften, wie chinesisch, haben viel mehr Zeichen und daher setzen sich heutige Zeichen auch aus mehr Bytes zusammen, wie auch bei utf-8.

Das geht eben mit utf-8:

Bild

Und auch hier geht es:

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk


class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        # widget definitions ===================================
        self.button = tk.Button(self,text='房屋, 屋顶, 爱屋及乌')
        self.button.pack()

if __name__ == '__main__':
    Application().mainloop()
UTF-8 ist eben der heutige Standard
BlackJack

@Alfons Mittelmeyer: Ich habe im Quelltext vom GuiDesigner aber kein `open()` zum schreiben gefunden bei dem irgendwie dafür gesorgt wird, das in UTF-8 gespeichert wird. Das passiert ja nicht automatisch, sondern höchstens zufällig, wenn man zum Beispiel Linux verwendet und dort in der Regel UTF-8 im System eingestellt ist ohne das der Anwender da etwas für tun muss. Unter Windows ist UTF-8 aber eher sehr selten die Systemeinstellung. Das muss der Anwender selbst irgendwo einstellen.

@Melewo: Doch, ziemlich sicher verwechselst Du ANSI und ASCII. Denn das was Du schreibst gilt für ASCII, aber nicht für ANSI. ASCII ist von den fast allen Zeichenkodierungen die wir hier im Westen benutzen eine Untermenge. Unter anderem ist ASCII auch von ANSI und von UTF-8 und von allen ISO-8859-*-Kodierungen und vielen Windows-Codepages eine Untermenge. ANSI ist dagegen weder von UTF-8 noch von den ISO-8859-*-Kodierungen eine Untermenge. Du kannst also nicht als ANSI speichern und problemlos als UTF-8 oder ISO-8859-irgendwas öffnen. Das geht aber mit ASCII.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Alfons Mittelmeyer hat geschrieben:Ich meine, man sollte heutzutage utf-8 nehmen ubnd nicht veraltete ISO Normen für nationale Zeichensätze für ASII Werte von 128 bis 255.
Womit Du zu 100% Recht hast.
Es war auch etwas abseits vom Thema, weil der Notepad++ halt alle Dateien, die keine speziellen Zeichen enthalten, die nicht im ANSI oder ASCII enthalten sind, als ANSI öffnet und speichert. So liegen bei mir nach einem Download z.B. alle Dateien einer WordPress-Installation lokal als ANSI-Dateien auf dem Rechner, obwohl diese im Web und lokal problemlos unter UTF-8 laufen.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons, ich denke Blackjack hat recht. Das heißt nicht, dass der GUIDesigner nicht gut ist. Du musst das eben anfangen, dass was bei dir oder einem anderen Editor automatisch passiert.
Wenn Notepad dann ANSI erkennt, ist das eben so. Schau mal nach, ob es da eine Deklaration gibt, so dass Notepad keine Fehler mehr macht.Nimm es daher als Ratschlag an und verbessere dein Programm, um das Fehlgehen andere Programme zu verhinder.

Übrigens stimmt das. Das Notepad hebt farblich wirklich wenig hervor.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

stefanxfg hat geschrieben:Übrigens stimmt das. Das Notepad hebt farblich wirklich wenig hervor.
Der hebt bei mir nicht weniger farblich hervor, als vergleichsweise bei PHP und darüber hinaus noch selbstdefinierte Funktionen, die bei PHP nicht hervorgehoben werden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Hallo Alfons, ich denke Blackjack hat recht. Das heißt nicht, dass der GUIDesigner nicht gut ist. Du musst das eben anfangen, dass was bei dir oder einem anderen Editor automatisch passiert.
Ja stimmt, ich habe mir das unter windows noch gar nicht angeschaut. Sondern nur in Linux. Unter Windows wird anscheinend cp1252 gschgrieben. Da paßt natürlich der Kommentar nicht.

Da muß ich, wie Blackjack bereits angemerkt hatte, bei den paar Stellen, wo ich speichere, dafür sorgen, dass da in UTF-8 geschrieben wird. Und gut wäre, wenn die Script-Files auch alle utf-8 wären, obwohl das in Python3 nichts ausmacht, solange man nicht editieren will. Naja ist nur eine Kleinigkeit. Aber man merkt es eben nicht, wenn man nur in Linux gearbeitet hat.

Das mache ich gleich.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Man findet of das, was hier in der ersten Zeile steht:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
Sieht aus, wie ein Linux Verzeichnis. Hat das eine Bedeutung?
BlackJack

@Alfons Mittelmeyer: Dadurch weiss Linux wie es die Datei ausführen soll. Wenn man die Datei mit ``chmod`` als ausführbar kennzeichnet, dann wird das nach dem ``#!`` genommen und der Dateiname hinten angehängt und dann das ganze ausgeführt. Also wenn das in einer Datei mit dem Namen ``hallo.py`` steht, dann würde bei der Eingabe von ``./hallo.py`` in dem Verzeichnis in einer Shell ``/usr/bin/env python hallo.py`` ausgeführt. Dadurch kann man in Linux jede Textdatei als Programm ausführen und sagen welches Programm die Textdatei interpretieren soll. Das Programm muss dann nur diese erste Zeile ignorieren, entweder explizit, oder weil das ``#`` sowieso als Kennzeichen für eine Kommentarzeile behandelt wird.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

BlackJack hat geschrieben:@Melewo: Doch, ziemlich sicher verwechselst Du ANSI und ASCII. Denn das was Du schreibst gilt für ASCII, aber nicht für ANSI. ASCII ist von den fast allen Zeichenkodierungen die wir hier im Westen benutzen eine Untermenge. Unter anderem ist ASCII auch von ANSI und von UTF-8 und von allen ISO-8859-*-Kodierungen und vielen Windows-Codepages eine Untermenge. ANSI ist dagegen weder von UTF-8 noch von den ISO-8859-*-Kodierungen eine Untermenge. Du kannst also nicht als ANSI speichern und problemlos als UTF-8 oder ISO-8859-irgendwas öffnen. Das geht aber mit ASCII.
Gut, dann dürfte es am Editor liegen, dass der solange in einer Datei nur Zeichen entsprechend ASCII enthalten sind, diese mit ANSI anzeigt, obwohl eigentlich ASCII zutreffend sein sollte. Jedenfalls gab es noch nie Probleme damit, weil ein Parser oder Interpreter die enthaltenen Codes richtig abarbeitet und allenfalls die Ausgabe oder Speicherung in einer DB von betroffen sein könnte. Bei einer Verbindung zur Datenbank muss ich nur einen Charset setzen, bei Regex war es auch mit Umlauten so.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Hallo Alfons, ich denke Blackjack hat recht. Das heißt nicht, dass der GUIDesigner nicht gut ist. Du musst das eben anfangen, dass was bei dir oder einem anderen Editor automatisch passiert.
Habe jetzt utf-8 Codierung bei allen read und write Dateioperationen implementiert. Ist schon auf GitHub. Damit dürften die Probleme unter Windows beseitigt sein.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Der GuiDesigner legt ja für jeden Container eine neue Klasse an. Wenn ich nun aus dem call_code eine Funktion, sagen wir über eine Buttonbetätigung aus einer Toolbbar, anspreche, möchte ich Hauptframes ein- und ausblenden.
Aber ehrlich gesagt bin ich zu faul um jeden Frame für alle Klassen anprechbar zu machen. Wäre das dann besser über Eventbroker zu bewerkstelligen?

Zur Verdeutlichung:
Buttonbetätigung aus Toolbar => liegt in einer Datei
Input in einem Hauptframe => liegt in anderer Datei
Output in einem Hauptframe => nochaml andere Datei
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

stefanxfg hat geschrieben:Aber ehrlich gesagt bin ich zu faul um jeden Frame für alle Klassen anprechbar zu machen. Wäre das dann besser über Eventbroker zu bewerkstelligen?
Das wäre auch völlig falsch. Da würdest Du ja den Code verflechten. Früher gab es mal im Auto ein Kabelgewirr. Heute erfolgt alles ordentlich über gemeinsame Busse.

Da fangen wir einmal an mit einem Schalter. Der Schalter sollte eine funktionelle Einheit sein, und überall verwendet werden können. Um natürlich etwas bewirken zu können, muss er angeschlossen werden.

Das ist die GUI für den Schalter (inklusive App):

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

#import DynTkInter as tk # for GuiDesigner

import switch_code

# Application definition ============================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.minsize(800, 100)
        # widget definitions ===================================
        self.switch = Switch(self,name='#0_switch')
        self.switch.pack(fill='both', expand=1)

class Switch(tk.Frame):

    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        self.myclass = 'Switch'
        # widget definitions ===================================
        self.canvas = Canvas(self,name='#1_canvas')
        self.canvas.place(x=0,y=0)

        # call Code ===================================
        switch_code.switch_init(self)

class Canvas(tk.Canvas):

    def __init__(self,master,**kwargs):
        tk.Canvas.__init__(self,master,**kwargs)
        self.config(height='100', relief='raised', highlightbackground='#e8e9ee', insertwidth='0', bg='#615e58', bd='10', selectborderwidth='0', highlightthickness='10', width='100', highlightcolor='lightgray')
        self.start_width = 100
        # widget definitions ===================================
        coords = (30,30,110,110)
        item = self.create_oval(*coords)
        self.itemconfig(item,width = '0.0',fill = '#ff7431',tags = 'led')


if __name__ == '__main__':
    #Application().mainloop('guidesigner/Guidesigner.py') # for GuiDesigner
    Application().mainloop()
Und das ist der Code:

switch_code.py

Code: Alles auswählen

def switch_init(self):

    # for connection =========================
    self.eventbroker = None
    self.message = None
    # ========================================

    # bindings ===============================
    
    def width_height(event):
        frame_height = event.height

        bd = frame_height/14
        canvas_height = frame_height-4*bd
        scale_factor = canvas_height/self.canvas_height_before
        self.canvas_height_before = canvas_height
        self.canvas.config(width = canvas_height,height = canvas_height,bd = bd,highlightthickness=bd)
        self.canvas.scale('led',0,0,scale_factor,scale_factor)

    def switch(event):
        self.on = not self.on
        self.canvas.itemconfig('led',fill = 'lightgreen' if self.on else '#ff7431')
        self.canvas['relief'] = 'sunken' if self.on else'raised'
        if self.eventbroker:
            self.eventbroker.publish(self.message,self.on)

    self.canvas_height_before = 100
    self.bind('<Configure>',width_height,'+')
    self.on = False
    self.canvas.bind('<Button-1>',switch)
Dieser Switch Schalter ist überall verwendbar für eine horizontale Toolbar, da er sich der Höhe anpasst und außerdem nicht selber auf irgendetwas außerhalb zugreift, außer man schließt ihn an.

Hierfür sind in Zeile 4 und 5 zwei Attribute vorgesehen, über die man ihn anschließen kann.

@stefanxfg: Frage, soll dieser Schalter in der Toolbar den Button beim Toggleframe ersetzten? Wenn Ja, dann könnte man ihn einmal anschließen.
stefanxfg
User
Beiträge: 85
Registriert: Sonntag 2. April 2017, 14:11

Hallo Alfons, nein. Der CHeckbutton im ToggleFrame ist nur für das Einklappen der SubFrames verantwortlich. Demnach kann er sogar lokal gesetzt werden.
Der Switch soll von der Toolbar aus gesteuert werden. Demnach von einer andere Datei aus.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@stefanxfg: Die Logik mit eventbroker ist doch ganz einfach.

Zuvor habe ich nochmals mir das Modul communication angeschaut. Es ist doch überflüssig, hier noch dein Modul zu importieren. Da sollte man doch auch gleich den event broker mit reinmachen:

communication.py:

Code: Alles auswählen

import sys
def output(param):
    sys.stdout.write(param+'\n')

class EventBroker():
    def __init__(self):
        self._dictionary_ = {}
 
    def subscribe(self,message_id,callback):
        if message_id in self._dictionary_:
            output('EventBroker: callback already defined for message id: {}\nThe callback before will be overwritten'.format(message_id))

        self._dictionary_[message_id] = callback
 
    def publish(self,message_id,*args,**kwargs):
        if message_id not in self._dictionary_:
            output('EventBroker: no callback defined for message: {},*{},**{}'.format(message_id,args,kwargs))
        else:
            self._dictionary_[message_id](*args,**kwargs)


eventbroker = EventBroker()
publish = eventbroker.publish
subscribe = eventbroker.subscribe
Dann denke ich mir. dass zuerst die Eingabe angezeigt werden soll und da ist der Schalder noch auf aus. Gedrückter Schalter heißt also 'AUSGABE_EIN'. Deshalb habe ich die Message mal so genannt. DAnn habe ich gesehen, dass der Code Aufruf bei der Eingage groß geschrieben war und 'InputCode' heißt. Dann wird das wohl eine Klasse sein und für die Ausgabe wird es wohl dann 'OutpuCode' heißen?

Jedenfalls wäre das dann die Lösung:

testcode.py

Code: Alles auswählen

from communication import eventbroker, publish, subscribe

class InputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_eingabe)

    def lift_eingabe(self,ausgabe_ein):
        if not ausgabe_ein:
            self.container.lift()

class OutputCode:

    def __init__(self,container):
        self.container = container
        subscribe('AUSGABE_EIN',self.lift_ausgabe)

    def lift_ausgabe(self,ausgabe_ein):
        if ausgabe_ein:
            self.container.lift()
Ach so, Du hattest mir mitgeteilt, bei Dir hätte lift nicht funktioniert. Du mußt für Eingabe und Ausgabe dieselbe Grid Zelle nehmen und keine andere Zeile oder Spalte, sonst kann es natürlich nicht funktionieren.
Antworten