OOP Problem mit Zugriff auf Objekte

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.
spartacus
User
Beiträge: 16
Registriert: Montag 11. Mai 2009, 19:00

Hallo zusammen

Ich mache in den letzten paar Wochen meine erste Gehversuche mit Python und überhaupt mit OOP. Ich habe nun ein Programm geschrieben, dass das Internet nach Covers zu einer Band durchsucht und runterlädt. Dies klappt eigentlich schon mehr oder weniger gut. Nun kommt jedoch der OOP Ansatz.


Ich habe in einem Modul mainWindow meine GUI untergebracht, die ein QGraphicsView Objekt namens grafik enthält, um das Bild anzuzeigen.

Im einem anderem Modul namens searcher dachte ich, schreibe ich ein Classe Resultat. Diese Klasse sollte ein paar Infos über Artist, Releasedatum, URL zum Bild usw enthalten.
Weiter sollte sie die Funktion haben, die Infos und das Bild anzuzeigen. Nun meine Frage. Wie kann ich von dieser Classe aus auf das grafik Objekt im mainWindow Modul zugreifen?


Um es etwas verständlicher zu machen:

Code: Alles auswählen

#Modul mainWindow

from PyQt4 import QtCore, QtGui

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
    .
    .
    .
    self.grafik = QtGui.QGraphicsView(self.anzeigewidget)
    self.grafik.setObjectName("grafik")
    .
    .
    .

Code: Alles auswählen

#searcher Modul

class Result(object):
    def __init__(self):
        self.artist = 'unbekannt'
        self.title = 'unbekannt'
        self.releaseDate = 'unbekannt'
        self.smallImageURL = 'unbekannt'
        self.largeImageURL = 'unbekannt'
        
    def printInfo(self):
        print 'Artist: %s' % self.artist
        print 'Titel: %s' % self.title
        print 'Release Datum: %s' % self.releaseDate
        print ''
        
    def displayCover(self):
        self.scene = QtGui.QGraphicsScene()
        self.scene.addPixmap(QtGui.QPixmap('/home/theo/Dokumente/covercatcher v0.2/Bilder/cover0.jpg'))
        self.grafik.setScene(self.scene)

Ich dachte zuerst. vielleicht müsste ich die Classe Result mit der Ui_MainWindow verknüpfen (vererben). Dies hat jedoch bei mir nicht funktioniert und scheint aus meinen Augen auch nicht ganz sauber zu sein, da ich ja eigentlich nur das Objekt grafik bräuchte.
Andere Vorschläge, wie ich die Objekte anlegen könnte nehme ich natürlich gerne entgegen :wink:

Gruss spartacus
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

sich direkt auf OOP _und_ GUI-programmierung zu stürzen halte ich für gewagt.

also erstmal unabhängig von der GUI-programmierung.

du brauchst um zugriff von einer klasse auf eine andere zu bekommen - wie du schon vermutet hast - keine vererbung. sondern eine assoziation von klassen.
das heisst, dass mind zwei klassen in einer beziehung zueinander stehen. sie müssen also irgendwie voneinander wissen.

Code: Alles auswählen


In [2]: class foo:
   ...:     def __init__(self, bar):
   ...:         self.bar = bar
   ...:

In [3]: class bar:
   ...:     def m(self):
   ...:         print "foo ruft uta"
   ...:

In [4]: f = foo(bar())

In [5]: f
Out[5]: <__main__.foo instance at 0x00F19440>

In [6]: f.bar
Out[6]: <__main__.bar instance at 0x00F193C8>

In [7]: f.bar.m
Out[7]: <bound method bar.m of <__main__.bar instance at 0x00F193C8>>

In [8]: f.bar.m()
foo ruft uta
    
eine foo instanz hält hier eine referenz auf eine bar instanz.

welche literatur bemühst du zum thema oop, wenn ich fragen darf?
http://www.kinderpornos.info
spartacus
User
Beiträge: 16
Registriert: Montag 11. Mai 2009, 19:00

Wer nichts wagt verliert nichts :wink: Nein, etwas Programmiererfahrung habe ich schon. Ich schrieb viele C Projekte auf uController Ebene und hab mich mal für ein halbes Jahr mit Java angelegt (Beides beruflich bedingt). Nun suchte ich etwas leichtere Kost als Java und bin zu Python gestossen.

Um Python zu erlernen kaufte ich mir das Buch "Einführung in Python" von OReilly. In diesem Buch ist auch etwas über Klassen und OOP beschrieben. Doch ich habe nun selbst bemerkt, dass wenn das Projekt wächst, schnell die Übersicht verloren geht und man an grenzen stösst, wenn man nicht im voraus sauber plant und den OO Ansatz verstanden hat.

