Is there a way to reduce the amount of times the canvas.create_rectangle method appears in my code? (DRY Principle)

Viewed 20

So I created a Tkinter app, with 3 frames. The user interface is designed with Figma, so the objects are explicitly called.

I managed to reduce the canvas object being explicitly called by creating a top-level class that inherits from Canvas.

class MyCanvas (Canvas):
    def __init__(self, *args, **kwargs):
        Canvas.__init__(self, *args, **kwargs)
        self['bg'] = "#FFFFFF",
        self['height'] = 519,
        self['width'] = 862,
        self['bd'] = 0,
        self['highlightthickness'] = 0,
        self['relief'] = "ridge"
        self.place(x=0,y=0)

This is one of the frames:

class HomePage (tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        canvas = MyCanvas(self)

Now, the code after this is:

canvas.create_rectangle(
            0.0,
            0.0,
            587.0,
            519.0,
            fill="#00A4D2",
            outline="")

This block of code appears three times (I have 3 frames). Is there a way to reduce the repetition of this?

Different methods I tried:

  1. @classmethod
@classmethod
        def Createrect(cls):
            self.create_rectangle(0.0,
            0.0,
            587.0,
            519.0,
            fill="#00A4D2",
            outline="")

Then calling that method after the

canvas = MyCanvas(self)

Didn't work.

  1. Creating another top level class
class createrect(MyCanvas)
def createrect1():
MyCanvas.create_rectangle(.0,
            0.0,
            587.0,
            519.0,
            fill="#00A4D2",
            outline="")

Another trial and error, that obviously didn't hit.

Thanks for your future help!

Right now the app is working, but it is 600+ lines. Initially it was 900 (lol) but due to me discovering the Inheritance concept, it was reduced to 600+. Still I believe it can be reduced further. And, my apologies if the code is not robust, this is my first Python app.

I hope I gave all the necessary details. Feel free to comment if additional details are needed.

2 Answers

I am not sure if you are overthinking or I am underthinking :)

Option 1: Just create a regular method in MyCanvas to call it multiple times. Only works for the current canvas.

class MyCanvas(Canvas):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self['bg'] = "#FFFFFF",
        self['height'] = 519,
        self['width'] = 862,
        self['bd'] = 0,
        self['highlightthickness'] = 0,
        self['relief'] = "ridge"
        self.place(x=0, y=0)
        
        self.create_rect()
        self.create_rect()
        self.create_rect()

    def create_rect(self):
        self.create_rectangle(0.0, 0.0, 587.0, 519.0, fill="#00A4D2", outline="")

Option 2: Have a MainCanvas that holds the reusable methods

class MainCanvas(Canvas):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def create_rect(self):
        self.create_rectangle(0.0, 0.0, 587.0, 519.0, fill="#00A4D2", outline="")


class MyCanvas(MainCanvas):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self['bg'] = "#FFFFFF",
        self['height'] = 519,
        self['width'] = 862,
        self['bd'] = 0,
        self['highlightthickness'] = 0,
        self['relief'] = "ridge"
        self.place(x=0, y=0)

        self.create_rect()
        self.create_rect()
        self.create_rect()

rectangle items are expected as one of the form:

pathName create rectangle x1 y1 x2 y2 ?option value ...?
pathName create rectangle coordList ?option value ...?

So all is needed to create an rectangle are a list of coordinates or as written out positional arguments. After those coordinates you have a variety of optional keyword arguments. With this in mind you can easily do something like:

coords = [x1,y1,x2,y2]
options= {
    'fill':"#00A4D2", 
    'outline':""}
[self.create_rectangle(*coords,**options) for i in range(4)]

This code uses the python feature unpacking and a list comprehension. Also note that I did not use self. as those variables seems pointless to keep in my opinion and get garbage collected.

Short enough ?

Related