Dashboard

Quiz Dashboard

Above UI we are going to create by Tkinter for Dashboard in our Quiz system desktop application. For that first we create a window and configure its title, size and color. Here we are creating a window at Top Level so we can add this window at the top of any other window.

    
        self.root = tk.Toplevel()
        self.root.title("Dashboard")
        self.root.configure(bg="#585858")
        self.root.resizable(width=0, height=0)
        win_width = 1280
        
        win_height = int(1280 * 56.25 / 100)

        self.root.geometry(str(win_width) + "x" + str(win_height))
        Util.center(self.root)

        login_root = None
        if args.__sizeof__() > 0:
            login_root = args[0]

        
        # Get screen size
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        Util.set_font_size(screen_width, screen_height, win_width, win_height)
        self.width = win_width * 96 / 100
        self.height = win_height * 94 / 100
    

After creation of window, we will add a canvas background.

    
    bg_canvas = RoundBackgroundFrame(self.root, self.width, self.height, padding, corner_radius, color, "#585858")
    bg_canvas.place(width=self.width, height=self.height,x=win_width / 2 - self.width / 2, y=win_height / 2 - self.height / 2)

    class RoundBackgroundFrame(tk.Canvas):

        def __init__(self, parent, width, height, padding, cornerradius, color, bg):
            tk.Canvas.__init__(self, parent, borderwidth=0, relief="flat", highlightthickness=0, bg=bg)
            self.width = width
            self.height = height
            self.padding = padding
            self.cornerradius = cornerradius
            self.color = color
  
            original = Image.open("images/corner.png")
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_r_t = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_l_t = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_l_b = ImageTk.PhotoImage(resized)

            original = original.rotate(90, expand=0)
            resized = original.resize((int(40), int(40)), Image.ANTIALIAS)
            self.image_r_b = ImageTk.PhotoImage(resized)

            self.shape(width, height, padding, cornerradius, color)
            (x0, y0, x1, y1) = self.bbox("all")
            width = (x1 - x0)
            height = (y1 - y0)
            self.configure(width=width, height=height)

        def shape(self, width, height, padding, cornerradius, color):
            self.create_polygon((padding, height - cornerradius - padding, padding, cornerradius + padding,
                                padding + cornerradius, padding, width - padding - cornerradius, padding,
                                width - padding, cornerradius + padding, width - padding,
                                height - cornerradius - padding, width - padding - cornerradius, height - padding,
                                padding + cornerradius, height - padding), fill=color, outline=color)

            self.create_image(width - self.image_r_t.width(), 0, image=self.image_r_t, anchor=NW)
            self.create_image(0, 0, image=self.image_l_t, anchor=NW)
            self.create_image(0, height - self.image_l_b.width(), image=self.image_l_b, anchor=NW)
            self.create_image(width - self.image_r_b.width(), height - self.image_r_b.width(), image=self.image_r_b, anchor=NW)


    

Once your window is created with custom background, we will create a frame with some reduced dimensions.

    
    self.width = self.width * 98 / 100
        self.height = self.height * 96 / 100
        self.base_frame = Frame(self.root, width=self.width, height=self.height, bg=color)
        bg_canvas.create_window(self.width / 100, self.height * 2 / 100, anchor=NW, window=self.base_frame)

        #Add Heading
        self.add_heading_label(self.base_frame, color, AppConstant.FONT_SIZE)

        def add_heading_label(self, base_frame, color, font_size):
            label_heading = Label(base_frame, text="Quiz App", anchor=CENTER, bg=color,font=("Lucida Grande", font_size + 6))
            label_heading.place(width=self.width * 90 / 100, height=self.height * 7 / 100, x=self.width * 5 / 100,y=self.height * 0.5 / 100)
       
       
        #Adding Line
        self.add_line_border(self.base_frame, color, AppConstant.FONT_SIZE, self.root, login_root)


        def add_line_border(self, base_frame, color, font_size, root, login_root):
            line_canvas = Canvas(base_frame, bg=color, borderwidth=0, relief="flat", highlightthickness=0)
            line_canvas.place(width=self.width - 4, height=5, x=2, y=self.height * 13 / 100)
            line_canvas.create_line(0, 0, self.width, 0, fill="#787878")
            line_canvas2 = Canvas(base_frame, bg=color, borderwidth=0, relief="flat", highlightthickness=0)
            line_canvas2.place(width=self.width - 4, height=5, x=2, y=self.height * 20 / 100)
            line_canvas2.create_line(0, 0, self.width, 0, fill="#787878")
            frame_name = Frame(base_frame, bg=color)
            frame_name.place(width=self.width * 94 / 100, x=self.width * 3 / 100, y=self.height * 14.3 / 100)
            frame_name.grid_propagate(False)
            label_name = Label(frame_name, font=("Lucida Grande", font_size - 6), text="Hi, " + AppConstant.USER_NAME,anchor=CENTER,bg=color)
        
            label_name.pack(side="left", fill=None, expand=False)
      
            button_logout = RoundedButton(frame_name, 130, 130 / 4.2, color, "images/button3.png", "Logout",font=("Lucida Grande", font_size - 6),
                                            command=lambda: logout_click(root, login_root))
            button_logout.pack(side="right", fill=None, expand=False)
          
          
        
    

