Trojians 发表于 2023-3-31 14:29

Python+Tkinter如何控制下载进度条

起因:昨天逛论坛时,看到一位大佬@redballoon 写了一个“[学习记录] 91韩漫采集(异步协程)-- 打包成exe”91韩漫采集(异步协程)-- 打包成exe
https://www.52pojie.cn/thread-1767266-1-1.html
(出处: 吾爱破解论坛)
的帖子,而且也给提供了源代码,下载下来运行了一下,可以下载漫画。但是我想把它改写成GUI程序,现在程序可以正常下载漫画,但是我写的进度条没有工作。而且程序在运行时,会出现“未响应”的提示。
哪位大神或者老哥们路过,可以帮忙看看是怎么回事吗?
# _*_ coding: utf-8 _*_
# @Time: 2023/3/28 23:25
# @Author: 🎈
# @GUI:Trojians
# @File: 91hanman


import aiohttp
import asyncio
import aiofiles
import requests
from urllib.parse import quote
from lxml import etree
import os
from re import sub
import tkinter as tk
import ttkbootstrap as ttk
import tkinter.messagebox
class MangaDownloaderGUI():
    def __init__(self, master):
      self.master = master
      master.geometry('600x371+{}+{}'.format(int((master.winfo_screenwidth()-600)/2), int((master.winfo_screenheight()-371)/2)))
      self.base_url = 'https://9y03.xyz'
      self.label1 = ttk.Label(master,text="请输入漫画名称:", bootstyle='SUCCESS',font=("Helvetica",16))
      self.label1.pack(pady=10)
      self.entry1 = ttk.Entry(master)
      self.entry1.pack(pady=10)
      self.button1 = ttk.Button(master,text="开始下载",command=self.start_download,style='SUCCESS.OUTLINE')
      self.button1.pack(pady=10)
      self.progress_var = tk.StringVar()
      self.progressbar = ttk.Progressbar(master, orient="horizontal", length=400, mode="determinate", variable=self.progress_var)
      self.progressbar.pack(pady=10)
    def get_page(self,url):
      headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54',
            'cookie': 'SL_G_WPT_TO=zh; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1',
            'referer': 'https://9y03.xyz/',
      }
      try:
            with requests.get(url, headers=headers, timeout=(20,50)) as response:
                if response.status_code == 200:
                  return response.text
                else:
                  return 'code error!'
      except ConnectionError as e:
            return
    def parse(self,html):
      dom_tree = etree.HTML(html)
      r_url = dom_tree.xpath('//p[@class="comic__title"]/a/@href')
      detail_url = self.base_url + r_url
      detail_page = self.get_page(detail_url)
      detail_page_tree = etree.HTML(detail_page)
      lis = detail_page_tree.xpath('//div[@class="chapter__list clearfix"]/ul/li')
      for li in lis:
            chapter_link = li.xpath('./a/@href')
            chapter_name = li.xpath('./a/text()').strip()
            mango_url = self.base_url + chapter_link
            yield chapter_name, mango_url
    def get_chapter(self,title, pics_url):
      pic_page_html = self.get_page(pics_url)
      pic_page_tree = etree.HTML(pic_page_html)
      divs = pic_page_tree.xpath('/html/body/div/div')
      for div in divs:
            img_url = div.xpath('./div/img/@data-original')
            img_name = div.xpath('./div/img/@alt')
            yield list(zip(img_url, img_name))
    async def download(self,url, name, folder_path):
      async with aiohttp.ClientSession() as session:
            async with session.get(url)as response:
                img_content = await response.content.read()
                file_path = os.path.join(folder_path, name)
                async with aiofiles.open(file_path, mode='wb')as f:
                  await f.write(img_content)
                  await asyncio.sleep(0.1)
                  self.progressbar.step(1)
                  self.progress_var.set(f"下载进度:{self.progressbar['value']}%") # 更新进度条的文本显示
    async def main(self):
      kw = self.entry1.get()
      url = f'https://9y03.xyz/index.php/search?key={quote(kw)}'
      html = self.get_page(url)
      chapter_data = self.parse(html)
      for chapter_title, chapter_url in chapter_data:
            folder_path = f'{kw}\\'
            title = sub(r'[\\/:\*\?"<>\|]', '', chapter_title)
            folder_path = os.path.join(folder_path, title)
            os.path.exists(folder_path) or os.makedirs(folder_path)
            img_data = self.get_chapter(chapter_title, chapter_url)
            tasks = []
            num_tasks = 0
            for data in img_data:
                for pic_url, pic_name in data:
                  tasks.append(asyncio.ensure_future(self.download(pic_url, pic_name, folder_path)))
                  num_tasks += 1
            self.progressbar["maximum"] = num_tasks # 设置进度条的最大值
            self.progress_var.set("下载进度:0%") # 更新进度条的文本显示
            await asyncio.gather(*tasks)
    def start_download(self):
      asyncio.run(self.main())
root = ttk.Window("漫画下载程序 by redballoon GUI:Trojians","superhero")
my_gui = MangaDownloaderGUI(root)
root.mainloop()



新人第一次发帖,有写的不对的地方,望各位见谅。

狐白本白 发表于 2023-3-31 14:48

增加一个按钮 控制下载函数即可

admib木木 发表于 2023-3-31 16:11

这个问题可能是因为你的下载程序在主线程上运行,导致GUI程序出现“未响应”的提示。建议将下载代码放在单独的线程或进程中运行。

你可以使用Python的threading模块或multiprocessing模块来创建一个新的线程或进程,并在其中运行下载代码。这样,下载将在后台运行,而不会阻塞主GUI线程,从而避免了“未响应”的提示和进度条不工作的问题。

例如,可以将以下代码添加到您的MangaDownloaderGUI类中,以启动一个新的线程并运行下载程序:
import threading

def start_download_thread(self):
    t = threading.Thread(target=self.start_download)
    t.start()
然后,将您的“开始下载”按钮的命令设置为start_download_thread方法,而不是start_download方法:
self.button1 = ttk.Button(master,text="开始下载",command=self.start_download_thread,style='SUCCESS.OUTLINE')
这样,当用户单击“开始下载”按钮时,将启动一个新的线程来运行下载程序,GUI程序将继续响应用户输入和操作,并且您的进度条也应该工作正常了。

Trojians 发表于 2023-3-31 16:21

admib木木 发表于 2023-3-31 16:11
这个问题可能是因为你的下载程序在主线程上运行,导致GUI程序出现“未响应”的提示。建议将下载代码放在单 ...

好的,我看明白了,我去试试,谢谢木木大佬{:1_893:}

kit249 发表于 2023-3-31 17:05

正需要,一起学习,谢谢。

yiwangtang 发表于 2023-3-31 17:53

都是大神级别,厉害厉害&#128077;&#127995;&#128077;&#127995;&#128077;&#127995;&#128077;&#127995;&#128077;&#127995;&#128077;&#127995;&#128077;&#127995;

redballoon 发表于 2023-3-31 18:15

可以的,一起学习。哈哈

redballoon 发表于 2023-4-1 01:46

终于解决未响应问题了,还额外增加了个数据下载显示

