new ConnectionDialog for managing server settings, nickname and password profile with input validation, statusbar tips

This commit is contained in:
Xevion
2021-01-25 11:25:58 -06:00
parent 0dcfa234ad
commit 9f317f3b96
5 changed files with 561 additions and 1 deletions

176
client/ConnectionDialog.py Normal file
View File

@@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '.\client\ui\ConnectionDialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ConnectionDialog(object):
def setupUi(self, ConnectionDialog):
ConnectionDialog.setObjectName("ConnectionDialog")
ConnectionDialog.setWindowModality(QtCore.Qt.WindowModal)
ConnectionDialog.resize(665, 450)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(3)
sizePolicy.setHeightForWidth(ConnectionDialog.sizePolicy().hasHeightForWidth())
ConnectionDialog.setSizePolicy(sizePolicy)
ConnectionDialog.setMinimumSize(QtCore.QSize(660, 450))
self.gridLayout = QtWidgets.QGridLayout(ConnectionDialog)
self.gridLayout.setObjectName("gridLayout")
self.server_connections_tab = QtWidgets.QTabWidget(ConnectionDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(2)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.server_connections_tab.sizePolicy().hasHeightForWidth())
self.server_connections_tab.setSizePolicy(sizePolicy)
self.server_connections_tab.setToolTip("")
self.server_connections_tab.setTabPosition(QtWidgets.QTabWidget.North)
self.server_connections_tab.setTabShape(QtWidgets.QTabWidget.Rounded)
self.server_connections_tab.setObjectName("server_connections_tab")
self.favorites_tab = QtWidgets.QWidget()
self.favorites_tab.setObjectName("favorites_tab")
self.gridLayout_3 = QtWidgets.QGridLayout(self.favorites_tab)
self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
self.gridLayout_3.setObjectName("gridLayout_3")
self.favorite_connections_list = QtWidgets.QListWidget(self.favorites_tab)
self.favorite_connections_list.setToolTip("")
self.favorite_connections_list.setFrameShape(QtWidgets.QFrame.NoFrame)
self.favorite_connections_list.setObjectName("favorite_connections_list")
self.gridLayout_3.addWidget(self.favorite_connections_list, 0, 0, 1, 1)
self.server_connections_tab.addTab(self.favorites_tab, "")
self.recent_tab = QtWidgets.QWidget()
self.recent_tab.setAutoFillBackground(False)
self.recent_tab.setObjectName("recent_tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.recent_tab)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setHorizontalSpacing(7)
self.gridLayout_2.setObjectName("gridLayout_2")
self.recent_connections_list = QtWidgets.QListView(self.recent_tab)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.recent_connections_list.sizePolicy().hasHeightForWidth())
self.recent_connections_list.setSizePolicy(sizePolicy)
self.recent_connections_list.setAutoFillBackground(False)
self.recent_connections_list.setFrameShape(QtWidgets.QFrame.NoFrame)
self.recent_connections_list.setFrameShadow(QtWidgets.QFrame.Plain)
self.recent_connections_list.setLineWidth(1)
self.recent_connections_list.setObjectName("recent_connections_list")
self.gridLayout_2.addWidget(self.recent_connections_list, 0, 0, 1, 1)
self.server_connections_tab.addTab(self.recent_tab, "")
self.gridLayout.addWidget(self.server_connections_tab, 0, 0, 1, 1)
self.connection_groupbox = QtWidgets.QGroupBox(ConnectionDialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.connection_groupbox.sizePolicy().hasHeightForWidth())
self.connection_groupbox.setSizePolicy(sizePolicy)
self.connection_groupbox.setMinimumSize(QtCore.QSize(0, 100))
self.connection_groupbox.setObjectName("connection_groupbox")
self.gridLayout_4 = QtWidgets.QGridLayout(self.connection_groupbox)
self.gridLayout_4.setObjectName("gridLayout_4")
self.connect_layout = QtWidgets.QHBoxLayout()
self.connect_layout.setObjectName("connect_layout")
self.test_connection_button = QtWidgets.QPushButton(self.connection_groupbox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(35)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.test_connection_button.sizePolicy().hasHeightForWidth())
self.test_connection_button.setSizePolicy(sizePolicy)
self.test_connection_button.setFocusPolicy(QtCore.Qt.NoFocus)
self.test_connection_button.setObjectName("test_connection_button")
self.connect_layout.addWidget(self.test_connection_button)
self.connect_button = QtWidgets.QPushButton(self.connection_groupbox)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(100)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.connect_button.sizePolicy().hasHeightForWidth())
self.connect_button.setSizePolicy(sizePolicy)
self.connect_button.setFocusPolicy(QtCore.Qt.NoFocus)
self.connect_button.setObjectName("connect_button")
self.connect_layout.addWidget(self.connect_button)
self.gridLayout_4.addLayout(self.connect_layout, 4, 0, 1, 1)
self.server_address_layout = QtWidgets.QHBoxLayout()
self.server_address_layout.setObjectName("server_address_layout")
self.server_address_label = QtWidgets.QLabel(self.connection_groupbox)
self.server_address_label.setObjectName("server_address_label")
self.server_address_layout.addWidget(self.server_address_label)
self.server_address_input = QtWidgets.QLineEdit(self.connection_groupbox)
self.server_address_input.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
self.server_address_input.setObjectName("server_address_input")
self.server_address_layout.addWidget(self.server_address_input)
self.port_label = QtWidgets.QLabel(self.connection_groupbox)
self.port_label.setObjectName("port_label")
self.server_address_layout.addWidget(self.port_label)
self.port_input = QtWidgets.QLineEdit(self.connection_groupbox)
self.port_input.setInputMethodHints(QtCore.Qt.ImhDigitsOnly)
self.port_input.setObjectName("port_input")
self.server_address_layout.addWidget(self.port_input)
self.gridLayout_4.addLayout(self.server_address_layout, 0, 0, 1, 1)
self.nickname_layout = QtWidgets.QHBoxLayout()
self.nickname_layout.setObjectName("nickname_layout")
self.nickname_label = QtWidgets.QLabel(self.connection_groupbox)
self.nickname_label.setObjectName("nickname_label")
self.nickname_layout.addWidget(self.nickname_label)
self.nickname_input = QtWidgets.QLineEdit(self.connection_groupbox)
self.nickname_input.setObjectName("nickname_input")
self.nickname_layout.addWidget(self.nickname_input)
self.password_label = QtWidgets.QLabel(self.connection_groupbox)
self.password_label.setObjectName("password_label")
self.nickname_layout.addWidget(self.password_label)
self.password_input = QtWidgets.QLineEdit(self.connection_groupbox)
self.password_input.setEchoMode(QtWidgets.QLineEdit.PasswordEchoOnEdit)
self.password_input.setObjectName("password_input")
self.nickname_layout.addWidget(self.password_input)
self.remember_checkbox = QtWidgets.QCheckBox(self.connection_groupbox)
self.remember_checkbox.setObjectName("remember_checkbox")
self.nickname_layout.addWidget(self.remember_checkbox)
self.gridLayout_4.addLayout(self.nickname_layout, 1, 0, 1, 1)
self.gridLayout.addWidget(self.connection_groupbox, 2, 0, 3, 1)
spacerItem = QtWidgets.QSpacerItem(20, 5, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
self.gridLayout.addItem(spacerItem, 1, 0, 1, 1)
self.status_layout = QtWidgets.QHBoxLayout()
self.status_layout.setObjectName("status_layout")
self.gridLayout.addLayout(self.status_layout, 5, 0, 1, 1)
self.retranslateUi(ConnectionDialog)
self.server_connections_tab.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(ConnectionDialog)
def retranslateUi(self, ConnectionDialog):
_translate = QtCore.QCoreApplication.translate
ConnectionDialog.setWindowTitle(_translate("ConnectionDialog", "Connect to Server"))
self.server_connections_tab.setStatusTip(_translate("ConnectionDialog", "Connect to your favorite or most recent servers"))
self.favorites_tab.setStatusTip(_translate("ConnectionDialog", "Servers that you have set as favorites in the Recent tab."))
self.favorite_connections_list.setStatusTip(_translate("ConnectionDialog", "Servers that you have set as favorites in the Recent tab."))
self.server_connections_tab.setTabText(self.server_connections_tab.indexOf(self.favorites_tab), _translate("ConnectionDialog", "Favorites"))
self.recent_tab.setStatusTip(_translate("ConnectionDialog", "Servers that you have connected to most recently."))
self.recent_connections_list.setStatusTip(_translate("ConnectionDialog", "Servers that you have connected to most recently."))
self.server_connections_tab.setTabText(self.server_connections_tab.indexOf(self.recent_tab), _translate("ConnectionDialog", "Recent"))
self.connection_groupbox.setStatusTip(_translate("ConnectionDialog", "Edit your connection settings."))
self.connection_groupbox.setTitle(_translate("ConnectionDialog", "Connection Settings"))
self.test_connection_button.setStatusTip(_translate("ConnectionDialog", "Test your ability to connect to the server"))
self.test_connection_button.setText(_translate("ConnectionDialog", "Test Connection"))
self.connect_button.setStatusTip(_translate("ConnectionDialog", "Connect to the TCPChat server immediately"))
self.connect_button.setText(_translate("ConnectionDialog", "Connect"))
self.server_address_label.setText(_translate("ConnectionDialog", "Server Address"))
self.server_address_input.setStatusTip(_translate("ConnectionDialog", "A IPv4 address to the server host, local or not."))
self.server_address_input.setPlaceholderText(_translate("ConnectionDialog", "127.0.0.1"))
self.port_label.setText(_translate("ConnectionDialog", "Port"))
self.port_input.setStatusTip(_translate("ConnectionDialog", "The port number the server is running on."))
self.port_input.setPlaceholderText(_translate("ConnectionDialog", "5555"))
self.nickname_label.setText(_translate("ConnectionDialog", "Nickname"))
self.nickname_input.setStatusTip(_translate("ConnectionDialog", "Your human identifier. Without a password, uniqueness is not ensured."))
self.nickname_input.setPlaceholderText(_translate("ConnectionDialog", "Type your nickname here..."))
self.password_label.setText(_translate("ConnectionDialog", "Password"))
self.password_input.setStatusTip(_translate("ConnectionDialog", "A optional password for preserving your nickname with."))
self.password_input.setPlaceholderText(_translate("ConnectionDialog", "Optional"))
self.remember_checkbox.setStatusTip(_translate("ConnectionDialog", "Remember this password in plaintext. Kept in Recent and/or Favorites."))
self.remember_checkbox.setText(_translate("ConnectionDialog", "Remember"))

View File

@@ -1,6 +1,14 @@
from PyQt5.QtWidgets import QDialog import re
from typing import Tuple
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QDialog, QStatusBar, QWidget
import constants
from client.ConnectionDialog import Ui_ConnectionDialog
from client.nickname import Ui_NicknameDialog from client.nickname import Ui_NicknameDialog
from constants import ConnectionOptions
class NicknameDialog(QDialog, Ui_NicknameDialog): class NicknameDialog(QDialog, Ui_NicknameDialog):
@@ -33,3 +41,50 @@ class NicknameDialog(QDialog, Ui_NicknameDialog):
"""Tries to submit the Dialog through the QLineEdit via Enter key""" """Tries to submit the Dialog through the QLineEdit via Enter key"""
if not self.disabled: if not self.disabled:
self.accept() self.accept()
class ConnectionDialog(QDialog, Ui_ConnectionDialog):
def __init__(self, *args, **kwargs):
super(ConnectionDialog, self).__init__(*args, **kwargs)
self.setupUi(self)
self.connect_button.setDisabled(True)
self.server_address_input.textEdited.connect(self.validation)
self.port_input.textEdited.connect(self.validation)
self.nickname_input.textEdited.connect(self.validation)
self.connect_button.pressed.connect(self.connect)
self.status_bar = QStatusBar(self)
self.status_layout.addWidget(self.status_bar)
self.connect_pressed = False
self.show()
def validation(self, full: bool = True) -> None:
address, port = self.validate_address()
if not address and not port:
self.status_bar.showMessage('Please fill in a valid server address and port.', 3000)
elif not address:
self.status_bar.showMessage('Please fill in a valid server address.', 3000)
elif not port:
self.status_bar.showMessage('Please fill in a valid port number.', 3000)
elif full and not self.validate_nickname():
self.status_bar.showMessage('Please use a valid nickname. Letters and digits, 3-15 characters long.', 3000)
self.connect_button.setDisabled(not (self.validate_address() and self.validate_nickname()))
def validate_nickname(self) -> bool:
"""Returns True if the nickname follows the nickname guidelines requested."""
return re.match(r'^[A-z0-9]{3,15}$', self.nickname_input.text()) is not None
def validate_address(self) -> Tuple[bool, bool]:
"""Returns True if the server address and port combination is valid"""
address = self.server_address_input.text() or constants.DEFAULT_IP
port = self.port_input.text() or constants.DEFAULT_PORT
valid_address = len(address) > 0 and re.match(r'^\d{1,4}\.\d{1,4}\.\d{1,4}\.\d{1,4}|localhost$', address) is not None
valid_port = len(port) > 0 and re.match(r'^\d{4,5}$', port) is not None and 1024 <= int(port) <= 65536
return valid_address, valid_port

View File

@@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConnectionDialog</class>
<widget class="QDialog" name="ConnectionDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>665</width>
<height>450</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>660</width>
<height>450</height>
</size>
</property>
<property name="windowTitle">
<string>Connect to Server</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="server_connections_tab">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
<property name="statusTip">
<string>Connect to your favorite or most recent servers</string>
</property>
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="favorites_tab">
<property name="statusTip">
<string>Servers that you have set as favorites in the Recent tab.</string>
</property>
<attribute name="title">
<string>Favorites</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QListWidget" name="favorite_connections_list">
<property name="toolTip">
<string/>
</property>
<property name="statusTip">
<string>Servers that you have set as favorites in the Recent tab.</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="recent_tab">
<property name="statusTip">
<string>Servers that you have connected to most recently.</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<attribute name="title">
<string>Recent</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>7</number>
</property>
<item row="0" column="0">
<widget class="QListView" name="recent_connections_list">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="statusTip">
<string>Servers that you have connected to most recently.</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0" rowspan="3">
<widget class="QGroupBox" name="connection_groupbox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="statusTip">
<string>Edit your connection settings.</string>
</property>
<property name="title">
<string>Connection Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="4" column="0">
<layout class="QHBoxLayout" name="connect_layout">
<item>
<widget class="QPushButton" name="test_connection_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>35</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="statusTip">
<string>Test your ability to connect to the server</string>
</property>
<property name="text">
<string>Test Connection</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="connect_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>100</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="statusTip">
<string>Connect to the TCPChat server immediately</string>
</property>
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="server_address_layout">
<item>
<widget class="QLabel" name="server_address_label">
<property name="text">
<string>Server Address</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="server_address_input">
<property name="statusTip">
<string>A IPv4 address to the server host, local or not.</string>
</property>
<property name="inputMethodHints">
<set>Qt::ImhPreferNumbers</set>
</property>
<property name="placeholderText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="port_label">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="port_input">
<property name="statusTip">
<string>The port number the server is running on.</string>
</property>
<property name="inputMethodHints">
<set>Qt::ImhDigitsOnly</set>
</property>
<property name="placeholderText">
<string>5555</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="nickname_layout">
<item>
<widget class="QLabel" name="nickname_label">
<property name="text">
<string>Nickname</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="nickname_input">
<property name="statusTip">
<string>Your human identifier. Without a password, uniqueness is not ensured.</string>
</property>
<property name="placeholderText">
<string>Type your nickname here...</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="password_label">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="password_input">
<property name="statusTip">
<string>A optional password for preserving your nickname with.</string>
</property>
<property name="echoMode">
<enum>QLineEdit::PasswordEchoOnEdit</enum>
</property>
<property name="placeholderText">
<string>Optional</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="remember_checkbox">
<property name="statusTip">
<string>Remember this password in plaintext. Kept in Recent and/or Favorites.</string>
</property>
<property name="text">
<string>Remember</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="status_layout"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>