mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-26 15:59:48 -05:00
#1115 Add QR Code for onion URL
This commit is contained in:
parent
1c424500f0
commit
4cf508ed8e
@ -24,7 +24,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
from onionshare import strings
|
||||
|
||||
from ..widgets import Alert
|
||||
|
||||
from ..widgets import Image
|
||||
from ..widgets import QRCodeDialog
|
||||
|
||||
class ServerStatus(QtWidgets.QWidget):
|
||||
"""
|
||||
@ -96,6 +97,14 @@ class ServerStatus(QtWidgets.QWidget):
|
||||
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 +112,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 +200,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:
|
||||
@ -364,6 +376,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.
|
||||
|
@ -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,60 @@ class MinimumWidthWidget(QtWidgets.QWidget):
|
||||
super(MinimumWidthWidget, self).__init__()
|
||||
self.setMinimumWidth(width)
|
||||
|
||||
|
||||
class Image(qrcode.image.base.BaseImage):
|
||||
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_()
|
||||
|
||||
|
30
poetry.lock
generated
30
poetry.lock
generated
@ -53,9 +53,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "7.1.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Cross-platform colored terminal text."
|
||||
marker = "sys_platform == \"win32\""
|
||||
marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
|
||||
name = "colorama"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
@ -356,6 +356,24 @@ pytest = ">=3.0.0"
|
||||
dev = ["pre-commit", "tox"]
|
||||
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]]
|
||||
category = "main"
|
||||
description = "Python HTTP for Humans."
|
||||
@ -375,7 +393,7 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
name = "six"
|
||||
optional = false
|
||||
@ -451,7 +469,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["jaraco.itertools", "func-timeout"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "41d68ea93701fdaa1aa56159195db7a65863e3b34cc7305ef4a3f5d02f2bdf13"
|
||||
content-hash = "3f46cfec01bcb5166c9f354aaf4439064b477955f3ea2373fcfdb65d5b89276e"
|
||||
python-versions = "^3.7"
|
||||
|
||||
[metadata.files]
|
||||
@ -671,6 +689,10 @@ pytest-qt = [
|
||||
{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"},
|
||||
]
|
||||
qrcode = [
|
||||
{file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
|
||||
{file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"},
|
||||
{file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"},
|
||||
|
@ -30,6 +30,7 @@ urllib3 = "*"
|
||||
Werkzeug = "*"
|
||||
watchdog = "*"
|
||||
psutil = "*"
|
||||
qrcode = "^6.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
atomicwrites = "*"
|
||||
|
@ -28,6 +28,9 @@
|
||||
"gui_copied_url": "OnionShare address copied to clipboard",
|
||||
"gui_copied_hidservauth_title": "Copied HidServAuth",
|
||||
"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 share it over an application such as Signal.",
|
||||
"gui_waiting_to_start": "Scheduled to start in {}. 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.",
|
||||
@ -205,4 +208,4 @@
|
||||
"mode_settings_receive_data_dir_label": "Save files to",
|
||||
"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)"
|
||||
}
|
||||
}
|
||||
|
@ -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}",
|
||||
)
|
||||
|
||||
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):
|
||||
"""Test that the Server Status indicator shows we are started"""
|
||||
if type(tab.get_mode()) == ReceiveMode:
|
||||
|
@ -112,6 +112,7 @@ class TestReceive(GuiBaseTest):
|
||||
self.have_a_password(tab)
|
||||
self.url_description_shown(tab)
|
||||
self.have_copy_url_button(tab)
|
||||
self.have_show_qr_code_button(tab)
|
||||
self.server_status_indicator_says_started(tab)
|
||||
self.web_page(tab, "Select the files you want to send, then click")
|
||||
|
||||
|
@ -231,6 +231,7 @@ class TestShare(GuiBaseTest):
|
||||
self.have_a_password(tab)
|
||||
self.url_description_shown(tab)
|
||||
self.have_copy_url_button(tab)
|
||||
self.have_show_qr_code_button(tab)
|
||||
self.server_status_indicator_says_started(tab)
|
||||
|
||||
def run_all_share_mode_download_tests(self, tab):
|
||||
|
@ -70,6 +70,7 @@ class TestWebsite(GuiBaseTest):
|
||||
self.have_a_password(tab)
|
||||
self.url_description_shown(tab)
|
||||
self.have_copy_url_button(tab)
|
||||
self.have_show_qr_code_button(tab)
|
||||
self.server_status_indicator_says_started(tab)
|
||||
|
||||
def run_all_website_mode_download_tests(self, tab):
|
||||
|
Loading…
Reference in New Issue
Block a user