Updated Protocol Page / Update Flow / Connection Type

This commit is contained in:
Your Name 2025-04-25 20:07:40 +01:00
parent f0fadbdd3a
commit 9b6deffa61
3 changed files with 296 additions and 237 deletions

View file

@ -1,5 +1,5 @@
from PyQt6.QtWidgets import QComboBox,QButtonGroup, QLineEdit,QMainWindow, QLabel, QWidget, QVBoxLayout, QStackedWidget, QApplication, QPushButton, QTextEdit, QFrame, QHBoxLayout, QVBoxLayout, QScrollArea, QSystemTrayIcon, QMessageBox, QGridLayout, QCheckBox, QStackedLayout, QGroupBox from PyQt6.QtWidgets import QComboBox,QButtonGroup, QLineEdit,QMainWindow, QLabel, QWidget, QVBoxLayout, QStackedWidget, QApplication, QPushButton, QTextEdit, QFrame, QHBoxLayout, QVBoxLayout, QScrollArea, QSystemTrayIcon, QMessageBox, QGridLayout, QCheckBox, QStackedLayout, QGroupBox
from PyQt6.QtGui import QIcon, QPixmap,QIcon, QPixmap, QTransform, QPainter, QColor, QFont from PyQt6.QtGui import QIcon, QPixmap,QIcon, QPixmap, QTransform, QPainter, QColor, QFont, QPen
from PyQt6 import QtGui from PyQt6 import QtGui
from PyQt6 import QtCore from PyQt6 import QtCore
from PyQt6.QtCore import Qt,QSize,QThread,pyqtSignal, QTimer, QPointF, QRect, QMutex, QMutexLocker, QObject from PyQt6.QtCore import Qt,QSize,QThread,pyqtSignal, QTimer, QPointF, QRect, QMutex, QMutexLocker, QObject
@ -12,7 +12,7 @@ import traceback
import random import random
import subprocess import subprocess
from typing import Union from typing import Union
from core.Errors import CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError from core.Errors import UnknownConnectionTypeError, CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError
from core.controllers.ApplicationVersionController import ApplicationVersionController from core.controllers.ApplicationVersionController import ApplicationVersionController
from core.controllers.ApplicationController import ApplicationController from core.controllers.ApplicationController import ApplicationController
from core.controllers.ClientController import ClientController from core.controllers.ClientController import ClientController
@ -77,10 +77,8 @@ class WorkerThread(QThread):
self.get_subscription() self.get_subscription()
elif self.action == 'DISABLE_PROFILE': elif self.action == 'DISABLE_PROFILE':
self.disable_profile() self.disable_profile()
elif self.action == 'SYNC': elif self.action == 'SYNC' or self.action == 'SYNC_TOR':
self.sync() self.sync()
elif self.action == 'SYNC_TOR':
self.tor_sync()
elif self.action == 'GET_CONNECTION': elif self.action == 'GET_CONNECTION':
self.get_connection() self.get_connection()
elif self.action == 'SET_CONNECTION': elif self.action == 'SET_CONNECTION':
@ -116,7 +114,7 @@ class WorkerThread(QThread):
def download_update(self): def download_update(self):
self.text_output.emit("Starting update process...") self.text_output.emit("Starting update process...")
ClientController.__update(client_observer=client_observer) ClientController.update(client_observer=client_observer)
client_observer.subscribe('update_progressing', lambda event: self.text_output.emit(f"Downloading: {event.meta.get('progress'):.1f}%")) client_observer.subscribe('update_progressing', lambda event: self.text_output.emit(f"Downloading: {event.meta.get('progress'):.1f}%"))
client_observer.subscribe('updated', lambda event: self.text_output.emit("Update process completed")) client_observer.subscribe('updated', lambda event: self.text_output.emit("Update process completed"))
@ -230,7 +228,10 @@ class WorkerThread(QThread):
def sync(self): def sync(self):
try: try:
ConfigurationController.set_connection('system') if self.action == 'SYNC_TOR':
ConfigurationController.set_connection('tor')
else:
ConfigurationController.set_connection('system')
ClientController.sync(client_observer=client_observer, connection_observer=connection_observer) ClientController.sync(client_observer=client_observer, connection_observer=connection_observer)
locations = LocationController.get_all() locations = LocationController.get_all()
print(locations) print(locations)
@ -240,18 +241,6 @@ class WorkerThread(QThread):
print(e) print(e)
self.sync_output.emit([], False, False, [], False) self.sync_output.emit([], False, False, [], False)
def tor_sync(self):
try:
ConfigurationController.set_connection('tor')
self.text_output.emit('Syncing in progress...')
ClientController.sync(client_observer=client_observer, connection_observer=connection_observer)
locations = LocationController.get_all()
all_location_codes = [location.country_name for location in locations]
self.sync_output.emit(all_location_codes, True, True, locations, False)
except ConnectionError:
self.sync_output.emit([], False, False, [], False)
except CommandNotFoundError:
self.sync_output.emit([], False, False, [], True)
def get_connection(self): def get_connection(self):
@ -336,8 +325,11 @@ class CustomWindow(QMainWindow):
self.is_downloading = False self.is_downloading = False
self.current_profile_id = None self.current_profile_id = None
self.connection_manager = ConnectionManager() self.connection_manager = ConnectionManager()
current_connection = ConfigurationController.get_connection() current_connection = self.get_current_connection()
if current_connection is None:
ConfigurationController.set_connection('system')
current_connection = 'system'
self.is_tor_mode = current_connection == 'tor' self.is_tor_mode = current_connection == 'tor'
self.is_animating = False self.is_animating = False
self.animation_step = 0 self.animation_step = 0
@ -416,6 +408,13 @@ class CustomWindow(QMainWindow):
self.check_logging() self.check_logging()
self.init_ui() self.init_ui()
def get_current_connection(self):
try:
return ConfigurationController.get_connection()
except UnknownConnectionTypeError:
return None
def _setup_gui_directory(self): def _setup_gui_directory(self):
os.makedirs(self.gui_dir, exist_ok=True) os.makedirs(self.gui_dir, exist_ok=True)
@ -739,26 +738,24 @@ class CustomWindow(QMainWindow):
self.menu_page = MenuPage(self.page_stack, self) self.menu_page = MenuPage(self.page_stack, self)
self.editor_page = EditorPage(self.page_stack, self) self.editor_page = EditorPage(self.page_stack, self)
self.pages = [MenuPage(self.page_stack, self), self.pages = [MenuPage(self.page_stack, self),
ProtocolPage(self.page_stack, self), ProtocolPage(self.page_stack, self),
WireGuardPage(self.page_stack, self), LocationPage(self.page_stack, self),
ResidentialPage(self.page_stack, self), BrowserPage(self.page_stack, self),
HidetorPage(self.page_stack, self), ScreenPage(self.page_stack, self),
lokinetPage(self.page_stack, self), ResumePage(self.page_stack, self),
OpenPage(self.page_stack, self), WireGuardPage(self.page_stack, self),
TorPage(self.page_stack, self), ResidentialPage(self.page_stack, self),
TorLocationPage(self.page_stack, self), HidetorPage(self.page_stack, self),
PaymentPage(self.page_stack, self), InstallSystemPackage(self.page_stack, self),
LocationPage(self.page_stack, self), WelcomePage(self.page_stack, self),
BrowserPage(self.page_stack, self), PaymentPage(self.page_stack, self),
ScreenPage(self.page_stack, self), PaymentConfirmed(self.page_stack, self),
ResumePage(self.page_stack, self), IdPage(self.page_stack, self),
IdPage(self.page_stack, self), TorPage(self.page_stack, self),
InstallSystemPackage(self.page_stack, self), EditorPage(self.page_stack, self),
WelcomePage(self.page_stack, self), Settings(self.page_stack, self),
PaymentConfirmed(self.page_stack, self), ConnectionPage(self.page_stack, self),
EditorPage(self.page_stack, self), SyncScreen(self.page_stack, self)]
Settings(self.page_stack, self),
ConnectionPage(self.page_stack, self)]
for page in self.pages: for page in self.pages:
self.page_stack.addWidget(page) self.page_stack.addWidget(page)
# Conectar la señal currentChanged al método page_changed # Conectar la señal currentChanged al método page_changed
@ -801,7 +798,7 @@ class CustomWindow(QMainWindow):
self.disable_marquee() self.disable_marquee()
def check_first_launch(self): def check_first_launch(self):
config_file = os.path.join(Constants.HV_CONFIG_HOME, '.first_launch') config_file = os.path.join(self.gui_dir, "first_launch.txt")
os.makedirs(Constants.HV_CONFIG_HOME, exist_ok=True) os.makedirs(Constants.HV_CONFIG_HOME, exist_ok=True)
@ -875,9 +872,7 @@ class CustomWindow(QMainWindow):
elif isinstance(current_page, ResumePage): elif isinstance(current_page, ResumePage):
self.update_status('Choose a name for your profile') self.update_status('Choose a name for your profile')
current_page.eliminacion() current_page.eliminacion()
elif isinstance(current_page, ProtocolPage):
if not self.connection_manager.is_synced():
self.update_status('Choose a sync method')
elif isinstance(current_page, IdPage): elif isinstance(current_page, IdPage):
pass pass
elif isinstance(current_page, LocationPage): elif isinstance(current_page, LocationPage):
@ -1026,8 +1021,6 @@ class Worker(QObject):
self.update_signal.emit("Application version file integrity could not be verified.", False, None, None, None) self.update_signal.emit("Application version file integrity could not be verified.", False, None, None, None)
except ProfileModificationError: except ProfileModificationError:
self.update_signal.emit("WireGuard configuration could not be attached.", False, None, None, None) self.update_signal.emit("WireGuard configuration could not be attached.", False, None, None, None)
#except OSError:
# self.update_signal.emit("Connection could not be established.", False, None, None, None)
except ProfileStateConflictError: except ProfileStateConflictError:
self.update_signal.emit("The profile is already being enabled...", False, None, None, None) self.update_signal.emit("The profile is already being enabled...", False, None, None, None)
except CommandNotFoundError as e : except CommandNotFoundError as e :
@ -1111,79 +1104,23 @@ class MenuPage(Page):
self.is_system_connected = False self.is_system_connected = False
self.profile_button_map = {} self.profile_button_map = {}
self.update_button = QPushButton(self)
self.update_button.setGeometry(750, 530, 25, 25)
self.update_button.setObjectName("update_button")
update_icon = QtGui.QIcon(os.path.join(self.btn_path, "available-updates.png"))
self.update_button.setIcon(update_icon)
self.update_button.setIconSize(self.update_button.size())
self.update_button.clicked.connect(self.check_updates)
#self.label.setStyleSheet("background-color: rgba(0, 255, 0, 51);") #self.label.setStyleSheet("background-color: rgba(0, 255, 0, 51);")
self.create_interface_elements() # Establecer el color de fondo y el color del texto utilizando hojas de estilo self.create_interface_elements() # Establecer el color de fondo y el color del texto utilizando hojas de estilo
def on_update_check_finished(self):
def check_updates(self):
self.update_status.update_status("Checking for updates...")
self.worker = WorkerThread('CHECK_FOR_UPDATE')
self.worker.text_output.connect(self.update_status.update_status)
self.worker.finished.connect(self.on_update_check_finished)
self.worker.start()
def on_update_check_finished(self, result):
if result:
reply = QMessageBox.question(self, 'Update Available', reply = QMessageBox.question(self, 'Update Available',
'An update is available. Would you like to download it?', 'An update is available. Would you like to download it?',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes: if reply == QMessageBox.StandardButton.Yes:
self.worker = WorkerThread('DOWNLOAD_UPDATE') self.worker = WorkerThread('DOWNLOAD_UPDATE')
self.worker.text_output.connect(self.update_status.update_status)
self.worker.update_finished.connect(self.on_update_download_finished)
self.worker.start() self.worker.start()
else: else:
self.update_status.update_status("Update cancelled by user.") self.update_status.update_status("Update cancelled by user.")
else:
self.update_status.update_status("No updates available.")
def on_update_download_finished(self, result: dict):
if result["status"] and result["path"]:
self.countdown_timer = QTimer()
self.countdown_timer.setInterval(1000)
self.countdown_seconds = 3
self.new_app_path = result["path"]
def update_countdown():
if self.countdown_seconds > 0:
self.update_status.update_status(f"New version downloaded. Restarting in {self.countdown_seconds} second{'s' if self.countdown_seconds > 1 else ''}...")
self.countdown_seconds -= 1
else:
self.countdown_timer.stop()
self.restart_application()
def restart_application():
QApplication.quit()
current_app = Constants.APPIMAGE_PATH
if os.path.exists(self.new_app_path):
try:
os.remove(current_app)
os.execv(self.new_app_path, [self.new_app_path] + sys.argv[1:])
except PermissionError:
self.update_status.update_status(f"Permission denied: Unable to remove {current_app}")
else:
self.update_status.update_status(f"Error: New application not found at {self.new_app_path}")
self.restart_application = restart_application
self.countdown_timer.timeout.connect(update_countdown)
self.countdown_timer.start()
atexit.register(restart_application)
else:
self.update_status.update_status("Failed to download update.")
def create_interface_elements(self): def create_interface_elements(self):
for icon_name, function, position in [ for icon_name, function, position in [
@ -1579,11 +1516,6 @@ class MenuPage(Page):
editar_profile = {"profile_number": profile_number, "name": name, "protocol": protocol} editar_profile = {"profile_number": profile_number, "name": name, "protocol": protocol}
self.page_stack.currentWidget().add_selected_profile(editar_profile) self.page_stack.currentWidget().add_selected_profile(editar_profile)
def launch(self): def launch(self):
pass pass
@ -1648,7 +1580,10 @@ class MenuPage(Page):
if len(self.profile_info) == 6: if len(self.profile_info) == 6:
self.update_status.update_status('Maximum number of profiles reached!') self.update_status.update_status('Maximum number of profiles reached!')
return return
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage))) if not self.connection_manager.is_synced():
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(SyncScreen)))
else:
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage)))
@ -2517,138 +2452,12 @@ class ProtocolPage(Page):
self.coming_soon_label.setGeometry(210, 50, 200, 40) self.coming_soon_label.setGeometry(210, 50, 200, 40)
self.coming_soon_label.setStyleSheet("font-size: 22px;") self.coming_soon_label.setStyleSheet("font-size: 22px;")
self.sync_label = QLabel("Sync finds out what plans, countries, \n& browsers are available", self)
self.sync_label.setGeometry(100, 120,500,60)
self.sync_label.setStyleSheet("font-size: 20px;")
self.button_sync = QPushButton(self)
self.button_sync.setObjectName("sync_button")
self.button_sync.setGeometry(100, 250,150,60)
self.button_sync.setIconSize(self.button_sync.size())
icon = QIcon(os.path.join(self.btn_path, "sync_button.png"))
self.button_sync.setIcon(icon)
self.button_sync.clicked.connect(self.handle_core_action_sync_button)
self.tor_button_sync = QPushButton(self)
self.tor_button_sync.setObjectName("tor_sync")
self.tor_button_sync.setGeometry(300, 250,150,60)
self.tor_button_sync.setIconSize(self.tor_button_sync.size())
tor_icon = QIcon(os.path.join(self.btn_path, "tor_sync.png"))
self.tor_button_sync.setIcon(tor_icon)
self.tor_button_sync.clicked.connect(lambda: self.handle_core_action_sync_button(True))
self.button_sync.setVisible(True)
self.tor_button_sync.setVisible(True)
self.coming_soon_label.setVisible(False) self.coming_soon_label.setVisible(False)
self.title.setGeometry(585, 40, 185, 40); self.title.setText("Pick a Protocol") self.title.setGeometry(585, 40, 185, 40); self.title.setText("Pick a Protocol")
self.display.setGeometry(QtCore.QRect(0, 50, 580, 435))#relacion 4:3 self.display.setGeometry(QtCore.QRect(0, 50, 580, 435))#relacion 4:3
self.create_interface_elements() self.create_interface_elements()
def sync_button(self, toggle: bool, sync_complete: bool=False):
self.button_sync.setDisabled(toggle)
self.tor_button_sync.setDisabled(toggle)
if sync_complete:
for button in self.buttons:
button.setDisabled(toggle)
self.button_sync.setVisible(False)
self.tor_button_sync.setVisible(False)
self.sync_label.setVisible(False)
def handle_core_action_sync_button(self, tor: bool = False):
self.sync_button(True)
if tor:
self.main_window.set_toggle_state(True)
self.sync_button(True)
self.update_status.update_status('Syncing through Tor in progress...')
self.worker_thread = WorkerThread('SYNC_TOR')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.text_output.connect(self.update_text_output)
self.worker_thread.start()
return
self.update_status.update_status('Syncing in progress...')
self.worker_thread = WorkerThread('SYNC')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.start()
def update_text_output(self, text):
self.update_status.update_status(text)
def update_output(self, available_locations, status, is_tor, locations, is_os_error):
if is_os_error:
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='tor', distro='debian', is_sync=True)
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
self.sync_button(False)
return
if status is False:
self.sync_button(False)
self.update_status.update_status('An error occurred during sync')
return
self.update_status.update_status('Sync complete')
self.connection_manager.set_synced(True)
available_locations_list = []
self.connection_manager.store_locations(locations)
grid_positions = [
(395, 90, 185, 75),
(585, 90, 185, 75),
(395, 195, 185, 75),
(585, 195, 185, 75),
(395, 300, 185, 75),
(585, 300, 185, 75),
(395, 405, 185, 75),
(585, 405, 185, 75)
]
list1 = []
for i, location in enumerate(available_locations):
position_index = i % len(grid_positions)
list1.append((QPushButton, location, grid_positions[position_index]))
for item in list1:
available_locations_list.append(item)
self.sync_button(False, True)
location_page = self.find_location_page()
hidetor_page = self.find_hidetor_page()
print(f' the available locations list is {available_locations_list}')
if location_page:
location_page.create_interface_elements(available_locations_list)
if hidetor_page:
hidetor_page.create_interface_elements(available_locations_list)
def find_location_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, LocationPage):
return page
return None
def find_hidetor_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, HidetorPage):
return page
return None
def create_interface_elements(self): def create_interface_elements(self):
self.buttonGroup = QButtonGroup(self) self.buttonGroup = QButtonGroup(self)
self.buttons = [] self.buttons = []
@ -2667,6 +2476,10 @@ class ProtocolPage(Page):
self.buttons.append(boton) self.buttons.append(boton)
self.buttonGroup.addButton(boton, j) self.buttonGroup.addButton(boton, j)
boton.clicked.connect(lambda _, page=page_class, protocol=icon_name: self.show_protocol(page, protocol)) boton.clicked.connect(lambda _, page=page_class, protocol=icon_name: self.show_protocol(page, protocol))
def enable_protocol_buttons(self):
for button in self.buttons:
button.setDisabled(False)
def update_swarp_json(self): def update_swarp_json(self):
self.update_status.write_data({"protocol": self.selected_protocol_icon}) self.update_status.write_data({"protocol": self.selected_protocol_icon})
@ -5724,6 +5537,252 @@ class ClickableValueLabel(QLabel):
self.setStyleSheet(self.default_style) self.setStyleSheet(self.default_style)
class SyncScreen(Page):
def __init__(self, page_stack, main_window=None, parent=None):
super().__init__("SyncScreen", page_stack, main_window, parent)
self.main_window = main_window
self.btn_path = main_window.btn_path
self.update_status = main_window
self.connection_manager = main_window.connection_manager
self.is_tor_mode = main_window.is_tor_mode
self.heading_label = QLabel("You're about to fetch data from\nSimplified Privacy.", self)
self.heading_label.setGeometry(15, 80, 750, 80)
self.heading_label.setStyleSheet("font-size: 36px; font-weight: bold; color: white;")
self.data_description = QLabel("This data includes what plans, countries,\nbrowsers, and version upgrades are\navailable. As well as coordinating billing.", self)
self.data_description.setGeometry(22, 190, 750, 90)
self.data_description.setStyleSheet("font-size: 26px; font-weight: bold; color: #ffff00;")
self.instructions = QLabel("Use the toggle in the bottom right to\ndecide if you want to use Tor or not.\nThen hit \"Next\"", self)
self.instructions.setGeometry(22, 345, 750, 100)
self.instructions.setStyleSheet("font-size: 30px; font-weight: bold; color: white;")
self.arrow_label = QLabel(self)
self.arrow_label.setGeometry(520, 400, 180, 130)
arrow_pixmap = QPixmap(os.path.join(self.btn_path, "arrow-down.png"))
self.arrow_label.setPixmap(arrow_pixmap)
self.arrow_label.raise_()
self.button_go.setVisible(True)
self.button_back.setVisible(True)
self.button_go.clicked.connect(self.perform_sync)
self.button_back.clicked.connect(self.go_back)
def go_back(self):
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(MenuPage)))
def perform_sync(self):
self.button_go.setEnabled(False)
self.button_back.setEnabled(False)
self.update_status.update_status('Syncing in progress...')
if self.main_window.get_current_connection() == 'tor':
self.worker_thread = WorkerThread('SYNC_TOR')
else:
self.worker_thread = WorkerThread('SYNC')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.start()
def update_output(self, available_locations, status, is_tor, locations, is_os_error):
if is_os_error:
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='tor', distro='debian', is_sync=True)
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
self.button_go.setEnabled(True)
self.button_back.setEnabled(True)
return
if status is False:
self.button_go.setEnabled(True)
self.button_back.setEnabled(True)
self.update_status.update_status('An error occurred during sync')
return
self.update_status.update_status('Sync complete')
self.connection_manager.set_synced(True)
update_available = ClientController.can_be_updated()
if not update_available:
menu_page = self.page_stack.findChild(MenuPage)
menu_page.on_update_check_finished()
available_locations_list = []
self.connection_manager.store_locations(locations)
grid_positions = [
(395, 90, 185, 75),
(585, 90, 185, 75),
(395, 195, 185, 75),
(585, 195, 185, 75),
(395, 300, 185, 75),
(585, 300, 185, 75),
(395, 405, 185, 75),
(585, 405, 185, 75)
]
list1 = []
for i, location in enumerate(available_locations):
position_index = i % len(grid_positions)
list1.append((QPushButton, location, grid_positions[position_index]))
for item in list1:
available_locations_list.append(item)
location_page = self.find_location_page()
hidetor_page = self.find_hidetor_page()
protocol_page = self.find_protocol_page()
if location_page:
location_page.create_interface_elements(available_locations_list)
if hidetor_page:
hidetor_page.create_interface_elements(available_locations_list)
if protocol_page:
protocol_page.enable_protocol_buttons()
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage)))
def find_location_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, LocationPage):
return page
return None
def find_hidetor_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, HidetorPage):
return page
return None
def find_protocol_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, ProtocolPage):
return page
return None
class WelcomePage(Page):
def __init__(self, page_stack, main_window=None, parent=None):
super().__init__("Welcome", page_stack, main_window, parent)
self.btn_path = main_window.btn_path
self.update_status = main_window
self.ui_elements = []
self.button_next.clicked.connect(self.go_to_install)
self.button_next.setVisible(True)
self._setup_welcome_ui()
self._setup_stats_display()
def _setup_welcome_ui(self):
welcome_title = QLabel("Welcome to HydraVeilVPN", self)
welcome_title.setGeometry(20, 45, 760, 60)
welcome_title.setStyleSheet("""
font-size: 32px;
font-weight: bold;
color: cyan;
""")
welcome_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
welcome_msg = QLabel(
"Before we begin your journey, we need to set up a few essential components. "
"Click 'Next' to take you to the installation page.", self)
welcome_msg.setGeometry(40, 100, 720, 80)
welcome_msg.setWordWrap(True)
welcome_msg.setStyleSheet("""
font-size: 16px;
color: cyan;
""")
welcome_msg.setAlignment(Qt.AlignmentFlag.AlignCenter)
def _setup_stats_display(self):
stats_container = QWidget(self)
stats_container.setGeometry(70, 200, 730, 240)
stats_title = QLabel(stats_container)
stats_title.setText("Features & Services")
stats_title.setGeometry(190, 10, 300, 30)
stats_title.setStyleSheet("""
font-size: 22px;
font-weight: bold;
color: cyan;
""")
grid_widget = QWidget(stats_container)
grid_widget.setGeometry(0, 70, 700, 190)
grid_layout = QGridLayout(grid_widget)
grid_layout.setSpacing(30)
stats = [
("Browsers", "browsers_mini.png", "10 Supported Browsers", True),
("WireGuard", "wireguard_mini.png", "6 Global Locations", True),
("Proxy", "just proxy_mini.png", "5 Proxy Locations", True),
("Tor", "toricon_mini.png", "Tor Network Access", True)
]
for i, (title, icon_name, count, available) in enumerate(stats):
row = i // 2
col = i % 2
stat_widget = QWidget()
stat_layout = QHBoxLayout(stat_widget)
stat_layout.setContentsMargins(20, 10, 2, 10)
stat_layout.setSpacing(15)
icon_label = QLabel()
icon_path = os.path.join(self.btn_path, icon_name)
icon_label.setPixmap(QPixmap(icon_path).scaled(30, 30, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
icon_label.setFixedSize(30, 30)
stat_layout.addWidget(icon_label)
text_widget = QWidget()
text_layout = QVBoxLayout(text_widget)
text_layout.setSpacing(5)
text_layout.setContentsMargins(0, 0, 30, 0)
title_widget = QWidget()
title_layout = QHBoxLayout(title_widget)
title_layout.setContentsMargins(0, 0, 0, 0)
title_layout.setSpacing(10)
title_label = QLabel(title)
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #2c3e50;")
title_layout.addWidget(title_label)
status_indicator = QLabel("")
status_indicator.setStyleSheet("color: #2ecc71; font-size: 16px;")
title_layout.addWidget(status_indicator)
title_layout.addStretch()
text_layout.addWidget(title_widget)
count_label = QLabel(count)
count_label.setStyleSheet("color: #34495e;")
text_layout.addWidget(count_label)
stat_layout.addWidget(text_widget, stretch=1)
stat_widget.setStyleSheet("""
QWidget {
background-color: transparent;
border-radius: 10px;
}
""")
grid_layout.addWidget(stat_widget, row, col)
def go_to_install(self):
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='all', distro='debian')
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = CustomWindow() window = CustomWindow()

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 32 KiB