Here Is logic for logout when click "Logout" Button.

     
    def logout_click(root, login_root):
        db = Util.connect_db()
        cursor = db.cursor()
        key_is_login = "IS_LOGIN"
        cursor.execute("UPDATE system_setting SET value = ? WHERE key = ?",
                        (False, key_is_login,))
        db.commit()
        root.destroy()
        Util.center(login_root)
        login_root.deiconify()
    
    

We have created a Database named "quiz_db.db", lets connect that database and put that code in seperate class "Util" because we are going to use it multiple time in our project.

    
    class Util:

        @staticmethod
        def connect_db():
            db = None
            try:
                db = sql.connect("quiz_db.db")
                return db
            except sql.Error as error:
                print("Failed to insert data into sqlite table", error)
                

Now we Design Welcome UI.

    
    #Welcome UI
    self.labelframe_welcome = LabelFrame(self.base_frame, text="", font=font.Font(family="Lucida Grande", size=14, weight='normal'),
                                        pady=lvl_frame_width * 2 / 100, padx=lvl_frame_width * 2 / 100, bg=color)

    self.welcome_lvl = Label(self.base_frame,text="WELCOME",anchor=CENTER, bg=color, fg="#00FF00",
                                font=("Lucida Grande", AppConstant.FONT_SIZE + 20,))

    self.welcome_data_lvl = Label(self.base_frame,text="Happy to have you..!!",anchor=CENTER, bg=color, fg="#0000FF",
                                    font=("Lucida Grande", AppConstant.FONT_SIZE))
        
    

Now We create Question UI for Quiz .

    
    # Question ui
    self.labelframe_question = LabelFrame(self.base_frame, text="",font=font.Font(family="Lucida Grande", size=13, weight='normal'),
                                              pady=lvl_frame_width * 2 / 100, padx=lvl_frame_width * 2 / 100, bg=color)

    self.question_number_lvl = Label(self.base_frame,text="Question No 1",anchor=W, bg=color,
                                    font=("Lucida Grande", AppConstant.FONT_SIZE + 6))

    self.timer_lvl = Label(self.base_frame,text="Time:",anchor=E, bg=color,
                            font=("Lucida Grande", AppConstant.FONT_SIZE))

    self.question_data_lvl = Label(self.base_frame,text="Question Data",anchor=W, bg=color,
                                    font=("Lucida Grande", AppConstant.FONT_SIZE - 6))

    self.option1 = tk.Radiobutton(self.base_frame, text='Option A', variable=self.var, anchor=W, value='A',
                                      command=self.save_answer)
    self.option2 = tk.Radiobutton(self.base_frame, text='Option B', variable=self.var, anchor=W, value='B',
                                      command=self.save_answer)
    self.option3 = tk.Radiobutton(self.base_frame, text='Option C', variable=self.var, anchor=W, value='C',
                                      command=self.save_answer)
    self.option4 = tk.Radiobutton(self.base_frame, text='Option D', variable=self.var, anchor=W, value='D',
                                      command=self.save_answer)

    self.button_submit = RoundedButton(self.base_frame, 250, 130 / 2.9, color, "images/button3.png", "SUBMIT",
                                        font=("Lucida Grande", 14),command=lambda: self.show_confirmation_dialog(self.height, lvl_frame_width))

    self.button_next = RoundedButton(self.base_frame, 250, 130 / 2.9, color, "images/button3.png", "NEXT",font=("Lucida Grande", 14),
                                        command=lambda: self.navigate_to_next_question())

    self.button_previous = RoundedButton(self.base_frame, 250, 130 / 2.9, color, "images/button3.png", "PREVIOUS",font=("Lucida Grande", 14),
                                            command=lambda: self.navigate_to_pre_question())
    
    

