简体   繁体   English

TKinter未将字符串插入文本小部件

[英]TKinter not inserting a string to Text widget

I Apologize in advance for the long code and if I break any rules as this is my first time asking a question on this site. 对于长代码,如果在我违反本规则之前,我事先表示歉意,因为这是我第一次在此站点上提问。 I'm fairly new to Python but I'm currently writing a program for a school project that requests data from the OpenFEC API and then display it in a GUI that I created using TKinter. 我对Python相当陌生,但是我目前正在为学校项目编写程序,该程序从OpenFEC API请求数据,然后将其显示在使用TKinter创建的GUI中。 Everything works well if I print to the console but once I try to insert my data to the Text widget I get an error and not all the data is shown. 如果我打印到控制台,一切都会很好,但是一旦尝试将数据插入到“文本”小部件中,我将收到错误消息,并且不会显示所有数据。 The error is 'TypeError: must be str, not NoneType', I've tried converting the variable to a string using str(varName) but doesn't work. 错误为“ TypeError:必须为str,而不是NoneType”,我尝试使用str(varName)将变量转换为字符串,但不起作用。 I've been working on this specific issue for a couple of days now and have searched all over the internet for possible solutions but I haven't found anything that helps me. 我已经在这个特定问题上工作了几天,并且在互联网上搜索了可能的解决方案,但是我没有找到任何可以帮助我的东西。 I appreciate any help that I can receive. 感谢您能获得的任何帮助。

import json
import urllib.request
import urllib.parse
import urllib.error
from tkinter import *
from tkinter import ttk

def inputValidation(year):
    # Ask the user for a year
    # year = yearEntry.get()
    # Input validation
    year = int(year)  # Converting input to integer for comparison purposes
    if year >= 2003 and year <= 2020:
        message = "Here are the election dates  for " + str(year) + ':' + '\n'
    else:
        message = "Please enter a valid year."
    # Clear out any existing text from entry window
    data.config(state=NORMAL)
    data.delete(0.0, END)
    # Set the data window
    data.insert(1.0, message)
    data.config(state=DISABLED)
    # Convert the year back to a string
    year = str(year)
    return year

# Function to get API data
def apiCall(event, year, pageNumber):
    """ Requests data from OpenFEC API by constructing a url using predetermined
     values. """
    apiKey = 'rdudHBjgS5srIohVWYyyUL64AOsqVfRkGZD4gvMU'
    perPage = '90'  # Number of items to print per page
    electionYear = year
    nullHide = 'true'
    nullOnly = 'true'
    sort = sortNum.get()
    if sort == 1:
        sort = '-election_state'
    if sort == 2:
        sort = '-election_date'
    if sort == 3:
        sort = 'office_sought'
    url = ('https://api.open.fec.gov/v1/election-dates/?per_page=' + perPage +
           '&api_key=' + apiKey +
           '&election_year=' + electionYear +
           '&page=' + pageNumber +
           '&sort_hide_null=' + nullHide +
           '&sort_null_only=' + nullOnly +
           '&sort=' + sort)
    uh = urllib.request.urlopen(url)
    data = uh.read().decode()
    js = json.loads(data)
    print(url)
    return js                         # We receive a dictionary with all the 
info requested

# Function to print the API info
# Provide a year between 2003 - 2020
def electionDates(event):
    year = yearEntry.get()          # User provided year
    year = inputValidation(year)
    pageNumber = '1'
    js = apiCall(event, year, pageNumber)  # Call the API by using the first function
    pages = js['pagination']['pages']
    print('TOTAL PAGES: ', pages)
    # print('TOTAL ITEMS: ', items)
    while int(pages) >= int(pageNumber):
        idx = 0
        totalItems = 0
        items = 0
        print('PAGE', pageNumber, 'OF', pages)
        for item in js['results']:
            state = js['results'][idx]['election_state']
            date = js['results'][idx]['election_date']
            electionType = js['results'][idx]['election_type_full']
            notes = js['results'][idx]['election_notes']
            office = js['results'][idx]['office_sought']
            # Changing initials from API to full office names
            if office == 'S':
                office = office.replace('S', 'Senate')  # Print out the full word instead of just the initial
            if office == 'H':
                office = office.replace('H', 'House of Representatives')  # Print out the full word, not the initial
            if office == 'P':
                office = office.replace('P', 'President')  # Print out the full word instead of just the initial
            idx = idx + 1  # idx allows us to iterate through each item in the dictionary

            # Displaying Data in Text Box
            data.config(state=NORMAL)
            data.insert(2.0, '' +
                    '\n' 'Date: ' + date +
                    '\n' 'State: ' + state +
                    '\n' 'Election Type: ' + electionType +
                    '\n' 'Office: ' + office +
                    '\n' 'Notes: ' + notes +
                    '\n', END)
            data.config(state=DISABLED)
            items = items + 1
        pageNumber = int(pageNumber) + 1
        pageNumber = str(pageNumber)
        js = apiCall(event, year, pageNumber)  # Re-call the API function to print the next page


