ZOOM für tkinter-GUI bei verschiedenen Displaygrößen?

Fragen zu Tkinter.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Habe mir mit tkinter eine GUI für meine Firmware erstellt.
Das Display meines PC ist 1680 x 1050 und meine tkinter-GUI habe ich darauf angepasst.
Leider habe ich versäumt, daß meine tkinter-GUI auch auf anderen Displaygrößen, die z.B. auch kleiner sind, verwenden zu können.

Nun habe ich das selbst gemachte Problem, daß auf meinem 15 Zoll Notebook, ein Teil meiner tkinter-GUI einfach abgechnitten ist.

Gibt es da eine Möglichkeit, die komplette GUI zu Zoomen, so daß alle sich darin befindlichen grafischen Elemente, der aktuellen Displaygröße anpassen?

Ich poste hier mal meine tkinter GUI (Grundgerüst), sollte bei Euch auch lauffähig sein.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x


"""Python Module"""
import os
import time
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import font
import tkinter.messagebox

import datetime
from datetime import date
now = date.today()
heute = now.strftime('%Y.%m.%d')

########################################################################

"""Programmname"""
PROGRAMMNAME = 'gui_work.py'

########################################################################

"""Hauptkonfiguration für die GUI Tkinter."""
CONFIG = {
    'width' : 0,
    'button_width' : 12,
    'title' : "EDV-Work",
    'start_font': ('NimbusSansL', 80), # Startfenster
    'info_font': ('NimbusSansL', 40), # Infofenster
    'bbig_font': ('NimbusSansL', 14, 'bold'), # Buttons
    'btxt_bground': 'darkgrey', # Buttons
    'bfont_white': 'white',
    'bfont_color': 'darkred',
    'big_font': ('NimbusSansL', 14), # Buttons, Texteingabe
    'sub_font': ('NimbusSansL', 12), # Buttons, Texteingabe
    'txt_font': ('NimbusSansL', 12), # Textausgabe
    'txt_bground': 'grey', # Texteingabe, Textfenster, Buttons
    'font_color': 'black',
    'back_ground' : 'darkgrey'
    }

########################################################################


def myinfo(text):
    """Aufruf Messagebox.
     Bei leerem Text myinfo(''), wird Text 'Bitte warten ...' ausgegeben.
     Bei Text z.B. myinfo('Möchten Sie da machen?'), wird mit Ja (True) 
     der Ablauf fortgesetzt und mit 'Nein' (False) abgebrochen."""
     
    if text == '':
        text = 'Bitte warten .....................'
    return tkinter.messagebox.askokcancel('Info', text)


def yesno(frage):
    """Aufruf Messagebox.
     Bei Frage z.B. yesno('Möchten Sie da machen?'), wird mit Ja (True) 
     der Ablauf fortgesetzt und mit 'Nein' (False) abgebrochen."""
     
    if frage != '':
        return tkinter.messagebox.askyesno('Frage', frage)


class MyButton(tk.Button):
    """Steuerung der Hauptbuttons"""

    selected_button_obj = None
    HIGHLIGHT_COLOR = 'green'
    HIGHLIGHT_CONF = dict(bg=HIGHLIGHT_COLOR,
        activebackground=HIGHLIGHT_COLOR)
   
    def __init__(self, parent, **options):
       
        try:
            self.command_call = options['command']
            del options['command']
        except KeyError:
            self.command_call = None
           
        tk.Button.__init__(self, parent, **options)
        self.bind('<Button-1>', self.callback)
        self.std_conf = dict(bg=self['bg'],
            activebackground=self['activebackground'])
       
    def callback(self, event=None):
        button_obj = MyButton.selected_button_obj
        if button_obj is not None:
            button_obj.config(self.std_conf)
               
        if self.command_call:
            self.config(MyButton.HIGHLIGHT_CONF)
            MyButton.selected_button_obj = self
            self.command_call(event.widget['text'])


class MySubButton(tk.Button):
    """Steuerung der Subbuttons"""

    selected_sub_button_obj = None
    HIGHLIGHT_COLOR = 'green'
    HIGHLIGHT_CONF = dict(bg=HIGHLIGHT_COLOR,
        activebackground=HIGHLIGHT_COLOR)
   
    def __init__(self, parent, **options):
       
        try:
            self.command_call = options['command']
            del options['command']
        except KeyError:
            self.command_call = None
               
        tk.Button.__init__(self, parent, **options)
        self.bind('<Button-1>', self.callback)
        self.std_conf = dict(bg=self['bg'],
            activebackground=self['activebackground'])
       
    def callback(self, event=None):
        sub_button_obj = MySubButton.selected_sub_button_obj
        if sub_button_obj is not None:
            sub_button_obj.config(self.std_conf)
               
        if self.command_call:
            self.config(MySubButton.HIGHLIGHT_CONF)
            MySubButton.selected_sub_button_obj = self
            self.command_call(event.widget['text'])

########################################################################

