python - 正确关闭函数调用后使用的 Tkinter
问题描述
现在我使用 Tkinter 作为前端框架来显示通过队列传入的数据。然后使用我的 draw() 函数等将这些数据绘制到屏幕上。
循环部分在 n 毫秒后调用特定函数的函数后使用 Tkinter。我有一种感觉,当调用销毁或只是关闭窗口时,这个循环会停止?导致一些后端进程不满足。
我已经在下面发布了代码,因为您将缺少抽象类。删除init中的 (Updater) 和 super将满足它,因为抽象类更像是一个接口。
重现问题:在脚本运行期间,如果 Tkinter 窗口在将队列中的所有数据绘制到绘图之前以任何可用方式关闭。该脚本永远不会返回到命令行并且永远卡住。这种情况是不利的,因为能够退出窗口并期望进程被破坏是我所寻求的。
进一步追踪: oscy.run() 传递失败,程序立即退出但不返回命令行;正如在完成之前运行并迅速退出程序所看到的那样。这开始暗示init中发生了什么?
from __future__ import division
import logging
import atexit
import matplotlib
import numpy as np
matplotlib.use('TkAgg')
from functools import wraps
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from Tkinter import Scale, Button, Tk, TOP, BOTTOM, BOTH, HORIZONTAL
class Oscilloscope():
""" Displays the Oscilloscope provided the data streaming in. """
def __init__(
self,
data_queue,
closed_callback,
title="DRP",
xlabel="Range Cells",
ylabel="Magnitude"
):
"""
Initialization function for the Osc oscilloscope.
:param data_queue: List data representative of magnitudes per update.
:type data_queue: Queue
:param closed_callback: When the figure is closed, callback should be used to remove the figure.
:type closed_callback: Function
:param title: Title of the plot being drawn for the Oscilloscope.
:type title: String
:param xlabel: X-axis of the plot being drawn, should either be Range or Doppler.
:type xlabel: String
:param ylabel: Y-axis of the plot being drawn, should always be Magnitude.
:type ylabel: String
"""
self.data_queue = data_queue
self.closed_callback = closed_callback
self.window = Tk()
atexit.register(self.closed_callback)
self.title = title
self.xlabel = xlabel
self.ylabel = ylabel
self.y_limits = np.array([0, np.finfo(np.float).eps])
def _adjust_ylim_if_req(self, magnitude):
"""
Changes the limits based on magnitudes.
:param magnitude: Size of the magnitude being plotted.
:type magnitude: Float
"""
if magnitude < self.y_limits[0]:
self.y_limits[0] = magnitude
elif magnitude > self.y_limits[1]:
self.y_limits[1] = magnitude
self.ax.set_ylim(self.y_limits[0], self.y_limits[1])
def draw(self):
"""
Draws the main line plot.
"""
try:
magnitudes = self.data_queue.get_nowait()
except:
pass
else:
# Adjust y limits
for magnitude in magnitudes:
self._adjust_ylim_if_req(magnitude)
# Plot the graph
self.ax.cla()
self.ax.set_title(self.title, fontdict={'fontsize': 16, 'fontweight': 'medium'})
self.ax.set_xlabel(self.xlabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
self.ax.set_ylabel(self.ylabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
self.ax.plot([n for n in range(len(magnitudes))], magnitudes, '-bo')
def run(self):
"""
Sets up and runs the main logic of the Window
"""
self.plot()
self.updateplot()
self.window.mainloop()
def plot(self):
"""
Creates the initial base plot
"""
figure = matplotlib.figure.Figure()
self.ax = figure.add_subplot(1,1,1)
self.ax.set_title(self.title, fontdict={'fontsize': 16, 'fontweight': 'medium'})
self.ax.set_xlabel(self.xlabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
self.ax.set_ylabel(self.ylabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
self.canvas = FigureCanvasTkAgg(figure, master=self.window)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)
self.draw()
def close_fig():
self.window.destroy()
self.closed_callback
button = Button(self.window, text='Close', command=close_fig)
button.pack()
def updateplot(self):
"""
Updates the plot by gathering more data from the Queue.
"""
print('Start')
self.draw()
self.canvas.draw()
self.window.after(1, self.updateplot)
print('End')
if __name__ == '__main__':
from threading import Thread
from multiprocessing import Queue
q = Queue()
for i in xrange(100):
l = []
for i in xrange(1000):
l.append(np.random.randint(0, 100))
q.put(l)
def cb():
print('Closed')
oscy = Oscilloscope(q, cb)
oscy.run()
解决方案
这是一个非常奇怪的问题,我永远不会想到。
解决方案
使用 multiprocessing.Queue 是问题所在,要解决此问题,请使用常规队列。
发生了什么
多处理队列似乎通过管道输入它的数据。当程序终止并且 main 的范围丢失时,这些数据仍然被持有,等待被删除。然后这会导致“挂起”,即什么都没有发生,但在后台发生了一些事情。
在程序执行之前调用 close() 可以解决问题。由于多处理模块本身就是一个基于进程的“线程”接口。
推荐阅读
- javascript - MySQL获取每个用户ID的最大日期并将它们转换为sequelize
- python - 如何使用 python-pptx 从形状的连接点到另一个形状的连接点画一条线?
- firebase - 在颤振中添加if条件后,列表视图卡不起作用
- python - python函数参数列表中的字符串
- python-3.x - 如何解决ansible问题:无法获取远程文件的信息...不是目录
- python - QTableview checkitems,从对应的第二列获取值
- php - 如何在 Laravel 中将数据从私有函数传递到公共函数?
- pytest - 如何在不同的进程中调用带有夹具的测试
- python - 是否可以在基于 QWidget 的应用程序上使用 QQmlApplicationEngine?
- javascript - 如何在现有的本地存储对象中将键值添加到对中?