cs2tracker 2.1.13__py3-none-any.whl → 2.1.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cs2tracker might be problematic. Click here for more details.

@@ -4,7 +4,6 @@ from shutil import copy
4
4
  from subprocess import PIPE, STDOUT
5
5
  from threading import Thread
6
6
  from tkinter import messagebox, ttk
7
- from urllib.parse import unquote
8
7
 
9
8
  from nodejs import node
10
9
  from ttk_text import ThemedText
@@ -12,6 +11,7 @@ from ttk_text import ThemedText
12
11
  from cs2tracker.constants import (
13
12
  CONFIG_FILE,
14
13
  CONFIG_FILE_BACKUP,
14
+ DATA_DIR,
15
15
  INVENTORY_IMPORT_FILE,
16
16
  INVENTORY_IMPORT_SCRIPT,
17
17
  )
@@ -21,7 +21,7 @@ ADD_CUSTOM_ITEM_TITLE = "Add Custom Item"
21
21
  ADD_CUSTOM_ITEM_SIZE = "500x220"
22
22
 
23
23
  IMPORT_INVENTORY_TITLE = "Import Steam Inventory"
24
- IMPORT_INVENTORY_SIZE = "500x450"
24
+ IMPORT_INVENTORY_SIZE = "600x550"
25
25
 
26
26
  IMPORT_INVENTORY_PROCESS_TITLE = "Importing Steam Inventory..."
27
27
  IMPORT_INVENTORY_PROCESS_SIZE = "700x500"
@@ -37,8 +37,11 @@ class ConfigEditorFrame(ttk.Frame):
37
37
  super().__init__(parent, padding=15)
38
38
 
39
39
  self.parent = parent
40
+ self.edit_entry = None
40
41
  self._add_widgets()
41
42
 
43
+ self.tree.focus_set()
44
+
42
45
  def _add_widgets(self):
43
46
  """Configure the main editor frame which displays the configuration options in a
44
47
  structured way.
@@ -46,59 +49,109 @@ class ConfigEditorFrame(ttk.Frame):
46
49
  self._configure_treeview()
47
50
  self.tree.pack(expand=True, fill="both")
48
51
 
49
- button_frame = ConfigEditorButtonFrame(self, self.tree)
52
+ button_frame = ConfigEditorButtonFrame(self)
50
53
  button_frame.pack(side="bottom", padx=10, pady=(0, 10))
51
54
 
52
- def _set_cell_value(self, event):
55
+ def save_config(self):
56
+ """Save the current configuration from the treeview to the config file."""
57
+ config.delete_display_sections()
58
+ for section in self.tree.get_children():
59
+ config.add_section(section)
60
+ for item in self.tree.get_children(section):
61
+ item_name = self.tree.item(item, "text")
62
+ if section not in ("App Settings", "User Settings"):
63
+ config_option = config.name_to_option(item_name, href=True)
64
+ else:
65
+ config_option = config.name_to_option(item_name)
66
+ value = self.tree.item(item, "values")[0]
67
+ config.set(section, config_option, value)
68
+
69
+ config.write_to_file()
70
+ if not config.valid:
71
+ config.load_from_file()
72
+ self.reload_config_into_tree()
73
+ messagebox.showerror(
74
+ "Config Error", f"The configuration is invalid. ({config.last_error})", parent=self
75
+ )
76
+ self.parent.focus_set()
77
+ self.tree.focus_set()
78
+
79
+ def _save_edit(self, event, row, column):
80
+ """Save the edited value in the treeview and destroy the entry widget."""
81
+ self.tree.set(row, column=column, value=event.widget.get())
82
+ self.save_config()
83
+ event.widget.destroy()
84
+
85
+ def _set_cell_value(self, event, row=None, column=None):
53
86
  """
54
- Set the value of a cell in the treeview to be editable when double- clicked.
87
+ Set the value of a cell in the treeview to be editable when double-clicked.
55
88
 
56
89
  Source: https://stackoverflow.com/questions/75787251/create-an-editable-tkinter-treeview-with-keyword-connection
