First of all, I wanted to put a camera switch option on a piece of code found here. First, I found the possible camera indexes and tried to change the camera sources with the push of a button. Even though the function worked, the camera source was not changed. I am looking for a solution for this.
In other words, how can I change the camera indexes through the function. The "returnCameraIndexes ()" function that I wrote before starting the 'main' section in this topic creates the total camera indexes. The "ChangeCam(" function is the function that performs the exchange between all the resources I have acquired.
import numpy as np
from multiprocessing import Process, Queue
from Queue import Empty
import cv2
from PIL import Image, ImageTk
import time
import Tkinter as tk
#tkinter GUI functions----------------------------------------------------------
def quit_(root, process):
process.terminate()
root.destroy()
def update_image(image_label, queue):
frame = queue.get()
im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
a = Image.fromarray(im)
b = ImageTk.PhotoImage(image=a)
image_label.configure(image=b)
image_label._image_cache = b # avoid garbage collection
root.update()
def update_all(root, image_label, queue):
update_image(image_label, queue)
root.after(0, func=lambda: update_all(root, image_label, queue))
#multiprocessing image processing functions-------------------------------------
def image_capture(queue,change_camera_flag):
vidFile = cv2.VideoCapture(change_camera_flag)
while True:
try:
flag, frame=vidFile.read()
if flag==0:
break
queue.put(frame)
cv2.waitKey(20)
except:
continue
#check all camera Sources
def returnCameraIndexes():
# checks the first 10 indexes.
index = 0
arr = []
i = 10
while i > 0:
cap = cv2.VideoCapture(index)
if cap.read()[0]:
arr.append(index)
cap.release()
index += 1
i -= 1
return arr
camera_list = returnCameraIndexes()
change_camera_flag = 0
# Switch camera sourcess
def ChangeCam():
global change_camera_flag,cap_1
if(len(camera_list)-1 == change_camera_flag):
change_camera_flag = 0
else:
change_camera_flag = change_camera_flag + 1
print(change_camera_flag)
if __name__ == '__main__':
queue = Queue()
print ('queue initialized...')
root = tk.Tk()
print ('GUI initialized...')
image_label = tk.Label(master=root)# label for the video frame
image_label.pack()
print ('GUI image label initialized...')
p = Process(target=image_capture, args=(queue,change_camera_flag))
p.start()
print ('image capture process has started...')
change_source_button = tk.Button(master=root, text='ChangeCamera',command=lambda: ChangeCam())
change_source_button.pack()
# quit button
quit_button = tk.Button(master=root, text='Quit',command=lambda: quit_(root,p))
quit_button.pack()
print ('quit button initialized...')
# setup the update callback
root.after(0, func=lambda: update_all(root, image_label, queue))
print ('root.after was called...')
root.mainloop()
print ('mainloop exit')
p.join()
print ('image capture process exit')
Related
I'm trying to play a GIF and let it loop for 4 secs while loading a progress bar. I've tried so many combinations of solutions to no avail. Below is the closest I got. The loadingAnimation() function runs the progress bar well but I can't get the gif part together(M_95()). Ideally would love for the GIF to play in the middle of the screen at the same time the progress bar loads, then close the GIF window when it's finished.
import threading
import tkinter
root = tkinter.Tk()
frames = [PhotoImage(file='./myScripts/M-95.gif',format = 'gif -index %i' %(i)) for i in range(10)]
def M_95() :
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
def loadingAnimation(process):
while process.is_alive() :
animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
for i in range(len(animation)):
sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
sys.stdout.flush()
time.sleep(0.4)
loading_process = threading.Thread(target = M_95)
loading_process.start()
loadingAnimation(loading_process)
loading_process.join()
First using while loop and time.sleep() in the main thread of a tkinter application is not recommended because it will block tkinter mainloop() and then cause the GUI not responding.
Suggest to:
use .after() to show the animated GIF because it is not safe to update tkinter widgets in a thread
use thread on loadingAnimation() instead
import threading
import tkinter
import sys
import time
root = tkinter.Tk()
frames = [tkinter.PhotoImage(file='./myScripts/M-95.gif', format='gif -index %i'%(i)) for i in range(10)]
def center_window(win):
win.wait_visibility() # make sure the window is ready
x = (win.winfo_screenwidth() - win.winfo_width()) // 2
y = (win.winfo_screenheight() - win.winfo_height()) // 2
win.geometry(f'+{x}+{y}')
def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive # used in loadingAnimation()
delay = 4000 // len(frames) # make one cycle of animation around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
if n < len(frames)-1:
lbl.config(image=frames[n])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.deiconify()
process_is_alive = False
def loadingAnimation():
animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
i = 0
while process_is_alive:
sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
sys.stdout.flush()
time.sleep(0.4)
i += 1
M_95() # start GIF animation
threading.Thread(target=loadingAnimation).start()
root.mainloop()
Update: animate GIF more than one cycle in around 4 secs:
def M_95(n=0, top=None, lbl=None):
# Play GIF (file name = m95.gif) in a 320x320 tkinter window
# Play GIF concurrently with the loading animation below
# Close tkinter window after play
global process_is_alive
num_cycles = 2
count = len(frames) * num_cycles
delay = 4000 // count # make required cycles of animation in around 4 secs
if n == 0:
root.withdraw()
top = tkinter.Toplevel()
lbl = tkinter.Label(top, image=frames[0])
lbl.pack()
center_window(top)
process_is_alive = True
lbl.after(delay, M_95, n+1, top, lbl)
elif n < count-1:
lbl.config(image=frames[n%len(frames)])
lbl.after(delay, M_95, n+1, top, lbl)
else:
top.destroy()
root.destroy()
process_is_alive = False
I am trying to record a video using opencv in python when doing multithreading within the thread that displays the stream on the window. I m fairly new to multithreading and I am not sure what is the reason I am unable to get a video recorded. I save a file but it does not have the stream in it. Pointers greatly appreciated.This is my code:
import cv2
import os
import threading
import shutil
import json
import re
import datetime
import time
now=datetime.datetime.now()
class camThread(threading.Thread):
def __init__(self, previewName, camID):
threading.Thread.__init__(self)
self.previewName = previewName
self.camID = camID
def run(self):
print("Starting " + self.previewName)
camPreview(self.previewName, self.camID)
def camPreview(previewName, camID):
cv2.namedWindow(previewName)
cam = cv2.VideoCapture(camID)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 480)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if cam.isOpened():
rval, frame = cam.read()
frame_width = int(cam.get(3))
frame_height = int(cam.get(4))
else:
rval = False
while rval:
cv2.namedWindow(previewName, cv2.WINDOW_NORMAL)
if (camID == 2):
frame= cv2.flip(frame,-1)
# cv2.setWindowProperty(previewName, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.imshow(previewName, frame)
# cam.set(CV_CAP_PROP_SETTINGS, 0)
rval, frame = cam.read()
key = cv2.waitKey(20)
if key == 115 :
Cam1="Cam"+str(camID)+"_"+timestr
ts=datetime.datetime.now()
filename="{}.avi".format(Cam1+ts.strftime("%Y%m%d_%H-%M-%S"))
out=cv2.VideoWriter(filename,cv2.VideoWriter_fourcc('M','J', 'P','G'),10,(480,720))
out.write(frame)
if key == 27:
print("Stopping recording")
break
if key == 27: # exit on ESC
break
cv2.destroyWindow(previewName)
# Create threads as follows
thread1 = camThread("Camera 1", 0)
thread2 = camThread("Camera 2", 2)
thread3 = camThread("Camera 3", 3)
timestr=str(now.strftime("%Y%m%d_%H-%M-%S"))
print("Working Directory:")
print(timestr)
#thread1.start()
thread2.start()
thread3.start()
print()
print("Active threads", threading.activeCount())
from threading import Thread
import cv2
import time
class VideoWriterWidget(object):
def __init__(self, video_file_name, src=0):
# Create a VideoCapture object
self.frame_name = str(src) # if using webcams, else just use src as it is.
self.video_file = video_file_name
self.video_file_name = video_file_name + '.avi'
self.capture = cv2.VideoCapture(src)
# Default resolutions of the frame are obtained (system dependent)
self.frame_width = int(self.capture.get(3))
self.frame_height = int(self.capture.get(4))
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))
# Start the thread to read frames from the video stream
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
# Start another thread to show/save frames
self.start_recording()
print('initialized {}'.format(self.video_file))
def update(self):
# Read the next frame from the stream in a different thread
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
def show_frame(self):
# Display frames in main program
if self.status:
cv2.imshow(self.frame_name, self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.release()
self.output_video.release()
cv2.destroyAllWindows()
exit(1)
def save_frame(self):
# Save obtained frame into video output file
self.output_video.write(self.frame)
def start_recording(self):
# Create another thread to show/save frames
def start_recording_thread():
while True:
try:
self.show_frame()
self.save_frame()
except AttributeError:
pass
self.recording_thread = Thread(target=start_recording_thread, args=())
self.recording_thread.daemon = True
self.recording_thread.start()
if __name__ == '__main__':
src1 = 'Your link1'
video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
src2 = 'Your link2'
video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
src3 = 'Your link3'
video_writer_widget3 = VideoWriterWidget('Camera 3', src3)
# Since each video player is in its own thread, we need to keep the main thread alive.
# Keep spinning using time.sleep() so the background threads keep running
# Threads are set to daemon=True so they will automatically die
# when the main thread dies
while True:
time.sleep(5)
I think you're on the right track but I was unable to save a file with your code. Here's a video-stream-to-video widget using multithreading to obtain the frames. There are two threads for each camera stream:
Thread #1 - Dedicated to only reading frames from the camera stream.
Thread #2 - Dedicated for processing frames (showing and writing).
We separate reading frames from showing/writing because cv2.VideoCapture.read() is a blocking operation. Thus we read frames in its own independent thread to 'improve' FPS by reducing latency due to I/O operations. In addition, by isolating frame capture to its own thread, there will always be a frame ready to be processed instead of having to wait for the I/O operation to complete and return a new frame. In our second thread dedicated to processing, we are now freely able to show and save each frame to our output file.
Also by encapsulating all this into a single object, we can create a set of threads for each camera which scales easily no matter how many cameras are being used. Since each camera stream is spawned in a background thread, we must keep the main thread alive. Be sure to change the src string to your own camera. Here's an example of recording three video streams simultaneously.
from threading import Thread
import cv2
import time
class VideoWriterWidget(object):
def __init__(self, video_file_name, src=0):
# Create a VideoCapture object
self.frame_name = str(src)
self.video_file = video_file_name
self.video_file_name = video_file_name + '.avi'
self.capture = cv2.VideoCapture(src)
# Default resolutions of the frame are obtained (system dependent)
self.frame_width = int(self.capture.get(3))
self.frame_height = int(self.capture.get(4))
# Set up codec and output video settings
self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))
# Start the thread to read frames from the video stream
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
# Start another thread to show/save frames
self.start_recording()
print('initialized {}'.format(self.video_file))
def update(self):
# Read the next frame from the stream in a different thread
while True:
if self.capture.isOpened():
(self.status, self.frame) = self.capture.read()
def show_frame(self):
# Display frames in main program
if self.status:
cv2.imshow(self.frame_name, self.frame)
# Press Q on keyboard to stop recording
key = cv2.waitKey(1)
if key == ord('q'):
self.capture.release()
self.output_video.release()
cv2.destroyAllWindows()
exit(1)
def save_frame(self):
# Save obtained frame into video output file
self.output_video.write(self.frame)
def start_recording(self):
# Create another thread to show/save frames
def start_recording_thread():
while True:
try:
self.show_frame()
self.save_frame()
except AttributeError:
pass
self.recording_thread = Thread(target=start_recording_thread, args=())
self.recording_thread.daemon = True
self.recording_thread.start()
if __name__ == '__main__':
src1 = 'Your link1'
video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
src2 = 'Your link2'
video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
src3 = 'Your link3'
video_writer_widget3 = VideoWriterWidget('Camera 3', src3)
# Since each video player is in its own thread, we need to keep the main thread alive.
# Keep spinning using time.sleep() so the background threads keep running
# Threads are set to daemon=True so they will automatically die
# when the main thread dies
while True:
time.sleep(5)
I am trying to display a count variable from a background task in the main task which is my tkinter GUI. Why? I want to display that the long time taking background task is performing and later use this count variable to visualize it with a progress bar.
My problem is, that even when using a Queue, I am not able to display the count variable. Maybe I've got problems in understanding python and its behaviour with objects and/or threads.
import threading
import time
import Queue
import Tkinter as Tk
import Tkconstants as TkConst
from ScrolledText import ScrolledText
from tkFont import Font
import loop_simulation as loop
def on_after_elapsed():
while True:
try:
v = dataQ.get(timeout=0.1)
except:
break
scrText.insert(TkConst.END, str(v)) # "value=%d\n" % v
scrText.see(TkConst.END)
scrText.update()
top.after(100, on_after_elapsed)
def thread_proc1():
x = -1
dataQ.put(x)
x = loop.loop_simulation().start_counting()
# th_proc = threading.Thread(target=x.start_counting())
# th_proc.start()
for i in range(5):
for j in range(20):
dataQ.put(x.get_i())
time.sleep(0.1)
# x += 1
time.sleep(0.5)
dataQ.put(x.get_i())
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
f = Font(family='Courier New', size=12)
scrText = ScrolledText(master=top, height=20, width=120, font=f)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15, expand=True)
th = threading.Thread(target=thread_proc1)
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
th.join()
In thread_proc1() I want to get the value of the counter of background task. This is the background task:
import time
class loop_simulation:
def __init__(self):
self.j = 0
# self.start_counting()
def start_counting(self):
for i in range(0, 1000000):
self.j = i
time.sleep(0.5)
def get_i(self):
return str(self.j)
The reason the count variable isn't being displayed is due to the
x = loop.loop_simulation().start_counting()
statement in thread_proc1(). This creates a loop_simulation instance and calls its start_counting() method. However, other than already inserting a -1 into the dataQ, thread_proc1() doesn't do anything else until start_counting() returns, which won't be for a long time (500K seconds).
Meanwhile, the rest of your script is running and displaying only that initial -1 that was put in.
Also note that if start_counting() ever did return, its value of None is going to be assigned to x which later code attempts to use with: x.get_i().
Below is reworking of your code that fixes these issues and also follows the PEP 8 - Style Guide for Python Code more closely. To avoid the main problem of calling start_counting(), I changed your loop_simulation class into a subclass of threading.Thread and renamed it LoopSimulation, and create an instance of it in thread_proc1, so there are now two background threads in addition to the main one handling the tkinter-based GUI.
import loop_simulation as loop
from ScrolledText import ScrolledText
import threading
import Tkinter as Tk
import Tkconstants as TkConst
from tkFont import Font
import time
import Queue
def on_after_elapsed():
# removes all data currently in the queue and displays it in the text box
while True:
try:
v = dataQ.get_nowait()
scrText.insert(TkConst.END, str(v)+'\n')
scrText.see(TkConst.END)
except Queue.Empty:
top.after(100, on_after_elapsed)
break
def thread_proc1():
dataQ.put(-1)
ls = loop.LoopSimulation() # slow background task Thread
ls.start_counting()
while ls.is_alive(): # background task still running?
for i in range(5):
for j in range(20):
dataQ.put(ls.get_i())
time.sleep(0.1)
time.sleep(0.5)
dataQ.put('background task finished')
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
font = Font(family='Courier New', size=12)
scrText = ScrolledText(top, height=20, width=120, font=font)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15,
expand=TkConst.YES)
th = threading.Thread(target=thread_proc1)
th.daemon = True # OK for main to exit even if thread is still running
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
loop_simulation.py module:
import threading
import time
class LoopSimulation(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True # OK for main to exit even if instance still running
self.lock = threading.Lock()
self.j = 0
start_counting = threading.Thread.start # an alias for starting thread
def run(self):
for i in range(1000000):
with self.lock:
self.j = i
time.sleep(0.5)
def get_i(self):
with self.lock:
return self.j
Is it possible to display a window which has been made by OpenCV using Tkinter? I want to open it using Tkinter so that I can provide more GUI functions. Has this been done before? I checked google and SO itself but did not find anything.
So as kobejohn suggested, I am attaching the code for the camera capture and display.
import cv2
import urllib
import numpy as np
import subprocess
stream=urllib.urlopen('IP Address')
bytes=''
while True:
bytes+=stream.read(1024)
a = bytes.find('\xff\xd8')
b = bytes.find('\xff\xd9')
if a!=-1 and b!=-1:
jpg = bytes[a:b+2]
bytes= bytes[b+2:]
i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.CV_LOAD_IMAGE_COLOR)
cv2.imshow('i',i)
if cv2.waitKey(1) ==27:
exit(0)
This code is based on the discussion in comments. It doesn't put the opencv window into tkinter. It just takes opencv images and puts them into tkinter.
Prakhar, I don't have an available IP camera so can you try this? I have confirmed that it works with the USB code at the bottom of this answer.
Basically, I just inserted your jpg reading code into a simplified version of this SO question to get the code below. It uses a 2-step conversion: bytes --> opencv image --> tkinter image. There may be a more efficient way to convert directly from the bytes to a tkinter image but you can fix that if performance becomes a problem.
IP Camera
import cv2
import numpy as np
import PIL.Image
import PIL.ImageTk
import Tkinter as tk
import urllib
stream = urllib.urlopen('IP Address')
bytes_ = ''
def update_image(image_label):
global bytes_
bytes_ += stream.read(1024)
a = bytes_.find('\xff\xd8')
b = bytes_.find('\xff\xd9')
if (a != -1) and (b != -1):
jpg = bytes_[a:b+2]
bytes_ = bytes_[b+2:]
cv_image = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),
cv2.CV_LOAD_IMAGE_COLOR)
cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
pil_image = PIL.Image.fromarray(cv_image)
tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
image_label.configure(image=tk_image)
image_label._image_cache = tk_image # avoid garbage collection
root.update()
def update_all(root, image_label):
if root.quit_flag:
root.destroy() # this avoids the update event being in limbo
else:
update_image(image_label)
root.after(1, func=lambda: update_all(root, image_label))
if __name__ == '__main__':
root = tk.Tk()
setattr(root, 'quit_flag', False)
def set_quit_flag():
root.quit_flag = True
root.protocol('WM_DELETE_WINDOW', set_quit_flag)
image_label = tk.Label(master=root) # label for the video frame
image_label.pack()
root.after(0, func=lambda: update_all(root, image_label))
root.mainloop()
USB Camera
*edit - I have confirmed that the code below works to take video from a USB camera using opencv and send it to a tkinter window. So hopefully the above code will work for your ip camera.
import cv2
import PIL.Image
import PIL.ImageTk
import Tkinter as tk
def update_image(image_label, cv_capture):
cv_image = cv_capture.read()[1]
cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
pil_image = PIL.Image.fromarray(cv_image)
tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
image_label.configure(image=tk_image)
image_label._image_cache = tk_image # avoid garbage collection
root.update()
def update_all(root, image_label, cv_capture):
if root.quit_flag:
root.destroy() # this avoids the update event being in limbo
else:
update_image(image_label, cv_capture)
root.after(10, func=lambda: update_all(root, image_label, cv_capture))
if __name__ == '__main__':
cv_capture = cv2.VideoCapture()
cv_capture.open(0) # have to use whatever your camera id actually is
root = tk.Tk()
setattr(root, 'quit_flag', False)
def set_quit_flag():
root.quit_flag = True
root.protocol('WM_DELETE_WINDOW', set_quit_flag) # avoid errors on exit
image_label = tk.Label(master=root) # the video will go here
image_label.pack()
root.after(0, func=lambda: update_all(root, image_label, cv_capture))
root.mainloop()
I'm planning to write a small GUI around a numerical simulation which is why I'm now playing around with Tkinter. Simulations should be launched from GUI in seperate processes. To play around a bit I defined a function random_process that generates pairs of randn numbers (this should later be a real simulation process). As that function is meant to be launched in a seperate process, two mp.Event objects and one mp.Pipe object are passed as parameters.
The main application can use one event to request accumulated data from the process, another event is used as a "Poison Pill" to kill the "simulation" process. A pipe is then used to pass the data.
In the main application, I use Tkinter's after-function to regularly check if new data has arrived and then plot it. Starting and stopping the "simulation process" is done by buttons in the main app, the same goes for requesting data from it.
At least that was the idea, in practice the program doesn't play nice. When I click on the "go!" button that is meant to launch the simulation process, a second Tkinter window appears, identical to the main one. I don't have the slightest clue why that happens. The communication with the process doesn't work neither, no data seems to be send. When googling for a solution, I found a working example of a Tkinter program launching processes and talking to them, but I didn't find out what makes it not work in my case. Has anybody got a clue?
BTW, the OS is Windows-7.
Cheers,
Jan
import matplotlib
matplotlib.use('TkAgg')
import time
import multiprocessing as mp
import Tkinter as Tk
import numpy.random as npr
import matplotlib.figure
import matplotlib.backends.backend_tkagg as tkagg
def random_process(delay, data_request, data_in, poison):
while not poison.is_set():
time.sleep(delay)
print("Generating pair of random numbers...")
x,y = npr.randn(), npr.randn()
try:
random_process.l.append((x,y))
except:
random_process.l = [(x,y)]
if data_request.is_set():
data_request.clear()
try:
ll = len(random_process.l)
if ll > 0:
print("Sending %d pairs to main program.." % ll)
data_in.send(random_process.l)
random_process.l = []
except:
print("data requested, but none there.")
# when poison event is set, clear it:
poison.clear()
class GuiInterfaceApp:
def __init__(self, parent):
self.myParent = parent
self.previewplot_container = Tk.Frame(self.myParent)
self.f = matplotlib.figure.Figure()
self.ax = self.f.add_subplot(111)
self.preview_canvas = tkagg.FigureCanvasTkAgg(self.f, master=self.previewplot_container)
self.preview_canvas.show()
self.button_container = Tk.Frame(self.myParent)
self.hellobutton = Tk.Button(self.button_container, text="hello!")
self.hellobutton.config(command = self.printhello)
self.startbutton = Tk.Button(self.button_container, text="go!")
self.startbutton.config(command=self.run_simulation)
self.plotbutton = Tk.Button(self.button_container, text="show!")
self.plotbutton.config(command=self.request_data)
self.stopbutton = Tk.Button(self.button_container, text="stop.")
self.stopbutton.config(command=self.stop_simulation)
self.quitbutton = Tk.Button(self.button_container, text="get me outta here!")
self.quitbutton.config(command=self.quit_program)
self.previewplot_container.pack(side = Tk.TOP)
self.preview_canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
self.button_container.pack(side = Tk.BOTTOM)
self.hellobutton.pack(side = Tk.LEFT)
self.startbutton.pack(side = Tk.LEFT)
self.plotbutton.pack(side = Tk.LEFT)
self.stopbutton.pack(side = Tk.LEFT)
self.quitbutton.pack(side = Tk.LEFT)
self.simulation_running = False
self.datarequest = mp.Event()
self.DataIn, self.DataOut = mp.Pipe()
self.PoisonEvent = mp.Event()
self.p = mp.Process(target = random_process, args=(1.0, self.datarequest, self.DataIn, self.PoisonEvent))
self.l = [] # list of received pairs to plot
self.mytask_time = 100 # delay in ms between calls to self.mytask
def printhello(self):
print("hello!")
def run_simulation(self):
print("startbutton pressed.")
if not self.simulation_running:
print("starting simulation...")
self.p.start()
self.simulation_running = True # attention: no error checking
def stop_simulation(self):
print("stop button pressed.")
if self.simulation_running:
print("Sending poison pill to simulation process..")
self.PoisonEvent.set()
self.simulation_running = False
# todo: wait a short amount of time and check if simu stopped.
def request_data(self):
print("plotbutton pressed.")
if self.simulation_running:
print("requesting data from simulation process")
self.datarequest.set()
def update_plot(self):
print("update_plot called.")
if len(self.l) > 0:
print("there is data to plot.")
while len(self.l) > 0:
x,y = self.l.pop()
print("plotting point (%.2f, %.2f)" % (x,y))
self.ax.plot([x], [y], '.', color='blue')
print("drawing the hole thing..")
self.ax.draw()
else:
print("nothing to draw")
def quit_program(self):
print("quitbutton pressed.")
if self.simulation_running:
print("sending poison pill to simulation process..")
self.PoisonEvent.set()
print("quitting mainloop..")
self.myParent.quit()
print("destroying root window..")
self.myParent.destroy()
def receive_data(self):
if self.DataOut.poll():
print("receiving data..")
data = self.DataOut.recv()
self.l.append(data)
self.update_plot()
def my_tasks(self):
self.receive_data()
self.myParent.after(self.mytask_time, self.my_tasks)
return
root = Tk.Tk()
myGuiInterfaceApp = GuiInterfaceApp(root)
root.after(100, myGuiInterfaceApp.my_tasks)
root.mainloop()
Try hiding your main logic behind a test for whether the code is being run or imported.
if __name__ == "__main__":
root = Tk.Tk()
...