walexia
walexia

Reputation: 171

Tkinter Mousewheel action for scrolling on canvas

I am new to Python and I have an issue adding mouse scrolling action to my canvas. I have a vertical scrollbar. The scrollbar works fine when I manually scroll it, or mouse over it and roll my mousewheel. My issue is that I would like to be able roll my mousewheel on my canvas or even just my frame and have the contents inside it scroll as long as my mouse is hovering over it.

I have spent hours looking at all the similar questions related to this here on stackoverflow, and none of my modifications seem to work. I am currently getting a weird error that I cannot rectify. Due to my modifications, the error ONLY shows up when I start scrolling, however it does not cause a crash. The error is:

Exception in Tkinter callback
    Traceback (most recent call last):
        File "C:\Users\twaku\Anaconda3\lib\tkinter\__init__.py", line 1699, in __call__
                return self.func(*args)
        File "C:/Users/twaku/PycharmProjects/DCSui/FileToSubmit.py", line 152, in _on_mousewheel
                self.canvas.yview_scroll(int(-1 * (event.delta / 120), "units"))
        TypeError: 'str' object cannot be interpreted as an integer

Now if I remove the int typecast I get this error:

Exception in Tkinter callback
    Traceback (most recent call last):
        File "C:\Users\twaku\Anaconda3\lib\tkinter\__init__.py", line 1699, in __call__
                return self.func(*args)
        File "C:/Users/twaku/PycharmProjects/DCSui/FileToSubmit.py", line 156, in _on_mousewheel
                self.canvas.yview_scroll(-1 * (event.delta / 120), "units")
        File "C:\Users\twaku\Anaconda3\lib\tkinter\__init__.py", line 1745, in yview_scroll
                self.tk.call(self._w, 'yview', 'scroll', number, what)
            _tkinter.TclError: expected integer but got "1.0"

So now I am stuck going back and forth between the two errors.

Here is my code:

from tkinter import *
import tkinter as tk
import time
import xlrd


root = Tk()


root.state('zoomed')  # full screen -windowed

# ------------------INTRODUCTION BLOCK--------------
f1 = Frame(root, width=900, height=700, relief=SUNKEN)
f1.grid_rowconfigure(1, weight=1)
f1.grid_columnconfigure(2, weight=1)
f1.pack(fill=BOTH, expand=1, side=BOTTOM)

root.title("Diagram Scroll Test")

Tops = Frame(root, width=1600, height=50, relief=SUNKEN)
Tops.pack(side=TOP)

# ------------------TIME--------------
localtime = time.asctime(time.localtime(time.time()))
# -----------------INFO TOP------------
lblinfo = Label(Tops, font=('aria', 30, 'bold'), text="My Diagram Scroll Test",
                fg="steel blue", bd=10, anchor='w')
lblinfo.grid(row=0, column=0)
lblinfo = Label(Tops, font=('aria', 20,), text=localtime, fg="steel blue", anchor=W)
lblinfo.grid(row=1, column=0)
lblinfo = Label(Tops, font=('aria', 15, 'bold'), text="Please help", fg="steel blue", bd=10,
                anchor='w')
lblinfo.grid(row=2, column=0)

# ------------------CANVAS DEFINITION-------------

