(Tkinter) bitte kritisieren ... geht das eleganter ?

Fragen zu Tkinter.
Antworten
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

Samstag 1. März 2008, 22:20

Hi, habe mich endlich mit Tkinter angefreundet und einen kleinen Bildbetrachter geschrieben.
Gedacht ist das ganze als Comic-Vorleser (espeak liest Text vor).

Ich möchte sowohl das Bild in einem Photoimage als auch den dazugehörigen Text möglichst elegant umschalten können, bis jetzt mache ich das mit .config().
Meine Frage: geht der Tkinter-Code so oder geht das eleganter?

Code: Alles auswählen


import Tkinter as t
import PIL.Image
import PIL.ImageTk
import os

f=file("liste2.txt")
#Jede Zeile in diesem file soll das Format haben:
#filename | Beschreibung (langer Text)

#Beispiel:
#foto.png | das ist ein Foto von einem Hund

liste = []
while True:
	line = f.readline()
	if len(line)==0:
		break
	worte = line.split("|")
	liste.append((worte[0].strip(), worte[1])) 
f.close()
class App:
    def __init__(self, master):
        frame = t.Frame(master)
        frame.pack()
        self.bildnummer = 0
        self.bildobjekt = PIL.Image.open(liste[self.bildnummer][0])
        self.photo = PIL.ImageTk.PhotoImage(self.bildobjekt)
        self.bildname=t.Label(frame, text=liste[self.bildnummer][0])
        self.bildname.pack(side=t.TOP)
        self.bildplatz=t.Label(frame, image=self.photo)
        self.bildplatz.pack(side=t.TOP)
        self.schriftplatz=t.Label(frame, text=liste[self.bildnummer][1])
        self.schriftplatz.pack(side=t.TOP)
        self.button = t.Button(frame, text="Quit",
                               fg="red", command=frame.quit)
        self.button.pack(side=t.LEFT)
        self.sprich=t.Button(frame, text="vorlesen", command=self.vorlesen)
        self.sprich.pack(side=t.LEFT)      
        self.bildwechsel = t.Button(frame, text="naechstes Bild", command=self.bild_weiter)
        self.bildwechsel.pack(side=t.LEFT)
    def vorlesen(self):
    	os.system('espeak'' ''"' +liste[self.bildnummer][1] +'"')
    def bild_weiter(self):
    	self.bildnummer += 1
    	if self.bildnummer >= len(liste):
    		self.bildnummer = 0
    	self.bildobjekt = PIL.Image.open(liste[self.bildnummer][0])
    	self.photo = PIL.ImageTk.PhotoImage(self.bildobjekt)
    	self.bildplatz.config(image=self.photo)
    	self.bildname.config(text=liste[self.bildnummer][0])
    	self.schriftplatz.config(text=liste[self.bildnummer][1])    	
root = t.Tk()
app = App(root)
root.mainloop()
root.destroy()
http://spielend-programmieren.at
BlackJack

Samstag 1. März 2008, 23:44

Für meinen Geschmack fehlen Leerzeilen und der Code auf Modulebene sollte in einer Funktion verschwinden. Damit fiele dann auch auf, dass `liste` (nichtssagender Name) "global" ist -> Böse™ ;-)

Über Dateiobjekte kann man direkt iterieren:

Code: Alles auswählen

def main():
    pictures = list()
    lines = open('liste2.txt')
    for line in lines:
        name, description = line.split('|', 1)
        pictures.append(name.strip(), description.strip())
    lines.close()
Du bindest alle Namen in `App` an das Objekt, da sind einige dabei, mit denen man das nicht machen muss.

In der `__init__()` und in `bild_weiter()` wiederholt sich ein gutes Stück Quelltext, den man in eine `bild_anzeigen()`-Methode auslagern könnte.

