OO PNG als Button

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
jonbob
User
Beiträge: 14
Registriert: Montag 24. November 2008, 10:40

Hallo Leute,
ich fange gerade an Python zu lernen. Ich habe schon Erfahrung mit Java und will daher vonanfangan Python Objekt-Orientiert programmieren.
Ich habe bisher folgenden Code geschrieben:

Code: Alles auswählen


import glob
from Tkinter import *
from PIL import Image, ImageTk

contentDir = '…\Content'
imageDir= r'\Images\Button'
menueDir=r'\Main\*'

mainFrame =Tk()

def generateButton(mainFrame, contentDir, menueDir, imageDir):
    image = Image.open(contentDir+imageDir+r"\button_menue_up.png")
    photo = ImageTk.PhotoImage(image)
    Button(mainFrame, image=photo).pack()
    mainFrame.mainloop()
      
generateButton(mainFrame, contentDir, menueDir, imageDir)


Es wird ein Button mit einem PNG als Hintergrund geladen. Jetzt will ich das ganze OO programmieren. (Später will ich mit der Klasse mehrere Button erzeugen). Ich habe es bis her so umgesetzt :

Code: Alles auswählen

import os,sys
import glob
from Tkinter import *
from PIL import Image, ImageTk

class MyButton(Frame):
    
    def __init__(self,contentDir,imageDir,menueDir,parent=None):
       Frame.__init__(self, parent)
       b1= self.generateButton( contentDir, menueDir, imageDir)
       b1.pack()

   def generateButton(self, contentDir, menueDir, imageDir):
       image = Image.open(contentDir+imageDir+r"\button_menue_up.png")
       photo = ImageTk.PhotoImage(image)
       button=Button(self, text="jjjo", image=photo, command = self.printSS) #, image=photo, 
       return button

if __name__ == '__main__':
    window = MyButton(r'…\Content', r'\Images\Button', r'\Main\*')
    window.pack()
    window.mainloop()

Mir unerklärlicherweise wird das Bild vom Button dargestellt noch wird die „Event“ Methode (self.printSS) aufgerufen.

Es währe echt nett wen mir jemand weiter helfen könnte.


Noch mal zusammen gefast was ich will ;-):
- Eine Klasse die mehrere Button mit einem PNG als Hintergrund erzeugt

Schon mal vielen Dank für Helfen
Mit freundlichen Grüßen Jon
BlackJack

Das Bild wird nicht dargestellt, weil Du eine Referenz auf das Bild behalten musst, sonst wird das von Python freigegeben ohne das Tk das mitbekommt.

Welche Methode sollte ausgerufen werden? Ich sehe da in Zeile 17 einen `AttributeError`.

Zum Verbinden von Pfaden gibt's `os.path.join()`.

Und wenn Du einen eigenen `Button`-Typ erstellen willst, wäre es vielleicht direkter auch von `Button` zu erben.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hallo jonbob, willkommen im Forum,
jonbob hat geschrieben:ich fange gerade an Python zu lernen. Ich habe schon Erfahrung mit Java und will daher vonanfangan Python Objekt-Orientiert programmieren.
Uh, oh. Python is not Java.

Außerdem, ein Button ist doch keine Kindklasse von Frame. Du willst eher ein Weidget definieren, als eine neue Fensterklasse, oder? Das würde ja schon unter Java sinnlos sein. Noch dazu solltest du Sternchen-Imports vermeiden, das wurde hier im Forum schon endlos diskutiert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
C4S3
User
Beiträge: 292
Registriert: Donnerstag 21. September 2006, 10:07
Wohnort: Oberösterreich

offtopic:
Danke für den Link! Schön zu lesen.

Aber eine Frage bleibt:
PJE hat geschrieben:Got a switch statement? The Python translation is a hash table, not a bunch of if-then statments. Got a bunch of if-then's that wouldn't be a switch statement in Java because strings are involved? It's still a hash table.
Wie darf ich mir das denn vorstellen?
Gruß!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Etwa so, das ist eigentlich öfters in Python-Code zu sehen.