class View(object):

    def __init__(self, controller):
 
        self.root = tk.Tk()
        self.conf = CONFIG
        self.root.title(self.conf['title'])
        self.controller = controller

        xpos = 0
        ypos = 0
        self.screenx = self.root.winfo_screenwidth()
        self.screeny =  self.root.winfo_screenheight()
        self.root.geometry("%dx%d+%d+%d" % (self.screenx, self.screeny,
            xpos, ypos))

        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', fill='both', expand=True)

        # Frame links
        self.button_frame_left = tk.Label(self.frame, width=16,
            bg=self.conf['back_ground'])
        self.button_frame_left.pack(side='left', fill='both')

        # Frame rechts
        button_frame_right = tk.Label(self.frame,
            bg=self.conf['back_ground'])
        button_frame_right.pack(side='right', fill='both')

        # Frame für Textfenster
        self.winLabel = tk.Label(self.frame, bg=self.conf['back_ground'],
            fg='brown', text=self.conf['title'],
            font=(self.conf['start_font']))
        self.winLabel.pack(anchor='n', expand='YES', fill='both')

        # Frame unten
        self.label_down = tk.Label(self.frame, heigh=8,
            bg=self.conf['back_ground'])
        self.label_down.pack(anchor='s', fill='x')
        # Frame unten, für Beschriftung,Eingabefeld und Buttons 
        self.label_down1 = tk.Label(self.label_down, heigh=2,
            bg=self.conf['back_ground'])
        self.label_down1.pack(anchor='e')
        self.label_down2 = tk.Label(self.label_down, heigh=3,
            bg=self.conf['back_ground'])
        self.label_down2.pack(anchor='e', pady=10)
        self.label_down4 = tk.Label(self.label_down, heigh=3,
            bg=self.conf['back_ground'])
        self.label_down4.pack(anchor='w', side='left')
        self.label_down5 = tk.Label(self.label_down, heigh=3,
            bg=self.conf['back_ground'])
        self.label_down5.pack(anchor='e', side='right')


        self.listboxWindow = tk.Listbox(self.winLabel,
            bg=self.conf['txt_bground'], fg=self.conf['font_color'], 
            font=self.conf['big_font'], selectmode=tk.SINGLE,
            bd=0, highlightthickness=0)
        self.listboxWindow.pack(anchor='n', expand=True, fill='both')


        """Konfiguration Text-Eingabefelder unten"""
        # Text-Beschriftung
        self.blank = tk.Label(self.label_down1, text='', width=34, 
            bg=self.conf['back_ground'])
        self.blank.pack(anchor='e', side='left', padx=34, pady=10)
        self.txt = tk.Label(self.label_down1, text='', width=17, 
            bg=self.conf['back_ground'], fg=self.conf['font_color'],
            font=(self.conf['big_font']))
        self.txt.pack(anchor='e', side='left', padx=30)
        self.txt2 = tk.Label(self.label_down1, text='Textfeld', width=60, 
            bg=self.conf['back_ground'], fg=self.conf['font_color'],
            font=(self.conf['big_font']))
        self.txt2.pack(anchor='e', side='right', padx=20)
        # Text-Eingabe Suchfelder
        self.article = tk.Entry(self.label_down2, width=17, 
            bg=self.conf['txt_bground'], fg=self.conf['font_color'],
            font=(self.conf['big_font']))
        self.article.pack(anchor='e', side='left', ipady=3, padx=30)
        self.txt_line = tk.Entry(self.label_down2, width=60, 
            bg=self.conf['txt_bground'], fg=self.conf['font_color'],
            font=(self.conf['big_font']))
        self.txt_line.pack(anchor='e', side='right', ipady=3, padx=20)


