Skip to content

Commit 60de491

Browse files
committed
fixed creashes
1 parent 721ac57 commit 60de491

7 files changed

Lines changed: 71 additions & 20 deletions

File tree

pymcl/main_window.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def init_ui(self):
147147
left_widget = QWidget()
148148
left_widget.setObjectName("left_title_container")
149149
# Ensure the widget itself has transparent bg if styled otherwise in stylesheet
150-
left_widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
150+
left_widget.setAttribute(Qt.WidgetAttribute.WA_StyledBackground)
151151

152152
left_layout = QVBoxLayout(left_widget)
153153
left_layout.setSpacing(5)

pymcl/mod_browser.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,14 @@ def on_search_text_changed(self, text):
159159
self.search_timer.start()
160160

161161
def populate_results(self):
162-
# Clear existing results
162+
# Clear existing results safely
163163
for i in reversed(range(self.results_layout.count())):
164-
self.results_layout.itemAt(i).widget().setParent(None)
164+
item = self.results_layout.itemAt(i)
165+
if item:
166+
widget = item.widget()
167+
if widget:
168+
widget.setParent(None)
169+
widget.deleteLater()
165170

166171
if not self.search_results:
167172
self.results_layout.addWidget(QLabel("No results found."), 0, 0, 1, 3)
@@ -194,7 +199,10 @@ def on_icon_downloaded(self, mod_id, icon_path):
194199
return
195200

196201
for i in range(self.results_layout.count()):
197-
widget = self.results_layout.itemAt(i).widget()
202+
item = self.results_layout.itemAt(i)
203+
if not item:
204+
continue
205+
widget = item.widget()
198206
if isinstance(widget, ModListItem) and widget.mod_data.get("project_id") == mod_id:
199207
widget.set_icon(icon_path)
200208
break

pymcl/mod_manager.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,15 @@ def on_updates_found(self, updates):
168168
self.check_updates_button.setEnabled(True)
169169
self.check_updates_button.setText("Check for Mod Updates")
170170

171+
if not updates:
172+
self.download_status_label.setText("All mods are up to date.")
173+
return
174+
171175
count = 0
172176
for i in range(self.mod_list_widget.count()):
173177
item = self.mod_list_widget.item(i)
178+
if not item:
179+
continue
174180
mod_path = item.data(Qt.ItemDataRole.UserRole)
175181
widget = self.mod_list_widget.itemWidget(item)
176182

@@ -227,7 +233,15 @@ def get_mods_directory(self):
227233

228234
@pyqtSlot()
229235
def populate_mods_list(self):
236+
# Clear list widget correctly
237+
for i in range(self.mod_list_widget.count()):
238+
item = self.mod_list_widget.item(i)
239+
if item:
240+
widget = self.mod_list_widget.itemWidget(item)
241+
if widget:
242+
widget.deleteLater()
230243
self.mod_list_widget.clear()
244+
231245
try:
232246
mods_dir = self.get_mods_directory()
233247
if not os.path.exists(mods_dir):

pymcl/modrinth_client.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ def search(self, query, game_versions=None, loader=None, limit=20):
2525
try:
2626
response = self.session.get(f"{self.BASE_URL}/search", params=params, timeout=15)
2727
response.raise_for_status()
28-
return response.json().get("hits", [])
28+
try:
29+
data = response.json()
30+
return data.get("hits", []) if isinstance(data, dict) else []
31+
except (json.JSONDecodeError, ValueError):
32+
return []
2933
except requests.RequestException as e:
3034
print(f"Error searching Modrinth: {e}")
3135
return []
@@ -34,7 +38,10 @@ def get_project(self, slug):
3438
try:
3539
response = self.session.get(f"{self.BASE_URL}/project/{slug}", timeout=15)
3640
response.raise_for_status()
37-
return response.json()
41+
try:
42+
return response.json()
43+
except (json.JSONDecodeError, ValueError):
44+
return {}
3845
except requests.RequestException as e:
3946
print(f"Error getting project from Modrinth: {e}")
4047
return {}
@@ -49,7 +56,10 @@ def get_updates(self, file_hashes):
4956
)
5057
response.raise_for_status()
5158
print("ModrinthClient: Successfully received update response.")
52-
return response.json()
59+
try:
60+
return response.json()
61+
except (json.JSONDecodeError, ValueError):
62+
return {}
5363
except requests.RequestException as e:
5464
print(f"ModrinthClient: Error getting updates: {e}")
5565
return {}
@@ -64,7 +74,10 @@ def get_versions(self, mod_id, game_versions=None, loader=None):
6474
try:
6575
response = self.session.get(f"{self.BASE_URL}/project/{mod_id}/version", params=params, timeout=15)
6676
response.raise_for_status()
67-
return response.json()
77+
try:
78+
return response.json()
79+
except (json.JSONDecodeError, ValueError):
80+
return []
6881
except requests.RequestException as e:
6982
print(f"Error getting versions from Modrinth: {e}")
7083
return []

