I would like to save my drawing on a tkinter canvas as an image so I can open it for later use. I currently use this save system from this post however this is not a good way for me. First I would need to add an offset and second if i set the application so only some part of the canvas is actually visible, the part where the canvas is not visible appears black when saving the image.
only part of the canvas is actually visible. If I open the saved image this is what it looks like only what was visible is actually there(the entire image was yellow before saving it).
The code of saving the image.
def save(widget(canvas), filelocation):
x=root.winfo_rootx()+widget.winfo_x() + 74
y=root.winfo_rooty()+widget.winfo_y() + 109
x1=x+widget.winfo_width()
y1=y+widget.winfo_height()
ImageGrab.grab().crop((x,y,x1,y1)).save(filelocation)
Idea
After reading from this post it explains i could recreate all the stuff i drew on the canvas. So my idea is to put all the stuff i drew on the canvas such as lines i created on an invisible layer and paste it on the image. However i dont know if this is possible(may be possible with PIL, numpy or cv2)
Code(Is minimal reproducable)
import tkinter as tk
from tkinter import colorchooser, Canvas, N
from tkinter.ttk import *
from PIL import Image, ImageTk, ImageGrab
import keyboard
def save(widget, filelocation):
x=root.winfo_rootx()+widget.winfo_x()
y=root.winfo_rooty()+widget.winfo_y()
x1=x+widget.winfo_width()
y1=y+widget.winfo_height()
ImageGrab.grab().crop((x,y,x1,y1)).save(filelocation)
def type_of(color):
type_pen = 'marker'
if type_pen == 'marker':
pencil_motion_marker(color = color)
#pixel pen
def pencil_motion_marker(color):
stage.bind('<Button-1>', get_pos_marker)
stage.bind('<B1-Motion>', lambda event, color = color: pencil_draw_marker(event, color))
def get_pos_marker(event):
global lastx, lasty
lastx, lasty = event.x, event.y
def pencil_draw_marker(event, color):
stage.create_line((lastx, lasty, event.x, event.y), width = width.get(), fill = color, capstyle = 'round')
get_pos_marker(event)
def choose_pen_color():
pencilcolor = colorchooser.askcolor(title = 'Pencil Color')
type_of(pencilcolor[1])
##
def pencil_click():
global width, opacity
Whitepencolb = Button(optionsframe, text = 'Whitepencolimg', style = 'COLBG.TButton', command = lambda m = 'White': type_of(m))
Whitepencolb.grid(row = 0, column = 0, padx = 10, pady = 1)
Redpencolb = Button(optionsframe, text = 'Redpencolimg', style = 'COLBG.TButton', command = lambda m = 'Red': type_of(m))
Redpencolb.grid(row = 1, column = 0, padx = 10, pady = 1)
Magentapencolb = Button(optionsframe, text = 'Magentapencolimg', style = 'COLBG.TButton', command = lambda m = 'Magenta': type_of(m))
Magentapencolb.grid(row = 0, column = 1, padx = 10, pady = 1)
Limegreenpencolb = Button(optionsframe, text = 'Limegreenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Lime': type_of(m))
Limegreenpencolb.grid(row = 1, column = 1, padx = 10, pady = 1)
Greenpencolb = Button(optionsframe, text = 'Greenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Green': type_of(m))
Greenpencolb.grid(row = 0, column = 2, padx = 10, pady = 1)
Bluepencolb = Button(optionsframe, text = 'Bluepencolimg', style = 'COLBG.TButton', command = lambda m = 'Blue': type_of(m))
Bluepencolb.grid(row = 1, column = 2, padx = 10, pady = 1)
Cyanpencolb = Button(optionsframe, text = 'Cyanpencolimg', style = 'COLBG.TButton', command = lambda m = 'Cyan': type_of(m))
Cyanpencolb.grid(row = 0, column = 3, padx = 10, pady = 1)
Yellowpencolb = Button(optionsframe, text = 'Yellowpencolimg', style = 'COLBG.TButton', command = lambda m = 'Yellow': type_of(m))
Yellowpencolb.grid(row = 1, column = 3, padx = 10, pady = 1)
Orangepencolb = Button(optionsframe, text = 'Orangepencolimg', style = 'COLBG.TButton', command = lambda m = 'Orange': type_of(m))
Orangepencolb.grid(row = 0, column = 4, padx = 10, pady = 1)
Graypencolb = Button(optionsframe, text = 'Graypencolimg', style = 'COLBG.TButton', command = lambda m = 'Gray': type_of(m))
Graypencolb.grid(row = 1, column = 4, padx = 10, pady = 1)
Blackpencolb = Button(optionsframe, text = 'Blackpencolimg', style = 'COLBG.TButton', command = lambda m = 'Black': type_of(m))
Blackpencolb.grid(row = 0, column = 5, padx = 10, pady = 1)
Createnewpencolb = Button(optionsframe, text = 'Createnewpencolimg', style = 'COLBG.TButton', command = choose_pen_color)
Createnewpencolb.grid(row = 1, column = 5, padx = 10, pady = 1)
widthlabel = Label(optionsframe, text = 'Width: ', style = 'LABELBG.TLabel')
width = Scale(optionsframe, from_ = 1, to = 20, style = 'SCALEBG.Horizontal.TScale')
widthlabel.grid(row = 0, column = 6)
width.grid(row = 0, column = 7)
width.set(20)
opacitylabel = Label(optionsframe, text = 'Opacity: ', style = 'LABELBG.TLabel')
opacity = Scale(optionsframe, from_ = 0, to = 1.0, style = 'SCALEBG.Horizontal.TScale')
opacitylabel.grid(row = 1, column = 6)
opacity.grid(row = 1, column = 7)
opacity.set(1.0)
def setup(filelocation):
global stage, img_id, optionsframe, draw
for widgets in root.winfo_children():
widgets.destroy()
root.config(bg = '#454545')
iconsframewidth = int(screen_width / 20)
frames = Style()
frames.configure('FRAMES.TFrame', background = '#2a2a2a')
sep = Style()
sep.configure('SEP.TFrame', background = '#1a1a1a')
style = Style()
style.configure('STAGE.TFrame', background = '#454545')
icon = Style()
icon.configure('ICON.TButton', background = '#2a2a2a', foreground = '#2a2a2a')
iconsframe = Frame(root, width = iconsframewidth, style = 'FRAMES.TFrame')
iconsframe.pack(side = 'left', expand = False, fill = 'y')
iconsframe.pack_propagate(0)
sep1frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep1frame.pack(side = 'left', expand = False, fill = 'y')
optionsframe = Frame(root, style = 'FRAMES.TFrame', height = 100)
optionsframe.pack(side = 'top', expand = False, fill = 'x')
optionsframe.pack_propagate(0)
sep2frame = Frame(root, style = 'SEP.TFrame', height = 5)
sep2frame.pack(side = 'top', expand = False, fill = 'x')
propertyframe = Frame(root, style = 'FRAMES.TFrame', width = 150)
propertyframe.pack(side = 'right', expand = False, fill = 'y')
propertyframe.pack_propagate(0)
sep3frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep3frame.pack(side = 'right', expand = False, fill = 'y')
stageframe = Frame(root, style = 'STAGE.TFrame')
stageframe.pack(side = 'top', expand = True, fill = 'both')
stageframe.pack_propagate(0)
image = Image.open(filelocation)
width, height = image.size
stage = Canvas(stageframe, width = width, height = height)
stage.pack(side="top", anchor = 'c', expand=True)
root.update()
keyboard.add_hotkey("ctrl+s", lambda widget = stage, filelocation = filelocation: save(widget, filelocation))
pencilbutton = Button(iconsframe, text = 'pencilimg', command = pencil_click, style = 'ICON.TButton')
pencilbutton.pack(anchor = N, pady = 10)
imgtk = ImageTk.PhotoImage(Image.open(filelocation))
img_id = stage.create_image(stage.winfo_width() / 2, stage.winfo_height() / 2, image = imgtk)
stage.image = imgtk
root = tk.Tk()
root.title('App')
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
w = 1150
h = 600
x = (screen_width / 2) - (w / 2)
y = (screen_height / 2) - (h / 2)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root.minsize(1150, 600)
setup('Test.png')
root.mainloop()
Image
Small Problem
Replying to #Claudio : I am using the screenshot technique for saving Canvas as an image to a file right now. I noticed that the saved canvas image looks like this at the corner and after saving and reopening the image it looks like this ( the border of the canvas increases the size of the canvas image ).
Update 2. June 2022: Small Problem Solved by the updated code provided in the accepted answer.
How to save a tkinter Canvas graphics as an image?
It seems to be a fact that tkinter doesn't provide a direct method
allowing to get an image of Canvas graphics for saving it to an image
file. There are two ways around this problem requiring solely an import
of the Python PIL module (Pillow).
One of this ways is to perform a screenshot of painting on the
Canvas area which can be done using PIL.ImageGrab.grab() or any other
of the various methods for performing (cropped) screenshots and saving
them to an image file
( see e.g. Fast screenshot of a small part of the screen in Python
for a Python screenshot module fast enough to allow to make a video of
the progressing painting on the Canvas ).
Another way is to paint on a Python PIL image updating the tkinter Canvas
with the modified PIL image saving it then to a file using the .save()
method available for saving PIL image objects.
The code provided in the question works generally as expected if save()
uses both the Frame (stageframe) and the Canvas (stage) widgets
required for getting the right x,y values for cropping of the screenshot
in case the Canvas is placed within a Frame AND if the bounding box for cropping
the screenshot takes into account that tkinter Canvas widget size
includes a Canvas border and a Canvas highlight-border.
The code below is the in the question provided code with some added
comments and appropriate modifications. It doesn't require the keyboard
module and saves the by painting modified Canvas as image file by
clicking on the most-left upper pencilbutton handled by the
pencil_click() function.
It provides both methods for saving the graphics of the tkinter Canvas
to an image file. Select one of them by assigning appropriate value to
the global method variable ( method = 'screenshot' or
method = 'imagepaint' ):
# https://stackoverflow.com/questions/72459847/how-to-save-a-tkinter-canvas-as-an-image
from tkinter import Tk, colorchooser, Canvas, N, PhotoImage
from tkinter.ttk import Style, Frame, Button, Label, Scale
from PIL import Image, ImageTk # required to load images in tkinter
# method = 'screenshot' or 'imagepaint'
method = 'screenshot'
borderthickness_bd = 2
highlightthickness = 1
if method == 'imagepaint':
from PIL import ImageDraw # required to draw on the image
if method == 'screenshot':
from PIL import ImageGrab # required for the screenshot
filelocation = 'Test.png'
savelocation = 'Test_.png'
def save(stageframe, stage, savelocation):
if method == 'imagepaint':
global image
image.save(savelocation)
if method == 'screenshot':
global borderthickness_bd, highlightthickness
brdt = borderthickness_bd + highlightthickness
# +1 and -2 because of thicknesses of Canvas borders (bd-border and highlight-border):
x=root.winfo_rootx()+stageframe.winfo_x()+stage.winfo_x() +1*brdt
y=root.winfo_rooty()+stageframe.winfo_y()+stage.winfo_y() +1*brdt
x1=x+stage.winfo_width() -2*brdt
y1=y+stage.winfo_height()-2*brdt
ImageGrab.grab().crop((x,y,x1,y1)).save(savelocation)
def type_of(color):
type_pen = 'marker'
if type_pen == 'marker':
pencil_motion_marker(color = color)
#pixel pen
def pencil_motion_marker(color):
stage.bind('<Button-1>' , get_pos_marker)
stage.bind('<B1-Motion>', lambda event, color = color: pencil_draw_marker(event, color))
def get_pos_marker(event):
global lastx, lasty
lastx, lasty = event.x, event.y
def pencil_draw_marker(event, color):
global method, lastx, lasty, draw, image, img_id
# print( (lastx, lasty, event.x, event.y), color, int(width.get()) )
if method == 'screenshot':
stage.create_line((lastx, lasty, event.x, event.y), width = width.get(), fill = color, capstyle = 'round')
get_pos_marker(event)
if method == 'imagepaint':
w12 = int(width.get()/2)
draw.ellipse( (event.x-w12, event.y-w12, event.x+w12, event.y+w12), fill=color )
imgtk = ImageTk.PhotoImage(image)
stage.itemconfig(img_id, image=imgtk)
stage.image = imgtk
def choose_pen_color():
pencilcolor = colorchooser.askcolor(title = 'Pencil Color')
type_of(pencilcolor[1])
##
def pencil_click():
global width, opacity, stageframe, stage, savelocation
# imgToSave = stage.image # gives a PhotoImage object
# imgToSave._PhotoImage__photo.write("Test.gif", format='gif') # which can be saved, but ...
# ^--- ... with no painting done on Canvas - only the image.
save(stageframe, stage, savelocation)
Whitepencolb = Button(optionsframe, text = 'Whitepencolimg', style = 'COLBG.TButton', command = lambda m = 'White': type_of(m))
Whitepencolb.grid(row = 0, column = 0, padx = 10, pady = 1)
Redpencolb = Button(optionsframe, text = 'Redpencolimg', style = 'COLBG.TButton', command = lambda m = 'Red': type_of(m))
Redpencolb.grid(row = 1, column = 0, padx = 10, pady = 1)
Magentapencolb = Button(optionsframe, text = 'Magentapencolimg', style = 'COLBG.TButton', command = lambda m = 'Magenta': type_of(m))
Magentapencolb.grid(row = 0, column = 1, padx = 10, pady = 1)
Limegreenpencolb = Button(optionsframe, text = 'Limegreenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Lime': type_of(m))
Limegreenpencolb.grid(row = 1, column = 1, padx = 10, pady = 1)
Greenpencolb = Button(optionsframe, text = 'Greenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Green': type_of(m))
Greenpencolb.grid(row = 0, column = 2, padx = 10, pady = 1)
Bluepencolb = Button(optionsframe, text = 'Bluepencolimg', style = 'COLBG.TButton', command = lambda m = 'Blue': type_of(m))
Bluepencolb.grid(row = 1, column = 2, padx = 10, pady = 1)
Cyanpencolb = Button(optionsframe, text = 'Cyanpencolimg', style = 'COLBG.TButton', command = lambda m = 'Cyan': type_of(m))
Cyanpencolb.grid(row = 0, column = 3, padx = 10, pady = 1)
Yellowpencolb = Button(optionsframe, text = 'Yellowpencolimg', style = 'COLBG.TButton', command = lambda m = 'Yellow': type_of(m))
Yellowpencolb.grid(row = 1, column = 3, padx = 10, pady = 1)
Orangepencolb = Button(optionsframe, text = 'Orangepencolimg', style = 'COLBG.TButton', command = lambda m = 'Orange': type_of(m))
Orangepencolb.grid(row = 0, column = 4, padx = 10, pady = 1)
Graypencolb = Button(optionsframe, text = 'Graypencolimg', style = 'COLBG.TButton', command = lambda m = 'Gray': type_of(m))
Graypencolb.grid(row = 1, column = 4, padx = 10, pady = 1)
Blackpencolb = Button(optionsframe, text = 'Blackpencolimg', style = 'COLBG.TButton', command = lambda m = 'Black': type_of(m))
Blackpencolb.grid(row = 0, column = 5, padx = 10, pady = 1)
Createnewpencolb = Button(optionsframe, text = 'Createnewpencolimg', style = 'COLBG.TButton', command = choose_pen_color)
Createnewpencolb.grid(row = 1, column = 5, padx = 10, pady = 1)
widthlabel = Label(optionsframe, text = 'Width: ', style = 'LABELBG.TLabel')
width = Scale(optionsframe, from_ = 1, to = 100, style = 'SCALEBG.Horizontal.TScale')
widthlabel.grid(row = 0, column = 6)
width.grid(row = 0, column = 7)
width.set(20)
opacitylabel = Label(optionsframe, text = 'Opacity: ', style = 'LABELBG.TLabel')
opacity = Scale(optionsframe, from_ = 0, to = 1.0, style = 'SCALEBG.Horizontal.TScale')
opacitylabel.grid(row = 1, column = 6)
opacity.grid(row = 1, column = 7)
opacity.set(1.0)
def setup(filelocation):
global stage, stageframe, img_id, optionsframe, draw, image, img_id, method
global borderthickness_bd, highlightthickness
for widgets in root.winfo_children():
widgets.destroy()
root.config(bg = '#454545')
iconsframewidth = int(screen_width / 20)
frames = Style()
frames.configure('FRAMES.TFrame', background = '#2a2a2a')
sep = Style()
sep.configure('SEP.TFrame', background = '#1a1a1a')
style = Style()
style.configure('STAGE.TFrame', background = '#454545')
icon = Style()
icon.configure('ICON.TButton', background = '#2a2a2a', foreground = '#2a2a2a')
iconsframe = Frame(root, width = iconsframewidth, style = 'FRAMES.TFrame')
iconsframe.pack(side = 'left', expand = False, fill = 'y')
iconsframe.pack_propagate(0)
sep1frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep1frame.pack(side = 'left', expand = False, fill = 'y')
optionsframe = Frame(root, style = 'FRAMES.TFrame', height = 100)
optionsframe.pack(side = 'top', expand = False, fill = 'x')
optionsframe.pack_propagate(0)
sep2frame = Frame(root, style = 'SEP.TFrame', height = 5)
sep2frame.pack(side = 'top', expand = False, fill = 'x')
propertyframe = Frame(root, style = 'FRAMES.TFrame', width = 150)
propertyframe.pack(side = 'right', expand = False, fill = 'y')
propertyframe.pack_propagate(0)
sep3frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep3frame.pack(side = 'right', expand = False, fill = 'y')
stageframe = Frame(root, style = 'STAGE.TFrame')
stageframe.pack(side = 'top', expand = True, fill = 'both')
stageframe.pack_propagate(0)
image = Image.open(filelocation)
width, height = image.size
if method == 'imagepaint':
draw = ImageDraw.Draw(image)
imgtk = ImageTk.PhotoImage(image)
# width, height = imgtk._PhotoImage__size
# imgtk = PhotoImage(filelocation)
# ^--- no width, hight information ???
stage = Canvas(stageframe, width = width, height = height, bd=borderthickness_bd, highlightthickness=highlightthickness) # default: bd=2, highlightthickness=1
stage.pack(side="top", anchor = 'c', expand=True)
root.update()
# keyboard.add_hotkey("ctrl+s", lambda widget = stageframe, filelocation = filelocation: save(widget, filelocation))
pencilbutton = Button(iconsframe, text = 'pencilimg', command = pencil_click, style = 'ICON.TButton')
pencilbutton.pack(anchor = N, pady = 10)
img_id = stage.create_image(stage.winfo_width() / 2, stage.winfo_height() / 2, image = imgtk)
stage.image = imgtk
root = Tk()
root.title('App')
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
w = 1150
h = 600
x = (screen_width / 2) - (w / 2)
y = (screen_height / 2) - (h / 2)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root.minsize(1150, 600)
setup(filelocation)
root.mainloop()
Cropping a screenshot as a way of saving the graphics of tkinter Canvas
is to be preferred over painting on a PIL image updating the tkinter
Canvas because the latter has the side effect of slowing graphics
down so painting smoothness suffer.
To see how to change the look of a button in tkinter (to change
after the first click the pencilbutton to a savebutton), check out
Python tkinter: error _tkinter.TclError: bad window path name ".!button2" for how it can be done.
I am developing a python code where my computer reads data being transmitted using serial communication and my program presents this data in graphical format. New data is received every 0.1 seconds after which the graph is updated.
The code is:
import matplotlib
matplotlib.use("TkAgg")
from matplotlib import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from random import *
from Serial import *
import Tkinter as tk
import ttk
import xlwt
import serial
COMPORT = "COM7"
BUAD = 115200
def init():
connected = False
global ser
global COMPORT, BUAD
ser = serial.Serial(COMPORT, BUAD)
while not connected:
serin = ser.read()
connected = True
def TransmitCommand(Command):
SCommand = Command + 'y'
ser.write(SCommand)
def TransmitData(Data):
SData = str(Data) + 'y'
ser.write(SData)
def ClosePort():
ser.close()
def RecieveData():
return ser.readline()
################################################################################
############################ Graph1 #######################################
################################################################################
F1 = Figure(figsize=(15,8), dpi=80)
rc("font", size = 10)
Figure
a1 = F1.add_subplot(111)
tList = []
xList = []
yList = []
zList = []
fList = []
xfList = []
yfList = []
zfList = []
fxList = []
fyList = []
fzList = []
t = 0.00
t1 = 0
x = 0
y = 0
z = 0
ex = 0
ey = 0
ez = 0
r1 = 0
r2 = 0
r3 = 0
l = 0
txp = 0
typ = 0
tzp = 0
global fx1, fy1, fz1
fx1 = 0.00000
fy1 = 0.00000
fz1 = 0.00000
PPS1 = 1
def Play1():
global PPS1
TransmitCommand('SendData')
#print 'Done'
PPS1 = 0
def Pause1():
global PPS1
TransmitCommand('StopData')
PPS1 = 1
def Stop1():
global PPS1
TransmitCommand('StopData')
PPS1 = 2
def Extract1():
pass
def Save1():
pass
def SaveGraph1():
pass
def animate1(i):
global l, ex, ey, ez, t, x, y, z, tList, xList, yList, zList, r1, r2, r3
global fx1, fy1, fz1, txp, typ, tzp, xt, yt, zt
if(PPS1 == 0):
tList.append(t)
xList.append(x)
yList.append(y)
zList.append(z)
t = int(RecieveData())
x = int(RecieveData())
if(l == 0):
x = (x*0.707)/300
else:
x = ex - (x*0.707)/300
if(x > 0):
if(xList[l-1] == 0)|(xList[l-1] < 0):
fx1 = (1.0/(t - txp))*1000
txp = t
r1 = 1
y = int(RecieveData())
if(l == 0):
y = (y*0.707)/300
else:
y = ey - (y*0.707)/300
if(y > 0):
if(yList[l-1] == 0)|(yList[l-1] < 0):
fy1 = (1.0/(t - typ))*1000
typ = t
r2 = 1
z = int(RecieveData())
if(l == 0):
z = (z*0.707)/300
else:
z = ez - (z*0.707)/300
if(z > 0):
if(zList[l-1] == 0)|(zList[l-1] < 0):
fz1 = (1.0/(t - tzp))*1000
tzp = t
r3 = 1
if(l == 0):
ex = x
ey = y
ez = z
l = l+1
if(PPS1 == 2):
tList = []
xList = []
yList = []
zList = []
t = 0
x = 0
y = 0
z = 0
a1.clear()
a1.plot(tList, xList, label = "ax")
a1.plot(tList, yList, 'r', label = "ay")
a1.plot(tList, zList, 'g', label = "az")
a1.set_ylim(-1,1)
a1.set_xlabel("Time (ms)")
a1.set_ylabel("Acceleration (g)")
a1.legend()
def GenerateGraph1(Master):
dataPlot = FigureCanvasTkAgg(F1, master=Master)
dataPlot.show()
dataPlot.get_tk_widget()
display1 = tk.Canvas(Master, width=100, height=400, bg ="white")
display1.pack(side = 'right')
button1 = tk.Button(display1, text='Play', command=Play1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 0, column = 0)
button2 = tk.Button(display1, text='Pause', command=Pause1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 1, column = 0)
button3 = tk.Button(display1, text='Stop', command=Stop1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 2, column = 0)
button4 = tk.Button(display1, text='Extract Data', command=Extract1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 3, column = 0)
button5 = tk.Button(display1, text='Save Data', command=Save1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 4, column = 0)
button5 = tk.Button(display1, text='Save Graph', command=SaveGraph1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 5, column = 0)
button5 = tk.Button(display1, text='Send Mail', command=Save1, font='Times 12', bd=5,
height = 2, width = 10, anchor = 'w').grid(row = 6, column = 0)
toolbar = NavigationToolbar2TkAgg(dataPlot, Master)
toolbar.update()
dataPlot._tkcanvas.pack()
def show_frame():
frame = GenerateGraph(Graph)
frame.tkraise()
Main = tk.Tk()
init()
n = ttk.Notebook(Main, width= 800, height = 400)
n.grid(row=6,column=0, columnspan=9)
n.columnconfigure(0, weight=1)
n.rowconfigure(6, weight=1)
f1 = ttk.Frame(n);
n.add(f1, text='Acceleration vs Time')
GenerateGraph1(f1)
ani1 = animation.FuncAnimation(F1, animate1, interval=100)
mainloop()
Arduino code is:
int toggle1 = 0;
boolean Graph = 0;
int x = 0;
int y = 0;
int z = 0;
int i = 0;
const int groundpin = 18; // analog input pin 4 -- ground
const int powerpin = 19; // analog input pin 5 -- voltage
const int xpin = A3; // x-axis of the accelerometer
const int ypin = A2; // y-axis
const int zpin = A1; // z-axis (only on 3-axis models)
#include <elapsedMillis.h>
elapsedMillis timeElapsed;
void Acceleration(){
Serial.print(timeElapsed);
Serial.print("\n");
Serial.print(analogRead(xpin));
Serial.print("\n");
Serial.print(analogRead(ypin));
Serial.print("\n");
Serial.print(analogRead(zpin));
Serial.print("\n");
}
void setup()
{
Serial.begin(115200);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(xpin, INPUT);
pinMode(ypin, INPUT);
pinMode(zpin, INPUT);
Serial.write('1');
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 25000; // compare match register 16MHz/64/10Hz
TCCR1B |= (1 << WGM12); // CTC mode
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS11);
pinMode(groundpin, OUTPUT);
pinMode(powerpin, OUTPUT);
digitalWrite(groundpin, LOW);
digitalWrite(powerpin, HIGH);
}
ISR(TIMER1_COMPA_vect){
if(Graph == 1){
Acceleration();
}}
void loop() {
String brightness, recieve, r1;
int b1, b2, ledPin;
while(Serial.available() == 0) {}
recieve = Serial.readStringUntil('y');
r1 = recieve + "t";
if(recieve == "SendData"){Graph = 1;}
if(recieve == "StopData"){Graph = 0;timeElapsed = 0;}
if(recieve == "motor1"){
ledPin = 8;
while(Serial.available() == 0){}
brightness = Serial.readStringUntil('y');
b1 = brightness.toInt();
analogWrite(ledPin, b1);}
if(recieve == "motor2"){
ledPin = 9;
while(Serial.available() == 0){}
brightness = Serial.readStringUntil('y');
b1 = brightness.toInt();
analogWrite(ledPin, b1);}
}
}
I am taking data from a vibration sensor. I used stopwatch to time the difference between the time that the vibrations actually started and the time at which the graph began to show the change in data. For the first data set the time difference was 5 seconds, for data set 2 the difference was 1 minute 10 seconds and for the third set it was 2 minutes. The effect became more pronounced as I added a second graph and the rest of my GUI. I need this software to be able to run for hours which means at one point my GUI might become unresponsive and I also need the data to be real-time. What could be the problem causing this and how do I solve it. There may be a better way to show real-time data in GUI as a graph but since I am new to python I dont know.
this simple animation example will plot a new red dot every second, the method should be more stable than what you're doing; I've had an app running over 4 months that plots once a minute like this:
import matplotlib.pyplot as plt
import random
import time
plt.ion()
x = 0
while x<100:
y = random.random()
plt.plot(x,y,marker='.',c='red')
x+=1
time.sleep(1)
plt.tight_layout()
plt.show()
plt.pause(0.1)
You said you need it to be "real time", but it may be that the execution time of your code is taking longer than the 0.1 seconds that you get new data. It might be best to update the graph every 10 data points; or as I'm doing... once a minute.
I am a fairly novice programmer and through coffee, google, and an immense loss of hair and fingernails have managed to write a very messy code. I am asking anyone to help me simplify the code if possible.
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
one = 0
why = 'Total Number: {}'
no = 0
clack = 0
click = 'Clicks: {}'
s = ttk.Style()
s.theme_use('clam')
s.configure('red.Vertical.TProgressbar', foreground = 'red', background = 'red')
s.configure('green.Vertical.TProgressbar', foreground = 'green', background = 'green')
s.configure('TButton', relief = 'groove')
def iround(x):
y = round(x) - .5
return int(y) + (y > 0)
class Application(ttk.Frame):
def __init__(self, master = None):
ttk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def Number(self):
global one
cost = 10*(self.w['to'])
if self.number['text'] == "Make Number go up":
self.number['text'] = one
if iround(self.w.get()) == 0:
one += 1
else:
one += iround(self.w.get())
self.number['text'] = one
if self.number['text'] >= cost:
self.buy['state'] = 'normal'
else:
self.buy['state'] = 'disabled'
self.number['text'] >= cost
self.progress['value'] = one
if self.number['text'] >= cost:
self.progress['style'] = 'red.Vertical.TProgressbar'
else:
self.progress['style'] = 'green.Vertical.TProgressbar'
def Buy(self):
global one
self.w['to'] += 1
cost = 10*(self.w['to'])
one = self.number['text'] = (one + 10 - cost)
self.buy['text'] = ('+1 to slider | Cost: {}'.format(cost))
if self.number['text'] < (cost):
self.buy['state'] = 'disabled'
self.progress['value'] = one
self.progress['maximum'] += 10
if self.number['text'] >= cost:
self.progress['style'] = 'red.Vertical.TProgressbar'
else:
self.progress['style'] = 'green.Vertical.TProgressbar'
def scaleValue(self, event):
self.v['text'] = 'Slider Bonus + ' + str(iround(self.w.get()))
def clicks(self, event):
global click
global clack
if self.Clicks['text'] == 'Clicks: 0':
clack += 1
self.Clicks['text'] = click.format(clack)
self.Clicks['text'] = click.format(clack)
clack += 1
def NumberVal(self, event):
global why
global no
if self.fun['text'] == "Total Number: 0":
self.fun['text'] = why.format(no)
if iround(self.w.get()) == 0:
no += 1
else:
no += iround(self.w.get())
self.fun['text'] = why.format(no)
def createWidgets(self):
self.number = Button(self, text = 'Make number go up', command = self.Number, width = 20, style = 'TButton')
self.number.grid(row = 1, column = 1)
self.number.bind('<ButtonRelease>', self.clicks, add = '+')
self.number.bind('<ButtonRelease>', self.NumberVal, add = '+')
self.buy = Button(self, text = '+1 to Slider | Cost: 10', command = self.Buy, width = 20, style = 'TButton')
self.buy.grid(row = 2, column = 1)
self.buy.config(state = 'disabled')
self.v = Label(self, text = 'Slider Bonus + 0', width = 20, anchor = 'center')
self.v.grid(row = 3, column = 3)
self.w = Scale(self, from_ = 0, to = 1, orient = 'horizontal')
self.w.grid(row = 3, column = 1)
self.w.bind('<Motion>', self.scaleValue)
self.progress = Progressbar(self, value = 0, orient = 'vertical', maximum = 10, mode = 'determinate')
self.progress.grid(row = 1, rowspan = 5, column = 2)
self.Clicks = Label(self, text = 'Clicks: 0', width = 20, anchor = 'center')
self.Clicks.grid(row = 1, column = 3)
self.fun = Label(self, text = 'Total Number: 0', width = 20, anchor = 'center')
self.fun.grid(row = 2, column = 3)
app = Application()
app.master.title('Number')
app.mainloop()
from tkinter import *
import tkinter.messagebox
from ProjectHeader import *
class Gui(object):
def __init__(self, parent):
self.gui = parent
self.gui.geometry("350x200")
self.gui.title("Converter")
self.checked1 = IntVar()
self.checked2 = IntVar()
self.c1 = Radiobutton(self.gui, text='(1)Centimeters', variable=self.checked1, value = 1)
self.c2 = Radiobutton(self.gui, text='(2)Meter', variable=self.checked1, value = 2)
self.c3 = Radiobutton(self.gui, text='(3)Millimeters', variable=self.checked1, value = 3)
self.c4 = Radiobutton(self.gui, text='(4)Kilometers', variable=self.checked1, value = 4)
self.c5 = Radiobutton(self.gui, text='(5)Centimeters', variable=self.checked2, value = 5)
self.c6 = Radiobutton(self.gui, text='(6)Meter', variable=self.checked2, value = 6)
self.c7 = Radiobutton(self.gui, text='(7)Millimeters', variable=self.checked2, value = 7)
self.c8 = Radiobutton(self.gui, text='(8)Kilometers', variable=self.checked2, value = 8)
self.b1 = Button(self.gui, text="Convert", command=self.callback)
self.l1 = Label(self.gui, text="Value")
self.l2 = Label(self.gui, text="Convert ->")
self.e1 = Entry(self.gui, bd = 5)
self.e1.insert(0, "0")
self.c1.pack(side = BOTTOM)
self.c2.pack(side = BOTTOM)
self.c3.pack(side = BOTTOM)
self.c4.pack(side = BOTTOM)
self.c5.pack(side = BOTTOM)
self.c6.pack(side = BOTTOM)
self.c7.pack(side = BOTTOM)
self.c8.pack(side = BOTTOM)
self.l1.pack(side = TOP)
self.l2.pack(side = TOP)
self.e1.pack(side = TOP)
self.b1.pack(side = TOP)
self.c1.place(x = 3, y = 90)
self.c2.place(x = 3, y = 110)
self.c3.place(x = 3, y = 130)
self.c4.place(x = 3, y = 150)
self.c5.place(x = 235, y = 90)
self.c6.place(x = 235, y = 110)
self.c7.place(x = 235, y = 130)
self.c8.place(x = 235, y = 150)
self.l2.place(x = 140, y = 110)
def callback(self):
if self.e1.get() == "0":
messagebox.showinfo("Error", "Please enter a value")
elif self.checked1.get(): #Centiemters
C = Centimeters(self.e1.get())
if self.checked2.get():
messagebox.showinfo("Error", "Converting the same unit!")
if self.checked2.get():
value = C.ToMeters()
messagebox.showinfo("Value", str(value) + ' cm')
if self.checked2.get():
value = C.ToMillimeters()
messagebox.showinfo("Value", str(value) + ' cm')
if self.checked2.get():
value = C.ToKilometers()
messagebox.showinfo("Value", str(value) + ' cm')
root = Tk()
my_window = Gui(root)
root.mainloop()
so now I changed the self.checked1.get() == 1 to self.checked1.get()
because you said by checking on the radiobutton, it would select the option
but now, when I the check the (1)Centimeters button, or either (5)Centimeters to (8)Kilometers button, the program would select all the options
for example, if I checked (1)Centimeters button and (8)Kilometers, the program would pop up not only converted value of Kilometers, but also the converted value of meters, millimeters.
so my question is how can I fix the program that when I checked the 1st box on the left side, and either one of the boxes on the right side, the program would give the correct selection?
Here is the interface of checkedbutton
http://postimg.org/image/6uqwkybw5/
If I understand what you want correctly, you should use the following code. Use self.checked1.get() to see what is the original unit and use self.checked2.get() to see to which unit it has to be converted.
if self.e1.get() == "0":
messagebox.showinfo("Error", "Please enter a value")
elif self.checked1.get() == 1: #Centimeters
C = Centimeters(self.e1.get())
if self.checked2.get() == 5:
messagebox.showinfo("Error", "Converting the same unit!")
if self.checked2.get() == 6:
value = C.ToMeters()
messagebox.showinfo("Value", str(value) + ' m')
if self.checked2.get() == 7:
value = C.ToMillimeters()
messagebox.showinfo("Value", str(value) + ' mm')
if self.checked2.get() == 8:
value = C.ToKilometers()
messagebox.showinfo("Value", str(value) + ' km')