########################################################################
## Button-Definition
## Hauptmenü
## Hauptbuttons (Masterbutton) rechte Seite
## Unterbuttons zu Hauptbuttons (Subbutton) linke Seite
## Buttons, Fensterleiste unten
########################################################################

        """Konfiguration Hauptmenü Kopfleiste"""
        # Hauptmenü
        self.mainMenu = tk.Menu(self.frame, bg=self.conf['back_ground'])
        self.root.config(menu=self.mainMenu)
        mainmenu = {
            '01_Datei' : 'Datei',
            '02_EDV' : 'EDV',
            '03_Sicherheit' : 'Sicherheit',
        }
        submenu = {
            '011_Öffnen' : ['Öffnen', ''],
            '012_Speichern' : ['Speichern', ''],
            '013_Einstellungen' : ['Einstellungen', ''],
            '014_Beenden' : ['Beenden', self.close_Window_Work],
            '021_Daten_Update' : ['Daten-Update', ''],
            '031_Backup' : ['Backup', ''],
            '032_Restore' : ['Restore', ''],
        }
        for m in sorted(mainmenu):
            name = ''.join(m[:2])
            name = tk.Menu(self.mainMenu)
            self.mainMenu.add_cascade(label=m[3:], menu=name)
            for s in sorted(submenu):
                if m[:2] == s[:2]:
                    name.add_command(label=submenu[s][0],
                        command=submenu[s][1])


        """Hauptbuttons rechts mit Bezug auf Unterbuttons links"""
        # Namen der Sub-Buttons für Kunden (linke Seite)
        sub_buttons_kunden = ['Neuanlage', 'Filialeanlage',
            'Statusänderung', 'LieferNorm', 'RechnungNorm', 'Kunden-ALT',
            'Formulare', 'Kunden', 'Bestellung', 'Auftrag', 'Angebot']
        # Namen der Sub-Buttons für Lieferanten (linke Seite)
        sub_buttons_lieferanten = ['Neuanlage', 'Filialeanlage',
            'Lieferanten-ALT', 'Formulare', 'Lieferanten', 'Bestellung',
            'Auftrag']
        # Namen der Sub-Buttons für Hersteller (linke Seite)
        sub_buttons_hersteller = ['Neuanlage', 'Hersteller',
            'Kurz-EAN', 'Produkt-EAN', 'Check-EAN', 'Datenupdate']
        # Produktgruppen   
        sub_buttons_gruppen = ['Basisgruppen', 'Hauptgruppen', 'Gruppen']
        # Namen der Sub-Buttons für Verwaltung (linke Seite)    
        sub_buttons_verwaltung = ['Raumkosten', 'Kommunikation',
            'Versandkosten', 'Beiträge', 'Beratung', 'Büroverbrauch']
        # Namen der Sub-Buttons für Aufträge (linke Seite)    
        sub_buttons_verwaltung2 = ['Lagerbestand', 'Leergut']
        # Namen der Sub-Buttons für Buchhaltung (linke Seite)    
        sub_buttons_buchhaltung = ['Kunden', 'Lieferanten', 'Änderung']
        # Daten-Update    
        sub_buttons_update = ['Backup', 'Restore', 'FullUpdate', 
            'Lieferantendaten', 'Update-EAN', 'Update-VE',
            'Update-Gruppen', 'Update-Base', 'Update-List', 'Bericht']
        # Namen der Sub-Buttons für Kalkulation (linke Seite)    
        sub_buttons_kalkulation = ['Hauptgruppen', 'Untergruppen',
            'Sonderpreise', 'Sonderprodukt', 'Update-Preise',
            'MSAccess-Datei']
        # Produkte    
        sub_buttons_produkte = ['Neuanlage', 'Basisdaten',
            'Produktdaten', 'Produktliste', 'Produkte']
   
        # Zusammenfassung aller Sub-Buttons
        self.my_sub_buttons = {
            'Kunden': sub_buttons_kunden,
            'Lieferanten': sub_buttons_lieferanten,
            'Hersteller': sub_buttons_hersteller,
            'Produktgruppen': sub_buttons_gruppen,
            'Verwaltung': sub_buttons_verwaltung,
            'Verwaltung2': sub_buttons_verwaltung2,
            'Buchhaltung': sub_buttons_buchhaltung,
            'Daten-Update': sub_buttons_update,
            'Kalkulation': sub_buttons_kalkulation,
            'Produkte': sub_buttons_produkte,
            }
   
        # Namen der Haupt-Buttons (rechte Seite)    
        my_buttons = ['Kunden', 'Lieferanten', 'Hersteller',
            'Produktgruppen', 'Verwaltung', 'Verwaltung2', 'Buchhaltung',
            'Daten-Update', 'Kalkulation', 'Produkte']

        # Erzeuge Haupt-Buttons (rechte Seite)
        for button_name in my_buttons:
            MyButton(button_frame_right, width=self.conf['button_width'],
            font=(self.conf['big_font']), text=button_name,
            command=self.button_callback).pack(padx=8, ipady=2, pady=19,
            fill='x')


        """Konfiguration der Buttons unten (Arbeitsbuttons)"""
        # Buttons unten
        button = {
            '01-Löschen' : lambda: self.check_base_button('Löschen'),
            '02-Erstellen' : lambda: self.check_base_button('Erstellen'),
            '03-Ändern' : lambda: self.check_base_button('Ändern'),
            '04-Suchen' : lambda: self.check_base_button('Suchen'),
            '05-Übernehmen' : lambda: self.check_base_button('Übernehmen'),
            '06-Zurück' : lambda: self.check_base_button('Zurück'),
            '07-Clean' : lambda: self.check_base_button('Clean'),
        }
        for index, b in enumerate(sorted(button)):
            if index < 3:
                self.workButtonLeft = tk.Button(self.label_down4, width=10, 
                    text=b.split('-')[1], bg=self.conf['btxt_bground'], 
                    font=(self.conf['bbig_font']), state='disabled',
                    fg=self.conf['bfont_color'], command=button[b])
                self.workButtonLeft.pack(side='left', padx=15, pady=10)
            if index > 2:
                self.workButtonRight = tk.Button(self.label_down5, width=11, 
                    text=b.split('-')[1], bg=self.conf['txt_bground'], 
                    font=(self.conf['big_font']), state='disabled', 
                    fg=self.conf['font_color'], command=button[b])
                self.workButtonRight.pack(side='left', padx=34, pady=10)
            if index == 0:
                self.workButton_del = self.workButtonLeft # Löschen
            if index == 1:
                self.workButton_new = self.workButtonLeft # Erstellen
            if index == 2:
                self.workButton_chance = self.workButtonLeft # Ändern
            if index == 3:
                self.workButton_search = self.workButtonRight # Suchen
            if index == 4:
                self.workButton_take = self.workButtonRight # Übernehmen
            if index == 5:
                self.workButton_back = self.workButtonRight # Zurück
            if index == 6:
                self.workButton_clean = self.workButtonRight # Clean

        # Abfangen von X-Button des Hauptfensters,
        # sowie über das Auswahlmenü 'Schließen' über die Fensterleiste.
        self.root.protocol("WM_DELETE_WINDOW", self.close_Window_Work)


########################################################################
## PROGRAMMENDE
## Überprüfung auf Datenänderung
## Manuelle Abfrage bei Datensicherung für geänderte Daten
########################################################################
 
    ## Programmende abfangen, zur Datensicherung
    def close_Window_Work(self):
        """Auswertung und Ablauf für Programmende"""

        text = 'Möchten Sie das Programm beenden?'
        if myinfo(text) == True:
            return self.root.quit(), self.root.destroy(), exit()


########################################################################
## StringVar()
## Übergebe die Daten aus Var an self.new_data
## Für Auswertung und Weierverarbeitung
########################################################################

    def my_new_data(self):
        """Hole die Daten von StringVar() aus:
        self.set_scrolled_entries(label_names, result)
        TWork.Controller(listresult, name, max_width, AB_SPALTENNUMMER)
        und übergebe diese zur Weiterverarbeitung."""

        self.new_data = dict()
        try:
            for index, entry_var in enumerate(self.entry_vars):
                self.new_data[index] = entry_var.get()
        except AttributeError:
            pass
        return self.new_data


########################################################################
## Button-Komunikations
## Hauptbuttons (Masterbutton) rechte Seite
## Unterbuttons zu Hauptbuttons (Subrbutton) linke Seite
## Funktionen:  button_callback
##              sub_button_callback
########################################################################

    def button_callback(self, button_name):
        """Rückruf der Haupt-Buttons"""
        print("Button (rechts): {0}".format(button_name))

        self.master_check = button_name
        self.sub_check = ''

        # Entferne alle bestehende Sub-Buttons aus dem linken Frame
        for button in self.button_frame_left.winfo_children():
            button.destroy()

        # Kontrolle: Hat der aktivierte rechte Button zugehörige Sub-Buttons?
        try:
            MySubButton.selected_sub_button_obj = None
            for button_name in self.my_sub_buttons[button_name]:
                MySubButton(self.button_frame_left,
                width=self.conf['button_width'],
                font=(self.conf['sub_font']), text=button_name,
                command=self.sub_button_callback).pack(padx=8, ipady=2, 
                pady=19, fill='x')
            # Ja es gibt Sub-Buttons!
        except KeyError:
            print("Für {} gibt es keine Liste mit Sub-Buttons!".format(
                button_name))


    def sub_button_callback(self, sub_button_name):
        """Rückruf der Sub-Buttons"""
        print("Sub-Button (links): {0}".format(sub_button_name))


    def run(self):
        self.frame.mainloop()


