О приложении:
- Существует основное приложение, которое позволяет вам выбрать, какой набор изображений вы хотите загрузить.
- После того, как пользователь выбирает набор (можно выбрать несколько наборов одновременно), отображается окно загрузки с индикатором выполнения.
- Основным приложением является root окно и загружаемые файлы windows toplevel windows с родителем как root.
- При загрузке набора изображений используется
multiprocessing.pool.ThreadPool
Проблема:
- Когда есть одна загрузка, т.е. только одно окно верхнего уровня, никаких проблем не возникает, но если есть одновременные загрузки, то есть несколько уровней верхнего уровня windows, то эти windows влияют друг на друга.
- Под влиянием друг на друга я имею в виду, что процентная метка повреждена и смешана с другими процентными метками, а также сам процесс загрузки (запись содержимого в файл) также перепутан.
Что я пробовал:
- Создание верхнего уровня windows с root в качестве родителя, например
top = tk.Toplevel(root)
.
Результат : Та же проблема, что и раньше. - Создание верхнего уровня windows без родителя, например
top = tk.Toplevel()
.
Результат: Та же проблема, что и раньше. - Использование
ThreadPoolExecutor
из concurrent.futures
с методами map()
и submit()
.
Результат: Та же проблема, что и раньше. - Удаление
ThreadPool
и использование простого for
l oop.
Результат: Основное приложение зависает до тех пор, пока не завершится l oop, а также загрузка замедляется при загрузке по одной за раз.
Код:
# imports done already
# running main application
def start_gui():
root = tk.Tk()
root.wm_iconbitmap('logo.ico')
MainWindow(root)
root.mainloop()
# class for download window
class DownloadBox:
def __init__(self, root, urls, name, download_path):
# creating toplevel window without parent
top = tk.Toplevel()
top.protocol('WM_DELETE_WINDOW', lambda: None)
top.geometry('400x120+{}+{}'.format((root.winfo_rootx() + root.winfo_width() - top.winfo_width()) // 2,
(root.winfo_rooty() + root.winfo_height() - top.winfo_height()) // 2))
top.resizable(0, 0)
top.title(name)
top.wm_iconbitmap('logo.ico')
top.configure(background='#d9d9d9')
# ... adding widgets to window
# progressbar widget
self.style = ttk.Style(top)
self.style.layout('text.Horizontal.TProgressbar',
[('Horizontal.Progressbar.trough',
{'children': [('Horizontal.Progressbar.pbar',
{'side': 'left', 'sticky': 'ns'})],
'sticky': 'nswe'}),
('Horizontal.Progressbar.label', {'sticky': ''})])
self.style.configure('text.Horizontal.TProgressbar', text='0%')
self.progress_bar = ttk.Progressbar(top)
self.progress_bar.place(relx=0.025, rely=0.450, height=20, width=380)
self.progress_bar.configure(orient='horizontal')
self.progress_bar.configure(length=len(urls))
self.progress_bar.configure(mode='determinate')
self.progress_bar.configure(style='text.Horizontal.TProgressbar')
self.progress_bar.configure(value=0)
# ThreadPool implementation
ThreadPool(10).imap_unordered(self.download, self.urls)
# tried this
# for url in self.urls:
# self.download(url)
# progress bar increment function
def increment_progressbar(self):
self.current_value += 1
self.progress_bar['value'] = self.current_value * 100 / len(self.urls)
# this percentage label gets mixed up with other windows
self.style.configure('text.Horizontal.TProgressbar', text='{:0.0%}'.format(self.current_value / len(self.urls)))
if self.current_value == len(self.urls):
self.top.after(1000, self.top.destroy)
# ThreadPool calls this function
def download(self, url):
if not self.cancel_download:
try:
response = requests.get(url, stream=True)
with open(self.download_path + '/' + url.split('/')[-1], 'wb') as f:
for chunk in response:
f.write(chunk)
if self.top.winfo_exists():
self.increment_progressbar()
except:
messagebox.showinfo(title='Connection Timed Out', message='{} Couldn\'t be Downloaded... Check Internet Connection or Try Again Later'.format(self.name))
self.cancel()
# cancel button functionality
def cancel(self):
self.cancel_download = True
self.top.destroy()
# class for main applicaton
class MainWindow:
def __init__(self, top):
top.geometry('810x600+300+80')
top.resizable(0, 0)
top.configure(background='#d9d9d9')
self.top = top
# initializing on startup
self.init_func()
def init_func(self):
# doing somework
# pseudocode for checking for button click
if button.cliked():
self.download(args)
def download(self, args):
urls, name, download_path = args
# passing parent window only to get dimensions
DownloadBox(self.top, urls, name, download_path)
if __name__ == '__main__':
start_gui()
Что я ожидаю:
- Сделайте загрузку windows Inde зависит друг от друга, то есть загрузка должна обрабатываться независимо, а также процентные метки
- Использовать
ThreadPool
при загрузке
Вопросы:
- Существуют ли какие-либо другие средства для загрузки?
- Должен ли я использовать
ProcessPool
, если да, то как?
Обновление:
# inside __init__ method of DownloadBox class
self.style.layout('{}.text.Horizontal.TProgressbar'.format(self.name),
[('Horizontal.Progressbar.trough',
{'children': [('Horizontal.Progressbar.pbar',
{'side': 'left', 'sticky': 'ns'})],
'sticky': 'nswe'}),
('Horizontal.Progressbar.label', {'sticky': ''})])
self.style.configure('{}.text.Horizontal.TProgressbar'.format(self.name), text='0%')
# inside increment_progressbar method
# here the variable name is passed as parameter to increment_progressbar method
self.style.configure('{}.text.Horizontal.TProgressbar'.format(name), text='{:0.0%}'.format(self.current_value / len(self.urls)))
Результат: progress_bar
работает нормально ... Но проблема все еще существует с ThreadPool