canvas-diagramm speichern

Fragen zu Tkinter.
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hallo, habe vor einiger Zeit unter dem Betreff "errechnete Werte in Canvas anzeigen" gute Hilfe und berechtigte Kritik bekommen. Einer der Kritikpunkte war, dass ich die Diagrammdaten "wild" im Code verstreut hatte. Habe nun etwas Zeit und wollte mein Diagramm nun etwas universeller gestalten. Ein wichtiger Punkt für mich wäre, dass ich das fertige Diagramm abspeichern kann. Wahrscheinlich sehe ich den Wald vor lauter Bäumen nicht, aber ich finde keine Funktion, die so etwas macht. Alles was ich finde, bezieht sich auf Diagramme aus Matplotlib. Vielleicht kann ich einen Tip bekommen, wonach oder wo ich suchen muß.
Vielen Dank und Gruß, Rainer
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Liebe Leute, ich weiß zwar nicht, ob das der richtige Weg ist, aber keine Hilfe???
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@pyzip: tkinters Canvas sieht nur vor, den Inhalt als postscript mit der gleichnamigen Funktion zu speichern.
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

@Sirius3: Danke! Damit habe ich wenigstens ein paar Beispiele gefunden, mit denen ich spielen kann.
Rainer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi pyzip

Hier eine Variante mit PIL:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import math
try:
    # Tkinter for Python 2.xx
    import Tkinter as tk
except ImportError:
    # Tkinter for Python 3.xx
    import tkinter as tk
from PIL import Image, ImageDraw, ImageFont, ImageTk

APP_TITLE = "Save Canvas Image"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 500
APP_HEIGHT = 400

GRAPH_TITLE = "sin(x)=blue  cos(x)=red"
GRAPH_WIDTH = 400
GRAPH_HEIGHT = 300
GRAPH_TITLE_FONT = ImageFont.truetype("arial.ttf", 18)
GRAPH_FILE_NAME = "My_Graph.jpg"

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)

CENTER_Y = GRAPH_HEIGHT / 2
INCREMENT_X = 1
FACTOR_X = 0.04
AMPLITUDE = 80


class MyGraph(tk.Frame):

    def __init__(self, app_win, width, height, **kwargs):
        self.app_win = app_win
        self.width = width
        self.height = height
        
        tk.Frame.__init__(self, app_win, **kwargs)
        
        self.build()
        
    def build(self):
        self.draw_frame = tk.Label(self)
        self.draw_frame.pack(fill='both', expand=True)

        self.graph_plane = Image.new("RGB", (self.width, self.height), WHITE)
        self.drawing = ImageDraw.Draw(self.graph_plane)
        
        self.calculate()
        
        self.label_image = ImageTk.PhotoImage(self.graph_plane)
        self.draw_frame['image'] = self.label_image
        
    def calculate(self):
        sine_values = list()
        for x in range(400):
            x_coords = x * INCREMENT_X
            y_coords = int(math.sin(x * FACTOR_X) * AMPLITUDE) + CENTER_Y
            sine_values.append((x_coords, y_coords))
            
        cos_values = list()
        for x in range(400):
            x_coords = x * INCREMENT_X
            y_coords = int(math.cos(x * FACTOR_X) * AMPLITUDE) + CENTER_Y
            cos_values.append((x_coords, y_coords))
            
        self.draw_graph(sine_values, cos_values)
        self.graph_plane.save(GRAPH_FILE_NAME)

    def draw_graph(self, sin_values, cos_values):
        self.drawing.line(sin_values, fill=BLUE)
        self.drawing.line(cos_values, fill=RED)
        self.drawing.text(
            (10, 20), GRAPH_TITLE, fill=BLACK,font=GRAPH_TITLE_FONT)
 
       
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = MyGraph(app_win, GRAPH_WIDTH, GRAPH_HEIGHT
        ).pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()
Gruss wuf :wink:
Take it easy Mates!
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi wuf, danke für den ausführlichen Code! Ich erinnere mich, dass du mir seinerzeit schon einmal das Modul "PIL" vorgeschlagen hast. Ich hatte da aber erhebliche Schwierigkeiten und nach der Hilfe eines Mod. hat das Anzeigen der Zwischenergebnisse in einem Canvas dann ja geklappt. Werde aber trotzdem dein Beispiel durcharbeiten...hoffentlich mit etwas mehr Erfolg als damals...stehe nun ja nicht mehr ganz am Anfang.
Gruß Rainer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi pyzip

Wenn du eine Grafik erstellst die anschliessend auch als Bilddatei gespeichert werden muss würde ich an Stelle eines Tk.Canvas-Widgets die Draw-Klasse des Pil-ImageDraw Moduls verwenden. Diese Klasse verfügt über genügend Methoden um eine saubere Grafik zu konstruieren. Die Grafik wird dann nur im Speicher erstellt. Um diese Grafik danach aus dem Speicher in einem Tk-Widget anzuzeigen muss sie mittels der PhotoImage-Methode des Pil-ImageTk Moduls in das Tk-Bildformat umgewandelt werden. Dann eignet sich das tk.Label-Widget für die einfache Anzeige der Grafik am besten. Du kannst die Grafik natürlich auch in einem tk.Canvas-Widget mittels der canvas.create_image-Methode anzeigen. Um die Grafik anschliessend in einer Datei mit einem gewünschten Bildformat zu speichern bedient man sich der save-Methode des Pil-Image-Objektes.