class Controller(object):
    def __init__(self):
        self.view = View(self)

    def run(self):
        self.view.run()


def main():
    Controller().run()


if __name__ == '__main__':
    main()
Grüße Nobuddy
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Nobuddy

Als erste Vorschlag ohne die ganze GUI abzuändern sehe ich nur ein scrollbares Fenster für kleinere Monitorabmessungen.

Gruss wuf :wink:
Take it easy Mates!
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo wuf,

an diese Möglichkeit, habe ich auch schon gedacht.

Das Problem ist allerdings, daß die Schriftgröße bei den Buttons, wie auch im Textfenster, bei kleineren Displayś zu groß ist und das Layout dadurch schlecht zu handeln ist.

Eine andere Möglichkeit, als die ganze GUI abzuändern, gibt es wohl dann nicht?

Ich habe mir schon etwas in der Art überlegt, weiß aber nicht ob dies auch der richtige Weg ist.

Code: Alles auswählen

        screen_basex = 1680 
        screen_basey = 1050
        xzoom = round(screen_basex / self.root.winfo_screenwidth())
        yzoom = round(screen_basey / self.root.winfo_screenheight())
        xpos = 0
        ypos = 0
        self.root.geometry("%dx%d+%d+%d" % (self.screenx, self.screeny,
            xpos, ypos))
Dann mit xzoom bzw. yzoom die ganzen grafischen Details anpassen.
Wäre das ein Weg, oder gibt es dafür noch eine bessere Lösung?

Grüße Nobuddy
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Vieleicht sollte man sich verschiedene Fontgroessengruppen vordefinieren. Das Programm waehlt dann in Abhaengigkeit der Screengroesse die passende Gruppe aus. Da muss man natuerlich vorher erst die passenden Fontgroessen fuer die unterschiedlichen Screens austesten.
:wink:
yipyip
BlackJack

Vielleicht sollte man auch einfach gar keine Schriftgrössen fest vorgeben, sondern darauf vertrauen, dass der *Benutzer* sein System schon so eingestellt haben wird, dass er die Texte gut lesen kann, ohne dass sie zu gross dargestellt werden. Wenn man unbedingt an den Schriftgrössen herumschrauben möchte, dann würde ich das relativ zu den Systemeinstellungen machen und am besten dem Benutzer noch eine Eingriffsmöglichkeit über eine Konfigurationsdatei geben.

Bei einer modernen GUI könnte man das GUI-Layout auch als Datendatei auffassen und vom Code getrennt handhaben. Und für kleinere Anzeigen, dann zum Beispiel ein komplett anderes Layout laden, ohne grosse Änderungen am Code vornehmen zu müssen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Nobuddy

Was ist die Auflösung deines 15 Zoll Notebook-Displays?

self.root.winfo_screenwidth() = ?
self.root.winfo_screenheight() = ?


Ich nehme an, dass du deine Anwendung nicht mit kleineren Auflösungen als dein 15 Zoll Notebook fahren möchtest?

Gruß wuf :wink:
Take it easy Mates!
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen und Danke für Euren Input!

@yipyip, hatte auch schon den Gedanken.
Mal abgesehen, daß ich kein kleineres Display wie 15 Zoll ansprechen möchte, gibt es doch erhebliche Displayvarianten, was dann das Layout betreffend verunklimpfen kann. Aber der Gedanke ist damit noch nicht verworfen. :wink:

@BlackJack, Dein Vorschlag geht in die Richtung von yipyip, aber noch ausgefeilter, womit ich mich anfreunden könnte.
Das mit den Schriftgrößen, den Systemeinstellungen zu überlassen, müßte ich mal testen.
Ich komme auf jeden Fall, nochmals auf Deinen Vorschlag zurück, brauche da einfach mehr Zeit ... denn die Räder drehen sich bei mir nicht mehr ganz so schnell. :wink:

@wuf, wie schon bei yipyip, soll 15 Zoll das Kleinste sein, kleiner macht dann keinen Sinn mehr.
Hier mal die Ausgabe von meinem 15 Zoll Notebook:
self.root.winfo_screenwidth() = 1600
self.root.winfo_screenheight() = 900

Melde mich wieder, wenn ich ein Stückchen weiter bin, bzw. wenn von Euch weiterer Input kommt. :wink:

Grüße Nobuddy
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, habe gerade mal an einem kleinen Beispiel getetstet, wie es ist keine Vorgaben in den Schriftgrößen zu mach, so daß die Systemeinstellungen greifen. Geht schon in die richtige Richtung, auch wenn manche Systemeinstellungen für meine GUI einfach zu klein sind. Aber an den Systemeinstellungen (Kubuntu 12.04) möchte ich nichts ändern, da diese ja speziell für das Layout des Systems erstellt wurden.
Die einzige Meldung, was mit ausgegeben wurde, war:
Forntconfig warning :"/etc/fonts/conf.d/50-user.conf", line 9: reading configurations from ~/.fonts.conf is deprecated.
Die andere Möglichkeit, was Du schon angesprochen hast, wäre das:
BlackJack hat geschrieben:Bei einer modernen GUI könnte man das GUI-Layout auch als Datendatei auffassen und vom Code getrennt handhaben. Und für kleinere Anzeigen, dann zum Beispiel ein komplett anderes Layout laden, ohne grosse Änderungen am Code vornehmen zu müssen.


