Python копирование файла с прогресс баром
Я уже встречал здесь вопросы о Python и копировании файлов, но мне приходится иметь дело с другим сценарием.
Я почти закончил с установщиком дистрибутива Linux, над которым работал, и теперь все, что ему нужно сделать, это скопировать файлы в целевой раздел. Поскольку у большинства установщиков дистрибутивов есть индикатор выполнения, я тоже надеялся добавить его.
Прямо сейчас я использую PyQt4, и мой код выглядит так:
Это довольно неэффективно из-за индикатора выполнения. Все изображение имеет размер 2.1GB , и скрипту требуется действительно много времени, чтобы скопировать файлы. Намного длиннее простого cp -r .
Есть ли какой-нибудь эффективный способ сделать это? Для индикаторов выполнения однофайлового копирования все, что вы делаете, - это читаете по частям за раз, но я понятия не имею, как это сделать для каталога с файлами 91,489 .
Любая помощь будет полезна. Спасибо!
1 ответ
Вы можете попробовать использовать shutil.copy для копирования файлов вместо обращения к ОС с помощью os.system (что создает отдельный процесс). Вы также можете использовать os.mkdir для создания новых каталогов. Однако вы уверены, что он работает медленно из-за индикатора выполнения, а не из-за чего-то еще?
Излучаемый сигнал довольно сильно замедляет работу. Но я попробую эти изменения, может быть, на этот раз процессы действительно тормозят.
Если у вас есть 91489 файлов, и вы запускаете сигнал после каждых 100 файлов, то это всего лишь 914 выбросов сигнала или около того - это не должно быть проблемой. Будет ли намного быстрее, если вы закомментируете часть self.emit в цикле for и скопируете файлы один за другим, используя os.system , как указано выше?
Сейчас тестирую. Кажется, что он застрял на неработающих символических ссылках (которые работают, если исходный каталог был корневым, но не работает в хост-системе).
По-видимому, вы должны сами обрабатывать такие угловые случаи, то есть заранее проверяя, является ли файл неработающей символической ссылкой ( os.path.exists должен возвращать False ), а затем создавая символическую ссылку вручную, используя os.symlink .
Символические ссылки работают, если корневая папка / является папкой, из которой я копирую, поскольку папка, из которой я копирую, является файловой системой Linux FS. Python не любит битые символические ссылки, поэтому, думаю, мне придется вручную cp их .
I've got an application from which a file is copied from src to dst :
I wish to have the application query the progress of the copy every 5 seconds without locking up the application itself. Is this possible?
My intention is to set this progress to a QtGui.QLabel to give the user feedback on the file copy.
Can this be achieved when copying using a threaded shutil file copy?
42 Answers 42
With tqdm ( conda install tqdm or pip install tqdm ) you can add a progress meter to your loops in a second:
You can use tqdm.auto instead of tqdm.notebook to work in both a terminal and notebooks.
tqdm.contrib contains some helper functions to do things like enumerate , map , and zip . There are concurrent maps in tqdm.contrib.concurrent .
You can even get progress sent to your phone after disconnecting from a jupyter notebook using tqdm.contrib.telegram or tqdm.contrib.discord .
@JoshUsre Yes it should work with any iterable, for the moment I didn't see any iterable it choked on. However, the display of the ETA (remaining time) requires the iterable to have a __len__ property or the user must supply the total argument to tqdm . Else, the bar will work but with no ETA.
@gaborous: How come this isn't the top voted answer? This simple solution works both in terminal and in Jupyter notebook unlike the top answer.
for running in a jupyter notebook use from tqdm import tqdm_notebook as tqdm . Otherwise doesn't write it on one line.
There are specific libraries (like this one here) but maybe something very simple would do:
Note: progressbar2 is a fork of progressbar which hasn't been maintained in years.
I tried this code, and it threw a NameError: name 'xrange' is not defined error. Am I missing a module?
Use alive-progress, the coolest progress bar ever!
To use any progress bar framework in a useful manner, i.e. to get a percentage of completion and an estimated time of arrival (ETA), you need to be able to tell how many steps your processing will have.
Then you can just insert an yield to mark an item has been processed, and you're good to go!
Then just use it like:
To get an awesome and alive progress bar!
The above suggestions are pretty good, but I think most people just want a ready made solution, with no dependencies on external packages, but is also reusable.
I got the best points of all the above, and made it into a function, along with a test cases.
To use it, just copy the lines under "def update_progress(progress)" but not the test script. Don't forget to import sys. Call this whenever you need to display or update the progress bar.
This works by directly sending the "\r" symbol to console to move cursor back to the start. "print" in python does not recongise the above symbol for this purpose, hence we need 'sys'
This is what the result of the test script shows (The last progress bar animates):
при копировании больших файлов с помощью shutil.copy() , вы не получаете никаких указаний на то, как операция прогрессирует..
Я собрал что - то, что работает-он использует простой класс ProgressBar (который simple возвращает простой индикатор выполнения ASCII, как строку) и цикл open().read() и .write() для фактического копирования. Он отображает индикатор выполнения с помощью sys.stdout.write("r%sr" % (the_progress_bar)) что немного банально,но работает.
вы можете увидеть код (в контексте) на github вот!--10-->
есть ли встроенный модуль, который сделает это лучше? Есть ли какие-либо улучшения, которые могут быть внесены в этот код?
- Я бы сделал размер блока по умолчанию a много больше, чем 512. Я бы начал с 16384 и, возможно, больше.
- для модульности, возможно, было бы лучше иметь copy_with_prog функция не выводит сам индикатор выполнения, но вызывает функцию обратного вызова, чтобы вызывающий мог решить, как отобразить прогресс.
возможно, что-то вроде этого:
перебор? Возможно. Но почти в любой системе, Linux, Mac и с быстрой установкой wxWidgets на Windows, вы можете иметь реальную сделку, с кнопками паузы и отмены в настройке gui. Маки поставляются с wxWidgets в эти дни, и это общий пакет на Linux.
один файл очень быстрый (он сразу завершится и будет выглядеть сломанным), поэтому вы можете рассмотреть возможность создания задания набора файлов, которое тикает один раз на файл, а не один раз на блок. Наслаждайтесь!
Если вы хотите использовать диалоговое окно копирования Windows с прогрессом, вы можете использовать следующие:
У меня это shutil.скопируйте () с индикатором выполнения, сделанным простым способом, только со встроенными модулями. Если вы используете кодировку utf-8, вы можете получить прогресс, как второй пример в gif-изображении: Progress bars примеры для этого:
прочитайте комментарии, чтобы изменить стиль и цвета. Первый и последний примеры не нуждаются в utf-8. Вы можете использовать команду CPprogress (SOURCE, DESTINATION) только там, где у вас был shutil.копировать (src, dst):
Если вы хотите общий прогресс, вы можете использовать что-то вроде этого (сделано для другого скрипта). Обратите внимание, что в этом случае 'резьбонарезание.Поток", который вызывает индикатор выполнения, был помещен вне цикла "for". Кроме того, меры должны приниматься по-другому. Это третий пример (не utf-8) из gif-изображения в предыдущем ответе. Он добавляет файлы "того" подсчета:
у меня есть приложение, из которого копируется файл из src to dst :
Я хочу, чтобы приложение запрашивало ход выполнения копии каждые 5 секунд без блокировки самого приложения. Возможно ли это?
мое намерение состоит в том, чтобы установить этот прогресс в QtGui.QLabel чтобы дать отзыв пользователя о копии файла.
может ли это быть достигнуто при копировании с помощью потоковой копии файла shutil?
shutil.copy() не предлагает никаких вариантов для отслеживания прогресса, нет. Самое большее, вы можете контролировать размер файла назначения (используя os.* функции в целевом имени файла).
альтернативой было бы реализовать вашу собственную функцию копирования. Реализация действительно довольно проста; shutil.copy() в основном shutil.copyfile() плюс shutil.copymode() звонок; shutil.copyfile() в свою очередь, делегирует реальную работу в shutil.copyfileobj() (ссылки на Исходный код на языке Python).
реализуя свой shutil.copyfileobj() включить прогресс должен быть тривиальным; внедрить поддержку функции обратного вызова, чтобы сообщить, что ваша программа каждый раз, когда другой блок скопировал:
и сравнить copied размер с размером файла.
я совместил Martijn Питерс ответ С прогресс-бар кода ответ с модификациями для работы в PyCharm от ответ что дает мне следующее. Функция copy_with_progress была моя цель.
нет, так нельзя, потому что shutil.copy нет никаких средств для обеспечения прогресса.
но вы можете написать свою функцию копирования (или даже вилки код shutil --обратите внимание, что это один из модулей, который содержит ссылку источник вверху, что означает, что он должен быть так же полезен для образца кода,как и для использования as-is). Ваша функция может, например, взять функцию обратного вызова прогресса в качестве дополнительного аргумента и вызвать ее после каждый буфер (или каждый N буферов, или каждый n байт, или каждые N секунд). Что-то вроде:
кроме того, вы можете сделать это так, как вы предложили: иметь основной поток сигнала фонового потока (например, путем публикации на queue.Queue или вызывая Event или Condition ) и ваш copy проверка функции для этого сигнала каждый раз через петлю и отвечает. Но это кажется и более сложным, и менее отзывчивым.
еще одна вещь: Qt имеет свою собственную резьбу библиотека, и вы можете использовать ее вместо родной Python, потому что вы можете прикрепить слот непосредственно к QThread объект и сделать это ваш обратный вызов. Я не уверен, но Qt может даже иметь свои собственные методы копирования файлов с прогрессом где-то там; они пытаются обернуть все это может быть вообще по-разному между платформами и смутно связано с GUIs.
в дополнение к Martijn Питерс отличный ответ, если (как я,Я идиот) вам нужно выяснить, как передать фактический обратный вызов в copyfileobj() функция, вы можете сделать это так:
5 Answers 5
shutil.copy() doesn't offer any options to track the progress, no. At most you could monitor the size of the destination file (using os.* functions on the target filename).
The alternative would be to implement your own copy function. The implementation is really quite simple; shutil.copy() is basically a shutil.copyfile() plus shutil.copymode() call; shutil.copyfile() in turn delegates the real work to shutil.copyfileobj() * (links to the Python 3.8.2 source code).
Implementing your own shutil.copyfileobj() to include progress should be trivial; inject support for a callback function to report inform your program each time another block has copied:
and then, in the callback, compare the copied size with the file size.
Note that in the above implementation we look for the opportunity to use a different method for binary files, where you can use fileobj.readinto() and a memoryview object to avoid redundant data copying; see the original _copyfileobj_readinto() implementation for comparison.
* footnote to … delegates the real work to shutil.copyfileobj() : As of Python 3.8, on OS X and Linux the copyfile() implementation delegates file copying to OS-specific, optimised system calls (to fcopyfile() and sendfile() , respectively) but these calls have no hooks whatsoever to track progress, and so if you need to track progress you'd want to disable these delegation paths anyway. On Windows the code uses the aforementioned _copyfileobj_readinto() function.
Thanks for this! I implemented this with the other necessary functions from shutil and a progress bar function to create a complete solution which I posted as another answer.
@MattM: okay, all updated, including the Windows-specific optimisation made generic for OS X and Linux (as you can't track process via the system calls).
@Jay: depending on the platform, using the code in my answer to track per-block copying progress could well be slower, because you then can't use the OS-level APIs, yes.
I combined Martijn Pieters answer with some progress bar code from this answer with modifications to work in PyCharm from this answer which gives me the following. The function copy_with_progress was my goal.
This might be a bit hacky but it works:
No, it can't be done this way, because shutil.copy doesn't have any means of providing progress.
But you can write your own copy function (or even fork the code from shutil --notice that it's one of the modules that includes a link to the source at the top, meaning it's meant to be as useful for sample code as for just using as-is). Your function can, e.g., take a progress callback function as an extra argument and calls it after each buffer (or each N buffers, or each N bytes, or each N seconds). Something like:
Now, that callback is still going to be called in the background thread, not the main thread. With most GUI frameworks, that means it can't directly touch any GUI widgets. But most GUI frameworks have a way to post a message to the main thread's event loop from a background thread, so just make the callback do that. With Qt you do this with signals and slots, exactly the same way you do within the main thread; there's lots of great tutorials out there if you don't know how.
Alternatively, you could do it the way you suggested: have the main thread signal the background thread (e.g., by posting on a queue.Queue or triggering an Event or Condition ) and have your copy function check for that signal every time through the loop and respond. But that seems both more complicated and less responsive.
One more thing: Qt has its own threading library, and you may want to use it instead of Python's native one, because you can attach a slot directly to QThread object and make that your callback. I'm not sure, but Qt might even have its own file-copy-with-progress methods in there somewhere; they try to wrap up everything that might be at all different between platforms and vaguely related to GUIs.
How do I use a progress bar when my script is doing some task that is likely to take time?
For example, a function which takes some time to complete and returns True when done. How can I display a progress bar during the time the function is being executed?
Note that I need this to be in real time, so I can't figure out what to do about it. Do I need a thread for this? I have no idea.
Right now I am not printing anything while the function is being executed, however a progress bar would be nice. Also I am more interested in how this can be done from a code point of view.
CLI. But I can use a third party library, that is no issue. With GUI I can do this, but I was interested in the CLI part.
Possible duplicate of Text Progress Bar in the Console Note that while this question was posted three days earlier, the linked question is more frequently viewed.
Читайте также: