How to format text being called from BS4 onto Tkinter?
Question:
This is for a personal D&D project of mine.
I’m trying to get text to come out nicely in my last frame using BS4 and Tkinter.
The goal is that when I click on a new spell in my listbox, the appropriate information will display neatly below what I clicked.
Here is my code:
from tkinter import *
from bs4 import BeautifulSoup
import requests
book = Tk()
book.title('Pick a spell')
book.geometry("900x900")
html_text = requests.get('http://dnd5e.wikidot.com/spell:haste').text
soup = BeautifulSoup(html_text, 'html.parser')
spell_levels = {"Cantrip": "0", "First Level": "1", "Second Level": "2",
"Third Level": "3", "Fourth Level": "4", "Fifth Level": "5",
"Sixth Level": "6", "Seventh Level": "7","Eighth Level": "8",
"Ninth Level": "9"}
spells_by_class = ["All Spells", "Artificer", "Bard", "Cleric", "Druid",
"Eldritch Knight", "Paladin", "Ranger", "Arcane Trickster", "Sorcerer",
"Warlock", "Wizard"]
def spell_text(event):
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
print(spell_name)
for spell in spell_info:
set(spell.text)
def popup():
response = messagebox.showerror("Error",
"There are no spells available for this selection.")
def take_spell_box():
my_listbox.delete(0, END)
url = 'http://dnd5e.wikidot.com/spells'
number = spell_level_clicked.get()
number = spell_levels[number]
if spell_class_clicked.get() == "All Spells":
html_text = requests.get(url).text
soup = BeautifulSoup(html_text, 'html.parser')
elif spell_class_clicked.get() != "All Spells":
dnd_class = spell_class_clicked.get()
html_text = requests.get(url + ":" + dnd_class.replace(" ", "-").lower())
soup = BeautifulSoup(html_text.text, 'html.parser')
list_of_spells = soup.find('div', id = f'wiki-tab-0-{number}')
try:
spells = list_of_spells.find_all("a")
for spell in spells:
my_listbox.insert(END, spell.text)
except AttributeError:
popup()
def define_spell(event):
url = 'http://dnd5e.wikidot.com/spell'
#selected_spell_info.delete(ANCHOR)
selected_spell = my_listbox.get(ANCHOR) or listbox_to_bd.get(ANCHOR)
html_text = requests.get(url + ":" +
selected_spell.replace(" (HB)", "").replace(" (UA)", "").replace(" ", "-").lower())
soup = BeautifulSoup(html_text.text, 'html.parser')
def spell_text():
spell_info_list = []
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
for spell in spell_info:
spell_info_list.append(spell.text)
my_var.set(spell_info_list[:])
spell_text()
#Create frame
frame_one = Frame(book, width=300, height=200)
frame_one.columnconfigure(1, weight=1)
#Classes drop down menu and information
spell_class_clicked = StringVar(frame_one)
spell_class_clicked.set(spells_by_class[0])
spell_class_drop = OptionMenu(frame_one, spell_class_clicked, *spells_by_class)
spell_class_drop.config(width=13)
spell_class_drop.grid(row=1, column=0, columnspan=2, padx=(10,0), sticky=W+E)
#Spell Levels drop down menu and information
spell_level_clicked = StringVar(frame_one)
spell_level_clicked.set("Cantrip")
spell_level_drop = OptionMenu(frame_one, spell_level_clicked, *spell_levels)
spell_level_drop.config(width=13)
spell_level_drop.grid(row=1, column=2, sticky=W+E)
#Submit button to grab the information from the drop down menu
submit_button = Button(frame_one, text="Show Selection", command=take_spell_box)
submit_button.grid(row=2, column=1, columnspan=2, sticky=N)
frame_one.pack()
#Create frame and scrollbar
frame_two = Frame(book, width=300, height=200, pady=10)
listbox_scrollbar = Scrollbar(frame_two, orient=VERTICAL)
#List Box: SINGLE, BROWSE, MULTIPLE, EXTENDED
my_listbox = Listbox(frame_two, width=30,
yscrollcommand=listbox_scrollbar.set, selectmode=SINGLE)
#Configure scrollbar
listbox_scrollbar.config(command=my_listbox.yview)
listbox_scrollbar.grid(row=1, column=3, ipady=56)
my_listbox.grid(row=1, column=1, columnspan=2)
frame_two.pack()
frame_three = Frame(book)
my_listbox.bind("<<ListboxSelect>>", define_spell)
my_var = StringVar()
my_var.set("")
selected_spell_info = Label(frame_three, textvariable=my_var)
#selected_spell_info.config(text="")
selected_spell_info.grid(row=0, column=0, rowspan=5)
frame_three.pack()
book.mainloop()
This is the closest I’ve gotten to what I’m trying to get.
I’ve tried without a list, with my_var.set() in the for loop, but that only gets me the last line of all of the text I am trying to grab.
Not sure what else to try, I’ve searched everything I could think of online to help.
I’m trying to get the text in the previous code to come out like how this prints out:
from bs4 import BeautifulSoup
import requests
html_text = requests.get('http://dnd5e.wikidot.com/spell:haste').text
soup = BeautifulSoup(html_text, 'html.parser')
def spell_text():
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
print(spell_name)
for spell in spell_info:
print(spell.text)
spell_text()
Answers:
The text can be formatted more nicely using
my_var.set("n".join(spell_info_list))
This will join the items with a newline. To stop the text overflowing, add the wraplength
attribute to the Label
selected_spell_info = Label(frame_three, textvariable=my_var, wraplength = 500, justify = "left")
I’ve also added justify = "left"
for left text alignment.
You may also want to consider using a Text
widget instead as these are better suited for multiline text and also allow for more complex formatting.
This is for a personal D&D project of mine.
I’m trying to get text to come out nicely in my last frame using BS4 and Tkinter.
The goal is that when I click on a new spell in my listbox, the appropriate information will display neatly below what I clicked.
Here is my code:
from tkinter import *
from bs4 import BeautifulSoup
import requests
book = Tk()
book.title('Pick a spell')
book.geometry("900x900")
html_text = requests.get('http://dnd5e.wikidot.com/spell:haste').text
soup = BeautifulSoup(html_text, 'html.parser')
spell_levels = {"Cantrip": "0", "First Level": "1", "Second Level": "2",
"Third Level": "3", "Fourth Level": "4", "Fifth Level": "5",
"Sixth Level": "6", "Seventh Level": "7","Eighth Level": "8",
"Ninth Level": "9"}
spells_by_class = ["All Spells", "Artificer", "Bard", "Cleric", "Druid",
"Eldritch Knight", "Paladin", "Ranger", "Arcane Trickster", "Sorcerer",
"Warlock", "Wizard"]
def spell_text(event):
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
print(spell_name)
for spell in spell_info:
set(spell.text)
def popup():
response = messagebox.showerror("Error",
"There are no spells available for this selection.")
def take_spell_box():
my_listbox.delete(0, END)
url = 'http://dnd5e.wikidot.com/spells'
number = spell_level_clicked.get()
number = spell_levels[number]
if spell_class_clicked.get() == "All Spells":
html_text = requests.get(url).text
soup = BeautifulSoup(html_text, 'html.parser')
elif spell_class_clicked.get() != "All Spells":
dnd_class = spell_class_clicked.get()
html_text = requests.get(url + ":" + dnd_class.replace(" ", "-").lower())
soup = BeautifulSoup(html_text.text, 'html.parser')
list_of_spells = soup.find('div', id = f'wiki-tab-0-{number}')
try:
spells = list_of_spells.find_all("a")
for spell in spells:
my_listbox.insert(END, spell.text)
except AttributeError:
popup()
def define_spell(event):
url = 'http://dnd5e.wikidot.com/spell'
#selected_spell_info.delete(ANCHOR)
selected_spell = my_listbox.get(ANCHOR) or listbox_to_bd.get(ANCHOR)
html_text = requests.get(url + ":" +
selected_spell.replace(" (HB)", "").replace(" (UA)", "").replace(" ", "-").lower())
soup = BeautifulSoup(html_text.text, 'html.parser')
def spell_text():
spell_info_list = []
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
for spell in spell_info:
spell_info_list.append(spell.text)
my_var.set(spell_info_list[:])
spell_text()
#Create frame
frame_one = Frame(book, width=300, height=200)
frame_one.columnconfigure(1, weight=1)
#Classes drop down menu and information
spell_class_clicked = StringVar(frame_one)
spell_class_clicked.set(spells_by_class[0])
spell_class_drop = OptionMenu(frame_one, spell_class_clicked, *spells_by_class)
spell_class_drop.config(width=13)
spell_class_drop.grid(row=1, column=0, columnspan=2, padx=(10,0), sticky=W+E)
#Spell Levels drop down menu and information
spell_level_clicked = StringVar(frame_one)
spell_level_clicked.set("Cantrip")
spell_level_drop = OptionMenu(frame_one, spell_level_clicked, *spell_levels)
spell_level_drop.config(width=13)
spell_level_drop.grid(row=1, column=2, sticky=W+E)
#Submit button to grab the information from the drop down menu
submit_button = Button(frame_one, text="Show Selection", command=take_spell_box)
submit_button.grid(row=2, column=1, columnspan=2, sticky=N)
frame_one.pack()
#Create frame and scrollbar
frame_two = Frame(book, width=300, height=200, pady=10)
listbox_scrollbar = Scrollbar(frame_two, orient=VERTICAL)
#List Box: SINGLE, BROWSE, MULTIPLE, EXTENDED
my_listbox = Listbox(frame_two, width=30,
yscrollcommand=listbox_scrollbar.set, selectmode=SINGLE)
#Configure scrollbar
listbox_scrollbar.config(command=my_listbox.yview)
listbox_scrollbar.grid(row=1, column=3, ipady=56)
my_listbox.grid(row=1, column=1, columnspan=2)
frame_two.pack()
frame_three = Frame(book)
my_listbox.bind("<<ListboxSelect>>", define_spell)
my_var = StringVar()
my_var.set("")
selected_spell_info = Label(frame_three, textvariable=my_var)
#selected_spell_info.config(text="")
selected_spell_info.grid(row=0, column=0, rowspan=5)
frame_three.pack()
book.mainloop()
This is the closest I’ve gotten to what I’m trying to get.
I’ve tried without a list, with my_var.set() in the for loop, but that only gets me the last line of all of the text I am trying to grab.
Not sure what else to try, I’ve searched everything I could think of online to help.
I’m trying to get the text in the previous code to come out like how this prints out:
from bs4 import BeautifulSoup
import requests
html_text = requests.get('http://dnd5e.wikidot.com/spell:haste').text
soup = BeautifulSoup(html_text, 'html.parser')
def spell_text():
spell_name = soup.find('div', class_ = 'page-title page-header').text
spell_info = soup.find_all('p')
print(spell_name)
for spell in spell_info:
print(spell.text)
spell_text()
The text can be formatted more nicely using
my_var.set("n".join(spell_info_list))
This will join the items with a newline. To stop the text overflowing, add the wraplength
attribute to the Label
selected_spell_info = Label(frame_three, textvariable=my_var, wraplength = 500, justify = "left")
I’ve also added justify = "left"
for left text alignment.
You may also want to consider using a Text
widget instead as these are better suited for multiline text and also allow for more complex formatting.