class CanvasDemo(Frame):


    def __init__(self,root):
        Frame.__init__(self,root)


        self.canvas = tk.Canvas(root, borderwidth=0)
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
        self.frame = tk.Frame(self.canvas)
        self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.vsb.set)
        root.state('zoomed')
        self.vsb.pack(side="right", fill="y")
        self.canvas.config(width=root.winfo_screenwidth(), height=root.winfo_screenheight())
        # self.canvas.pack(side="left", fill="both", expand="1")
        self.canvas.pack(fill="both", expand="1")
        self.canvas.create_window((4, 4), window=self.frame, anchor="nw",
                                    tags="self.frame")

        self.frame.bind("<Configure>", self.onFrameConfigure)

        self.populate()

    # ------------------CODE TO CREATE BLOCK DIAGRAMS-------------

    def populate(self):


        i = 0
        turnCount = 0  # Keeps track of how many boxes is used to trigger a turn

        # Create Small starter box
        lineVarx1 = 70
        lineVary1 = 50
        lineVarx2 = 120
        lineVary2 = 50

        varx1 = 120
        vary1 = 25
        varx2 = 220
        vary2 = 75

        varblk = 1
        varline = 1

        self.canvas.create_rectangle(20, 40, 70, 60, fill="green", tags="start")



        while i < 200:  # Provides 104 blocks

            # ------------------IF STATEMENT TO CONTROL WHEN DIAGRAM TURNS-------------

            if turnCount == 12:  # At Turn Point, initiating turn sequence
                lineVarx2 = lineVarx2 - 25
                self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")
                # Downward line
                lineVarx1 = lineVarx2
                lineVary1 = lineVary2
                lineVary2 = lineVary2 + 50
                self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")

                # long line to left
                lineVarx1 = lineVarx2
                lineVary1 = lineVary2
                lineVarx2 = lineVarx2 - 1825
                self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")

                # Downward line
                lineVarx1 = lineVarx2
                lineVary1 = lineVary2
                lineVary2 = lineVary2 + 50
                self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")

                lineVary1 = lineVary2
                lineVarx2 = lineVarx2 + 50

                varx1 = lineVarx2
                vary1 = lineVary2 - 25
                varx2 = lineVarx2 + 100
                vary2 = lineVary2 + 25
                turnCount = 0

            self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")
            self.canvas.create_rectangle(varx1, vary1, varx2, vary2, fill="bisque", tags="r1")
            self.canvas.create_text(varx1 + 20, vary1, fill="darkblue", anchor=NW,
                                    text="Hi")
            self.canvas.create_text(varx1 + 20, vary1 + 10, fill="darkblue", anchor=NW,
                                    text="bye")
            self.canvas.create_text(varx1 + 20, vary1 + 20, fill="darkblue", anchor=NW,
                                    text="fly")
            lineVarx1 = lineVarx1 + 150
            lineVarx2 = lineVarx2 + 150
            varx1 = varx1 + 150
            varx2 = varx2 + 150
            i += 1
            turnCount += 1

        # "End" Block
        lineVarx1 = varx2 - 150
        lineVarx2 = lineVarx1 + 50

        varx1 = lineVarx2
        vary1 = lineVary1 - 10
        varx2 = varx1 + 50
        vary2 = lineVary1 + 10

        self.canvas.create_line(lineVarx1, lineVary1, lineVarx2, lineVary2, arrow="last", tags="to_r1")
        self.canvas.create_rectangle(varx1, vary1, varx2, vary2, fill="red", tags="r1")






    def _on_mousewheel(self, event):
            self.canvas.yview_scroll(-1 * (event.delta / 120), "units")



    def onFrameConfigure(self, event):
        '''Reset the scroll region to encompass the inner frame'''
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))



def qexit():
    root.destroy()


btnexit = Button(f1, padx=16, pady=7, bd=10, fg="black", font=('ariel', 12, 'bold'), width=8, text="EXIT",
                    bg="powder blue", command=qexit)
btnexit.grid(row=15, column=2)





canvas = CanvasDemo(root)
canvas.pack()

mainloop()

Upvotes: 2

Views: 3622

Answers (1)

figbeam
figbeam

Reputation: 7176

When you divide the result is a float. Try with integer division instead:

def _on_mousewheel(self, event):
    self.canvas.yview_scroll(-1 * (event.delta // 120), "units")
                                        here:--^

Now, you may want to check which widget the mouse pointer is hovering above because the _on_mousewheel() function is invoked when the mouse pointer hovers on the scrollbar, making it scroll twice as fast.

Upvotes: 5

Related Questions