`os.system` ist keine gute Idee. Das `subprocess`-Modul und ohne Umweg über eine Shell gibt zum Beispiel keine Probleme wenn der Beschreibungstext 'Und die Kuh sagt "Muh"!' lautet.

Beim weitersetzen der Bildnummer kann man mit Modulorechnung arbeiten: ``self.bildnummer = (self.bildnummer + 1) % len(self.pictures)``. Falls man immer nur in eine Richtung "blättern" können soll, kann man sich das mit der Nummer komplett sparen und mit `itertools.cycle()` arbeiten.
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

Sonntag 2. März 2008, 11:06

Danke für die Kritik!

Ich habe fast alles berücksichtigt, bis auf den Modulo... ich will vor- und zurückblättern. Ich ahne aber das dies auch besser lösbar ist.

Andere Frage zu Tkinter: ich mag manche Buttons mit Grafik darstellen, brauch ich dafür immer 2 Zeilen (einmal mit PIL Grafikobjekt erzeugen und dann mit Tk in ein PhotoImage umwandeln) ?
Ich freue mich das es funktioniert aber es kommt mir umständlich vor.

Andere Frage: ist "play" der korrekte Weg um wave-dateien abzuspielen ? Wie mache ich das am einfachsten unter Windows oder Mac ?

===================

Code: Alles auswählen

# -*- coding: utf-8 -*-
import Tkinter as t
import PIL.Image
import PIL.ImageTk
import csv
import subprocess

#liste3.csv wird eingelesen und sollte folgendes format haben:
#csv format, Textrenner ist das doppelte Anführungszeichen, Feldtrenner ist das Komma
#erste Spalte der Filename
#zweite Spalte die Beschreibung
#restlichen Spalten (optional) filenamen von wav-Dateien
#z.B:
#"foto.png", "Es war ein Mal ein Hund..."
#"foto2.png", "der hörte ein Geräusch", "ufo.wav", "wuff.wav"

#zum vorlesen muss espeak installiert sein:
#http://espeak.sourceforge.net/download.html

#die soundeffekte werden über das Kommando "play" abgespielt