Gruss wuf :wink:
Take it easy Mates!
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi wuf, danke für deine weiteren Ausführungen. Mein Problem war, in einem Diagramm jeweils Zwischenergebnisse einer Berechnung anzuzeigen. Es geht erst in 2ter Linie darum, ein fertiges Diagramm zu speichern. Es muß auch mit meiner Randbedingung erzeugt werden. Und genau das hat dein Beispiel nicht gebracht. Der Code, den ich damals bekommen habe, macht das alles schon perfekt und nun will ich zum Schluss auch noch das Diagramm abspeichern. Möglichst so, dass ich es später einfach per Standard-Viewer ansehen kann! Muß mich aber noch ausgiebig mit deinem Vorschlag auseinander setzen.
Danke und Gruß, Rainer
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi Wuf, habe noch eine Schwierigkeit mit deinem Script...mein PIL kennt kein ImageTk und ich bekomme immer eine Fehlermeldung:

File "/usr/lib/python3/dist-packages/PIL/ImageFont.py", line 238, in truetype
return FreeTypeFont(font, size, index, encoding)

File "/usr/lib/python3/dist-packages/PIL/ImageFont.py", line 127, in __init__
self.font = core.getfont(font, size, index, encoding)

OSError: cannot open resource

...kann aber nichts damit anfangen. Habe verschiedene Fonts versucht, aber nichts geht.

@ Sirius3, hallo, dass mit postscript habe ich versucht. Habe ein Beispiel gefunden.

Code: Alles auswählen

def safe(self)
    ps = self.canvas.postscript(colormode='color')
    img = Image.open(io.BytesIO(ps.encode('utf-8')))
    img.save(path+'test.jpg')
Es funktioniert, aber das Ergebnis ist ein grottenschlechtes Bild! Mach' ich da was falsch und lohnt es sich da weiter zu forschen??

Danke Rainer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi pyzip

a)
Kannst du einmal nachschauen ob das PIL ImageTk-Modul auf deinem Gerät überhaupt installiert ist? Es sollte sich unter folgendem Pfad befinden:
/usr/lib/python3/dist-packages/PIL/ImageTk.py
Wenn das PIL ImageTk-Modul nicht auffindbar ist musst du es nachinstallieren.

b)
Das Skript findet auf deinem Gerät eventuell meine verwendete Font-Resource ImageFont.truetype("arial.ttf", 18) nicht. Die verschiedenen Truetype Font-Resourcen befinden sich auf meinem Gerät unter:
/usr/share/fonts/truetype/

Ein Beispiel aus der PIL-Beschreibung "Python Imaging Library (PIL)" von John W. Shipman (NMT) um einen Truetype-Font in das Skript zu laden::

Code: Alles auswählen

