Merge branch 'develop' into 910_flatpak

This commit is contained in:
Micah Lee 2020-06-30 10:56:25 -07:00
commit 17b0af98f2
No known key found for this signature in database
GPG Key ID: 403C2657CD994F73
24 changed files with 462 additions and 168 deletions

View File

@ -103,7 +103,7 @@ Create a .rpm on Fedora-like distros: `./install/build_rpm.sh`
For openSUSE: There are instructions for building [in the wiki](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support#opensuse-leap-150). For openSUSE: There are instructions for building [in the wiki](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support#opensuse-leap-150).
For ArchLinux: There is a PKBUILD available [here](https://aur.archlinux.org/packages/onionshare/) that can be used to install OnionShare. For ArchLinux: There is a PKBUILD available [here](https://www.archlinux.org/packages/community/any/onionshare/) that can be used to install OnionShare.
If you find that these instructions don't work for your Linux distribution or version, consult the [Linux Distribution Support wiki guide](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support), which might contain extra instructions. If you find that these instructions don't work for your Linux distribution or version, consult the [Linux Distribution Support wiki guide](https://github.com/micahflee/onionshare/wiki/Linux-Distribution-Support), which might contain extra instructions.

View File

@ -211,6 +211,11 @@ class GuiCommon:
color: #cc0000; color: #cc0000;
}""", }""",
# Share mode and child widget styles # Share mode and child widget styles
"share_delete_all_files_button": """
QPushButton {
color: #3f7fcf;
}
""",
"share_zip_progess_bar": """ "share_zip_progess_bar": """
QProgressBar { QProgressBar {
border: 1px solid #4e064f; border: 1px solid #4e064f;

View File

@ -138,7 +138,7 @@ class Mode(QtWidgets.QWidget):
""" """
# If this is a scheduled share, display the countdown til the share starts # 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.status == ServerStatus.STATUS_WORKING:
if self.server_status.autostart_timer_datetime: if self.settings.get("general", "autostart_timer"):
now = QtCore.QDateTime.currentDateTime() now = QtCore.QDateTime.currentDateTime()
if self.server_status.local_only: if self.server_status.local_only:
seconds_remaining = now.secsTo( 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, # 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 # the OnionThread will start and exit 'early' to obtain the port, password
# and onion address, but it will not start the WebThread yet. # 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) 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.common.log("Mode", "start_server", "Starting auto-start timer")
self.startup_thread = AutoStartTimer(self) self.startup_thread = AutoStartTimer(self)
# Once the timer has finished, start the real share, with a WebThread # 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.error.connect(self.start_server_error)
self.startup_thread.canceled = False self.startup_thread.canceled = False
self.startup_thread.start() self.startup_thread.start()
else:
self.start_onion_thread()
def start_onion_thread(self, obtain_onion_early=False): def start_onion_thread(self, obtain_onion_early=False):
self.common.log("Mode", "start_server", "Starting an onion thread") self.common.log("Mode", "start_server", "Starting an onion thread")

View File

@ -338,8 +338,8 @@ class FileSelection(QtWidgets.QVBoxLayout):
else: else:
self.add_button = QtWidgets.QPushButton(strings._("gui_add")) self.add_button = QtWidgets.QPushButton(strings._("gui_add"))
self.add_button.clicked.connect(self.add) self.add_button.clicked.connect(self.add)
self.delete_button = QtWidgets.QPushButton(strings._("gui_delete")) self.remove_button = QtWidgets.QPushButton(strings._("gui_remove"))
self.delete_button.clicked.connect(self.delete) self.remove_button.clicked.connect(self.delete)
button_layout = QtWidgets.QHBoxLayout() button_layout = QtWidgets.QHBoxLayout()
button_layout.addStretch() button_layout.addStretch()
if self.common.platform == "Darwin": if self.common.platform == "Darwin":
@ -347,7 +347,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
button_layout.addWidget(self.add_folder_button) button_layout.addWidget(self.add_folder_button)
else: else:
button_layout.addWidget(self.add_button) button_layout.addWidget(self.add_button)
button_layout.addWidget(self.delete_button) button_layout.addWidget(self.remove_button)
# Add the widgets # Add the widgets
self.addWidget(self.file_list) self.addWidget(self.file_list)
@ -366,7 +366,7 @@ class FileSelection(QtWidgets.QVBoxLayout):
self.add_folder_button.hide() self.add_folder_button.hide()
else: else:
self.add_button.hide() self.add_button.hide()
self.delete_button.hide() self.remove_button.hide()
else: else:
if self.common.platform == "Darwin": if self.common.platform == "Darwin":
self.add_files_button.show() self.add_files_button.show()
@ -376,9 +376,9 @@ class FileSelection(QtWidgets.QVBoxLayout):
# Delete button should be hidden if item isn't selected # Delete button should be hidden if item isn't selected
if len(self.file_list.selectedItems()) == 0: if len(self.file_list.selectedItems()) == 0:
self.delete_button.hide() self.remove_button.hide()
else: else:
self.delete_button.show() self.remove_button.show()
# Update the file list # Update the file list
self.file_list.update() self.file_list.update()

View File

@ -76,7 +76,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostart_timer_widget.setCurrentSection( self.autostart_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection 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
autostart_timer_layout = QtWidgets.QHBoxLayout() autostart_timer_layout = QtWidgets.QHBoxLayout()
@ -104,7 +107,10 @@ class ModeSettingsWidget(QtWidgets.QWidget):
self.autostop_timer_widget.setCurrentSection( self.autostop_timer_widget.setCurrentSection(
QtWidgets.QDateTimeEdit.MinuteSection 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
autostop_timer_layout = QtWidgets.QHBoxLayout() autostop_timer_layout = QtWidgets.QHBoxLayout()

View File

@ -111,6 +111,17 @@ class ShareMode(Mode):
self.info_label = QtWidgets.QLabel() self.info_label = QtWidgets.QLabel()
self.info_label.hide() 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 # Toggle history
self.toggle_history = ToggleHistory( self.toggle_history = ToggleHistory(
self.common, self.common,
@ -126,6 +137,7 @@ class ShareMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout() top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label) top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch() top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history) top_bar_layout.addWidget(self.toggle_history)
# Primary action layout # Primary action layout
@ -198,6 +210,8 @@ class ShareMode(Mode):
# Hide and reset the downloads if we have previously shared # Hide and reset the downloads if we have previously shared
self.reset_info_counters() self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self): def start_server_step2_custom(self):
""" """
Step 2 in starting the server. Zipping up files. Step 2 in starting the server. Zipping up files.
@ -257,6 +271,8 @@ class ShareMode(Mode):
self.history.update_in_progress() self.history.update_in_progress()
self.file_selection.file_list.adjustSize() self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self): def cancel_server_custom(self):
""" """
Stop the compression thread on cancel Stop the compression thread on cancel
@ -343,6 +359,7 @@ class ShareMode(Mode):
if self.server_status.file_selection.get_num_files() > 0: if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show() self.primary_action.show()
self.info_label.show() self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self): def update_primary_action(self):
self.common.log("ShareMode", "update_primary_action") self.common.log("ShareMode", "update_primary_action")
@ -352,6 +369,7 @@ class ShareMode(Mode):
if file_count > 0: if file_count > 0:
self.primary_action.show() self.primary_action.show()
self.info_label.show() self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label # Update the file count in the info label
total_size_bytes = 0 total_size_bytes = 0
@ -374,6 +392,7 @@ class ShareMode(Mode):
else: else:
self.primary_action.hide() self.primary_action.hide()
self.info_label.hide() self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self): def reset_info_counters(self):
""" """
@ -383,6 +402,15 @@ class ShareMode(Mode):
self.toggle_history.indicator_count = 0 self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator() 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 @staticmethod
def _compute_total_size(filenames): def _compute_total_size(filenames):
total_size = 0 total_size = 0

View File

@ -114,6 +114,17 @@ class WebsiteMode(Mode):
self.info_label = QtWidgets.QLabel() self.info_label = QtWidgets.QLabel()
self.info_label.hide() 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 # Toggle history
self.toggle_history = ToggleHistory( self.toggle_history = ToggleHistory(
self.common, self.common,
@ -129,6 +140,7 @@ class WebsiteMode(Mode):
top_bar_layout = QtWidgets.QHBoxLayout() top_bar_layout = QtWidgets.QHBoxLayout()
top_bar_layout.addWidget(self.info_label) top_bar_layout.addWidget(self.info_label)
top_bar_layout.addStretch() top_bar_layout.addStretch()
top_bar_layout.addWidget(self.remove_all_button)
top_bar_layout.addWidget(self.toggle_history) top_bar_layout.addWidget(self.toggle_history)
# Primary action layout # Primary action layout
@ -191,6 +203,8 @@ class WebsiteMode(Mode):
# Hide and reset the downloads if we have previously shared # Hide and reset the downloads if we have previously shared
self.reset_info_counters() self.reset_info_counters()
self.remove_all_button.hide()
def start_server_step2_custom(self): def start_server_step2_custom(self):
""" """
Step 2 in starting the server. Zipping up files. Step 2 in starting the server. Zipping up files.
@ -228,6 +242,8 @@ class WebsiteMode(Mode):
self.history.completed_count = 0 self.history.completed_count = 0
self.file_selection.file_list.adjustSize() self.file_selection.file_list.adjustSize()
self.remove_all_button.show()
def cancel_server_custom(self): def cancel_server_custom(self):
""" """
Log that the server has been cancelled Log that the server has been cancelled
@ -248,6 +264,7 @@ class WebsiteMode(Mode):
if self.server_status.file_selection.get_num_files() > 0: if self.server_status.file_selection.get_num_files() > 0:
self.primary_action.show() self.primary_action.show()
self.info_label.show() self.info_label.show()
self.remove_all_button.show()
def update_primary_action(self): def update_primary_action(self):
self.common.log("WebsiteMode", "update_primary_action") self.common.log("WebsiteMode", "update_primary_action")
@ -257,6 +274,7 @@ class WebsiteMode(Mode):
if file_count > 0: if file_count > 0:
self.primary_action.show() self.primary_action.show()
self.info_label.show() self.info_label.show()
self.remove_all_button.show()
# Update the file count in the info label # Update the file count in the info label
total_size_bytes = 0 total_size_bytes = 0
@ -279,6 +297,7 @@ class WebsiteMode(Mode):
else: else:
self.primary_action.hide() self.primary_action.hide()
self.info_label.hide() self.info_label.hide()
self.remove_all_button.hide()
def reset_info_counters(self): def reset_info_counters(self):
""" """
@ -288,6 +307,15 @@ class WebsiteMode(Mode):
self.toggle_history.indicator_count = 0 self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator() 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 @staticmethod
def _compute_total_size(filenames): def _compute_total_size(filenames):
total_size = 0 total_size = 0

View File

@ -20,10 +20,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import platform import platform
import textwrap import textwrap
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import Qt
from onionshare import strings from onionshare import strings
from ..widgets import Alert from ..widgets import Alert
from ..widgets import QRCodeDialog
class ServerStatus(QtWidgets.QWidget): class ServerStatus(QtWidgets.QWidget):
@ -85,17 +87,31 @@ class ServerStatus(QtWidgets.QWidget):
self.url.setWordWrap(True) self.url.setWordWrap(True)
self.url.setMinimumSize(self.url.sizeHint()) self.url.setMinimumSize(self.url.sizeHint())
self.url.setStyleSheet(self.common.gui.css["server_status_url"]) 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 = QtWidgets.QPushButton(strings._("gui_copy_url"))
self.copy_url_button.setFlat(True) self.copy_url_button.setFlat(True)
self.copy_url_button.setStyleSheet( self.copy_url_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"] 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_url_button.clicked.connect(self.copy_url)
self.copy_hidservauth_button = QtWidgets.QPushButton( self.copy_hidservauth_button = QtWidgets.QPushButton(
strings._("gui_copy_hidservauth") 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.setFlat(True)
self.copy_hidservauth_button.setStyleSheet( self.copy_hidservauth_button.setStyleSheet(
self.common.gui.css["server_status_url_buttons"] 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) self.copy_hidservauth_button.clicked.connect(self.copy_hidservauth)
url_buttons_layout = QtWidgets.QHBoxLayout() url_buttons_layout = QtWidgets.QHBoxLayout()
url_buttons_layout.addWidget(self.copy_url_button) 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.addWidget(self.copy_hidservauth_button)
url_buttons_layout.addStretch() url_buttons_layout.addStretch()
@ -190,6 +207,8 @@ class ServerStatus(QtWidgets.QWidget):
self.url.show() self.url.show()
self.copy_url_button.show() self.copy_url_button.show()
self.show_url_qr_code_button.show()
if self.settings.get("general", "client_auth"): if self.settings.get("general", "client_auth"):
self.copy_hidservauth_button.show() self.copy_hidservauth_button.show()
else: else:
@ -269,7 +288,7 @@ class ServerStatus(QtWidgets.QWidget):
self.common.gui.css["server_status_button_working"] self.common.gui.css["server_status_button_working"]
) )
self.server_button.setEnabled(True) self.server_button.setEnabled(True)
if self.autostart_timer_datetime: if self.settings.get("general", "autostart_timer"):
self.server_button.setToolTip( self.server_button.setToolTip(
strings._("gui_start_server_autostart_timer_tooltip").format( strings._("gui_start_server_autostart_timer_tooltip").format(
self.mode_settings_widget.autostart_timer_widget.dateTime().toString( self.mode_settings_widget.autostart_timer_widget.dateTime().toString(
@ -279,15 +298,6 @@ class ServerStatus(QtWidgets.QWidget):
) )
else: else:
self.server_button.setText(strings._("gui_please_wait")) 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: else:
self.server_button.setStyleSheet( self.server_button.setStyleSheet(
self.common.gui.css["server_status_button_working"] self.common.gui.css["server_status_button_working"]
@ -364,6 +374,12 @@ class ServerStatus(QtWidgets.QWidget):
self.cancel_server() self.cancel_server()
self.button_clicked.emit() 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): def start_server(self):
""" """
Start the server. Start the server.
@ -377,7 +393,7 @@ class ServerStatus(QtWidgets.QWidget):
The server has finished starting. The server has finished starting.
""" """
self.status = self.STATUS_STARTED self.status = self.STATUS_STARTED
self.copy_url() # self.copy_url()
self.update() self.update()
self.server_started_finished.emit() self.server_started_finished.emit()

View File

@ -287,7 +287,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped") strings._("gui_status_indicator_share_stopped")
) )
elif self.share_mode.server_status.status == ServerStatus.STATUS_WORKING: 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( self.set_server_status_indicator_working(
strings._("gui_status_indicator_share_scheduled") strings._("gui_status_indicator_share_scheduled")
) )
@ -306,9 +306,14 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_share_stopped") strings._("gui_status_indicator_share_stopped")
) )
elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING: elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING:
self.set_server_status_indicator_working( if self.website_mode.server_status.autostart_timer_datetime:
strings._("gui_status_indicator_share_working") 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: elif self.website_mode.server_status.status == ServerStatus.STATUS_STARTED:
self.set_server_status_indicator_started( self.set_server_status_indicator_started(
strings._("gui_status_indicator_share_started") strings._("gui_status_indicator_share_started")
@ -320,7 +325,7 @@ class Tab(QtWidgets.QWidget):
strings._("gui_status_indicator_receive_stopped") strings._("gui_status_indicator_receive_stopped")
) )
elif self.receive_mode.server_status.status == ServerStatus.STATUS_WORKING: 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( self.set_server_status_indicator_working(
strings._("gui_status_indicator_receive_scheduled") strings._("gui_status_indicator_receive_scheduled")
) )

View File

@ -59,6 +59,7 @@ class TabWidget(QtWidgets.QTabWidget):
# Use a custom tab bar # Use a custom tab bar
tab_bar = TabBar() tab_bar = TabBar()
tab_bar.move_new_tab_button.connect(self.move_new_tab_button) tab_bar.move_new_tab_button.connect(self.move_new_tab_button)
tab_bar.currentChanged.connect(self.tab_changed)
self.setTabBar(tab_bar) self.setTabBar(tab_bar)
# Set up the tab widget # Set up the tab widget
@ -106,6 +107,24 @@ class TabWidget(QtWidgets.QTabWidget):
self.new_tab_button.move(pos) self.new_tab_button.move(pos)
self.new_tab_button.raise_() 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): def new_tab_clicked(self):
# Create a new tab # Create a new tab
self.add_tab() self.add_tab()

View File

@ -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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
import qrcode
class Alert(QtWidgets.QMessageBox): class Alert(QtWidgets.QMessageBox):
""" """
@ -90,3 +91,64 @@ class MinimumWidthWidget(QtWidgets.QWidget):
super(MinimumWidthWidget, self).__init__() super(MinimumWidthWidget, self).__init__()
self.setMinimumWidth(width) 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_()

276
poetry.lock generated
View File

@ -12,7 +12,7 @@ description = "Atomic file writes."
name = "atomicwrites" name = "atomicwrites"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.0" version = "1.4.0"
[[package]] [[package]]
category = "dev" category = "dev"
@ -34,7 +34,7 @@ description = "Python package for providing Mozilla's CA Bundle."
name = "certifi" name = "certifi"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "2020.4.5.1" version = "2020.6.20"
[[package]] [[package]]
category = "main" category = "main"
@ -50,12 +50,12 @@ description = "Composable command line interface toolkit"
name = "click" name = "click"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "7.1.1" version = "7.1.2"
[[package]] [[package]]
category = "dev" category = "main"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\"" marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
name = "colorama" name = "colorama"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@ -95,7 +95,7 @@ description = "Basic and Digest HTTP authentication for Flask routes"
name = "flask-httpauth" name = "flask-httpauth"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "3.3.0" version = "4.1.0"
[package.dependencies] [package.dependencies]
Flask = "*" Flask = "*"
@ -114,7 +114,7 @@ description = "Internationalized Domain Names in Applications (IDNA)"
name = "idna" name = "idna"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.9" version = "2.10"
[[package]] [[package]]
category = "dev" category = "dev"
@ -123,14 +123,14 @@ marker = "python_version < \"3.8\""
name = "importlib-metadata" name = "importlib-metadata"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "1.6.0" version = "1.7.0"
[package.dependencies] [package.dependencies]
zipp = ">=0.5" zipp = ">=0.5"
[package.extras] [package.extras]
docs = ["sphinx", "rst.linker"] docs = ["sphinx", "rst.linker"]
testing = ["packaging", "importlib-resources"] testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
[[package]] [[package]]
category = "main" category = "main"
@ -146,7 +146,7 @@ description = "A very fast and expressive template engine."
name = "jinja2" name = "jinja2"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.11.1" version = "2.11.2"
[package.dependencies] [package.dependencies]
MarkupSafe = ">=0.23" MarkupSafe = ">=0.23"
@ -179,7 +179,7 @@ description = "More routines for operating on iterables, beyond itertools"
name = "more-itertools" name = "more-itertools"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "8.2.0" version = "8.4.0"
[[package]] [[package]]
category = "dev" category = "dev"
@ -187,12 +187,20 @@ description = "Core utilities for Python packages"
name = "packaging" name = "packaging"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "20.3" version = "20.4"
[package.dependencies] [package.dependencies]
pyparsing = ">=2.0.2" pyparsing = ">=2.0.2"
six = "*" six = "*"
[[package]]
category = "main"
description = "File system general utilities"
name = "pathtools"
optional = false
python-versions = "*"
version = "0.1.2"
[[package]] [[package]]
category = "main" category = "main"
description = "Python PE parsing module" description = "Python PE parsing module"
@ -220,13 +228,24 @@ version = ">=0.12"
[package.extras] [package.extras]
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
[[package]]
category = "main"
description = "Cross-platform lib for process and system monitoring in Python."
name = "psutil"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "5.7.0"
[package.extras]
enum = ["enum34"]
[[package]] [[package]]
category = "dev" category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities" description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py" name = "py"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.8.1" version = "1.9.0"
[[package]] [[package]]
category = "main" category = "main"
@ -234,7 +253,7 @@ description = "Cryptographic library for Python"
name = "pycryptodome" name = "pycryptodome"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.9.7" version = "3.9.8"
[[package]] [[package]]
category = "dev" category = "dev"
@ -275,7 +294,7 @@ description = "The sip module support for PyQt5"
name = "pyqt5-sip" name = "pyqt5-sip"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "12.7.2" version = "12.8.0"
[[package]] [[package]]
category = "main" category = "main"
@ -291,7 +310,7 @@ description = "pytest: simple powerful testing with Python"
name = "pytest" name = "pytest"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
version = "5.4.1" version = "5.4.3"
[package.dependencies] [package.dependencies]
atomicwrites = ">=1.0" atomicwrites = ">=1.0"
@ -337,13 +356,31 @@ pytest = ">=3.0.0"
dev = ["pre-commit", "tox"] dev = ["pre-commit", "tox"]
doc = ["sphinx", "sphinx-rtd-theme"] doc = ["sphinx", "sphinx-rtd-theme"]
[[package]]
category = "main"
description = "QR Code image generator"
name = "qrcode"
optional = false
python-versions = "*"
version = "6.1"
[package.dependencies]
colorama = "*"
six = "*"
[package.extras]
dev = ["tox", "pytest", "mock"]
maintainer = ["zest.releaser"]
pil = ["pillow"]
test = ["pytest", "pytest-cov", "mock"]
[[package]] [[package]]
category = "main" category = "main"
description = "Python HTTP for Humans." description = "Python HTTP for Humans."
name = "requests" name = "requests"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.23.0" version = "2.24.0"
[package.dependencies] [package.dependencies]
certifi = ">=2017.4.17" certifi = ">=2017.4.17"
@ -356,12 +393,12 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
[[package]] [[package]]
category = "dev" category = "main"
description = "Python 2 and 3 compatibility utilities" description = "Python 2 and 3 compatibility utilities"
name = "six" name = "six"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
version = "1.14.0" version = "1.15.0"
[[package]] [[package]]
category = "main" category = "main"
@ -377,20 +414,34 @@ description = "HTTP library with thread-safe connection pooling, file post, and
name = "urllib3" name = "urllib3"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "1.25.8" version = "1.25.9"
[package.extras] [package.extras]
brotli = ["brotlipy (>=0.6.0)"] brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
[[package]]
category = "main"
description = "Filesystem events monitoring"
name = "watchdog"
optional = false
python-versions = "*"
version = "0.10.3"
[package.dependencies]
pathtools = ">=0.1.1"
[package.extras]
watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"]
[[package]] [[package]]
category = "dev" category = "dev"
description = "Measures number of Terminal column cells of wide-character codes" description = "Measures the displayed width of unicode strings in a terminal"
name = "wcwidth" name = "wcwidth"
optional = false optional = false
python-versions = "*" python-versions = "*"
version = "0.1.9" version = "0.2.5"
[[package]] [[package]]
category = "main" category = "main"
@ -418,7 +469,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"] testing = ["jaraco.itertools", "func-timeout"]
[metadata] [metadata]
content-hash = "e341ce712d9198499f5e816edc1a62d75f5a913f22880ed631a8698c78d88fb7" content-hash = "3f46cfec01bcb5166c9f354aaf4439064b477955f3ea2373fcfdb65d5b89276e"
python-versions = "^3.7" python-versions = "^3.7"
[metadata.files] [metadata.files]
@ -427,24 +478,24 @@ altgraph = [
{file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"}, {file = "altgraph-0.17.tar.gz", hash = "sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa"},
] ]
atomicwrites = [ atomicwrites = [
{file = "atomicwrites-1.3.0-py2.py3-none-any.whl", hash = "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"}, {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.3.0.tar.gz", hash = "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
] ]
attrs = [ attrs = [
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
{file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
] ]
certifi = [ certifi = [
{file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
{file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
] ]
chardet = [ chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
{file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
] ]
click = [ click = [
{file = "click-7.1.1-py2.py3-none-any.whl", hash = "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"}, {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
{file = "click-7.1.1.tar.gz", hash = "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
] ]
colorama = [ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
@ -460,27 +511,27 @@ flask = [
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
] ]
flask-httpauth = [ flask-httpauth = [
{file = "Flask-HTTPAuth-3.3.0.tar.gz", hash = "sha256:6ef8b761332e780f9ff74d5f9056c2616f52babc1998b01d9f361a1e439e61b9"}, {file = "Flask-HTTPAuth-4.1.0.tar.gz", hash = "sha256:9e028e4375039a49031eb9ecc40be4761f0540476040f6eff329a31dabd4d000"},
{file = "Flask_HTTPAuth-3.3.0-py2.py3-none-any.whl", hash = "sha256:0149953720489407e51ec24bc2f86273597b7973d71cd51f9443bd0e2a89bd72"}, {file = "Flask_HTTPAuth-4.1.0-py2.py3-none-any.whl", hash = "sha256:29e0288869a213c7387f0323b6bf2c7191584fb1da8aa024d9af118e5cd70de7"},
] ]
future = [ future = [
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
] ]
idna = [ idna = [
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
] ]
importlib-metadata = [ importlib-metadata = [
{file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
{file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
] ]
itsdangerous = [ itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
] ]
jinja2 = [ jinja2 = [
{file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"}, {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
{file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"}, {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
] ]
macholib = [ macholib = [
{file = "macholib-1.14-py2.py3-none-any.whl", hash = "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"}, {file = "macholib-1.14-py2.py3-none-any.whl", hash = "sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"},
@ -522,12 +573,15 @@ markupsafe = [
{file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
] ]
more-itertools = [ more-itertools = [
{file = "more-itertools-8.2.0.tar.gz", hash = "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"}, {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
{file = "more_itertools-8.2.0-py3-none-any.whl", hash = "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c"}, {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"},
] ]
packaging = [ packaging = [
{file = "packaging-20.3-py2.py3-none-any.whl", hash = "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"}, {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
{file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"}, {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
]
pathtools = [
{file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"},
] ]
pefile = [ pefile = [
{file = "pefile-2019.4.18.tar.gz", hash = "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645"}, {file = "pefile-2019.4.18.tar.gz", hash = "sha256:a5d6e8305c6b210849b47a6174ddf9c452b2888340b8177874b862ba6c207645"},
@ -536,41 +590,54 @@ pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
] ]
psutil = [
{file = "psutil-5.7.0-cp27-none-win32.whl", hash = "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953"},
{file = "psutil-5.7.0-cp27-none-win_amd64.whl", hash = "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38"},
{file = "psutil-5.7.0-cp35-cp35m-win32.whl", hash = "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310"},
{file = "psutil-5.7.0-cp35-cp35m-win_amd64.whl", hash = "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5"},
{file = "psutil-5.7.0-cp36-cp36m-win32.whl", hash = "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e"},
{file = "psutil-5.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058"},
{file = "psutil-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8"},
{file = "psutil-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f"},
{file = "psutil-5.7.0-cp38-cp38-win32.whl", hash = "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4"},
{file = "psutil-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26"},
{file = "psutil-5.7.0.tar.gz", hash = "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e"},
]
py = [ py = [
{file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
{file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
] ]
pycryptodome = [ pycryptodome = [
{file = "pycryptodome-3.9.7-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2"}, {file = "pycryptodome-3.9.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856"},
{file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343"}, {file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82"},
{file = "pycryptodome-3.9.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862"}, {file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94"},
{file = "pycryptodome-3.9.7-cp27-cp27m-win32.whl", hash = "sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8"}, {file = "pycryptodome-3.9.8-cp27-cp27m-win32.whl", hash = "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc"},
{file = "pycryptodome-3.9.7-cp27-cp27m-win_amd64.whl", hash = "sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557"}, {file = "pycryptodome-3.9.8-cp27-cp27m-win_amd64.whl", hash = "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb"},
{file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a"}, {file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5"},
{file = "pycryptodome-3.9.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439"}, {file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0"},
{file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5"}, {file = "pycryptodome-3.9.8-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2"},
{file = "pycryptodome-3.9.7-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324"}, {file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2"},
{file = "pycryptodome-3.9.7-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476"}, {file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345"},
{file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f"}, {file = "pycryptodome-3.9.8-cp35-cp35m-win32.whl", hash = "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23"},
{file = "pycryptodome-3.9.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0"}, {file = "pycryptodome-3.9.8-cp35-cp35m-win_amd64.whl", hash = "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b"},
{file = "pycryptodome-3.9.7-cp35-cp35m-win32.whl", hash = "sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a"}, {file = "pycryptodome-3.9.8-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299"},
{file = "pycryptodome-3.9.7-cp35-cp35m-win_amd64.whl", hash = "sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04"}, {file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982"},
{file = "pycryptodome-3.9.7-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36"}, {file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1"},
{file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65"}, {file = "pycryptodome-3.9.8-cp36-cp36m-win32.whl", hash = "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739"},
{file = "pycryptodome-3.9.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520"}, {file = "pycryptodome-3.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e"},
{file = "pycryptodome-3.9.7-cp36-cp36m-win32.whl", hash = "sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a"}, {file = "pycryptodome-3.9.8-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a"},
{file = "pycryptodome-3.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd"}, {file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c"},
{file = "pycryptodome-3.9.7-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a"}, {file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21"},
{file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd"}, {file = "pycryptodome-3.9.8-cp37-cp37m-win32.whl", hash = "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876"},
{file = "pycryptodome-3.9.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95"}, {file = "pycryptodome-3.9.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8"},
{file = "pycryptodome-3.9.7-cp37-cp37m-win32.whl", hash = "sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed"}, {file = "pycryptodome-3.9.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a"},
{file = "pycryptodome-3.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8"}, {file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149"},
{file = "pycryptodome-3.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35"}, {file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6"},
{file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4"}, {file = "pycryptodome-3.9.8-cp38-cp38-win32.whl", hash = "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba"},
{file = "pycryptodome-3.9.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad"}, {file = "pycryptodome-3.9.8-cp38-cp38-win_amd64.whl", hash = "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68"},
{file = "pycryptodome-3.9.7-cp38-cp38-win32.whl", hash = "sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40"}, {file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_i686.whl", hash = "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60"},
{file = "pycryptodome-3.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e"}, {file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a"},
{file = "pycryptodome-3.9.7.tar.gz", hash = "sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2"}, {file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"},
] ]
pyinstaller = [ pyinstaller = [
{file = "PyInstaller-3.6.tar.gz", hash = "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"}, {file = "PyInstaller-3.6.tar.gz", hash = "sha256:3730fa80d088f8bb7084d32480eb87cbb4ddb64123363763cf8f2a1378c1c4b7"},
@ -587,23 +654,23 @@ pyqt5 = [
{file = "PyQt5-5.14.0.tar.gz", hash = "sha256:0145a6b7de15756366decb736c349a0cb510d706c83fda5b8cd9e0557bc1da72"}, {file = "PyQt5-5.14.0.tar.gz", hash = "sha256:0145a6b7de15756366decb736c349a0cb510d706c83fda5b8cd9e0557bc1da72"},
] ]
pyqt5-sip = [ pyqt5-sip = [
{file = "PyQt5_sip-12.7.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:df4f5cdb86f47df5f6fc35be29cc45df7b5a2c171d07dbf377d558b226554ea3"}, {file = "PyQt5_sip-12.8.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:9ef12754021bcc1246f97e00ea62b5594dd5c61192830639ab4a1640bd4b7940"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:360de29634e2ce1df84d2b588bd8c1a29b768f3a5225869d63adb03bc21bd32a"}, {file = "PyQt5_sip-12.8.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fa3d70f370604efc67085849d3d1d3d2109faa716c520faf601d15845df64de6"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-win32.whl", hash = "sha256:31c74602ccd6b70e4352550eb41aa980dc1d6009444f3c8eb1b844e84bd144cf"}, {file = "PyQt5_sip-12.8.0-cp35-cp35m-win32.whl", hash = "sha256:61aa60fb848d740581646603a12c2dcb8d7c4cbd2a9c476a1c891ec360ff0b87"},
{file = "PyQt5_sip-12.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:168a6d700daf366b7cf255a8cabf8d07bfe2294859e6b3b2636c36c2f89265c9"}, {file = "PyQt5_sip-12.8.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8d9f4dc7dbae9783c5dafd66801875a2ebf9302c3addd5739f772285c1c1e91c"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:b68cfe632a512c0551e8860f35c1fcab5cd1ad5e168b4814fddd88121f447b0a"}, {file = "PyQt5_sip-12.8.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:9b69db29571dde679908fb237784a8e7af4a2cbf1b7bb25bdb86e487210e04d2"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b068f4791e97427d82a27e7df28cc3ee33f7e4353f48ed6a123f8cdba44266b2"}, {file = "PyQt5_sip-12.8.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:58eae636e0b1926cddec98a703319a47f671cef07d73aaa525ba421cd4adfeb5"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-win32.whl", hash = "sha256:9dd5769e83e64d017d02981563c8159d825425b6c4998c937a880888f4dcb7a3"}, {file = "PyQt5_sip-12.8.0-cp36-cp36m-win32.whl", hash = "sha256:e6254647fa35e1260282aeb9c32a3dd363287b9a1ffcc4f22bd27e54178e92e4"},
{file = "PyQt5_sip-12.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:16a9a4daf85bfaa3aec35237ff28d8773a3ec937d9f8dc7fc3db7716de42d4a9"}, {file = "PyQt5_sip-12.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f4c294bfaf2be8004583266d4621bfd3a387e12946f548f966a7fbec91845f1b"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb9076ba0e574b2f026759103eb0e12051128714f5aa136cca53229d3ad72d1"}, {file = "PyQt5_sip-12.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:224e2fbb7088595940c348d168a317caa2110cbb7a5b957a8c3fc0d9296ee069"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:65fceeea2ac738a92f7e3e459ece1b4e2fbf49fd1d6b732a73d0d4bcfc434452"}, {file = "PyQt5_sip-12.8.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c19c4ad67af087e8f4411da7422391b236b941f5f0697f615c5816455d1355d"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-win32.whl", hash = "sha256:11f8cc2de287c3457fee53e781f06fb71f04251e7ae408ed22696ed65fd2bcf4"}, {file = "PyQt5_sip-12.8.0-cp37-cp37m-win32.whl", hash = "sha256:2a1153cda63f2632d3d5698f0cf29f6b1f1d5162305dc6f5b23336ad8f1039ed"},
{file = "PyQt5_sip-12.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:85e68b8936f1756060ddcb3ef0a84af78ce89993fa6594b3049a0eca53d6d2fa"}, {file = "PyQt5_sip-12.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:94c80677b1e8c92fa080e24045d54ace5e4343c4ee6d0216675cd91d6f8e122a"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4f87d59d29ca1c5a4005bbec27af002be787210dc5f8f87fe5d747883a836083"}, {file = "PyQt5_sip-12.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a2239d16a49ce6eaf10166a84424543111f8ebe49d3c124d02af91b01a58425"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b34c1f227a8f8e97059f20e5424f117f66a302b42e34d4039158494c6371b1ce"}, {file = "PyQt5_sip-12.8.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b1bbe763d431d26f9565cba3e99866768761366ab6d609d2506d194882156fa7"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-win32.whl", hash = "sha256:01919371d32b26208b2f0318f1e15680d3aa60d1ced1812a5dac8bdb483fea69"}, {file = "PyQt5_sip-12.8.0-cp38-cp38-win32.whl", hash = "sha256:d7b8a8f89385ad9e3da38e0123c22c0efc18005e0e2731b6b95e4c21db2049d2"},
{file = "PyQt5_sip-12.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:a8a6c0512641fc042726b6253b2d5f3f3f800098334d198d8ebdf337d85ab3d7"}, {file = "PyQt5_sip-12.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:1d65ce08a56282fb0273dd06585b8927b88d4fba71c01a54f8e2ac87ac1ed387"},
{file = "PyQt5_sip-12.7.2.tar.gz", hash = "sha256:16a19b9f36985b8bff30b89fb8859d831713dd528fba5600563e36ff077960a2"}, {file = "PyQt5_sip-12.8.0.tar.gz", hash = "sha256:0a34b6596bdd28d52da3a51fa8d9bb0b287bcb605c2512aa3251b9028cc71f4d"},
] ]
pysocks = [ pysocks = [
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
@ -611,8 +678,8 @@ pysocks = [
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
] ]
pytest = [ pytest = [
{file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"}, {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
{file = "pytest-5.4.1.tar.gz", hash = "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970"}, {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
] ]
pytest-faulthandler = [ pytest-faulthandler = [
{file = "pytest-faulthandler-2.0.1.tar.gz", hash = "sha256:ed72bbce87ac344da81eb7d882196a457d4a1026a3da4a57154dacd85cd71ae5"}, {file = "pytest-faulthandler-2.0.1.tar.gz", hash = "sha256:ed72bbce87ac344da81eb7d882196a457d4a1026a3da4a57154dacd85cd71ae5"},
@ -622,24 +689,31 @@ pytest-qt = [
{file = "pytest-qt-3.3.0.tar.gz", hash = "sha256:714b0bf86c5313413f2d300ac613515db3a1aef595051ab8ba2ffe619dbe8925"}, {file = "pytest-qt-3.3.0.tar.gz", hash = "sha256:714b0bf86c5313413f2d300ac613515db3a1aef595051ab8ba2ffe619dbe8925"},
{file = "pytest_qt-3.3.0-py2.py3-none-any.whl", hash = "sha256:5f8928288f50489d83f5d38caf2d7d9fcd6e7cf769947902caa4661dc7c851e3"}, {file = "pytest_qt-3.3.0-py2.py3-none-any.whl", hash = "sha256:5f8928288f50489d83f5d38caf2d7d9fcd6e7cf769947902caa4661dc7c851e3"},
] ]
qrcode = [
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
]
requests = [ requests = [
{file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
{file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
] ]
six = [ six = [
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
{file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
] ]
stem = [ stem = [
{file = "stem-1.8.0.tar.gz", hash = "sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2"}, {file = "stem-1.8.0.tar.gz", hash = "sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2"},
] ]
urllib3 = [ urllib3 = [
{file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"}, {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"},
{file = "urllib3-1.25.8.tar.gz", hash = "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"}, {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
]
watchdog = [
{file = "watchdog-0.10.3.tar.gz", hash = "sha256:4214e1379d128b0588021880ccaf40317ee156d4603ac388b9adcf29165e0c04"},
] ]
wcwidth = [ wcwidth = [
{file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
{file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
] ]
werkzeug = [ werkzeug = [
{file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"},

View File

@ -28,6 +28,9 @@ requests = "*"
stem = "*" stem = "*"
urllib3 = "*" urllib3 = "*"
Werkzeug = "*" Werkzeug = "*"
watchdog = "*"
psutil = "*"
qrcode = "^6.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
atomicwrites = "*" atomicwrites = "*"

View File

@ -11,7 +11,8 @@
"gui_add": "Add", "gui_add": "Add",
"gui_add_files": "Add Files", "gui_add_files": "Add Files",
"gui_add_folder": "Add Folder", "gui_add_folder": "Add Folder",
"gui_delete": "Delete", "gui_remove": "Remove",
"gui_file_selection_remove_all": "Remove All",
"gui_choose_items": "Choose", "gui_choose_items": "Choose",
"gui_share_start_server": "Start sharing", "gui_share_start_server": "Start sharing",
"gui_share_stop_server": "Stop sharing", "gui_share_stop_server": "Stop sharing",
@ -28,6 +29,9 @@
"gui_copied_url": "OnionShare address copied to clipboard", "gui_copied_url": "OnionShare address copied to clipboard",
"gui_copied_hidservauth_title": "Copied HidServAuth", "gui_copied_hidservauth_title": "Copied HidServAuth",
"gui_copied_hidservauth": "HidServAuth line copied to clipboard", "gui_copied_hidservauth": "HidServAuth line copied to clipboard",
"gui_show_url_qr_code": "Show QR code",
"gui_qr_code_dialog_title": "OnionShare QR Code",
"gui_qr_code_description": "Scan this QR code with a QR reader, such as the camera on your phone, in order to more easily share the OnionShare address with someone.",
"gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.", "gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.",
"gui_please_wait": "Starting… Click to cancel.", "gui_please_wait": "Starting… Click to cancel.",
"error_rate_limit": "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.", "error_rate_limit": "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share.",
@ -205,4 +209,4 @@
"mode_settings_receive_data_dir_label": "Save files to", "mode_settings_receive_data_dir_label": "Save files to",
"mode_settings_receive_data_dir_browse_button": "Browse", "mode_settings_receive_data_dir_browse_button": "Browse",
"mode_settings_website_disable_csp_checkbox": "Disable Content Security Policy header (allows your website to use third-party resources)" "mode_settings_website_disable_csp_checkbox": "Disable Content Security Policy header (allows your website to use third-party resources)"
} }

File diff suppressed because one or more lines are too long

2
share/static/js/jquery-3.5.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,9 @@ $(function(){
$('#flashes').append($('<li>').addClass(category).text(message)); $('#flashes').append($('<li>').addClass(category).text(message));
}; };
var scriptSrc = document.getElementById('receive-script').src;
var staticImgPath = scriptSrc.substr(0, scriptSrc.lastIndexOf( '/' )+1).replace('js', 'img');
// Intercept submitting the form // Intercept submitting the form
$('#send').submit(function(event){ $('#send').submit(function(event){
event.preventDefault(); event.preventDefault();
@ -38,7 +41,7 @@ $(function(){
// and update the status // and update the status
if(event.loaded == event.total) { if(event.loaded == event.total) {
$('.cancel', ajax.$upload_div).remove(); $('.cancel', ajax.$upload_div).remove();
$('.upload-status', ajax.$upload_div).html('<img src="/static/img/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...'); $('.upload-status', ajax.$upload_div).html('<img src="' + staticImgPath + '/ajax.gif" alt="" /> Waiting for data to finish traversing Tor network ...');
} }
}, false); }, false);

View File

@ -38,7 +38,7 @@
</form> </form>
</div> </div>
<script src="{{ static_url_path }}/js/jquery-3.4.0.min.js"></script> <script src="{{ static_url_path }}/js/jquery-3.5.1.min.js"></script>
<script async src="{{ static_url_path }}/js/receive.js"></script> <script async src="{{ static_url_path }}/js/receive.js" id="receive-script"></script>
</body> </body>
</html> </html>

View File

@ -297,6 +297,17 @@ class GuiBaseTest(unittest.TestCase):
f"http://onionshare:{tab.get_mode().server_status.web.password}@127.0.0.1:{tab.app.port}", f"http://onionshare:{tab.get_mode().server_status.web.password}@127.0.0.1:{tab.app.port}",
) )
def have_show_qr_code_button(self, tab):
"""Test that the Show QR Code URL button is shown and that it loads a QR Code Dialog"""
self.assertTrue(tab.get_mode().server_status.show_url_qr_code_button.isVisible())
def accept_dialog():
window = tab.common.gui.qtapp.activeWindow()
if window:
window.close()
QtCore.QTimer.singleShot(500, accept_dialog)
tab.get_mode().server_status.show_url_qr_code_button.click()
def server_status_indicator_says_started(self, tab): def server_status_indicator_says_started(self, tab):
"""Test that the Server Status indicator shows we are started""" """Test that the Server Status indicator shows we are started"""
if type(tab.get_mode()) == ReceiveMode: if type(tab.get_mode()) == ReceiveMode:
@ -388,13 +399,13 @@ class GuiBaseTest(unittest.TestCase):
tab.get_mode().server_status.file_selection.get_num_files(), num tab.get_mode().server_status.file_selection.get_num_files(), num
) )
def add_delete_buttons_hidden(self, tab): def add_remove_buttons_hidden(self, tab):
"""Test that the add and delete buttons are hidden when the server starts""" """Test that the add and remove buttons are hidden when the server starts"""
self.assertFalse( self.assertFalse(
tab.get_mode().server_status.file_selection.add_button.isVisible() tab.get_mode().server_status.file_selection.add_button.isVisible()
) )
self.assertFalse( self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible() tab.get_mode().server_status.file_selection.remove_button.isVisible()
) )
# Auto-stop timer tests # Auto-stop timer tests

View File

@ -20,7 +20,7 @@ do
shift shift
done done
pytest $PARAMS -vvv ./tests/test_cli*.py pytest $PARAMS -vvv ./tests/test_cli*.py || exit 1
for filename in ./tests/test_gui_*.py; do for filename in ./tests/test_gui_*.py; do
pytest $PARAMS -vvv --no-qt-log $filename pytest $PARAMS -vvv --no-qt-log $filename || exit 1
done done

View File

@ -112,6 +112,7 @@ class TestReceive(GuiBaseTest):
self.have_a_password(tab) self.have_a_password(tab)
self.url_description_shown(tab) self.url_description_shown(tab)
self.have_copy_url_button(tab) self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab) self.server_status_indicator_says_started(tab)
self.web_page(tab, "Select the files you want to send, then click") self.web_page(tab, "Select the files you want to send, then click")

View File

@ -12,8 +12,8 @@ from .gui_base_test import GuiBaseTest
class TestShare(GuiBaseTest): class TestShare(GuiBaseTest):
# Shared test methods # Shared test methods
def deleting_all_files_hides_delete_button(self, tab): def removing_all_files_hides_remove_button(self, tab):
"""Test that clicking on the file item shows the delete button. Test that deleting the only item in the list hides the delete button""" """Test that clicking on the file item shows the remove button. Test that removing the only item in the list hides the remove button"""
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect( rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0) tab.get_mode().server_status.file_selection.file_list.item(0)
) )
@ -22,12 +22,12 @@ class TestShare(GuiBaseTest):
QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
pos=rect.center(), pos=rect.center(),
) )
# Delete button should be visible # Remove button should be visible
self.assertTrue( self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible() tab.get_mode().server_status.file_selection.remove_button.isVisible()
) )
# Click delete, delete button should still be visible since we have one more file # Click remove, remove button should still be visible since we have one more file
tab.get_mode().server_status.file_selection.delete_button.click() tab.get_mode().server_status.file_selection.remove_button.click()
rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect( rect = tab.get_mode().server_status.file_selection.file_list.visualItemRect(
tab.get_mode().server_status.file_selection.file_list.item(0) tab.get_mode().server_status.file_selection.file_list.item(0)
) )
@ -37,17 +37,17 @@ class TestShare(GuiBaseTest):
pos=rect.center(), pos=rect.center(),
) )
self.assertTrue( self.assertTrue(
tab.get_mode().server_status.file_selection.delete_button.isVisible() tab.get_mode().server_status.file_selection.remove_button.isVisible()
) )
tab.get_mode().server_status.file_selection.delete_button.click() tab.get_mode().server_status.file_selection.remove_button.click()
# No more files, the delete button should be hidden # No more files, the remove button should be hidden
self.assertFalse( self.assertFalse(
tab.get_mode().server_status.file_selection.delete_button.isVisible() tab.get_mode().server_status.file_selection.remove_button.isVisible()
) )
def add_a_file_and_delete_using_its_delete_widget(self, tab): def add_a_file_and_remove_using_its_remove_widget(self, tab):
"""Test that we can also delete a file by clicking on its [X] widget""" """Test that we can also remove a file by clicking on its [X] widget"""
num_files = tab.get_mode().server_status.file_selection.get_num_files() num_files = tab.get_mode().server_status.file_selection.get_num_files()
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0]) tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.item( tab.get_mode().server_status.file_selection.file_list.item(
@ -55,6 +55,14 @@ class TestShare(GuiBaseTest):
).item_button.click() ).item_button.click()
self.file_selection_widget_has_files(tab, num_files) self.file_selection_widget_has_files(tab, num_files)
def add_a_file_and_remove_using_remove_all_widget(self, tab):
"""Test that we can also remove all files by clicking on the Remove All widget"""
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[0])
tab.get_mode().server_status.file_selection.file_list.add_file(self.tmpfiles[1])
tab.get_mode().remove_all_button.click()
# Should be no files after clearing all
self.file_selection_widget_has_files(tab, 0)
def file_selection_widget_read_files(self, tab): def file_selection_widget_read_files(self, tab):
"""Re-add some files to the list so we can share""" """Re-add some files to the list so we can share"""
num_files = tab.get_mode().server_status.file_selection.get_num_files() num_files = tab.get_mode().server_status.file_selection.get_num_files()
@ -189,7 +197,7 @@ class TestShare(GuiBaseTest):
"""Test that we can cancel a share before it's started up """ """Test that we can cancel a share before it's started up """
self.server_working_on_start_button_pressed(tab) self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_scheduled(tab) self.server_status_indicator_says_scheduled(tab)
self.add_delete_buttons_hidden(tab) self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab) self.mode_settings_widget_is_hidden(tab)
self.set_autostart_timer(tab, 10) self.set_autostart_timer(tab, 10)
QtTest.QTest.mousePress( QtTest.QTest.mousePress(
@ -216,21 +224,22 @@ class TestShare(GuiBaseTest):
self.history_is_not_visible(tab) self.history_is_not_visible(tab)
self.click_toggle_history(tab) self.click_toggle_history(tab)
self.history_is_visible(tab) self.history_is_visible(tab)
self.deleting_all_files_hides_delete_button(tab) self.removing_all_files_hides_remove_button(tab)
self.add_a_file_and_delete_using_its_delete_widget(tab) self.add_a_file_and_remove_using_its_remove_widget(tab)
self.file_selection_widget_read_files(tab) self.file_selection_widget_read_files(tab)
def run_all_share_mode_started_tests(self, tab, startup_time=2000): def run_all_share_mode_started_tests(self, tab, startup_time=2000):
"""Tests in share mode after starting a share""" """Tests in share mode after starting a share"""
self.server_working_on_start_button_pressed(tab) self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab) self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab) self.add_remove_buttons_hidden(tab)
self.mode_settings_widget_is_hidden(tab) self.mode_settings_widget_is_hidden(tab)
self.server_is_started(tab, startup_time) self.server_is_started(tab, startup_time)
self.web_server_is_running(tab) self.web_server_is_running(tab)
self.have_a_password(tab) self.have_a_password(tab)
self.url_description_shown(tab) self.url_description_shown(tab)
self.have_copy_url_button(tab) self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab) self.server_status_indicator_says_started(tab)
def run_all_share_mode_download_tests(self, tab): def run_all_share_mode_download_tests(self, tab):
@ -269,7 +278,7 @@ class TestShare(GuiBaseTest):
self.run_all_share_mode_started_tests(tab) self.run_all_share_mode_started_tests(tab)
self.run_all_share_mode_download_tests(tab) self.run_all_share_mode_download_tests(tab)
def run_all_clear_all_button_tests(self, tab): def run_all_clear_all_history_button_tests(self, tab):
"""Test the Clear All history button""" """Test the Clear All history button"""
self.run_all_share_mode_setup_tests(tab) self.run_all_share_mode_setup_tests(tab)
self.run_all_share_mode_started_tests(tab) self.run_all_share_mode_started_tests(tab)
@ -279,6 +288,11 @@ class TestShare(GuiBaseTest):
self.individual_file_is_viewable_or_not(tab) self.individual_file_is_viewable_or_not(tab)
self.clear_all_history_items(tab, 2) self.clear_all_history_items(tab, 2)
def run_all_remove_all_file_selection_button_tests(self, tab):
"""Test the Remove All File Selection button"""
self.run_all_share_mode_setup_tests(tab)
self.add_a_file_and_remove_using_remove_all_widget(tab)
def run_all_share_mode_individual_file_tests(self, tab): def run_all_share_mode_individual_file_tests(self, tab):
"""Tests in share mode when viewing an individual file""" """Tests in share mode when viewing an individual file"""
self.run_all_share_mode_setup_tests(tab) self.run_all_share_mode_setup_tests(tab)
@ -375,15 +389,27 @@ class TestShare(GuiBaseTest):
self.close_all_tabs() self.close_all_tabs()
@pytest.mark.gui @pytest.mark.gui
def test_clear_all_button(self): def test_clear_all_history_button(self):
""" """
Test canceling a scheduled share Test clearing all history items
""" """
tab = self.new_share_tab() tab = self.new_share_tab()
tab.get_mode().autostop_sharing_checkbox.click() tab.get_mode().autostop_sharing_checkbox.click()
self.run_all_common_setup_tests() self.run_all_common_setup_tests()
self.run_all_clear_all_button_tests(tab) self.run_all_clear_all_history_button_tests(tab)
self.close_all_tabs()
@pytest.mark.gui
def test_remove_all_file_selection_button(self):
"""
Test remove all file items at once
"""
tab = self.new_share_tab()
self.run_all_common_setup_tests()
self.run_all_remove_all_file_selection_button_tests(tab)
self.close_all_tabs() self.close_all_tabs()
@ -561,6 +587,7 @@ class TestShare(GuiBaseTest):
Rate limit should be triggered Rate limit should be triggered
""" """
tab = self.new_share_tab() tab = self.new_share_tab()
def accept_dialog(): def accept_dialog():
window = tab.common.gui.qtapp.activeWindow() window = tab.common.gui.qtapp.activeWindow()
if window: if window:
@ -580,6 +607,7 @@ class TestShare(GuiBaseTest):
Public mode should skip the rate limit Public mode should skip the rate limit
""" """
tab = self.new_share_tab() tab = self.new_share_tab()
def accept_dialog(): def accept_dialog():
window = tab.common.gui.qtapp.activeWindow() window = tab.common.gui.qtapp.activeWindow()
if window: if window:

View File

@ -138,18 +138,21 @@ class TestTabs(GuiBaseTest):
self.gui.tabs.widget(1).share_button.click() self.gui.tabs.widget(1).share_button.click()
self.assertFalse(self.gui.tabs.widget(1).new_tab.isVisible()) self.assertFalse(self.gui.tabs.widget(1).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(1).share_mode.isVisible()) self.assertTrue(self.gui.tabs.widget(1).share_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# New tab, receive files # New tab, receive files
self.gui.tabs.new_tab_button.click() self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(2).receive_button.click() self.gui.tabs.widget(2).receive_button.click()
self.assertFalse(self.gui.tabs.widget(2).new_tab.isVisible()) self.assertFalse(self.gui.tabs.widget(2).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(2).receive_mode.isVisible()) self.assertTrue(self.gui.tabs.widget(2).receive_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to receive')
# New tab, publish website # New tab, publish website
self.gui.tabs.new_tab_button.click() self.gui.tabs.new_tab_button.click()
self.gui.tabs.widget(3).website_button.click() self.gui.tabs.widget(3).website_button.click()
self.assertFalse(self.gui.tabs.widget(3).new_tab.isVisible()) self.assertFalse(self.gui.tabs.widget(3).new_tab.isVisible())
self.assertTrue(self.gui.tabs.widget(3).website_mode.isVisible()) self.assertTrue(self.gui.tabs.widget(3).website_mode.isVisible())
self.assertEqual(self.gui.status_bar.server_status_label.text(), 'Ready to share')
# Close tabs # Close tabs
self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide).click() self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide).click()

View File

@ -64,12 +64,13 @@ class TestWebsite(GuiBaseTest):
"""Tests in website mode after starting a share""" """Tests in website mode after starting a share"""
self.server_working_on_start_button_pressed(tab) self.server_working_on_start_button_pressed(tab)
self.server_status_indicator_says_starting(tab) self.server_status_indicator_says_starting(tab)
self.add_delete_buttons_hidden(tab) self.add_remove_buttons_hidden(tab)
self.server_is_started(tab, startup_time) self.server_is_started(tab, startup_time)
self.web_server_is_running(tab) self.web_server_is_running(tab)
self.have_a_password(tab) self.have_a_password(tab)
self.url_description_shown(tab) self.url_description_shown(tab)
self.have_copy_url_button(tab) self.have_copy_url_button(tab)
self.have_show_qr_code_button(tab)
self.server_status_indicator_says_started(tab) self.server_status_indicator_says_started(tab)
def run_all_website_mode_download_tests(self, tab): def run_all_website_mode_download_tests(self, tab):