PyGTK und psycopg2

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Hi!

Ich habe folgende Frage:
Ich möchte per Button eine Verbindung zum PostgreSQL Server herstellen und diese soll bestehen bleiben, bis der Disconnect Button aktiviert wird.
Mein Programm sieht zur Zeit folgendermaßen aus:

Code: Alles auswählen

import pygtk
pygtk.require("2.0")
import gtk
import psycopg2

class portOpt(object):    
	def __init__(self):
	    self.builder = gtk.Builder()
	    self.builder.add_from_file("portoptv3.glade")
	    self.builder.connect_signals(self)
	    self.window = self.builder.get_object("window1")
	    self.window.show()
	    
	def execute(self, *args):
		host = self.builder.get_object('host').get_text()
		db = self.builder.get_object('db').get_text()
		user = self.builder.get_object('user').get_text()
		pw = self.builder.get_object('pw').get_text()
		conn_string = "host="+ host +" dbname="+ db +" user="+ user +" password="+ pw +""
		conn = psycopg2.connect(conn_string)
		cur = conn.cursor
				
	def on_bt_quit_activate(self,widget, data=None):
		gtk.main_quit()
		
	def on_window1_destroy(self,widget, data=None):
		gtk.main_quit()
		
	def on_act_con_activate(self, *args):
		self.execute()
		
	def on_act_discon_activate(self, *args):
		cur.close()
		conn.close()
				
if __name__ == "__main__":
	app = portOpt()
	gtk.main()
Das Connecten an sich funktioniert, jedoch bleibt die Verbindung nicht aufrecht erhalten.
Dazu kommt noch, dass ich keine Ahnung habe wie ich die "conn" und "cur" auf die act_discon übergeben kann.
Diese Aktion soll ja den Cursor nach beendigung der Aktionen (Die im Moment noch nicht enthalten sind) schließen und dann die Datenbankverbindung trennen.

Dank und Gruß...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Naja, Du müßtest einfach die Connection in einem Attribut Deines Objektes hinterlegen :K

Ich würde die Methode zum Verbindungsaufbau auch nicht ``execute`` nennen - so heißt ja beim DB-API2 die Methode zum Absetzen eines SQL-Kommandos. Das ist imho verwirrend.

Klassennamen schreibt man "CamelCase" nach PEP8 - oder ist das bei GtK ggf. eine Ausnahme und bewusst so gehalten?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Hyperion hat geschrieben:Naja, Du müßtest einfach die Connection in einem Attribut Deines Objektes hinterlegen :K

Ich würde die Methode zum Verbindungsaufbau auch nicht ``execute`` nennen - so heißt ja beim DB-API2 die Methode zum Absetzen eines SQL-Kommandos. Das ist imho verwirrend.

Klassennamen schreibt man "CamelCase" nach PEP8 - oder ist das bei GtK ggf. eine Ausnahme und bewusst so gehalten?
Das execute war nur testweise und ändert sich definitiv wenns mal funktioniert :)

Ok zu deiner Antwort.
Ich will ja mit dem einen Object die Verbindung herstellen und halten
und mit dem anderen die Verbindung trennen.
da ''conn'' aber nicht global festgelegt sonder nur im "Verbindung herstellen" object übernimmt er das nicht in das "Verbindung trennen" object.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

truehumandesign hat geschrieben: da ''conn'' aber nicht global festgelegt sonder nur im "Verbindung herstellen" object übernimmt er das nicht in das "Verbindung trennen" object.
Äh... was sollen das für Objekte sein? Redest Du von den Methoden der Klasse ``portOpt``? Diese haben aber keine "Attribute", sondern kennen nur Namen, die entweder lokal definiert sind - so wie momentan ``conn`` - oder aber das jeweilige "Instanz"-Objekt, welches sie über ``self`` bekommen.

