Geetansh G
Geetansh G

Reputation: 17

Python tkinter can't use Entry.get() fucntion

So I have been working on a quizzing application for some time now (about 4 days). I managed to make all the logical part of the code (the quiz taking, the quiz question handling, score outputting, etc.) I know that this code is neither the best nor the most efficient as it can be but I'm just a beginner. Anyways, the get() function for the entry function for tkinter does not return anything. I am aware that there is a way to fix it however I'm not sure how to implement the solution with an external loop. Please help me. Here is my code:

import random
from time import sleep
import tkinter as tk
from tkinter import *
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        #label = tk.Label(self.root, text="Hello World")
        #label.pack()

        #Button(self.root, text = "Choose", command=btnPressed).pack()

        tk.Label(self.root, text="Answer: ").grid(row=0,column=0)
        #answerField_get = StringVar()
        answerField = tk.Entry(self.root)
        answerField.grid(row=0, column=1)
        #Button(self.root, text='Show').grid(row=3, column=1, sticky=tk.W, pady=4)
        print(str(answerField.get()))
        Button(self.root, text='Show', command = lambda arg1=answerField.get():btnPressed("<"+str(arg1)+">")).grid(row=3, column=1, sticky=tk.W, pady=4)
        self.root.mainloop()

    def sendTMP(self, sendStr):
        btnPressed(sendStr)

SCORE_SET = []
qasTmp = {}
qas = {}
qasSequenced = {}
incorrectResponses = []
incorrectResponses_replies = []
quiztaker_name = ""
attachAnsCode = "%% Fun fact: rgb computer parts lovers are somewhat weird hehe %%********^^&&^^&&^^&&"
qasQsSequenced = False
qasQsL = 0
qasQsL_divisionFactor = 2
qasINDEX = 0
err_noCode = "<!>NO_CODE<!>"
codes_answerCorrect = "A_C"
codes_answerIncorrect = "A_I"
answerCode = err_noCode
score = 0
randQs = False

# File
# the metadata will corrupt the reading from the file, a separate file, created in the targeted system, must be used to properly read the data.
# comment out the file name that is not being used.
filename_windows = "qas-windows"
filename_rpi = "qas-rpi"

filename = filename_windows
fileformat = "txt"
file_name_format = filename + "." + fileformat
spaceIndicator = "|"
char_commentLine_qasFile = "*"
char_newline = "`"

print("Information about modes: ")
print("     *Easy: No point deductions for an incorrect response")
print("     *Hard: One point deducted for every incorrect response")

modes_err = "0"
modes_ez = "1"
modes_hard = "2"
gameOn = False
selectedMode = modes_err
askReplay = True
data_prev = []

with open("SCORES.txt", 'r') as scores_prev:
    data_prev = scores_prev.readlines()
    scores_prev.close()

for i in range(0, len(data_prev)):
    SCORE_SET.append(data_prev[i])

def btnPressInform():
    print("A button has been pressed!")

def importAndClean():
    # import questions from qas-windows.txt
    with open(file_name_format, 'r') as document:
        for line in document:
            if line.strip():
                key, value = line.split(None, 1)
                if key[0] != char_commentLine_qasFile:  # Custom comments for the txt file
                    qasTmp[key] = value.split()

                    # Clean up dictionary input from the txt file
    for i in range(0, len(qasTmp)):  # FIVE FOR LOOPS!!!! (FOUR IN THIS ONE)
        for ii in qasTmp:
            output = ""
            output_ans = ""

            for iii in range(0, len(ii)):
                if ii[iii] != spaceIndicator:
                    output += ii[iii]
                else:
                    output += " "
            for iiii in range(0, len(qasTmp[ii])):
                TEMP = str(qasTmp[ii])
                for iiiii in range(2, len(TEMP) - 2):  # IGNORE [' and ']
                    # print(TEMP[iiiii])
                    if TEMP[iiiii] != spaceIndicator:
                        output_ans += TEMP[iiiii]
                    else:
                        output_ans += " "
            # print(output + " : " + output_ans) #Output question: answer
            qas[output] = output_ans


importAndClean()


def getL():
    qasQsL = len(qas) / qasQsL_divisionFactor  # only ask 1/qasQsL_divisionFactor the questions
    qasQsL = int(qasQsL)  # round to an integer as odd numbers will end in .5 after division
    if qasQsL < 1:
        qasQsL = 1  # Have atleast ONE question
    return qasQsL


def debug1(keys, vals, index, i):
    print(str(index) + "/" + str((len(keys) - 1)))
    print(keys)
    print(vals)
    print()
    print(keys[index] + " : " + vals[index] + "\n")
    print("Sorting original index " + str(i) + " at random index " + str(index))


def debug2(keys, vals, index):
    print(keys)
    print(vals)
    print("\n")


def debugFinal():
    print("Temp (OG reading): ")
    print(qasTmp)
    print("\nQAS (Non-sequenced, cleaned): ")
    print(qas)
    print("\nQAS Sequenced (Randomly sequenced, cleaned): ")
    print(qasSequenced)


def randomize(qasQsL_tmp):
    qas_keys = list(qas.keys())
    qas_vals = list(qas.values())

    if randQs == False:
        qasQsL_tmp = len(qas_keys)  # all questions
        print("You will be asked all " + str(qasQsL_tmp) + " questions")
    else:
        qasQsL_tmp = getL()  # random question
        print("You will be asked " + str(qasQsL_tmp) + " questions out of " + str(len(qas)) + " possible questions!")
    print("\n\nRandomly sequencing questions...")
    for i in range(0, qasQsL_tmp):
        INDEX = random.randint(0, qasQsL_tmp - 1)
        # debug1(qas_keys, qas_vals, INDEX, i)
        qasSequenced[qas_keys[INDEX]] = qas_vals[INDEX]
        qas_keys.pop(INDEX)
        qas_vals.pop(INDEX)
        qasQsL_tmp -= 1
        # debug2(qas_keys, qas_vals, INDEX)
        sleep(0.05)
    # debugFinal()
    print("Done sequencing! Starting quiz now! \n\n")
    return "0"


def quizController(index):
    qas_keys = list(qasSequenced.keys())
    qas_vals = list(qasSequenced.values())
    # print(qas_keys)
    # print(qas_vals)
    lines = []
    lines_index = 0
    tmp = ""
    # Splitter
    for i in range(0, len(qas_keys[index])):
        if lines_index < len(qas_keys[index]) - 1:
            if qas_keys[index][i] != char_newline:
                tmp += qas_keys[index][i]
            else:
                lines.append(tmp)
                tmp = ""
    lines.append(tmp)

    # Multiple choice
    mChoiceQ = False
    mChoice_startBrackets = 0
    mChoice_endBrackets = 0
    mChoice_options = []
    mChoice_numOptions = 0
    mChoice_seperatorsAt = []
    for i in range(0, len(qas_keys[index])):
        if qas_keys[index][i] == "[":
            mChoice_startBrackets = i
            mChoiceQ = True
        elif qas_keys[index][i] == "]":
            mChoice_endBrackets = i
        elif qas_keys[index][i] == "/":
            mChoice_seperatorsAt.append(i)

    if mChoiceQ == True:
        TEMP = ""
        for i in range(mChoice_startBrackets, mChoice_endBrackets + 1):
            if qas_keys[index][i] != "[":
                if qas_keys[index][i] != "/" and qas_keys[index][i] != "]":
                    TEMP += qas_keys[index][i]
                else:
                    mChoice_options.append(TEMP)
                    TEMP = ""

    mChoice_numOptions = len(mChoice_seperatorsAt) + 1
    # Default options (yes, no) full names
    for i in range(0, len(mChoice_options)):
        if mChoice_options[i].lower() == "y":
            mChoice_options.append("yes")
        elif mChoice_options[i].lower() == "n":
            mChoice_options.append("no")

    # if mChoiceQ == True:
    #    print("It is a multiple choice question! There are " + str(mChoice_numOptions) + " options. They are: ")
    #    print(mChoice_options)

    print("\nQuestion " + str(index + 1) + "/" + str(qasQsL) + ":")

    for i in range(0, len(lines)):
        print(lines[i])

    # answer = ""
    answer = input(">")
    # answer = input(qas_keys[index]+ ": ")

    if mChoiceQ == False:
        if len(answer) > 0:
            if answer.lower() == str(qas_vals[index]).lower():
                return codes_answerCorrect
            else:
                incorrectResponses.append(qas_keys[index])
                incorrectResponses_replies.append(answer)
                # print("DEBUG: Incorrect response! Expected '" + str(qas_vals[index]).lower() + "', received " + answer.lower())
                return codes_answerIncorrect
        else:
            print("Please insert an answer!")
    else:
        allowedResponse = False
        for i in range(0, len(mChoice_options)):
            if answer.lower() == mChoice_options[i].lower():
                allowedResponse = True

        if allowedResponse == True:
            ans = qas_vals[index].lower()
            yn = False
            ans_yesno = ""
            if ans.lower() == "y" or ans.lower() == "n":
                yn = True
            else:
                yn = False

            if yn == True:
                if ans == "y":
                    ans_yesno = "yes"
                elif ans == "n":
                    ans_yesno = "no"

            if len(answer) > 0:
                if yn == True:
                    if answer.lower() == ans.lower() or answer.lower() == ans_yesno.lower():
                        return codes_answerCorrect
                    else:
                        return codes_answerIncorrect
                        incorrectResponses.append(qas_keys[index])
                        incorrectResponses_replies.append(answer)
                else:
                    if answer.lower() == ans.lower():
                        return codes_answerCorrect
                    else:
                        return codes_answerIncorrect
                        incorrectResponses.append(qas_keys[index])
                        incorrectResponses_replies.append(answer)
            else:
                print("Please insert an answer!")
        else:
            print("Invalid response! You may only enter the following: " + str(mChoice_options))


def saveScore():
    # Clear file!
    score_file_CLEAR = open("SCORES.txt", "wt")
    score_file_CLEAR.close()
    # Save contents
    score_file = open("SCORES.txt", "wt")
    for i in range(0, len(SCORE_SET)):
        score_file.write(SCORE_SET[i])
    print("Done saving!")

def btnPressed(tmp):
    print(tmp)

app = App()

while True:
    qasQsL = len(qasSequenced)
    if gameOn == True and selectedMode != modes_err:
        if qasQsSequenced == True:
            if qasINDEX < qasQsL:
                answerCode = quizController(qasINDEX)
        else:
            output = randomize(qasQsL)

        if output == "0":
            qasQsSequenced = True

        if qasINDEX < qasQsL:
            if answerCode == codes_answerCorrect:
                score += 1
                qasINDEX += 1
                # print("DEBUG: Correct! Score set to: " + str(score))
            elif answerCode == codes_answerIncorrect:
                if selectedMode == modes_hard:
                    score -= 1
                qasINDEX += 1
                # print("Score set to: " + str(score))
        else:
            print("")
            if qasQsL != 0:
                score_per = score / qasQsL
                if score_per < 0:
                    score_per = 0
                if score < 0:
                    score = 0
                    print("You score was lower than 0, therefore it was set to 0")

                # print("Your score: " + str(score) + "/" + str(len(qasSequenced)) + " (" + str(int(score_per*100)) + "%)")
                # if score != qasQsL:
                #    print("You responded to the following questions incorrectly:")
                #    print(incorrectResponses)

                if score / qasQsL == 1:
                    SCORE_SET.append(quiztaker_name + " scored " + str(score) + " out of " + str(qasQsL) + "(" + str(
                        int(score / qasQsL) * 100) + "%). PART OF Qs: " + str(
                        int(randQs)) + " at division factor 1/" + str(qasQsL_divisionFactor) + ", MODE: " + str(
                        int(selectedMode)) + "\n")
                if score / qasQsL != 1:
                    SCORE_SET.append(quiztaker_name + " scored " + str(score) + " out of " + str(qasQsL) + " (" + str(
                        int(score / qasQsL) * 100) + "%). PART OF Qs: " + str(
                        int(randQs)) + " at division factor 1/" + str(qasQsL_divisionFactor) + ", MODE: " + str(
                        int(selectedMode)) + " They got the following questions wrong: \n")
                    for i in range(0, len(incorrectResponses)):
                        SCORE_SET.append("      " + str(i + 1) + ") " + incorrectResponses[i] + " --RESPONSE-- " +
                                         incorrectResponses_replies[i] + "\n")
                    SCORE_SET.append("\n")

                saveScore()
                qasQsSequenced = False
                gameOn = False
                print("\nGame over!")
                askReplay = True

            else:
                continue
    elif askReplay == False:
        TEMP = input("What mode would you like? (E = Easy, H = Hard): ")
        if len(str(TEMP)) > 0:
            if str(TEMP).lower() == "e":
                selectedMode = modes_ez
                gameOn = True
                print("Set mode to: NO POINT DEDUCTIONS")
            elif str(TEMP).lower() == "h":
                selectedMode = modes_hard
                gameOn = True
                print("Set mode to: POINT DEDUCTIONS ALLOWED")
            else:
                print("Error: Undefined response. Please try again!")

    elif askReplay == True:
        TEMP = input("Would you like to (re)do the quiz? (Y/N): ")
        if len(str(TEMP)) > 0:
            if str(TEMP).lower() == "y":
                askReplay = False
                qasQsSequenced = False
                qasQsL = 0
                qas.clear()
                qasSequenced.clear()
                qasTmp.clear()
                qasINDEX = 0
                incorrectResponses.clear()
                answerCode = err_noCode
                score = 0
                selectedMode = modes_err
                importAndClean()
                randQs = False
                USER_TEMP = input("Please enter your name >")
                if len(USER_TEMP) > 0:
                    quiztaker_name = str(USER_TEMP)
                    print("Welcome " + quiztaker_name + "!")
                USER_TEMP = input("Would you like all questions (a) or a part of the questions(p)? (A/P) > ")
                if len(USER_TEMP) > 0:
                    if USER_TEMP.lower() == "a":
                        print("Set to all questions!")
                        randQs = False
                    elif USER_TEMP.lower() == "p":
                        print("Set to 1/" + str(qasQsL_divisionFactor) + " questions (pre-set variable)")
                        randQs = True
                    else:
                        print("Undefined response! Setting to default value (ALL)")
                        randQs = False
                    gameOn = False
                    askReplay = False
            elif str(TEMP).lower() == "n":
                selectedMode = modes_hard
                gameOn = False
                print("Exiting now!")
                saveScore()
                sleep(2)
                exit(0)
            else:
                print("Error: Undefined response. Please try again!")

Upvotes: 0

Views: 69

Answers (1)

furas
furas

Reputation: 142631

Entry() doesn't work like input(). It doesn't wait for your data but it only informs tkitner that you want to display Entry widget (and mainloop() will display it) and Python goes to next lines of code and it runs print(str(answerField.get())) before it even displays window - so you try to get from empty Entry.

You should get it in function assigned to Button which you will press after you put some text in Entry.

The same problem is with

lambda arg1=self.answerField.get():print(arg1)

it assigns to args value from Entry only once when lambda is defined at start - so it get empty string. You should use it inside function

command=lambda:print(self.answerField.get())

or create normal function and assign to button.


Minimal working code

import tkinter as tk
import threading

class App(threading.Thread):

    def run(self):
        self.root = tk.Tk()
        #self.root.protocol("WM_DELETE_WINDOW", self.on_close)

        self.answerField = tk.Entry(self.root)
        self.answerField.grid(row=0, column=1)

        #b = tk.Button(self.root, text='Show', command=lambda:print(self.answerField.get()))
        b = tk.Button(self.root, text='Show', command=self.on_click)
        b.grid(row=1, column=1)

        self.root.mainloop()

    def on_click(self):
        print(self.answerField.get())

    #def on_close(self):
    #    self.root.destroy()

App().start()
#App().run()

Upvotes: 1

Related Questions