Code: Alles auswählen

callbacks = {
  '+' : operator.add
  '-' : operator.sub
}
callbacks[op](operand1, operand2)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@C4S3: Naja, es ist nicht 100% äquivalent zu ``switch``, aber man kann ein Dictionary verwenden, um von Werten auf Funktionen abzubilden, und sich davon dann eine raus suchen und die Funktion ausführen.

Code: Alles auswählen

#     switch (x) {
#         case "hallo": begruessung(); break;
#         case "bye": shutdown(); break;
#         default: errormessage();
#     }

    {'hallo': begruessung,
     'bye': shutdown}.get(x, errormessage)()
Benutzeravatar
C4S3
User
Beiträge: 292
Registriert: Donnerstag 21. September 2006, 10:07
Wohnort: Oberösterreich

Ah, danke.
Wieder was dazugelernt.
:)
Gruß!
jonbob
User
Beiträge: 14
Registriert: Montag 24. November 2008, 10:40

Hallo
erstmal vielen Dank für die Hilfe. Der Artikel war auch sehr interessant.
... if you don't feel like you're at least ten times more productive with Python than Java, chances are good that you've been forgetting to use the time machine!
Aber zur Zeit habe ich noch das Gefühl Java ist effektiver. Aber ich arbeite dran ;-)

Und Ja man hätte genauso gut von Button ableiten können. Allerding wollte ich die Button ursprünglich in einem neuen Fenstert anzeigen lassen.

@BlackJack ich dachte ursprünglich das es reicht ein Objekt der Klasse MyButton zu erhalten… aber mit na Klassenvariable geht’s :idea: :idea: :idea:

Code: Alles auswählen

photo = ImageTk.PhotoImage(image)
self.photo = photo
eine Frage habe ich da noch da zu, wie so kann geht das nicht so?

Code: Alles auswählen

self.photo = ImageTk.PhotoImage(image)
Vielleicht habt Ihr noch nen Tipp. Gibt es für Python eine schöne übersichtliche API?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

jonbob hat geschrieben:@BlackJack ich dachte ursprünglich das es reicht ein Objekt der Klasse MyButton zu erhalten… aber mit na Klassenvariable geht’s :idea: :idea: :idea:
photo ist in dem Beispiel keine Klassenvariable und self.photo ist eine "Instanzvariable", zumindest solange wir mal kurzfristig ignorieren dass man in Python nur Referenzen auf Objekte und keine Variablen hat.
eine Frage habe ich da noch da zu, wie so kann geht das nicht so?
Wieso sollte es nicht so gehen?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

jonbob hat geschrieben:… aber mit na Klassenvariable geht’s :idea: :idea: :idea:

Code: Alles auswählen

photo = ImageTk.PhotoImage(image)
self.photo = photo
"self.photo" ist keine Klassenvariable (nicht mal in Java wäre es das ...), sondern ein Instanzattribut.
jonbob hat geschrieben:eine Frage habe ich da noch da zu, wie so kann geht das nicht so?

Code: Alles auswählen

self.photo = ImageTk.PhotoImage(image)
Sicher geht das.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo jonbob

Code: Alles auswählen

button=Button(self, text="jjjo", image=photo, command = self.printSS)
Bei einem Tkinter-Button wird nur die Option 'text' oder 'image' wirksam aber nicht beides zusammen. (vielleicht ist dies dir schon bekannt)

Übrigens dein Post passte besser in unser Subforum 'Tkinter'

Gruss wuf :wink:
Take it easy Mates!
jonbob
User
Beiträge: 14
Registriert: Montag 24. November 2008, 10:40

@ numerix: jo stimmt! Der Fehler hatte einen anderen Grund