fontPath = "/usr/share/fonts/dejavu-lgc/DejaVuLGCSansCondensed-Bold.ttf"
sans16 = ImageFont.truetype ( fontPath, 16
Gruss wuf :wink:
Take it easy Mates!
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi Wuf, danke. Natürlich ist das Modul PIL ImageTk-Modul nicht da...habe mich nur gewundert, dass "Image, ImageDraw und ImageFont" da sind! Werde gleich mal versuchen, das ImageTK-Modul zu installieren. Und das Beispiel aus der PIL-Beschreibung "Python Imaging Library (PIL)" werde ich auch probieren. Hoffentlich klappts.
Gruß Rainer
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi Wuf, habe mal nach ImageTk.py gesucht und in /home/ich/.local/lib/python3.5/site-packages/PIL/ImageTk.py gefunden. Ich vermute mal, dass ich das File nicht einfach kopieren und in /usr/lib/python3/dist-packages/PIL/ einfügen kann. Bin etwas ratlos...
Gruß Rainer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi pyzip

Ich nehme an das Verzeichnis PILexistierte bereits unter dem Pfad /usr/lib/python3/dist-packages/? und du hast es sicher nicht von Hand erstellt? Das Modul PIL Image.Tk würde ich nur mit einem Paketmanager erstellen. Bei mir habe ich es mit der Synaptic-Paketverwaltung mit dem Namen python3-pil.imagetk installiert. Du kannst es auch über das Terminal mit sudo apt-get install python3-pil.imagetk installieren. Aber auf keinen Fall das Modul von Hand herum kopieren! Für mich ist es eher ungewohnt, dass Python unter dem Heimverzeichnis /home/BENUTZER/.local/lib/python3.5/ installiert ist.

Gruss wuf :wink:
Take it easy Mates!
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hi Wuf, danke. Bevor ich irgendwas mache...ich habe Ubuntu-Studio-17... und ich habe natürlich "nichts gemacht"! Das Verzeichnis PIL existiert bereits unter dem Pfad /usr/lib/python3/dist-packages/...und wenn ich versuche ImageTk zu installieren, bekomme ich ominöse Fehlermeldungen, die ich so interpretiere: für mein Python 3. gibt es kein ImageTk. Ist leider alles sehr verwirrend! Sorry und danke für deine Geduld.
Gruß Rainer
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

OK. Funktioniert das folgende Skript bei dir ohne Fehlermeldungen und was zeigt es auf dem Monitor?

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
from PIL import Image, ImageDraw, ImageFont, ImageTk

APP_TITLE = "Save Canvas Image"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 500
APP_HEIGHT = 400


class MyGraph(tk.Frame):

    def __init__(self, app_win, **kwargs):
        self.app_win = app_win
        
        tk.Frame.__init__(self, app_win, **kwargs)
                
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = MyGraph(app_win, bg='yellow').pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()      
Gruss wuf :wink:
Take it easy Mates!
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Funktioniert das folgende zweite Skript auch ohne Fehlermeldungen? Was siehst du jetzt auf deinem Monitor?

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
from PIL import Image, ImageDraw, ImageFont, ImageTk

APP_TITLE = "Save Canvas Image"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 500
APP_HEIGHT = 400

WHITE = (255, 255, 255)

class MyGraph(tk.Frame):

    def __init__(self, app_win, **kwargs):
        self.app_win = app_win
        
        tk.Frame.__init__(self, app_win, **kwargs)

        self.graph_plane = Image.new("RGB", (200, 200), WHITE)
        self.drawing = ImageDraw.Draw(self.graph_plane)
        self.label_image = ImageTk.PhotoImage(self.graph_plane)

        self.draw_frame = tk.Label(self, image=self.label_image)
        self.draw_frame.pack(expand=True)
                
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = MyGraph(app_win, bg='steelblue').pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()      
Grss wuf :wink:
Take it easy Mates!
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@wuf: Pakete, die man als lokaler Nutzer installiert, werden unter ~/.local/lib/python... abgelegt. Das ist also ganz normal.

@pyzip: Du hast irgendwann ImageTk lokal installiert. DA Du keine Fehlermeldungen gepostet hast, kann man nicht sagen, warum das bei Dir nicht funktioniert. Erster Schritt wäre immer, bei Python-Modulen mit vielen Abhängigkeiten, diese über den Paketmanager Deiner Linuxdistribution zu installieren.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Sirius3

Danke für deine Unterstützen.
@wuf: Pakete, die man als lokaler Nutzer installiert, werden unter ~/.local/lib/python... abgelegt. Das ist also ganz normal.
Wusste ich noch nicht. Python habe ich bis jetzt noch nie lokal installiert da ich der einzige Benutzer des Systems bin. Bei einer Neuinstallation von Ubuntu wird Python ohnehin automatisch Systemweit angelegt. Zusätzliche Pakete installierte ich dann meistens über einer Paketverwaltung, welche immer Legitimation als Administrator verlangte. Spezielle Pakete die nicht mit Hilfe einer Paketverwaltung installierbar waren habe ich dann aus einem Terminal mit sudo pythonx setup.py PAKET nachinstalliert. Ich vermute, dass bei unserem Kollegen pyzip etwas mit den Umgebungsvariablen nicht stimmt.

Gruss wuf :wink:
Take it easy Mates!
pyzip
User
Beiträge: 89
Registriert: Freitag 16. Juni 2017, 19:36

Hallo, danke. @Wuf: ich habe deinen Code in Spider gestartet und bekomme natürlich diese Fehlermeldung:

runfile('/home/zip/py/nn/diagramm_pil_2.py', wdir='/home/zip/py/nn')
Traceback (most recent call last):

File "<ipython-input-1-4c6bb4b0f268>", line 1, in <module>
runfile('/home/zip/py/nn/diagramm_pil_2.py', wdir='/home/zip/py/nn')

File "/home/zip/.local/lib/python3.6/site-packages/spyder/utils/site/sitecustomize.py", line 705, in runfile
execfile(filename, namespace)

File "/home/zip/.local/lib/python3.6/site-packages/spyder/utils/site/sitecustomize.py", line 102, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)

File "/home/zip/py/nn/diagramm_pil_2.py", line 9, in <module>
from PIL import Image, ImageDraw, ImageFont, ImageTk

ImportError: cannot import name 'ImageTk'

Und @Sirius3: Ich habe immer einen User unter Ubuntu laufen und ich habe bisher immer mit pip bzw. mit pip3 installiert. Das sollte doch keine Probleme bereiten oder bringt sudo etwa die Vermischung von "Systemweit" und User? Bin aber echt überfordert!
Gruß Rainer
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@pyzip: Du hast ja auch ImageTk für Python 3.5 installiert und startest das Programm mit Python 3.6.
Antworten