Now We Create Result UI.

    
    # result UI
    self.labelframe_result = LabelFrame(self.base_frame, text="", font=font.Font(family="Lucida Grande", size=14, weight='normal'),
                                        pady=lvl_frame_width * 2 / 100, padx=lvl_frame_width * 2 / 100, bg=color)

    self.result_head_lvl = Label(self.base_frame, text="REPORT CARD", anchor=CENTER, bg=color, fg="#FF0000",
                                     font=("Lucida Grande", AppConstant.FONT_SIZE + 25))

    self.result_data_lvl = Label(self.base_frame, text="", anchor=CENTER, bg=color, fg="#00FF00",
                                     font=("Lucida Grande", AppConstant.FONT_SIZE - 2))

    self.result_data2_lvl = Label(self.base_frame, text="", anchor=CENTER, bg=color, fg="#FF00FF",
                                      font=("Lucida Grande", AppConstant.FONT_SIZE))
    
    

Now All UI Complete, Here's Configration of Welcome UI And "Start Quiz" Button.

    
    #Configration
    self.button_start_quiz = RoundedButton(self.base_frame, 250, 130 / 2.56, color, "images/button3.png", "START QUIZ",font=("Lucida Grande", 14),
                                            command=lambda: self.show_question_ui(self.width, self.height, lvl_frame_width))

    self.show_welcome_ui(self.width, self.height, lvl_frame_width)

        

Here is our class RoundedButton,create class RoundedButton and put that code in seperate class "Util" because we are going to use it multiple time in our project:

    
class RoundedButton(tk.Canvas):

    def __init__(self, parent, width, height, bg, icon_path, text, font, command=None):
        tk.Canvas.__init__(self, parent, borderwidth=0, relief="flat", highlightthickness=0, bg=bg)
        self.command = command
        self.width = width
        self.height = height
        self.text = text
        self.font = font

        original = Image.open(icon_path)
        resized = original.resize((int(width), int(height)), Image.ANTIALIAS)
        self.image = ImageTk.PhotoImage(resized)

        self.shape(width, height)
        (x0, y0, x1, y1) = self.bbox("all")
        width = (x1 - x0)
        height = (y1 - y0)
        self.configure(width=width, height=height)
        self.bind("", self._on_press)
        self.bind("", self._on_release)

    def shape(self, width, height):
        self.create_image(0, 0, image=self.image, anchor=NW)
        self.create_text(width / 2, height / 2, anchor=CENTER, font=self.font, text=self.text, fill="#FFFFFF")

    def shape_press(self, width, height):
        self.create_image(0, 0, image=self.image, anchor=NW)
        self.create_text(width / 2, height / 2, anchor=CENTER, font=self.font, text=self.text, fill="#000000")

    def _on_press(self, event):
        self.configure(relief="sunken")
        self.delete("all")

        self.shape_press(self.width, self.height)

    def _on_release(self, event):
        self.configure(relief="raised")
        self.delete("all")
        self.shape(self.width, self.height)
        if self.command is not None:
            self.command()

    

Here is logic for show welocome UI.

    
    def show_welcome_ui(self, width, height, lvl_frame_width):
        # Welcome UI
        # placing these data by place manager
        self.labelframe_welcome.place(width=lvl_frame_width, height=height * 70 / 100,
                                      x=width * 3 / 100, y=height * 22 / 100)
        self.welcome_lvl.place(width=width * 90 / 100, height=height * 7 / 100,
                               x=width * 5 / 100, y=height * 30 / 100)
        self.welcome_data_lvl.place(width=width * 90 / 100, height=height * 5 / 100,
                                    x=width * 5 / 100, y=height * 45 / 100)
        self.button_start_quiz.place(x=width / 2 - 250 / 2, y=height * 65 / 100)

    

