Merge DownloadModel into DownloadManager.

This commit is contained in:
Florian Bruhin 2014-10-08 20:18:44 +02:00
parent fe1215c74d
commit 61a836fec5
3 changed files with 79 additions and 141 deletions

View File

@ -24,7 +24,9 @@ import os.path
import functools
import collections
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QTimer, QStandardPaths
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QTimer,
QStandardPaths, Qt, QVariant, QAbstractListModel,
QModelIndex)
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
# We need this import so PyQt can use it inside pyqtSlot
from PyQt5.QtWebKitWidgets import QWebPage # pylint: disable=unused-import
@ -32,7 +34,11 @@ from PyQt5.QtWebKitWidgets import QWebPage # pylint: disable=unused-import
from qutebrowser.config import config
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import (message, http, usertypes, log, utils, urlutils,
objreg, standarddir)
objreg, standarddir, qtutils)
ModelRole = usertypes.enum('ModelRole', ['item'], start=Qt.UserRole,
is_int=True)
class DownloadItem(QObject):
@ -320,31 +326,15 @@ class DownloadItem(QObject):
self.data_changed.emit()
class DownloadManager(QObject):
class DownloadManager(QAbstractListModel):
"""Manager for running downloads.
"""Manager and model for currently running downloads.
Attributes:
downloads: A list of active DownloadItems.
questions: A list of Question objects to not GC them.
Signals:
download_about_to_be_added: A new download will be added.
arg: The index of the new download.
download_added: A new download was added.
download_about_to_be_finished: A download will be finished and removed.
arg: The index of the new download.
download_finished: A download was finished and removed.
data_changed: The data to be displayed in a model changed.
arg: The index of the download which changed.
"""
download_about_to_be_added = pyqtSignal(int)
download_added = pyqtSignal()
download_about_to_be_finished = pyqtSignal(int)
download_finished = pyqtSignal()
data_changed = pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.downloads = []
@ -393,6 +383,7 @@ class DownloadManager(QObject):
_inline, suggested_filename = http.parse_content_disposition(reply)
log.downloads.debug("fetch: {} -> {}".format(reply.url(),
suggested_filename))
download = DownloadItem(reply, self)
download.finished.connect(
functools.partial(self.on_finished, download))
@ -400,9 +391,10 @@ class DownloadManager(QObject):
functools.partial(self.on_data_changed, download))
download.error.connect(self.on_error)
download.basename = suggested_filename
self.download_about_to_be_added.emit(len(self.downloads) + 1)
idx = len(self.downloads) + 1
self.beginInsertRows(QModelIndex(), idx, idx)
self.downloads.append(download)
self.download_added.emit()
self.endInsertRows()
q = usertypes.Question(self)
q.text = "Save file to:"
@ -423,18 +415,69 @@ class DownloadManager(QObject):
"""Remove finished download."""
log.downloads.debug("on_finished: {}".format(download))
idx = self.downloads.index(download)
self.download_about_to_be_finished.emit(idx)
self.beginRemoveRows(QModelIndex(), idx, idx)
del self.downloads[idx]
self.download_finished.emit()
self.endRemoveRows()
download.deleteLater()
@pyqtSlot(DownloadItem)
def on_data_changed(self, download):
"""Emit data_changed signal when download data changed."""
idx = self.downloads.index(download)
self.data_changed.emit(idx)
model_idx = self.index(idx, 0)
qtutils.ensure_valid(model_idx)
self.dataChanged.emit(model_idx, model_idx)
@pyqtSlot(str)
def on_error(self, msg):
"""Display error message on download errors."""
message.error('current', "Download error: {}".format(msg))
def last_index(self):
"""Get the last index in the model.
Return:
A (possibly invalid) QModelIndex.
"""
idx = self.index(self.rowCount() - 1)
return idx
def headerData(self, section, orientation, role):
"""Simple constant header."""
if (section == 0 and orientation == Qt.Horizontal and
role == Qt.DisplayRole):
return "Downloads"
else:
return ""
def data(self, index, role):
"""Download data from DownloadManager."""
qtutils.ensure_valid(index)
if index.parent().isValid() or index.column() != 0:
return QVariant()
item = objreg.get('download-manager').downloads[index.row()]
if role == Qt.DisplayRole:
data = str(item)
elif role == Qt.ForegroundRole:
data = config.get('colors', 'downloads.fg')
elif role == Qt.BackgroundRole:
data = item.bg_color()
elif role == ModelRole.item:
data = item
else:
data = QVariant()
return data
def flags(self, _index):
"""Override flags so items aren't selectable.
The default would be Qt.ItemIsEnabled | Qt.ItemIsSelectable."""
return Qt.ItemIsEnabled
def rowCount(self, parent=QModelIndex()):
"""Get count of active downloads."""
if parent.isValid():
# We don't have children
return 0
return len(objreg.get('download-manager').downloads)

View File

@ -1,108 +0,0 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser 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.
#
# qutebrowser 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 qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Glue code for qutebrowser.{browser,widgets}.download."""
from PyQt5.QtCore import (pyqtSlot, Qt, QVariant, QAbstractListModel,
QModelIndex)
from qutebrowser.config import config
from qutebrowser.utils import usertypes, qtutils, objreg, utils
Role = usertypes.enum('Role', ['item'], start=Qt.UserRole, is_int=True)
class DownloadModel(QAbstractListModel):
"""Glue model to show downloads in a QListView.
Glue between qutebrowser.browser.download (DownloadManager) and
qutebrowser.widgets.download (DownloadView).
"""
def __init__(self, parent=None):
super().__init__(parent)
download_manager = objreg.get('download-manager')
download_manager.download_about_to_be_added.connect(
lambda idx: self.beginInsertRows(QModelIndex(), idx, idx))
download_manager.download_added.connect(self.endInsertRows)
download_manager.download_about_to_be_finished.connect(
lambda idx: self.beginRemoveRows(QModelIndex(), idx, idx))
download_manager.download_finished.connect(self.endRemoveRows)
download_manager.data_changed.connect(self.on_data_changed)
def __repr__(self):
return utils.get_repr(self, count=self.rowCount())
@pyqtSlot(int)
def on_data_changed(self, idx):
"""Update view when DownloadManager data changed."""
model_idx = self.index(idx, 0)
qtutils.ensure_valid(model_idx)
self.dataChanged.emit(model_idx, model_idx)
def last_index(self):
"""Get the last index in the model.
Return:
A (possibly invalid) QModelIndex.
"""
idx = self.index(self.rowCount() - 1)
return idx
def headerData(self, section, orientation, role):
"""Simple constant header."""
if (section == 0 and orientation == Qt.Horizontal and
role == Qt.DisplayRole):
return "Downloads"
else:
return ""
def data(self, index, role):
"""Download data from DownloadManager."""
qtutils.ensure_valid(index)
if index.parent().isValid() or index.column() != 0:
return QVariant()
item = objreg.get('download-manager').downloads[index.row()]
if role == Qt.DisplayRole:
data = str(item)
elif role == Qt.ForegroundRole:
data = config.get('colors', 'downloads.fg')
elif role == Qt.BackgroundRole:
data = item.bg_color()
elif role == Role.item:
data = item
else:
data = QVariant()
return data
def flags(self, _index):
"""Override flags so items aren't selectable.
The default would be Qt.ItemIsEnabled | Qt.ItemIsSelectable."""
return Qt.ItemIsEnabled
def rowCount(self, parent=QModelIndex()):
"""Get count of active downloads."""
if parent.isValid():
# We don't have children
return 0
return len(objreg.get('download-manager').downloads)

View File

@ -22,9 +22,9 @@
from PyQt5.QtCore import pyqtSlot, QSize, Qt
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
from qutebrowser.models import downloadmodel
from qutebrowser.browser import downloads
from qutebrowser.config import style
from qutebrowser.utils import qtutils, utils
from qutebrowser.utils import qtutils, utils, objreg
class DownloadView(QListView):
@ -55,10 +55,10 @@ class DownloadView(QListView):
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
self.setFlow(QListView.LeftToRight)
self._menu = None
self._model = downloadmodel.DownloadModel(self)
self._model.rowsInserted.connect(self.updateGeometry)
self._model.rowsRemoved.connect(self.updateGeometry)
self.setModel(self._model)
model = objreg.get('download-manager')
model.rowsInserted.connect(self.updateGeometry)
model.rowsRemoved.connect(self.updateGeometry)
self.setModel(model)
self.setWrapping(True)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_context_menu)
@ -77,7 +77,7 @@ class DownloadView(QListView):
index = self.indexAt(point)
if not index.isValid():
return
item = self.model().data(index, downloadmodel.Role.item)
item = self.model().data(index, downloads.ModelRole.item)
self._menu = QMenu(self)
cancel = self._menu.addAction("Cancel")
cancel.triggered.connect(item.cancel)
@ -89,7 +89,10 @@ class DownloadView(QListView):
def sizeHint(self):
"""Return sizeHint based on the view contents."""
idx = self.model().last_index()
try:
idx = self.model().last_index()
except RuntimeError:
pass
height = self.visualRect(idx).bottom()
if height != -1:
size = QSize(0, height + 2)