Abstand zwischen Widgets / Sprachenänderung

Fragen zu Tkinter.
Antworten
GuardDog
User
Beiträge: 45
Registriert: Mittwoch 5. April 2017, 20:53
Wohnort: Hannover

Montag 25. Dezember 2017, 21:41

Hallo liebe Community,

erstmal wünsche ich euch (und eurer Familie) eine frohe und besinnliche Weihnachtszeit. Nun aber zu meinen 2 Fragen:

1. Ich möchte einen vertikalen Abstand zwischen meinen Widgets (Button) haben, Ich habe mich bereits im Internet schlaugemacht und die "pady"-Methode entdeckt. Komischer Weise fügt diese ein Abstand innerhalb des Buttons hinzu. Als Layoutmanager nutze ich "grid".

2. Möchte ich einen Weg haben, um die Sprache meiner Applikation umzuschalten. Dies möchte ich mit zwei Buttons mit Länderflaggen umsetzten. Wie ich die Bilder hinzufüge ist mir klar, aber gibt es eine Möglichkeit, um die Applikation praktisch neu zu laden,, allerdings mit anderssprachigen Buttons sobald man den dazugehörigen Knopf gedrückt hat? Ich würde es mit der ".destroy()"-Methode versuchen, aber die Umsetzung ist mir etwas schleierhaft.

Ich benutze Python in der Version 3.6. Hier mein Code:

Code: Alles auswählen

from tkinter import *
import os

root = Tk()
root.title("Republic at War 1.2")

def install():
	os.system('start "" "data\installer\Republic at War 1.2.exe"')

def uninstall():
	os.system('start "" "data\installer\RaW Uninstaller.exe"')

def launcher():
	print("test")

def depatch():
	print("test")

def manual():
	os.system('"start "" "data\nmanual.pdf"')

install_btn = Button(root, text="Install", command=install, relief="solid", borderwidth=1, pady=1, width=45)
uninstall_btn = Button(root, text="Uninstall", command=uninstall, relief="solid", borderwidth=1, pady=1, width=45)
launcher_btn = Button(root, text="Apply launcher", command=launcher, relief="solid", borderwidth=1, pady=1, width=45)
depatch_btn = Button(root, text="Install the german patch", command=depatch, relief="solid", borderwidth=1, pady=1, width=45)
manual_btn = Button(root, text="Read the manual", command=manual, relief="solid", borderwidth=1, pady=1, width=45)

install_btn.grid(row=1, column=1)
uninstall_btn.grid(row=2, column=1)
launcher_btn.grid(row=3, column=1)
depatch_btn.grid(row=4, column=1)
manual_btn.grid(row=5, column=1)

root.mainloop()
Benutzeravatar
noisefloor
User
Beiträge: 2423
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Montag 25. Dezember 2017, 22:13

Hallo,

du solltest deine Code mal dringend in eine Klasse packen, dass ist sauberer, übersichtlicher, besser wartbar.

Zum Abstand: IMHO musst du den Abstand dem `install_btn.grid(row=1, column=1)` mitgeben, also z.B. `install_btn.grid(row=1, column=1, pady=15)`, siehe auch: http://effbot.org/tkinterbook/grid.htm

Zur Sprache: von Prinzip sollte das so gehen, dass du ein Event an den Button bindest, dass bei einem Klcik darauf alle Labels / Text / Beschriftungen / ... ändert.

`os.system` ist veraltet, das steht auch wörtlich in der Python-Doku. Besser ist, das `subprocess` Modul zu nutzen.

Sternchenimporte sind schlecht, weil du dir dann alles mögliche in einen Namensraum holst - was zu unerwünschten und schwer nachzuvollziehenden Nebeneffekten führen kann.

Gruß, noisefloor
GuardDog
User
Beiträge: 45
Registriert: Mittwoch 5. April 2017, 20:53
Wohnort: Hannover

Montag 25. Dezember 2017, 22:20

Hi,

erstmal ein großes DANKE :) für deine schnelle Antwort. Leider habe ich das Nutzen von *-importen und das "klassenlose Programmieren" so gelernt und bin mit den anderen Methoden nie so richtig warm geworden. Aber da jetzt ja Ferien sind, hab' ich ja genug Zeit!
suk
User
Beiträge: 17
Registriert: Sonntag 17. Dezember 2017, 01:18

Montag 25. Dezember 2017, 23:09

Ein Ansatz wäre, Deine Applikation erstmal ohne Beschriftungen mit leeren Platzhaltern aufzubauen.
Dann könntest Du Dir zusätzlich eine Funktion "Beschriftung" schreiben, welche Dir per config alle Beschriftungen aus einer Liste zieht und setzt. Die Liste ließe sich ja z.B. auch als ini-Datei o.ä. speichern und von extern warten.
Die Liste lädst Du dann je nach Sprachauswahl länderspezifisch und rufst noch "Beschriftung" auf.
So würdest Du Dir die Rechenleistung für das Destroy und Neubauen aller Grafikelemente sparen.

Hinweis: Bin selber noch kräftig am lesen und lernen und kann somit leider noch kein konkretes Beispiel bringen. Das wäre jedoch mein theoretischer Ansatz, um flexible Beschriftungen leicht nachpflegbar zu realisieren.
Benutzeravatar
wuf
User
Beiträge: 1481
Registriert: Sonntag 8. Juni 2003, 09:50