Du musst die Verbindung innerhalb einer Instanz von ``portOpt`` hinterlegen - oder aber direkt als Klassenattribut. Auf jeden Fall muss das ganze "global" innerhalb Deiner Klasse verfügbar sein - ansonsten kannst Du die Klasse auch weglassen und die DB-Verbindung extern verwalten...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
deets

Ich sehe hier vor allem die Gefahr einer wirklich unheiligen Vermischung aus GUI-Code & Anwendungs-Schicht.

Du koenntest dem Problem zum Beispiel dadurch begegnen, indem du ein Application-Objekt oder etwas aenhliches erstellst, das einem Singleton oder Borg-Pattern folgt.

Damit kannst du dann in deine GUI-Callbacks sage "Application().setup_connection()" oder so.

In dem Application-Objekt solltest du natuerlich auch eine eventuelle Konfiguration speichern, und wenn die via GUI neu definiert wird, dann darin erstmal setzen. Oder "Application().setup_connection(db_uri)" machen.

Und zum zusammenbau von Strings benutze doch bitte string-formatierung, und nicht den +-Operator... (Ja, problembaer, der ist auch nicht verboten vom Interpreter, aber trotzdem schlecht...)
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Hyperion hat geschrieben:
truehumandesign hat geschrieben: da ''conn'' aber nicht global festgelegt sonder nur im "Verbindung herstellen" object übernimmt er das nicht in das "Verbindung trennen" object.
Äh... was sollen das für Objekte sein? Redest Du von den Methoden der Klasse ``portOpt``? Diese haben aber keine "Attribute", sondern kennen nur Namen, die entweder lokal definiert sind - so wie momentan ``conn`` - oder aber das jeweilige "Instanz"-Objekt, welches sie über ``self`` bekommen.

Du musst die Verbindung innerhalb einer Instanz von ``portOpt`` hinterlegen - oder aber direkt als Klassenattribut. Auf jeden Fall muss das ganze "global" innerhalb Deiner Klasse verfügbar sein - ansonsten kannst Du die Klasse auch weglassen und die DB-Verbindung extern verwalten...
Sorry ich bin ein absoluter python noob hab mich falsch ausgedrückt.. natürlich meinte ich nicht Objekte sondern die Methoden der Klasse portOpt.
Ich mach mich morgen nochmal an das ganze und melde mich ob ichs hinbekommen habe.

Danke mal soweit!
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Soo.. ich hab mir mal das Python Tutorial zu den Klassen zu gemüte geführt.

Der Code sieht nun folgendermaßen aus:

Code: Alles auswählen

import pygtk
pygtk.require("2.0")
import gtk
import psycopg2

class portOptPrg(object):
	
	def con_db(self, *args):
		host = self.builder.get_object('host').get_text()
		db = self.builder.get_object('db').get_text()
		user = self.builder.get_object('user').get_text()
		pw = self.builder.get_object('pw').get_text()
		conn_string = "host=self.host dbname=self.db user=self.user password=self.pw"
		conn = psycopg2.connect(conn_string)

	def con_disc(self, *args):
		conn.close()

class portOptGui(portOptPrg):    
	def __init__(self):
	    self.builder = gtk.Builder()
	    self.builder.add_from_file("portoptv3.glade")
	    self.builder.connect_signals(self)
	    self.window = self.builder.get_object("window1")
	    self.window.show()
				
	def on_bt_quit_activate(self,widget, data=None):
		gtk.main_quit()
		
	def on_window1_destroy(self,widget, data=None):
		gtk.main_quit()
		
	def on_act_con_activate(self, *args):
		self.con_db()
		
	def on_act_discon_activate(self, *args):
		self.con_disc()
				
if __name__ == "__main__":
	app = portOptGui()
	gtk.main()

Jetzt steh ich nur wieder vor dem gleichen Problem und ich bekomm die Verbindung nicht geschlossen.
Ich hab schon in der psycopg2 doc geschaut ob es ein disconnect befehl oder ähnliches gibt.. finde aber nichts dazu.
Ich bekomm den Parameter "conn" einfach nicht übergeben
Python bringt mir folgenden fehler:
Traceback (most recent call last):
File "portoopv4.py", line 37, in on_act_discon_activate
self.con_disc()
File "portoopv4.py", line 17, in con_disc
conn.close()
NameError: global name 'conn' is not defined
Wo hakts hier noch?
deets

Oh mann... das ist wirklich gruselig.

Zuerstmal hakt es immer noch, weil du deine conn nirgendwo speicherst. Du musst

Code: Alles auswählen

      self.conn = psycopg2.connect(conn_string)
machen, und natuerlich auch in disconnect dich auf self.conn beziehen.

Und dann mal ein paar Anmerkungen:

Die Zerteilung in zwei Klassen hat das ganze deutlich verschlimmert. Denn du erstellst eine Basisklasse, die jede Menge Detailwissen vorraussetzt ueber eine Klasse, die von ihr ableitet. Das ist ganz schlechtes Design. Denn wer sagt denn, dass du nicht auch eine GUI programmieren willst, die den connection string als ganzes, inklusive username/passwort usw entgegennimmt - also nur *ein* Texteingabefeld hat? Das wuerde mit deinem jetztigen Ansatz nicht funktionieren.

Also, portOptPrg bekommt den conn_string als Argument an con_db, und das war's.

Und portOptGui erbt nicht von portOptPrg, sondern bekommt eine Instanz davon bei Konstruktion, oder es gibt davon eine globale Variable.

Dann ist deine Namenskonvention ungewoehnlich - Klassen haben CamelCase. Und die ganzen Abkuerzungen sind verwirrend. Warum heissen con_db und con_disc nicht einfach connect_db & disconnect_db?

Last but not least - wenn das, was du hier postest, wirklich dein echter Code ist, dann ist der conn_string voellig sinnfrei zusammengebaut. So funktioniert string-formatting nicht (falls es das sein sollte).
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

deets hat geschrieben:Oh mann... das ist wirklich gruselig.

Zuerstmal hakt es immer noch, weil du deine conn nirgendwo speicherst. Du musst

Code: Alles auswählen

      self.conn = psycopg2.connect(conn_string)
machen, und natuerlich auch in disconnect dich auf self.conn beziehen.

Und dann mal ein paar Anmerkungen:

Die Zerteilung in zwei Klassen hat das ganze deutlich verschlimmert. Denn du erstellst eine Basisklasse, die jede Menge Detailwissen vorraussetzt ueber eine Klasse, die von ihr ableitet. Das ist ganz schlechtes Design. Denn wer sagt denn, dass du nicht auch eine GUI programmieren willst, die den connection string als ganzes, inklusive username/passwort usw entgegennimmt - also nur *ein* Texteingabefeld hat? Das wuerde mit deinem jetztigen Ansatz nicht funktionieren.

Also, portOptPrg bekommt den conn_string als Argument an con_db, und das war's.

Und portOptGui erbt nicht von portOptPrg, sondern bekommt eine Instanz davon bei Konstruktion, oder es gibt davon eine globale Variable.

Dann ist deine Namenskonvention ungewoehnlich - Klassen haben CamelCase. Und die ganzen Abkuerzungen sind verwirrend. Warum heissen con_db und con_disc nicht einfach connect_db & disconnect_db?

Last but not least - wenn das, was du hier postest, wirklich dein echter Code ist, dann ist der conn_string voellig sinnfrei zusammengebaut. So funktioniert string-formatting nicht (falls es das sein sollte).
Ich nehme gerne Tipps entgegen, jedoch könntest du vllt. mal auf meine vorherigen Posts achten und du wirst erkennen, dass ich geschrieben habe:
Sorry ich bin ein absoluter python noob
Also kannst du mir das bitte auch normal erklären und nicht (für mich redest du teilweise in rätseln) mit deiner Formatierungsvorgabe kommen und lieber mal auf eine Seite verweisen die diese ganzen Standards vorweist die du mir da aufzählst.
Schreib doch zum Beispiel lieber mal was "Sinnfrei" am con_string ist, dann lern ich auch was dabei.
Oder einfach mal den Gesamten Code, so wie du es meinst.. (wenns nicht zu viel verlangt ist).
Ein Freundlicher Umgangston scheint aber unter Codern (mit n00bs) eh nicht gang und gebe zu sein (den Eindruck habe ich zumindest)

