0

I am relatively new in programming with python and I'm now trying to master the use of classes and inheritance in Tkinter a bit. In the code here I try to arrange two canvas panels above each other and to place data panel beside these two canvas panels. I tried to do that by defining a leftFrame in which the canvas panels are placed and a rightFrame for the data panel. However, it fails to show both canvas panels. I hope somebody can show me the right way.

import tkinter as tk
class Data():
    def __init__(self):               
        self.borderSize = 8

class Frame():
    def __init__(self, master):
        self.leftFrame = tk.Frame(master)
        self.leftFrame.grid(row=0, column=0)
        self.rightFrame = tk.Frame(master)
        self.rightFrame.grid(row=0, column=1)
        
class CanvasPanel(Frame):
    def __init__(self,master, width, height, row, column, bg=None):
        super().__init__(master)
        self.borderFrame = tk.Frame(self.leftFrame, border = data.borderSize)
        self.borderFrame.grid(row=row, column=column)        
        self.cWidth = width   
        self.cHeight = height         
        self.canvas = tk.Canvas(self.borderFrame, width=self.cWidth, height=self.cHeight,
                               borderwidth = 0, highlightthickness=0, bg=bg)
        self.canvas.pack()      
        self.canvas.create_rectangle(0,0,width, height) 

class DataPanel(Frame):
    def __init__(self, master, width, height, row, column, bg = None):
        super().__init__(master)
        self.borderFrame = tk.Frame(self.rightFrame, border = data.borderSize)
        self.borderFrame.grid(row=row, column=column)
        self.dataFrame = tk.Frame(self.borderFrame, width = width, height = height,bg=bg)
        self.dataFrame.pack()                               