Wäre das dann, so etwas in der Art, nur Oberes ausgelagert als Class:

Code: Alles auswählen

screenlayout = {'1600 x 900' : {'textfont' : 11, 
            'buttonfont' : 12, usw....},
            '1680 x 1050' : {'textfont' : 12, 
            'buttonfont' : 14, usw....}
}

self.root = tk.Tk()

xpos = 0
ypos = 0
self.screenx = self.root.winfo_screenwidth()
self.screeny =  self.root.winfo_screenheight()
for row in screenlayout.get('{} x {}'.format(self.screenx, self.screeny)):
            if row == 'textfont':
                        self.txtfont = screenlayout[row]
            usw....

self.root.geometry("%dx%d+%d+%d" % (self.screenx, self.screeny,
            xpos, ypos))
Bestimmt, hast Du da noch einen besseren Vorschlag.

Grüße Nobuddy
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Bei meinem letzten Post, war die for-Schleife noch nicht ganz richtig, so funktioniert es:

Code: Alles auswählen

        self.screenx = self.root.winfo_screenwidth()
        self.screeny =  self.root.winfo_screenheight()
        for row in [self.screenlayout.get('{} x {}'.format(self.screenx, self.screeny))]:
            self.txtfont = row.get('textfont')
            self.btfont = row.get('buttonfont')
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Ich habe jetzt mal angefangen, die Konfiguration des Layouts, in die Datei 'screenconf.py' auszulagern.
Diese sieht mal vorerst so aus:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x


class ScreenConf(object):
    """
    Gebe für die Displaygröße, das entsprechende Layout aus.
    """

    def __init__(self, screenx, screeny):

        self.screenx = screenx
        self.screeny = screeny


    def myscreen(self):

        screenconf = {
            '1600 x 900' : {'textfont' : 10, 'textfont2' : 12, 
                'buttonx' : 10, 'frameunteny' : 7, 'distanzx' : 32, 
                'distanzx2' : 28, 'distanzx3' : 18, 'distanzx4' : 13, 
                'eingabex' : 15, 'eingabex2' : 55},
            '1680 x 1050' : {'textfont' : 12, 'textfont2' : 14, 
                'buttonx' : 12, 'frameunteny' : 8, 'distanzx' : 34, 
                'distanzx2' : 30, 'distanzx3' : 20, 'distanzx4' : 15, 
                'eingabex' : 17, 'eingabex2' : 60}
            }

        # Schriftart
        self.font = 'NimbusSansL'

        screenline = [screenconf.get('{} x {}'.format(self.screenx,
            self.screeny))]
        for row in screenline:
            self.txt = (self.font, row.get('textfont'))
            self.txt2 = (self.font, row.get('textfont2'))
            self.txt2b = (self.font, row.get('textfont2'), 'bold')
            self.btx = row.get('buttonx')
            self.fluy = row.get('frameunteny')
            self.distanzx = row.get('distanzx')
            self.distanzx2 = row.get('distanzx2')
            self.distanzx3 = row.get('distanzx3')
            self.distanzx4 = row.get('distanzx4')
            self.eingabex = row.get('eingabex')
            self.eingabex2 = row.get('eingabex2')

        self.fc = 'black'   # Schriftfarbe schwarz
        self.bfcdg = 'darkgrey' # Schriftfarbe dunkelgrau, Buttons
        self.bfcdr = 'darkred'    # Schriftfarbe dunkelrot, Buttons
        self.bg = 'grey'    # Hintergrundfarbe grau
                            # Texteingabe, Textfenster, Buttons

        return (self.txt, self.txt2, self.txt2b, self.btx, self.fluy,
            self.distanzx, self.distanzx2, self.distanzx3, 
            self.distanzx4, self.eingabex, self.eingabex2, self.fc, 
            self.bfcdg, self.bfcdr, self.bg)


if __name__ == '__main__':
    # Test
    ScreenConf(1680, 1050).myscreen()
So kann ich dann von jeder GUI, die zu diesem Layout gehört, das entsprechende Layout abrufen.

Code: Alles auswählen

"""Private Module"""
from screenconf import ScreenConf

        self.screenx = self.root.winfo_screenwidth()
        self.screeny =  self.root.winfo_screenheight()
        self.root.geometry("%dx%d+%d+%d" % (self.screenx,
            self.screeny, xpos, ypos))

        # Layout Definition
        (self.txt, self.txt2, self.txt2b, self.btx, self.fluy,
            self.distanzx, self.distanzx2, self.distanzx3, 
            self.distanzx4, self.eingabex, self.eingabex2, self.fc, 
            self.bfcdg, self.bfcdr, self.bg) = ScreenConf(self.screenx,
            self.screeny).myscreen()
Denke mal, daß ich auf dem richtigen Weg bin, auch wenn sich da noch vieles verändern und verbessern lässt.

Bitte gebt mir Input, für weitere Veränderungen bzw. Verbesserungen!

Grüße Nobuddy
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum schreibst Du alle Formatierungen in »ScreenConf.myscreen« in Attribute? Alle Attribute einer Instanz sollten in »__init__« angelegt werden. Zum Ersten ist es unnötig, die Eigenschaften in Attribute zu schreiben, wenn Du sie als Rückgabewert zurückgibst, zum Zweiten ist die ganze Funktion »myscreen« seltsam. Statt die Layoutdefinitionen im Hauptteil des Programms zu kopieren, scheint mir die Klasse »ScreenConf« als richtiger Ort zum Sammeln der ganzen Information.
Die Namen sind auch noch nicht optimal. Es ist z.B. aus dem Name nicht ersichtlich, wann distanzex2 und wann distanzx4 zu verwenden ist.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo Sirius3

Danke für Deine Info. :wink:

