Python Forum
"Interactive" figure in a GUI
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
"Interactive" figure in a GUI
#1
Hello

I´ve written the code below, which creates a figure with two subplots. Each subplot has a "draggable line" that returns the x-value when released. Is it possible to implement this draggable line in a GUI using a framework such as Tkinter or PyQT? So far, I´ve only managed embed the plots and lines in a GUI but the "Interactive" part is lost.

Any help is greatly appreciated

import matplotlib.pyplot as plt
import matplotlib.lines as lines
import numpy as np
import random

class draggableline:
    def __init__(self, ax, XorY):
        self.ax = ax
        self.c = ax.get_figure().canvas
       
        self.XorY = XorY

        x = [XorY, XorY]
        y = [-1, 10]
        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]

        print (self.XorY)

        self.c.mpl_disconnect(self.releaser)
        self.c.mpl_disconnect(self.follower)
        

class draggableline2:
    def __init__(self, ax2, XorY):
        self.ax2 = ax2
        self.c = ax2.get_figure().canvas
       
        self.XorY = XorY

        x = [XorY, XorY]
        y = [-1, 10]
        self.line = lines.Line2D(x, y, color='red', picker=5)
        self.ax2.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]

        print (self.XorY)

        self.c.mpl_disconnect(self.releaser)
        self.c.mpl_disconnect(self.follower)
        

fig, (ax, ax2) = plt.subplots(2)
ax.plot([0, 1, 2, 3, 4, 5], [1, 1, 6, 4, 6, 2])
ax2.plot([0, 1, 2, 3, 4, 5], [1, 1, 2, 4, 2, 1])
Tline = draggableline(ax, 0.1)
Tline2 = draggableline2(ax2, 0.1)
plt.show()
Reply
#2
In tkinter you can bind canvas items (such as lines) to events, such as a mouse click by using the canvas.tag_bind() method. From there you can implement dragging canvas items on the canvas. I haven't written such code for a long time but I know it is possible.
Reply
#3
I´ve updated the initial script with a modified version of Bryan-Oakley´s answer here stackoverflow.com/a/6789351/7432 It now has 2 canvas. 1 with the draggable rectangle and 1 with the plot. I would like the rectangle to be dragged along the x-axis on the plot if that is possible?

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()
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] Matplotlib figure not showing cursor coordinates in the figure window mart79 0 2,052 Mar-26-2020, 01:48 PM
Last Post: mart79
  python interactive output with Qt4 eegrek39 8 4,951 Mar-18-2018, 08:41 PM
Last Post: eegrek39

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020