Seite 1 von 1

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

Verfasst: Samstag 1. März 2008, 22:20
von HorstJENS
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()

Verfasst: Samstag 1. März 2008, 23:44
von BlackJack
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.

verbesserter Comic-Betrachter

Verfasst: Sonntag 2. März 2008, 11:06
von HorstJENS
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()

Re: verbesserter Comic-Betrachter

Verfasst: Sonntag 2. März 2008, 12:09
von Leonidas
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``.

Re: verbesserter Comic-Betrachter

Verfasst: Sonntag 2. März 2008, 13:01
von HorstJENS
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 ?

Re: verbesserter Comic-Betrachter

Verfasst: Sonntag 2. März 2008, 13:57
von lunar
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.

Verfasst: Sonntag 2. März 2008, 13:58
von audax
pygame suchst du.

Re: verbesserter Comic-Betrachter

Verfasst: Sonntag 2. März 2008, 14:00
von Leonidas
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.

sound

Verfasst: Sonntag 2. März 2008, 14:18
von HorstJENS
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?

Verfasst: Sonntag 2. März 2008, 14:27
von BlackJack
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"))

Verfasst: Sonntag 2. März 2008, 20:26
von numerix
Unter Linux kann man auch Snack einsetzen, um Klangdateien abzuspielen.
Braucht allerdings Tkinter - aber das ist ja in diesem Thread ohnehin dabei.

Verfasst: Sonntag 2. März 2008, 20:37
von lunar
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.

Verfasst: Sonntag 2. März 2008, 22:17
von Leonidas
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.