Ich habe die Attribute in »__init__« angelegt und habe das Ganze so geändert:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x


class ScreenConf(object):
    """
    Gebe für die Displaygröße, das entsprechende Layout aus.
    """

    def __init__(self, screenx, screeny):

        self.screenx = screenx
        self.screeny = screeny

        # Schriftart
        self.font = 'NimbusSansL'
        self.screenconf = {
            '1600 x 900' : {'textfont' : 10, 'textfont2' : 12, 
                'buttonx' : 10, 'frameunteny' : 7, 'distanzx' : 32, 
                'distanzx2' : 28, 'distanzx3' : 18, 'distanzx4' : 13, 
                'eingabex' : 15, 'eingabex2' : 55},
            '1680 x 1050' : {'textfont' : 12, 'textfont2' : 14, 
                'buttonx' : 12, 'frameunteny' : 8, 'distanzx' : 34, 
                'distanzx2' : 30, 'distanzx3' : 20, 'distanzx4' : 15, 
                'eingabex' : 17, 'eingabex2' : 60}
            }

        self.screenline = [self.screenconf.get('{} x {}'.format(
            self.screenx, self.screeny))]


    def myscreen(self):
        """
        Rüchgabewert:
        - Text normal, für Textausgabe
        - Text ca. 16 % größer als normal, für Buttons und Texteingabe
        - wie oben, Schrift fett
        - Breite von Buttons
        - Höhe von unterem Frame
        - Breite Distanz
        - Breite Distanz2
        - Breite Distanz3
        - Breite Distanz4
        - Breite Eingabe Textfeld
        - Breite Eingabe2 Textfeld
        - Schriftfarbe schwarz
        - Schriftfarbe weiß
        - Schriftfarbe dunkelgrau, Buttons
        - Schriftfarbe dunkelrot, Buttons
        - Hintergrundfarbe grau, für Texteingabe, Textfenster, Buttons
        """

        mylist = list()
        for row in self.screenline:
            mylist.append((self.font, row.get('textfont')))
            mylist.append((self.font, row.get('textfont2')))
            mylist.append((self.font, row.get('textfont2'), 'bold'))
            mylist.append(row.get('buttonx'))
            mylist.append(row.get('frameunteny'))
            mylist.append(row.get('distanzx'))
            mylist.append(row.get('distanzx2'))
            mylist.append(row.get('distanzx3'))
            mylist.append(row.get('distanzx4'))
            mylist.append(row.get('eingabex'))
            mylist.append(row.get('eingabex2'))
            mylist.append('black')
            mylist.append('white')
            mylist.append('darkgrey')
            mylist.append('darkred')
            mylist.append('grey')

        print(mylist)
        return mylist


if __name__ == '__main__':
    # Test
    ScreenConf(1680, 1050).myscreen()
Ich hoffe, daß es so ist, wie Du es mir geschrieben hast?

Ja mit den Namen hab ichś, liegt wohl unter anderem an meinen absolut schlechten Englischkenntnissen.

Das mit distanzex, wird hoffe ich zukünftig nicht mehr benötigt werden, waren bisher Platzhalter.
Da bin ich dabei, dies zu lösen.

Grüße Nobuddy
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hier nach etwas optimaler.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x


class ScreenConf(object):
    """
    Gebe für die Displaygröße, das entsprechende Layout aus.
    """

    def __init__(self, screenx, screeny):

        self.screenx = screenx
        self.screeny = screeny

        # Schriftart
        self.font = 'NimbusSansL'

        # Definition für Displaygröße
        0   # textfont
        1   # textfont2
        2   # buttonx
        3   # frameunteny
        4   # distanzx
        5   # distanzx2
        6   # distanzx3
        7   # distanzx4
        8   # eingabex
        9   # eingabex2
        self.screenconf = {
            '1600 x 900' : {0 : 10, 1 : 12, 2 : 10, 3 : 7, 4 : 32, 
                5 : 28, 6 : 18, 7 : 13, 8: 15, 9 : 55},
            '1680 x 1050' : {0 : 12, 1 : 14, 2 : 12, 3 : 8, 4 : 34, 
                5 : 30, 6 : 20, 7 : 15, 8 : 17, 9 : 60}
            }

        self.screenline = [self.screenconf.get('{} x {}'.format(
            self.screenx, self.screeny))]


    def myscreen(self):
        """
        Rüchgabewert:
        - Text normal, für Textausgabe
        - Text ca. 16 % größer als normal, für Buttons und Texteingabe
        - wie oben, Schrift fett
        - Breite von Buttons
        - Höhe von unterem Frame
        - Breite Distanz
        - Breite Distanz2
        - Breite Distanz3
        - Breite Distanz4
        - Breite Eingabe Textfeld
        - Breite Eingabe2 Textfeld
        - Schriftfarbe schwarz
        - Schriftfarbe weiß
        - Schriftfarbe dunkelgrau, Buttons
        - Schriftfarbe dunkelrot, Buttons
        - Hintergrundfarbe grau, für Texteingabe, Textfenster, Buttons
        """

        mylist = list()
        for i in self.screenline[0]:
            if i < 2:
                mylist.append((self.font, self.screenline[0].get(i)))
            if i == 1:
                mylist.append((self.font, self.screenline[0].get(i),
                    'bold'))
            if i > 1:
                mylist.append(self.screenline[0].get(i))
        mylist.append('black')
        mylist.append('white')
        mylist.append('darkgrey')
        mylist.append('darkred')
        mylist.append('grey')

        print(mylist)
        return mylist


if __name__ == '__main__':
    # Test
    ScreenConf(1680, 1050).myscreen()
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Bei meiner Test-GUI, die ich am Anfang gepostet habe, habe ich folgendes bemerkt.

Wenn ich gleich nach