Dienstag 26. Dezember 2017, 01:25

Hi GuardDog

Hier dein Skript als Klasse und den angepassten vertikal- & horizontal Abständen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
from functools import partial

try:
    # Tkinter for Python 2.xx
    import Tkinter as tk
except ImportError:
    # Tkinter for Python 3.xx
    import tkinter as tk

APP_TITLE = "Republic at War 1.2"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 300
APP_HEIGHT = 200

BUTTONS = [
    "Install",
    "Uninstall",
    "Apply launcher",
    "Install the german patch",
    "Read the manual"
    ]

SUBPROCESSES = [
    ['os', 'start "" "data\installer\Republic at War 1.2.exe"'],
    ['os', 'start "" "data\installer\RaW Uninstaller.exe"'],
    ['launcher', "test"],
    ['dispatch', "test"],
    ['os', '"start "" "data\nmanual.pdf"']
    ]

    
class Application(tk.Frame):

    def __init__(self, master):
        self.master = master
        tk.Frame.__init__(self, master)
        
        for row, button_name in enumerate(BUTTONS):
            tk.Button(self, text=button_name, relief='solid', bd=1, width=45,
                command=partial(self.button_callback, row)
                ). grid(row=row, column=0, padx=2, pady=2)
                
    def button_callback(self, row):
        function, text = SUBPROCESSES[row]

        if function == 'os':
            os.system(text)
            # evt. Durch 'subprocess' ersetzen
            
        if function == 'launcher':
            print(text)
            
        if function == 'dispatch':
            print(text)
        
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    #app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = Application(app_win).pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()      
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
noisefloor
User
Beiträge: 2423
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Dienstag 26. Dezember 2017, 08:48

Hallo,

@wuf: in SUBPROCESSES musst du die Backslashs mit eine Backslash escapen - so funktioniert das nicht. `\n` ist sonst ein Linebreak. Außerdem benutzt du auch immer noch `os.system` statt `subprocess`.

Gruß, noisefloor
Sirius3
User
Beiträge: 8102
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 26. Dezember 2017, 09:48

@wuf: statt zwei Listen, in denen die Information parallel gespeichert wird, solltest Du nur eine gemeinsame verwenden. Wenn man mehrere sich ausschließende if hat, dann benutzt man elif.

Code: Alles auswählen

from functools import partial
import tkinter as tk
from subprocess import call

ACTIONS = [
    ("Install", "start", r"data\installer\Republic at War 1.2.exe"),
    ("Uninstall", "start", r"data\installer\RaW Uninstaller.exe"),
    ("Apply launcher", "launch", "test"),
    ("Install the german patch", "dispatch", "test"),
    ("Read the manual", "start", r"data\nmanual.pdf"),
]
   
class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Republic at War 1.2")
       
        for row, (title, command, argument) in enumerate(ACTIONS):
            tk.Button(self, text=title, relief='solid', bd=1, width=45,
                command=partial(self.button_callback, row)
                ). grid(row=row, column=0, padx=2, pady=2)
               
    def button_callback(self, command. argument):
        if command == 'start':
            call(["start", "", argument])
        elif command == 'launcher':
            print(text)
        elif command == 'dispatch':
            print(text)
       
def main():
    app = Application()
    app.mainloop()
 
if __name__ == '__main__':
    main()
GuardDog
User
Beiträge: 45
Registriert: Mittwoch 5. April 2017, 20:53
Wohnort: Hannover

Dienstag 26. Dezember 2017, 23:25

Hey,

eine Frage hätte ich da noch. Wie kann man eine Datei kopieren? Ich würde hier wieder os.system nehmen. Der Zielordner soll eine Variable sein. Geht das irgendwie?
Sirius3
User
Beiträge: 8102
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 26. Dezember 2017, 23:46

@GuardDog: dafür gibt es shutil.copyfile
Benutzeravatar
wuf
User
Beiträge: 1481
Registriert: Sonntag 8. Juni 2003, 09:50

Sonntag 31. Dezember 2017, 12:12

noisefloor hat geschrieben:@wuf: in SUBPROCESSES musst du die Backslashs mit eine Backslash escapen - so funktioniert das nicht. `\n` ist sonst ein Linebreak. Außerdem benutzt du auch immer noch `os.system` statt `subprocess`.
Danke für deinen Hinweis. Habe das linear geschriebene Skript des OP's in eine Klasse umgewandelt. Die von dir erwähnten Textzeilen, welche mich nicht weiter interessierten habe ich mit Copy & Paste vom Beitrag übernommen und in eine Liste eingefügt. Wollte dessen Korrektur dem OP überlassen.

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

Sonntag 31. Dezember 2017, 12:18

Sirius3 hat geschrieben:@wuf: statt zwei Listen, in denen die Information parallel gespeichert wird, solltest Du nur eine gemeinsame verwenden. Wenn man mehrere sich ausschließende if hat, dann benutzt man elif.
Danke Sirius3 für deine lehrreichen Hinweise und die sukksessive Verfeinerung des Skripts. Bei deinem Skript hat sich noch ein kleines Fehlerchen eingeschlichen:

Code: Alles auswählen

def button_callback(self, command. argument)
korrigiert:

Code: Alles auswählen

def button_callback(self, command, argument)
Gruss wuf :wink:
Take it easy Mates!
Antworten