2017-01-29 10 views
1

私はTkinterとmatplotlibを使ってイメージクリッピングツールを作成しています。画像は、Tkinterキャンバスに埋め込まれたmatplotlib(FigureCanvasTkAgg)を使用して表示されます。ユーザーは、画像上に4点を置くことができます。 Shiftキーを押しながらマウスを左クリックするとポイントが置かれます。マウスを右クリックするとポイントが削除されます。 4点を接続するポリゴンが描画されます。ユーザーは、Tkinter Spinboxウィジェットの値を変更することによって、ポリゴンの各辺を押し出すことができます。Matplotlib Tk Spinboxウィジェットを使用した後、FigureCanvasTkAggがキー押しを検出しない

ここに問題があります。 Spinboxの入力ボックス部分が使用されている場合、キャンバス/ matplotlibのFigure/axisからフォーカスが移動します。マウスの左クリックは検出されますが、シフトキーの押下が検出されなくなったので、これ以上ポイントを設定することはできません!

ほとんどの不要なコードを削除しようとしました。 イメージファイルへのパスは205行目でハードコードされていますので、これを変更する必要があります。

# -*- coding: utf-8 -*- 
""" 
Created on Wed Jan 18 20:28:52 2017 

Sample of a script which is used to crop an image at user placed points. 
The user will place 4 points on the displayed image. 
A quadrangle will then be created by joining the 4 points. 
Each side of the quadrangle can be extruded by changing the 'buffer values'. 

Instructions: 
Hold shift and left click on image to place a new point. 
Right click to delete a point. 
""" 
from __future__ import division 

import Tkinter as Tk 

# Matplotlib (mpl) - for plotting data/arrays/images. 
import matplotlib 
matplotlib.use('TkAgg') 

from matplotlib import figure 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg 
from matplotlib.backend_bases import key_press_handler 


class MyApp(Tk.Frame): 
    def __init__(self, master=None): 
     self.master = master 
     # Drawing points and lines class 
     self.Vectors = Vectors(parent_class=self) 

     Tk.Frame.__init__(self, master) 
     self.master.wm_title("Sample Script!") 
     # Position of top-left corner of the app. (from left side of screen, from top of screen). 
     self.master.geometry("+100+10") 

     self.frame1 = Tk.Frame(self.master) # Canvas 1 
     self.frame1.pack(side=Tk.LEFT) 

     self.frame2 = Tk.Frame(self.master) # Buttons 
     self.frame2.pack(side=Tk.BOTTOM) 

     self.fig = figure.Figure(dpi=110)#plt.figure() 
     self.ax1 = self.fig.add_subplot(1,1,1) 
     self.ax1.set_aspect('equal', 'datalim') 
     self.ax1.grid(True) 
     self.ax1.autoscale(True) 

     self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame1) 
     self.canvas.show() 
     self.canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) 

     # Events 
     # keyboard events on the canvas (image) 
     self.canvas.mpl_connect('key_press_event', self.on_canvas_key_press) 
     # mouse click events on the canvas (image/plot) 
     self.canvas.mpl_connect("button_press_event", self.on_button_canvas1) 
     # clicking on drawn artists events: 
     self.canvas.mpl_connect('pick_event', self.on_pick_canvas1) 

     # Mouse Events 
     self.canvas.mpl_connect('figure_enter_event', self.enter_figure) 
     self.canvas.mpl_connect('figure_leave_event', self.leave_figure) 
     self.canvas.mpl_connect('axes_enter_event', self.enter_axes) 
     self.canvas.mpl_connect('axes_leave_event', self.leave_axes) 

     self.cursor_x = None 
     self.cursor_y = None 
     # targest lines on ax1 at cursor position. 
     self.ax1_hline = None 
     self.ax1_vline = None 

     # add the matplotlib navigation toolbar, connected to the canvas. 
     self.add_toolbar() # add the Navigation toolbar for this canvas 

     # add the GUI for editing the rectangle buffer. 
     self.buffer_settings(self.frame2) 

     # Buttont which displays the test image. 
     Button_TestImage = Tk.Button(self.frame2, 
            text="test_disp_image()", 
            command = self.test_disp_image) 
     Button_TestImage.pack() 

    def add_toolbar(self): 
     """ Add the matplotlib inbuilt Navigation toolbar 
     http://matplotlib.org/users/navigation_toolbar.html """ 
     #self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.frame1) 
     self.toolbar = CustomToolbar(self.canvas, self.frame1)   

    def buffer_settings(self, frame): 
     """ GUI for setting buffer distance for each line around clipping 
     quadrangle. """ 
     self.buf_frame = Tk.Frame(frame) 
     self.buf_frame.pack(side=Tk.LEFT) 

     self.spinbox_frame = Tk.Frame(frame) 
     self.spinbox_frame.pack(side=Tk.RIGHT) 

     # Buffer settings: 
     self.buf_N = Tk.IntVar() 
     self.buf_N.set(50) 
     self.buf_N.trace('w', self.buff_callback) 

     self.buf_E = Tk.IntVar() 
     self.buf_E.set(140) 
     self.buf_E.trace('w', self.buff_callback) 

     self.buf_S = Tk.IntVar() 
     self.buf_S.set(50) 
     self.buf_S.trace('w', self.buff_callback) 


     self.buf_W = Tk.IntVar() 
     self.buf_W.set(250) 
     self.buf_W.trace('w', self.buff_callback) 

     # spinbox selector for each buffer selection. 
     n = Tk.Spinbox(self.spinbox_frame, from_=-1000, to=1000, 
         textvariable=self.buf_N) 
     n.grid(row=1, column=1) 
     w = Tk.Spinbox(self.spinbox_frame, from_=-1000, to=1000, 
         textvariable=self.buf_W) 
     w.grid(row=2, column=0) 
     e = Tk.Spinbox(self.spinbox_frame, from_=-1000, to=1000, 
         textvariable=self.buf_E) 
     e.grid(row=2, column=2) 
     s = Tk.Spinbox(self.spinbox_frame, from_=-1000, to=1000, 
         textvariable=self.buf_S) 
     s.grid(row=3, column=1) 


    def buff_callback(self, *args): 
     print "variable changed!" 
     self.update_polygon() 

    def update_polygon(self): 
     """ update buffered polygon when buffer distance is changed""" 
     # TODO - update polygon using adjusted bufer values 
     # Get buffer spinbox values 
     n, e, s, w = self.get_buffer_settings() 
     print "n, e, s, w", n, e, s, w 

    def get_buffer_settings(self): 
     """ get the buffer spinbox values """ 
     n = self.buf_N.get() 
     e = self.buf_E.get() 
     s = self.buf_S.get() 
     w = self.buf_W.get() 
     return (n, e, s, w) 

    def on_canvas_key_press(self, event): 
     print "on_canvas_key_press()" 
     key_press_handler(event, self.canvas, self.toolbar) 

    def on_pick_canvas1(self, event): 
     """ 
     This func called when an artist is picked on canvas 1 
     i.e. clicked on a point. 
     If multiple overlapping artists are picked, this func will be call 
     for each one.""" 
     print "on_pick_canvas1()" 
     if event.mouseevent.inaxes is not None and not hasattr(event, 'already_picked'): 
      mouseevent = event.mouseevent 

      if mouseevent.button == 3: 
       # delete point. 
       print "delete point!" 
       for saved_point_artist in self.Vectors.points: 
        if saved_point_artist.contains(mouseevent)[0] == True: 
         # remove the point from the canvas 
         saved_point_artist.remove() 
         # delete the saved artist from the list 
         self.Vectors.points.remove(saved_point_artist) 
         # Update the canvas 
         self.canvas.draw() 
     return 

    def on_button_canvas1(self, event): 
     """ mouse button events on the canvas. 
     http://matplotlib.org/api/backend_bases_api.html#matplotlib.backend_bases.MouseEvent""" 
     print "on_button_canvas1()" 
     button = event.button 
     key = event.key 
     print "button, key", button, key 
     if event.inaxes is not None: 
      # image pixel position (centre of pixel) 
      x, y = (event.xdata, event.ydata) 
      # offset to corner of pixel 
      x, y = x + 0.5, y + 0.5 
      print x,y 
      print('button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % 
       (event.button, event.x, event.y, event.xdata, event.ydata,)) 
      # process left click 
      if button == 1: 
       # hold shift to draw a point. 
       if key == 'shift': 
        self.Vectors.new_box_point(x, y) 
        self.canvas.draw() 
     return 

    def test_disp_image(self): 
     """ read and display a .jpg image file.""" 
     img_path = r"C:\Scripts\parade_dog.jpg" 
     # read image 
     self.im = matplotlib.image.imread(img_path) 
     self.ax1.imshow(self.im) 
     self.canvas.draw() 

    def enter_axes(self, event): 
     print('enter_axes', event.inaxes) 

    def leave_axes(self, event): 
     print('leave_axes', event.inaxes) 

    def enter_figure(self, event): 
     print('enter_figure', event.canvas.figure) 

    def leave_figure(self, event): 
     print('leave_figure', event.canvas.figure) 