@wuf in der Reihenfolge sollte es aber gehen. Zumindest wird der Text bei mir angezeigt.

Code: Alles auswählen

 image=self.photo1, text=“Name“ 
Vielen Dank noch mal
Mfg jon
BlackJack

Die Reihenfolge ist egal, falls das bei Dir funktioniert, ist das wohl Zufall und nicht portabel.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo jonbob

Hier meine Lösungs-Variante für deiner Aufgabe:

Code: Alles auswählen

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

#~~ Skriptname: png_on_button_01_02.py (wuf)

import os,sys
import glob
import Tkinter as tk
from PIL import Image, ImageTk

class MyButton(object, tk.Button):

    def __init__(self, parent, image_path):

        self.parent = parent
        self.image_path = image_path

        self.image = Image.open(self.image_path)
        self.photo = ImageTk.PhotoImage(self.image)
        tk.Button.__init__(self, self.parent, text="jonbob", image=self.photo,
            command=self.printSS)

    def printSS(self):
        print 'printSS'
        pass

def test():
    """Modul-Test"""

    test_window = tk.Tk()

#    image_path = r'…\Content', r'\Images\Button', r'\Main\*')
    image_path = "process_remove.png"

    #~~ 1. Schaltfläche
    my_button = MyButton(test_window, image_path)
    my_button.pack(side='left', padx=2, pady=2)

    #~~ 2. Schaltfläche
    my_button = MyButton(test_window, image_path)
    my_button.pack(side='left', padx=2, pady=2)

    #~~ 3. Schaltfläche
    my_button = MyButton(test_window, image_path)
    my_button.pack(side='left', padx=2, pady=2)

    test_window.mainloop()

if __name__ == '__main__':

    test()
Dies ist nur ein Vorschlag. Ich möchte dich bei deinem Lösungs-Prozess auf keinen Fall stören. :lol:

Bei mir wird der Text in der Option text='jonbob' nicht angezeigt, wenn die Option image=self.photo vorhanden ist.

Mein Setup hier ist:
SuSE 10.0, Python2.5

Mein Lösungs-Beispiel hinterlässt drei Schaltflächen mit einem PNG-Bildchen in einem Tk-Fenster.

Hier noch ein Photo:
Bild

Gruss wuf :wink:
Take it easy Mates!
tordmor
User
Beiträge: 100
Registriert: Donnerstag 20. November 2008, 10:29
Wohnort: Stuttgart

Mir ist aufgefallen, dass die Klasse zwar MyButton heisst, aber vom Verständnis her wohl eher eine Button Factory sein soll.

Im orginalcode meine ich drei Funktionen erkennen zu können:

1. Erzeugen des Pfades (bei konfigurierbarem Verzeichnis, aber festem Dateinamen :? )
2. Erzeugen des Images
3. Konfiguration des Buttons

Also:

Code: Alles auswählen

class ButtonFactory(object):
    def __init__(self, contentDir, imageDir):
        self.contentDir = contentDir
        self.imageDir = imageDir
        self.imageRef = {} # References to images

    def path(self, filename):
        return os.join(self.contentDir, self.imageDir, filename)

    def image(self, filename):
        if not filename in self.imageRef:
            self.imageRef[filename] = ImageTk.PhotoImage(self.path(filename))
        return self.imageRef[filename]

    def createButton(self, parent, callback):
        return Button(parent, image=self.image('button_menue_up.png'), command=callback)

def event():
    print "Don't press this button again!"

if __name__ == '__main__':
    factory = ButtonFactory(r'…\Content', r'\Images\Button')
    win = Tk()
    button = factory.createButton(win, event)
    button.pack()
    win.mainloop()
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