Here Is logic of Show Question UI .

    

    def show_question_ui(self, width, height, lvl_frame_width):
        # Ask Question UI
        self.hide_welcome_ui()

        lfbw = lvl_frame_width * 13 / 100

        # placing these data by place manager
        self.labelframe_question.place(width=lvl_frame_width, height=lfbw + lvl_frame_width * 22 / 100,
                                       x=self.width * 3 / 100,
                                       y=self.height * 22 / 100)

        self.question_number_lvl.place(width=self.width * 90 / 100, height=self.height * 7 / 100,
                                       x=self.width * 5 / 100, y=self.height * 28 / 100)

        self.timer_lvl.place(width=self.width * 90 / 100, height=self.height * 3 / 100,
                             x=self.width * 5 / 100, y=self.height * 25 / 100)

        self.question_data_lvl.place(width=width * 90 / 100, height=height * 4 / 100,
                                     x=width * 5 / 100, y=height * 35 / 100)
        self.option1.place(width=width * 90 / 100, height=height * 4 / 100, x=width * 5 / 100,
                           y=height * 60 / 100)
        self.option2.place(width=width * 90 / 100, height=height * 4 / 100, x=width * 5 / 100,
                           y=height * 65 / 100)
        self.option3.place(width=width * 90 / 100, height=height * 4 / 100, x=width * 5 / 100,
                           y=height * 70 / 100)
        self.option4.place(width=width * 90 / 100, height=height * 4 / 100, x=width * 5 / 100,
                           y=height * 75 / 100)
        self.button_submit.place(x=width - 250 - 50, y=height * 0.85)
        self.button_next.place(x=width - 250 - 50, y=height * 0.85)
        self.button_previous.place(x=50, y=height * 0.85)

        self.navigate_to_next_question()
        self.submit(height, lvl_frame_width)
        
        

Here is logic of hide Welcome UI when Question UI Appear.

    
    def hide_welcome_ui(self):

        self.labelframe_welcome.place_forget()
        self.welcome_lvl.place_forget()
        self.welcome_data_lvl.place_forget()
        self.button_start_quiz.place_forget()
                       

Here Is logic of navigate to preVious question when click "Privious" Button.

    
    def navigate_to_pre_question(self):

        if self.current_question_number > 0:
            self.current_question_number = self.current_question_number - 1
            self.next_question()

Here Is logic of navigate to next question when click "Next" Button.

    
    def navigate_to_next_question(self):

        if self.current_question_number < 9:
            self.current_question_number = self.current_question_number + 1
            self.next_question()

Here Is logic of get Next Question

    
    def next_question(self):

        df = pd.read_excel('text1.xlsx', engine="openpyxl")
    

        # Question No
        self.question_number_lvl["text"] = "Question No: " + str(
            df.iloc[self.current_question_number][0])

        # Question data
        self.question_data_lvl["text"] = "Question: " + str(
            df.iloc[self.current_question_number][1])

        # option 1
        self.option1["text"] = "A. " + str(df.iloc[self.current_question_number][2])

        # option 2
        self.option2["text"] = "B. " + str(df.iloc[self.current_question_number][3])

        # option 3
        self.option3["text"] = "C. " + str(df.iloc[self.current_question_number][4])

        # option 4
        self.option4["text"] = "D. " + str(df.iloc[self.current_question_number][5])

        if self.current_question_number == 0:
            self.button_previous.place_forget()
        elif self.current_question_number == 1:
            self.button_previous.place(x=50, y=self.height * 0.85)
        elif self.current_question_number == 8:
            self.button_submit.place_forget()
            self.button_next.place(x=self.width - 250 - 50, y=self.height * 0.85)
        elif self.current_question_number == 9:
            self.button_next.place_forget()
            self.button_submit.place(x=self.width - 250 - 50, y=self.height * 0.85)

        key = "question_number_" + str(df.iloc[self.current_question_number][0])
        if key in self.dict:
            self.var.set(self.dict[key][1])
        else:
            self.var.set("z")