57
90
  """
91
+ try:
92
+ if not row or not column:
93
+ row = self.tree.identify_row(event.y)
94
+ column = self.tree.identify_column(event.x)
58
95
 
59
- def save_edit(event):
60
- self.tree.set(row, column=column, value=event.widget.get())
61
- event.widget.destroy()
96
+ item_text = self.tree.item(row, "text")
97
+ if column == "#0" or any(item_text == section for section in config.sections()):
98
+ return
99
+ item_value = self.tree.item(row, "values")[0]
62
100
 
63
- try:
64
- row = self.tree.identify_row(event.y)
65
- column = self.tree.identify_column(event.x)
66
- item_text = self.tree.set(row, column)
67
- if item_text.strip() == "":
68
- left_item_text = self.tree.item(row, "text")
69
- # Don't allow editing of section headers
70
- if any(left_item_text == section for section in config.sections()):
71
- return
72
101
  x, y, w, h = self.tree.bbox(row, column)
73
- entryedit = ttk.Entry(self)
74
- entryedit.place(x=x, y=y, width=w, height=h + 3) # type: ignore
75
- entryedit.insert("end", item_text)
76
- entryedit.bind("<Return>", save_edit)
77
- entryedit.focus_set()
78
- entryedit.grab_set()
102
+ self.edit_entry = ttk.Entry(self, justify="center", font=("Helvetica", 11))
103
+ self.edit_entry.place(x=x, y=y, width=w, height=h + 3) # type: ignore
104
+ self.edit_entry.insert("end", item_value)
105
+ self.edit_entry.bind("<Return>", lambda e: self._save_edit(e, row, column))
106
+ self.edit_entry.focus_set()
107
+ self.edit_entry.grab_set()
79
108
  except Exception:
80
- pass
109
+ return
110
+
111
+ def _set_selection_value(self, _):
112
+ """Set the value of the currently selected cell in the treeview to be
113
+ editable.
114
+ """
115
+ selected = self.tree.selection()
116
+ if selected:
117
+ row = selected[0]
118
+ column = "#1"
119
+ self._set_cell_value(None, row=row, column=column)
81
120
 
82
- def _destroy_entries(self, _):
121
+ def _delete_selection_value(self, _):
122
+ """
123
+ Delete the value of the currently selected cell in the treeview.
124
+
125
+ This only works for custom items, as other sections are not editable.
126
+ """
127
+ selected = self.tree.selection()
128
+ if selected:
129
+ row = selected[0]
130
+ section_name = self.tree.parent(row)
131
+ if section_name == "Custom Items":
132
+ self.tree.delete(row)
133
+ self.save_config()
134
+ self.tree.focus("Custom Items")
135
+ self.tree.selection_set("Custom Items")
136
+
137
+ def _destroy_entry(self, _):
83
138
  """Destroy any entry widgets in the treeview on an event, such as a mouse wheel
84
139
  movement.
85
140
  """
86
- for widget in self.winfo_children():
87
- if isinstance(widget, ttk.Entry):
88
- widget.destroy()
89
-
90
- def _destroy_entry(self, event):
91
- """Destroy the entry widget on an even targeting it."""
92
- if isinstance(event.widget, ttk.Entry):
93
- event.widget.destroy()
141
+ if self.edit_entry:
142
+ self.edit_entry.destroy()
143
+ self.edit_entry = None
144
+ self.tree.focus_set()
94
145
 
95
146
  def _make_tree_editable(self):
96
147
  """Add a binding to the treeview that allows double-clicking on a cell to edit
97
148
  its value.
98
149
  """
99
150
  self.tree.bind("<Double-1>", self._set_cell_value)
100
- self.parent.bind("<MouseWheel>", self._destroy_entries) # type: ignore
101
- self.parent.bind("<Button-1>", self._destroy_entry) # type: ignore
151
+ self.tree.bind("<Return>", self._set_selection_value)
152
+ self.tree.bind("<BackSpace>", self._delete_selection_value)
153
+ self.parent.bind("<MouseWheel>", self._destroy_entry)
154
+ self.parent.bind("<Escape>", self._destroy_entry)
102
155
 
103
156
  def _load_config_into_tree(self):
104
157
  """Load the configuration options into the treeview for display and editing."""
@@ -107,13 +160,16 @@ class ConfigEditorFrame(ttk.Frame):
107
160
  continue
108
161
  section_level = self.tree.insert("", "end", iid=section, text=section)
109
162
  for config_option, value in config.items(section):
110
- if section == "Custom Items":
111
- custom_item_name = unquote(config_option.split("/")[-1])
112
- self.tree.insert(section_level, "end", text=custom_item_name, values=[value])
163
+ if section not in ("User Settings", "App Settings"):
164
+ option_name = config.option_to_name(config_option, href=True)
165
+ self.tree.insert(section_level, "end", text=option_name, values=[value])
113
166
  else:
114
- option_name = config_option.replace("_", " ").title()
167
+ option_name = config.option_to_name(config_option)
115
168
  self.tree.insert(section_level, "end", text=option_name, values=[value])
116
169
 
170
+ self.tree.focus("User Settings")
171
+ self.tree.selection_set("User Settings")
172
+
117
173
  def reload_config_into_tree(self):
118
174
  """Reload the configuration options into the treeview for display and
