画像操作に PIL を使用し、画像を表示するために Tkinter を使用して、イントロ CS コースで使用する画像操作クラスを作成しています。ユーザーが操作中に画像を見ることができるようにするために、別のスレッドでグラフィック操作を実行し、この質問に似たコードを使用します。これは機能しているように見えます (つまり、何もクラッシュしません) が、画像を表示できません。Tk は起動していますが、ウィンドウが表示されません。コードは次のようになります。
self.root = Tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
このtick
関数は、リンクされた質問のものと似ています。私の問題はTkinterの誤解によるものだと思いますが、本当にわかりません。
また、プログラムをうまく終了させることができないようです -これが実行されているdaemon=True
を構築するときに設定したとしても、終了時にヒットする必要があります。これはちょっと見栄えが悪いので、偽のエラー メッセージで学生を悩ませたくありません。Thread
C-c
編集:ここにいくつかのコードがあります。
class Picture():
##
# Constructor. Creates Picture either by passing in the path of an image file as param
# or by passing in a tuple in the format of (x, y) to indicate the size of a blank image.
# Example 1: Picture("image.jpg") constructs Picture with image.jpg
# Example 2: Picture((500, 500)) constructs Picture with a blank image of size 500*500
def __init__(self, param):
# Check if parameter is the right type, because we can't
# overload functions
if isinstance(param, tuple) and len(param) == 2:
self.image = Image.new('RGB', (param))
elif isinstance(param, str):
self.image = Image.open(param)
else:
raise TypeError('Parameter to Picture() should be a string or 2-tuple!')
# Default values for pen
self.pen_color = (0, 0, 0)
self.pen_position = (0, 0)
self.pen_width = 1.0
self.pen_rotation = 0
# Pixel data of the image
self.pixel = self.image.load()
# Draw object of the image
self.draw = ImageDraw.Draw(self.image)
# The main window, and associated widgets.
self.root = None
self.label = None
self.frame = None
# Threading support, so that we can show the image while
# continuing to draw on it.
self.request_queue = queue.Queue()
self.result_queue = queue.Queue()
self.thread = threading.Thread(target=self._thread_main)
self.thread.start()
def _thread_main(self):
"""
Runs the main Tkinter loop, as well as setting up all the
necessary GUI widgets and whatnot. By running Tkinter on
a separate thread, we can keep the picture displaying
even after the user's program is finished drawing on it.
"""
def tick():
"""
Called whenever Tk's main loop is idle. This lets us perform
drawing operations on the right thread.
"""
try:
f, args, kwargs = self.request_queue.get_nowait()
except queue.Empty:
pass
else:
value = f(*args, **kwargs)
self.result_queue.put(value)
self.root.after_idle(tick)
self.root = tk.Toplevel()
self.frame = tk.Frame(self.root, width=self.image.size[0], height=self.image.size[1])
img = ImageTk.PhotoImage(self.image)
self.label = tk.Label(self.frame, image=img)
# This line ensures that Python doesn't try to garbage collect
# our photo, due to a bug in Tk.
self.label.image = img
self.label.pack()
self.frame.pack()
tick()
self.root.mainloop()
def _submit_operation(self, f, *args, **kwargs):
"""
Submits an operation to the request queue. The arguments
should consist of a function, any positional arguments
to said function, and any keyword arguments to the function.
If f returns a value, that value will be returned.
Any function that does something with the picture (i.e.,
saving it, drawing to it, reading from it, etc.) should
be called only by submitting it to the queue.
"""
self.request_queue.put((f, args, kwargs))
return self.result_queue.get()
##
# Display the picture.
def display(self):
def display_func():
img = ImageTk.PhotoImage(self.image)
self.label.configure(image=img)
self.label.image = img
self.label.pack()
self.frame.pack()
self._submit_operation(display_func)