wuf hat geschrieben:Bei mir wird der Text in der Option text='jonbob' nicht angezeigt, wenn die Option image=self.photo vorhanden ist.
Button-Widgets haben eine Option "compound" zur Kombination von Text und Bild:
compound=
Controls how to combine text and image in the button. By default, if an image or bitmap is given, it is drawn instead of the text. If this option is set to CENTER, the text is drawn on top of the image. If this option is set to one of BOTTOM, LEFT, RIGHT, or TOP, the image is drawn besides the text (use BOTTOM to draw the image under the text, etc.). Default is NONE. (compound/Compound)
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo numerix

Danke für den nützlichen Hinweis. Ich muss mit Entsetzen feststellen, dass mir nach 5 Jahren Tkinter-Programmierung noch vieles unbekannt ist.

Der Grund hierfür ist vermutlich, dass meine Tkinter-Hauptrefernenz das PDF-Dokument des NMT (New Mexico Tech) ist. In diesem handlichen Dokument scheint die Option 'compound' ein zensuriertes Wort zu sein. Dies auch in der neusten Ausgabe vom 24.01.2008. Wenn ich aber in die Dokumentation von Effbot nachschaue ist es klar beschrieben. Frage gib es die Effbot-Sammlung auch als PDF-Dokument?

Den Tipp von 'jonbob' mit dem Tausch der Reihenfolge der Option 'image' bzw. 'text' ohne explizites konfigurieren der Option 'compound' kann ich bei mir nicht verifizieren.

Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

wuf hat geschrieben:Der Grund hierfür ist vermutlich, dass meine Tkinter-Hauptrefernenz das PDF-Dokument des NMT (New Mexico Tech) ist. In diesem handlichen Dokument scheint die Option 'compound' ein zensuriertes Wort zu sein. Dies auch in der neusten Ausgabe vom 24.01.2008. Wenn ich aber in die Dokumentation von Effbot nachschaue ist es klar beschrieben. Frage gib es die Effbot-Sammlung auch als PDF-Dokument?
Ja: http://www.pythonware.com/media/data/an ... kinter.pdf

Es scheint aber, dass die online-Version etwas(!) aktueller ist.

Ich benutze auch am liebsten die von dir genannte Doku von Shipman, weil sie ein sehr angenehmes Layout hat. Besonders im hinteren Teil ist sie zudem ausführlich und informativ. Allerdings habe ich auch schon mehrfach feststellen müssen, dass sie an anderen Stellen leider unvollständig ist. Die Klasse PhotoImage wird z.B. überhaupt nicht behandelt.

Allerdings ist auch Frederik Lundhs Werk nicht vollständig. Manchmal hilft dann nur noch der Blick in den Quelltext von Tkinter (wie z.B. bei der gestern in einem Thread aufgetauchten Option "after" des pack()-Managers).
wuf hat geschrieben:Den Tipp von 'jonbob' mit dem Tausch der Reihenfolge der Option 'image' bzw. 'text' ohne explizites konfigurieren der Option 'compound' kann ich bei mir nicht verifizieren.
Das kann ja auch nicht funktionieren. Es handelt sich hierbei um Schlüsselwortparameter und deren Eigenschaft ist ja gerade, dass es auf die Reihenfolge nicht ankommt.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi numerix

Ja ich kenne das Schriftstück (An Introduction to Tkinter) von Fredrik Lundh datiert auf 01.12.1999.

Es ist ein Schriftstück auf welches ich nur im Notfall zugriff. Wenn es hauptsächlich wieder einmal darum ging im dunklen Tkinter-Keller nach etwas zu suchen. :D
Ich habe in diesem Dokument einmal explizit die Optionen des Button- und Label-Widgets nachgeschaut. Beruhigend ist, dass Fredrik Lundh zur damaligen Zeit scheinbar auch noch nicht wusste, dass es die 'compound'-Option gibt. Du hast recht als Alternative schaut man am besten ins Tkinter.py Skript.

Last but not least gibt es zum Glück noch unser Forum! :lol:

Danke für den Tipp. Gruss wuf :wink:
Take it easy Mates!
Antworten