class App:
    def __init__(self, master):

        inputfile=file("liste3.csv", "rb")
        self.pictures = []
        
        for row in csv.reader(inputfile):
        	self.pictures.append(row) 
        inputfile.close()
    	
        frame = t.Frame(master)
        frame.pack(side=t.TOP, ancho=t.W)
        self.bildnummer = 0
        self.fxnummer = 0
        self.max_fxnummer = 0

        self.button = t.Button(frame, text="Quit",
                               fg="red", command=frame.quit)
        self.button.pack(side=t.LEFT)

        self.pfeilobjeklinks = PIL.Image.open("pfeil_links.png")
        self.pfeillinks = PIL.ImageTk.PhotoImage(self.pfeilobjeklinks)
        self.retour = t.Button(frame, image=self.pfeillinks, command=self.bild_retour)
        self.retour.pack(side=t.LEFT)
        
        self.pfeilobjektrechts = PIL.Image.open("pfeil_rechts.png")
        self.pfeilrechts = PIL.ImageTk.PhotoImage(self.pfeilobjektrechts)
        self.bildwechsel = t.Button(frame,  image=self.pfeilrechts, command=self.bild_weiter)
        self.bildwechsel.pack(side=t.LEFT)

        #self.hi_there=t.Button(frame, text="hello", command=self.say_hi)
        #self.hi_there.pack(side=t.LEFT)

        self.sprich=t.Button(frame, text="vorlesen", command=self.vorlesen)
        self.sprich.pack(side=t.LEFT)
        
        self.fx=t.Button(frame)
        self.fx.pack(side=t.LEFT)
        
        self.bildname=t.Label(frame)
        self.bildname.pack(side=t.LEFT)

        frame2 = t.Frame(master)
        frame2.pack(side=t.TOP)

        self.bildplatz=t.Label(frame2)
        self.bildplatz.pack(side=t.TOP)

        self.schriftplatz=t.Label(frame2)
        self.schriftplatz.pack(side=t.TOP)

        self.zeichne_Bild()

    #def say_hi(self):
    #    print "hallo zusammen"
        
    def vorlesen(self):
    	retcode = subprocess.call(["espeak",self.pictures[self.bildnummer][1]])
    	
    def play_fx(self):
    	retcode = subprocess.call(["play",self.pictures[self.bildnummer][self.fxnummer+1]])
    	if self.fxnummer == self.max_fxnummer:
    		self.fxnummer = 1
    	else:
    		self.fxnummer += 1
    	self.zeichne_Bild()
    	
    def get_fx(self):
    	self.fxnummer=0
    	self.max_fxnummer=0
        if len(self.pictures[self.bildnummer])> 2:
        	if len(self.pictures[self.bildnummer][2]) > 0:
        	    self.fxnummer = 1
        	    self.max_fxnummer=1
        	    for soundfile in self.pictures[self.bildnummer][3:]:
        	        if soundfile <> "":
        	        	self.max_fxnummer +=1
        
    def bild_weiter(self):
    	self.bildnummer += 1
    	if self.bildnummer > len(self.pictures)-1:
    		self.bildnummer = 0
    	self.get_fx()
    	self.zeichne_Bild()
    	
    def bild_retour(self):
    	self.bildnummer -= 1
    	if self.bildnummer < 0:
    		self.bildnummer = len(self.pictures)-1
    	self.get_fx()
    	self.zeichne_Bild()
    	
    def zeichne_Bild(self):
    	self.bildobjekt = PIL.Image.open(self.pictures[self.bildnummer][0])
    	self.photo = PIL.ImageTk.PhotoImage(self.bildobjekt)
        self.bildname.config(text="Bild Nr %i  von %i : %s" % (self.bildnummer +1 , len(self.pictures), self.pictures[self.bildnummer][0]))
    	self.bildplatz.config(image=self.photo)
    	self.schriftplatz.config(text=self.pictures[self.bildnummer][1]) 
    	if self.fxnummer > 0:
    		self.fx.config(text="Soundeffekt %i von %i"%(self.fxnummer, self.max_fxnummer), command=self.play_fx)
    	else:
    		self.fx.config(text="kein Soundeffekt", command="")

root = t.Tk()
app = App(root)

root.mainloop()
root.destroy()
http://spielend-programmieren.at
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 2. März 2008, 12:09

HorstJENS hat geschrieben:Andere Frage: ist "play" der korrekte Weg um wave-dateien abzuspielen ? Wie mache ich das am einfachsten unter Windows oder Mac ?
Naja, auf meinem Gentoo-System gibt es kein ``play``. Dafür gibts das Modul ``ossaudiodev`` und unter Windows ``winsound``.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

Sonntag 2. März 2008, 13:01

Leonidas hat geschrieben: Dafür gibts das Modul ``ossaudiodev`` und unter Windows ``winsound``.
die dokumentation zu ossaudiodev auf www.python.org verstehe ich nicht (brauch ein Code-Schnipsel), im wiki habe ich nichts gefunden und im Netz nur dasda:

Code: Alles auswählen

from wave import open as waveOpen
from ossaudiodev import open as ossOpen
s = waveOpen('boing.wav','rb')
(nc,sw,fr,nf,comptype, compname) = s.getparams( )
dsp = ossOpen('/dev/dsp','w')
try:
	from ossaudiodev import AFMT_S16_NE
except ImportError:
	if byteorder == "little":
		AFMT_S16_NE = ossaudiodev.AFMT_S16_LE
	else:
		AFMT_S16_NE = ossaudiodev.AFMT_S16_BE