```python
import aiohttp
import asyncio
import aiofiles
import requests
from urllib.parse import quote
from lxml import etree
import os
from re import sub
import tkinter as tk
import ttkbootstrap as ttk
from tkinter import scrolledtext
from threading import Thread


class MangaDownloaderGUI():

    def __init__(self, master):
      self.master = master
      master.geometry('600x371+{}+{}'.format(int((master.winfo_screenwidth() - 600) / 2),
                                             int((master.winfo_screenheight() - 371) / 2)))
      self.base_url = 'https://9y03.xyz'
      self.label1 = ttk.Label(master, text="请输入漫画名称:", bootstyle='SUCCESS', font=("Helvetica", 16))
      self.label1.pack(pady=10)
      self.entry1 = ttk.Entry(master)
      self.entry1.pack(pady=10)
      self.button1 = ttk.Button(master, text="开始下载", command=self.start_download_thread, style='SUCCESS.OUTLINE')
      self.button1.pack(pady=10)
      self.progress_var = tk.StringVar()
      self.progressbar = ttk.Progressbar(master, orient="horizontal", length=400, mode="determinate",
                                           variable=self.progress_var)
      self.progressbar.pack(pady=10)

      self.frame = ttk.Frame(master)
      self.frame.pack(pady=20)
      self.scrolled_text = scrolledtext.ScrolledText(self.frame)
      self.scrolled_text.pack()

    def get_page(self, url):
      headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54',
            'cookie': 'SL_G_WPT_TO=zh; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1',
            'referer': 'https://9y03.xyz/',
      }
      try:
            with requests.get(url, headers=headers, timeout=(20, 50)) as response:
                if response.status_code == 200:
                  return response.text
                else:
                  return 'code error!'
      except ConnectionError as e:
            return

    def parse(self, html):
      dom_tree = etree.HTML(html)
      r_url = dom_tree.xpath('//p[@class="comic__title"]/a/@href')
      detail_url = self.base_url + r_url
      detail_page = self.get_page(detail_url)
      detail_page_tree = etree.HTML(detail_page)
      lis = detail_page_tree.xpath('//div[@class="chapter__list clearfix"]/ul/li')
      for li in lis:
            chapter_link = li.xpath('./a/@href')
            chapter_name = li.xpath('./a/text()').strip()
            mango_url = self.base_url + chapter_link
            yield chapter_name, mango_url

    def get_chapter(self, title, pics_url):
      pic_page_html = self.get_page(pics_url)
      pic_page_tree = etree.HTML(pic_page_html)
      divs = pic_page_tree.xpath('/html/body/div/div')
      for div in divs:
            img_url = div.xpath('./div/img/@data-original')
            img_name = div.xpath('./div/img/@alt')
            yield list(zip(img_url, img_name))

    async def download(self, url, name, folder_path):
      async with aiohttp.ClientSession() as session:
            async with session.get(url)as response:
                img_content = await response.content.read()
                await asyncio.sleep(0)
                file_path = os.path.join(folder_path, name)
                async with aiofiles.open(file_path, mode='wb')as f:
                  await f.write(img_content)
                  await asyncio.sleep(0)
                  self.progressbar.step(1)
                  self.progress_var.set(f"下载进度:{self.progressbar['value']}%")# 更新进度条的文本显示

    async def main(self):
      kw = self.entry1.get()
      url = f'https://9y03.xyz/index.php/search?key={quote(kw)}'
      html = self.get_page(url)
      chapter_data = self.parse(html)
      for chapter_title, chapter_url in chapter_data:
            folder_path = f'{kw}\\'
            title = sub(r'[\\/:\*\?"<>\|]', '', chapter_title)
            folder_path = os.path.join(folder_path, title)
            os.path.exists(folder_path) or os.makedirs(folder_path)
            img_data = self.get_chapter(chapter_title, chapter_url)
            tasks = []
            num_tasks = 0

            self.scrolled_text.insert(tk.END, f'正在下载>>> {title}' + '\n')

            for data in img_data:
                for pic_url, pic_name in data:
                  tasks.append(asyncio.create_task(self.download(pic_url, pic_name, folder_path)))
                  num_tasks += 1
            self.progressbar["maximum"] = num_tasks# 设置进度条的最大值
            self.progress_var.set("下载进度:0%")# 更新进度条的文本显示
            await asyncio.gather(*tasks)

    def start_download(self):
      # loop = asyncio.get_event_loop()
      # loop.run_until_complete(self.main())
      # 报错不在事件循环中, 解决是既然没有event loop,把程序最初的loop传递到thread中去:
      # asyncio.set_event_loop(loop) ,这个loop在程序主函数中保留好,传递到thread中去使用。
      loop = asyncio.new_event_loop()
      asyncio.set_event_loop(loop)
      loop.run_until_complete(self.main())

    def start_download_thread(self, ):
      # 根据坛友 admib木木 的解答留言思路创建一个线程函数
      t = Thread(target=self.start_download)
      t.setDaemon(True)
      t.start()


root = ttk.Window("漫画下载程序 by redballoon GUI:Trojians", "superhero")
my_gui = MangaDownloaderGUI(root)
root.mainloop()

```

shojnhv 发表于 2023-4-1 06:08

一起学习一下
页: [1]
查看完整版本: Python+Tkinter如何控制下载进度条