data = Data()
root = tk.Tk()
root.title("PANELS")
canvas1 = CanvasPanel(root,600,300,0,0,'yellow')
canvas2 = CanvasPanel(root,600,300,1,0,'red')
dataPanel = DataPanel(root,100,600,0,0,'light grey')
root.mainloop()
Sjaak
  • 53
  • 1
  • 7
  • Please look at the tkinter documentation first and show us what you have tried so far. e.g. https://docs.python.org/3/library/tkinter.html#the-packer – buhtz Jun 22 '20 at 09:41
  • 1
    Does this answer your question? [tkinter gui layout using frames and grid](https://stackoverflow.com/a/34277295/7414759) – stovfl Jun 22 '20 at 12:50
  • 1
    Relevant [best-way-to-structure-a-tkinter-application](https://stackoverflow.com/questions/17466561) – stovfl Jun 22 '20 at 12:59
  • @stovfl: why did you vote to close this? I don't see anything remotely off-topic here. I wish you would reconsider voting to reopen. – Bryan Oakley Jun 22 '20 at 14:10
  • @buhtz: could you explain why you voted for this question to be closed? I don't see anything wrong with the question, and the OP has no way of knowing what they need to do to improve the question. I would like to encourage you to consder voting to reopen this question. – Bryan Oakley Jun 22 '20 at 14:12
  • @BryanOakley; ***reconsider voting to reopen***: I voted to close as **typo**, the OP is stacking `Frame`s. – stovfl Jun 22 '20 at 14:13
  • @stovfl: yes, they are stacking the frames. They don't understand that and want to know how/why to fix it. It's most definitely not a typo. A typo is accidentally introducing one or two bad characters, not when you simply don't understand how something works. – Bryan Oakley Jun 22 '20 at 14:15
  • @Brian Tompsett - 汤莱恩: I encourage you to consider voting to reopen this question. You and the other closers have given the OP no clues as to what they need to fix, and frankly I don't see anything that needs fixing. – Bryan Oakley Jun 22 '20 at 14:17
  • @BryanOakley, Feel free to not see this as typo. – stovfl Jun 22 '20 at 14:18
  • @stovfl: it is literally not a typo (which is [defined as a misprint or mistake](https://en.wikipedia.org/wiki/Typographical_error)) What misprint or mistake are they supposed to fix? – Bryan Oakley Jun 22 '20 at 14:20
  • I will certainly read the suggested documentation. So many things for it. Sjaak – Sjaak Jun 22 '20 at 15:33
  • @Sjaak since I can't currently write an answer, one misunderstanding you might be having is that each of your panels gets its own `leftFrame` and `rightFrame` - they don't all share the same frames. What's happening is that each of your panels is being put in row 0, column 0 of the root window, so they are stacked. As a good rule of thumb, a class should't call `pack`, `place`, or `grid` on itself. The better way is for the code that creates the panel is the one that adds it to the layout (eg: `foo=CanvasPanel(...); foo.grid(...)`. – Bryan Oakley Jun 22 '20 at 15:42
  • I honestly don't know why this question is closed - you wrote a good question, and it deserves an answer – Bryan Oakley Jun 22 '20 at 15:42
  • I stated in my first comment that the answer could be found in the tkinter documentation. The question is to broad for StackOverflow. It is part of each Tkinter tutorial and introducation how to layout a tkinter gui. – buhtz Jun 22 '20 at 22:19
  • Bryan, thank you very much for your explanation. I have a most in procedural coding. Grasping the ins and outs of OOP takes some time. – Sjaak Jun 23 '20 at 12:56

2 Answers2

0

The red and yellow frames in your code are overlapping with each other. You dont need to write so many classes for such simple example. Placement of frames or other widgets in root window can be easily done via functions using single class. I have edited your code to illustrate the same -

import tkinter as tk

class Frame():
    def __init__(self, master):
        self.borderSize = 8
        self.leftFrame = tk.Frame(master)
        self.leftFrame.grid(row=0, column=0)
        self.rightFrame = tk.Frame(master)
        self.rightFrame.grid(row=0, column=1)

        self.canvas_panel(600, 300, 0, 0, 'yellow')
        self.canvas_panel(600, 300, 1, 0, 'red')
        self.panel_frame(100, 600, 0, 0, 'light grey')


    def canvas_panel(self, width, height, row, column, bg=None):

        self.borderFrame = tk.Frame(self.leftFrame, border=self.borderSize)
        self.borderFrame.grid(row=row, column=column)
        self.cWidth = width
        self.cHeight = height
        self.canvas = tk.Canvas(self.borderFrame, width=self.cWidth, height=self.cHeight,
                                borderwidth=0, highlightthickness=0, bg=bg)
        self.canvas.pack()
        self.canvas.create_rectangle(0, 0, width, height)

    def panel_frame(self,  width, height, row, column, bg=None):

        self.borderFrame = tk.Frame(self.rightFrame, border=self.borderSize)
        self.borderFrame.grid(row=row, column=column)
        self.dataFrame = tk.Frame(self.borderFrame, width=width, height=height, bg=bg)
        self.dataFrame.pack()


root = tk.Tk()
root.title("PANELS")
frame = Frame(root)
root.mainloop()

tan_an
  • 196
  • 1
  • 10
  • I think changing from an OOP design to non-OOP is not what the OP had in mind. This is especially true when they explicitly say they are trying to learn how to use classes and inheritance. – Bryan Oakley Jun 22 '20 at 14:06
  • Thank you for the editing the code. The resulting frame structure is exactly what I had in mind. Yes, it is true, this is a very simple code. However, I just simplified my program to make my problems clear. I aim to add several methods to each panel class which it wil make the code much more complex. Through defining different classes I hope to create a more readable code. However, I still do not understand why my code did not work. – Sjaak Jun 22 '20 at 15:30
  • @Sjaak ***why my code did not work***: You are passing `CanvasPanel(..., 0, 0, ...` which is `row, column` but you dont use this arguments. Change to `class Frame(): def __init__(self, master, row, column): ... .leftFrame.grid(row=row, column=column)` and initalize accordingly: `super().__init__(master, row, column)` – stovfl Jun 22 '20 at 20:38
  • 1
    Stovfl. Your suggestion works fine for the CanvasPanels in the left frame. However, I noticed that arranging the DataPanel at the intended place requires more subframes, which will make my code rather complex for such a simple problem. I’ll follow the code suggestion by tan-an. Many thanks to all for the suggestions and explanation. – Sjaak Jun 23 '20 at 13:14
  • 1
    @Sjaak Read up on [What should I do when someone answers my question?](https://stackoverflow.com/help/someone-answers) – stovfl Jun 23 '20 at 16:42
0

The root of the problem lies in the fact that Frame is putting leftFrame and rightFrame in the master at fixed coordinates. Each time you create a new panel it overlays previously created panels because they all are placed at the same coordinates.

You don't need Frame. Instead, your panels should inherit from tkFrame, and the code that creates the panels should be responsible for putting them in the left or right frame.

For example, the CanvasPanel should look something like this:

class CanvasPanel(BasePanel):
    def __init__(self,master, width, height, bg=None):
        super().__init__(master)
        self.borderFrame = tk.Frame(self, border = data.borderSize)
        self.borderFrame.grid(row=row, column=column)        
        self.cWidth = width   
        self.cHeight = height         
        self.canvas = tk.Canvas(self.borderFrame, width=self.cWidth, height=self.cHeight,
                           borderwidth = 0, highlightthickness=0, bg=bg)
        self.canvas.pack()      
        self.canvas.create_rectangle(0,0,width, height) 

You should make similar changes to DataPanel (ie: placing borderFrame directly in self).

You can now use these classes like you would any other tkinter widget: you first create an instance, and then you add it to the window. You don't need to create a leftFrame or rightFrame, because your code is in control of where the widgets are placed.

root = tk.Tk()

canvas1 = CanvasPanel(root, width=600, height=300, bg='yellow')
canvas2 = CanvasPanel(root, width=600, height=300, bg='red')
dataPanel = DataPanel(root, width=100, height=600, bg='light grey')

canvas1.grid(row=0, column=0, sticky="nsew")
canvas2.grid(row=1, column=0, sticky="nsew")
dataPanel.grid(row=0, column=1, rowspan=2, sticky="nsew")
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685