1- # main.py
21import sys
32import os
4- from PySide6 .QtWidgets import QApplication , QMainWindow , QLabel , QPushButton , QGridLayout , QVBoxLayout , QWidget , QHBoxLayout , QMenu , QSpacerItem
3+ import subprocess
4+ from PySide6 .QtWidgets import QApplication , QMainWindow , QLabel , QPushButton , QGridLayout , QVBoxLayout , QWidget , QHBoxLayout , QMenu , QGraphicsOpacityEffect
55from PySide6 .QtGui import QPixmap , QFont
66from PySide6 .QtCore import Qt , QPropertyAnimation , QRect , QUrl , QTimer , QEasingCurve
77from PySide6 .QtNetwork import QNetworkAccessManager , QNetworkRequest , QNetworkReply
@@ -15,17 +15,30 @@ def __init__(self):
1515 self .icons = {}
1616
1717 def load_icon (self , url , callback ):
18+ if not url :
19+ logging .error ("Icon URL is empty" )
20+ callback (QPixmap ()) # Return empty pixmap as fallback
21+ return
1822 request = QNetworkRequest (QUrl (url ))
1923 reply = self .manager .get (request )
20- reply .finished .connect (lambda : self .icon_loaded (reply , callback ))
24+ if reply :
25+ reply .finished .connect (lambda : self .icon_loaded (reply , callback ))
26+ else :
27+ logging .error (f"Failed to create network reply for URL: { url } " )
28+ callback (QPixmap ()) # Return empty pixmap as fallback
2129
2230 def icon_loaded (self , reply , callback ):
2331 if reply .error () == QNetworkReply .NoError :
2432 pixmap = QPixmap ()
2533 pixmap .loadFromData (reply .readAll ())
26- callback (pixmap .scaled (140 , 140 , Qt .KeepAspectRatio , Qt .SmoothTransformation ))
34+ if not pixmap .isNull ():
35+ callback (pixmap .scaled (140 , 140 , Qt .KeepAspectRatio , Qt .SmoothTransformation ))
36+ else :
37+ logging .error (f"Failed to load pixmap from URL: { reply .url ().toString ()} " )
38+ callback (QPixmap ()) # Fallback empty pixmap
2739 else :
2840 logging .error (f"Failed to load icon: { reply .errorString ()} " )
41+ callback (QPixmap ()) # Fallback empty pixmap
2942 reply .deleteLater ()
3043
3144icon_loader = IconLoader ()
@@ -39,49 +52,61 @@ def __init__(self):
3952 self .setCentralWidget (self .central_widget )
4053 self .layout = QVBoxLayout (self .central_widget )
4154 self .layout .setAlignment (Qt .AlignCenter )
42- self .layout .setSpacing (60 ) # Increased spacing for better layout
43- self .layout .setContentsMargins (80 , 80 , 80 , 80 ) # Larger margins
55+ self .layout .setSpacing (60 )
56+ self .layout .setContentsMargins (80 , 80 , 80 , 80 )
4457
4558 self .top_layout = QHBoxLayout ()
4659 self .hackeros_logo = QLabel ()
4760 self .hackeros_logo .setObjectName ('logo' )
4861 pixmap_hackeros = QPixmap ('/usr/share/HackerOS/ICONS/HackerOS.png' )
49- self .hackeros_logo .setPixmap (pixmap_hackeros .scaled (120 , 120 , Qt .KeepAspectRatio , Qt .SmoothTransformation )) # Larger logo
62+ if pixmap_hackeros .isNull ():
63+ logging .error ("Failed to load HackerOS.png" )
64+ pixmap_hackeros = QPixmap (120 , 120 ) # Fallback empty pixmap
65+ pixmap_hackeros .fill (Qt .transparent )
66+ self .hackeros_logo .setPixmap (pixmap_hackeros .scaled (120 , 120 , Qt .KeepAspectRatio , Qt .SmoothTransformation ))
67+ self .hackeros_logo_opacity = QGraphicsOpacityEffect ()
68+ self .hackeros_logo .setGraphicsEffect (self .hackeros_logo_opacity )
5069 self .top_layout .addWidget (self .hackeros_logo , alignment = Qt .AlignLeft )
5170 self .top_layout .addStretch ()
5271 self .hackermode_logo = QLabel ()
5372 self .hackermode_logo .setObjectName ('logo' )
5473 pixmap_hackermode = QPixmap ('/usr/share/HackerOS/ICONS/Hacker-Mode.png' )
55- self .hackermode_logo .setPixmap (pixmap_hackermode .scaled (120 , 120 , Qt .KeepAspectRatio , Qt .SmoothTransformation )) # Larger logo
74+ if pixmap_hackermode .isNull ():
75+ logging .error ("Failed to load Hacker-Mode.png" )
76+ pixmap_hackermode = QPixmap (120 , 120 ) # Fallback empty pixmap
77+ pixmap_hackermode .fill (Qt .transparent )
78+ self .hackermode_logo .setPixmap (pixmap_hackermode .scaled (120 , 120 , Qt .KeepAspectRatio , Qt .SmoothTransformation ))
79+ self .hackermode_logo_opacity = QGraphicsOpacityEffect ()
80+ self .hackermode_logo .setGraphicsEffect (self .hackermode_logo_opacity )
5681 self .top_layout .addWidget (self .hackermode_logo , alignment = Qt .AlignRight )
5782 self .layout .addLayout (self .top_layout )
5883
5984 self .title = QLabel (get_text ('title' ))
60- self .title .setFont (QFont ('Hack' , 70 , QFont .Bold )) # Larger font
85+ self .title .setFont (QFont ('Hack' , 70 , QFont .Bold ))
6186 self .title .setObjectName ('neon-text' )
6287 self .layout .addWidget (self .title , alignment = Qt .AlignCenter )
6388
6489 self .grid_layout = QGridLayout ()
65- self .grid_layout .setSpacing (80 ) # Increased spacing
66- self .grid_layout .setContentsMargins (60 , 60 , 60 , 60 ) # Larger margins
90+ self .grid_layout .setSpacing (80 )
91+ self .grid_layout .setContentsMargins (60 , 60 , 60 , 60 )
6792 self .layout .addLayout (self .grid_layout )
6893
6994 self .launcher_buttons = []
7095 self .add_launcher_button ('steam' , 'https://store.steampowered.com/favicon.ico' , 0 , 0 )
7196 self .add_launcher_button ('heroic' , 'https://www.heroicgameslauncher.com/favicon.ico' , 0 , 1 )
72- self .add_launcher_button ('hyperplay' , 'https://cdn-icons-png.flaticon .com/512/5968/5968778.png ' , 0 , 2 )
97+ self .add_launcher_button ('hyperplay' , 'https://www.hyperplaygaming .com/favicon.ico ' , 0 , 2 )
7398 self .add_launcher_button ('lutris' , 'https://lutris.net/static/images/logo.png' , 0 , 3 )
7499
75100 self .bottom_layout = QHBoxLayout ()
76101 self .hacker_menu_btn = QPushButton (get_text ('hacker_menu' ))
77102 self .hacker_menu_btn .setObjectName ('action-btn' )
78- self .hacker_menu_btn .setFixedSize (250 , 80 ) # Larger button
103+ self .hacker_menu_btn .setFixedSize (250 , 80 )
79104 self .hacker_menu_btn .clicked .connect (self .show_hacker_menu )
80105 self .bottom_layout .addWidget (self .hacker_menu_btn , alignment = Qt .AlignLeft )
81106 self .bottom_layout .addStretch ()
82107 self .settings_btn = QPushButton (get_text ('settings' ))
83108 self .settings_btn .setObjectName ('action-btn' )
84- self .settings_btn .setFixedSize (250 , 80 ) # Larger button
109+ self .settings_btn .setFixedSize (250 , 80 )
85110 self .settings_btn .clicked .connect (self .launch_settings )
86111 self .bottom_layout .addWidget (self .settings_btn , alignment = Qt .AlignRight )
87112 self .layout .addLayout (self .bottom_layout )
@@ -93,20 +118,20 @@ def animate_elements(self):
93118 for i , btn in enumerate (self .launcher_buttons ):
94119 anim = QPropertyAnimation (btn , b"geometry" )
95120 rect = btn .geometry ()
96- anim .setStartValue (QRect (rect .x (), rect .y () + 300 , rect .width (), rect .height ())) # More dramatic animation
121+ anim .setStartValue (QRect (rect .x (), rect .y () + 300 , rect .width (), rect .height ()))
97122 anim .setEndValue (rect )
98- anim .setDuration (2000 ) # Slower animation
123+ anim .setDuration (2000 )
99124 anim .setEasingCurve (QEasingCurve .OutQuint )
100- QTimer .singleShot (i * 400 , lambda : anim .start ()) # More delay between animations
125+ QTimer .singleShot (i * 400 , lambda : anim .start ())
101126
102- logo_anim1 = QPropertyAnimation (self .hackeros_logo , b"opacity" )
127+ logo_anim1 = QPropertyAnimation (self .hackeros_logo_opacity , b"opacity" )
103128 logo_anim1 .setStartValue (0.0 )
104129 logo_anim1 .setEndValue (1.0 )
105130 logo_anim1 .setDuration (2000 )
106131 logo_anim1 .setEasingCurve (QEasingCurve .OutQuint )
107132 QTimer .singleShot (400 , lambda : logo_anim1 .start ())
108133
109- logo_anim2 = QPropertyAnimation (self .hackermode_logo , b"opacity" )
134+ logo_anim2 = QPropertyAnimation (self .hackermode_logo_opacity , b"opacity" )
110135 logo_anim2 .setStartValue (0.0 )
111136 logo_anim2 .setEndValue (1.0 )
112137 logo_anim2 .setDuration (2000 )
@@ -115,14 +140,14 @@ def animate_elements(self):
115140
116141 def add_launcher_button (self , app_name , icon_url , row , col ):
117142 btn = QPushButton (objectName = 'launcher-btn' )
118- btn .setFixedSize (280 , 280 ) # Larger buttons
143+ btn .setFixedSize (280 , 280 )
119144 layout = QVBoxLayout (btn )
120- layout .setContentsMargins (30 , 30 , 30 , 30 ) # Larger margins
145+ layout .setContentsMargins (30 , 30 , 30 , 30 )
121146 icon_label = QLabel ()
122147 icon_label .setAlignment (Qt .AlignCenter )
123148 layout .addWidget (icon_label )
124149 text_label = QLabel (get_text (app_name ))
125- text_label .setFont (QFont ('Hack' , 24 )) # Larger font
150+ text_label .setFont (QFont ('Hack' , 24 ))
126151 text_label .setAlignment (Qt .AlignCenter )
127152 layout .addWidget (text_label )
128153 icon_loader .load_icon (icon_url , icon_label .setPixmap )
@@ -165,76 +190,51 @@ def show_hacker_menu(self):
165190 app = QApplication (sys .argv )
166191 stylesheet = """
167192 QMainWindow, QWidget {
168- background: qradialgradient(cx:0.5, cy:0.5, radius:1.5, stop:0 #1a1a1a, stop:1 #000000); /* Softer gradient */
193+ background: qradialgradient(cx:0.5, cy:0.5, radius:1.5, stop:0 #1a1a1a, stop:1 #000000);
169194 color: white;
170195 font-family: 'Hack', 'Courier New', monospace;
171196 }
172197 #neon-text {
173198 color: #ffffff;
174- text-shadow: 0 0 15px #ffffff, 0 0 30px #ffffff, 0 0 50px #ffffff, 0 0 70px #ffffff; /* Enhanced neon */
175- animation: neon 1.0s ease-in-out infinite alternate;
176- }
177- @keyframes neon {
178- from {
179- text-shadow: 0 0 8px #ffffff, 0 0 15px #ffffff, 0 0 25px #ffffff;
180- }
181- to {
182- text-shadow: 0 0 20px #ffffff, 0 0 40px #ffffff, 0 0 60px #ffffff, 0 0 80px #ffffff;
183- }
199+ font-weight: bold;
184200 }
185201 #logo {
186- border: 3px solid #ffffff; /* Thicker border */
202+ border: 3px solid #ffffff;
187203 border-radius: 15px;
188- box-shadow: 0 0 25px rgba(255, 255, 255, 0.6);
189- transition: all 0.4s ease;
190- }
191- #logo:hover {
192- box-shadow: 0 0 40px rgba(255, 255, 255, 0.8);
193- transform: scale(1.1);
204+ background: rgba(255, 255, 255, 0.1);
194205 }
195206 QPushButton#launcher-btn {
196207 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #151515);
197- border-radius: 40px; /* More rounded */
208+ border-radius: 40px;
198209 padding: 35px;
199- border: 3px solid #ffffff; /* Thicker border */
200- box-shadow: 0 0 25px rgba(255, 255, 255, 0.6);
201- transition: all 0.4s ease;
210+ border: 3px solid #ffffff;
211+ background: rgba(255, 255, 255, 0.1);
202212 }
203213 QPushButton#launcher-btn:hover {
204214 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3d3d3d, stop:1 #252525);
205- transform: scale(1.15);
206- box-shadow: 0 0 50px rgba(255, 255, 255, 0.8);
207215 }
208216 QWidget#setting-panel {
209217 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #151515);
210- border-radius: 40px; /* More rounded */
218+ border-radius: 40px;
211219 padding: 35px;
212- border: 3px solid #ffffff; /* Thicker border */
213- box-shadow: 0 0 25px rgba(255, 255, 255, 0.6);
214- transition: all 0.4s ease;
215- }
216- QWidget#setting-panel:hover {
217- transform: translateY(-15px);
218- box-shadow: 0 0 50px rgba(255, 255, 255, 0.8);
220+ border: 3px solid #ffffff;
221+ background: rgba(255, 255, 255, 0.1);
219222 }
220223 QPushButton#action-btn, QPushButton#setting-btn, QPushButton#list-btn {
221224 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #151515);
222- border-radius: 20px; /* More rounded */
225+ border-radius: 20px;
223226 padding: 18px;
224227 color: white;
225- font-size: 24px; /* Larger font */
226- border: 3px solid #ffffff; /* Thicker border */
227- box-shadow: 0 0 25px rgba(255, 255, 255, 0.7);
228- transition: all 0.4s ease;
228+ font-size: 24px;
229+ border: 3px solid #ffffff;
230+ background: rgba(255, 255, 255, 0.1);
229231 }
230232 QPushButton#action-btn:hover, QPushButton#setting-btn:hover, QPushButton#list-btn:hover {
231233 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #3d3d3d, stop:1 #252525);
232- transform: translateY(-8px);
233- box-shadow: 0 0 40px rgba(255, 255, 255, 0.8);
234234 }
235235 QLabel {
236236 color: white;
237- font-size: 22px; /* Larger font */
237+ font-size: 22px;
238238 }
239239 QLabel#list-label {
240240 font-size: 20px;
@@ -246,29 +246,29 @@ def show_hacker_menu(self):
246246 border-radius: 15px;
247247 padding: 15px;
248248 border: 2px solid #ffffff;
249- font-size: 22px; /* Larger font */
249+ font-size: 22px;
250+ background: rgba(255, 255, 255, 0.1);
250251 }
251252 QScrollArea {
252253 background: transparent;
253254 border: none;
254255 }
255256 QCheckBox#checkbox {
256257 color: white;
257- font-size: 22px; /* Larger font */
258+ font-size: 22px;
258259 }
259260 QMenu#hacker-menu {
260261 background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #2a2a2a, stop:1 #151515);
261262 border-radius: 20px;
262- border: 3px solid #ffffff; /* Thicker border */
263+ border: 3px solid #ffffff;
263264 color: white;
264265 padding: 15px;
265- box-shadow: 0 0 30px rgba(255, 255, 255, 0.6 );
266+ background: rgba(255, 255, 255, 0.1 );
266267 }
267268 QMenu#hacker-menu::item {
268269 padding: 15px 30px;
269270 border-radius: 12px;
270- font-size: 22px; /* Larger font */
271- transition: all 0.3s ease;
271+ font-size: 22px;
272272 }
273273 QMenu#hacker-menu::item:hover {
274274 background: #3d3d3d;
@@ -287,3 +287,4 @@ def show_hacker_menu(self):
287287 except Exception as e :
288288 logging .error (f'Error setting fullscreen in Wayfire: { e } ' )
289289 sys .exit (app .exec ())
290+
0 commit comments