Merge DownloadModel into DownloadManager.
This commit is contained in:
parent
fe1215c74d
commit
61a836fec5
@ -24,7 +24,9 @@ import os.path
|
|||||||
import functools
|
import functools
|
||||||
import collections
|
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
|
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
|
||||||
# We need this import so PyQt can use it inside pyqtSlot
|
# We need this import so PyQt can use it inside pyqtSlot
|
||||||
from PyQt5.QtWebKitWidgets import QWebPage # pylint: disable=unused-import
|
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.config import config
|
||||||
from qutebrowser.commands import cmdexc, cmdutils
|
from qutebrowser.commands import cmdexc, cmdutils
|
||||||
from qutebrowser.utils import (message, http, usertypes, log, utils, urlutils,
|
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):
|
class DownloadItem(QObject):
|
||||||
@ -320,31 +326,15 @@ class DownloadItem(QObject):
|
|||||||
self.data_changed.emit()
|
self.data_changed.emit()
|
||||||
|
|
||||||
|
|
||||||
class DownloadManager(QObject):
|
class DownloadManager(QAbstractListModel):
|
||||||
|
|
||||||
"""Manager for running downloads.
|
"""Manager and model for currently running downloads.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
downloads: A list of active DownloadItems.
|
downloads: A list of active DownloadItems.
|
||||||
questions: A list of Question objects to not GC them.
|
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):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.downloads = []
|
self.downloads = []
|
||||||
@ -393,6 +383,7 @@ class DownloadManager(QObject):
|
|||||||
_inline, suggested_filename = http.parse_content_disposition(reply)
|
_inline, suggested_filename = http.parse_content_disposition(reply)
|
||||||
log.downloads.debug("fetch: {} -> {}".format(reply.url(),
|
log.downloads.debug("fetch: {} -> {}".format(reply.url(),
|
||||||
suggested_filename))
|
suggested_filename))
|
||||||
|
|
||||||
download = DownloadItem(reply, self)
|
download = DownloadItem(reply, self)
|
||||||
download.finished.connect(
|
download.finished.connect(
|
||||||
functools.partial(self.on_finished, download))
|
functools.partial(self.on_finished, download))
|
||||||
@ -400,9 +391,10 @@ class DownloadManager(QObject):
|
|||||||
functools.partial(self.on_data_changed, download))
|
functools.partial(self.on_data_changed, download))
|
||||||
download.error.connect(self.on_error)
|
download.error.connect(self.on_error)
|
||||||
download.basename = suggested_filename
|
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.downloads.append(download)
|
||||||
self.download_added.emit()
|
self.endInsertRows()
|
||||||
|
|
||||||
q = usertypes.Question(self)
|
q = usertypes.Question(self)
|
||||||
q.text = "Save file to:"
|
q.text = "Save file to:"
|
||||||
@ -423,18 +415,69 @@ class DownloadManager(QObject):
|
|||||||
"""Remove finished download."""
|
"""Remove finished download."""
|
||||||
log.downloads.debug("on_finished: {}".format(download))
|
log.downloads.debug("on_finished: {}".format(download))
|
||||||
idx = self.downloads.index(download)
|
idx = self.downloads.index(download)
|
||||||
self.download_about_to_be_finished.emit(idx)
|
self.beginRemoveRows(QModelIndex(), idx, idx)
|
||||||
del self.downloads[idx]
|
del self.downloads[idx]
|
||||||
self.download_finished.emit()
|
self.endRemoveRows()
|
||||||
download.deleteLater()
|
download.deleteLater()
|
||||||
|
|
||||||
@pyqtSlot(DownloadItem)
|
@pyqtSlot(DownloadItem)
|
||||||
def on_data_changed(self, download):
|
def on_data_changed(self, download):
|
||||||
"""Emit data_changed signal when download data changed."""
|
"""Emit data_changed signal when download data changed."""
|
||||||
idx = self.downloads.index(download)
|
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)
|
@pyqtSlot(str)
|
||||||
def on_error(self, msg):
|
def on_error(self, msg):
|
||||||
"""Display error message on download errors."""
|
"""Display error message on download errors."""
|
||||||
message.error('current', "Download error: {}".format(msg))
|
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)
|
||||||
|
@ -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)
|
|
@ -22,9 +22,9 @@
|
|||||||
from PyQt5.QtCore import pyqtSlot, QSize, Qt
|
from PyQt5.QtCore import pyqtSlot, QSize, Qt
|
||||||
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
|
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
|
||||||
|
|
||||||
from qutebrowser.models import downloadmodel
|
from qutebrowser.browser import downloads
|
||||||
from qutebrowser.config import style
|
from qutebrowser.config import style
|
||||||
from qutebrowser.utils import qtutils, utils
|
from qutebrowser.utils import qtutils, utils, objreg
|
||||||
|
|
||||||
|
|
||||||
class DownloadView(QListView):
|
class DownloadView(QListView):
|
||||||
@ -55,10 +55,10 @@ class DownloadView(QListView):
|
|||||||
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
||||||
self.setFlow(QListView.LeftToRight)
|
self.setFlow(QListView.LeftToRight)
|
||||||
self._menu = None
|
self._menu = None
|
||||||
self._model = downloadmodel.DownloadModel(self)
|
model = objreg.get('download-manager')
|
||||||
self._model.rowsInserted.connect(self.updateGeometry)
|
model.rowsInserted.connect(self.updateGeometry)
|
||||||
self._model.rowsRemoved.connect(self.updateGeometry)
|
model.rowsRemoved.connect(self.updateGeometry)
|
||||||
self.setModel(self._model)
|
self.setModel(model)
|
||||||
self.setWrapping(True)
|
self.setWrapping(True)
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
self.customContextMenuRequested.connect(self.show_context_menu)
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
||||||
@ -77,7 +77,7 @@ class DownloadView(QListView):
|
|||||||
index = self.indexAt(point)
|
index = self.indexAt(point)
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return
|
return
|
||||||
item = self.model().data(index, downloadmodel.Role.item)
|
item = self.model().data(index, downloads.ModelRole.item)
|
||||||
self._menu = QMenu(self)
|
self._menu = QMenu(self)
|
||||||
cancel = self._menu.addAction("Cancel")
|
cancel = self._menu.addAction("Cancel")
|
||||||
cancel.triggered.connect(item.cancel)
|
cancel.triggered.connect(item.cancel)
|
||||||
@ -89,7 +89,10 @@ class DownloadView(QListView):
|
|||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
"""Return sizeHint based on the view contents."""
|
"""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()
|
height = self.visualRect(idx).bottom()
|
||||||
if height != -1:
|
if height != -1:
|
||||||
size = QSize(0, height + 2)
|
size = QSize(0, height + 2)
|
||||||
|
Loading…
Reference in New Issue
Block a user