On tkinter: scrolling multiple widgets simultaneously in different frames
Remove unnecessary imports

You're importing parts of tkinter twice, Remove this line:

from tkinter import Frame, Canvas, Scrollbar, Button


Then, whenever you need to use Frame, Canvas, etc, use tk.Frame, tk.Canvas, etc.

Don't create your own event loop

You have code that looks like this:

while True:
    &lt;some code&gt;
    root.update()


Instead, call mainloop once, move &lt;some code&gt; into a function, and call it with after if you need it to run in a loop.

def redraw():
    &lt;some code&gt;
    root.after(50, redraw)
root.mainloop()


Don't continually delete and recreate items on a canvas

You're continually deleting and recreating identical items on a canvas. This is inefficient and will eventually call tkinter to crash. The canvas widget does not re-use canvas ids, so each time you create a new object you get a new id. There is a limited number of ids that tkinter can manage.

Instead, create the items once, and then if you need to change them, change the existing items

def draw():
    global block1, block2
    block1 = canvas1.create_rectangle(100, 100, size_x, size_y,
                                      fill='orange')
    block2 = canvas2.create_rectangle(100, 100, size_x, size_y,
                                      fill='blue')

def redraw():
    global block1, block2
    canvas1.itemconfigure(block1, ...)
    canvas2.itemconfigure(block2, ...)


Group your layout code together

Intermixing widget creation and widget layout makes the code hard to read.

Consider this:

frame1 = Frame(root, bg='grey')
frame1.grid(row=0, column=0)
frame2 = Frame(root, bg='grey')
frame2.grid(row=0, column=1)
frame3 = Frame(root, bg='grey')
frame3.grid(row=0, column=2)


I recommend splitting this into two blocks:

frame1 = Frame(root, bg='grey')
frame2 = Frame(root, bg='grey')
frame3 = Frame(root, bg='grey')

frame1.grid(row=0, column=0)
frame2.grid(row=0, column=1)
frame3.grid(row=0, column=2)


I think this makes the code much easier to read because I can see at a glance that you're creating three frames, and that each frame is going into a separate column. In the original code it's not nearly as easy to see this without closely reading the code.

Use separate functions for scrolling in x and y direction

Your use of an argument to specify x-axis or y-axis makes the code a little bit harder to understand. Instead, I recommend using two separate functions.

For example:

def scroll_x(*args):
    global canvas1, canvas2
    canvas1.xview(*args)
    canvas2.xview(*args)

def scroll_y(*args):
    global canvas1, canvas2
    canvas1.yview(*args)
    canvas2.yview(*args)
...
yscrollbar = Scrollbar(frame3, orient='vertical', command=scroll_y)
xscrollbar = Scrollbar(frame3, orient='horizontal', command=scroll_x)


Notice how it makes the code much easier to read, since we can replace the complicated lambda with a much simpler command.