So using tkinter I have made a simple program that changes the color of all shapes on the canvas to a random color every second ,what I am looking for is a way to register every screen change and write down the time between screen changes in a separate file.I also need to do it without the help of too many external libraries if possible.
My code so far:
#!/usr/bin/python -W ignore::DeprecationWarning
import Tkinter
import tkMessageBox
import time
import random
colors = ['DarkOrchid1','chocolate3','gold2','khaki2','chartreuse2','deep pink','white','grey','orange']
top = Tkinter.Tk()
global b
b=0
C = Tkinter.Canvas(top, bg="blue", height=600, width=800)
bcg1=C.create_rectangle(0,0,800,100,fill=random.choice(colors))
bcg2=C.create_rectangle(0,100,800,200,fill=random.choice(colors))
bcg3=C.create_rectangle(0,200,800,300,fill=random.choice(colors))
bcg4=C.create_rectangle(0,300,800,400,fill=random.choice(colors))
bcg5=C.create_rectangle(0,400,800,500,fill=random.choice(colors))
bcg6=C.create_rectangle(0,500,800,600,fill=random.choice(colors))
bcgs=[bcg1,bcg2,bcg3,bcg4,bcg5,bcg6]
coord = 10, 50, 240, 210
rect1=C.create_rectangle(0,0,100,100,fill="green")
rect2=C.create_rectangle(700,500,800,600,fill="green")
rect3=C.create_rectangle(0,500,100,600,fill="green")
rect4=C.create_rectangle(700,0,800,100,fill="green")
def color():
global b
global bcgs
global color
if b==0:
C.itemconfig(rect1,fill='green')
C.itemconfig(rect2,fill='green')
C.itemconfig(rect3,fill='green')
C.itemconfig(rect4,fill='green')
b=1
count=0
for i in range(len(bcgs)):
C.itemconfig(bcgs[i],fill=random.choice(colors))
elif b==1:
C.itemconfig(rect1,fill='red')
C.itemconfig(rect2,fill='red')
C.itemconfig(rect3,fill='red')
C.itemconfig(rect4,fill='red')
b=0
for i in range(len(bcgs)):
C.itemconfig(bcgs[i],fill=random.choice(colors))
top.after(2000, color)
C.pack()
color()
top.mainloop()
Unless you use a real time OS, you will never get perfect timing. You can bank on being a few milliseconds off the mark. To see how much, you can calculate the difference in time.time(). For the best accuracy, move the after call to the first thing in the function.
That plus some other improvements:
#!/usr/bin/python -W ignore::DeprecationWarning
import Tkinter
import time
import random
from itertools import cycle
colors = ['DarkOrchid1','chocolate3','gold2','khaki2','chartreuse2','deep pink','white','grey','orange']
rect_colors = cycle(['green', 'red'])
top = Tkinter.Tk()
C = Tkinter.Canvas(top, bg="blue", height=600, width=800)
bcg1=C.create_rectangle(0,0,800,100,fill=random.choice(colors))
bcg2=C.create_rectangle(0,100,800,200,fill=random.choice(colors))
bcg3=C.create_rectangle(0,200,800,300,fill=random.choice(colors))
bcg4=C.create_rectangle(0,300,800,400,fill=random.choice(colors))
bcg5=C.create_rectangle(0,400,800,500,fill=random.choice(colors))
bcg6=C.create_rectangle(0,500,800,600,fill=random.choice(colors))
bcgs=[bcg1,bcg2,bcg3,bcg4,bcg5,bcg6]
coord = 10, 50, 240, 210
rect1=C.create_rectangle(0,0,100,100,fill="green")
rect2=C.create_rectangle(700,500,800,600,fill="green")
rect3=C.create_rectangle(0,500,100,600,fill="green")
rect4=C.create_rectangle(700,0,800,100,fill="green")
rects = [rect1,rect2,rect3,rect4]
last_time = time.time()
def color():
top.after(2000, color)
global last_time
rect_color = next(rect_colors)
for item in rects:
C.itemconfig(item, fill=rect_color)
for item in bcgs:
C.itemconfig(item, fill=random.choice(colors))
print("{} seconds since the last run".format(time.time() - last_time))
last_time = time.time()
C.pack()
color()
top.mainloop()
Related
from Tkinter import *
import Tkinter as tk
import ttk
import tkFileDialog
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class Look():
def __init__(self, master):
self.master = master
self.master.title("Demo")
self.master.configure(background = "grey91") #the color will be changed later
self.master.minsize(500, 300) # width + height
self.master.resizable(False, False)
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
ttk.Button(self.top_frame, text = "Load file", command = self.load_file,
style = "TButton").pack()
ttk.Button(self.top_frame, text = "Reset", command = self.clear_file,
style = "TButton").pack()
ttk.Button(self.top_frame, text = "Plot", command = self.plot_file,
style = "TButton").pack()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig = plt.figure(figsize=(12, 5), dpi=100) ##create a figure; modify the size here
self.fig.add_subplot()
plt.title("blah")
self.canvas = FigureCanvasTkAgg(self.fig, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_file(self):
self.file = tkFileDialog.askopenfilename(defaultextension = ".txt", filetypes = [("Text Documents", "*.txt")])
def clear_file(self):
self.fig.clf()
self.fig.add_subplot()
plt.xticks()
plt.yticks()
self.canvas.draw()
def plot_file(self):
self.r, self.g = np.loadtxt(self.file).transpose()
self.fig.clf()
plt.plot(self.r, self.g)
self.canvas.show()
def main():
root = Tk()
GUI = Look(root)
root.mainloop()
if __name__ == "__main__": main()
The code above creates a program has three buttons. The Load file buttom loads a txt file, Reset button is supposed to clear the plot and delete the file just loaded into the memory. Plot button is to plot the figure onto canvas below.
I have a question about how to write function associated with Reset function, i.e. clear_file function. Currently what it does is just clear the plot from canvas. But it seems the file that has been loaded to plot is stored in memory, since click again Plot, it will display the plot. My goal is to use Reset button to bring it to a fresh start--nothing stored in memory. I know loading a new file will overwrite the previous file. But when there are multiple buttons for loading different files, the situations could become complicated. Therefore I hope Reset can do the job.
If you want to try this little program, you may create a simple txt with two column data to be loaded into the program.
Thanks.
I would make 2 changes. Move the code that sets up your plot into its own method and reset the frame each time the method is called. This can be done with destroying the frame and remaking it. The 2nd change I would change the command on your reset to reference this new method.
Taking your code this is what I would change to be able to reset the plot.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class Look():
def __init__(self, master):
self.master = master
self.master.title("Demo")
self.master.configure(background = "grey91") #the color will be changed later
self.master.minsize(500, 300) # width + height
self.master.resizable(False, False)
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
ttk.Button(self.top_frame, text = "Load file", command = self.load_file,style = "TButton").pack()
ttk.Button(self.top_frame, text = "Reset", command = self.new_plot,style = "TButton").pack()
ttk.Button(self.top_frame, text = "Plot", command = self.plot_file,style = "TButton").pack()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.new_plot()
# this function will reset your plot with a fresh one.
def new_plot(self):
self.bottom_frame.destroy()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig = plt.figure(figsize=(12, 5), dpi=100) ##create a figure; modify the size here
self.fig.add_subplot()
plt.title("blah")
self.canvas = FigureCanvasTkAgg(self.fig, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_file(self):
self.file = filedialog.askopenfilename(defaultextension = ".txt", filetypes = [("Text Documents", "*.txt")])
def clear_file(self):
self.fig.clf()
self.fig.add_subplot()
plt.xticks()
plt.yticks()
self.canvas.draw()
def plot_file(self):
self.r, self.g = np.loadtxt(self.file).transpose()
self.fig.clf()
plt.plot(self.r, self.g)
self.canvas.show()
if __name__ == "__main__":
root = tk.Tk()
GUI = Look(root)
root.mainloop()
I'm working on a display project that will have two screens each showing gauges and I can't get the kivy garden gauge code to work with a screenmanager.
It works fine as a standalone app, but I can't find any instructions to help me incorporate it into a screen. The kivy calculator example works fine inside a screenmanager screen, but that's only .kv code, my GaugeApp class won't run any way I've tried when placed in a screenmanager screen.
Here's the working standalone code:
from kivy.garden.gauge import Gauge
from kivy.app import App
dirflag = 1
value = 50
from kivy.uix.slider import Slider
from kivy.properties import NumericProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
class GaugeApp(App):
increasing = NumericProperty(1)
begin = NumericProperty(50)
begin2 = NumericProperty(25)
begin3 = NumericProperty(75)
step = NumericProperty(1)
def build(self):
box = FloatLayout(orientation='horizontal', padding=5)
self.gauge = Gauge(value=50, size_gauge=156, size_text=25, pos_hint={'x': .5, 'y': .2})
self.gauge2 = Gauge(value=10, size_gauge=226, size_text=25, pos_hint={'x': .22, 'y': .3})
self.gauge3 = Gauge(value=10, size_gauge=156, size_text=25, pos_hint={'x': .02, 'y': .2})
box.add_widget(self.gauge)
box.add_widget(self.gauge2)
box.add_widget(self.gauge3)
Clock.schedule_interval(lambda *t: self.gauge_increment(), 0.1)
return box
def gauge_increment(self):
begin = self.begin
begin += self.step * self.increasing
if begin > 0 and begin < 100:
self.gauge.value = begin
else:
self.increasing *= -1
self.begin = begin
begin2 = self.begin2
begin2 += self.step * self.increasing
if begin2 > 0 and begin2 < 100:
self.gauge2.value = begin2
else:
self.increasing *= -1
self.begin2 = begin2
# Main program
GaugeApp().run()
Any help to incorporate this into a screen layout will be greatly appreciated.
it's long ago but if You still have the problem:
https://groups.google.com/d/msg/kivy-users/I0_xBF3DlAM/9M2ppsWnngQJ
I copied the "garden.gauge" folder in c:\.kivy\garden\ giving it a new name. Then making the changes to init.py as described in the link. Now it works.
I wanted to use the matplotlib slider as seen in an example from a previous question (below) inside a GUI window (such as TkInter etc). But, in a non-global context, the variables for the plot ("spos, fig, ax") are not defined. My understanding is that because update is used as a callback function, one can't or shouldn't pass arguments.
If so, how can a plot be updated without global variables? or
How can I obtain the slider position outside the callback function?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
t = np.arange(0.0, 100.0, 0.1)
s = np.sin(2*np.pi*t)
l, = plt.plot(t,s)
plt.axis([0, 10, -1, 1])
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], axisbg=axcolor)
spos = Slider(axpos, 'Pos', 0.1, 90.0)
def update(val):
pos = spos.val
ax.axis([pos,pos+10,-1,1])
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
Related:
1) Another related question seems to cover this topic but does not seem to address how the position of the slider is obtained.
2) A similar question was asked and solved with Slider.set_val(). It seems in my case I would need Slider.get_val() instead.
It is possible to pass more arguments to the callback function, for example with functools.partial
def update(data, val):
pos = spos.val
ax.axis([pos,pos+10,-1,1])
fig.canvas.draw_idle()
data['position'] = pos
import functools
data = dict()
spos.on_changed(functools.partial(update, data))
plt.show()
try:
print data['position']
except KeyError:
pass
A class with __call__ method could also be used as a callback.
I have plotted a contour plot as background which represent the altitude of the area.
And 100 scatter points were set represent the real pollutant emission source. Is there a method to obtain the altitude of each point?
This is my code:
%matplotlib inline
fig=plt.figure(figsize=(16,16))
ax=plt.subplot()
xi,yi = np.linspace(195.2260,391.2260,50),
np.linspace(4108.9341,4304.9341,50)
height=np.array(list(csv.reader(open("/Users/HYF/Documents/SJZ_vis/Concentration/work/terr_grd.csv","rb"),delimiter=','))).astype('float')
cmap = cm.get_cmap(name='terrain', lut=None)
terrf = plt.contourf(xi, yi, height,100, cmap=cmap)
terr = plt.contour(xi, yi, height, 100,
colors='k',alpha=0.5
)
plt.clabel(terr, fontsize=7, inline=20)
ax.autoscale(False)
point= plt.scatter(dat_so2["xp"], dat_so2["yp"], marker='o',c="grey",s=40)
ax.autoscale(False)
for i in range(0,len(dat_so2["xp"]),1):
plt.text(dat_so2["xp"][i], dat_so2["yp"][i],
str(i),color="White",fontsize=16)
ax.set_xlim(225,275)
ax.set_ylim(4200,4260)
plt.show()
You can do this with scipy.interpolate.interp2d
For example, you could add to your code:
from scipy import interpolate
hfunc = interpolate.interp2d(xi,yi,height)
pointheights = np.zeros(dat_so2["xp"].shape)
for i,(x,y) in enumerate(zip(dat_so2["xp"],dat_so2["yp"])):
pointheights[i]=hfunc(x,y)
Putting this together with the rest of your script, and some sample data, gives this (I've simplified a couple of things here, but you get the idea):
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
from scipy import interpolate
fig=plt.figure(figsize=(8,8))
ax=plt.subplot()
#xi,yi = np.linspace(195.2260,391.2260,50),np.linspace(4108.9341,4304.9341,50)
xi,yi = np.linspace(225,275,50),np.linspace(4200,4260,50)
# A made up function of height (in place of your data)
XI,YI = np.meshgrid(xi,yi)
height = (XI-230.)**2 + (YI-4220.)**2
#height=np.array(list(csv.reader(open("/Users/HYF/Documents/SJZ_vis/Concentration/work/terr_grd.csv","rb"),delimiter=','))).astype('float')
cmap = cm.get_cmap(name='terrain', lut=None)
terrf = plt.contourf(xi, yi, height,10, cmap=cmap)
terr = plt.contour(xi, yi, height, 10,
colors='k',alpha=0.5
)
plt.clabel(terr, fontsize=7, inline=20)
ax.autoscale(False)
# Some made up sample points
dat_so2 = np.array([(230,4210),(240,4220),(250,4230),(260,4240),(270,4250)],dtype=[("xp","f4"),("yp","f4")])
point= plt.scatter(dat_so2["xp"], dat_so2["yp"], marker='o',c="grey",s=40)
# The interpolation function
hfunc = interpolate.interp2d(xi,yi,height)
# Now, for each point, lets interpolate the height
pointheights = np.zeros(dat_so2["xp"].shape)
for i,(x,y) in enumerate(zip(dat_so2["xp"],dat_so2["yp"])):
pointheights[i]=hfunc(x,y)
print pointheights
ax.autoscale(False)
for i in range(0,len(dat_so2["xp"]),1):
plt.text(dat_so2["xp"][i], dat_so2["yp"][i],
str(i),color="White",fontsize=16)
# We can also add a height label to the plot
plt.text(dat_so2["xp"][i], dat_so2["yp"][i],
"{:4.1f}".format(pointheights[i]),color="black",fontsize=16,ha='right',va='top')
ax.set_xlim(225,275)
ax.set_ylim(4200,4260)
plt.show()
Here is my code (Python beginner, please bear any of the unprofessional code), and basically what I want is to let the two turtles move on the same circle together (as you may guess, my task is about simulating a spaceship chasing the ISS). In my code, first turtle will move around the circle, then the second turtle:
from turtle import *
rocket=Turtle()
ISS=Turtle()
counter=1
title("ISS")
screensize(750,750)
ISS.hideturtle()
rocket.hideturtle()
ISS.penup()
ISS.left(90)
ISS.fd(250)
ISS.left(90)
ISS.showturtle()
ISS.pendown()
rocket.penup()
rocket.fd(250)
rocket.left(90)
rocket.showturtle()
rocket.pendown()
while counter==1:
ISS.speed(1)
rocket.speed(2)
ISS.circle(250)
rocket.circle(250)
My teacher has told me that "threading" will work for this, but I don't quite understand that. It would be much appreciated if someone can help me out with this ;)
There is a turtle limitation that doesn't allow it to work multithreaded.
Although, you don't have to move the turtle around the whole circle, you can just move it part way. Also, I think you've misunderstood what speed does. It's just the speed that the turtle draws at.
from turtle import *
def move(thing, distance):
thing.circle(250, distance)
def main():
rocket = Turtle()
ISS = Turtle()
rocket.speed(10)
ISS.speed(10)
counter = 1
title("ISS")
screensize(750, 750)
ISS.hideturtle()
rocket.hideturtle()
ISS.penup()
ISS.left(90)
ISS.fd(250)
ISS.left(90)
ISS.showturtle()
ISS.pendown()
rocket.penup()
rocket.fd(250)
rocket.left(90)
rocket.showturtle()
rocket.pendown()
while counter == 1:
move(ISS, 3)
move(rocket, 4)
if __name__ == '__main__':
main()
I took out the repeated step of moving the thing, either the ISS or the rocket, and made that a function. I upped the speed of the drawing to 10, because I thought it just looked smoother. The ISS now moves only like 3/4 as far as the rocket on each step.
In an event-driven world like turtle, you shouldn't do this:
counter = 1
...
while counter==1:
This has the potential of locking out events. Also, there's no way to quit this program cleanly. Instead of moving different distances in a while loop, let's move a constant distance but with different delays on in an ontimer() event (should work with Python 2 or 3):
from turtle import Turtle, Screen
RADIUS = 250
ISS_DELAY = 100 # milliseconds
ROCKET_DELAY = 75 # milliseconds
CURSOR_SIZE = 20
def move_ISS():
ISS.circle(RADIUS, 1)
screen.ontimer(move_ISS, ISS_DELAY)
def move_rocket():
rocket.circle(RADIUS, 1)
# rocket slows to ISS' speed once docked
delay = ISS_DELAY if rocket.distance(ISS) <= CURSOR_SIZE else ROCKET_DELAY
screen.ontimer(move_rocket, delay)
screen = Screen()
screen.title("ISS")
screen.setup(750, 750)
ISS = Turtle("square", visible=False)
ISS.speed('fastest')
ISS.penup()
ISS.left(180)
ISS.sety(RADIUS)
ISS.showturtle()
ISS.pendown()
rocket = Turtle("triangle", visible=False)
rocket.speed('fastest')
rocket.penup()
rocket.left(90)
rocket.setx(RADIUS)
rocket.showturtle()
rocket.pendown()
move_ISS()
move_rocket()
screen.exitonclick()
You can click anywhere on the window at any time to exit the program cleanly with respect to the event handler. And I threw in some logic to make the space ship stick with the ISS once they dock rather than fly past.
This also can be done with threads, but all the graphical operations have to be channeled through the main thread to avoid Tkinter errors (a Python3-only solution):
from threading import Thread, active_count
from turtle import Turtle, Screen
from queue import Queue # use for thread-safe communications
from time import sleep
RADIUS = 250
ISS_DISTANCE = 3
ROCKET_DISTANCE = 4
CURSOR_SIZE = 20
QUEUE_SIZE = 1
def move_ISS(turtle):
while True:
actions.put((turtle.circle, RADIUS, ISS_DISTANCE))
sleep(0.1)
def move_rocket(turtle):
while True:
# rocket slows to ISS' speed once docked
distance = ISS_DISTANCE if rocket.distance(ISS) <= CURSOR_SIZE else ROCKET_DISTANCE
actions.put((turtle.circle, RADIUS, distance))
sleep(0.1)
def process_queue():
while not actions.empty():
action, *arguments = actions.get()
action(*arguments)
if active_count() > 1:
screen.ontimer(process_queue, 100)
actions = Queue(QUEUE_SIZE)
screen = Screen()
screen.title("ISS")
screen.setup(750, 750)
ISS = Turtle("square", visible=False)
ISS.speed('fastest')
ISS.penup()
ISS.left(180)
ISS.sety(RADIUS)
ISS.showturtle()
ISS.pendown()
ISS_thread = Thread(target=move_ISS, args=[ISS], daemon=True)
rocket = Turtle("triangle", visible=False)
rocket.speed('fastest')
rocket.penup()
rocket.left(90)
rocket.setx(RADIUS)
rocket.showturtle()
rocket.pendown()
rocket_thread = Thread(target=move_rocket, args=[rocket], daemon=True)
ISS_thread.start()
rocket_thread.start()
process_queue()
screen.exitonclick()