Wahrscheinlich einfaches Problem

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
gugugs
User
Beiträge: 113
Registriert: Dienstag 30. Dezember 2008, 12:38

Ich hab ein Fenster, in dem 2 RadioButton sind.

Und zwar, würde ich gern, das wenn z.B. : radiobutton1 aktiv ist, dass ich dann 4 entrys habe und sobald ich auf radiobutton2 klicke dass dann nur 3 entrys da sind, und bei radiobutton1 halt wieder 4 entrys

Wie stelle ich das am besten an?

Ich habe mir erst gedacht, das wenn Radio Button 1 toggled wurde, das er dann in eine Funktion geht, in der er die 4 entrys in eine box macht, aber wie bekomme ich die dann im Fenster angezeigt?

Und wie schaffe ich es dann, das wenn Button 2 toggled wurde, das er die 4 entrys aus der box löscht und 3 wieder herein schreibt?


Es handelt sich um GTK

ich hatte mir das jetzt so vorgestellt:
der abschnitt in dem ich das vor habe:

Code: Alles auswählen


class umfang:

	def change(self, widget, data):
		if data=="radio1":
			entry1=gtk.Entry()
			entry2=gtk.Entry()
			entry3=gtk.Entry()
			entry4=gtk.Entry()
			vbox.pack_start(entry1)
			vbox.pack_start(entry2)
			vbox.pack_start(entry3)
			vbox.pack_start(entry4)
		if data=="radio2":
			entry1=gtk.Entry()
			entry2=gtk.Entry()
			entry3=gtk.Entry()
			vbox.pack_start(entry1)
			vbox.pack_start(entry2)
			vbox.pack_start(entry3)
			
			

	
	def __init__(self):
		window=gtk.Window(gtk.WINDOW_TOPLEVEL)
		window.set_title("Rechner - Umfang")
		window.set_border_width(0)

		vbox=gtk.VBox(False, 0)
		window.add(vbox)

		frame=gtk.Frame("Erklaerung:")
		label=gtk.Label("Waehlen sie zunaechst ob Viereck oder Dreieck.\n"
				"Danach geben sie entweder 4 Informationen fuer das Viereck\n"
				"oder 3 fuer das Dreieck.")
		frame.add(label)
		vbox.pack_start(frame)

		radio1=gtk.RadioButton(None, "Viereck")
		radio1.connect("toggled", self.change, "radio1")
		vbox.pack_start(radio1)

		radio2=gtk.RadioButton(radio1, "Dreieck")
		radio2.connect("toggled", self.change, "radio2")
		vbox.pack_start(radio2)

		window.show_all()


Aber so funktioniert das nicht, allein schon, weil dort, vbox ja noch nicht definiert ist. Aber ich weis nicht, wie ich das sonst anstellen sollte.
Weis irgendjemand wie mir zu helfen ist? Es kann auch ganz anders sein, hauptsache das Prinzip wird verwirklicht

Bitte um jede Hilfe
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Ich würde einfach ein `gtk.Notebook` nehmen, da hat dann einfach jede geometrische Figur eine eigene Seite. Und PyGtk kann auch Umlaute anzeigen, wenn man Unicode-Strings (oder UTF8-kodierte Bytestrings) übergibt.

Ansonsten ist das Scope-Problem eher grundsätzlicher Natur als ein Gtk-Problem, also vielleicht solltest du dich erst an GUI-Programmierung wagen, wenn du an einem soliden Basiswissen über Python verfügst.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Hi gugugs,

du hast wohl die Benutzung von Klasse nicht ganz verstanden.

Wenn ich eine Variable als klassenübergreifend definieren will, dann erstelle ich sie "auf" der Instanz:

Code: Alles auswählen

[...]
def do_bla(self):
    self.myvar = "foo"
[...]
Eine Variable, die nicht in der Instanz erstellt wurde, wird nach Ende einer Funktion wieder gelöscht.

Kleines Beispiel:

Code: Alles auswählen

In [2]:
class Blah:
    def __init__(self):
        self.blah1 = "foo"
        blah2 = "bar"
        self.do_blah()
    def do_blah(self):
         self.blah3 = "blubb"
         blah4 = "blah"

In [8]: b = Blah()

In [9]: b.blah1
Out[9]: 'foo'

In [10]: b.blah2
---------------------------------------------------------------------------
<type 'exceptions.AttributeError'>: Blah instance has no attribute 'blah2'

In [11]: b.blah3
Out[11]: 'blubb'