119
175
  editing.
@@ -146,14 +202,13 @@ class ConfigEditorFrame(ttk.Frame):
146
202
 
147
203
 
148
204
  class ConfigEditorButtonFrame(ttk.Frame):
149
- def __init__(self, parent, tree):
205
+ def __init__(self, editor_frame):
150
206
  """Initialize the button frame that contains buttons for saving the updated
151
207
  configuration and adding custom items.
152
208
  """
153
- super().__init__(parent, padding=10)
209
+ super().__init__(editor_frame, padding=10)
154
210
 
155
- self.parent = parent
156
- self.tree = tree
211
+ self.editor_frame = editor_frame
157
212
  self.custom_item_dialog = None
158
213
 
159
214
  self._add_widgets()
@@ -162,9 +217,6 @@ class ConfigEditorButtonFrame(ttk.Frame):
162
217
  """Add buttons to the button frame for saving the configuration and adding
163
218
  custom items.
164
219
  """
165
- save_button = ttk.Button(self, text="Save", command=self._save_config)
166
- save_button.pack(side="left", expand=True, padx=5)
167
-
168
220
  reset_button = ttk.Button(self, text="Reset", command=self._reset_config)
169
221
  reset_button.pack(side="left", expand=True, padx=5)
170
222
 
@@ -176,67 +228,47 @@ class ConfigEditorButtonFrame(ttk.Frame):
176
228
  )
177
229
  import_inventory_button.pack(side="left", expand=True, padx=5)
178
230
 
179
- def _save_config(self):
180
- """Save the current configuration from the treeview to the config file."""
181
- for child in self.tree.get_children():
182
- for item in self.tree.get_children(child):
183
- title_option = self.tree.item(item, "text")
184
- config_option = title_option.lower().replace(" ", "_")
185
- value = self.tree.item(item, "values")[0]
186
- section = self.tree.parent(item)
187
- section_name = self.tree.item(section, "text")
188
- if section_name == "Custom Items":
189
- # custom items are already saved upon creation (Saving them again would result in duplicates)
190
- continue
191
- config.set(section_name, config_option, value)
192
-
193
- config.write_to_file()
194
- if config.valid:
195
- messagebox.showinfo("Config Saved", "The configuration has been saved successfully.")
196
- else:
197
- config.load()
198
- self.parent.reload_config_into_tree()
199
- messagebox.showerror(
200
- "Config Error",
201
- f"The configuration is invalid. ({config.last_error})",
202
- )
203
-
204
231
  def _reset_config(self):
205
232
  """Reset the configuration file to its default state."""
206
233
  confirm = messagebox.askokcancel(
207
- "Reset Config", "Are you sure you want to reset the configuration?"
234
+ "Reset Config", "Are you sure you want to reset the configuration?", parent=self
208
235
  )
209
236
  if confirm:
210
237
  copy(CONFIG_FILE_BACKUP, CONFIG_FILE)
211
- config.load()
212
- self.parent.reload_config_into_tree()
238
+ config.load_from_file()
239
+ self.editor_frame.reload_config_into_tree()
240
+ self.editor_frame.focus_set()
241
+ self.editor_frame.tree.focus_set()
213
242
 
214
243
  def _add_custom_item(self):
215
244
  """Open a window to add a new custom item."""
216
- custom_item_window = tk.Toplevel(self.parent)
245
+ custom_item_window = tk.Toplevel(self.editor_frame)
217
246
  custom_item_window.title(ADD_CUSTOM_ITEM_TITLE)
218
247
  custom_item_window.geometry(ADD_CUSTOM_ITEM_SIZE)
248
+ custom_item_window.focus_set()
219
249
 
