1

I am trying to create an interface with tkinter. It should have a control panel on the left, and a set of 3 tabs on the right.

The tabs will show images, which may be too large for the screen, however the exact size will only be known after they have been dynamically created by the program. So I want to have a Notebook that expands to fill the top window, and if the image is too large for the notebook frame, scrollbars should appear to allow for all the image to be seen. I can't find a way to attach scrollbars to the notebook, so I have attached them to the canvases inside the notebook.

My code below expands the canvas to fit the image, but the scrollbar is useless as it stretches to fit the canvas, which is larger than the frame, and so can end up outside the window.

I think I want to set the scrollregion to the size of the containing frame. Dynamically changing scrollregion of a canvas in Tkinter seems to be related, but I can't work out how to apply that to my code:

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk


class App(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        self.parent = parent
        super().__init__(parent, *args, **kwargs)
        self.pack(expand=True, fill=tk.BOTH)
        self.inputframe = ttk.Frame(self)
        self.inputframe.pack(side=tk.LEFT, expand=False, fill=tk.Y)
        self.outputnotebook = ttk.Notebook(self)
        self.outputnotebook.pack(side=tk.RIGHT, expand=True, fill=tk.BOTH)
        self.build_inputframe()
        self.build_outputnotebook()

    def build_inputframe(self):
        run_button = ttk.Button(self.inputframe, text="Run", command=self.draw)
        run_button.grid(column=2, row=0, sticky=(tk.W, tk.E))
        # rest of app...

    def build_outputnotebook(self):
        actnet_frame = ttk.Frame(self.outputnotebook)
        critnet_frame = ttk.Frame(self.outputnotebook)
        xscrollbar = ttk.Scrollbar(actnet_frame, orient=tk.HORIZONTAL)
        xscrollbar.grid(row=1, column=0, sticky=tk.E + tk.W)
        self.outputnotebook.add(
            actnet_frame,
            text="Tab 1",
            sticky=tk.N + tk.S + tk.E + tk.W)
        self.outputnotebook.add(critnet_frame, text="Tab 2")
        self.actnet_canvas = tk.Canvas(actnet_frame, width=400, height=400,
                                       xscrollcommand=xscrollbar.set)
        self.actnet_canvas.grid(row=0, sticky=tk.N + tk.S + tk.E + tk.W)
        xscrollbar.config(command=self.actnet_canvas.xview)

    def draw(self):
        act_image = Image.open('critnet.png') # image is 875 x 175 
        width, height = act_image.size
        actphoto = ImageTk.PhotoImage(act_image)
        self.actnet_canvas.delete("all")
        self.actnet_canvas.create_image(0, 0, anchor=tk.NW, image=actphoto)
        self.actnet_canvas.image = actphoto
        self.actnet_canvas['scrollregion'] = (0, 0, width, height)
        self.actnet_canvas['width'] = width
        self.actnet_canvas['height'] = height
        #do similar for other tabs.

root = tk.Tk()
root.geometry("800x600")
app = App(root)
app.mainloop()
Community
  • 1
  • 1
James K
  • 3,692
  • 1
  • 28
  • 36

1 Answers1

3

The problem in your code is that you resize the canvas at the size of the picture, so the size of the canvas and of the scrollregion are the same. And it makes your canvas to big for your tab and your scrollbar useless.

I suppressed the lines

self.actnet_canvas['width'] = width
self.actnet_canvas['height'] = height

from the draw function and the scrollbar behaved as expected.

Edit: I had missed the other part of the question. To resize the canvas and scrollbar with the window, I added the following lines in build_outputnotebook:

actnet_frame.rowconfigure(0,weight=1)
actnet_frame.columnconfigure(0,weight=1)
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • That certainly helps. The scroll bar works, can you also answer the part about making the canvas expand or shrink to the size of the frame. If the window is resized, the scrollbars get hidden. – James K Jul 19 '16 at 19:41