Радиокнопка навигации и хранения значений - PullRequest
2 голосов
/ 26 апреля 2019

Я пытаюсь написать тест с несколькими вариантами ответов, используя Python Tkinter. У меня есть вопрос 2 части. У меня есть переключатели, которые отображают варианты и собирают выбранные параметры. У меня также есть созданная кнопка для перехода к следующему вопросу или к предыдущему вопросу, а также другая кнопка для просмотра оценки.

  • Часть 1. Как сохранить выбранную опцию переключателя для каждого вопроса при переходе назад / вперед по викторине?

  • Часть 2 - Я подумал, как должна работать кнопка view rating :

    1. Сравните каждый собранный вариант (сохраненный в list?) С правильным ответом
    2. Рассчитать балл
    3. Дисплей


    Пункты 2 и 3 - самая легкая часть для меня. Можете ли вы указать мне правильное направление, чтобы перейти к пункту номер один?

from tkinter import messagebox
import tkinter as tk

from tkinter import *
# question list
q = [
    "question 1", "question 2", "question 3", "question 4"
]

# options list
options = [
    ["a","b","c","d"],
    ["b","c","d","a"],
    ["c","d","a","b"],
    ["d","a","b","c"],
]

# correct answers list
a = [3,4,1,2]


class Quiz:
    def __init__(self, master):
        self.opt_selected = IntVar()
        self.qn = 0
        self.correct = 0
        self.ques = self.create_q(master, self.qn)
        self.opts = self.create_options(master, 4)
        self.display_q(self.qn)

        self.button = Button(master, text="Previous Question", command=self.back_btn,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

        self.button = Button(master, text="Next Question", command=self.next_btn,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

        self.button = Button(master, text="View Score", command=self.score_viewer,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

    # define questions
    def create_q(self, master, qn):

        w = Label(master, text=q[qn],
                  anchor='w',
                  wraplength=400, justify=LEFT)
        w.pack(anchor='w')

        return w

    # define multiple options
    def create_options(self, master, n):
        b_val = 0
        b = []
        while b_val < n:
            btn = Radiobutton(master, text="foo", variable=self.opt_selected, value=b_val+1)
            b.append(btn)
            btn.pack(side=TOP, anchor="w")
            b_val = b_val + 1
        return b

    # define questions for display when clicking on the NEXT Question Button
    def display_q(self, qn):
        b_val = 0
        self.opt_selected.set(0)
        self.ques['text'] = q[qn]
        for op in options[qn]:
            self.opts[b_val]['text'] = op
            b_val = b_val + 1

    # define questions for display when clicking on the PREVIOUS Question Button
    def display_prev_q(self, qn):
        b_val = 0
        self.opt_selected.set(0)
        self.ques['text'] = q[qn]
        for op in options[qn]:
            self.opts[b_val]['text'] = op
            b_val = b_val + 1

    # check option selected against correct answer list
    def check_q(self, qn):
        if self.opt_selected.get() == a[qn]:
            self.correct += 1
        else:
            self.correct += 0

    # print results
    def print_results(self):
        print("Score: ", self.correct, "/", len(q))

    # define PREVIOUS button
    def back_btn(self):
        self.qn = self.qn - 1
        self.display_prev_q(self.qn)

   # define NEXT button
    def next_btn(self):
        # if self.check_q(self.qn):
        #     print("Correct")
        #     self.correct += 1
        self.qn = self.qn + 1
        self.display_prev_q(self.qn)
        # if self.qn >= len(q):
        #     self.print_results()
        # else:
        #     self.display_q(self.qn)

    # define SCORE view button and score results
    def score_viewer(self):
        score_viewer = messagebox.askquestion("Warning", 'Would you like to view your current score?', icon='warning')
        if score_viewer == 'yes':
            self.check_q(self.qn)
            corr_ans = self.correct
            total_quest = len(q)
            output = '{:.1%}'.format(self.correct / len(q))
            score_text = "\nScore: %s " % output
            output_text = "Correctly answered %a out of  %d questions. %s" % (corr_ans, total_quest, score_text)
            messagebox.showinfo("Score", output_text)
        else:
            tk.messagebox.showinfo('Return', 'Returning to quiz')

Ответы [ 2 ]

1 голос
/ 26 апреля 2019

К сожалению, я думаю, вам нужно изменить основную архитектуру вашей программы и сделать ее намного более объектно-ориентированной.В частности, вместо с кучей отдельных list s, как у вас:

# question list
q = [
    "question 1", "question 2", "question 3", "question 4"
]

# options list
options = [
    ["a","b","c","d"],
    ["b","c","d","a"],
    ["c","d","a","b"],
    ["d","a","b","c"],
]

# correct answers list
a = [3,4,1,2]

Я думаю, вы должны определить пользовательский class для инкапсуляции вопросов и их текущего состояния,а затем создать (один) list из них во время инициализации приложения.Этот подход не только позволяет относительно легко переключаться с отображения одного на другое (не говоря уже о отслеживании текущего состояния каждого из них), но также делает довольно простым выполнение всех связанных с вами действий, которые вы хотите выполнить.

Вот полная реализация, иллюстрирующая, что я имею в виду.Обратите внимание, что он использует метод переключения кадров @Bryan Oakley, аналогичный тому, что есть в его ответе на вопрос Переключение между двумя кадрами в tkinter для отображения каждого вопроса.Основное отличие состоит в том, что «страницы» (вопросы) хранятся в list, на который ссылается индекс, а не в dict, доступ к которому осуществляется по имени класса.

Еще один приятный аспект этого дизайна заключается в том, чтоданные вопроса полностью отделены от кода Quiz, что означает, что при желании они могут храниться в файле или базе данных извне.

Я также пытался привести код в соответствие с PEP 8 - СтильРуководство по Python Code (которое вы также должны делать как можно больше).

import tkinter as tk
from tkinter.constants import *
from tkinter import messagebox


class Question(tk.Frame):
    """ Frame subclass encapsulating a multiple-option question. """
    def __init__(self, master, text, options, correct_ans):
        super(Question, self).__init__(master)

        self.text = text
        self.options = options
        self.correct_ans = correct_ans
        self.opt_selected = tk.IntVar()

        tk.Label(self, text=self.text, anchor=W, wraplength=400,
                 justify=LEFT).pack(anchor=W)

        for b_val, option in enumerate(self.options, start=1):
            tk.Radiobutton(self, text=option, variable=self.opt_selected,
                           value=b_val).pack(side=TOP, anchor=W)

    def check_q(self):
        """ Check if currently selected option is correct answer. """
        return self.opt_selected.get() == self.correct_ans


class Quiz:
    def __init__(self, master, quiz_questions):
        self.master = master

        # The container is a stack of question Frames on top of one another.
        # The one we want visible will be raised above the others.
        container = tk.Frame(master)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        # Create internal list of question Frames.
        self.questions = []
        for args in quiz_questions:
            q_frame = Question(container, *args)
            q_frame.grid(row=0, column=0, sticky=NSEW)
            self.questions.append(q_frame)

        self.qn = 0  # Current question number.
        self.display_q()  # Show it.

        # Create naviagtion Buttons.
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="Previous Question", command=self.display_prev_q)
        btn.pack(side=LEFT)
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="Next Question", command=self.display_next_q)
        btn.pack(side=LEFT)
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="View Score", command=self.score_viewer)
        btn.pack(side=LEFT)

    def display_q(self):
        """ Show the current question by lifting it to top. """
        frame = self.questions[self.qn]
        frame.tkraise()

    def display_next_q(self):
        """ Increment question number, wrapping to first one at end,
            and display it.
        """
        self.qn = (self.qn+1) % len(self.questions)
        self.display_q()

    def display_prev_q(self):
        """ Decrement question number, wrapping to last one at beginning,
            and display it.
        """
        self.qn = (self.qn-1) % len(self.questions)
        self.display_q()

    def score_viewer(self):
        """ Score results with user consent. """

        view_score = messagebox.askquestion(
            "Warning", 'Would you like to view your current score?',
            icon='warning')

        if view_score != 'yes':
            tk.messagebox.showinfo('Return', 'Returning to quiz')
        else:
            # Calculate number of correct answers and percentage correct.
            correct = sum(question.check_q() for question in self.questions)
            accuracy = correct / len(self.questions) * 100
            messagebox.showinfo("Score",
                "You have correctly answered %d out of %d questions.\n"
                "Score: %.1f%%" % (correct, len(self.questions), accuracy))


if __name__ == '__main__':

    # Note this data could also be stored separately, such as in a file.
    question_data = [('Question 1', ("a1", "b1", "c1", "d1"), 3),
                     ('Question 2', ("b2", "c2", "d2", "a2"), 4),
                     ('Question 3', ("c3", "d3", "a3"), 1),
                     ('Question 4', ("d4", "a4", "b4", "c4"), 2)]

    root = tk.Tk()
    root.title('Quiz')
    quiz = Quiz(root, question_data)
    root.mainloop()
0 голосов
/ 26 апреля 2019

Вы можете просто сохранить свои результаты в dict и вызывать их при просмотре партитуры или даже кнопок вперед / назад.

class Quiz:
    def __init__(self, master):
        self.opt_selected = IntVar()
        self.answer = {}
        ...

    def create_q(self, master, qn): # I'm using the label text as key

        self.w = Label(master, text=q[qn],
                  anchor='w',
                  wraplength=400, justify=LEFT)
        self.w.pack(anchor='w')

        return self.w

    def create_options(self, master, n):
        b_val = 0
        b = []
        while b_val < n:
            btn = Radiobutton(master, text="foo", variable=self.opt_selected, value=b_val+1,command=lambda : self.set_dict(self.opt_selected.get()))
            b.append(btn)
            btn.pack(side=TOP, anchor="w")
            b_val = b_val + 1
        return b

    def set_dict(self,ans):
        self.answer[self.w["text"]] = ans
        print (self.answer)

    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...