220
- custom_item_frame = CustomItemFrame(custom_item_window, self.parent, self.tree)
250
+ custom_item_frame = CustomItemFrame(custom_item_window, self.editor_frame)
221
251
  custom_item_frame.pack(expand=True, fill="both", padx=15, pady=15)
252
+ self.editor_frame.tree.focus_set()
222
253
 
223
254
  def _import_steam_inventory(self):
224
255
  """Open a window to import the user's Steam inventory."""
225
- steam_inventory_window = tk.Toplevel(self.parent)
256
+ steam_inventory_window = tk.Toplevel(self.editor_frame)
226
257
  steam_inventory_window.title(IMPORT_INVENTORY_TITLE)
227
258
  steam_inventory_window.geometry(IMPORT_INVENTORY_SIZE)
259
+ steam_inventory_window.focus_set()
228
260
 
229
- steam_inventory_frame = InventoryImportFrame(steam_inventory_window, self)
261
+ steam_inventory_frame = InventoryImportFrame(steam_inventory_window, self.editor_frame)
230
262
  steam_inventory_frame.pack(expand=True, fill="both", padx=15, pady=15)
263
+ self.editor_frame.tree.focus_set()
231
264
 
232
265
 
233
266
  class CustomItemFrame(ttk.Frame):
234
- def __init__(self, parent, grandparent, tree):
267
+ def __init__(self, parent, editor_frame):
235
268
  """Initialize the custom item frame that allows users to add custom items."""
236
269
  super().__init__(parent, style="Card.TFrame", padding=15)
237
270
  self.parent = parent
238
- self.grandparent = grandparent
239
- self.tree = tree
271
+ self.editor_frame = editor_frame
240
272
  self._add_widgets()
241
273
 
242
274
  def _add_widgets(self):
@@ -255,68 +287,108 @@ class CustomItemFrame(ttk.Frame):
255
287
  command=lambda: self._add_custom_item(item_url_entry.get(), item_owned_entry.get()),
256
288
  )
257
289
  add_button.pack(pady=10)
290
+ self.parent.bind("<Return>", lambda _: add_button.invoke())
258
291
 
259
- def _add_custom_item(self, item_url, item_owned):
292
+ def _add_custom_item(self, item_href, item_owned):
260
293
  """Add a custom item to the configuration."""
261
- if not item_url or not item_owned:
262
- messagebox.showerror("Input Error", "All fields must be filled out.")
263
- return
264
- try:
265
- if int(item_owned) < 0:
266
- raise ValueError("Owned count must be a non-negative integer.")
267
- except ValueError as error:
268
- messagebox.showerror("Input Error", f"Invalid owned count: {error}")
294
+ if not item_href or not item_owned:
295
+ messagebox.showerror("Input Error", "All fields must be filled out.", parent=self)
296
+ self.editor_frame.focus_set()
297
+ self.parent.focus_set()
269
298
  return
270
299
 
271
- config.set("Custom Items", item_url, item_owned)
272
- config.write_to_file()
273
- if config.valid:
274
- config.load()
275
- self.grandparent.reload_config_into_tree()
276
- self.parent.destroy()
277
- else:
278
- config.remove_option("Custom Items", item_url)
279
- messagebox.showerror(
280
- "Config Error",
281
- f"The configuration is invalid. ({config.last_error})",
282
- )
300
+ item_name = config.option_to_name(item_href, href=True)
301
+
302
+ # Make sure not to reinsert custom items that have already been added
303
+ for option in self.editor_frame.tree.get_children("Custom Items"):
304
+ option_name = self.editor_frame.tree.item(option, "text")
305
+ if option_name == item_name:
306
+ self.editor_frame.tree.set(option, column="#1", value=item_owned)
307
+ self.editor_frame.focus_set()
308
+ self.editor_frame.save_config()
309
+ self.parent.destroy()
310
+ return
311
+
312
+ self.editor_frame.tree.insert(
313
+ "Custom Items",
314
+ "end",
315
+ text=item_name,
316
+ values=[item_owned],
317
+ )
318
+ self.editor_frame.focus_set()
319
+ self.editor_frame.save_config()
320
+ self.parent.destroy()
283
321
 
284
322
 
285
323
  class InventoryImportFrame(ttk.Frame):
286
324
  # pylint: disable=too-many-instance-attributes
287
- def __init__(self, parent, grandparent):
325
+ def __init__(self, parent, editor_frame):
288
326
  """Initialize the inventory import frame that allows users to import their Steam
289
327
  inventory.
290
328
  """