In [12]: b.blah4
---------------------------------------------------------------------------
<type 'exceptions.AttributeError'>: Blah instance has no attribute 'blah4'
Du machst also Variablen wie deine Fenster-Elemente klassenübergreifend nutzbar, sodass du sie auch in anderen Funktionen als __init__() benutzen kannst.

PS: Dadurch musst du dann auch in den connect-Funktionen das "data"-Argument nicht mehr mitgeben, weil du einfach abfragst, ob das den-Event-auslösende Widget das erste oder zweite war:

Code: Alles auswählen

    def change(self, widget):
        if widget==self.radio1:
            entry1=gtk.Entry()
            entry2=gtk.Entry()
            entry3=gtk.Entry()
            entry4=gtk.Entry()
            vbox.pack_start(entry1)
            vbox.pack_start(entry2)
            vbox.pack_start(entry3)
            vbox.pack_start(entry4)
        elif widget==self.radio2:
            entry1=gtk.Entry()
            entry2=gtk.Entry()
            entry3=gtk.Entry()
            vbox.pack_start(entry1)
            vbox.pack_start(entry2)
            vbox.pack_start(entry3)
außerdem könnte man die Funktion noch ein wenig verbessern - du führst in beiden Fällen identischen Code aus, das ist so, als ob würdest du sagen "Wenn du hier zwei Äpfel hast springe einmal im Kreis und wenn nicht tus trotzdem":

Code: Alles auswählen

    def change(self, widget):
            entry1=gtk.Entry()
            entry2=gtk.Entry()
            entry3=gtk.Entry()
            vbox.pack_start(entry1)
            vbox.pack_start(entry2)
            vbox.pack_start(entry3)
        if widget==self.radio1:
            entry4=gtk.Entry()
            vbox.pack_start(entry4)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dauerbaustelle hat geschrieben:Wenn ich eine Variable als klassenübergreifend definieren will, dann erstelle ich sie "auf" der Instanz
Nicht ganz ;) Wenn ich sie KLASSENübergreifend haben will, dann definier ich sie auch in der Klasse - nicht in einer Funktion(!), denn sonst ist sie nicht lokal zur Klasse, sondern lokal zur Funktion. Will ich sie INSTANZübergreifend benutzen, dann definiere ich sie auf der Instanz ;)
Dauerbaustelle hat geschrieben:Eine Variable, die nicht in der Instanz erstellt wurde, wird nach Ende einer Funktion wieder gelöscht.
Auch wieder nicht ganz ;) Ist die "Variable" lokal zur Funktion, dann ja, lokal zur Klasse bzw einem anderen äußeren Scope - bis einschliesslich Modulebene, dann nicht ;)

Soo ich werf mal 20 cent in die Klugscheisserkasse :oops:
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

-.-

Dir ist aber schon klar, dass wir es hier mit einem Einsteiger zu tun haben?!

-______________________-
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Naja das heisst aber nicht, dass die Information falsch sein darf, gerade bei einem Anfänger der das ja nicht erkennen kann, besonders wichtig.
Und der Schwierigkeitsgrad das zu verstehen erhöht sich auch nicht sonderlich.
gugugs
User
Beiträge: 113
Registriert: Dienstag 30. Dezember 2008, 12:38

Vielen Dank für alle Beiträge, hab jetzt wieder ein wenig mehr verstanden. Hab mir auch das mit den lokalen und globalen Variablen und das Instanzen Thema etwas mehr durchgelesen. Habe das ganze aber jetzt sowieso anders gemacht, mit einem NoteBook war es dann doch einfacher.


Jetzt stehe ich leider schon wieder vor einem neuen Problem, wenn man mir nun da noch helfen könnte, wär echt super. Und zwar, warum kommt bei diesem kleinen Berreich, die Fehlermeldung:

AttributeError: 'NoneType' object has no attribute 'draw_line'


Berreich:

Code: Alles auswählen

		window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		window.set_title("Drawing Area Example")
		window.connect("destroy", lambda w: gtk.main_quit())
		window.set_size_request(400, 300)
		area = gtk.DrawingArea()
		area.show()
		area.set_size_request(400, 300)
		drawable = area.window
		table = gtk.Table(2,2)
		hruler = gtk.HRuler()
		vruler = gtk.VRuler()
		hruler.set_range(0, 400, 0, 400)
		vruler.set_range(0, 300, 0, 300)
		table.attach(hruler, 1, 2, 0, 1, yoptions=0)
		table.attach(vruler, 0, 1, 1, 2, xoptions=0)

		style = area.get_style()
        	gc = style.fg_gc[gtk.STATE_NORMAL]
		drawable.draw_line(gc, 10, 30, 20, 40)

		window.add(table)

		window.show_all()
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Widgets müssen erst "realized" sein (hat da jemand spontan ein schöne Übersetzung dafür?) sein, bevor sie mit einem `gtk.gdk.Window` verbunden sind. Und das geschieht erst, wenn `realize()` aufgerufen wird, und dazu muss das Widget in einem Toplevel-Window (sichtbar) sein. IdR geschieht der Aufruf indirekt (bspw. durch einen Aufruf von `show()`). Vorher gibt ``widget.window`` eben `None` zurück, wie in deinem Code zu sehen ist.

Außerdem genügt es nicht, nur einmal zu zeichnen. Wenn z.B. die Größe verändert wird oder ein anderes Fenster über das Fenster geschoben wird oder das Fenster minimiert und wieder maximiert wird, muss ja wieder neu gezeichnet werden. Deshalb solltest du den Zeichencode in eine Funktion stecken und diese mit dem ``expose-event``-Signal verbinden. Dann wird jedes Mal die Funktion aufgerufen, wenn das Widget sich neu zeichnen soll.

Desweiteren finde ich es wesentlich angenehmer mit Cairo zu arbeiten als mit den ganzen GTK+-Zeichenfunktionen.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Trundle, du hast glaube ich vergessen zu erwähnen, dass eben dieses draw_line erst funktioniert, wenn die Widgets ge-realized wurden (was du ja erwähnt hattest).

@gugugs, das heißt ganze einfach, dass sich das Widget schon IM Fenster befinden muss. Dies tut es allerdings nicht, da sein Parent (das gtk.Table-Widget) noch nicht hinzugefügt wurde.

Das heißt: Zuerst window.add(table) und dann ...draw_blubb.

Außerdem empfiehlt es sich wie bereits erwähnt die Widgets auf der Instanz zu erstellen, also wie bereits erklärt mit self.foo und self.bar anzusprechen.

Grüße
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

@Dauerbaustelle
Ich habe geschrieben, dass dann ``widget.window`` einen None-Typ zurückgibt, woraus sich ergibt, dass eben `drawable` ein None-Typ ist und kein `gtk.gdk.Window`. Dass man weiß, dass `None` keine `draw_line`-Methode hat, davon gehe ich eigentlich aus.

Außerdem muss erst das Widget selbst und *sämtliche* Container (also auch das Toplevel-Fester) sichtbar sein, bevor überhaupt ein `gtk.gdk.Window` mit dem Widget verbunden ist. Es bringt also schlicht und ergreifend nichts, "zuerst window.add(table) und dann ...draw_blubb" zu machen, weil davon das Fenster immer noch nicht angezeigt wird, mal ganz davon abgesehen, dass die Area im ganzen Code in keinen Container gesteckt wird. Und selbst wenn es funktionieren würde, wäre es immer noch falsch. Der richtige Weg ist es, nach einem Expose-Event neu zu zeichen.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
gugugs
User
Beiträge: 113
Registriert: Dienstag 30. Dezember 2008, 12:38

Oke schon mal vielen Dank.

Um den Umgang mit GTK zu lernen, verfolge ich das dieses Tutorial:
http://www.pygtk.org/pygtk2tutorial/

Bis jetzt hab ich dort auch alles verstanden, und die Beispiele wurden sehr verständlich gemacht, doch bei dem Beispiele bei 12.2:

http://www.pygtk.org/pygtk2tutorial/sec ... thods.html

bei dem es genau darum geht, wovon gerade die Rede ist, verstehe ich leider überhaupt nicht. Wie insgesammte Aufbau ist, und wie die Funktionen UNTEN funktionieren, verstehe ich auch. Doch der Anfang, wo der Aufbau des Fensters ist, da habe ich große Probleme in diesem Berreich:

Code: Alles auswählen

    def __init__(self):
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_title("Drawing Area Example")
        window.connect("destroy", lambda w: gtk.main_quit())
        self.area = gtk.DrawingArea()
        self.area.set_size_request(400, 300)
        self.pangolayout = self.area.create_pango_layout("")
        self.sw = gtk.ScrolledWindow()
        self.sw.add_with_viewport(self.area)
        self.table = gtk.Table(2,2)
        self.hruler = gtk.HRuler()
        self.vruler = gtk.VRuler()
        self.hruler.set_range(0, 400, 0, 400)
        self.vruler.set_range(0, 300, 0, 300)
        self.table.attach(self.hruler, 1, 2, 0, 1, yoptions=0)
        self.table.attach(self.vruler, 0, 1, 1, 2, xoptions=0)
        self.table.attach(self.sw, 1, 2, 1, 2)
        window.add(self.table)
        self.area.set_events(gtk.gdk.POINTER_MOTION_MASK |
                             gtk.gdk.POINTER_MOTION_HINT_MASK )
        self.area.connect("expose-event", self.area_expose_cb)
        def motion_notify(ruler, event):
            return ruler.emit("motion_notify_event", event)
        self.area.connect_object("motion_notify_event", motion_notify,
                                 self.hruler)
        self.area.connect_object("motion_notify_event", motion_notify,
                                 self.vruler)
        self.hadj = self.sw.get_hadjustment()
        self.vadj = self.sw.get_vadjustment()
        def val_cb(adj, ruler, horiz):
            if horiz:
                span = self.sw.get_allocation()[3]
            else:
                span = self.sw.get_allocation()[2]
            l,u,p,m = ruler.get_range()
            v = adj.value
            ruler.set_range(v, v+span, p, m)
            while gtk.events_pending():
                gtk.main_iteration()
        self.hadj.connect('value-changed', val_cb, self.hruler, True)
        self.vadj.connect('value-changed', val_cb, self.vruler, False)
        def size_allocate_cb(wid, allocation):
            x, y, w, h = allocation
            l,u,p,m = self.hruler.get_range()
            m = max(m, w)
            self.hruler.set_range(l, l+w, p, m)
            l,u,p,m = self.vruler.get_range()
            m = max(m, h)
            self.vruler.set_range(l, l+h, p, m)
        self.sw.connect('size-allocate', size_allocate_cb)
        self.area.show()
        self.hruler.show()
        self.vruler.show()
        self.sw.show()
        self.table.show()
        window.show()

Es wäre echt total Spitze, wenn es sich vllt jemand ein bisschen Zeit nehmen könnte mir diesen Teil zu erklären:

Code: Alles auswählen

        self.area.set_events(gtk.gdk.POINTER_MOTION_MASK |
                             gtk.gdk.POINTER_MOTION_HINT_MASK )
        self.area.connect("expose-event", self.area_expose_cb)
        def motion_notify(ruler, event):
            return ruler.emit("motion_notify_event", event)
        self.area.connect_object("motion_notify_event", motion_notify,
                                 self.hruler)
        self.area.connect_object("motion_notify_event", motion_notify,
                                 self.vruler)
        self.hadj = self.sw.get_hadjustment()
        self.vadj = self.sw.get_vadjustment()
        def val_cb(adj, ruler, horiz):
            if horiz:
                span = self.sw.get_allocation()[3]
            else:
                span = self.sw.get_allocation()[2]
            l,u,p,m = ruler.get_range()
            v = adj.value
            ruler.set_range(v, v+span, p, m)
            while gtk.events_pending():
                gtk.main_iteration()
        self.hadj.connect('value-changed', val_cb, self.hruler, True)
        self.vadj.connect('value-changed', val_cb, self.vruler, False)
        def size_allocate_cb(wid, allocation):
            x, y, w, h = allocation
            l,u,p,m = self.hruler.get_range()
            m = max(m, w)
            self.hruler.set_range(l, l+w, p, m)
            l,u,p,m = self.vruler.get_range()
            m = max(m, h)
            self.vruler.set_range(l, l+h, p, m)
        self.sw.connect('size-allocate', size_allocate_cb)
Denn dieser Teil ist leider nicht so wirklich toll beschrieben wie ich finde.
Für jemand der nur bis zu diesem Teil mit GTK zu tun hatte, ist er wirklich nicht so einfach zu verstehen.

Vielen Dank schon mal.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

O________o

Der Code ist grausig. Junger Vadder!

(Sorry dass ich nix konstruktives beitragen konnte^^)
gugugs
User
Beiträge: 113
Registriert: Dienstag 30. Dezember 2008, 12:38

Hm, oke, kennt dann vllt jemand ein besseres Tutorial? Oder eine bessere Möglichkeit, dieses Thema zu lernen?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ja, natürlich: Einfach selbst ausprobieren!

