#python #python-3.x #multithreading #tkinter #python-imaging-library
#python #python-3.x #многопоточность #tkinter #python-imaging-library
Вопрос:
Я пытаюсь создать систему для отображения заставки, когда загружается первая страница моего основного графического интерфейса (обе сделаны с помощью tkinter). Я прочитал в Интернете, что tkinter должен иметь все графические команды для определенного окна tkinter в одном потоке. Итак, моя идея отобразить заставку в окне верхнего уровня во втором потоке, в то время как главная страница загружается в первом потоке (главная страница скрыта, пока отображается заставка withdraw()
), не сработает. Поэтому я решил создать отдельные графические интерфейсы для заставки и основного приложения. Главная страница будет инициализироваться во время отображения заставки, и после этого заставка будет уничтожена, а главная страница снова появится с использованием deiconify()
. Я попробовал эту идею, используя 2 простых графических интерфейса, и она сработала, но когда я реализовал ее в своем коде для заставки, по какой-то причине я получил следующую ошибку.
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libthreading.py", line 932, in _bootstrap_inner
self.run()
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libthreading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "c:/Jovan/Captain!!Project/Captain_repo/rough.py", line 7, in import_
import rough1
File "c:JovanCaptain!!ProjectCaptain_reporough1.py", line 71, in <module>
splash.display()
File "c:JovanCaptain!!ProjectCaptain_reporough1.py", line 56, in display
self.tkimage = ImageTk.PhotoImage(image1)
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libsite-packagesPILImageTk.py", line 112, in __init__
self.__photo = tkinter.PhotoImage(**kw)
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libtkinter__init__.py", line 4061, in __init__
Image.__init__(self, 'photo', name, cnf, master, **kw)
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libtkinter__init__.py", line 4006, in __init__
self.tk.call(('image', 'create', imgtype, name,) options)
RuntimeError: main thread is not in main loop
Exception ignored in: <function PhotoImage.__del__ at 0x04EF02B0>
Traceback (most recent call last):
File "C:UsersjovanAppDataLocalProgramsPythonPython38-32libsite-packagesPILImageTk.py", line 118, in __del__
name = self.__photo.name
AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
Я нашел в Интернете вопросы с похожими ошибками, но все они предполагали, что изображение, переданное в PhotoImage, не было PIL-изображением, что мне не очень помогло. Заставка работает нормально, если я просто выполняю ее или импортирую из другого файла, но когда я помещаю ее в поток, я получаю указанную выше ошибку. Вот воспроизводимый код для моей проблемы.
'''The code is executed here.'''
import tkinter as tk
import time
import threading
def import_() :
import rough1
# creating and starting thread to display splash
splash_thread = threading.Thread(target=import_)
splash_thread.daemon = True
splash_thread.start()
# Main GUI
root = tk.Tk()
root.withdraw()
root.geometry("300x300")
root.protocol("WM_DELETE_WINDOW",root.quit())
root.title("rough")
root.update()
tk.Button(root,text="Hey").pack()
tk.Label(root,text="Hello").pack()
root.update_idletasks()
# Re-displays the main GUI if the splash has been destroyed.
print(threading.active_count())
while threading.active_count() == 2 :
print("loading...")
time.sleep(0.5)
else :
root.deiconify()
print(threading.active_count())
root.mainloop()
Код для заставки:
import tkinter as tk
from PIL import Image, ImageTk
import tkinter.ttk as ttk
import time
from ttkthemes import ThemedTk
class SplashScreen(ThemedTk) :
def __init__(self,**kwargs) :
ThemedTk.__init__(self,theme="black",**kwargs)
self.overrideredirect(True)
# self.title("[GAME INITIALIZING...]")
self.splash()
self.update()
self.window()
def splash(self) :
self.image1 = Image.open('project_pics\splash.png')
def window(self) :
width, height = self.image1.size
setwinwidth = (self.winfo_screenwidth()-width)//2
setwinheight = (self.winfo_screenheight()-height)//2
self.geometry(f"{width}x{height} {setwinwidth} {setwinheight}")
self.update()
def display(self) :
print("splash_Displayed!!")
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
splash_canvas = tk.Canvas(self,width=screen_width,height=screen_height)
splash_canvas.pack()
self.update_idletasks()
self.tkimage = ImageTk.PhotoImage(self.image1)
self.update_idletasks()
splash_canvas.create_image(0,0,image=self.tkimage,anchor="nw")
splash_canvas.image = self.tkimage
self.update_idletasks()
splash_canvas.create_text(200,34,text="[LOADING]...PLEASE WAIT",font=("small fonts",20),fill="white")
progressbar = ttk.Progressbar(orient=tk.HORIZONTAL, length=900, mode='determinate',style="Horizontal.TProgressbar")
progressbar.place(x=450,y=25)
progressbar.start()
self.after(5050,self.destroy)
self.mainloop()
splash = SplashScreen()
splash.display()
Итак, это мои запросы. Почему я получаю указанную выше ошибку и как мне ее исправить??
Комментарии:
1.
Toplevel
не является отдельной частью, и ее следует использовать в основном потоке.2. ошибка сначала покажет
main thread is not in main loop
вам, что Tkinter должен запускать только одинmainloop
, и он должен запускать его в основном потоке. Итак, ваша настоящая проблема неPhotoImage
в том, ноmainloop
и в использовании thread . Вам действительно нужно использовать thread?3. @furas Главная страница моего графического интерфейса загружается не очень быстро. Таким образом, вы можете увидеть что-то вроде сбоя на экране в течение секунды, пока он настраивается. Я хотел скрыть это, загрузив главную страницу во время отображения заставки. Вот почему я использовал поток для параллельного выполнения обоих.
4. что такого большого вы загружаете на этой странице? если вы загружаете какой-либо файл, вы можете загрузить его в разделенный файл, но позже вы должны добавить его в окно tkinter в основном потоке. И в то же время заставка должна отображаться в основном потоке
5.
time.sleep()
заставка, чтобы у вашего главного окна было некоторое время для загрузки