291
329
  super().__init__(parent, style="Card.TFrame", padding=10)
292
330
  self.parent = parent
293
- self.grandparent = grandparent
331
+ self.editor_frame = editor_frame
294
332
  self._add_widgets()
295
333
 
296
334
  def _add_widgets(self):
297
335
  """Add widgets to the inventory import frame."""
298
336
  self._configure_checkboxes()
299
- self.import_cases_checkbox.pack(anchor="w", padx=10, pady=5)
300
- self.import_sticker_capsules_checkbox.pack(anchor="w", padx=10, pady=5)
301
- self.import_stickers_checkbox.pack(anchor="w", padx=10, pady=5)
302
- self.import_others_checkbox.pack(anchor="w", padx=10, pady=5)
337
+ self.storage_units_checkbox.pack(anchor="w", padx=20, pady=(15, 5))
338
+ self.regular_inventory_checkbox.pack(anchor="w", padx=20, pady=5)
339
+
340
+ self.import_cases_checkbox.pack(anchor="w", padx=20, pady=5)
341
+ self.import_sticker_capsules_checkbox.pack(anchor="w", padx=20, pady=5)
342
+ self.import_stickers_checkbox.pack(anchor="w", padx=20, pady=5)
343
+ self.import_others_checkbox.pack(anchor="w", padx=20, pady=5)
303
344
 
304
345
  self._configure_entries()
305
- self.user_name_label.pack(pady=10)
346
+ self.user_name_label.pack(pady=(20, 10))
306
347
  self.user_name_entry.pack(fill="x", padx=50)
307
348
  self.password_label.pack(pady=10)
308
349
  self.password_entry.pack(fill="x", padx=50)
309
350
  self.two_factor_label.pack(pady=10)
310
351
  self.two_factor_entry.pack(fill="x", padx=50)
311
352
 
312
- self.import_button = ttk.Button(self, text="Import", command=self._import_inventory)
353
+ self.import_button = ttk.Button(
354
+ self, text="Import", command=self._import_inventory, state="disabled"
355
+ )
313
356
  self.import_button.pack(pady=10)
357
+ self.parent.bind("<Return>", lambda _: self.import_button.invoke())
358
+
359
+ def form_complete(_):
360
+ if (
361
+ len(self.user_name_entry.get().strip()) > 0
362
+ and len(self.password_entry.get().strip()) > 0
363
+ and len(self.two_factor_entry.get().strip()) > 0
364
+ ):
365
+ self.import_button.configure(state="normal")
366
+ else:
367
+ self.import_button.configure(state="disabled")
368
+
369
+ self.parent.bind("<KeyRelease>", form_complete)
314
370
 
315
371
  def _configure_checkboxes(self):
316
372
  # pylint: disable=attribute-defined-outside-init
317
373
  """Configure the checkboxes for selecting what to import from the Steam
318
374
  inventory.
319
375
  """
376
+ self.regular_inventory_value = tk.BooleanVar(value=False)
377
+ self.regular_inventory_checkbox = ttk.Checkbutton(
378
+ self,
379
+ text="Regular Inventory",
380
+ variable=self.regular_inventory_value,
381
+ style="Switch.TCheckbutton",
382
+ )
383
+
384
+ self.storage_units_value = tk.BooleanVar(value=True)
385
+ self.storage_units_checkbox = ttk.Checkbutton(
386
+ self,
387
+ text="Storage Units",
388
+ variable=self.storage_units_value,
389
+ style="Switch.TCheckbutton",
390
+ )
391
+
320
392
  self.import_cases_value = tk.BooleanVar(value=True)
321
393
  self.import_cases_checkbox = ttk.Checkbutton(
322
394
  self, text="Import Cases", variable=self.import_cases_value, style="Switch.TCheckbutton"
@@ -330,7 +402,7 @@ class InventoryImportFrame(ttk.Frame):
330
402
  style="Switch.TCheckbutton",
331
403
  )
332
404
 
333
- self.import_stickers_value = tk.BooleanVar(value=True)
405
+ self.import_stickers_value = tk.BooleanVar(value=False)
334
406
  self.import_stickers_checkbox = ttk.Checkbutton(
335
407
  self,
336
408
  text="Import Stickers",
@@ -338,7 +410,7 @@ class InventoryImportFrame(ttk.Frame):
338
410
  style="Switch.TCheckbutton",
339
411
  )
