Source code for lib.file_helpers
# -*- coding: utf-8 -*-
# Copyright Martin Manns
# Distributed under the terms of the GNU General Public License
# --------------------------------------------------------------------
# pyspread is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyspread is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyspread. If not, see <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------
"""
**Provides**
* :class:`ProgressDialogCanceled`
* :func:`progress_dialog`
* :func:`linecount`
* :func:`file_progress_gen`
"""
from contextlib import contextmanager
from functools import partial
from typing import BinaryIO, ContextManager, Generator, IO, Tuple
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QProgressDialog
[docs]
class ProgressDialogCanceled(Exception):
"""Raised when a progress dialog is canceled"""
pass
[docs]
@contextmanager
def progress_dialog(main_window: QMainWindow, title: str, label: str,
maximum: int) -> ContextManager[QProgressDialog]:
""":class:`~contextlib.contextmanager` that displays a progress dialog
:param main_window: Application main window
:param title: Progress dialog title
:param label: Progress dialog label
:param maximum: Maximum value for progress
"""
progress_dialog = QProgressDialog(main_window)
progress_dialog.setWindowTitle(title)
progress_dialog.setWindowModality(Qt.WindowModality.WindowModal)
progress_dialog.setLabelText(label)
progress_dialog.setMaximum(maximum)
yield progress_dialog
progress_dialog.setValue(maximum)
progress_dialog.close()
progress_dialog.deleteLater()
[docs]
def linecount(infile: BinaryIO, buffer_size: int = 1024*1024) -> int:
"""Count lines in infile
Starts at current position in file. Position is not preserved.
Idea taken from https://stackoverflow.com/questions/845058
:param infile: File like object for which lines are counted (binary mode!)
:param buffer_size: Buffer size for reading the file
:return: Number of newlines in infile
"""
buffer_gen = iter(partial(infile.raw.read, buffer_size), b'')
return sum(buffer.count(b'\n') for buffer in buffer_gen)
[docs]
def file_progress_gen(main_window, file: IO, title: str, label: str,
no_lines: int, step: int = 100) \
-> Generator[Tuple[int, str], None, None]:
"""A generator for file iteration that displays a progress bar
Yields (line number, line string).
Return value on user cancel via progress dialog is current line number
:param main_window: Application main window
:param file: File to be iterater over
:param title: Progress dialog title
:param label: Progress dialog label
:param no_lines: Number of lines that are remaining in file
:param step: Number of lines per progress bar update
"""
with progress_dialog(main_window, title, label, no_lines) as progress_dlg:
try:
for i, line in enumerate(file):
yield i, line
if not i % step:
progress_dlg.setValue(i)
QApplication.instance().processEvents()
if progress_dlg.wasCanceled():
msg = "File operation canceled at line {}.".format(i)
raise ProgressDialogCanceled(msg)
except GeneratorExit: # Catch for cleaning up with progress_dialog
pass
except Exception as error:
main_window.grid.model.reset()
main_window.statusBar().showMessage(str(error))