class Vectors: 
    def __init__(self, parent_class): 
     self.parent = parent_class # the plotting class 
     self.points = [] 

    def new_box_point(self, x, y): 
     """ master new point funtion""" 
     # draw a point on the canvas, and save point to list. 
     point_artist = self.draw_point(x, y) 
     self.points.append(point_artist) 

    def draw_point(self, x, y): 
     centre = (x,y) 
     fluro_green = (0.271, 0.99, 0.271)# equivalent to (70,250, 70) 
     point = matplotlib.patches.Circle(xy=centre, radius=5, 
              edgecolor=fluro_green, 
              facecolor=fluro_green, 
              fill=True, 
              visible=True, 
              picker=15) 
     self.parent.ax1.add_patch(point) 
     return point # return the artist. 


class CustomToolbar(NavigationToolbar2TkAgg): 
    def __init__(self,canvas_,parent_): 
     self.toolitems = (
      ('Home', "Reset original view\n\ 
      ('h' or 'r')", 'home', 'home'), 
      ('Back', "previous view\n\ 
      ('c' or 'left arrow' or 'backspace')", 'back', 'back'), 
      ('Forward', "next view\n\ 
      ('v' or 'left arrow')", 'forward', 'forward'), 
      (None, None, None, None), 
      ('Pan', "Pan axis (left mouse), Zoom (right mouse).\n\ 
      ('p')", 'move', 'pan'), 
      ('Zoom', "Zoom box\n\ 
      ('o')", 'zoom_to_rect', 'zoom'), 
      (None, None, None, None), 
#   ('Subplots', 'Configure subplots axis', 'subplots', 'configure_subplots'), 
      ('Save', 'Save plot', 'filesave', 'save_figure'), 
      ) 
     NavigationToolbar2TkAgg.__init__(self,canvas_,parent_) 

if __name__ == "__main__": 
    app = MyApp() 
    app.mainloop() 

答えて

1

ここで不足している成分は、キャンバスにエントリウィジェットからフォーカスを変更するために、私たちにfocus_force()機能を私を許可キャンバスウィジェットを、アクセスするためにget_tk_widget()を使用していました。

self.fig.canvas.get_tk_widget().focus_force() 
関連する問題