3
\$\begingroup\$

I am making a quiz app using tkinter in Python. I will be having a database that will have questions and options to display in application. Is it good to structure the program as shown, or should it be re-structured in some other way?

import tkinter as tk

def iQuiz(source,row=0,column=0,rowspan=None,columnspan=None):
    storeObj=tk.Frame(source,bg="powder blue")
    storeObj.grid(row=row,column=column,rowspan=rowspan,columnspan=columnspan)
    return storeObj

def button(source,text,command=None,row=0,column=0,rowspan=None,columnspan=None):
    storeObj=tk.Button(source,text=text,command=command)
    storeObj.grid(row=row,column=column,rowspan=rowspan,columnspan=columnspan)
    return storeObj

def radioButton(source,text,indicator='off',command=None,row=0,column=0,rowspan=None,columnspan=None):
     storeObj=tk.Radiobutton(source,text=text,indicator=indicator,command=command)
    storeObj.grid(row=row,column=column,rowspan=rowspan,columnspan=columnspan)
    return storeObj

def label(source,text,row=0,column=0,rowspan=None,columnspan=None):
    storeObj=tk.Label(source,text=text)
    storeObj.grid(row=row,column=column,rowspan=rowspan,columnspan=columnspan)
    return storeObj





class Application(tk.Frame):

    def __init__(self,title):
        tk.Frame.__init__(self)
        self.master.title(title)
        self.option_add('*Font','aerial 20 bold')
        self.pack(expand=tk.YES, fill=tk.BOTH)

        #widget for quiz app
        frame=iQuiz(self,column=1)
        frame.config(width=1000,height=500)
        frame.grid_propagate(0)
        que_label=label(frame,"question is here",row=1)
        opt1=radioButton(frame,"Option A is here",row=2)
        opt2=radioButton(frame,"Option B is here",row=3)
        opt3=radioButton(frame,"Option C is here",row=4)
        opt4=radioButton(frame,"Option D is here",row=5)
        ans_label=label(frame,"\nanswer will be shown on click of show me answer",row=6)
        answer=button(frame,"Show me answer",row=7)


        frame_pre=iQuiz(self,column=0)
        pre=button(frame_pre,'Previous')
        pre.config(bg='Powder blue',fg='white')

        frame_next=iQuiz(self,column=2)
        nxt=button(frame_next,'Next')
        nxt.config(bg='powder blue',fg='white')












if __name__=='__main__':
    Application('Quiz App').mainloop()
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Why is there some much white space? \$\endgroup\$ Commented Apr 4, 2017 at 2:58

1 Answer 1

3
\$\begingroup\$

There are many things to discuss about your code and I have two options to do do that:

  1. Redesign your program by applying a suitable design pattern.
  2. Obey to my legendary laziness and point out only the minimum things you should care about.

For some considerations, I chose the second option. So here are some random notes you should ponder about:

Do not confuse me

Before diving in any details, I see a bunch of functions and a class. That was the first thing that attracts my attention. This is confusing because it does not let the maintainer of your code to understand the ideology of your program and whether you belong to the OOP or procedural sects.

Opting only for one of those programming types will help to understand the flow logic of your program. This will also reflect you are not versatile but self-confident and a good decision maker.

As developers, we need to take a set of decision before coding anything. This is a big chance for us to train ourselves to take the right decisions. And the decision regarding which programming type to adopt for your program is not that difficult. Your program needs to comply to an OOP approach because:

  • You are designing a GUI, so do not forget that the leaves of history inform us that object-oriented concept is the legitimate child of the simulation and graphical design.
  • Python is FCF programming language. So be pythonic by adopting OOP since even functions are objects in this language.

We do not need to be more gossip because this is already convincing on why you should write those functions in a proper class instead.

You can be monadic

Let me pick this function, for instance:

button(source,text,command=None,row=0,column=0,rowspan=None,columnspan=None)

Parameters are heretics that must be used parsimoniously because they are a different level of abstraction. The ideal is, when possible, to use monadic functions; and in your situation this is possible because row, column, columnspan and rowspan are already options of the grid() geometry manager, so it is useless1 to inject them as parameters to the enclosing functions. For the command and bg parameters,you also can inject them directly as an option to the widgets you need to use it for (tkinter.Button() in this function)

Container's type as a name: bad!

You used function names such as button() and label() while you already have widget types which names are Button() and Label(). Do not use the container's type as a name for your functions. This is a source of confusion as stated by Robert C. Martin in his book Clean code.

Function names must divulge the function's purpose:

What about this function name you used: iQuiz()? You should rename this function to be informative in that if Donald Trump reads the name of that function he can instantly understand its purpose.

Choose the right names for variables

Your program is small, so maybe I can be enough smart and patient to guess, but also remember, why these variables are useful for: opt1, opt2, opt3, opt4. As your program grows, these variables will make your source code unclear and thus difficult to maintain. Also, if I am a mathematician, I may tend to understand that opt stands for optimum or anything else you do not think.

Code a lighter initializer

You are doing too much things in the initializer __init__(). Initialize only the necessary things and delegate everything else to other functions.

init() takes a useless argument

The string argument you provide to init() is a bad decision. A good practice is to use as less parameters as possible. Plus, you can simply set the title of your GUI in a separate configuration function like this one:

def configure_gui(self):
    self.window_height = 500
    self.window_width = 1000
    self.background_color = 'powder blue'
    self.master.title('Quiz')
    self.master.option_add('*Font','aerial 12')
    self.master.config(width=self.window_width, height=self.window_height) 

and use this configuration function within __init__():

def __init(self, master):
   self.master = master
   self.configure_gui()

Improve readability

Put spaces between the the coma and the option in all similar situations to this one:

storeObj.grid(row=row,column=column,rowspan=rowspan,columnspan=columnspan)

Put space also for your variables. I mean you have many situation like this one: pre=button(frame_pre,'Previous') which should be written: pre = button(frame_pre,'Previous')

Remove unpractical graphical choices

You set the font weight to bold and font seize to 20: that is too much. This will lead to an ugly text, and you punish yourself if one of the answers or questions contains a half dozen or more of words.


1. But also may be harmful for the scalability of your program.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.