diff options
author | Micah Lee <micah@micahflee.com> | 2020-06-30 10:56:25 -0700 |
---|---|---|
committer | Micah Lee <micah@micahflee.com> | 2020-06-30 10:56:25 -0700 |
commit | 17b0af98f27c1550c115226450ac244a45832b56 (patch) | |
tree | 04560f5f13eeb19a3eb3f82fc10e0c358b2c86ba /onionshare_gui | |
parent | 40b43b66ab788c60fe3753f9c44207e360ff8fad (diff) | |
parent | f65e50f399f1716162343d84b6003957adc46185 (diff) | |
download | onionshare-17b0af98f27c1550c115226450ac244a45832b56.tar.gz onionshare-17b0af98f27c1550c115226450ac244a45832b56.zip |
Merge branch 'develop' into 910_flatpak
Diffstat (limited to 'onionshare_gui')
-rw-r--r-- | onionshare_gui/gui_common.py | 5 | ||||
-rw-r--r-- | onionshare_gui/tab/mode/__init__.py | 11 | ||||
-rw-r--r-- | onionshare_gui/tab/mode/file_selection.py | 12 | ||||
-rw-r--r-- | onionshare_gui/tab/mode/mode_settings_widget.py | 10 | ||||
-rw-r--r-- | onionshare_gui/tab/mode/share_mode/__init__.py | 28 | ||||
-rw-r--r-- | onionshare_gui/tab/mode/website_mode/__init__.py | 28 | ||||
-rw-r--r-- | onionshare_gui/tab/server_status.py | 40 | ||||
-rw-r--r-- | onionshare_gui/tab/tab.py | 15 | ||||
-rw-r--r-- | onionshare_gui/tab_widget.py | 19 | ||||
-rw-r--r-- | onionshare_gui/widgets.py | 64 |
10 files changed, 199 insertions, 33 deletions
diff --git a/onionshare_gui/gui_common.py b/onionshare_gui/gui_common.py index 18edded1..4b8fe5d4 100644 --- a/onionshare_gui/gui_common.py +++ b/onionshare_gui/gui_common.py @@ -211,6 +211,11 @@ class GuiCommon: color: #cc0000; }""", # Share mode and child widget styles + "share_delete_all_files_button": """ + QPushButton { + color: #3f7fcf; + } + """, "share_zip_progess_bar": """ QProgressBar { border: 1px solid #4e064f; diff --git a/onionshare_gui/tab/mode/__init__.py b/onionshare_gui/tab/mode/__init__.py index 7696f79e..fc79d0ad 100644 --- a/onionshare_gui/tab/mode/__init__.py +++ b/onionshare_gui/tab/mode/__init__.py @@ -138,7 +138,7 @@ class Mode(QtWidgets.QWidget): """ # If this is a scheduled share, display the countdown til the share starts if self.server_status.status == ServerStatus.STATUS_WORKING: - if self.server_status.autostart_timer_datetime: + if self.settings.get("general", "autostart_timer"): now = QtCore.QDateTime.currentDateTime() if self.server_status.local_only: seconds_remaining = now.secsTo( @@ -227,13 +227,8 @@ class Mode(QtWidgets.QWidget): # Start the onion thread. If this share was scheduled for a future date, # the OnionThread will start and exit 'early' to obtain the port, password # and onion address, but it will not start the WebThread yet. - if self.server_status.autostart_timer_datetime: + if self.settings.get("general", "autostart_timer"): self.start_onion_thread(obtain_onion_early=True) - else: - self.start_onion_thread() - - # If scheduling a share, delay starting the real share - if self.server_status.autostart_timer_datetime: self.common.log("Mode", "start_server", "Starting auto-start timer") self.startup_thread = AutoStartTimer(self) # Once the timer has finished, start the real share, with a WebThread @@ -241,6 +236,8 @@ class Mode(QtWidgets.QWidget): self.startup_thread.error.connect(self.start_server_error) self.startup_thread.canceled = False self.startup_thread.start() + else: + self.start_onion_thread() def start_onion_thread(self, obtain_onion_early=False): self.common.log("Mode", "start_server", "Starting an onion thread") diff --git a/onionshare_gui/tab/mode/file_selection.py b/onionshare_gui/tab/mode/file_selection.py index baa460f3..5a9e5d59 100644 --- a/onionshare_gui/tab/mode/file_selection.py +++ b/onionshare_gui/tab/mode/file_selection.py @@ -338,8 +338,8 @@ class FileSelection(QtWidgets.QVBoxLayout): else: self.add_button = QtWidgets.QPushButton(strings._("gui_add")) self.add_button.clicked.connect(self.add) - self.delete_button = QtWidgets.QPushButton(strings._("gui_delete")) - self.delete_button.clicked.connect(self.delete) + self.remove_button = QtWidgets.QPushButton(strings._("gui_remove")) + self.remove_button.clicked.connect(self.delete) button_layout = QtWidgets.QHBoxLayout() button_layout.addStretch() if self.common.platform == "Darwin": @@ -347,7 +347,7 @@ class FileSelection(QtWidgets.QVBoxLayout): button_layout.addWidget(self.add_folder_button) else: button_layout.addWidget(self.add_button) - button_layout.addWidget(self.delete_button) + button_layout.addWidget(self.remove_button) # Add the widgets self.addWidget(self.file_list) @@ -366,7 +366,7 @@ class FileSelection(QtWidgets.QVBoxLayout): self.add_folder_button.hide() else: self.add_button.hide() - self.delete_button.hide() + self.remove_button.hide() else: if self.common.platform == "Darwin": self.add_files_button.show() @@ -376,9 +376,9 @@ class FileSelection(QtWidgets.QVBoxLayout): # Delete button should be hidden if item isn't selected if len(self.file_list.selectedItems()) == 0: - self.delete_button.hide() + self.remove_button.hide() else: - self.delete_button.show() + self.remove_button.show() # Update the file list self.file_list.update() diff --git a/onionshare_gui/tab/mode/mode_settings_widget.py b/onionshare_gui/tab/mode/mode_settings_widget.py index 34335494..ed224589 100644 --- a/onionshare_gui/tab/mode/mode_settings_widget.py +++ b/onionshare_gui/tab/mode/mode_settings_widget.py @@ -76,7 +76,10 @@ class ModeSettingsWidget(QtWidgets.QWidget): self.autostart_timer_widget.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) - self.autostart_timer_widget.hide() + if self.settings.get("general", "autostart_timer"): + self.autostart_timer_widget.show() + else: + self.autostart_timer_widget.hide() # Autostart timer layout autostart_timer_layout = QtWidgets.QHBoxLayout() @@ -104,7 +107,10 @@ class ModeSettingsWidget(QtWidgets.QWidget): self.autostop_timer_widget.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) - self.autostop_timer_widget.hide() + if self.settings.get("general", "autostop_timer"): + self.autostop_timer_widget.show() + else: + self.autostop_timer_widget.hide() # Autostop timer layout autostop_timer_layout = QtWidgets.QHBoxLayout() diff --git a/onionshare_gui/tab/mode/share_mode/__init__.py b/onionshare_gui/tab/mode/share_mode/__init__.py index 1423d60a..a81f83d4 100644 --- a/onionshare_gui/tab/mode/share_mode/__init__.py +++ b/onionshare_gui/tab/mode/share_mode/__init__.py @@ -111,6 +111,17 @@ class ShareMode(Mode): self.info_label = QtWidgets.QLabel() self.info_label.hide() + # Delete all files button + self.remove_all_button = QtWidgets.QPushButton( + strings._("gui_file_selection_remove_all") + ) + self.remove_all_button.setFlat(True) + self.remove_all_button.setStyleSheet( + self.common.gui.css["share_delete_all_files_button"] + ) + self.remove_all_button.clicked.connect(self.delete_all) + self.remove_all_button.hide() + # Toggle history self.toggle_history = ToggleHistory( self.common, @@ -126,6 +137,7 @@ class ShareMode(Mode): top_bar_layout = QtWidgets.QHBoxLayout() top_bar_layout.addWidget(self.info_label) top_bar_layout.addStretch() + top_bar_layout.addWidget(self.remove_all_button) top_bar_layout.addWidget(self.toggle_history) # Primary action layout @@ -198,6 +210,8 @@ class ShareMode(Mode): # Hide and reset the downloads if we have previously shared self.reset_info_counters() + self.remove_all_button.hide() + def start_server_step2_custom(self): """ Step 2 in starting the server. Zipping up files. @@ -257,6 +271,8 @@ class ShareMode(Mode): self.history.update_in_progress() self.file_selection.file_list.adjustSize() + self.remove_all_button.show() + def cancel_server_custom(self): """ Stop the compression thread on cancel @@ -343,6 +359,7 @@ class ShareMode(Mode): if self.server_status.file_selection.get_num_files() > 0: self.primary_action.show() self.info_label.show() + self.remove_all_button.show() def update_primary_action(self): self.common.log("ShareMode", "update_primary_action") @@ -352,6 +369,7 @@ class ShareMode(Mode): if file_count > 0: self.primary_action.show() self.info_label.show() + self.remove_all_button.show() # Update the file count in the info label total_size_bytes = 0 @@ -374,6 +392,7 @@ class ShareMode(Mode): else: self.primary_action.hide() self.info_label.hide() + self.remove_all_button.hide() def reset_info_counters(self): """ @@ -383,6 +402,15 @@ class ShareMode(Mode): self.toggle_history.indicator_count = 0 self.toggle_history.update_indicator() + def delete_all(self): + """ + Delete All button clicked + """ + self.file_selection.file_list.clear() + self.file_selection.file_list.files_updated.emit() + + self.file_selection.file_list.setCurrentItem(None) + @staticmethod def _compute_total_size(filenames): total_size = 0 diff --git a/onionshare_gui/tab/mode/website_mode/__init__.py b/onionshare_gui/tab/mode/website_mode/__init__.py index db8dbf09..6199812c 100644 --- a/onionshare_gui/tab/mode/website_mode/__init__.py +++ b/onionshare_gui/tab/mode/website_mode/__init__.py @@ -114,6 +114,17 @@ class WebsiteMode(Mode): self.info_label = QtWidgets.QLabel() self.info_label.hide() + # Delete all files button + self.remove_all_button = QtWidgets.QPushButton( + strings._("gui_file_selection_remove_all") + ) + self.remove_all_button.setFlat(True) + self.remove_all_button.setStyleSheet( + self.common.gui.css["share_delete_all_files_button"] + ) + self.remove_all_button.clicked.connect(self.delete_all) + self.remove_all_button.hide() + # Toggle history self.toggle_history = ToggleHistory( self.common, @@ -129,6 +140,7 @@ class WebsiteMode(Mode): top_bar_layout = QtWidgets.QHBoxLayout() top_bar_layout.addWidget(self.info_label) top_bar_layout.addStretch() + top_bar_layout.addWidget(self.remove_all_button) top_bar_layout.addWidget(self.toggle_history) # Primary action layout @@ -191,6 +203,8 @@ class WebsiteMode(Mode): # Hide and reset the downloads if we have previously shared self.reset_info_counters() + self.remove_all_button.hide() + def start_server_step2_custom(self): """ Step 2 in starting the server. Zipping up files. @@ -228,6 +242,8 @@ class WebsiteMode(Mode): self.history.completed_count = 0 self.file_selection.file_list.adjustSize() + self.remove_all_button.show() + def cancel_server_custom(self): """ Log that the server has been cancelled @@ -248,6 +264,7 @@ class WebsiteMode(Mode): if self.server_status.file_selection.get_num_files() > 0: self.primary_action.show() self.info_label.show() + self.remove_all_button.show() def update_primary_action(self): self.common.log("WebsiteMode", "update_primary_action") @@ -257,6 +274,7 @@ class WebsiteMode(Mode): if file_count > 0: self.primary_action.show() self.info_label.show() + self.remove_all_button.show() # Update the file count in the info label total_size_bytes = 0 @@ -279,6 +297,7 @@ class WebsiteMode(Mode): else: self.primary_action.hide() self.info_label.hide() + self.remove_all_button.hide() def reset_info_counters(self): """ @@ -288,6 +307,15 @@ class WebsiteMode(Mode): self.toggle_history.indicator_count = 0 self.toggle_history.update_indicator() + def delete_all(self): + """ + Delete All button clicked + """ + self.file_selection.file_list.clear() + self.file_selection.file_list.files_updated.emit() + + self.file_selection.file_list.setCurrentItem(None) + @staticmethod def _compute_total_size(filenames): total_size = 0 diff --git a/onionshare_gui/tab/server_status.py b/onionshare_gui/tab/server_status.py index 0fbc11b6..9bfbce78 100644 --- a/onionshare_gui/tab/server_status.py +++ b/onionshare_gui/tab/server_status.py @@ -20,10 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. import platform import textwrap from PyQt5 import QtCore, QtWidgets, QtGui +from PyQt5.QtCore import Qt from onionshare import strings from ..widgets import Alert +from ..widgets import QRCodeDialog class ServerStatus(QtWidgets.QWidget): @@ -85,17 +87,31 @@ class ServerStatus(QtWidgets.QWidget): self.url.setWordWrap(True) self.url.setMinimumSize(self.url.sizeHint()) self.url.setStyleSheet(self.common.gui.css["server_status_url"]) + self.url.setTextInteractionFlags( + Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard + ) self.copy_url_button = QtWidgets.QPushButton(strings._("gui_copy_url")) self.copy_url_button.setFlat(True) self.copy_url_button.setStyleSheet( self.common.gui.css["server_status_url_buttons"] ) - self.copy_url_button.setMinimumHeight(65) self.copy_url_button.clicked.connect(self.copy_url) self.copy_hidservauth_button = QtWidgets.QPushButton( strings._("gui_copy_hidservauth") ) + self.show_url_qr_code_button = QtWidgets.QPushButton( + strings._("gui_show_url_qr_code") + ) + self.show_url_qr_code_button.hide() + self.show_url_qr_code_button.clicked.connect( + self.show_url_qr_code_button_clicked + ) + self.show_url_qr_code_button.setFlat(True) + self.show_url_qr_code_button.setStyleSheet( + self.common.gui.css["server_status_url_buttons"] + ) + self.copy_hidservauth_button.setFlat(True) self.copy_hidservauth_button.setStyleSheet( self.common.gui.css["server_status_url_buttons"] @@ -103,6 +119,7 @@ class ServerStatus(QtWidgets.QWidget): self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth) url_buttons_layout = QtWidgets.QHBoxLayout() url_buttons_layout.addWidget(self.copy_url_button) + url_buttons_layout.addWidget(self.show_url_qr_code_button) url_buttons_layout.addWidget(self.copy_hidservauth_button) url_buttons_layout.addStretch() @@ -190,6 +207,8 @@ class ServerStatus(QtWidgets.QWidget): self.url.show() self.copy_url_button.show() + self.show_url_qr_code_button.show() + if self.settings.get("general", "client_auth"): self.copy_hidservauth_button.show() else: @@ -269,7 +288,7 @@ class ServerStatus(QtWidgets.QWidget): self.common.gui.css["server_status_button_working"] ) self.server_button.setEnabled(True) - if self.autostart_timer_datetime: + if self.settings.get("general", "autostart_timer"): self.server_button.setToolTip( strings._("gui_start_server_autostart_timer_tooltip").format( self.mode_settings_widget.autostart_timer_widget.dateTime().toString( @@ -279,15 +298,6 @@ class ServerStatus(QtWidgets.QWidget): ) else: self.server_button.setText(strings._("gui_please_wait")) - - if self.settings.get("general", "autostart_timer"): - self.server_button.setToolTip( - strings._("gui_start_server_autostart_timer_tooltip").format( - self.mode_settings_widget.autostart_timer_widget.dateTime().toString( - "h:mm AP, MMMM dd, yyyy" - ) - ) - ) else: self.server_button.setStyleSheet( self.common.gui.css["server_status_button_working"] @@ -364,6 +374,12 @@ class ServerStatus(QtWidgets.QWidget): self.cancel_server() self.button_clicked.emit() + def show_url_qr_code_button_clicked(self): + """ + Show a QR code of the onion URL. + """ + self.qr_code_dialog = QRCodeDialog(self.common, self.get_url()) + def start_server(self): """ Start the server. @@ -377,7 +393,7 @@ class ServerStatus(QtWidgets.QWidget): The server has finished starting. """ self.status = self.STATUS_STARTED - self.copy_url() + # self.copy_url() self.update() self.server_started_finished.emit() diff --git a/onionshare_gui/tab/tab.py b/onionshare_gui/tab/tab.py index aa4518b5..5d1fde55 100644 --- a/onionshare_gui/tab/tab.py +++ b/onionshare_gui/tab/tab.py @@ -287,7 +287,7 @@ class Tab(QtWidgets.QWidget): strings._("gui_status_indicator_share_stopped") ) elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING: - if self.share_mode.server_status.autostart_timer_datetime: + if self.settings.get("general", "autostart_timer"): self.set_server_status_indicator_working( strings._("gui_status_indicator_share_scheduled") ) @@ -306,9 +306,14 @@ class Tab(QtWidgets.QWidget): strings._("gui_status_indicator_share_stopped") ) elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING: - self.set_server_status_indicator_working( - strings._("gui_status_indicator_share_working") - ) + if self.website_mode.server_status.autostart_timer_datetime: + self.set_server_status_indicator_working( + strings._("gui_status_indicator_share_scheduled") + ) + else: + self.set_server_status_indicator_working( + strings._("gui_status_indicator_share_working") + ) elif self.website_mode.server_status.status == ServerStatus.STATUS_STARTED: self.set_server_status_indicator_started( strings._("gui_status_indicator_share_started") @@ -320,7 +325,7 @@ class Tab(QtWidgets.QWidget): strings._("gui_status_indicator_receive_stopped") ) elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING: - if self.receive_mode.server_status.autostart_timer_datetime: + if self.settings.get("general", "autostart_timer"): self.set_server_status_indicator_working( strings._("gui_status_indicator_receive_scheduled") ) diff --git a/onionshare_gui/tab_widget.py b/onionshare_gui/tab_widget.py index e14d1f27..1ef82cad 100644 --- a/onionshare_gui/tab_widget.py +++ b/onionshare_gui/tab_widget.py @@ -59,6 +59,7 @@ class TabWidget(QtWidgets.QTabWidget): # Use a custom tab bar tab_bar = TabBar() tab_bar.move_new_tab_button.connect(self.move_new_tab_button) + tab_bar.currentChanged.connect(self.tab_changed) self.setTabBar(tab_bar) # Set up the tab widget @@ -106,6 +107,24 @@ class TabWidget(QtWidgets.QTabWidget): self.new_tab_button.move(pos) self.new_tab_button.raise_() + def tab_changed(self): + # Active tab was changed + tab_id = self.currentIndex() + self.common.log("TabWidget", "tab_changed", f"Tab was changed to {tab_id}") + try: + mode = self.tabs[tab_id].get_mode() + if mode: + # Update the server status indicator to reflect that of the current tab + self.tabs[tab_id].update_server_status_indicator() + else: + # If this tab doesn't have a mode set yet, blank the server status indicator + self.status_bar.server_status_image_label.clear() + self.status_bar.server_status_label.clear() + except KeyError: + # When all current tabs are closed, index briefly drops to -1 before resetting to 0 + # which will otherwise trigger a KeyError on tab.get_mode() above. + pass + def new_tab_clicked(self): # Create a new tab self.add_tab() diff --git a/onionshare_gui/widgets.py b/onionshare_gui/widgets.py index 74ef2c88..58ddd1b5 100644 --- a/onionshare_gui/widgets.py +++ b/onionshare_gui/widgets.py @@ -18,7 +18,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ from PyQt5 import QtCore, QtWidgets, QtGui - +from onionshare import strings +import qrcode class Alert(QtWidgets.QMessageBox): """ @@ -90,3 +91,64 @@ class MinimumWidthWidget(QtWidgets.QWidget): super(MinimumWidthWidget, self).__init__() self.setMinimumWidth(width) + +class Image(qrcode.image.base.BaseImage): + """ + A custom Image class, for use with the QR Code pixmap. + """ + + def __init__(self, border, width, box_size): + self.border = border + self.width = width + self.box_size = box_size + size = (width + border * 2) * box_size + self._image = QtGui.QImage( + size, size, QtGui.QImage.Format_RGB16) + self._image.fill(QtCore.Qt.white) + + def pixmap(self): + return QtGui.QPixmap.fromImage(self._image) + + def drawrect(self, row, col): + painter = QtGui.QPainter(self._image) + painter.fillRect( + (col + self.border) * self.box_size, + (row + self.border) * self.box_size, + self.box_size, self.box_size, + QtCore.Qt.black) + + def save(self, stream, kind=None): + pass + + +class QRCodeDialog(QtWidgets.QDialog): + """ + A dialog showing a QR code. + """ + + def __init__(self, common, text): + super(QRCodeDialog, self).__init__() + + self.common = common + self.text = text + + self.common.log("QrCode", "__init__") + + self.qr_label = QtWidgets.QLabel(self) + self.qr_label.setPixmap( + qrcode.make(self.text, image_factory=Image).pixmap()) + + self.qr_label_description = QtWidgets.QLabel(self) + self.qr_label_description.setText(strings._("gui_qr_code_description")) + self.qr_label_description.setWordWrap(True) + + self.setWindowTitle(strings._("gui_qr_code_dialog_title")) + self.setWindowIcon( + QtGui.QIcon(self.common.get_resource_path("images/logo.png")) + ) + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.qr_label) + layout.addWidget(self.qr_label_description) + + self.exec_() + |