Kannst du irgendwelche Literatur zu OOP empfehlen? Dieses Thema würde mich noch reizen um mehr darüber zu erfahren.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

ich kann dir leider keine einführung in die OOP empfehlen, das ist zu lange her.

aber hier kannst du mal einen blick drauf werfen:

http://www.python-forum.de/topic-12791. ... hlight=oop

http://www.state-machine.com/devzone/cp ... manual.pdf
(kapitel 3, OOP in C, das dürfte für dich, bei deinem hintergrund, sehr hilfreich sein um die OOP zu entzaubern )
http://www.kinderpornos.info
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dill hat geschrieben:das heisst, dass mind zwei klassen in einer beziehung zueinander stehen. sie müssen also irgendwie voneinander wissen.
In deinem Beispiel sehe ich aber nicht, dass die eine Klasse an irgendeiner Stelle von der anderen weiß. ;)

`foo` weiß nur, dass es im Konstruktor ein Objekt an `bar` binden und es dann zu einem Klassenattribut von sich machen soll - mehr nicht. Und dass zwei Klassen gegenseitig voneinander wissen, kommt IMHO eher selten vor. Meistens leitet man ja von einer anderen Klasse ab:

Code: Alles auswählen

In [1]: class Foo(object):
   ...:     def say_bar(self):
   ...:         return 'bar!'
   ...:     
   ...:     

In [2]: class Spam(Foo): pass
   ...: 

In [3]: spam = Spam()

In [4]: spam.say_bar()
Out[4]: 'bar!'
Das einzige, was man hier sagen kann, ist, dass `Spam()` gewissermaßen mit `Foo()` bekannt gemacht wird: es hat die neue Methode `say_bar()`. `Foo()` ist das ganze aber relativ wurscht. Es funktioniert genau so wie vorher.

Im Übrigen finde ich - ohne jetzt kleinkariert rüberkommen zu wollen -, dass man sich auch in Beispielen ruhig an PEP8 halten darf und vor allem keine Old-Style-Klassen verwenden sollte. ;)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Da die Objekte sonst nichts miteinander zu tun haben, fände ich es sinnvoller, das Grafik-Objekt bei Bedarf zu übergeben.

Code: Alles auswählen

    def displayCoverOn(self, grafik):
        self.scene = QtGui.QGraphicsScene()
        self.scene.addPixmap(QtGui.QPixmap('/home/theo/Dokumente/covercatcher v0.2/Bilder/cover0.jpg'))
        grafik.setScene(self.scene)
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

snafu hat geschrieben: In deinem Beispiel sehe ich aber nicht, dass die eine Klasse an irgendeiner Stelle von der anderen weiß. ;)
`foo` weiß nur, dass es im Konstruktor ein Objekt an `bar` binden und es dann zu einem Klassenattribut von sich machen soll - mehr nicht.
:?: das ist eine assoziation. was erwartest du?

