Thread : main thread is not in mainloop()
Bonjour à tous,
J'espère que tout va bien pour vous en ces temps de crise sanitaire.
Pour un projet j'ai réalisé un programme en Python. Ce programme permet de scraper (avec bs4) un site de bourse et ensuite d'afficher les valeurs scrapées dans un graphique sur une fenêtre Tkinter. J'ai ajouté un thread pour pouvoir scraper les valeurs tout en continuant à mettre à jour le graphique.
Le programme fonctionne correctement à première vue, mais au bout du deuxième ou troisième lancement il plante et affiche une erreur "main thread is not in mainloop()" puis "une erreur est survenue lors du démarrage du noyau" ou "Tcl_AsyncDelete: async handler deleted by the wrong thread".
J'ai essayé de jouer sur les temps de boucle (after et time.sleep) et j'ai mis des join() pour être sûr que le thread se termine en fermeture du programme.
J'ai effectué différentes recherches, mais je n'ai toujours pas trouvé solution à mon problème.
Code :
Fichier principale
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
#Librairies
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
matplotlib.use('Agg')
import time
import pandas as pd
from threading import Thread
from tkinter import messagebox
#Importation du fichier scraping.py
import scraping
#variables
global plot_counter
plot_counter= 0
global launch_scraping
launch_scraping = False
global df
df = pd.DataFrame(columns = ['value', 'time', 'trend'])
#Appelle une fois la fonction start_scraping dans scraping.py
def data_scraping():
global df
print("Scraping")
df=scraping.start_scraping(df)
df = df.sort_values('time')
time.sleep(5)
#Appelle data_scraping si launch scraping est True
def scrap_thread():
global launch_scraping
print("Thread beginning")
while True:
if launch_scraping:
data_scraping()
else:
break
print("Thread ending")
# Si scrap_button est True launch scraping devient false et inversement
#S'il est faux => lance le thread et la fonction refresh
def click_scrap_button(event):
global launch_scraping
if launch_scraping:
launch_scraping = False
scrap_button_var.set("Launch scraping")
else:
scrap_button_var.set("Stop scraping")
launch_scraping = True
global thread_1
thread_1=Thread(target=scrap_thread)
thread_1.start()
refresh()
#refresh le graphique dans la fenêtre tkinter avec les nouvelles données scrapées
def refresh():
global plot_counter
global launch_scraping
if not launch_scraping:
thread_1.join()
print("No refreshing")
return plot_counter += 1
print("Refreshing") # deleting previous line
ax.lines.pop(0)
# create a new line
ax.plot(df.time[:plot_counter], df.value[:plot_counter])
canvas.draw()
#wait 5 s then call refresh again
app.after(8000, refresh)
#Permet de quitter l'application
def quit_app():
global launch_scraping
launch_scraping = False
thread_1.join()
if messagebox.askokcancel("Quit", "Do you want to quit?"):
app.destroy() #Creating the scraping application
#Initialisation de la fenêtre tkinter
app = tk.Tk()
frame_top = tk.Frame(app)
frame_top.pack(side='top', fill="both")
frame_title = tk.Frame(app)
frame_title.pack(fill="both")
title=tk.Label(frame_title, text="DAX price of share")
title.pack()
frame_center = tk.Frame(app)
frame_center.pack(fill="both")
frame_under_fig=tk.Frame(app)
frame_under_fig.pack(fill="both")
scrap_button_var= tk.StringVar()
scrap_button=tk.Button(frame_under_fig, textvariable=scrap_button_var)
scrap_button.pack()
scrap_button_var.set("Launch scraping") fig = Figure()
canvas = FigureCanvasTkAgg(fig, frame_center)
canvas.get_tk_widget().pack()
ax = fig.add_subplot(111)
ax.plot([], [])
app.bind("<ButtonRelease-1>", click_scrap_button)
app.protocol("WM_DELETE_WINDOW", quit_app)
app.mainloop() |
fichier scraping.py
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
import pandas as pd
from bs4 import BeautifulSoup
from datetime import datetime,timedelta
import requests
#Scrap les valeurs de la bourse et les insère dans une dataframe
def start_scraping(dataframe):
info_scrap={}
page = requests.get("https://www.boursorama.com/bourse/indices/cours/5pDAX/")
soup = BeautifulSoup(page.text,'lxml')
# we assign to "value" the last value of exchange rate we scraped
value= soup.find("span",attrs={"class":"c-instrument c-instrument--last"}).text
# this value is a string, we need to replace space by empty string before converting it into float
value=value.replace(' ','')
print(value)
#we assign to trend the last value of the trend we scraped
trend=soup.find("span",attrs={"class":"c-instrument c-instrument--variation"}).text
current_time=datetime.now()
# value is a string we convert it into float and then add it to the dictionnary info_scrap
info_scrap["value"] = float(value)
info_scrap["trend"]=trend
info_scrap["time"]=current_time
# each value of the dictionnary are appended to the dataframe
dataframe=dataframe.append(info_scrap, ignore_index=True)
# the function return the dataframe we put in parameter
return dataframe |
Merci d'avance pour votre aide,
Bonne journée,
Webuzo :)