Danke..
deets

@ truehumandesign

"sinnfrei" ist es, weil du schreibst

"host=self.host dbname=self.db user=self.user password=self.pw"

Das heisst einen String, der *genau* so aussieht. Was du aber offensichtlich wolltest ist, den connection-string basierend auf den ganzen parametern aufzubauen.

Also etwa so:

conn_string = "host='%s' dbname='%s'" % (host, dbname)

Natuerlich mit all den anderen parametern genauso.

Da dein Code da oben eh nie einen vernuenftigen conn-string hat zusammenbauen koennen, stellt sich natuerlich die Frage, wie du ueberhaupt jemals eine Datenbankverbindung aufgebaut hast, die du dann ueberhaupt disconnecten willst. Oder sollte es sich etwa gar nicht um den richtige Code handeln??? Dann ist das hier ja sowieso trocken-schwimmen...

Und was das Noob-Sein angeht: wenn du das Tutorial durchgearbeitet hast, und in der Lage bist, fuer sowas wie self.builder die richtige Herangehensweiseise zu waehlen, aber nicht bei "self.conn", dann kann ich da nur drauf hinweisen - aber dich da abzuholen bei Pontius & Pilatus scheint mir nicht noetig. Und wenn du Begriffe genannt bekommst, die du nicht kennst - wie zB string-formatting - dann kann man die auch googeln.
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

OK vielen Dank für die Tipps erstmal.
Hat mich doch schon weiter gebracht mittlerweile.
Mir fehlte der Zusammenhang, dass ein self. vor dem parameter diesen global ablegt und über self. dann wieder aufgerufen werden kann.
Das Programm soll keineswegs produktiv eingesetzt werden, sondern ist einfach nur ein Test bzw. eine Spielerei mit pygtk, und deshalb geh ich da jetzt nicht strikt nach Formatierungsvorgaben vor.
Wie dem auch sei.. die Verbindung kann nun getrennt werden und das ist es ja was ich wollte.
deets

@truehumandesign

Du hast da etwas sehr grundlegendes nicht verstanden: self.foo legt etwas *NICHT* global ab, sondern als Attribut einer Instanz deines Objektes:

Code: Alles auswählen


class Foo(object):

    def __init__(self, arg):
         self.arg = arg

a = Foo(10)
b = Foo(20)
assert a.arg != b.arg
Global geht auch (mit "global"), aber das sollte man wenn irgend moeglich immer vermeiden. Dazu findest du hier genug Diskussionen.

Und das Argument "ist nur zum ausprobieren" ist immer schwierig... nichts haelt so lang, wie ein Provisorium. Auch und gerade wenn es um's lernen geht, solltes du dich um vernuenftiges programmieren bemuehen. Wann sonst lernst du es - wenn die Zeit im Projekt draengt eher nicht...
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

deets hat geschrieben: Und das Argument "ist nur zum ausprobieren" ist immer schwierig... nichts haelt so lang, wie ein Provisorium. Auch und gerade wenn es um's lernen geht, solltes du dich um vernuenftiges programmieren bemuehen. Wann sonst lernst du es - wenn die Zeit im Projekt draengt eher nicht...
Wahre worte!

Danke für den Support!
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Hi!

Ich habe schon wieder eine Frage:
Undzwar möchte ich eine Datenbankabfrage gerne in einem TextView darstellen.
Jedoch geht das wohl nicht, weil die Formatierung des "fetch" von psycopg irgendwas verhaut (oder ich selbst :) )
Meine Funktion sieht folgendermaßen aus:

Code: Alles auswählen

def selectall(self, *args):
		cur = self.conn.cursor()
		cur.execute('SELECT * FROM foo')
		text = cur.fetchone()
		buffer = self.builder.get_object('view_log').get_buffer()
		buffer.set_text("%s") % text
		self.conn.commit()
		cur.close()
Wenn ich das Ganze als "print" ausgebe kommt das hier raus:
(1, 'ZL0000001', 'Klemmerle', 'Bushaltestelle 1', '12345', '99999')
Vielleicht hat mir irgendjemand einen Tipp hierfür
Danke!
BlackJack

@truehumandesign: Schau Dir mal in der Dokumentation oder auch live (`type()`-Funktion) bei dem Objekt was Du von `fetchone()` bekommst, wie das aufgebaut ist.
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Er spuckt mir folgendes aus:
<type 'tuple'>
Traceback (most recent call last):
File "portoopv5.py", line 63, in on_act_select_activate
self.selectall()
File "portoopv5.py", line 33, in selectall
buffer.set_text("%s") % text
TypeError: unsupported operand type(s) for %: 'NoneType' and 'tuple'
Ich google mal gerade zu tuples und textbuffer

Edit: so bekomm ich zumindest die Abfrage raus (nur nicht richtig formatiert) type müsste eigentlich unicode sein, da der Zeichensatz von Pgsql auf UTF-8 gestellt ist.
Und normalerweise sollte buffer.set_text(text) schon unicode sein.

Code: Alles auswählen

buffer.set_text(repr(text))
(1, u'ZL0000001', u'Klemmerle', u'Bushaltestelle 1', u'12345', u'99999')

so sieht die Ausgabe im textview aus
Zuletzt geändert von truehumandesign am Samstag 10. Dezember 2011, 16:02, insgesamt 1-mal geändert.
BlackJack

@truehumandesign: Wenn Du `tuple` nicht kennst, dann solltest Du jetzt Datenbank und GUI mal solange beiseite lassen bis Du die Python-Grundlagen drauf hast. Arbeite am besten mal das Tutorial aus der Python-Dokumentation durch.

Und in der Zeile ist noch ein anderes Problem gewesen: Schau mal was links vom ``%``-Operator steht.
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

BlackJack hat geschrieben:@truehumandesign: Wenn Du `tuple` nicht kennst, dann solltest Du jetzt Datenbank und GUI mal solange beiseite lassen bis Du die Python-Grundlagen drauf hast. Arbeite am besten mal das Tutorial aus der Python-Dokumentation durch.

Und in der Zeile ist noch ein anderes Problem gewesen: Schau mal was links vom ``%``-Operator steht.
Meinst du buffer.set_text?
Wegen der bezeichnung text?

Edit:

Na alla,
def selectall(self, *args):
cur = self.conn.cursor()
cur.execute('SELECT * FROM foo')
outp = cur.fetchone()
buffer = self.builder.get_object('view_log').get_buffer()
buffer.set_text(outp[2])
self.conn.commit()
cur.close()
So gehts!
BlackJack

@truehumandesign: Ich meinte keine Bezeichnung sondern den Typ der letztendlich links vom ``%``-Operator gestanden hat. Die Methode gibt `None` zurück, darauf kann man sowieso kein ``%`` anwenden, was die Ausnahme ja auch sehr deutlich aussagt.
truehumandesign
User
Beiträge: 20
Registriert: Sonntag 4. Dezember 2011, 19:20

Ich arbeite jetzt erstmal diverse tutorials durch bevor ich mich gleich mit so Sachen beschäftige, wird wohl besser sein. Ich weiss zum Beispiel jetzt nicht warum der None zurück gibt.
Kann mir jemand noch ein paar tutorials die nicht Auf der offiziellen Python Seite vorhanden sind empfehlen?
Danke!
Antworten