dsp.setparameters(AFMT_S16_NE, nc, fr)
data = s.readframes(nf)
s.close()
dsp.write(data)
dsp.close()
Das erzeugt statt wohlkingenden Tönen nur ein hässliches Quäken...
kannst du mir ein Beispiel zeigen anhand dessen ich sehe wie man wav-files unter linux korrekt abspielt ?
http://spielend-programmieren.at
lunar

Sonntag 2. März 2008, 13:57

Leonidas hat geschrieben:
HorstJENS hat geschrieben:Andere Frage: ist "play" der korrekte Weg um wave-dateien abzuspielen ? Wie mache ich das am einfachsten unter Windows oder Mac ?
Naja, auf meinem Gentoo-System gibt es kein ``play``. Dafür gibts das Modul ``ossaudiodev`` und unter Windows ``winsound``.
ossaudiodev ist ziemlich unbrauchbar. Das Modul ist zu komplex, um benutzbar zu sein, und OSS selbst ist schon lange veraltet.

Ich würde unter Linux "aplay" zum Abspielen von Wav's nutzen, das ist Bestandteil der alsa-utils und somit auf fast jedem System vorhanden.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Sonntag 2. März 2008, 13:58

pygame suchst du.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 2. März 2008, 14:00

lunar hat geschrieben:ossaudiodev ist ziemlich unbrauchbar. Das Modul ist zu komplex, um benutzbar zu sein, und OSS selbst ist schon lange veraltet.
Unter *BSD ist es immer noch die erste Wahl.. naja, auch die einzige.
lunar hat geschrieben:Ich würde unter Linux "aplay" zum Abspielen von Wav's nutzen, das ist Bestandteil der alsa-utils und somit auf fast jedem System vorhanden.
Ja, ``aplay`` geht, aber wie gesagt - für die BSDs müsste man sich dann was anderes einfallen lassen.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
HorstJENS
User
Beiträge: 123
Registriert: Donnerstag 9. Februar 2006, 21:41
Wohnort: Wien, Österreich
Kontaktdaten:

Sonntag 2. März 2008, 14:18

danke für den Hinweis mit aplay

pygame: ja, unter pygame macht sound abspielen wesentlich mehr Spaß :-)
Ich dachte unter den Standardmodulen gibt's was ähnlich praktisches.

PIL / Tk.PhotoImage:
ist das echt so umständlich (2 Zeilen Variablen zuweisen nur um ein Bild mit Tkinter darzustellen) oder mach ich das unnötig kompliziert?
http://spielend-programmieren.at
BlackJack

Sonntag 2. März 2008, 14:27

Eine Zeile kannst Du natürlich einsparen, wenn Du eine "Zwischenvariable" weglässt:

Code: Alles auswählen

a = PIL.Image.open("x.png")
b = PIL.ImageTk.PhotoImage(a)

# =>

b = PIL.ImageTk.PhotoImage(PIL.Image.open("x.png"))
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Sonntag 2. März 2008, 20:26

Unter Linux kann man auch Snack einsetzen, um Klangdateien abzuspielen.
Braucht allerdings Tkinter - aber das ist ja in diesem Thread ohnehin dabei.
lunar

Sonntag 2. März 2008, 20:37

pütone hat geschrieben:Unter Linux kann man auch Snack einsetzen, um Klangdateien abzuspielen.
Braucht allerdings Tkinter - aber das ist ja in diesem Thread ohnehin dabei.
Wenn schon eine externe Abhängigkeit ins Spiel gebracht wird, kann man auch gleich der Empfehlung von audax folgen und pygame nehmen, das ist wenigstens portabel.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 2. März 2008, 22:17

Ja, der Stdlib fehlt definitiv eine brauchbare Möglichkeit, Sound auszugeben. Klar, Pygame existiert, aber das kann schon wieder zu viel. Pymedia ist irgendwie seltsam und Pysonic baut auf FMOD auf, welches non-free ist. Da ist IMHO durchaus noch Platz für eine schönere Lösung, die es dann in die Stdlib schaffen könnte.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Antworten