Changeset 11483:b82955e67844 in orange


Ignore:
Timestamp:
04/30/13 10:56:55 (12 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Use new OWConcurrent classes, added a 'Cancel' button.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeWidgets/OWDatabasesUpdate.py

    r11473 r11483  
    55 
    66from datetime import datetime 
    7  
    8 import Orange 
     7from functools import partial 
     8 
     9from PyQt4.QtCore import pyqtSignal as Signal, pyqtSlot as Slot 
    910 
    1011from Orange.utils import serverfiles, environ 
     
    1213 
    1314from OWWidget import * 
    14 from OWConcurrent import * 
     15 
     16from OWConcurrent import Task, ThreadExecutor, methodinvoke 
    1517 
    1618import OWGUIEx 
     
    2426        self.setValue(self.value() + 1) 
    2527 
    26  
    27 class ProgressBarRedirect(QObject): 
    28     def __init__(self, parent, redirect): 
    29         QObject.__init__(self, parent) 
    30         self.redirect = redirect 
    31         self._delay = False 
    32  
    33     @pyqtSignature("advance()") 
    34     def advance(self): 
    35         # delay OWBaseWidget.progressBarSet call, because it calls 
    36         # qApp.processEvents which can result in 'event queue climbing' 
    37         # and max. recursion error if GUI thread gets another advance 
    38         # signal before it finishes with this one 
    39         if not self._delay: 
    40             try: 
    41                 self._delay = True 
    42                 self.redirect.advance() 
    43             finally: 
    44                 self._delay = False 
    45         else: 
    46             QTimer.singleShot(10, self.advance) 
    4728 
    4829_icons_dir = os.path.join(environ.canvas_install_dir, "icons") 
     
    163144        self.domain = domain 
    164145        self.filename = filename 
     146        self.task = None 
    165147        self.UpdateToolTip() 
    166148 
     
    182164        self.updateWidget.removeButton.setEnabled(False) 
    183165        self.updateWidget.updateButton.setEnabled(False) 
    184         self.setData(2, Qt.DisplayRole, QVariant("")) 
    185         serverFiles = serverfiles.ServerFiles( 
    186             access_code=self.master.accessCode if self.master.accessCode 
    187             else None 
    188         ) 
    189  
    190         pb = ItemProgressBar(self.treeWidget()) 
    191         pb.setRange(0, 100) 
    192         pb.setTextVisible(False) 
    193  
    194         self.task = AsyncCall(threadPool=QThreadPool.globalInstance()) 
    195  
    196         if not getattr(self.master, "_sum_progressBar", None): 
    197             self.master._sum_progressBar = OWGUI.ProgressBar(self.master, 0) 
    198             self.master._sum_progressBar.in_progress = 0 
    199         master_pb = self.master._sum_progressBar 
    200         master_pb.iter += 100 
    201         master_pb.in_progress += 1 
    202         self._progressBarRedirect = \ 
    203             ProgressBarRedirect(QThread.currentThread(), master_pb) 
    204         QObject.connect(self.task, 
    205                         SIGNAL("advance()"), 
    206                         pb.advance, 
    207                         Qt.QueuedConnection) 
    208         QObject.connect(self.task, 
    209                         SIGNAL("advance()"), 
    210                         self._progressBarRedirect.advance, 
    211                         Qt.QueuedConnection) 
    212         QObject.connect(self.task, 
    213                         SIGNAL("finished(QString)"), 
    214                         self.EndDownload, 
    215                         Qt.QueuedConnection) 
    216         self.treeWidget().setItemWidget(self, 2, pb) 
    217         pb.show() 
    218  
    219         self.task.apply_async(serverfiles.download, 
    220                               args=(self.domain, self.filename, serverFiles), 
    221                               kwargs=dict(callback=self.task.emitAdvance)) 
    222  
    223     def EndDownload(self, exitCode=0): 
    224         self.treeWidget().removeItemWidget(self, 2) 
    225         if str(exitCode) == "Ok": 
    226             self.state = 0 
    227             self.updateWidget.SetState(self.state) 
    228             self.setData(2, Qt.DisplayRole, 
    229                          QVariant(sizeof_fmt(float(self.size)))) 
    230             self.master.UpdateInfoLabel() 
    231             self.UpdateToolTip() 
    232         else: 
    233             self.updateWidget.SetState(1) 
    234             self.setData(2, Qt.DisplayRole, 
    235                          QVariant("Error occurred while downloading:" + 
    236                                   str(exitCode))) 
    237  
    238         master_pb = self.master._sum_progressBar 
    239  
    240         if master_pb and master_pb.in_progress == 1: 
    241             master_pb.finish() 
    242             self.master._sum_progressBar = None 
    243         elif master_pb: 
    244             master_pb.in_progress -= 1 
     166        self.master.SubmitDownloadTask(self.domain, self.filename) 
    245167 
    246168    def Remove(self): 
    247         serverfiles.remove(self.domain, self.filename) 
    248         self.state = 2 
    249         self.updateWidget.SetState(self.state) 
    250         self.master.UpdateInfoLabel() 
    251         self.UpdateToolTip() 
     169        self.master.SubmitRemoveTask(self.domain, self.filename) 
    252170 
    253171    def __contains__(self, item): 
     
    323241                     callback=self.DownloadFiltered, 
    324242                     tooltip="Download all filtered files shown") 
     243        OWGUI.button(box, self, "Cancel", callback=self.Cancel, 
     244                     tooltip="Cancel scheduled downloads/updates.") 
    325245        OWGUI.rubber(box) 
    326246        OWGUI.lineEdit(box, self, "accessCode", "Access Code", 
     
    348268        self.resize(800, 600) 
    349269 
    350         QTimer.singleShot(50, self.RetrieveFilesList) 
     270        self.progress = ProgressState(self, maximum=3) 
     271        self.progress.valueChanged.connect(self._updateProgress) 
     272        self.progress.rangeChanged.connect(self._updateProgress) 
     273        self.executor = ThreadExecutor( 
     274            threadPool=QThreadPool(maxThreadCount=2) 
     275        ) 
     276 
     277        task = Task(self, function=self.RetrieveFilesList) 
     278        task.exceptionReady.connect(self.HandleError) 
     279        task.start() 
     280 
     281        self._tasks = [] 
    351282 
    352283    def RetrieveFilesList(self): 
     284        self.progress.setRange(0, 3) 
    353285        self.serverFiles = serverfiles.ServerFiles(access_code=self.accessCode) 
    354         self.pb = ProgressBar(self, 3) 
    355         self.async_retrieve = createTask(retrieveFilesList, 
    356                                          (self.serverFiles, self.domains, 
    357                                           self.pb.advance), 
    358                                          onResult=self.SetFilesList, 
    359                                          onError=self.HandleError) 
     286 
     287        task = Task(function=partial(retrieveFilesList, self.serverFiles, 
     288                                     self.domains, 
     289                                     methodinvoke(self.progress, "advance"))) 
     290 
     291        task.resultReady.connect(self.SetFilesList) 
     292        task.exceptionReady.connect(self.HandleError) 
     293 
     294        self.executor.submit(task) 
    360295 
    361296        self.setEnabled(False) 
     
    389324        for item in items: 
    390325            self.updateItems.append(UpdateTreeWidgetItem(self, *item)) 
    391         self.pb.advance() 
     326        self.progress.advance() 
     327 
    392328        for column in range(4): 
    393329            whint = self.filesView.sizeHintForColumn(column) 
     
    399335        self.SearchUpdate() 
    400336        self.UpdateInfoLabel() 
    401         self.pb.finish() 
    402  
    403     def HandleError(self, (exc_type, exc_value, tb)): 
    404         if exc_type >= IOError: 
     337 
     338        self.progress.setRange(0, 0) 
     339 
     340    def HandleError(self, exception): 
     341        if isinstance(exception, IOError): 
    405342            self.error(0, 
    406343                       "Could not connect to server! Press the Retry " 
     
    408345            self.SetFilesList({}) 
    409346        else: 
    410             sys.excepthook(exc_type, exc_value, tb) 
    411             self.pb.finish() 
     347            sys.excepthook(type(exception), exception.args, None) 
     348            self.progress.setRange(0, 0) 
    412349            self.setEnabled(True) 
    413350 
     
    450387                tags.update(item.tags) 
    451388 
     389    def SubmitDownloadTask(self, domain, filename): 
     390        """ 
     391        Submit the (domain, filename) to be downloaded/updated. 
     392        """ 
     393        item = self._item(domain, filename) 
     394 
     395        if self.accessCode: 
     396            sf = serverfiles.ServerFiles(access_code=self.accessCode) 
     397        else: 
     398            sf = serverfiles.ServerFiles() 
     399 
     400        task = DownloadTask(domain, filename, sf) 
     401 
     402        future = self.executor.submit(task) 
     403 
     404#        watcher = FutureWatcher(future, parent=self) 
     405#        watcher.finished.connect(progress.finish) 
     406 
     407        self.progress.adjustRange(0, 100) 
     408 
     409        pb = ItemProgressBar(self.filesView) 
     410        pb.setRange(0, 100) 
     411        pb.setTextVisible(False) 
     412 
     413        task.advanced.connect(pb.advance) 
     414        task.advanced.connect(self.progress.advance) 
     415        task.finished.connect(pb.hide) 
     416        task.finished.connect(self.onDownloadFinished, Qt.QueuedConnection) 
     417        task.exception.connect(self.onDownloadError, Qt.QueuedConnection) 
     418 
     419        self.filesView.setItemWidget(item, 2, pb) 
     420 
     421        # Clear the text so it does not show behind the progress bar. 
     422        item.setData(2, Qt.DisplayRole, QVariant("")) 
     423        pb.show() 
     424 
     425        self._tasks.append(task) 
     426#        self._futures.append((future, watcher)) 
     427 
     428    def EndDownloadTask(self, task): 
     429        future = task.future() 
     430        item = self._item(task.domain, task.filename) 
     431 
     432        self.filesView.removeItemWidget(item, 2) 
     433 
     434        if future.cancelled(): 
     435            # Restore the previous state 
     436            item.updateWidget.SetState(item.state) 
     437            item.setData(2, Qt.DisplayRole, 
     438                         QVariant(sizeof_fmt(float(item.size)))) 
     439 
     440        elif future.exception(): 
     441            item.updateWidget.SetState(1) 
     442            item.setData(2, Qt.DisplayRole, 
     443                         QVariant("Error occurred while downloading:" + 
     444                                  str(future.exception()))) 
     445#            item.setErrorText(str(exception)) 
     446#            item.setState(UpdateTreeWidgetItem.Error) 
     447        else: 
     448            item.state = 0 
     449            item.updateWidget.SetState(item.state) 
     450            item.setData(2, Qt.DisplayRole, 
     451                         QVariant(sizeof_fmt(float(item.size)))) 
     452            item.UpdateToolTip() 
     453            self.UpdateInfoLabel() 
     454 
     455#            item.setState(UpdateTreeWidgetItem.Updated) 
     456#            item.setInfo(serverfiles.info(task.domain, task.filename)) 
     457 
     458    def SubmitRemoveTask(self, domain, filename): 
     459        serverfiles.remove(domain, filename) 
     460 
     461        item = self._item(domain, filename) 
     462        item.state = 2 
     463        item.updateWidget.SetState(item.state) 
     464 
     465        self.UpdateInfoLabel() 
     466        item.UpdateToolTip() 
     467 
     468    def Cancel(self): 
     469        """ 
     470        Cancel all pending update/download tasks (that have not yet started). 
     471        """ 
     472        print "Cancel" 
     473        for task in self._tasks: 
     474            print task, task.future().cancel() 
     475 
     476    def onDeleteWidget(self): 
     477        self.Cancel() 
     478        self.executor.shutdown(wait=False) 
     479        OWBaseWidget.onDeleteWidget(self) 
     480 
     481    def onDownloadFinished(self): 
     482        assert QThread.currentThread() is self.thread() 
     483        for task in list(self._tasks): 
     484            future = task.future() 
     485            if future.done(): 
     486                self.EndDownloadTask(task) 
     487                self._tasks.remove(task) 
     488 
     489        if not self._tasks: 
     490            # Clear/reset the overall progress 
     491            self.progress.setRange(0, 0) 
     492 
     493        print "Download finished" 
     494 
     495    def onDownloadError(self, exc_info): 
     496        sys.excepthook(*exc_info) 
     497 
     498    def _item(self, domain, filename): 
     499        return [item for item in self.updateItems 
     500                if item.domain == domain and item.filename == filename].pop() 
     501 
     502    def _updateProgress(self, *args): 
     503        rmin, rmax = self.progress.range() 
     504        if rmin != rmax: 
     505            if self.progressBarValue <= 0: 
     506                self.progressBarInit() 
     507 
     508            self.progressBarSet(self.progress.ratioCompleted() * 100, 
     509                                processEventsFlags=None) 
     510        if rmin == rmax: 
     511            self.progressBarFinished() 
     512 
     513 
     514class ProgressState(QObject): 
     515    valueChanged = Signal(int) 
     516    rangeChanged = Signal(int, int) 
     517    textChanged = Signal(str) 
     518    started = Signal() 
     519    finished = Signal() 
     520 
     521    def __init__(self, parent=None, minimum=0, maximum=0, text="", value=0): 
     522        QObject.__init__(self, parent) 
     523 
     524        self._minimum = minimum 
     525        self._maximum = max(maximum, minimum) 
     526        self._text = text 
     527        self._value = value 
     528 
     529    @Slot(int, int) 
     530    def setRange(self, minimum, maximum): 
     531        maximum = max(maximum, minimum) 
     532 
     533        if self._minimum != minimum or self._maximum != maximum: 
     534            self._minimum = minimum 
     535            self._maximum = maximum 
     536            self.rangeChanged.emit(minimum, maximum) 
     537 
     538            # Adjust the value to fit in the range 
     539            newvalue = min(max(self._value, minimum), maximum) 
     540            if newvalue != self._value: 
     541                self.setValue(newvalue) 
     542 
     543    def range(self): 
     544        return self._minimum, self._maximum 
     545 
     546    @Slot(int) 
     547    def setValue(self, value): 
     548        if self._value != value and value >= self._minimum and \ 
     549                value <= self._maximum: 
     550            self._value = value 
     551            self.valueChanged.emit(value) 
     552 
     553    def value(self): 
     554        return self._value 
     555 
     556    @Slot(str) 
     557    def setText(self, text): 
     558        if self._text != text: 
     559            self._text = text 
     560            self.textChanged.emit(text) 
     561 
     562    def text(self): 
     563        return self._text 
     564 
     565    @Slot() 
     566    @Slot(int) 
     567    def advance(self, value=1): 
     568        self.setValue(self._value + value) 
     569 
     570    def adjustRange(self, dmin, dmax): 
     571        self.setRange(self._minimum + dmin, self._maximum + dmax) 
     572 
     573    def ratioCompleted(self): 
     574        span = self._maximum - self._minimum 
     575        if span < 1e-3: 
     576            return 0.0 
     577 
     578        return min(max(float(self._value - self._minimum) / span, 0.0), 1.0) 
     579 
     580 
     581class DownloadTask(Task): 
     582    advanced = Signal() 
     583    exception = Signal(tuple) 
     584 
     585    def __init__(self, domain, filename, serverfiles, parent=None): 
     586        Task.__init__(self, parent) 
     587        self.filename = filename 
     588        self.domain = domain 
     589        self.serverfiles = serverfiles 
     590        self._interrupt = False 
     591 
     592    def interrupt(self): 
     593        """ 
     594        Interrupt the download. 
     595        """ 
     596        self._interrupt = True 
     597 
     598    def _advance(self): 
     599        self.advanced.emit() 
     600        if self._interrupt: 
     601            raise KeyboardInterrupt 
     602 
     603    def run(self): 
     604        print "Download task", QThread.currentThread() 
     605        try: 
     606            serverfiles.download(self.domain, self.filename, self.serverfiles, 
     607                                 callback=self._advance) 
     608        except Exception: 
     609            self.exception.emit(sys.exc_info()) 
     610 
     611        print "Finished" 
     612 
    452613 
    453614if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.