Hallo!
Würde gerne mit dem Canvas-Widget ein paar Linien in einem Koordinatensystem ziehen. Das wird das einzige Fenster sein, aus dem das Programm besteht. Nun hab ich es bereits mit Scrollbars versehen. Dies funktioniert sogar einwandfrei. Mein Problem ist jetzt allerdings, dass das Canvas-Widget immer nur einen bestimmten Teil meines gesamten Fensters ausmacht. Wenn ich also maximiere, sehe ich den gleichen Ausschnitt wie vorher; wie kann ich das ändern?!
Weitere Frage: Gibt es eine Möglichkeit das Bild zu skalieren(eventuell per Mausrad)? Höhe und Breite variieren nämlich , je nach Eingabe stark. Eventuell käme auch in betracht, dass das Bild automatisch so skaliert wird, dass min. 2/3 zu sehen sind, oder so ähnlich..
Gruß
Daniel
Canvas + Scrollbars + Skalieren
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Zuletzt geändert von Daniel_SGe am Mittwoch 5. November 2008, 20:07, insgesamt 1-mal geändert.
Zunächst einmal solltest du dir den Sternchenimport von Tkinter abgewöhnen.
Als nächstes würde ich empfehlen, sich eher mit dem pack()-Manager als mit dem grid()-Manager zu beschäftigen. Aus meiner Sicht, lassen sich die meisten (einfachen) GUIs (so wie deine) damit einfacher und nachvollziehbarer aufbauen (ist aber auch Geschmacks-/Gewöhnungssache).
Auf jeden Fall ist es nicht empfehlenswert, beides zu mischen, wie du es am Ende mit dem frame tust. Dann auch konsequent bleiben.
Offenbar arbeitest du mit IDLE als IDE, sonst würde sich dein Fenster sofort wieder schließen, denn am Schluss fehlt ein root.mainloop().
Jetzt zu deinem (ersten) Problem:
Du musst - z.B. nach der Instanziierung - die folgenden Zeilen einfügen:
Und statt frame.pack():
Als nächstes würde ich empfehlen, sich eher mit dem pack()-Manager als mit dem grid()-Manager zu beschäftigen. Aus meiner Sicht, lassen sich die meisten (einfachen) GUIs (so wie deine) damit einfacher und nachvollziehbarer aufbauen (ist aber auch Geschmacks-/Gewöhnungssache).
Auf jeden Fall ist es nicht empfehlenswert, beides zu mischen, wie du es am Ende mit dem frame tust. Dann auch konsequent bleiben.
Offenbar arbeitest du mit IDLE als IDE, sonst würde sich dein Fenster sofort wieder schließen, denn am Schluss fehlt ein root.mainloop().
Jetzt zu deinem (ersten) Problem:
Du musst - z.B. nach der Instanziierung - die folgenden Zeilen einfügen:
Code: Alles auswählen
root.rowconfigure(0,weight=1)
root.columnconfigure(0,weight=1)
Code: Alles auswählen
frame.grid(row=0, column=0,sticky=N+S+E+W)
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Und stattdessen wie vorgehen? Sry, hab erst vor kurzem angefangen mich überhaupt mit Programmieren zu beschäftigen. Ich weiß also noch nich so was man eher "macht" und was eben nicht;) .Zunächst einmal solltest du dir den Sternchenimport von Tkinter abgewöhnen.
okay, werd ich machen;)Als nächstes würde ich empfehlen, sich eher mit dem pack()-Manager als mit dem grid()-Manager zu beschäftigen. Aus meiner Sicht, lassen sich die meisten (einfachen) GUIs (so wie deine) damit einfacher und nachvollziehbarer aufbauen (ist aber auch Geschmacks-/Gewöhnungssache).
Auf jeden Fall ist es nicht empfehlenswert, beides zu mischen, wie du es am Ende mit dem frame tust. Dann auch konsequent bleiben.
Sorry, hatte vergessen den restlichen code zu pasten;)Offenbar arbeitest du mit IDLE als IDE, sonst würde sich dein Fenster sofort wieder schließen, denn am Schluss fehlt ein root.mainloop().
Und zu meinem 1. Problem: Ist damit gelöst, vielen Dank
//Edit:
Weiteres Problem: Ich hätte gerne 2 Beschriftungen der Y bzw. X-Achse, jeweils jedoch unabhängig vom Scrollen der X bzw. Y-Achse. Muss ich dafür eine neue Instanz "Canvas" ausführen? Ich möchte schließlich 2 Fenster neben den Scrollbalken, die unabhängig davon reagieren...
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Daniel_SGe hat geschrieben:Und stattdessen wie vorgehen? Sry, hab erst vor kurzem angefangen mich überhaupt mit Programmieren zu beschäftigen. Ich weiß also noch nich so was man eher "macht" und was eben nicht;) .Zunächst einmal solltest du dir den Sternchenimport von Tkinter abgewöhnen.
Code: Alles auswählen
import Tkinter as tk
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Verstehe ich nicht.Daniel_SGe hat geschrieben:Weiteres Problem: Ich hätte gerne 2 Beschriftungen der Y bzw. X-Achse, jeweils jedoch unabhängig vom Scrollen der X bzw. Y-Achse. Muss ich dafür eine neue Instanz "Canvas" ausführen? Ich möchte schließlich 2 Fenster neben den Scrollbalken, die unabhängig davon reagieren...
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Gut. Also nochmal;)
Stell dir mal ein Koordinatensystem aus mehreren Tausenden Pixeln in sowohl x als auch y-Richtung vor (nur 1. Quadrant). Das ganze passt natürlich nicht auf den Bildschirm. Zur besseren Orientierung sind sowohl x als auch y-Achse beschriftet. Wenn ich jetzt allerdings scrolle, "verschwinden" diese Linien ja, da sie am Rand angebracht waren.
Viel sinnvoller wäre es jedoch, wenn ich die Achsnebeschriftungen immer sehen könnte. Dafür müsste aber die Y-Achse wahrscheinlich unabhängig vom x-scrollbalken machen und die y-achse unabhängig vom x-scrollbalken. Meine Frage ist, ob ich dafür zwingend 2 neue canvas-Instanzen (-Fenster oder was auch immer) benötige, und 2. eventuell einen Lösungstipp/Ansatz, wie ich diese Instanzen dann schön in das Fenster unterbringen kann.
Stell dir mal ein Koordinatensystem aus mehreren Tausenden Pixeln in sowohl x als auch y-Richtung vor (nur 1. Quadrant). Das ganze passt natürlich nicht auf den Bildschirm. Zur besseren Orientierung sind sowohl x als auch y-Achse beschriftet. Wenn ich jetzt allerdings scrolle, "verschwinden" diese Linien ja, da sie am Rand angebracht waren.
Viel sinnvoller wäre es jedoch, wenn ich die Achsnebeschriftungen immer sehen könnte. Dafür müsste aber die Y-Achse wahrscheinlich unabhängig vom x-scrollbalken machen und die y-achse unabhängig vom x-scrollbalken. Meine Frage ist, ob ich dafür zwingend 2 neue canvas-Instanzen (-Fenster oder was auch immer) benötige, und 2. eventuell einen Lösungstipp/Ansatz, wie ich diese Instanzen dann schön in das Fenster unterbringen kann.
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
hmm...
Was ich vielleicht noch erwähnen sollte, ist dass ich so´päter in Meinem code noch verwende um den Scrollbereich anzupassen... Gibt es irgendwelche Optionen dafür, dass ich erst ab bestimmten x bzw. y-Werten den Bereich zählen kann?
Was ich vielleicht noch erwähnen sollte, ist dass ich so´päter in Meinem code noch
Code: Alles auswählen
cv.config(scrollregion=cv.bbox(ALL))
Hallo Daniel_SGe
Sorry habe das ganze falsch interpretiert. Ja es sieht fast so aus, dass du zwei weitere Canvas Instanzen brauchst um die vertikale und horizontale Skalabeschriftung darzustellen. Dann sind die beiden Skalen-Canvas mit den zugehörigen Scrollbars zu synchronisieren. Also die vertikale Sakal-Canvas mit der vertikalen Scrollbar und die horizontale Skala-Canvas mit der horizontalen Scrollbar. Vorab ist zu sagen, dass es bei Tkinter nicht möglich ist vertikal gedrehter Text anzuzeigen!
Wenn das letztere wichtig ist musst du dies noch abklären bevor du beabsichtigst mit dem Einsatz von Tkinter als GUI weiter zu arbeiten.
Gruss wuf
Sorry habe das ganze falsch interpretiert. Ja es sieht fast so aus, dass du zwei weitere Canvas Instanzen brauchst um die vertikale und horizontale Skalabeschriftung darzustellen. Dann sind die beiden Skalen-Canvas mit den zugehörigen Scrollbars zu synchronisieren. Also die vertikale Sakal-Canvas mit der vertikalen Scrollbar und die horizontale Skala-Canvas mit der horizontalen Scrollbar. Vorab ist zu sagen, dass es bei Tkinter nicht möglich ist vertikal gedrehter Text anzuzeigen!
Wenn das letztere wichtig ist musst du dies noch abklären bevor du beabsichtigst mit dem Einsatz von Tkinter als GUI weiter zu arbeiten.
Gruss wuf
Take it easy Mates!
Nochmal zum Verständnis, damit ich nicht in die falsche Richtung weiterdenke:
Beim Starten hast du ein Canvas mit links und unten einer beschrifteten Achse, z.B. von 0 .. 400. Dann verschiebst du mit den Scrollbalken den sichtbaren Ausschnitt des Canvas und die Anzeige auf den Achsen ändert sich mit, so dass z.B. die x-Achse irgendwann die Werte von 500 .. 900 und die y-Achse die von 1200 .. 1600 anzeigt.
Richtig?
Wie soll sich das Koordinatensystem verhalten, wenn der Anwender die gesamte Fenstergröße ändert? Oder hat das Canvas ein fixe Größe (oben also 400x400 px), die unverändert bleibt?
Beim Starten hast du ein Canvas mit links und unten einer beschrifteten Achse, z.B. von 0 .. 400. Dann verschiebst du mit den Scrollbalken den sichtbaren Ausschnitt des Canvas und die Anzeige auf den Achsen ändert sich mit, so dass z.B. die x-Achse irgendwann die Werte von 500 .. 900 und die y-Achse die von 1200 .. 1600 anzeigt.
Richtig?
Wie soll sich das Koordinatensystem verhalten, wenn der Anwender die gesamte Fenstergröße ändert? Oder hat das Canvas ein fixe Größe (oben also 400x400 px), die unverändert bleibt?
Klar geht das: Du musst nur allen benötigten Text mit einem geeigneten Programm als GIF-Grafik erstellen und diese dann im richtigen Moment anzeigen ..wuf hat geschrieben:Vorab ist zu sagen, dass es bei Tkinter nicht möglich ist vertikal gedrehter Text anzuzeigen!
Hallo numerix
Ist schon klar habe das auch schon gemacht. Das heisst aber, dass du für jede Beschriftung ein .gif auf Lager haben musst. Ich wollte nur andeuten das es bei Tkinter kein vertikaler Text gibt der einfach mit Stringmanipulationen veränderbar ist!
Gruss wuf
Ist schon klar habe das auch schon gemacht. Das heisst aber, dass du für jede Beschriftung ein .gif auf Lager haben musst. Ich wollte nur andeuten das es bei Tkinter kein vertikaler Text gibt der einfach mit Stringmanipulationen veränderbar ist!
Gruss wuf
Take it easy Mates!
Das war auch nicht ganz ernst gemeint.wuf hat geschrieben:Hallo numerix
Ist schon klar habe das auch schon gemacht. Das heisst aber, dass du für jede Beschriftung ein .gif auf Lager haben musst. Ich wollte nur andeuten das es bei Tkinter kein vertikaler Text gibt der einfach mit Stringmanipulationen veränderbar ist!
Gruss wuf
Natürlich hast du Recht und das ist schade. Das ging doch schon vor 20 Jahren mit TurboPascal ...
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Zum Thema vertikaler Text. Schade, dass das nicht möglich ist, aber auch nicht zwingend notwendig;)
Das Canvas sollte im Prinzip immer den gesamten Fenster- bzw. Frameinhalt ausfüllen. Andere Widgets, wie ein Menü oder Buttons, würden dann eher festen Fensterinhalt bekommen...
Genau. Wenn ich also diesen Ausschnitt [500, 900; 1200, 1600] "sehe" sollten x und y-achse entsprechen 500-1200 bzw. 900-1600 anzeigen.Beim Starten hast du ein Canvas mit links und unten einer beschrifteten Achse, z.B. von 0 .. 400. Dann verschiebst du mit den Scrollbalken den sichtbaren Ausschnitt des Canvas und die Anzeige auf den Achsen ändert sich mit, so dass z.B. die x-Achse irgendwann die Werte von 500 .. 900 und die y-Achse die von 1200 .. 1600 anzeigt.
Das Canvas sollte im Prinzip immer den gesamten Fenster- bzw. Frameinhalt ausfüllen. Andere Widgets, wie ein Menü oder Buttons, würden dann eher festen Fensterinhalt bekommen...
Ich würde es bei einem Canvas belassen und bei jeder Änderung des Fensterausschnittes - sei des durch Scrollen oder Größenänderung des Fensters die entsprechenden Achsen neu zeichnen bzw. beschriften.
Konkret sieht das dann so aus:
Ob das mit den Abständen zum Rand so passt, hängt von der verwendeten Schriftart ab. Bei meinem Default-Font passt es für die Achsenbeschriftung.
Ich habe die Achsen jetzt mal so orientiert wie das Canvas-Koordinatensystem. Sollen die Achsen entsprechend einem kartesischen System sein, müsste man das anpassen.
Ineffizient ist bei meiner Variante, dass jeweils beide Achsen samt Beschriftung neu gezeichnet werden. Falls das zu langsam ist, könnte man die Performance dadurch erhöhen, dass man z.B. bei horizontalem Scrollen die y-Achse nicht jeweils löscht und neu zeichnen lässt, sondern mittels move(tag) nur verschiebt.
Für den Fall ist die Unterscheidung zwischen den tags "xaxis" und "yaxis" notwendig, für den vorliegenden Code hätte auch ein gemeines tag genügt.
Konkret sieht das dann so aus:
Code: Alles auswählen
import Tkinter as tk
def create_axes(arg=None):
w,h = canv.winfo_width(),canv.winfo_height()
x0, y0 = int(canv.canvasx(0))+40, int(canv.canvasy(0))+25
x1, y1 = x0+w-45, y0+h-30
startx = x0//50*50+50
starty = y0//50*50+50
# create x-axis
canv.delete("xaxis")
canv.create_line(x0,y0,x1,y0,arrow=tk.LAST,width=2,tags="xaxis")
for x in xrange(startx,x1,50):
canv.create_line(x,y0-4,x,y0+4,tags="xaxis")
canv.create_text(x,y0-4,anchor=tk.S,text=str(x),tags="xaxis")
# create y-axis
canv.delete("yaxis")
canv.create_line(x0,y0,x0,y1,arrow=tk.LAST,width=2,tags="yaxis")
for y in xrange(starty,y1,50):
canv.create_line(x0-4,y,x0+4,y,tags="yaxis")
canv.create_text(x0-6,y,anchor=tk.E,text=str(y),tags="yaxis")
def xmove(*args):
canv.xview(*args)
create_axes()
def ymove(*args):
canv.yview(*args)
create_axes()
# -----------------
root = tk.Tk()
root.title = "Canvas with Scrollbars and Axis"
root.minsize(400,300)
root.bind("<Configure>",create_axes)
frame = tk.Frame(root)
frame.pack(side=tk.LEFT,fill=tk.BOTH,expand=True)
canv = tk.Canvas(frame,bg="white")
canv.pack(side=tk.TOP,fill=tk.BOTH,expand=True)
scroll_y = tk.Scrollbar(root,command=ymove)
scroll_y.pack(side=tk.LEFT,fill=tk.Y)
scroll_x = tk.Scrollbar(frame,command=xmove,orient=tk.HORIZONTAL)
scroll_x.pack(side=tk.BOTTOM,fill=tk.X)
canv.config(scrollregion=(0,0,20000,20000),width=400,height=300,
yscrollcommand=scroll_y.set,xscrollcommand=scroll_x.set)
create_axes()
canv.create_oval(140,120,200,180,fill="yellow",outline="darkgreen",width=4)
root.mainloop()
Ich habe die Achsen jetzt mal so orientiert wie das Canvas-Koordinatensystem. Sollen die Achsen entsprechend einem kartesischen System sein, müsste man das anpassen.
Ineffizient ist bei meiner Variante, dass jeweils beide Achsen samt Beschriftung neu gezeichnet werden. Falls das zu langsam ist, könnte man die Performance dadurch erhöhen, dass man z.B. bei horizontalem Scrollen die y-Achse nicht jeweils löscht und neu zeichnen lässt, sondern mittels move(tag) nur verschiebt.
Für den Fall ist die Unterscheidung zwischen den tags "xaxis" und "yaxis" notwendig, für den vorliegenden Code hätte auch ein gemeines tag genügt.
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Hi!
Der Ansatz sieht schonmal ganz nett aus.
Du hast ja jetzt pack verwendet. Ich überlege, ob ich nicht vll. doch komplett auf pack umsteigen soll. Dann müsstest du mir eventuell allerdings noch sagen, wie ich einen Befehl wie
hinbekomme. Das Fenster muss sich nämlich dynamisch an eine Eingabe anpassen können.
Der Ansatz sieht schonmal ganz nett aus.
Du hast ja jetzt pack verwendet. Ich überlege, ob ich nicht vll. doch komplett auf pack umsteigen soll. Dann müsstest du mir eventuell allerdings noch sagen, wie ich einen Befehl wie
Code: Alles auswählen
cv.config(scrollregion=cv.bbox(ALL))
Wo sollte da das Problem sein?Daniel_SGe hat geschrieben:Dann müsstest du mir eventuell allerdings noch sagen, wie ich einen Befehl wie
hinbekomme.Code: Alles auswählen
cv.config(scrollregion=cv.bbox(ALL))
Inwiefern: Soll der sichtbare Ausschnitt dann in den Bereich wechseln, wo gerade ein neues Item eingefügt wurde?Daniel_SGe hat geschrieben:Das Fenster muss sich nämlich dynamisch an eine Eingabe anpassen können.
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Ich bekomm die Fehlermeldung:Wo sollte da das Problem sein?
cv.config(scrollregion=cv.bbox(ALL))
NameError: name 'ALL' is not defined
Der sagen wir mal "Graph" ist je nach Eingabe größer oder kleiner. So dann auch das Canvas. Diese maximale Größe soll dann auch als maximaler Scrollbereich genommen werden...Inwiefern: Soll der sichtbare Ausschnitt dann in den Bereich wechseln, wo gerade ein neues Item eingefügt wurde?
Naja sicher bekommst du einen NameError, weil ALL eine Tkinter-Konstante ist, du Tkinter mittels Sternchenimport eingebunden hast und ich nicht.
Also muss es - für meinen Code - heißen tk.ALL oder zu verwendest stattdessen eine Zeichenkette "all".
Das Ändern des maximalen Scrollbereichs ist völlig unproblematisch.
Du musst nur die scrollregion neu setzen, wenn du ein neues Item auf das Canvas platzierst. Das heißt: Nachdem das Item hinzugefügt wurde, genügt deine Zeile mit dem bbox("all").
Also muss es - für meinen Code - heißen tk.ALL oder zu verwendest stattdessen eine Zeichenkette "all".
Das Ändern des maximalen Scrollbereichs ist völlig unproblematisch.
Du musst nur die scrollregion neu setzen, wenn du ein neues Item auf das Canvas platzierst. Das heißt: Nachdem das Item hinzugefügt wurde, genügt deine Zeile mit dem bbox("all").
-
- User
- Beiträge: 28
- Registriert: Montag 8. September 2008, 19:39
Hmm. Dummer Fehler. Hab jetzt allerdings ein weiteres Problem. Der Graph, den ich zeichnen möchte fängt bei einem bestimmten y-Wert an. Da der Graph so nie von Anfang an im Fenster auftaucht, würde ich den Graphen gerne auch dynamisch je nach maximaler Höhe des Graphen zeichnen lassen. Da die maximale Höhe jedoch von einem Parameter und einem Zufallswert abhängt, wäre die einzige Möglichkeit, die mir einfällt den ganzen Code in eine Funktion zu packen und diese dann das 2. Mal mit dem Höhenparameter aufzurufen. (Neuzeichnen kommt wohl eher nicht in Betracht, je nachdem sind es über 1000 Elemente.
Wenn dies dann funktioniert sollte der 1. Ausschnitt den man sieht, nicht links oben sein, sondern rechts oben. Lässt sich das einfach realisieren?
Noch ein kleiner "Schönheitsfehler": Beim Scrollen bleiben die x und Y-Achse zwar am Rand der Graph "schiebt" sich aber auch durch diese hindurch. Eine Option um dies abzustellen?
Wenn dies dann funktioniert sollte der 1. Ausschnitt den man sieht, nicht links oben sein, sondern rechts oben. Lässt sich das einfach realisieren?
Noch ein kleiner "Schönheitsfehler": Beim Scrollen bleiben die x und Y-Achse zwar am Rand der Graph "schiebt" sich aber auch durch diese hindurch. Eine Option um dies abzustellen?