I made some functions which grab information from websites like weather or news using Python and BeautifulSoup. Basically web scraping.
Now I would like to run them in a different window with Tkinter. How could I implement the weather function, as an example, in my code in order to have it shown not just in the terminal of my text editor but in this Tkinter window?
That's my code for the Tkinter window:
from tkinter import *
from PIL import ImageTk, Image
from weather import weather_def # that's the function I made in a different file
# main
window = Tk()
window.title('My Overview')
window.configure(background='white')
# create a entry text box
textentry = Entry(window, width=15, bg='white')
textentry.place(x=80, y=40)
# create label
label = Label(window, text='Empty label', bg='white', fg='black', font='none 12 bold')
label.place(x=80, y=120)
# add a submit button, command=click, width=10
btn = Button(window, text='Show information', command=weather_def)
btn.place(x=80, y=80)
# run the main loop
window.mainloop()
And that's the function which grabs information about the weather from a website:
from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup
url_weather = 'https://www.mein-wetter.com/wetter/ebermannstadt.htm'
# opening up connection, grabbing the page
uClient_weather = uReq(url_weather)
page_html_weather = uClient_weather.read()
uClient_weather.close()
# html parsing
page_soup_weather = soup(page_html_weather, "html.parser")
# grabs containers
containers_weather = page_soup_weather.find('ul', {'class': 'uldet'}).findAll('li')
# grabs time, temperature, text
def weather_def():
print('Wetter')
print('::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::')
for container in containers_weather:
time = container.findAll('h4')[0].text
temperature = container.findAll('dd', {'class': 'temp'})[0].text
image_alt = container.find('img').get('alt')
# only show weather from 6:00 to 23:00
if int(time) == 00:
break
elif int(time[1]) >= 6 or int(time[0]) > 0:
print(time + ':00 Uhr')
print(temperature)
print(image_alt)
print('-----------------------------------------')
Hi #jumuell and welcome to Stack Overflow. I have seen the comments, but I am answering your question because I found a problem that the others did not.
For your problem, you need to show multiple lines of text as a result. The Text widget allows multiple lines to be typed in it. But the logic part and the GUI part are in separate files.
Create a new Text widget in the GUI file.
name_of_text_widget = Text(window)
name_of_text_widget.pack
(You can change pack to grid or place as per your wish.)
Then in the function, make a list which has all the data to be returned. In your case it would be:
#Add this line at the bottom of the function named `weather_def`
return ['Wetter', ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::', time + ':00 Uhr',
temperature, image_alt, '-----------------------------------------']
Create a new function and link it in the GUI file to a button(instead of directly calling weather_def function).Then type the following code in there:
def function_name():
#This function should be in the GUI file
list_from_weather_output = weather_def()
for i in list_from_weather_output:
name_of_text_widget.insert(END, i)
Related
Im using a function to generate my labels and each label must open different URLs. So far no matter what text i click it opens the last URL
from tkinter import *
import webbrowser
def callback(url):
webbrowser.open_new(url)
def write_hello():
tuples = (('bing ', 'https://bing.com'), ('youtube ', 'https://youtube.com'), ('google ', 'google.com'))
for label_text, url in tuples:
text = mytext.cget("text") + '\n' + str(label_text)
mytext.pack()
mytext.configure(text=text)
mytext.bind("<Button-1>", lambda e, _url=url: callback(_url))
root = Tk()
button = Button(root, text = "Hello!", command = write_hello )
button.pack()
mytext=Label(root,bg="pink",width=30,height=20)
root.mainloop()
Try the following
from tkinter import *
import webbrowser
def callback(event, url):
print(url)
webbrowser.open_new(url)
def write_hello():
tuples = (('bing ', 'https://bing.com'), ('youtube ', 'https://youtube.com'), ('google ', 'google.com'))
for label_text, url in tuples:
new_label = Label(root)
new_label['text'] = label_text
new_label.bind("<Button-1>", lambda e,x=url: callback(e,x))
new_label.pack()
root = Tk()
button = Button(root, text = "Hello!", command = write_hello )
button.pack()
mytext=Label(root,bg="pink",width=30,height=20)
root.mainloop()
As per your code example, after you press the "Hello!" button, new labels will be created which contain the URL name and when clicked on will take you to the correct webpage.
The difficulty here is caused by using labels rather than buttons. The bind method for the clicking on the label sends an "event" parameter to the function called which needs to be taken in to account when creating your lambda function.
With regards to your related question, infinite loops and tkinter GUI's don't play well together as the infinite loop will block the GUI from reacting to user events. If you need to do something in an infinite loop, consider looking at threads (outside of the scope of this specific question)
In this code, I'm trying to open PDFs as a separate window using tkinter and tkPDFViewer. The way this is done is a simple UI opens up with 2 buttons for 2 articles that are accessed from my computer in this case (but I just labeled it "filepath" for the file path to avoid revealing personal info). However, while the buttons work and open up a PDF window and a download window below that (as expected), when you close the PDF window (and the download window closes along with it) and then re-click on a button (the same one or a different one), it shows the previous pdf and the current one that appeared when you re-click combined. As you repeat this process, the PDFs just append to each other. However, what should happen is that the corresponding PDF to the article that is described in the button is shown and no other PDFs. How do I fix this? I've already tried to move the PDF initialization outside the event function then re-initialize it, but that results in a "local variable used before being defined" error, so that doesn't work, and I currently do not know of any other way to do this.
pdfArray = [
["skweak: Weak Supervision Made Easy for NLP",
r"filepath",
"Pierre Lison, Jeremy Barnes, and Aliaksandr Hubin",
"skweak, NLP, Natural Language Processing"],
["Information Theoretic-Based Quality Measures for Clustering",
r"filepath",
"Barry Drake, and Tiffany Huang",
"Quality Measures, Clustering, Data"
]
]
#Import tkinter library
import os
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkPDFViewer import tkPDFViewer as pdfViewer
from functools import partial
import shutil
#Create an instance of tkinter frame or window
mainWin= Tk()
#Set the geometry of tkinter frame
mainWin.geometry("1000x600+20+20")
download_icon = tk.PhotoImage(file=r'filepath')
paw_icon = tk.PhotoImage(file=r'filepath')
def download_pdf(original_file):
#this if-else statment detects if the original file exists.
if os.path.isfile(original_file):
newFileStem = os.path.expanduser('~') + r"\Downloads\ChatBox_download"
num = 0
while os.path.isfile(newFileStem + "(%d).pdf"%num):
num = num + 1;
newFile = newFileStem + "(%d).pdf"%num
f = open(newFile, "x")
shutil.copyfile(original_file, newFile)
completeWin = Toplevel(mainWin)
completeWin.geometry("400x75+660+480")
completeWin.title("Download Complete")
Label(completeWin, text = "Download Complete!", font = ('Gabriola', 14, 'bold')).pack()
Label(completeWin, text = str(newFile), font = ('Gabriola', 10)).pack(pady=2)
else:
notFoundWin = Toplevel(mainWin)
notFoundWin.geometry("200x75+660+480")
notFoundWin.title("File Not Found")
Label(notFoundWin, text = "File Not Found", font = ('Gabriola', 14, 'bold')).pack(pady=20)
#Define a new function to open the window
def open_win(pdf_ID):
pdf_title = pdfArray[pdf_ID][0] #title of pdf
file_location = pdfArray[pdf_ID][1] #location of pdf
authors = pdfArray[pdf_ID][2] #authors
keywords = pdfArray[pdf_ID][3] #keywords
pdfWin = Toplevel(mainWin)
pdfWin.geometry("600x350+640+20")
pdfWin.title(pdf_title)
# Adding pdf location and width and height.
pdfViewer.ShowPdf().pdf_view(pdfWin, pdf_location=file_location, width=70, height=100).pack(ipadx = 10, ipady = 10)
infoWin = Toplevel(pdfWin)
infoWin.geometry("600x200+640+420")
infoWin.title("PDF Information")
Label(infoWin, text = "Information: ", font = ('Gabriola', 15, 'bold')).pack(pady=2)
Label(infoWin, text = "Title: " + pdf_title, font = ('Gabriola', 12)).pack(pady=1)
Label(infoWin, text = "Author(s): " + authors, font = ('Gabriola', 12)).pack(pady=1)
Label(infoWin, text = "Keyword(s): " + keywords, font = ('Gabriola', 12)).pack(pady=1)
tk.Button(infoWin, image=download_icon, borderwidth = 0, command=partial(download_pdf, file_location)).pack(pady=1)
#Create a label
Label(mainWin, text= "Click any button below to open a PDF", font= ('Gabriola', 25, 'bold')).pack(pady=30, padx = 10)
#Create a button to open a New Window
for ID in range(0, len(pdfArray)):
tk.Button(mainWin, image=paw_icon, compound=LEFT, text=" Open \" " + pdfArray[ID][0]+" \"", font= ('Gabriola', 12), command=partial(open_win, ID)).pack(pady=2)
mainWin.mainloop()
I ran into this issue as well when using tkPDFViewer.
To fix I had to go into the tkPDFViewer module and add a line:
self.img_object_li.clear()
immediately after the line:
def add_img():
I'm just a beginner myself so this was my fix to get around the issue.
From what I understand the line:
self.img_object_li.append(timg)
adds images indefinitely, I was trying to destroy the frame and widgets containing the objects from my main program but couldn't get it to work at all. Issue just kept persisting. This is probably not the best way to do it and someone else will ahve a better fix.
I write a code like this:
import tkinter as tk
from tkinter import *
def note1():
window = tk.Tk()
window.title("Not")
window.geometry("300x600+90+0")
notes = Entry(window,font="Verdana 14 italic")
notes.pack()
notesf = tk.Label(text=notes,font = "Verdana 10 italic" )
notesf.pack
window = tk.Tk()
window.title("Note")
window.geometry("1680x1050+90+0")
monhts = tk.Label(text="OCAK",font = "Verdana 50 italic")
monhts.pack()
day1= tk.Button(text="1 Ocak",font = "Verdana 30 italic",command=note1)
day1.place(x=75,y=250)
window.mainloop()
But my problem isn't with all code if you look at the Def line everything seems normal but I just want to save this entry like you just write x person will pay 10 dollars and you close the program after that. When you open it again it shouldn't disappear. I have just trying to solve this for a couple of hours but I still don't have an idea. Please help me.
Here we are writing entries:
Welcome to Stack Overflow!
In order to achieve this, you will need to save the contents in a text file and then retrieve them when required. Here is a working example of what you are looking for:
import tkinter as tk
from tkinter import *
def save(cont, win):
with open ('save.txt', 'w+') as file:
file.write(cont.get())
file.close()
win.destroy()
def retrieve(cont):
with open ('save.txt', 'r') as file:
data = file.read()
file.close()
return data
def note1():
window1 = tk.Toplevel()
window1.title("Not")
window1.geometry("300x600+90+0")
content = StringVar()
notes = Entry(window1, font="Verdana 14 italic", textvariable = content)
notes.pack()
try:
content.set(retrieve(content))
except:
pass
notesf = tk.Label(text=notes,font = "Verdana 10 italic" )
notesf.pack
window1.protocol("WM_DELETE_WINDOW", lambda cont = content, win = window1: save(cont, win))
window = tk.Tk()
window.title("Note")
window.geometry("1680x1050+90+0")
monhts = tk.Label(text="OCAK",font = "Verdana 50 italic")
monhts.pack()
day1= tk.Button(text="1 Ocak",font = "Verdana 30 italic",command=note1)
day1.place(x=75,y=250)
window.mainloop()
Notes:
I have attached the Entry to a StringVar(), by doing so, I can easily use the .get() and .set() method for getting and setting the content respectively.
You were having 2 instances of Tk() in the same mainloop(), which is not appreciated, make sure you have one Tk() in one mainloop() and the others as Toplevel().
I have used .protocol("WM_DELETE_WINDOW", ...) method on the window since you wanted the save to happen upon closing the window.
The approach that I have mentioned ensures your Entry will display the previously entered data even if you kill the parent window after saving, but if you like to not have the data after killing the parent window and only have it during the runtime, I suggest you to just make use of the StringVar and then set it's previous value, every time you click the button.
Goal: push a button to show and change the background of elements from a list one by one with a delay of n seconds until the entire text is highlighted.
I wrapped:
text2.insert(INSERT,sonsilabo,"silabo")
text2.pack()
time.sleep(2)
in a for-loop to do that for each element.
But the program still wait for the entire cycle to finish and show the final result.
from Tkinter import *
import Tkinter
import time
root = Tk()
text2 = Text(root)
text2.tag_config("silabo",background="green")
teksto = ['Sa','lu','to','n',' ','mi',' ','no','mi','ĝa','s',' ','A','b','de','l']
def kolorigu():
text2.delete('1.0', END)
for sonsilabo in teksto:
text2.insert(INSERT,sonsilabo,"silabo")
text2.pack()
time.sleep(2)
text2.pack()
B = Button(root, text ="Kolorigu", command = kolorigu)
B.pack()
root.mainloop()
Any idea?
After you add some text, you need to update the textbox by calling text2.update_idletasks:
def kolorigu():
text2.delete('1.0', END)
for sonsilabo in teksto:
text2.insert(INSERT,sonsilabo,"silabo")
########################
text2.update_idletasks()
########################
time.sleep(2)
Also, I removed the text2.pack() line inside kolorigu because it is unnecessary.
I'm trying to create a program in python using tkinter, and this program is supposed to have a list of books created by the user. On the main window (the one with the list), there should be a menubar with the option to add a book to the list. When clicked, this option should open another window, this time with one entrybox, where the user should enter the book's title and an add button, to add the button to the list.
The list is saved in a .txt file.
This is the program I wrote so far:
import sys
from tkinter import *
def newBook():
def add():
BookTitle = v.get()
bookTitle = '\n' + BookTitle
books = open('c:/digitalLibrary/books.txt', 'a')
books.write(bookTitle)
books.close()
addWindow = Tk()
v = StringVar()
addWindow.geometry('250x40+500+100')
addWindow.title('digitalLibrary - Add Book')
newBookEntry = Entry(addWindow,textvariable=v)
newBookEntry.pack()
addButton = Button(addWindow, text='ADD', command=add)
addButton.pack()
def refresh():
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
def quitProgram():
tfQuit = messagebox.askyesno(title='Close Program', message='Are you sure?')
if tfQuit:
window.destroy()
window = Tk()
menubar = Menu(window)
window.geometry('400x400+200+100')
window.title('digitalLibrary')
booksmenu = Menu(menubar, tearoff=0)
booksmenu.add_command(label='Add Book', command=newBook)
booksmenu.add_command(label='Delete Book')
booksmenu.add_command(label='Close Program', command=quitProgram)
menubar.add_cascade(label='digitalLibrary', menu=booksmenu)
books = open('c:/digitalLibrary/books.txt', 'r')
bookList = books.readlines()
books.close()
for i in range (0, len(bookList)):
bookOne = Label(window, text=bookList[i])
bookOne.grid(row=i, column=0, sticky=W)
refreshButton = Button(window, text='Refresh', command=refresh)
refreshButton.grid(row=0, column=1)
window.config(menu=menubar)
window.mainloop()
It seems logical to me that this should work, but it just doesn't. When I click the ADD button on the Add Book window, all it does is add the line break to the .txt file.
I know that it works if I use the OS library and create a separate python file for the add book window, but I'd rather put it all in one code, if possible.
I've tried many things, and tried searching it in the web, but I got nowhere.
The root cause of your problem is that you are creating more than once instance of Tk. You cannot do this. If you want to create a popup window, create an instance of Toplevel. A proper Tkinter application creates exactly once instance of Tk with exactly one invocation of mainloop.
If your main goal is to simply get input from the user (versus learning how to write your own dialog), you might want to consider using one of the built-in dialogs.
For example:
import tkinter.simpledialog as tkSimpleDialog # python 3.x
...
def newBook():
BookTitle = tkSimpleDialog.askstring("Add Book","What is the name of the book?")
if BookTitle is not None:
bookTitle = '\n' + BookTitle
books = open('/tmp/books.txt', 'a')
books.write(bookTitle)
books.close()