snafu hat geschrieben: Und dass zwei Klassen gegenseitig voneinander wissen, kommt IMHO eher selten vor.
stimmt, ist aber im beispiel doch auch nicht der fall?
snafu hat geschrieben: Meistens leitet man ja von einer anderen Klasse ab:
vererbungen werden öfter eingesetzt (was ich bezweifele) als assoziationen und deshalb sind sie toller?
http://www.kinderpornos.info
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich unterlag wohl dem Irrglauben, dass der Satz von dir, den ich zitiert habe, etwas mit deinem Code zu tun hat. :( Besonders über das "mind. 2 Klassen" bin ich gestolpert.

Übergibt man einer Klasse A die Instanz der Klasse B "nur" als Attribut, dann ändert dies ja quasi gar nichts für beide Klassen im Sinne von: "Sie haben keine Ahnung, welche Attribute und Methoden die andere besitzt". Oder was verstehst du unter "voneinander wissen"?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

wie setzt du denn eine binäre assoziation um?

"voneinander wissen" = assoziation.

http://de.wikipedia.org/wiki/Assoziation_(UML)
http://www.kinderpornos.info
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Darii hat geschrieben:Da die Objekte sonst nichts miteinander zu tun haben, fände ich es sinnvoller, das Grafik-Objekt bei Bedarf zu übergeben.

Code: Alles auswählen

    def displayCoverOn(self, grafik):
        self.scene = QtGui.QGraphicsScene()
        self.scene.addPixmap(QtGui.QPixmap('/home/theo/Dokumente/covercatcher v0.2/Bilder/cover0.jpg'))
        grafik.setScene(self.scene)
und wo kommt hier jetzt die url von dem bild her?

ich würde spartacus auf jedenfall raten sich erstmal mit der OOP vertraut zu machen und dann zu schauen wie man eine gui-applikation organisiert.


@spartacus: mir ist noch was eingefallen zu Qt und python:

http://www.amazon.de/Rapid-GUI-Programm ... 0132354187
http://www.kinderpornos.info
spartacus
User
Beiträge: 16
Registriert: Montag 11. Mai 2009, 19:00

Ich habe es nun mit einer Übergabe des Grafik-Objektes gelöst.

@Dill
Learning by doing! Ohne Fehler zu machen lernt man auch nichts. Und ich habe ehrlich gesagt noch keine gute Literatur zu OOP mit Python und allgemein zu Entwurfsmustern in der Softwareprogrammierung gefunden.

Eigentlich läuft das ganze Programm in den simplen Zügen ja auch schon (Inklusive GUI, Bilder und Info Suche, Anzeige des Bildes, usw.) Nur ist das Programm noch nicht sehr sauber programmiert und es fehlen noch ein paar Funktionen.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

spartacus hat geschrieben:Ich habe es nun mit einer Übergabe des Grafik-Objektes gelöst.
kannst du mal den code(teil) posten?

spartacus hat geschrieben: ... ich habe ehrlich gesagt noch keine gute Literatur zu OOP mit Python und allgemein zu Entwurfsmustern in der Softwareprogrammierung gefunden.
ich kenne da auch nichts, kann evtl jemand was empfehlen?
vergiss erstmal die pattern (muster). du musst zunächst verstehen wie und wann die grundsätzlichen OOP-elemente eingesetzt werden.
das einzige pattern was du momentan brauchen könntest wäre model/view. in dem buch zu py+qt ist dazu eine schöne einführung.
http://www.kinderpornos.info
spartacus
User
Beiträge: 16
Registriert: Montag 11. Mai 2009, 19:00

Zusagen wäre noch, dass meine Suchresultate, also meine Result Objekte, in der Resultate Liste gesammelt werden und die Liste global deklariert wurde

Code: Alles auswählen

import sys, searcher,  ecs
from PyQt4 import QtGui,  QtCore
from mainWindow import Ui_MainWindow as mWindow

class Window(QtGui.QMainWindow, mWindow): 

#Bla, bla, bla

    def on_suchButton(self):

            #Die ganze Sucheingabe
            keyword = self.sucheingabe.text()
            keyword = str(keyword)
            print keyword
            suche = searcher.SearchOnAmazon()
            suche.setLocale()
            suche.setLicenseKey()
            suche.search(keyword)

            #Bild anzeigen
            searcher.Resultate[0].displayCover(self.grafik)
BlackJack

@spartacus: Warum ist `searcher.Resultate` global deklariert? Dann kann man ja zum Beispiel gar keine Suchen parallel durchführen!? Ich hätte irgendwie erwartet, dass das Ergebnis einer Suche von `suche.search()` zurückgegeben wird.

An der Namensgebung könntest Du noch ein wenig feilen. Namen die mit Grossbuchstaben beginnen, sind per Konvention Namen von Klassen. Das trifft auf `Resultate` wohl nicht zu. Und Klassen sollten Namen haben, die "Dinge" beschreiben und keine Tätigkeiten. Letzteres ist die Aufgabe von Methoden. Also `SearchOnAmazon` sollte besser `AmazonSearch` heissen. Mischen von Deutsch und English ist unschön.

Und eigentlich sind in Python ausser für Klassen namen_in_kleinbuchstaben_mit_unterstrichen üblich. GUI-Toolkits halten sich da meistens nur nicht dran, wenn sie C++-Klassen wrappen, bei denen eine andere Namenskonvention üblich ist und dort die Namen der originalen API übernommen werden. Bei GUI-Code sollte man die Konvention des Toolkits übernehmen, aber der Quelltext für die Geschäftslogik sollte IMHO den Python-Konventionen folgen.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

du übergibst also das graphicsview an den searcher?
das ist sehr unschön. du solltest die darstellung und die logik trennen.
also besser nach dem ereignis "suche angefordert" suchen und das resultat im gui-code darstellen.

Code: Alles auswählen

class mygui(Qt.irgendwas):
    def on_search_button(self): 
        self.display_results(self.searcher(self.sucheingabe.text()))

    def display_results(self, results):
        #hier auf graphicsview zugreifen
ich habe übrigens "on_solch_ein_knopf" gelesen. "on_search_button" wäre schöner.
http://www.kinderpornos.info
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Dill hat geschrieben:du übergibst also das graphicsview an den searcher?
das ist sehr unschön. du solltest die darstellung und die logik trennen.
also besser nach dem ereignis "suche angefordert" suchen und das resultat im gui-code darstellen.
Es spricht nichts dagegen, dass sich das Resultat selbst auf eine beliebige QT-Grafik zeichnen kann. Das ist eine saubere Trennung. Unsauber wäre es nur, wenn das Resultat von selbst anfangen würde irgendwohin zu zeichnen.

Wenn man sich die Arbeit machen will kann man das Problem natürlich noch beliebig weit abstrahieren und da zehn Wrapper-Klassen draufschmeißen...

Code: Alles auswählen

class ResultDrawer:
    def __init__(self, result): self.result = result
    def draw(self, grafik):
         pass # self.result zeichnen

def draw_result(result, grafik):
    pass # result zeichnen

# Wenn jetzt statt
 ResultDrawer(result).draw(grafik)
# oder
 draw_result(result, grafik)
# anstelle von
 result.displayCover(grafik)
# schreibe habe ich jetzt auch nicht sooo viel gewonnen.
# übersichtlicher als das direkt in den Controller zu packen, ist das aber alles
Man muss sich das Leben aber auch nicht schwerer machen als es ist.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

Darii hat geschrieben:Es spricht nichts dagegen, dass sich das Resultat selbst auf eine beliebige QT-Grafik zeichnen kann. Das ist eine saubere Trennung.
es ist eben keine trennung. würde ich nicht so machen.
http://www.kinderpornos.info
spartacus
User
Beiträge: 16
Registriert: Montag 11. Mai 2009, 19:00

@BlackJack
Ja, Resultate ist zurzeit noch global. Ist mir klar, sehr unschön gelöst. Bezüglich Namensgebung hast du völlig recht. Ich habe ein Durcheinander mit Englisch und Deutsch, Unterstriche und Gross - Kleinschreibung usw. Ich werde wieder einmal die PEP8 durchlesen müssen.

@Dill
Du meinst also, wenn ich eine Funktion on_search_button habe, dass ich in dieser keine Logik schreibe sondern eine andere Funktion aufrufe, zB search_covers, die dann die Bilder sucht und das Bild darstellt? on_search_button dient dann quasi nur als Übergang zwischen GUI und Logik oder?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

es gibt da keinen einen weg.

aber die wahrscheinlich am häufigsten verwendete (oder mindestens meist-gelehrte) organisation einer GUI-applikation ist model/view oder model/view/controller (MVC).

hier werden "geschäftslogik" und präsentation strikt getrennt. wenn deine suche zb nur urls zurückgeben würde, so könntest du sie unverändert in Qt, gtk, und einer web-version einsetzen. wie du es momentan machst muss sie alle diese präsentationswege kennen.

das rührt ua daher, dass software ja meist nicht von einem menschen alleine geschrieben wird. so können sich die künstler auf die gui konzentrieren und die hacker auf den code im hintergrund.

ich habe letztens erst einen sehr schönen code gesehen, hat mir ein freund gezeigt, der ist eine art sw-feuerwehrmann. es geht da um einen der grössten webshops in D. die jungs haben sich nicht wirklich an MVC gehalten, so wurde ua die ratengrösse bei einer finanzierung direkt in der präsentationsschicht errechnet. und zwar an mehreren stellen, da sie zb direkt beim produkt und im warenkorb benötigt wird. das heisst für die dann bei änderung der zinssätze jeweils das anfassen von 5 stellen im code. zuerst war das mit sicherheit die einfachste lösung. rächt sich dann aber schnell. so schockierende dinge sind aber eigentlich standard. :)
http://www.kinderpornos.info
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

spartacus hat geschrieben:@Dill
Du meinst also, wenn ich eine Funktion on_search_button habe, dass ich in dieser keine Logik schreibe sondern eine andere Funktion aufrufe, zB search_covers, die dann die Bilder sucht und das Bild darstellt? on_search_button dient dann quasi nur als Übergang zwischen GUI und Logik oder?
Jein, on_search_button ist schon Teil der Logik (der Controller-Teil von MVC). Deswegen darfst du da so viel reinschreiben wie nötig. Je weniger du in eine Funktion reinschreibst desto übersichtlicher wird es allerdings. Und wenn du den selben Code irgendwo noch ein zweites Mal brauchst, bietet es sich sowieso an, den in eine extra Funktion auszulagern.

Dill meinte mit Logik vermutlich das Model (in deinem Fall das Result), das man nicht mit der View vermengen sollte. Hat er nicht unrecht, aber meiner Meinung nach muss man sich in einem so übersichtlichen Fall wie deinem das Leben nicht schwerer machen, als es sowieso schon ist. Und man hat da keinen großartigen Gewinn wenn man die Funktion jetzt umherschiebt.

Am besten mal ein Buch nehmen und etwas über MVC nachlesen. Am meisten hilft da aber learning-by-doing, wenn man sich erstmal verfranst hat, denkt man beim nächsten Mal vorher nach ;)
Antworten