# -------- GUI CODE --------
root = Tk()
root.title('InfoLection')
frame = Frame(root)
sortNum = IntVar()

""" Create label, button, entry and text widgets into our frame. """
# --- Create instruction label ---
yearLbl = ttk.Label(root, text='Enter Year: ')
yearLbl.grid(row=0, column=1, sticky=E)

# --- Create Entry Box ---
yearEntry = ttk.Entry(root)
yearEntry.grid(row=0, column=2, columnspan=1, sticky=W)
yearEntry.delete(0, END)
yearEntry.insert(0, '')

# --- Create Submit Button ---
submitBtn = ttk.Button(root, text='Submit')
submitBtn.bind('<Button-1>', electionDates)
submitBtn.grid(row=3, column=0, columnspan=5, sticky=NSEW)

# Menu Bar
menu = Menu(root)
root.config(menu=menu)
# Submenu
subMenu = Menu(menu)
menu.add_cascade(label='About', menu=subMenu)
subMenu.add_command(label="Information")
subMenu.add_command(label='Exit')

# --- Radio Buttons to Select Sorting Method ---
# Label
sortByRB = ttk.Label(root, text='Sort by:')
sortByRB.grid(row=1, column=0, sticky=E)
# Radio Buttons
stateSortRB = ttk.Radiobutton(root, text='State', value=1, variable=sortNum)    
# Sort by state
stateSortRB.grid(row=2, column=1, sticky=W)
dateSortRB = ttk.Radiobutton(root, text='Date', value=2, variable=sortNum)      
# Sort by date
dateSortRB.grid(row=2, column=2, sticky=W)
officeSortRB = ttk.Radiobutton(root, text='Office', value=3, 
variable=sortNum)  # Sort by Office
officeSortRB.grid(row=2, column=3, sticky=W)

# --- Text Widget To Display Data ---
data = Text(root, width=50, height=25, wrap=WORD)
data.grid(row=4, column=0, columnspan=4, sticky=NSEW)

# --- Scroll Bar ---
scroll = ttk.Scrollbar(root, command=data.yview)
data['yscrollcommand'] = scroll.set
scroll.grid(row=4, column=5, pady=3, sticky=NSEW)

# Window Icon

# --- Keep Window Open ---
root.mainloop()

Looks like your notes variable is coming back as a None. 看起来您的notes变量返回为None。 You can do this to make your code a little more robust and easier to debug: 您可以这样做,使您的代码更健壮,更易于调试:

        undefined_s = 'N/A'
        data.insert(2.0, '' +
                '\n' 'Date: ' + (date or undefined_s) +
                '\n' 'State: ' + (state or undefined_s) +
                '\n' 'Election Type: ' + (electionType or undefined_s) +
                '\n' 'Office: ' + (office or undefined_s) +
                '\n' 'Notes: ' + (notes or undefined_s) +
                '\n', END)

I found another bug and fixed it. 我发现了另一个错误并修复了它。

       '&sort=' + str(sort))
       #          ^^^ added the str()

and now the code runs. 现在代码运行了。 You should make one of the radiobuttons on by default. 默认情况下,您应该使单选按钮之一处于打开状态。

I do see that some of the Notes are coming back as undefined, but this is not a bug, I would say. 我确实看到一些说明返回的是未定义的,但是我想这不是错误。 It's just a matter of what info is on the page you're crawling. 只是您要爬网的页面上的什么信息的问题。 And I do see some of the other fields also coming back as undefined, but, again, that's a function of the pages you're crawling. 我确实看到其他一些字段也以未定义的形式返回,但这又是您正在爬网的页面的功能。

You're trying to concatenate "Notes: " and notes , but sometimes notes is None, and you can't add None to a string. 您正在尝试将"Notes: "notes串联起来,但是有时候notes是None,并且不能将None添加到字符串中。 You could manually convert: 您可以手动转换:

'\n' 'Notes: ' + str(notes) +

... But I think it would be easier to take advantage of Python's str.format() method, which automatically converts its arguments to strings (in the absence of any specification in the format string telling it otherwise): ...但是我认为利用Python的str.format()方法会更容易,该方法会自动将其参数转换为字符串(在格式字符串中没有任何说明的情况下):

        data.insert(2.0, ''
                '\n' 'Date: {}'
                '\n' 'State: {}'
                '\n' 'Election Type: {}'
                '\n' 'Office: {}'
                '\n' 'Notes: {}'
                '\n'.format(date, state, electionType, office, notes)
                , END)

... Or you could use f-strings, although those are only available in Python 3.6 and higher: ...或者您可以使用f字符串,尽管这些仅在Python 3.6和更高版本中可用:

        data.insert(2.0, '' +
                '\n' f'Date: {date}'
                '\n' f'State: {state}'
                '\n' f'Election Type: {electionType}'
                '\n' f'Office: {office}'
                '\n' f'Notes: {notes}'
                '\n', END)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM