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.
jonbob
User
Beiträge: 14
Registriert: Montag 24. November 2008, 10:40

Montag 24. November 2008, 12:08

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

Montag 24. November 2008, 12:38

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 24. November 2008, 12:44

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 Modvoice
Benutzeravatar
C4S3
User
Beiträge: 292
Registriert: Donnerstag 21. September 2006, 10:07
Wohnort: Oberösterreich

Montag 24. November 2008, 13:04

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Montag 24. November 2008, 13:14

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 Modvoice
BlackJack

Montag 24. November 2008, 13:16

@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

Montag 24. November 2008, 13:38

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

Montag 24. November 2008, 17:09

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: 2462
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Montag 24. November 2008, 17:21

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

Montag 24. November 2008, 17:23

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: 1483
Registriert: Sonntag 8. Juni 2003, 09:50

Montag 24. November 2008, 18:10

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

Montag 24. November 2008, 18:44

@ 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

Montag 24. November 2008, 19:02

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

Montag 24. November 2008, 19:18

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

Montag 24. November 2008, 20:21

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()
Antworten