3
\$\begingroup\$

I have an application using tkinter where different actions are required on mouse click, double click and when the mouse is being moved. The following class works however I wonder if there is no easier- or tkinter builtin way of doing this?

from tkinter import Tk, Canvas


class MouseControl:
    '''  Class for mouse control to establish if there is a 'click',
         'double click' or mouse is being moved '''
    def __init__(self, aw):
        self.double_click_flag = False
        self.button_released_flag = False
        self.aw = aw
        self.aw.bind('<Button-1>', self.clicked)  # bind left mouse click
        self.aw.bind('<Double-1>', self.double_click)  # bind double left clicks
        self.aw.bind('<ButtonRelease-1>', self.button_released)  # bind button release
        self.aw.bind('<B1-Motion>', self.moved)  # bring when mouse is moved

    def clicked(self, event):
        '''  add a little delay before calling action to allow for double click
         and button released to have occurred '''
        self.double_click_flag, self.button_released_flag = False, False
        self.aw.after(300, self.action, event)

    def double_click(self, event):
        '''  set flag when there is a double click '''
        self.double_click_flag = True

    def button_released(self, event):
        '''  set flag when button is released '''
        self.button_released_flag = True

    def moved(self, event):
        '''  define action on when mouse is moved in this case just printing
             the coordinates'''
        print('mouse position is at ({:03}. {:03})'.
              format(event.x, event.y), end='\r')

    def action(self, event):
        '''  define action on click and double click in this case just printing
             the event '''
        if self.button_released_flag:

            if self.double_click_flag:
                print('double mouse click event')

            else:
                print('single mouse click event')


root = Tk()
window = Canvas(root, width=400, height=400, bg='grey')
mouse = MouseControl(window)
window.place(x=0, y=0)
window.mainloop()
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Avoid wildcard imports

You should use import tkinter as tk instead of from tkinter import Tk, Canvas. This is a PEP8 recommendation where you can find the reason of this. Of course, in this case, you have to prefix your widgets with tk. For instance: my_button = tk.Button(...)

Remove useless comments

To be honest, most of your comments (docstrings, to be exact) can be removed. Let us take an example:

def double_click(self, event):
    '''  set flag when there is a double click '''
    self.double_click_flag = True

The docstring is just noisy. It does not bring any additional information when I read self.double_click_flag = True. That comment just repeats what the instruction already tells me. Avoid repeating yourself, even through comments.

The same is true when it comes to inline comments. Example:

self.aw.bind('<Button-1>', self.clicked)  # bind left mouse click

Use the main guard

Sometimes we want to run some code only if the program was used by itself and not when it was imported from another module, for this reason it is good to use if __name__ == "__main__":

def main():
    root = tk.Tk()
    window = tk.Canvas(root, width=400, height=400, bg='grey')
    mouse = MouseControl(window)
    window.place(x=0, y=0)
    window.mainloop()


if __name__ == "__main__":
    main()

Simplify the handlers

I find your handlers having unnecessary code. Simply take advantage of the button events themselves and get rid of all those unnecessary flags which are rather cumbersome in case you want to add more code in the future (because they play the role of global variables, which thing we do not like in programming).

Taking in consideration what has been said so far, here is your code cleaned:

import tkinter as tk


class MouseControl:        
    def __init__(self, canvas):            
        self.canvas = canvas
        self.canvas.bind('<Button-1>', self.clicked)  
        self.canvas.bind('<Double-1>', self.double_click)  
        self.canvas.bind('<ButtonRelease-1>', self.button_released)  
        self.canvas.bind('<B1-Motion>', self.moved)  

    def clicked(self, event):      
        print('single mouse click event at ({}, {})'.format(event.x, event.y))

    def double_click(self, event):
        print('double mouse click event')

    def button_released(self, event):        
        print('button released')

    def moved(self, event):        
        print('mouse position is at ({:03}. {:03})'.format(event.x, event.y), end='\r')    

def main():
    root = tk.Tk()
    window = tk.Canvas(root, width=400, height=400, bg='grey')
    mouse = MouseControl(window)
    window.place(x=0, y=0)
    window.mainloop()


if __name__ == "__main__":
    main()
\$\endgroup\$
5
  • \$\begingroup\$ Thanks for the input, will take it onboard. Still wondered if there is an easier way to makes the distinctions between 'click', 'double click' and 'mouse moving' in tkinter? It took me a while to figure it out especially the trick with the little delay and I couldn't find any explicit guidelines on the matter. \$\endgroup\$ Jul 8, 2018 at 6:18
  • \$\begingroup\$ Check my edit @BrunoVermeulen \$\endgroup\$ Jul 8, 2018 at 8:09
  • \$\begingroup\$ The issue I have with this version is that single_click is called every time, on double click and when mouse is moved, so if I want to do an action on single click it will do this action action also on double click and when mouse is moved. That is the reason I have to little delay to test is double click was occurring or mouse is being moved. \$\endgroup\$ Jul 8, 2018 at 8:15
  • \$\begingroup\$ Yes, that is the caveat of implementing both single and double click (the corresponding bindings will both activate on double click, and their handlers execute) \$\endgroup\$ Jul 8, 2018 at 8:18
  • \$\begingroup\$ Wildcard imports are from ... import *, not from ... import name1, name2. There's nothing wrong with the latter (except perhaps not knowing where the name originally came from). \$\endgroup\$
    – Daniel
    Jul 9, 2018 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.