Hi I'm new at programming in python and gtk.
I'm writing a program to do some measurement.
For plotting the measurement, I use matplotlib.
The program will have a function to turn a heater on and off and to make the measurement.
I want to use separate threads for the heater and the measurement.
For now the communication with the hardware hasn't been implemented yet in this program.
The problem is when I click the "measurebutton", the "progressbar" doesn't work anymore.
I get a message:
gtk.ProgressBar object at 0x29b8460 (uninitialized at 0x0)
When I only use the heaterbutton, the progressbar keeps working
What am I doing wrong ?
This is the code
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import time
import gobject
import threading
import matplotlib
import numpy as np
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
class measure:
# callback to quit
def delete_event(self, widget, event, data = None):
gtk.main_quit()
return False
def heater_helper(self, widget, heater, progressbar):
print "starting heater thread"
threading.Thread(target=self.heater_cb, args=(widget, heater, progressbar)).start()
def heater_cb(self, widget, heater, progressbar):
heaterstring = "6.3"
heater = eval(heaterstring)
stap = 1
j = 0.1
heatervalue = widget.get_active()
print heatervalue
progressbar.set_fraction(0.1)
while (stap <= 10 ):
if widget.get_active():
print widget.get_active()
fraction = j * stap
print fraction
progressbar.set_fraction(fraction)
stap = stap + 1
time.sleep(1)
else:
stap = 11
progressbar.set_fraction(0.0)
break
def do_measurement_helper(self, widget, fig):
print " Start measurement thread"
threading.Thread(target=self.do_measurement, args=(widget, fig)).start()
def do_measurement(self, widget, fig):
fig.clear()
ax = fig.add_subplot(111)
x = np.arange(0, 5*np.pi, 0.01)
y = np.sin(x**2)*np.exp(-x)
ax.plot(x, y)
fig.canvas.draw()
def __init__(self):
# Create new Window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("delete_event", self.delete_event)
self.window.show()
mainbox = gtk.HBox(False, 0)
self.window.add(mainbox)
mainbox.show()
leftvbox = gtk.VBox(False, spacing = 10)
mainbox.pack_start(leftvbox, expand = False, fill = False, padding = 0)
leftvbox.show()
rightvbox = gtk.VBox(False, spacing = 10)
mainbox.pack_start(rightvbox, expand = False, fill = False, padding =0)
rightvbox.show()
heaterprogressbar = gtk.ProgressBar()
leftvbox.pack_start(heaterprogressbar, expand = False, fill = False, padding = 0)
heaterprogressbar.show()
heaterbutton = gtk.ToggleButton("Heater")
leftvbox.pack_start(heaterbutton, expand = True, fill = False, padding = 0)
heaterbutton.show()
heaterbutton.connect("toggled", self.heater_helper, heaterbutton, heaterprogressbar)
fig = matplotlib.figure.Figure(figsize=(5,4), dpi=64)
canvas = FigureCanvas(fig)
rightvbox.pack_start(canvas, expand = True, fill = True, padding = 0 )
canvas.show()
measurebutton = gtk.Button("Measure")
rightvbox.pack_start(measurebutton, expand = False, fill = False, padding = 0)
measurebutton.show()
measurebutton.connect("clicked", self.do_measurement_helper, fig)
def main():
gtk.main()
return(0)
if __name__ == "__main__":
gtk.gdk.threads_init()
measure()
main()
gtk.gdk.threads_leave()
Kind regards,
Joris Weijters
Combining threads, Matplotlib, and the GTK main loop is probably not supported and difficult to debug exactly what is going on. My advice is not to do any GUI calls from threads, but instead schedule them using gobject.idle_add().
Threads, Matplotlib and the GTK main loop can be combined, if you keep in mind some things:
I use gobject.threads_init() instead of gtk.gdk.threads_init(), the gtk.gdk variant did not work for me in combination with Matplotlib. I think you can also omit the gtk.gdk.threads_leave().
As ptomato mentioned, you should let the main gtk thread handle anything that has to do with gtk widgets by calling the gobject.idle_add() and gobject.timeout_add() functions.
I usually make a helper function to periodically update the statusbar from a float variable:
def do_measurement(self):
self.data = []
self.progress = 0
self.abort = threading.Event()
gobject.timeout_add(100, self.update_progressbar)
for point in some_generator_yielding_100_values():
if self.abort.is_set():
break
self.data.append(point)
self.progress += 0.01
self.progress = None
def update_progressbar(self):
if self.progress is None:
self.progressbar.set_fraction(0) # reset bar
return False # do not run again
self.progressbar.set_fraction(self.progress)
return True # run again after 100ms
def start_measurement(self):
threading.Thread(target=self.do_measurement).start()
def stop_measurement(self):
self.abort.set()
But you can of course also just call gobject.idle_add(self.progressbar.set_fraction, x) to set the new value x asynchroneously.
Related
I want to run two loops at the same time in python (first_mainloop(), second_mainloop()), I use threading but it doesn't work at this situation (only first loop runs and after it gets closed second loops starts running) any ideas?
import threading
import runpy
from tkinter import *
root = Tk()
import os
def first_mainloop():
p1 = PhotoImage(file='Screenshot_13-removebg-preview.png')
root.iconphoto(False, p1)
frameCnt = 12
frames = [PhotoImage(file='light_ai_design_by_gleb.gif',format = 'gif -index %i' %(i)) for i in range(frameCnt)]
# set window size
root.geometry("500x400")
root.title("Assistant")
def update(ind):
frame = frames[ind]
ind += 1
if ind == frameCnt:
ind = 0
label.configure(image=frame)
root.after(100, update, ind)
label = Label(root)
label.pack()
root.resizable(False, False)
root.after(0, update, 0)
root.mainloop()
def second_mainloop():
runpy.run_path("main.py")
thread1 = threading.Thread(target=first_mainloop())
thread1.start()
thread2 = threading.Thread(target=second_mainloop())
thread2.start() ```
I think the problem is in parentheses, because when you write this thread1 = threading.Thread(target=first_mainloop()), you call the function. So remove the brackets, it should be something like: thread1 = threading.Thread(target=first_mainloop)
Here is great explanation of your problem: https://thispointer.com/python-how-to-create-a-thread-to-run-a-function-in-parallel/
SOLUTION
Remove the channel and associated code
Add a new update function inside the window class which takes the new shapes as a parameter
modify the initialisation of the class
call the update function
Modifications for the solution
Apologies, but the diff markdown doesn't seem to be displaying properly, hopefully you should still get an idea of how the solution works
Window class
class Window(Gtk.Window):
- __gsignals__ = {
- 'update_signal': (GObject.SIGNAL_RUN_FIRST, None,
- ())
- }
-
- def do_update_signal(self):
- print("UPDATE SIGNAL CALLED")
- self.shapes = self.shapes_channel.read()
- print("Num new shapes:", len(self.shapes))
- self.show_all()
in the class method init_ui
self.connect("delete-event", Gtk.main_quit)
+ self.show_all()
+ a = self.darea.get_allocation()
+ print (a.x, a.y, a.width, a.height)
+ self.img = cairo.ImageSurface(cairo.Format.RGB24, a.width, a.height)
a new class method update_shapes
+ def update_shapes(self, shapes):
+ self.shapes = shapes
+ cr = cairo.Context(self.img)
+ self.draw_background(cr)
+ for shape in self.shapes:
+ shape.draw(cr)
+ self.darea.queue_draw()
+ return True
Main code
- shapes_channel = Channel()
iter_num = 0
- def optimize(chan, prob, signaller):
+ def optimize(prob, signaller):
def print_iter_num(xk):
global iter_num
iter_num += 1
prob.update_positions(xk)
prob.update_grads(jacobian(xk))
new_shapes = convert_grid(prob.grid, building_size=1.0/GRID_SIZE)
- chan.write(new_shapes)
- signaller.emit("update_signal")
+ GLib.idle_add(signaller.update_shapes, new_shapes)
print("Iteration", iter_num, "complete...")
try:
sol = minimize(objective, x0, bounds = all_bounds, constraints=constraints, options={'maxiter': MAX_ITER, 'disp': True}, callback=print_iter_num, jac=jacobian)
prob.update_positions(sol.x)
except Exception as e:
print("ran into an error", e)
- window = new_window(shapes_channel=shapes_channel)
+ window = new_window()
- x = threading.Thread(target=optimize, args=(shapes_channel, optim_problem, window))
+ x = threading.Thread(target=optimize, args=(optim_problem, window))
x.start()
window.run()
QUESTION
Window class
import cairo
import gi
import math
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
class Line():
def __init__(self, start, end, thickness, colour):
self.start = start
self.end = end
self.thickness = thickness
self.colour = colour
def draw(self, cr):
cr.move_to(*self.start)
cr.line_to(*self.end)
cr.set_source_rgba(*self.colour)
cr.set_line_width(self.thickness)
cr.stroke()
class Polygon():
def __init__(self, points, line_colour, line_thickness, fill_colour=None):
self.points = points # points should be an iterable of points
self.line_colour = line_colour
self.line_thickness = line_thickness
self.fill_colour = fill_colour
def draw(self, cr):
cr.move_to(*self.points[0])
for point in self.points[1:]:
cr.line_to(*point)
cr.close_path()
cr.set_source_rgba(*self.line_colour)
cr.set_line_width(self.line_thickness)
cr.stroke()
if self.fill_colour is not None:
cr.move_to(*self.points[0])
for point in self.points[1:]:
cr.line_to(*point)
cr.close_path()
cr.set_source_rgba(*self.fill_colour)
cr.fill()
class Window(Gtk.Window):
__gsignals__ = {
'update_signal': (GObject.SIGNAL_RUN_FIRST, None,
())
}
def do_update_signal(self):
print("UPDATE SIGNAL CALLED")
self.shapes = self.shapes_channel.read()
print("Num new shapes:", len(self.shapes))
self.show_all()
def __init__(self, shapes_channel, window_size, background_colour=(1, 1, 1, 1), title="GTK window"):
super(Window, self).__init__()
self.width = window_size[0]
self.height = window_size[1]
self.background_colour = background_colour
self.title = title
self.shapes = []
self.shapes_channel = shapes_channel
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title(self.title)
self.resize(self.width, self.height)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
def draw_background(self, cr: cairo.Context):
cr.scale(self.width, self.height)
cr.rectangle(0, 0, 1, 1) # Rectangle(x0, y0, x1, y1)
cr.set_source_rgba(*self.background_colour)
cr.fill()
def on_draw(self, wid, cr: cairo.Context):
self.draw_background(cr)
for shape in self.shapes:
shape.draw(cr)
def run(self):
Gtk.main()
def new_window(shapes_channel,
window_size=(1000, 1000),
background_colour=(1,1,1,1),
title="3yp"):
return Window(shapes_channel,
window_size=window_size,
background_colour=background_colour,
title=title)
I'm trying to run a window that can draw the shapes I've defined (Lines and Polygons).
It worked fine before when I supplied it a list of shapes and ran it at the end of my application
However, I am trying to add interactivity and have it redraw a list of shapes when the update_signal gets called and a list of new shapes get passed along the shapes_channel that is part of the constructor.
Main Code
Here is the relevant bits from my main code:
shapes_channel = Channel()
iter_num = 0
def optimize(chan, prob, signaller):
def print_iter_num(xk):
global iter_num
iter_num += 1
prob.update_positions(xk)
prob.update_grads(jacobian(xk))
new_shapes = convert_grid(prob.grid, building_size=1.0/GRID_SIZE)
chan.write(new_shapes)
signaller.emit("update_signal")
print("Iteration", iter_num, "complete...")
try:
sol = minimize(objective, x0, bounds = all_bounds, constraints=constraints, options={'maxiter': MAX_ITER, 'disp': True}, callback=print_iter_num, jac=jacobian)
prob.update_positions(sol.x)
except Exception as e:
print("ran into an error", e)
window = new_window(shapes_channel=shapes_channel)
x = threading.Thread(target=optimize, args=(shapes_channel, optim_problem, window))
x.start()
window.run()
As you can see:
A Channel() object is created, named shapes_channel
A new window is created, with the shapes_channel passed into the constructor via the intermediate function new_window.
This window is passed to the other thread so that the other thread
can emit the relevant signal ("update_signal")
The other thread is run
The window is run in the main thread I get the following console output:
UPDATE SIGNAL CALLED
Num new shapes: 31
Gdk-Message: 01:27:14.090: main.py: Fatal IO error 0 (Success) on X server :0.
From the console output, we can infer that the signal is called successfully, and the new shapes are passed to the window and stored correctly, but it fails on the line self.show_all().
This is an object that was working fine previously, and producing graphical output, and I can only think of 2 possible things that may have changed from the objects perspective:
The Channel object works as intended, but perhaps the mere presence of an object that is shared across threads throws the whole thing into disarray
Even though it's on the main thread, it doesn't like that there are other threads.
I would really appreciate some guidance on this maddening occurrence.
About your assumptions:
It is unclear if your channel object is possible to safely access from two threads.
The signal handler is executed in the thread that emits the signal.
My guess would be that it is the fact that you emit the signal from another thread that causes the issue.
You can solve this by using GLib.idle_add(your_update_func). Instead of calling your_update_func directly, a request is added to the Gtk main loop, which executes it when there are no more events to process, preventing any threading issues.
Read more here: https://wiki.gnome.org/Projects/PyGObject/Threading
I'm trying to create a somewhat "responsive" design with ttk (tkinter). The basic placement of widgets is no problem at all, but making it fluid with the width of the program is something I cannot achieve. In CSS I know it's possible to say something along the lines of '"float: left" for all containers' and the page would adapt to the screen size. I haven't found something similar to that in Tkinter and frames.
My basic test program:
#!/usr/bin/python3
import tkinter
from tkinter import ttk
from ttkthemes import ThemedTk, THEMES
class quick_ui(ThemedTk):
def __init__(self):
ThemedTk.__init__(self, themebg=True)
self.geometry('{}x{}'.format(900, 150))
self.buttons = {}
self.frame1 = ttk.Frame(self)
self.frame1.pack(side="left")
self.frame2 = ttk.Frame(self)
self.frame2.pack(side="left")
#------------------------------------------------------- BUTTONS
i = 0
while (i < 5):
i += 1
self.buttons[i]= ttk.Button(self.frame1,
text='List 1 All ' + str(i),
command=self.dump)
self.buttons[i].pack(side="left")
while (i < 10):
i += 1
self.buttons[i]= ttk.Button(self.frame2,
text='List 2 All ' + str(i),
command=self.dump)
self.buttons[i].pack(side="left")
def dump(self):
print("dump called")
quick = quick_ui()
quick.mainloop()
This creates a window with 10 buttons all besides each other.
When I shrink the window to the point that the buttons no longer fit on the screen, I would like the buttons to appear below each other
So what I did was add a resize listener and setup the following method:
def resize(self, event):
w=self.winfo_width()
h=self.winfo_height()
# print("width: " + str(w) + ", height: " + str(h))
if(w < 830):
self.frame1.config(side="top")
self.frame2.config(side="top")
But Frame doesn't has the property side, which is a parameter given to the method pack. So that didn't work either.
And now I'm lost. I've spend way to long on this, trying grids and other solutions, but I've got the feeling that I'm missing out on one simple, but very important setting.
I've created a (hackish) solution, which is fine due to it being a very internal program. Since no answer is given in the mean time, I'll provide my solution here. It probably has a lot of room for improvements, but I hope that this can give somebody in the future some pointers on how to tackle this problem by him(or her) self.
#!/usr/bin/python3
import re
import sys
import tkinter
from tkinter import filedialog
from tkinter import ttk
from ttkthemes import ThemedTk, THEMES
import subprocess
import os
from tkinter.constants import UNITS
import json
from functools import partial
class quick_ui(ThemedTk):
def __init__(self):
ThemedTk.__init__(self, themebg=True)
self.minsize(600, 250)
self.elems = {}
self.resize_after_id = None
#------------------------------------------------------- Window menu bar contents
self.menubar = tkinter.Menu(self)
self.menubar.add_command(label="Open", command = self.dump)
self.menubar.add_command(label="Refresh", command = self.dump)
self.config(menu=self.menubar)
# Theme menu
self.themeMenu = tkinter.Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label="Theme", menu=self.themeMenu)
self.themeMenu.add_command(label="DEFAULT", command=partial(self.dump, "default"))
#---------------------------------------------------------------------- top_frame
self.top_frame = ttk.Frame(self)
self.top_frame.pack( side = tkinter.TOP, expand='YES', fill='both', padx=10)
self.top_top_frame = ttk.Frame(self.top_frame)
self.top_top_frame.pack(side=tkinter.TOP, expand='YES', fill='both')
self.top_bottom_frame = ttk.Frame(self.top_frame)
self.top_bottom_frame.pack(side=tkinter.BOTTOM)
self.top_bottom_top_frame = ttk.Frame(self.top_frame)
self.top_bottom_top_frame.pack(side=tkinter.TOP)
self.top_bottom_bottom_frame = ttk.Frame(self.top_frame)
self.top_bottom_bottom_frame.pack(side=tkinter.BOTTOM)
#------------------------------------------------------------------- bottom_frame
self.bottom_frame = ttk.Frame(self, relief="sunken")
self.bottom_frame.pack( side = tkinter.BOTTOM,
expand='YES',
fill='both',
padx=10,
pady=10 )
#------------------------------------------------------- BUTTONS
i = 0
while (i < 15):
self.elems[i]=ttk.Button(self.top_bottom_top_frame,
text='List All ' + str(i),
command=self.dump)
i += 1
self.label_test_strings1 = ttk.Label(self.top_top_frame, text='Test strings1')
self.label_test_strings2 = ttk.Label(self.top_bottom_frame, text='Test strings2')
self.label_test_strings4 = ttk.Label(self.top_bottom_bottom_frame, text='Test strings4')
self.label_test_strings1.pack(side = tkinter.TOP)
self.label_test_strings2.pack(side = tkinter.TOP)
self.label_test_strings4.pack(side = tkinter.TOP)
self.placeElems()
# Setup a hook triggered when the configuration (size of window) changes
self.bind('<Configure>', self.resize)
def placeElems(self):
for index in self.elems:
self.elems[index].grid(row=0, column=index, padx=5, pady=5)
# ------------------------------------------------------ Resize event handler
def resize(self, event):
# Set a low "time-out" for resizing, to limit the change of "fighting" for growing and shrinking
if self.resize_after_id is not None:
self.after_cancel(self.resize_after_id)
self.resize_after_id = self.after(200, self.resize_callback)
# ------------------------------------------------------ Callback for the resize event handler
def resize_callback(self):
# The max right position of the program
windowMaxRight = self.winfo_rootx() + self.winfo_width()
# Some basic declarations
found = False
willAdd = False
maxColumn = 0
currIndex = 0
currColumn = 0
currRow = 0
counter = 0
last_rootx = 0
last_maxRight = 0
# Program is still starting up, so ignore this one
if(windowMaxRight < 10):
return
# Loop through all the middle bar elements
for child in self.top_bottom_frame.children.values():
# Calculate the max right position of this element
elemMaxRight = child.winfo_rootx() + child.winfo_width() + 10
# If we already found the first 'changable' child, we need to remove the following child's also
if(found == True):
# Is the window growing?
if(willAdd == True):
# Check to see if we have room for one more object
calcMaxRight = last_maxRight + child.winfo_width() + 20
if(calcMaxRight < windowMaxRight):
maxColumn = counter + 1
# Remove this child from the view, to add it again later
child.grid_forget()
# If this child doesn't fit on the screen anymore
elif(elemMaxRight >= windowMaxRight):
# Remove this child from the view, to add it again later
child.grid_forget()
currIndex = counter
maxColumn = counter
currRow = 1
found = True
else:
# If this child's x position is lower than the last child
# we can asume it's on the next row
if(child.winfo_rootx() < last_rootx):
# Check to see if we have room for one more object on the first row
calcMaxRight = last_maxRight + child.winfo_width() + 20
if(calcMaxRight < windowMaxRight):
child.grid_forget()
currIndex = counter
currColumn = counter
maxColumn = counter + 1
found = True
willAdd = True
# Save some calculation data for the next run
last_rootx = child.winfo_rootx()
last_maxRight = elemMaxRight
counter += 1
# If we removed some elements from the UI
if(found == True):
counter = 0
# Loop through all the middle bar elements (including removed ones)
for child in self.top_bottom_frame.children.values():
# Ignore the elements still in place
if(counter < currIndex):
counter += 1
continue
# If we hit our maxColumn count, move to the next row
if(currColumn == maxColumn):
currColumn = 0
currRow += 1
# Place this element on the UI again
child.grid(row=currRow, column=currColumn, padx=5, pady=5)
currColumn += 1
counter += 1
def dump(self):
print("dump called")
quick = quick_ui()
quick.mainloop()
I have now tried to resolve this issue for multiple hours but no matter what I do, I never get the thing to work.
My project tracks live data and provides an endpoint for other services to get the latest(ish) measurement. But no matter what I do, the queue.get() always returns nothing.
Here is my code:
from collections import deque
import numpy as np
import argparse
import imutils
import cv2
from flask import Flask
from multiprocessing import Queue
import threading
import Queue as Q
app = Flask(__name__)
class ImageParser(object):
def dosmth(self, q):
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the (optional) video file")
ap.add_argument("-b", "--buffer", type=int, default=14, help="max buffer size")
args = vars(ap.parse_args())
greenLower = [(86, 61, 128)]
greenUpper = [(148, 183, 196)]
pts1 = deque(maxlen=args["buffer"])
pts2 = deque(maxlen=args["buffer"])
if not args.get("video", False):
camera = cv2.VideoCapture(0)
else:
camera = cv2.VideoCapture(args["video"])
while True:
(grabbed, frame) = camera.read()
if args.get("video") and not grabbed:
break
frame = imutils.resize(frame, width=1200)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
for j in range(len(greenLower)):
upper = greenUpper[j]
lower = greenLower[j]
mask = cv2.inRange(hsv, lower, upper)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
for i in range(len(cnts)):
center = None
if len(cnts) > 0:
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
if radius > 10:
q.put(center)
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
if j == 0:
pts1.appendleft(center)
for i in xrange(1, len(pts1)):
if pts1[i - 1] is None or pts1[i] is None:
continue
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
cv2.line(frame, pts1[i - 1], pts1[i], (255,0,0), thickness)
if j == 1:
pts2.appendleft(center)
for i in xrange(1, len(pts2)):
if pts2[i - 1] is None or pts2[i] is None:
continue
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
cv2.line(frame, pts2[i - 1], pts2[i], (51, 153, 255), thickness)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
camera.release()
cv2.destroyAllWindows()
imgPar = ImageParser()
q = Queue()
scp = threading.Thread(target=imgPar.dosmth, args=(q,))
scp.start()
def getVal():
try:
(x,y) = q.get_nowait()
except Q.Empty:
return -1 , -1
return (x,y)
#app.route('/', methods=['GET'])
def doMain():
x,y = getVal()
print x,y
return '{},{}'.format(x,y)
app.run(debug=True, host='10.21.8.52')
As I really do not have any other clue, what I should do, any help would be appreciated.
Everything is running on python 2.7.15 in an anaconda environment if that helps in any way.
As I really do not have
I took the liberty of stripping out the CV2 code as I don't have a camera, and replace the queue filler with a pair of random numbers every .5 seconds, and PEP8-ing the code a bit, and this way it works:
import random
import time
from flask import Flask
import threading
from multiprocessing import Queue
from Queue import Empty as QueueEmpty
app = Flask(__name__)
class ImageParser(object):
def __init__(self, queue):
self.queue = queue
self.source = random.random
self.pause = 0.5
def run(self):
while True:
value = (self.source(), self.source())
self.queue.put(value)
time.sleep(self.pause)
queue = Queue()
image_parser = ImageParser(queue)
image_thread = threading.Thread(target=image_parser.run)
#app.route('/', methods=['GET'])
def do_main():
try:
value = queue.get_nowait()
except QueueEmpty:
value = None
print(value)
return str(value)
if __name__ == '__main__':
image_thread.start()
app.run(debug=True, host='127.0.0.1')
Under http://127.0.0.1:5000/ I now get pairs of random numbers, and the occasional None when I reload too fast.
I therefore conclude that the problem probably lies with the image processing part. Specifically I noticed that only contours with an enclosing radius > 10 get put into the queue. Maybe that path of code just never gets executed. Are you quite sure that any values get put into the queue at all? Maybe a print x, y, radius before the if radius > 10 will shed some light. (And why put center instead of x and y?)
I am doing a graphics project and have to use pixel plotting function only. I came across pygame but it doesnt seem to provide its calls to be used in threads. Please suggest me workaround this or any other python library that can be used with threads.
#! /usr/bin/env python
import threading
import time
class plotterthread(threading.Thread):
def __init__(self,screen,queue):
threading.Thread.__init__(self)
self.screen = screen
self.__queue__ = queue
self._colorset = False
self.kill = False
def setcolor(self,r,g,b):
self._r = r
self._g = g
self._b = b
self._colorset = True
def run(self):
try:
while not self.__queue__.empty() and not self.kill:
(x,y) = self.__queue__.get(timeout=1)
if not self._colorset:
#this is where error occures
#this calls pygame.set_at() method
self.screen.plotpixel((x,y))
else:
self.screen.plotpixel((x,y),(self._r,self._g,self._b))
except:
pass
class plotter:
def __init__(self,screen,min,max,queue,ppt=50):
self.screen = screen
self.min = min
self.max = max
self.ppt = ppt # pixels per thread
self.coords = queue
def plot(self):
noOfcoords = self.coords.qsize()
noOfthreads = int(noOfcoords/self.ppt)
if noOfthreads > self.max:
noOfthreads = self.max
elif noOfthreads < self.min:
noOfthreads = self.min
self.threadpool = []
for i in range(noOfthreads):
pthread = plotterthread(screen = self.screen,queue = self.coords)
self.threadpool.append(pthread)
pthread.start()
while True:
try:
livethreads = 0
for t in self.threadpool:
if t is not None and t.isAlive():
livethreads += 1
t.join(timeout=1)
if livethreads == 0:
break
except KeyboardInterrupt:
for threads in self.threadpool:
if threads is not None and threads.isAlive():
threads.kill = True
break
Here, screen is the instance of the window on which I am trying to draw.
queue is the Queue which contains all the calculated coordinates to be plotted.
All I wanted to do was first calculate all the pixels and push them into a queue and then share the queue with threads to plot them in parallel to speed up the process.