pymcl/servers_page.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ def clear_list_ui(self):
8787
# Remove all widgets from layout except the stretch
8888
while self.list_layout.count() > 1:
8989
item = self.list_layout.takeAt(0)
90-
widget = item.widget()
91-
if widget:
92-
widget.deleteLater()
90+
if item:
91+
widget = item.widget()
92+
if widget:
93+
widget.deleteLater()
9394

9495
@pyqtSlot()
9596
def add_server(self):
@@ -121,7 +122,10 @@ def remove_server(self, ip):
121122
# Find and remove widget
122123
for i in range(self.list_layout.count()):
123124
item = self.list_layout.itemAt(i)
125+
if not item:
126+
continue
124127
widget = item.widget()
125128
if isinstance(widget, ServerCard) and widget.ip == ip:
129+
self.list_layout.takeAt(i)
126130
widget.deleteLater()
127131
break

pymcl/skin_manager.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,27 +105,27 @@ def paintEvent(self, event):
105105
painter = QPainter(self)
106106
painter.setRenderHint(QPainter.RenderHint.Antialiasing, False) # Keep pixel look
107107

108-
if not self.raw_skin:
108+
if not self.raw_skin or self.raw_skin.isNull():
109109
# Draw placeholder text centered
110110
painter.setPen(Qt.GlobalColor.white)
111111
painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, "No Skin Loaded")
112112
return
113113

114-
# Basic 2D Front View Mapping
115-
# Head: 8,8 (8x8)
116-
# Body: 20,20 (8x12)
117-
# Arms: 44,20 (4x12) - Note: Alex/Slim is 3x12
118-
# Legs: 4,20 (4x12)
119-
120114
# Scaling
121115
s = 16 # Draw scale
122116

123117
# Center X
124118
cx = self.width() // 2
125119
cy = 50
126120

121+
sw = self.raw_skin.width()
122+
sh = self.raw_skin.height()
123+
127124
# Helper to draw rect from skin to screen
128125
def draw_part(sx, sy, w, h, dx, dy):
126+
# Safety check for dimensions
127+
if sx + w > sw or sy + h > sh:
128+
return
129129
part = self.raw_skin.copy(sx, sy, w, h).scaled(w*s, h*s, Qt.AspectRatioMode.IgnoreAspectRatio)
130130
painter.drawPixmap(dx, dy, part)
131131

@@ -261,8 +261,14 @@ def refresh_skin(self):
261261
return
262262

263263
self.status_label.setText("Fetching skin...")
264+
if self.fetcher and self.fetcher.isRunning():
265+
self.fetcher.terminate()
266+
self.fetcher.wait()
267+
264268
self.fetcher = SkinFetcher(uuid)
265269
self.fetcher.finished.connect(self.on_skin_fetched)
270+
self.fetcher.finished.connect(self.fetcher.quit)
271+
self.fetcher.finished.connect(self.fetcher.deleteLater)
266272
self.fetcher.start()
267273

268274
@pyqtSlot(QPixmap, bool)
@@ -286,8 +292,14 @@ def upload_skin(self):
286292
token = self.minecraft_info.get("access_token")
287293

288294
self.status_label.setText("Uploading skin...")
295+
if self.uploader and self.uploader.isRunning():
296+
self.uploader.terminate()
297+
self.uploader.wait()
298+
289299
self.uploader = SkinUploader(token, file_path, variant)
290300
self.uploader.finished.connect(self.on_upload_finished)
301+
self.uploader.finished.connect(self.uploader.quit)
302+
self.uploader.finished.connect(self.uploader.deleteLater)
291303
self.uploader.start()
292304

293305
@pyqtSlot(bool, str)

pymcl/stylesheet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
}
232232
233233
QWidget#left_title_container {
234-
background-color: rgba(15, 17, 21, 0.65);
234+
background-color: rgba(10, 12, 16, 0.85);
235235
border: 1px solid rgba(255, 255, 255, 0.08);
236236
border-radius: 20px;
237237
padding: 15px;
@@ -250,7 +250,7 @@
250250
QPushButton#nav_button, QPushButton#nav_button_active {
251251
background-color: transparent;
252252
border: 1px solid transparent;
253-
color: #9ca3af;
253+
color: #e5e7eb;
254254
font-size: 15px;
255255
font-weight: 600;
256256
text-align: left;

0 commit comments

Comments
 (0)