Here Is logic of save Answer.

    
    def save_answer(self, ):
        
        df = pd.read_excel('text1.xlsx', engine="openpyxl")
    
        self.dict["question_number_" + str(self.current_question_number + 1)] = [
            df.iloc[self.current_question_number][6], self.var.get()]

Here Is logic for show confirmation dialog When Click "Submit" Button.

    
    def show_confirmation_dialog(self, height, lvl_frame_width):
        msg_box = messagebox.askokcancel(title="Submit", message="Do you want to submit your Quiz?")
        print(msg_box)
        if msg_box:
            self.show_result_ui(height, lvl_frame_width)
        else:
            pass
    
    

Here Is Quiz "Submit" Button.

    
    def submit(self, height, lvl_frame_width):
        try:
            # the input provided by the user is
            temp = int(0) * 3600 + int(self.minutes) * 60 + int(self.seconds)
        except:
            pass

        while temp > -1:

            mins, secs = divmod(temp, 60)

            # Converting the input entered in mins or secs to hours,
            hours = 0
            if mins > 60:
    
                hours, mins = divmod(mins, 60)

            # updating the GUI window after decrementing the
            self.timer_lvl.config(
                text="Time left:- " + str(mins).zfill(2) + ":" + str(secs).zfill(2))
            self.base_frame.update()
            time.sleep(1)

            # when temp value = 0; then a messagebox pop's up
            # with a message:"Time's up"
            if temp == 0:
                self.show_result_ui(height, lvl_frame_width)

            # after every one sec the value of temp will be decremented
            # by one
            temp -= 1
    
    

Here Is logic for show Result UI And logic of Calculating Result.

    

    def show_result_ui(self, height, lvl_frame_width):

        # Show Result UI
        self.hide_question_ui()

        # placing these data by place manager
        self.labelframe_result.place(width=lvl_frame_width, height=height * 70 / 100,x=self.width * 3 / 100,
                                        y=self.height * 22 / 100)

        self.result_head_lvl.place(width=self.width * 90 / 100, height=self.height * 10 / 100,
                                    x=self.width * 5 / 100, y=self.height * 30 / 100)
        self.result_data_lvl.place(width=self.width * 90 / 100, height=self.height * 4 / 100,
                                    x=self.width * 5 / 100, y=self.height * 50 / 100)
        self.result_data2_lvl.place(width=self.width * 90 / 100, height=self.height * 4 / 100,
                                    x=self.width * 5 / 100, y=self.height * 60 / 100)

        # calculating result
        total = 0
        for key in self.dict:
            if str(self.dict[key][0]) == str(self.dict[key][1]):
                total = total + 10

        self.result_data_lvl["text"] = "Marks obtained " + str(total) + " out of 100"
        if total <= 50:
            self.result_data2_lvl["text"] = "Hard Luck..!!  Try next time"
        elif total == 100:
            self.result_data2_lvl["text"] = "Excellent..!!"
        elif 50 < total < 100:
            self.result_data2_lvl["text"] = "Try Harder..!!"


    
    

Here Is logic for hide question UI When Result UI Appear.

    
    def hide_question_ui(self):
    
        self.labelframe_question.place_forget()
        self.question_number_lvl.place_forget()
        self.question_data_lvl.place_forget()
        self.option1.place_forget()
        self.option2.place_forget()
        self.option3.place_forget()
        self.option4.place_forget()
        self.button_submit.place_forget()
        self.button_next.place_forget()
        self.button_previous.place_forget()


At last we will manage closing of window

     
    def on_closing():
            self.root.destroy()
            args[0].deiconify()

        self.root.protocol("WM_DELETE_WINDOW", on_closing)
    
    

Programmer Mirta is for learning and training. Projects might be simple to improve learning. Projects are constantly reviewed to avoid errors, but we cannot assure full correctness of all content. While using Programmer Mitra, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 2021 by Programmer Mitra. All Rights Reserved.