340
412
 
341
- self.import_others_value = tk.BooleanVar(value=True)
413
+ self.import_others_value = tk.BooleanVar(value=False)
342
414
  self.import_others_checkbox = ttk.Checkbutton(
343
415
  self,
344
416
  text="Import Other Items",
@@ -352,13 +424,13 @@ class InventoryImportFrame(ttk.Frame):
352
424
  code.
353
425
  """
354
426
  self.user_name_label = ttk.Label(self, text="Steam Username:")
355
- self.user_name_entry = ttk.Entry(self)
427
+ self.user_name_entry = ttk.Entry(self, justify="center", font=("Helvetica", 11))
356
428
 
357
429
  self.password_label = ttk.Label(self, text="Steam Password:")
358
- self.password_entry = ttk.Entry(self, show="*")
430
+ self.password_entry = ttk.Entry(self, show="*", justify="center", font=("Helvetica", 11))
359
431
 
360
- self.two_factor_label = ttk.Label(self, text="Steam Guard Code (if enabled):")
361
- self.two_factor_entry = ttk.Entry(self)
432
+ self.two_factor_label = ttk.Label(self, text="Steam Guard Code:")
433
+ self.two_factor_entry = ttk.Entry(self, justify="center", font=("Helvetica", 11))
362
434
 
363
435
  def _import_inventory(self):
364
436
  """
@@ -367,6 +439,9 @@ class InventoryImportFrame(ttk.Frame):
367
439
  This will also install the necessary npm packages if they are not already
368
440
  installed.
369
441
  """
442
+ regular_inventory = self.regular_inventory_value.get()
443
+ storage_units = self.storage_units_value.get()
444
+
370
445
  import_cases = self.import_cases_value.get()
371
446
  import_sticker_capsules = self.import_sticker_capsules_value.get()
372
447
  import_stickers = self.import_stickers_value.get()
@@ -380,6 +455,8 @@ class InventoryImportFrame(ttk.Frame):
380
455
  [
381
456
  INVENTORY_IMPORT_SCRIPT,
382
457
  INVENTORY_IMPORT_FILE,
458
+ str(regular_inventory),
459
+ str(storage_units),
383
460
  str(import_cases),
384
461
  str(import_sticker_capsules),
385
462
  str(import_stickers),
@@ -393,23 +470,25 @@ class InventoryImportFrame(ttk.Frame):
393
470
  self.parent.destroy()
394
471
 
395
472
  def _display_node_subprocess(self, node_cmd):
396
- text_window = tk.Toplevel(self.grandparent)
473
+ text_window = tk.Toplevel(self.editor_frame)
397
474
  text_window.title(IMPORT_INVENTORY_PROCESS_TITLE)
398
475
  text_window.geometry(IMPORT_INVENTORY_PROCESS_SIZE)
476
+ text_window.focus_set()
399
477
 
400
- process_frame = InventoryImportProcessFrame(text_window)
478
+ process_frame = InventoryImportProcessFrame(text_window, self.editor_frame)
401
479
  process_frame.pack(expand=True, fill="both", padx=15, pady=15)
402
- process_frame.start(node_cmd)
403
480
  process_frame.console.focus_set()
481
+ process_frame.start(node_cmd)
404
482
 
405
483
 
406
484
  class InventoryImportProcessFrame(ttk.Frame):
407
485
  # pylint: disable=attribute-defined-outside-init
408
486
  # Source: https://stackoverflow.com/questions/27327886/issues-intercepting-subprocess-output-in-real-time
409
- def __init__(self, parent):
487
+ def __init__(self, parent, editor_frame):
410
488
  """Initialize the frame that displays the output of the subprocess."""
411
489
  super().__init__(parent)
412
490
  self.parent = parent
491
+ self.editor_frame = editor_frame
413
492
  self._add_widgets()
414
493
 
415
494
  def _add_widgets(self):
@@ -438,14 +517,14 @@ class InventoryImportProcessFrame(ttk.Frame):
438
517
  stdin=PIPE,
439
518
  stderr=STDOUT,
440
519
  text=True,
441
- bufsize=1,
442
520
  encoding="utf-8",
521
+ shell=True,
522
+ cwd=DATA_DIR,
443
523
  )
444
524
  self.queue = Queue()
445
525
  self.thread = Thread(target=self._read_lines, args=(self.process, self.queue), daemon=True)
446
526
  self.thread.start()
447
-
448
- self.after(100, self._update_lines)
527
+ self._update_lines()
449
528
 
450
529
  def _update_lines(self):
451
530
  """Update the text widget with lines from the subprocess output."""
@@ -459,7 +538,7 @@ class InventoryImportProcessFrame(ttk.Frame):
459
538
  pass
460
539
 
461
540
  if self.process.poll() is None or not self.queue.empty():
462
- self.after(100, self._update_lines)
541
+ self.after(50, self._update_lines)
463
542
  else:
464
543
  self._cleanup()
465
544
 
@@ -467,8 +546,10 @@ class InventoryImportProcessFrame(ttk.Frame):
467
546
  """Clean up the process and thread after completion and trigger a config update
468
547
  from the newly written inventory file.
469
548
  """
470
- config.read_from_inventory_file()
471
- self.parent.master.master.reload_config_into_tree()
472
-
473
549
  self.process.wait()
474
550
  self.thread.join()
551
+
552
+ config.read_from_inventory_file()
553
+ self.editor_frame.reload_config_into_tree()
554
+ self.editor_frame.tree.focus_set()
555
+ self.parent.destroy()
@@ -4,6 +4,8 @@ from tkinter.filedialog import asksaveasfilename
4
4
 
5
5
  from tksheet import Sheet
6
6
 
7
+ from cs2tracker.scraper.scraper import ParsingError
8
+
7
9
 
8
10
  class ScraperFrame(ttk.Frame):
9
11
  def __init__(self, parent, scraper, sheet_size, dark_theme):
@@ -43,18 +45,36 @@ class ScraperFrame(ttk.Frame):
43
45
  height=self.sheet_height,
44
46
  width=self.sheet_width,
45
47
  auto_resize_columns=150,
48
+ default_column_width=150,
46
49
  sticky="nsew",
47
50
  )
48
51
  self.sheet.enable_bindings()
52
+
53
+ source_titles = []
54
+ for price_source in self.scraper.parser.SOURCES:
55
+ source_titles += [
56
+ f"{price_source.value.title()} (USD)",
57
+ f"{price_source.value.title()} Owned (USD)",
58
+ ]
49
59
  self.sheet.insert_row(
50
- ["Item Name", "Item Owned", "Steam Market Price (USD)", "Total Value Owned (USD)"]
60
+ [
61
+ "Item Name",
62
+ "Item Owned",
63
+ ]
64
+ + source_titles
51
65
  )
52
- self.sheet.column_width(0, 220)
53
- self.sheet.column_width(1, 20)
54
66
  self.sheet.align_rows([0], "c")
55
- self.sheet.align_columns([1, 2, 3], "c")
56
- self.sheet.popup_menu_add_command("Save Sheet", self._save_sheet)
57
67
 
68
+ price_columns = list(range(2 * len(self.scraper.parser.SOURCES)))
69
+ price_columns = [1] + [column_index + 2 for column_index in price_columns]
70
+ self.sheet.align_columns(price_columns, "c")
71
+ self.sheet.column_width(0, 220)
72
+
73
+ required_window_width = 220 + 150 * len(price_columns)
74
+ if int(self.sheet_width) < required_window_width:
75
+ self.parent.geometry(f"{required_window_width}x{self.sheet_height}")
76
+
77
+ self.sheet.popup_menu_add_command("Save Sheet", self._save_sheet)
58
78
  self.parent.bind("<Configure>", self._readjust_sheet_size_with_window_size)
59
79
 
60
80
  def _save_sheet(self):
@@ -84,10 +104,7 @@ class ScraperFrame(ttk.Frame):
84
104
 
85
105
  self.scraper.scrape_prices(update_sheet_callback)
86
106
 
87
- row_heights = self.sheet.get_row_heights()
88
- last_row_index = len(row_heights) - 1
89
- self.sheet.align_rows(last_row_index, "c")
90
-
91
107
  if self.scraper.error_stack:
92
108
  last_error = self.scraper.error_stack[-1]
93
- messagebox.showerror("An Error Occurred", f"{last_error.message}")
109
+ if not isinstance(last_error, ParsingError):
110
+ messagebox.showerror("An Error Occurred", f"{last_error.message}", parent=self)