diff options
author | Micah Lee <micah@micahflee.com> | 2019-09-15 14:52:57 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-15 14:52:57 -0700 |
commit | 51a1f92d9975f76a0e4bfc36078715baeec203ce (patch) | |
tree | 4063a36658eeed59ce48295a0ae789f87cbcdc38 /onionshare_gui | |
parent | 414e7f9a38a4762ea845de278f24a8cff35bdbd3 (diff) | |
parent | 16fedabf400a696c452c644bcd7031af8b0e2b9e (diff) | |
download | onionshare-51a1f92d9975f76a0e4bfc36078715baeec203ce.tar.gz onionshare-51a1f92d9975f76a0e4bfc36078715baeec203ce.zip |
Merge pull request #1020 from micahflee/991_sharing_code
[WIP] Share code between share mode and website mode
Diffstat (limited to 'onionshare_gui')
-rw-r--r-- | onionshare_gui/mode/__init__.py | 33 | ||||
-rw-r--r-- | onionshare_gui/mode/history.py | 149 | ||||
-rw-r--r-- | onionshare_gui/mode/receive_mode/__init__.py | 6 | ||||
-rw-r--r-- | onionshare_gui/mode/share_mode/__init__.py | 12 | ||||
-rw-r--r-- | onionshare_gui/mode/share_mode/threads.py | 8 | ||||
-rw-r--r-- | onionshare_gui/mode/website_mode/__init__.py | 29 | ||||
-rw-r--r-- | onionshare_gui/onionshare_gui.py | 11 | ||||
-rw-r--r-- | onionshare_gui/settings_dialog.py | 3 |
8 files changed, 182 insertions, 69 deletions
diff --git a/onionshare_gui/mode/__init__.py b/onionshare_gui/mode/__init__.py index e92e36f8..3ef285c4 100644 --- a/onionshare_gui/mode/__init__.py +++ b/onionshare_gui/mode/__init__.py @@ -22,6 +22,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui from onionshare import strings from onionshare.common import AutoStopTimer +from .history import IndividualFileHistoryItem + from ..server_status import ServerStatus from ..threads import OnionThread from ..threads import AutoStartTimer @@ -29,7 +31,7 @@ from ..widgets import Alert class Mode(QtWidgets.QWidget): """ - The class that ShareMode and ReceiveMode inherit from. + The class that all modes inherit from """ start_server_finished = QtCore.pyqtSignal() stop_server_finished = QtCore.pyqtSignal() @@ -417,3 +419,32 @@ class Mode(QtWidgets.QWidget): Handle REQUEST_UPLOAD_CANCELED event. """ pass + + def handle_request_individual_file_started(self, event): + """ + Handle REQUEST_INDVIDIDUAL_FILES_STARTED event. + Used in both Share and Website modes, so implemented here. + """ + self.toggle_history.update_indicator(True) + self.history.requests_count += 1 + self.history.update_requests() + + item = IndividualFileHistoryItem(self.common, event["data"], event["path"]) + self.history.add(event["data"]["id"], item) + + def handle_request_individual_file_progress(self, event): + """ + Handle REQUEST_INDVIDIDUAL_FILES_PROGRESS event. + Used in both Share and Website modes, so implemented here. + """ + self.history.update(event["data"]["id"], event["data"]["bytes"]) + + if self.server_status.status == self.server_status.STATUS_STOPPED: + self.history.cancel(event["data"]["id"]) + + def handle_request_individual_file_canceled(self, event): + """ + Handle REQUEST_INDVIDIDUAL_FILES_CANCELED event. + Used in both Share and Website modes, so implemented here. + """ + self.history.cancel(event["data"]["id"]) diff --git a/onionshare_gui/mode/history.py b/onionshare_gui/mode/history.py index 51b36f9a..b8baebd1 100644 --- a/onionshare_gui/mode/history.py +++ b/onionshare_gui/mode/history.py @@ -237,6 +237,7 @@ class ReceiveHistoryItemFile(QtWidgets.QWidget): elif self.common.platform == 'Windows': subprocess.Popen(['explorer', '/select,{}'.format(abs_filename)]) + class ReceiveHistoryItem(HistoryItem): def __init__(self, common, id, content_length): super(ReceiveHistoryItem, self).__init__() @@ -341,35 +342,108 @@ class ReceiveHistoryItem(HistoryItem): self.label.setText(self.get_canceled_label_text(self.started)) -class VisitHistoryItem(HistoryItem): +class IndividualFileHistoryItem(HistoryItem): """ - Download history item, for share mode + Individual file history item, for share mode viewing of individual files """ - def __init__(self, common, id, total_bytes): - super(VisitHistoryItem, self).__init__() + def __init__(self, common, data, path): + super(IndividualFileHistoryItem, self).__init__() self.status = HistoryItem.STATUS_STARTED self.common = common self.id = id - self.visited = time.time() - self.visited_dt = datetime.fromtimestamp(self.visited) + self.path = path + self.total_bytes = 0 + self.downloaded_bytes = 0 + self.started = time.time() + self.started_dt = datetime.fromtimestamp(self.started) + self.status = HistoryItem.STATUS_STARTED - # Label - self.label = QtWidgets.QLabel(strings._('gui_visit_started').format(self.visited_dt.strftime("%b %d, %I:%M%p"))) + self.directory_listing = 'directory_listing' in data + + # Labels + self.timestamp_label = QtWidgets.QLabel(self.started_dt.strftime("%b %d, %I:%M%p")) + self.timestamp_label.setStyleSheet(self.common.css['history_individual_file_timestamp_label']) + self.path_label = QtWidgets.QLabel("{}".format(self.path)) + self.status_code_label = QtWidgets.QLabel() + + # Progress bar + self.progress_bar = QtWidgets.QProgressBar() + self.progress_bar.setTextVisible(True) + self.progress_bar.setAttribute(QtCore.Qt.WA_DeleteOnClose) + self.progress_bar.setAlignment(QtCore.Qt.AlignHCenter) + self.progress_bar.setValue(0) + self.progress_bar.setStyleSheet(self.common.css['downloads_uploads_progress_bar']) + + # Text layout + labels_layout = QtWidgets.QHBoxLayout() + labels_layout.addWidget(self.timestamp_label) + labels_layout.addWidget(self.path_label) + labels_layout.addWidget(self.status_code_label) + labels_layout.addStretch() # Layout layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) + layout.addLayout(labels_layout) + layout.addWidget(self.progress_bar) self.setLayout(layout) - def update(self): - self.label.setText(self.get_finished_label_text(self.started_dt)) - self.status = HistoryItem.STATUS_FINISHED + # Is a status code already sent? + if 'status_code' in data: + self.status_code_label.setText("{}".format(data['status_code'])) + if data['status_code'] >= 200 and data['status_code'] < 300: + self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx']) + if data['status_code'] >= 400 and data['status_code'] < 500: + self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_4xx']) + self.status = HistoryItem.STATUS_FINISHED + self.progress_bar.hide() + return + + else: + self.total_bytes = data['filesize'] + self.progress_bar.setMinimum(0) + self.progress_bar.setMaximum(data['filesize']) + self.progress_bar.total_bytes = data['filesize'] + + # Start at 0 + self.update(0) + + def update(self, downloaded_bytes): + self.downloaded_bytes = downloaded_bytes + + self.progress_bar.setValue(downloaded_bytes) + if downloaded_bytes == self.progress_bar.total_bytes: + self.status_code_label.setText("200") + self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx']) + self.progress_bar.hide() + self.status = HistoryItem.STATUS_FINISHED + + else: + elapsed = time.time() - self.started + if elapsed < 10: + # Wait a couple of seconds for the download rate to stabilize. + # This prevents a "Windows copy dialog"-esque experience at + # the beginning of the download. + pb_fmt = strings._('gui_all_modes_progress_starting').format( + self.common.human_readable_filesize(downloaded_bytes)) + else: + pb_fmt = strings._('gui_all_modes_progress_eta').format( + self.common.human_readable_filesize(downloaded_bytes), + self.estimated_time_remaining) + + self.progress_bar.setFormat(pb_fmt) def cancel(self): self.progress_bar.setFormat(strings._('gui_canceled')) self.status = HistoryItem.STATUS_CANCELED + @property + def estimated_time_remaining(self): + return self.common.estimated_time_remaining(self.downloaded_bytes, + self.total_bytes, + self.started) + + class HistoryItemList(QtWidgets.QScrollArea): """ List of items @@ -452,26 +526,30 @@ class History(QtWidgets.QWidget): # In progress and completed counters self.in_progress_count = 0 self.completed_count = 0 + self.requests_count = 0 - # In progress and completed labels + # In progress, completed, and requests labels self.in_progress_label = QtWidgets.QLabel() self.in_progress_label.setStyleSheet(self.common.css['mode_info_label']) self.completed_label = QtWidgets.QLabel() self.completed_label.setStyleSheet(self.common.css['mode_info_label']) + self.requests_label = QtWidgets.QLabel() + self.requests_label.setStyleSheet(self.common.css['mode_info_label']) # Header self.header_label = QtWidgets.QLabel(header_text) self.header_label.setStyleSheet(self.common.css['downloads_uploads_label']) - clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history')) - clear_button.setStyleSheet(self.common.css['downloads_uploads_clear']) - clear_button.setFlat(True) - clear_button.clicked.connect(self.reset) + self.clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history')) + self.clear_button.setStyleSheet(self.common.css['downloads_uploads_clear']) + self.clear_button.setFlat(True) + self.clear_button.clicked.connect(self.reset) header_layout = QtWidgets.QHBoxLayout() header_layout.addWidget(self.header_label) header_layout.addStretch() header_layout.addWidget(self.in_progress_label) header_layout.addWidget(self.completed_label) - header_layout.addWidget(clear_button) + header_layout.addWidget(self.requests_label) + header_layout.addWidget(self.clear_button) # When there are no items self.empty_image = QtWidgets.QLabel() @@ -549,14 +627,18 @@ class History(QtWidgets.QWidget): self.completed_count = 0 self.update_completed() + # Reset web requests counter + self.requests_count = 0 + self.update_requests() + def update_completed(self): """ Update the 'completed' widget. """ if self.completed_count == 0: - image = self.common.get_resource_path('images/share_completed_none.png') + image = self.common.get_resource_path('images/history_completed_none.png') else: - image = self.common.get_resource_path('images/share_completed.png') + image = self.common.get_resource_path('images/history_completed.png') self.completed_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.completed_count)) self.completed_label.setToolTip(strings._('history_completed_tooltip').format(self.completed_count)) @@ -564,14 +646,25 @@ class History(QtWidgets.QWidget): """ Update the 'in progress' widget. """ - if self.mode != 'website': - if self.in_progress_count == 0: - image = self.common.get_resource_path('images/share_in_progress_none.png') - else: - image = self.common.get_resource_path('images/share_in_progress.png') + if self.in_progress_count == 0: + image = self.common.get_resource_path('images/history_in_progress_none.png') + else: + image = self.common.get_resource_path('images/history_in_progress.png') + + self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count)) + self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count)) + + def update_requests(self): + """ + Update the 'web requests' widget. + """ + if self.requests_count == 0: + image = self.common.get_resource_path('images/history_requests_none.png') + else: + image = self.common.get_resource_path('images/history_requests.png') - self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count)) - self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count)) + self.requests_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.requests_count)) + self.requests_label.setToolTip(strings._('history_requests_tooltip').format(self.requests_count)) class ToggleHistory(QtWidgets.QPushButton): @@ -604,7 +697,7 @@ class ToggleHistory(QtWidgets.QPushButton): def update_indicator(self, increment=False): """ Update the display of the indicator count. If increment is True, then - only increment the counter if Downloads is hidden. + only increment the counter if History is hidden. """ if increment and not self.history_widget.isVisible(): self.indicator_count += 1 diff --git a/onionshare_gui/mode/receive_mode/__init__.py b/onionshare_gui/mode/receive_mode/__init__.py index dbc0bc73..ecbfa54a 100644 --- a/onionshare_gui/mode/receive_mode/__init__.py +++ b/onionshare_gui/mode/receive_mode/__init__.py @@ -97,7 +97,7 @@ class ReceiveMode(Mode): The auto-stop timer expired, should we stop the server? Returns a bool """ # If there were no attempts to upload files, or all uploads are done, we can stop - if self.web.receive_mode.upload_count == 0 or not self.web.receive_mode.uploads_in_progress: + if self.web.receive_mode.cur_history_id == 0 or not self.web.receive_mode.uploads_in_progress: self.server_status.stop_server() self.server_status_label.setText(strings._('close_on_autostop_timer')) return True @@ -112,7 +112,7 @@ class ReceiveMode(Mode): Starting the server. """ # Reset web counters - self.web.receive_mode.upload_count = 0 + self.web.receive_mode.cur_history_id = 0 self.web.reset_invalid_passwords() # Hide and reset the uploads if we have previously shared @@ -212,6 +212,8 @@ class ReceiveMode(Mode): Set the info counters back to zero. """ self.history.reset() + self.toggle_history.indicator_count = 0 + self.toggle_history.update_indicator() def update_primary_action(self): self.common.log('ReceiveMode', 'update_primary_action') diff --git a/onionshare_gui/mode/share_mode/__init__.py b/onionshare_gui/mode/share_mode/__init__.py index 143fd577..28b439af 100644 --- a/onionshare_gui/mode/share_mode/__init__.py +++ b/onionshare_gui/mode/share_mode/__init__.py @@ -132,7 +132,7 @@ class ShareMode(Mode): The auto-stop timer expired, should we stop the server? Returns a bool """ # If there were no attempts to download the share, or all downloads are done, we can stop - if self.web.share_mode.download_count == 0 or self.web.done: + if self.web.share_mode.cur_history_id == 0 or self.web.done: self.server_status.stop_server() self.server_status_label.setText(strings._('close_on_autostop_timer')) return True @@ -146,7 +146,7 @@ class ShareMode(Mode): Starting the server. """ # Reset web counters - self.web.share_mode.download_count = 0 + self.web.share_mode.cur_history_id = 0 self.web.reset_invalid_passwords() # Hide and reset the downloads if we have previously shared @@ -225,12 +225,6 @@ class ShareMode(Mode): """ self.primary_action.hide() - def handle_request_load(self, event): - """ - Handle REQUEST_LOAD event. - """ - self.system_tray.showMessage(strings._('systray_page_loaded_title'), strings._('systray_page_loaded_message')) - def handle_request_started(self, event): """ Handle REQUEST_STARTED event. @@ -325,6 +319,8 @@ class ShareMode(Mode): Set the info counters back to zero. """ self.history.reset() + self.toggle_history.indicator_count = 0 + self.toggle_history.update_indicator() @staticmethod def _compute_total_size(filenames): diff --git a/onionshare_gui/mode/share_mode/threads.py b/onionshare_gui/mode/share_mode/threads.py index 24e2c242..fed362eb 100644 --- a/onionshare_gui/mode/share_mode/threads.py +++ b/onionshare_gui/mode/share_mode/threads.py @@ -41,12 +41,8 @@ class CompressThread(QtCore.QThread): self.mode.common.log('CompressThread', 'run') try: - if self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size): - self.success.emit() - else: - # Cancelled - pass - + self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size) + self.success.emit() self.mode.app.cleanup_filenames += self.mode.web.share_mode.cleanup_filenames except OSError as e: self.error.emit(e.strerror) diff --git a/onionshare_gui/mode/website_mode/__init__.py b/onionshare_gui/mode/website_mode/__init__.py index 50af4725..b277b6c3 100644 --- a/onionshare_gui/mode/website_mode/__init__.py +++ b/onionshare_gui/mode/website_mode/__init__.py @@ -30,7 +30,7 @@ from onionshare.web import Web from ..file_selection import FileSelection from .. import Mode -from ..history import History, ToggleHistory, VisitHistoryItem +from ..history import History, ToggleHistory from ...widgets import Alert class WebsiteMode(Mode): @@ -80,6 +80,8 @@ class WebsiteMode(Mode): strings._('gui_all_modes_history'), 'website' ) + self.history.in_progress_label.hide() + self.history.completed_label.hide() self.history.hide() # Info label @@ -165,12 +167,8 @@ class WebsiteMode(Mode): Step 3 in starting the server. Display large filesize warning, if applicable. """ - - if self.web.website_mode.set_file_info(self.filenames): - self.success.emit() - else: - # Cancelled - pass + self.web.website_mode.set_file_info(self.filenames) + self.success.emit() def start_server_error_custom(self): """ @@ -208,21 +206,6 @@ class WebsiteMode(Mode): """ self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message')) - def handle_request_started(self, event): - """ - Handle REQUEST_STARTED event. - """ - if ( (event["path"] == '') or (event["path"].find(".htm") != -1 ) ): - item = VisitHistoryItem(self.common, event["data"]["id"], 0) - - self.history.add(event["data"]["id"], item) - self.toggle_history.update_indicator(True) - self.history.completed_count += 1 - self.history.update_completed() - - self.system_tray.showMessage(strings._('systray_website_started_title'), strings._('systray_website_started_message')) - - def on_reload_settings(self): """ If there were some files listed for sharing, we should be ok to re-enable @@ -262,6 +245,8 @@ class WebsiteMode(Mode): Set the info counters back to zero. """ self.history.reset() + self.toggle_history.indicator_count = 0 + self.toggle_history.update_indicator() @staticmethod def _compute_total_size(filenames): diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index bed86895..20873bc8 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -383,7 +383,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.share_mode.server_status.autostart_timer_container.hide() self.receive_mode.server_status.autostart_timer_container.hide() self.website_mode.server_status.autostart_timer_container.hide() - + d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only) d.settings_saved.connect(reload_settings) d.exec_() @@ -470,6 +470,15 @@ class OnionShareGui(QtWidgets.QMainWindow): elif event["type"] == Web.REQUEST_UPLOAD_CANCELED: mode.handle_request_upload_canceled(event) + elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_STARTED: + mode.handle_request_individual_file_started(event) + + elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_PROGRESS: + mode.handle_request_individual_file_progress(event) + + elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_CANCELED: + mode.handle_request_individual_file_canceled(event) + if event["type"] == Web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE: Alert(self.common, strings._('error_cannot_create_data_dir').format(event["data"]["receive_mode_dir"])) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index cb732aa2..25165688 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -212,10 +212,12 @@ class SettingsDialog(QtWidgets.QDialog): self.close_after_first_download_checkbox = QtWidgets.QCheckBox() self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Checked) self.close_after_first_download_checkbox.setText(strings._("gui_settings_close_after_first_download_option")) + individual_downloads_label = QtWidgets.QLabel(strings._("gui_settings_individual_downloads_label")) # Sharing options layout sharing_group_layout = QtWidgets.QVBoxLayout() sharing_group_layout.addWidget(self.close_after_first_download_checkbox) + sharing_group_layout.addWidget(individual_downloads_label) sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label")) sharing_group.setLayout(sharing_group_layout) @@ -638,7 +640,6 @@ class SettingsDialog(QtWidgets.QDialog): self.connect_to_tor_label.show() self.onion_settings_widget.hide() - def connection_type_bundled_toggled(self, checked): """ Connection type bundled was toggled. If checked, hide authentication fields. |