"Interactive" GUI 中的图形
"Interactive" figure in a GUI
我已经用 Bryan-Oakley 的 answer 的修改版本更新了初始脚本。它现在有 2 个 canvas,1 个带有可拖动矩形,1 个带有绘图。如果可能的话,我希望矩形沿着绘图上的 x 轴拖动?
import tkinter as tk # python 3
# import Tkinter as tk # python 2
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
class Example(tk.Frame):
"""Illustrate how to drag items on a Tkinter canvas"""
def __init__(self, parent):
tk.Frame.__init__(self, parent)
fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
fig.add_subplot(111).plot(t, 2 * np.sin(2 * np.pi * t))
#create a canvas
self.canvas = tk.Canvas(width=200, height=300)
self.canvas.pack(fill="both", expand=True)
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# this data is used to keep track of an
# item being dragged
self._drag_data = {"x": 0, "y": 0, "item": None}
# create a movable object
self.create_token(100, 150, "black")
# add bindings for clicking, dragging and releasing over
# any object with the "token" tag
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_rectangle(
x - 5,
y - 100,
x + 5,
y + 100,
outline=color,
fill=color,
tags=("token",),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
delta_x = event.x - self._drag_data["x"]
delta_y = 0
# move the object the appropriate amount
self.canvas.move(self._drag_data["item"], delta_x, delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
非常感谢任何帮助。
我不确定如何用 tkinter 或 pyQt 来做,但我知道如何用 PyGame 做这样的事情,这是 python 的另一种 GUI 解决方案。希望这个例子对你有帮助:
import pygame
SCREEN_WIDTH = 430
SCREEN_HEIGHT = 410
WHITE = (255, 255, 255)
RED = (255, 0, 0)
FPS = 30
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
rectangle = pygame.rect.Rect(176, 134, 17, 170)
rectangle_draging = False
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if rectangle.collidepoint(event.pos):
rectangle_draging = True
mouse_x, mouse_y = event.pos
offset_x = rectangle.x - mouse_x
offset_y = rectangle.y - mouse_y
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
rectangle_draging = False
print("Line is at: (", rectangle.x, ";", rectangle.y,")")
elif event.type == pygame.MOUSEMOTION:
if rectangle_draging:
mouse_x, mouse_y = event.pos
rectangle.x = mouse_x + offset_x
rectangle.y = mouse_y + offset_y
screen.fill(WHITE)
pygame.draw.rect(screen, RED, rectangle)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
当你四处移动线条时,控制台会打印它所在的位置。
您的代码的问题是您创建了两个 canvases,一个用于 matplotlib 图,一个用于可拖动的矩形,而您希望两者相同。
为了解决这个问题,我将问题的当前代码与编辑前的代码合并,所以整个 matplotlib 图现在嵌入到 Tkinter 中 window。我对 DraggableLine
class 所做的关键修改是它现在将 canvas 作为参数。
import tkinter as tk # python 3
# import Tkinter as tk # python 2
import numpy as np
import matplotlib.lines as lines
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
class DraggableLine:
def __init__(self, ax, canvas, XorY):
self.ax = ax
self.c = canvas
self.XorY = XorY
x = [XorY, XorY]
y = [-2, 2]
self.line = lines.Line2D(x, y, color='red', picker=5)
self.ax.add_line(self.line)
self.c.draw_idle()
self.sid = self.c.mpl_connect('pick_event', self.clickonline)
def clickonline(self, event):
if event.artist == self.line:
self.follower = self.c.mpl_connect("motion_notify_event", self.followmouse)
self.releaser = self.c.mpl_connect("button_press_event", self.releaseonclick)
def followmouse(self, event):
self.line.set_xdata([event.xdata, event.xdata])
self.c.draw_idle()
def releaseonclick(self, event):
self.XorY = self.line.get_xdata()[0]
self.c.mpl_disconnect(self.releaser)
self.c.mpl_disconnect(self.follower)
class Example(tk.Frame):
"""Illustrate how to drag items on a Tkinter canvas"""
def __init__(self, parent):
tk.Frame.__init__(self, parent)
fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
ax = fig.add_subplot(111)
ax.plot(t, 2 * np.sin(2 * np.pi * t))
# create the canvas
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.line = DraggableLine(ax, canvas, 0.1)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
我已经用 Bryan-Oakley 的 answer 的修改版本更新了初始脚本。它现在有 2 个 canvas,1 个带有可拖动矩形,1 个带有绘图。如果可能的话,我希望矩形沿着绘图上的 x 轴拖动?
import tkinter as tk # python 3
# import Tkinter as tk # python 2
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
class Example(tk.Frame):
"""Illustrate how to drag items on a Tkinter canvas"""
def __init__(self, parent):
tk.Frame.__init__(self, parent)
fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
fig.add_subplot(111).plot(t, 2 * np.sin(2 * np.pi * t))
#create a canvas
self.canvas = tk.Canvas(width=200, height=300)
self.canvas.pack(fill="both", expand=True)
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# this data is used to keep track of an
# item being dragged
self._drag_data = {"x": 0, "y": 0, "item": None}
# create a movable object
self.create_token(100, 150, "black")
# add bindings for clicking, dragging and releasing over
# any object with the "token" tag
self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
def create_token(self, x, y, color):
"""Create a token at the given coordinate in the given color"""
self.canvas.create_rectangle(
x - 5,
y - 100,
x + 5,
y + 100,
outline=color,
fill=color,
tags=("token",),
)
def drag_start(self, event):
"""Begining drag of an object"""
# record the item and its location
self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def drag_stop(self, event):
"""End drag of an object"""
# reset the drag information
self._drag_data["item"] = None
self._drag_data["x"] = 0
self._drag_data["y"] = 0
def drag(self, event):
"""Handle dragging of an object"""
# compute how much the mouse has moved
delta_x = event.x - self._drag_data["x"]
delta_y = 0
# move the object the appropriate amount
self.canvas.move(self._drag_data["item"], delta_x, delta_y)
# record the new position
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
非常感谢任何帮助。
我不确定如何用 tkinter 或 pyQt 来做,但我知道如何用 PyGame 做这样的事情,这是 python 的另一种 GUI 解决方案。希望这个例子对你有帮助:
import pygame
SCREEN_WIDTH = 430
SCREEN_HEIGHT = 410
WHITE = (255, 255, 255)
RED = (255, 0, 0)
FPS = 30
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
rectangle = pygame.rect.Rect(176, 134, 17, 170)
rectangle_draging = False
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if rectangle.collidepoint(event.pos):
rectangle_draging = True
mouse_x, mouse_y = event.pos
offset_x = rectangle.x - mouse_x
offset_y = rectangle.y - mouse_y
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
rectangle_draging = False
print("Line is at: (", rectangle.x, ";", rectangle.y,")")
elif event.type == pygame.MOUSEMOTION:
if rectangle_draging:
mouse_x, mouse_y = event.pos
rectangle.x = mouse_x + offset_x
rectangle.y = mouse_y + offset_y
screen.fill(WHITE)
pygame.draw.rect(screen, RED, rectangle)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
当你四处移动线条时,控制台会打印它所在的位置。
您的代码的问题是您创建了两个 canvases,一个用于 matplotlib 图,一个用于可拖动的矩形,而您希望两者相同。
为了解决这个问题,我将问题的当前代码与编辑前的代码合并,所以整个 matplotlib 图现在嵌入到 Tkinter 中 window。我对 DraggableLine
class 所做的关键修改是它现在将 canvas 作为参数。
import tkinter as tk # python 3
# import Tkinter as tk # python 2
import numpy as np
import matplotlib.lines as lines
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
class DraggableLine:
def __init__(self, ax, canvas, XorY):
self.ax = ax
self.c = canvas
self.XorY = XorY
x = [XorY, XorY]
y = [-2, 2]
self.line = lines.Line2D(x, y, color='red', picker=5)
self.ax.add_line(self.line)
self.c.draw_idle()
self.sid = self.c.mpl_connect('pick_event', self.clickonline)
def clickonline(self, event):
if event.artist == self.line:
self.follower = self.c.mpl_connect("motion_notify_event", self.followmouse)
self.releaser = self.c.mpl_connect("button_press_event", self.releaseonclick)
def followmouse(self, event):
self.line.set_xdata([event.xdata, event.xdata])
self.c.draw_idle()
def releaseonclick(self, event):
self.XorY = self.line.get_xdata()[0]
self.c.mpl_disconnect(self.releaser)
self.c.mpl_disconnect(self.follower)
class Example(tk.Frame):
"""Illustrate how to drag items on a Tkinter canvas"""
def __init__(self, parent):
tk.Frame.__init__(self, parent)
fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
ax = fig.add_subplot(111)
ax.plot(t, 2 * np.sin(2 * np.pi * t))
# create the canvas
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.line = DraggableLine(ax, canvas, 0.1)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()