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

Python TKinter未向文本小部件插入字符串,python,text,tkinter,widget,nonetype,Python,Text,Tkinter,Widget,Nonetype,我为冗长的代码提前道歉,如果我违反了任何规则,因为这是我第一次在这个网站上提问。我对Python相当陌生,但我目前正在为一个学校项目编写一个程序,该项目从OpenFECAPI请求数据,然后在我使用TKinter创建的GUI中显示数据。如果我打印到控制台,一切都很好,但一旦我尝试将数据插入文本小部件,就会出现错误,并且不会显示所有数据。错误是“TypeError:必须是str,而不是NoneType”,我已经尝试使用str(varName)将变量转换为字符串,但没有成功。我已经在这个特定的问题上工

我为冗长的代码提前道歉,如果我违反了任何规则,因为这是我第一次在这个网站上提问。我对Python相当陌生,但我目前正在为一个学校项目编写一个程序,该项目从OpenFECAPI请求数据,然后在我使用TKinter创建的GUI中显示数据。如果我打印到控制台,一切都很好,但一旦我尝试将数据插入文本小部件,就会出现错误,并且不会显示所有数据。错误是“TypeError:必须是str,而不是NoneType”,我已经尝试使用str(varName)将变量转换为字符串,但没有成功。我已经在这个特定的问题上工作了几天,在互联网上搜索了所有可能的解决方案,但我没有找到任何对我有帮助的东西。我很感激能得到的任何帮助

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()
导入json
导入urllib.request
导入urllib.parse
导入urllib.error
从tkinter进口*
从tkinter导入ttk
def输入验证(年份):
#询问用户一年
#year=yearEntry.get()
#输入验证
year=int(year)#将输入转换为整数进行比较
如果年份>=2003且年份=int(页码):
idx=0
totalItems=0
项目=0
打印('第页',页码,'共',页)
对于js['results']中的项:
state=js['results'][idx]['election_state']
日期=js['results'][idx]['election_date']
electionType=js['results'][idx]['election\u type\u full']
notes=js['results'][idx]['election_notes']
office=js['results'][idx]['office\u']
#将缩写从API更改为完整的办公室名称
如果office==“S”:
office=office.replace('S','Senate')#打印出完整的单词,而不仅仅是首字母
如果office==“H”:
office=office.replace('H','houseofrepresentations')#打印出完整的单词,而不是首字母
如果office=='P':
office=office.replace('P','President')#打印出完整的单词,而不仅仅是首字母
idx=idx+1#idx允许我们迭代字典中的每个项目
#在文本框中显示数据
data.config(状态=正常)
数据。插入(2.0,'+
“\n”日期:“+日期+
“\n”状态:“+状态+
“\n”选择类型:“+electionType”+
“\n”办公室:“+办公室+
“\n”注释:“+注释+
“\n”,结束)
data.config(状态=已禁用)
项目=项目+1
页码=int(页码)+1
页码=str(页码)
js=apiCall(事件、年份、页码)#重新调用API函数以打印下一页
#-----图形用户界面代码--------
root=Tk()
root.title('InfoLection')
帧=帧(根)
sortNum=IntVar()
“”“在我们的框架中创建标签、按钮、条目和文本小部件。”“”
#---创建说明标签---
yearLbl=ttk.Label(root,text='Enter Year:')
yearLbl.网格(行=0,列=1,粘性=E)
#---创建输入框---
yearEntry=ttk.Entry(根目录)
yearEntry.grid(行=0,列=2,列span=1,粘性=W)
yearEntry.delete(0,结束)
年份条目。插入(0,,)
#---创建提交按钮---
submitBtn=ttk.按钮(根,text='Submit')
提交日期绑定(“”,选择日期)
submitBtn.grid(行=3,列=0,列span=5,粘性=NSEW)
#菜单栏
菜单=菜单(根)
root.config(menu=menu)
#子菜单
子菜单=菜单(菜单)
menu.add_级联(label='About',menu=子菜单)
子菜单.添加命令(label=“Information”)
子菜单。添加命令(label='Exit')
#---选择排序方法的单选按钮---
#标签
sortByRB=ttk.Label(root,text='Sort by:')
sortByRB.grid(行=1,列=0,粘性=E)
#单选按钮
stateSortRB=ttk.Radiobutton(根,text='State',value=1,variable=sortNum)
#按州排序
stateSortRB.grid(行=2,列=1,粘性=W)
dateSortRB=ttk.Radiobutton(root,text='Date',value=2,variable=sortNum)
#按日期排序
dateSortRB.grid(行=2,列=2,粘性=W)
officeSortRB=ttk.Radiobutton(根,text='Office',value=3,
变量=sortNum)#按办公室排序
网格(行=2,列=3,粘性=W)
#---显示数据的文本小部件---
数据=文本(根,宽度=50,高度=25,换行=WORD)
网格(行=4,列=0,列span=4,粘性=NSEW)
#---滚动条---
scroll=ttk.Scrollbar(根,命令=data.yview)
数据['yscrollcommand']=scroll.set
scroll.grid(行=4,列=5,pady=3,粘性=NSEW)
#窗口图标
#---让窗户开着---
root.mainloop()
看起来您的notes变量返回为无。您可以这样做,使代码更健壮,更易于调试:

        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)
我发现了另一个bug并修复了它

       '&sort=' + str(sort))
       #          ^^^ added the str()
现在代码运行了。默认情况下,应打开其中一个单选按钮


我确实看到一些注释返回为未定义,但我想说,这不是一个bug。这只是一个你正在爬行的页面上有什么信息的问题。我确实看到一些其他字段也返回为未定义的,但是,同样,这是爬行页面的函数

您试图连接
“Notes:”
Notes
,但有时
Notes
是None,您无法将None添加到字符串中。您可以手动转换:

'\n' 'Notes: ' + str(notes) +
。。。但我认为利用Python的
str.format()
方法会更容易,它会自动将参数转换为字符串(在格式字符串中没有任何说明的情况下):

。。。也可以使用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)

始终进行完整的回溯,还有其他有用的信息,如果您将代码简化为关注文本小部件问题的代码,您就更有可能获得帮助。请求获取&JSON内容与此无关。只需制作一个最小的GUI,并提供一些假的
        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)