From fcdcfd7c39f642dfc2042031265a33da50cd2a7f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 27 Aug 2014 23:52:56 -0700 Subject: [PATCH] added download progress bars to GUI, and made close automatically act as stop server automatically --- onionshare/strings.json | 11 +------ onionshare/web.py | 22 ++++++++++---- onionshare_gui/downloads.py | 42 +++++++++++++++++++++------ onionshare_gui/onionshare_gui.py | 49 ++++++++++++++++++++++++++++---- onionshare_gui/options.py | 13 +++++++-- onionshare_gui/server_status.py | 2 +- 6 files changed, 107 insertions(+), 32 deletions(-) diff --git a/onionshare/strings.json b/onionshare/strings.json index 8a725061..eb5be018 100644 --- a/onionshare/strings.json +++ b/onionshare/strings.json @@ -14,8 +14,7 @@ "download_finished": "Download finished", "other_page_loaded": "Other page has been loaded", "tails_requires_root": "You must run OnionShare as root in Tails", - "close_on_finish": "Close automatically", - "close_countdown": "Closing in {0} seconds...", + "close_on_finish": "Stop server automatically", "choose_file": "Choose a file to share", "closing_automatically": "Closing automatically because download finished", "error_tails_invalid_port": "Invalid value, port must be an integer", @@ -52,7 +51,6 @@ "other_page_loaded": "En annen side har blitt lastet", "tails_requires_root": "OnionShare kan må kjøres som administrator i Tails", "close_on_finish": "Lukk automatisk", - "close_countdown": "Lukker om {0} sekunder", "choose_file": "Velg en fil å dele" }, "es": { "calculating_sha1": "Calculando suma de verificación SHA1.", @@ -70,7 +68,6 @@ "other_page_loaded": "Otra página se ha cargado", "tails_requires_root": "Debe ejecutar OnionShare como root en Tails", "close_on_finish": "Cerrar automáticamente.", - "close_countdown": "Cierre en {0} segundos...", "choose_file": "Elija un archivo para compartir" }, "fr": { "calculating_sha1": "Calculer un hachage SHA-1.", @@ -97,7 +94,6 @@ "other_page_loaded": "Un altra pagina é stata caricata", "tails_requires_root": "Con Tails devi avviare OnionShare come utente root", "close_on_finish": "Chiudi automaticamente", - "close_countdown": "Chiusura in {0} secondi...", "choose_file": "Scegli un file da condividere" }, "nl": { "calculating_sha1": "SHA1 controlecijfer berekenen.", @@ -115,7 +111,6 @@ "other_page_loaded": "Andere pagina is geladen", "tails_requires_root": "Je moet OnionShare als root draaien in Tails", "close_on_finish": "Sluit automatisch", - "close_countdown": "Sluit in {0} seconden...", "choose_file": "Kies betsand om te delen", "gui_copy_url": "Kopieer URL", "closing_automatically": "Sluit nu automatisch omdat download gereed is", @@ -142,7 +137,6 @@ "other_page_loaded": "Outra página tem sido carregada", "tails_requires_root": "Tem que rodar o OnionShare como root no Tails", "close_on_finish": "Fechar automaticamente", - "close_countdown": "Fechando em {0} segundos...", "choose_file": "Escolhe um arquivo para compartilhar" }, "ru": { "calculating_sha1": "Вычисляется SHA1 хешсумма.", @@ -160,7 +154,6 @@ "other_page_loaded": "Другая страница была загружена", "tails_requires_root": "Вы должны запустить OnionShare с правами root в Tails", "close_on_finish": "Закрыть автоматически", - "close_countdown": "Закроется через {0} секунд...", "choose_file": "Выберите файл", "gui_copy_url": "Скопировать ссылку" }, "de": { @@ -179,7 +172,6 @@ "other_page_loaded": "Andere Seite wurde geladen", "tails_requires_root": "Du musst Onionshare in Tails als root ausführen", "close_on_finish": "Beende automatisch", - "close_countdown": "Beende in {0} Sekunden...", "choose_file": "Wähle eine Datei zum teilen aus" },"tr": { "punching_a_hole": "Güvenlik duvarında bir yol açılıyor.", @@ -199,7 +191,6 @@ "other_page_loaded": "Diğer sayfa yüklendi", "tails_requires_root": "Tails'da OnionShare root olarak çalışır", "close_on_finish": "Kendiliğinden kapan", - "close_countdown": "Kapanıyor {0} saniye...", "choose_file": "Paylaşım için bir dosya seç", "gui_copy_url": "URL Kopyala" }} diff --git a/onionshare/web.py b/onionshare/web.py index 243c3900..db5e7f53 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -62,6 +62,15 @@ def set_stay_open(new_stay_open): def get_stay_open(): return stay_open +gui_mode = False +def set_gui_mode(new_gui_mode): + global gui_mode + gui_mode = new_gui_mode +def get_gui_mode(): + return gui_mode + + + def debug_mode(): import logging @@ -135,14 +144,16 @@ def download(slug_candidate): # download is finished, close the server if not stay_open: - print strings._("closing_automatically") - if shutdown_func is None: - raise RuntimeError('Not running with the Werkzeug Server') - shutdown_func() + if not gui_mode: + print strings._("closing_automatically") + if shutdown_func is None: + raise RuntimeError('Not running with the Werkzeug Server') + shutdown_func() r = Response(generate()) r.headers.add('Content-Length', zip_filesize) r.headers.add('Content-Disposition', 'attachment', filename=basename) + # guess content type (content_type, _) = mimetypes.guess_type(basename, strict=False) if content_type is not None: @@ -169,7 +180,8 @@ def shutdown(shutdown_slug_candidate): return "" -def start(port, stay_open=False): +def start(port, stay_open=False, gui_mode=False): set_stay_open(stay_open) + set_gui_mode(gui_mode) app.run(port=port) diff --git a/onionshare_gui/downloads.py b/onionshare_gui/downloads.py index de82a657..cae474fe 100644 --- a/onionshare_gui/downloads.py +++ b/onionshare_gui/downloads.py @@ -8,19 +8,43 @@ class Downloads(QtGui.QVBoxLayout): super(Downloads, self).__init__() self.addSpacing(10) + self.progress_bars = {} + # downloads label self.downloads_label = QtGui.QLabel(strings._('gui_downloads')) - """progress_bar = QtGui.QProgressBar() - progress_bar.setFormat("12.3 KiB, 17%") - progress_bar.setTextVisible(True) - progress_bar.setAlignment(QtCore.Qt.AlignHCenter) - progress_bar.setMinimum(0) - progress_bar.setMaximum(100) - progress_bar.setValue(17)""" - # hide downloads by default self.downloads_label.hide() # add the widgets self.addWidget(self.downloads_label) - #self.addWidget(progress_bar) + + def add_download(self, download_id, total_bytes): + self.downloads_label.show() + + # make a new progress bar + pb = QtGui.QProgressBar() + pb.setTextVisible(True) + pb.setAlignment(QtCore.Qt.AlignHCenter) + pb.setMinimum(0) + pb.setMaximum(total_bytes) + pb.setValue(0) + pb.setStyleSheet("QProgressBar::chunk { background-color: #05B8CC; }") + pb.total_bytes = total_bytes + + # add it to the list + self.progress_bars[download_id] = pb + self.addWidget(pb) + + # start at 0 + self.update_download(download_id, total_bytes, 0) + + def update_download(self, download_id, total_bytes, downloaded_bytes): + if download_id not in self.progress_bars: + self.add_download(download_id, total_bytes) + + pb = self.progress_bars[download_id] + pb.setValue(downloaded_bytes) + if downloaded_bytes == pb.total_bytes: + pb.setFormat("%p%") + else: + pb.setFormat("{0}, %p%".format(helpers.human_readable_filesize(downloaded_bytes))) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index d60d95e5..e4985767 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -48,20 +48,25 @@ class OnionShareGui(QtGui.QWidget): self.file_selection.file_list.files_updated.connect(self.server_status.update) # downloads - downloads = Downloads() + self.downloads = Downloads() # options - options = Options(web.stay_open) + self.options = Options(web) # main layout self.layout = QtGui.QVBoxLayout() self.layout.addLayout(self.file_selection) self.layout.addLayout(self.server_status) - self.layout.addLayout(downloads) - self.layout.addLayout(options) + self.layout.addLayout(self.downloads) + self.layout.addLayout(self.options) self.setLayout(self.layout) self.show() + # check for requests frequently + self.timer = QtCore.QTimer() + QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.check_for_requests) + self.timer.start(500) + def start_server(self): # start the hidden service try: @@ -76,7 +81,7 @@ class OnionShareGui(QtGui.QWidget): return # start onionshare service in new thread - t = threading.Thread(target=web.start, args=(self.app.port, self.app.stay_open)) + t = threading.Thread(target=web.start, args=(self.app.port, self.app.stay_open, True)) t.daemon = True t.start() @@ -93,6 +98,40 @@ class OnionShareGui(QtGui.QWidget): self.server_status.stop_server_finished() + def check_for_requests(self): + self.update() + # only check for requests if the server is running + if self.server_status.status != self.server_status.STATUS_STARTED: + return + + events = [] + + done = False + while not done: + try: + r = web.q.get(False) + events.append(r) + except web.Queue.Empty: + done = True + + for event in events: + #if event["path"] != '/favicon.ico': + # pass + #if event["type"] == web.REQUEST_LOAD: + # pass + + if event["type"] == web.REQUEST_DOWNLOAD: + self.downloads.add_download(event["data"]["id"], web.zip_filesize) + + elif event["type"] == web.REQUEST_PROGRESS: + self.downloads.update_download(event["data"]["id"], web.zip_filesize, event["data"]["bytes"]) + + # is the download complete? + if event["data"]["bytes"] == web.zip_filesize: + # close on finish? + if not web.get_stay_open(): + self.server_status.stop_server() + def alert(msg, icon=QtGui.QMessageBox.NoIcon): dialog = QtGui.QMessageBox() dialog.setWindowTitle("OnionShare") diff --git a/onionshare_gui/options.py b/onionshare_gui/options.py index 16e8a858..e0e3038a 100644 --- a/onionshare_gui/options.py +++ b/onionshare_gui/options.py @@ -4,18 +4,27 @@ import common from onionshare import strings, helpers class Options(QtGui.QHBoxLayout): - def __init__(self, stay_open=False): + def __init__(self, web): super(Options, self).__init__() self.addSpacing(10) + + self.web = web # close automatically self.close_automatically = QtGui.QCheckBox() - if stay_open: + if self.web.stay_open: self.close_automatically.setCheckState(QtCore.Qt.Unchecked) else: self.close_automatically.setCheckState(QtCore.Qt.Checked) self.close_automatically.setText(strings._("close_on_finish")) + self.connect(self.close_automatically, QtCore.SIGNAL('stateChanged(int)'), self.stay_open_changed) # add the widgets self.addWidget(self.close_automatically) + def stay_open_changed(self, state): + if state > 0: + self.web.set_stay_open(False) + else: + self.web.set_stay_open(True) + diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index df372d65..b42caf87 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -47,7 +47,6 @@ class ServerStatus(QtGui.QVBoxLayout): self.url_label.setFont(url_font) self.url_label.setWordWrap(True) self.url_label.setAlignment(QtCore.Qt.AlignCenter) - self.url_label.setMargin(3) self.copy_url_button = QtGui.QPushButton(strings._('gui_copy_url')) self.copy_url_button.clicked.connect(self.copy_url) url_layout = QtGui.QHBoxLayout() @@ -97,6 +96,7 @@ class ServerStatus(QtGui.QVBoxLayout): def start_server_finished(self): self.status = self.STATUS_STARTED + self.copy_url() self.update() def stop_server(self):