Code: Alles auswählen

        # Layout Definition
        (self.txt, self.txt2, self.txt2b, self.fluy,
            self.distanzx, self.distanzx2, self.distanzx3, 
            self.distanzx4, self.eingabex, self.eingabex2, self.fc, 
            self.fcw, self.fcdg, self.fcdr, self.bg) = ScreenConf(
            self.screenx, self.screeny).myscreen()
        print(self.txt2)
mir self.txt2 ausgeben lasse, so erhalte ich den richtigen Wert
('NimbusSansL', 14)
Wenn ich aber den Wert von self.txt2 weiter unten in meiner GUI, bei meinen Buttons in font eingebe, wird dieser Wert nicht verwendet.
Zum Überprüfen, habe ich oberhalb der Buttons, mir per Print-Anweisung nochmals den Wert ausgeben lassen, da erhalte ich dann dies:
.140046823793168.140046823796112.140046823796240.140046823870928
PS Nachtrag:
Habe gerade festgestellt, daß mit

Code: Alles auswählen

self.mytxt = copy.copy(self.txt2)
der Wert auch unten erhalten bleibt.

Kann mir von Euch Jemand sagen, wieso und warum dies so ist?

Grüße Nobuddy
BlackJack

@Nobuddy: Weil Du den Wert in der Zwischenzeit an etwas anderes gebunden hast vielleicht? Zum Beispiel an ein `Label`-Exemplar? Sowas steht jedenfalls hier irgendwo weiter oben.
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Das heißt dann, wenn der Wert irgendwo weiter oben schon verwendet wurde, ist er weiter unten nicht mehr da.

PS: Das mit copy.copy(wert), auch auch nur funktioniert, weil ich einen anderen Namen dafür vergeben habe, als weiter oben. :?

Wie kann ich das lösen, wenn der Wert mehrmals in der GUI benötigt wird?

Nachtrag:
Ob es die Lösung jetzt ist, weiß ich nicht.
Wenn ich mir das Layout so hole

Code: Alles auswählen

        self.mylayout = ScreenConf(self.screenx, self.screeny).myscreen()
und dann über jede Zeile, die einer der Werte aus self.mylayout benötigt, dies z.B. einfüge:

Code: Alles auswählen

        self.txt2 = copy.copy(self.mylayout[1])
dann funktioniert es.

Gibt es da evtl. noch eine bessere Lösung?
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, jetzt habe ich verstanden, was Du mir mit Deinem letzten Post, sagen wolltest.

Ich habe tatsächlich den Namen 'self.txt' und 'self.txt2' bei Labels vergeben, klar das nicht funktionieren kann .... :oops:
Nach dem ich das behoben habe, funktioniert es ohne Klimmzüge ... :wink:

Grüße Nobuddy
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, habe das Ganze optimiert und dürfte etwa dem entsprechen, was Du am Anfang mit auslagern usw. geschrieben hast.

Meine gui_screendefinition.txt, beinhaltet die Displaygröße mit Größenwerten (TAB-getrennt).
1600 x 900 10 11 7 28 25 16 12 14 49
1680 x 1050 12 14 8 34 30 20 15 17 60
Meine gui_screenconf.py, gibt das entsprechende Displaylayout aus, bzw. wenn das Displaylayout in gui_screendefinition.txt nicht vorhanden ist, wird das neue Displaylayout berechnet und in die gui_screendefinition.txt hinzugefügt und anschließend gleich ausgegeben.
Für die Berechnung des neuen Layouts, verwende ich als Basis 1680 x 1050 und setzte ein Verhältnis dazu.
Das neue Displaylayout, kann dann nach eigenen Wünschen verändert werden.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x


import os
import csv
import codecs
from functools import partial

"""Private Module"""
import data_load as Data


## Schreiben Daten in Datei
# Schreibe die Daten von datenpool in filename
def write_csv(filename, datenpool):
    with codecs.open(filename, "w") as zielfile:
        writer = csv.writer(zielfile, delimiter="\t", quotechar="^")
        writer.writerows(datenpool)

my_reader = partial(csv.reader, delimiter='\t', quotechar='^')

class ScreenConf(object):
    """
    Gebe für die Displaygröße, das entsprechende Layout aus.
    Rüchgabewert:
    - Text normal, für Textausgabe
    - Text ca. 16 % größer als normal, für Buttons und Texteingabe
    - wie oben, Schrift fett
    - Höhe von unterem Frame
    - Breite Distanz
    - Breite Distanz2
    - Breite Distanz3
    - Breite Distanz4
    - Breite Eingabe Textfeld
    - Breite Eingabe2 Textfeld
    - Schriftfarbe schwarz
    - Schriftfarbe weiß
    - Schriftfarbe dunkelgrau, Buttons
    - Schriftfarbe dunkelrot, Buttons
    - Hintergrundfarbe grau, für Texteingabe, Textfenster, Buttons
    """

    def __init__(self, screenx, screeny):

        self.screenx = screenx
        self.screeny = screeny
        self.base_xy = 1680, 1050
        self.font = 'NimbusSansL'   # Schriftart
        self.col1 = 'black'
        self.col2 = 'white'
        self.col3 = 'darkgrey'
        self.col4 = 'darkred'
        self.col5 = 'grey'

        self.filelist = 'screendefinition'
        self.screen = Data.ListDef(self.filelist, 'screen')
        with codecs.open(Data.Path(self.filelist), 'r') as infile:
            for row in my_reader(infile):
                if row[self.screen] == '{} x {}'.format(self.screenx,
                        self.screeny):
                    self.screenline = row


    def myscreen(self):
        """
        Berechne die neue Displaygröße anhand der Basiswerte.
        Erstelle Datensatz für neues Display in 'gui_screendefinition'.
        """

        try:
            self.mylist = list()
            for i, row in enumerate(self.screenline):
                if i != 0 and i < 3:
                    self.mylist.append((self.font, row))
                if i == 2:
                    self.mylist.append((self.font, row, 'bold'))
                if i > 2:
                    self.mylist.append(row)
            self.mylist.append(self.col1)
            self.mylist.append(self.col2)
            self.mylist.append(self.col3)
            self.mylist.append(self.col4)
            self.mylist.append(self.col5)
            return self.mylist
        except AttributeError:
            self.x_diff = round((self.screenx / self.base_xy[0]), 2)
            self.y_diff = round((self.screeny / self.base_xy[1]), 2)
            self.diff = self.x_diff * self.y_diff
            data = list()
            with codecs.open(Data.Path(self.filelist), 'r') as infile:
                for row in my_reader(infile):
                    if row[self.screen] == '{} x {}'.format(self.base_xy[0],
                        self.base_xy[1]):
                        self.baseline = row
                    data.append(row)
            mynewlist = list()
            for i, row in enumerate(self.baseline):
                if i == 0:
                    mynewlist.append('{} x {}'.format(self.screenx,
                        self.screeny))
                else:
                    mynewlist.append(round(float(row) * self.diff))
            newdata = list()
            newdata.append(mynewlist)
            [newdata.append(row) for row in data]
            write_csv(Data.Path(self.filelist), sorted(newdata))
            mylist = list()
            for i, row in enumerate(self.mynewlist):
                if i != 0 and i < 3:
                    self.mylist.append((self.font, row))
                if i == 2:
                    self.mylist.append((self.font, row, 'bold'))
                if i > 2:
                    self.mylist.append(row)
            self.mylist.append(self.col1)
            self.mylist.append(self.col2)
            self.mylist.append(self.col3)
            self.mylist.append(self.col4)
            self.mylist.append(self.col5)
            return mylist


if __name__ == '__main__':
    # Test
    ScreenConf(1600, 900).myscreen()
Um eine Gui mit den Werten zu steuern, habe ich folgenden Eintrag in der GUI.

Code: Alles auswählen

"""Private Module"""
from gui_screenconf import ScreenConf

class View(object):

    def __init__(self, controller):
        self.port = prog_check(PROGRAMMNAME)
        print('Port: %s, PID: %s' % (self.port, os.getpid()))
 
        self.root = tk.Tk()
        self.title = "EDV-Work"
        self.root.title(self.title)
        self.controller = controller
        xpos = 0
        ypos = 0
        self.screenx = self.root.winfo_screenwidth()
        self.screeny =  self.root.winfo_screenheight()
        self.root.geometry("%dx%d+%d+%d" % (self.screenx,
            self.screeny, xpos, ypos))

        # Layout Definition
        self.mylayout = ScreenConf(self.screenx, self.screeny).myscreen()
        self.txt = copy.copy(self.mylayout[0])
        self.txt2 = copy.copy(self.mylayout[1])
        self.txt2b = copy.copy(self.mylayout[2])
        self.fluy = copy.copy(self.mylayout[3])
        self.distanzx = copy.copy(self.mylayout[4])
        self.distanzx2 = copy.copy(self.mylayout[5])
        self.distanzx3 = copy.copy(self.mylayout[6])
        self.distanzx4 = copy.copy(self.mylayout[7])
        self.eingabex = copy.copy(self.mylayout[8])
        self.eingabex2 = copy.copy(self.mylayout[9])
        self.fc = copy.copy(self.mylayout[10])
        self.fcw = copy.copy(self.mylayout[11])
        self.fcdg = copy.copy(self.mylayout[12])
        self.fcdr = copy.copy(self.mylayout[13])
        self.bg = copy.copy(self.mylayout[14])
Funktionieren tut es, auch mit neuen Displaygrößen.
Hoffe, daß nicht all zuviele Fehler drin sind ... :wink:

Grüße Nobuddy
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum verwendest Du kein beschreibenderes Dateiformat, wie z.B. JSON?
Die ganze Konstruktion mit »AttributeError« verstehe ich nicht. Zudem hast Du noch fehlerhafte Zuweisungen. Mal benutzt Du »self.mylist« und mal nur »mylist«. Dabei sind sowohl der Name als auch die Konstruktion an sich schwer zu durchschauen und fehleranfällig. Weist Du überhaupt, was Du mit »copy.copy« kopierst? (Kleiner Tipp: gar nichts, weil bei nicht veränderbaren Datentypen die übergebene Referenz zurückgegeben wird)
Nobuddy
User
Beiträge: 996
Registriert: Montag 30. Januar 2012, 16:38

Hallo Sirius3
Sirius3 hat geschrieben:Warum verwendest Du kein beschreibenderes Dateiformat, wie z.B. JSON?
JSON, habe ich schon mal gehört, wußte aber nicht, daß es ein Dateiformat ist.
Was ist da so besonderes dran?
Sirius3 hat geschrieben:Die ganze Konstruktion mit »AttributeError« verstehe ich nicht.
In der <__init__> vergleiche ich die mit 'screenx, screeny' übergebene Displaygröße in meiner 'gui_screendefinition.txt', ob diese vorhanden ist. Wenn ja, so wird dies an 'self.screenline' übergeben. Ist diese Displaygröße noch nicht vorhanden, so erfolgt in der Funktion 'myscreen' eben der »AttributeError«, womit es dann dort unter 'except AttributeError' weitergeht.
Sirius3 hat geschrieben:Zudem hast Du noch fehlerhafte Zuweisungen. Mal benutzt Du »self.mylist« und mal nur »mylist«. Dabei sind sowohl der Name als auch die Konstruktion an sich schwer zu durchschauen und fehleranfällig.
Da werde ich nochmals drüber schauen und es korrigieren.
Sirius3 hat geschrieben:Weist Du überhaupt, was Du mit »copy.copy« kopierst? (Kleiner Tipp: gar nichts, weil bei nicht veränderbaren Datentypen die übergebene Referenz zurückgegeben wird)
Ok, dann ist das mit »copy.copy« überflüssig, werde das korrigieren.

Grüße Nobuddy
Antworten