Versuche, irgendwelche lustigen Spiele zu schreiben oder schreibe dir einen Anwendungsstarter für deine meistgenutzten Programme. Oder hack dir mit gtkmozemed nen Browser zusammen.

Oder so was in der Art. Am meisten und schnellsten und besten lernt man (sofern man die Grundlagen beherrscht), indem man selbst ausprobiert, nicht weiterkommt und auf die Nase fliegt.

Ich habe auch mit dem pygtk-Tutorial angefangen, ohne Vorwissen. Ich kann sagen, dass ich python mittlerweile ganz gut beherrsche.

<wisper>Obwohl mit manchmal das Hintergrundwissen fehlt, anwenden kann ichs</wisper>
gugugs
User
Beiträge: 113
Registriert: Dienstag 30. Dezember 2008, 12:38

Das versuche ich auch, so hab ich mich schon durch PHP gebissen^^

Und jetzt wollte ich ein Programm machen, bei dem man 2 Koordinaten eingibt, und dann eine Linie gezeichnet wird. Das mit dem draw_blub ist mir jetzt nach der vorherigen Erklärung klar. Aber da sind ja dann noch ein paar andere Probleme, z.B.: das mit dem, das wenn sich was ändert, sich das Programm neu zeichnen soll. Und das mit den Ruler hätte ich gern so gehabt, dass da wo die Maus ist, das die Ruler mit der Maus gehen und sich verändern. Jetzt wollte ich mir das Tutorial zur Hilfe nehmen, aber jetzt bin ich wie schon gesagt genau an der blöden Stelle, wo ich diesen Teil ÜBERHAUPT nicht verstehe, und dazu das Beispielsscript auch noch sehr schlecht ist :S
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Hier mal ein wenig Beispielcode, wie man das mit Hilfe von Cairo und einem eigenen Widget machen kann. Der Code erhebt nicht den Anspruch, ein Tutorial oder gar perfekt zu sein, aber vllt ist er ja für jemanden hilfreich.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
Dr.Miles
User
Beiträge: 38
Registriert: Montag 15. Dezember 2008, 08:33
Wohnort: Mannheim
Kontaktdaten:

Eine Variable, die nicht in der Instanz erstellt wurde, wird nach Ende einer Funktion wieder gelöscht.
Meinst du mit Funktion Methode?

Code: Alles auswählen

class Blah:
    def __init__(self):
        self.blah1 = "foo"
        blah2 = "bar"
        self.do_blah()
So wie ich es verstanden habe ist blah2 nur solange gültig, wie die Methode aktiv ist, stimmt das? und eine mit self.blah erstellte Variable ist in der ganzen Instanz gültig?

Edit: Ist eine Klasse dasselbe wie ein Datentyp?
Gruß
Dr.Miles
www.i2p2.de <--- sehr interressantes Anonymisierungsprojekt.
www.schaeuble-wegtreten.de <--- Petition gegen Schäuble
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dr.Miles hat geschrieben:Meinst du mit Funktion Methode?
Ja. Ich persönlich finde aber die Unterscheidung in Methode, Funktion und Prozedur recht sinnlos und unnötig. Insbesondere in Python ist Methode und Funktion so ziemlich das gleiche, weil wenn man eine Funktion als Attribut an eine Klasse binden kann wodurch es in Java-Sprech magisch zu einer Methode wird obwohl sich eigentlich nichts ändert. So kann man auch Methoden in Python wie Funktionen auf ihren Klassen aufrufen und einfach als Parameter ``self`` eine Instanz mitgeben. Will gar nicht wissen wie man sowas in das Methoden/Funktionen/Prozeduren-Weltbild einpassen würde.
Dr.Miles hat geschrieben:So wie ich es verstanden habe ist blah2 nur solange gültig, wie die Methode aktiv ist, stimmt das? und eine mit self.blah erstellte Variable ist in der ganzen Instanz gültig?
Es ist eine lokale Variable und daher nur in der Funktion gültig, ja. ``self.blah`` ist länger gültig, da es ein Attribut der Instanz ist und somit auch nach dem Ende der Funktion noch eine Referenz auf das Objekt was an ``self.blah`` besteht. Somit wird es nicht vom Garbage Collector weggeräumt, ``blah2`` aber schon.
Dr.Miles hat geschrieben:Edit: Ist eine Klasse dasselbe wie ein Datentyp?
Ja, eine Klasse ist ein Datentyp. Und eine Klasse hat auch eine Klasse (also einen Typ), die sogenannte Metaklasse. Und alles sind Objekte.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten