mirror of
https://github.com/GNS3/gns3-gui.git
synced 2026-05-17 00:46:01 +03:00
196 lines
6.3 KiB
Python
196 lines
6.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2014 GNS3 Technologies Inc.
|
|
#
|
|
# This program 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.
|
|
#
|
|
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
Progress dialog that blocking tasks (file operations, network connections etc.)
|
|
"""
|
|
|
|
from ..qt import sip
|
|
from gns3.version import __version__
|
|
from ..qt import QtWidgets, QtCore, qslot
|
|
|
|
import logging
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ProgressDialog(QtWidgets.QProgressDialog):
|
|
|
|
"""
|
|
Progress dialog implementation with thread support.
|
|
|
|
:param thread: thread to run
|
|
:param title: window title
|
|
:param label_text: text to describe the progress bar
|
|
:param cancel_button_text: text for the cancel button
|
|
:param busy: if True, the progress bar in "sliding mode"
|
|
:param delay: Countdown in seconds before starting the worker
|
|
:param create_thread: Start the worker in a dedicated thread
|
|
to show unknown progress.
|
|
:param parent: parent widget
|
|
"""
|
|
|
|
def __init__(self, worker, title, label_text, cancel_button_text, busy=False, parent=None, delay=0, create_thread=True, cancelable=False):
|
|
|
|
if "dev" in __version__:
|
|
assert QtCore.QThread.currentThread() == QtWidgets.QApplication.instance().thread()
|
|
|
|
minimum = 0
|
|
maximum = 100
|
|
|
|
if busy:
|
|
maximum = 0
|
|
|
|
super().__init__(label_text, cancel_button_text, minimum, maximum, parent)
|
|
self.setModal(True)
|
|
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
self._errors = []
|
|
self.setWindowTitle(title)
|
|
self.canceled.connect(self._canceledSlot)
|
|
self.destroyed.connect(self._cleanup)
|
|
self._cancelable = cancelable
|
|
self._worker = worker
|
|
self._worker.setObjectName(worker.__class__.__name__)
|
|
if create_thread:
|
|
self._thread = QtCore.QThread()
|
|
if "dev" in __version__:
|
|
assert not self._worker.parent()
|
|
log.debug("Set worker inside a thread {}".format(self._worker.__class__))
|
|
self._worker.moveToThread(self._thread)
|
|
else:
|
|
log.debug("Set worker outside of a thread {}".format(self._worker.__class__))
|
|
self._thread = None
|
|
self._worker.finished.connect(self.accept)
|
|
self._worker.updated.connect(self._updateProgressSlot)
|
|
self._worker.error.connect(self._error)
|
|
if self._thread:
|
|
self._thread.started.connect(self._worker.run)
|
|
|
|
self._countdownTimer = None
|
|
if delay == 0:
|
|
self._start()
|
|
else:
|
|
self._delay = delay
|
|
self._countdownTimer = QtCore.QTimer()
|
|
self._countdownTimer.setInterval(self._delay * 100)
|
|
self._countdownTimer.timeout.connect(self._updateCountdownSlot)
|
|
self._countdownTimer.start()
|
|
self._updateCountdownSlot()
|
|
|
|
@qslot
|
|
def _updateCountdownSlot(self):
|
|
"""
|
|
Called every second for countdown before
|
|
starting the worker
|
|
"""
|
|
if self._delay <= 0:
|
|
self.setCancelButtonText("Cancel")
|
|
self._countdownTimer.stop()
|
|
self._start()
|
|
else:
|
|
self.setCancelButtonText("Cancel start ({} seconds)".format(self._delay))
|
|
self._delay -= 1
|
|
|
|
def _start(self):
|
|
# connect the thread signals and start the thread
|
|
|
|
if self._thread:
|
|
self._thread.start()
|
|
log.debug("{} thread started".format(self._worker.objectName()))
|
|
elif self._worker:
|
|
self._worker.run()
|
|
|
|
@qslot
|
|
def _canceledSlot(self):
|
|
if self._cancelable and not self._thread:
|
|
self._worker.cancel()
|
|
log.debug("{} worker canceled".format(self._worker.objectName()))
|
|
|
|
if self._thread:
|
|
self._worker.cancel()
|
|
log.debug("{} thread canceled".format(self._worker.objectName()))
|
|
self._cleanup()
|
|
|
|
@qslot
|
|
def accept(self):
|
|
if self._worker:
|
|
log.debug("{} thread finished".format(self._worker.objectName()))
|
|
self._cleanup()
|
|
super().accept()
|
|
|
|
def __del__(self):
|
|
|
|
self._cleanup()
|
|
|
|
@qslot
|
|
def _cleanup(self):
|
|
"""
|
|
Delete the thread.
|
|
"""
|
|
|
|
if self._countdownTimer:
|
|
self._countdownTimer.stop()
|
|
|
|
if self._thread and not sip.isdeleted(self._thread):
|
|
if self._thread.isRunning():
|
|
log.debug("{} thread is being destroyed".format(self._worker.objectName()))
|
|
thread = self._thread
|
|
self._thread = None
|
|
thread.quit()
|
|
if not thread.wait(3000):
|
|
thread.terminate()
|
|
thread.wait()
|
|
log.debug("{} thread destroyed".format(self._worker.objectName()))
|
|
thread.deleteLater()
|
|
self._worker = None
|
|
|
|
@qslot
|
|
def _updateProgressSlot(self, value):
|
|
"""
|
|
Slot to update the progress bar value.
|
|
|
|
:param value: value for the progress bar (integer)
|
|
"""
|
|
|
|
if self._thread:
|
|
# It seems in some cases this is called on a deleted object and crash
|
|
self.setValue(value)
|
|
|
|
@qslot
|
|
def _error(self, message, stop=False):
|
|
"""
|
|
Slot to show an error message sent by the thread.
|
|
|
|
:param message: message
|
|
"""
|
|
|
|
if stop:
|
|
log.critical("{} thread stopping with an error: {}".format(self._worker.objectName(), message))
|
|
self._canceledSlot()
|
|
QtWidgets.QMessageBox.critical(self.parentWidget(), "Error", "{}".format(message))
|
|
else:
|
|
self._errors.append(message)
|
|
self._cleanup()
|
|
|
|
def errors(self):
|
|
"""
|
|
Returns error messages.
|
|
|
|
:returns: error message list
|
|
"""
|
|
|
|
return self._errors
|