ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/ns_dev/Python/NinoCode/Active_prgs/Gromulus/Gromulus_UI.py
Revision: 979
Committed: Sat Feb 14 03:49:44 2026 UTC (6 weeks ago) by nino.borges
Content type: text/x-python
File size: 21177 byte(s)
Log Message:
Refactor metadata import and UI workflows; add system-scoped DAT handling, artwork ingestion, and artwork previews

- Scope No-Intro/TOSEC imports to selected local system and track import metadata
- Improve hash matching using file-level hashes with system-aware joins
- Add artwork ingest workflow with type selection, dry-run mode, and busy indicators
- Add bottom-row artwork previews (box/title/ingame) driven by selected game
- Improve UI responsiveness and layout (system-switch busy dialog, taller game list, preview row positioning)

File Contents

# User Rev Content
1 nino.borges 795 """
2     Created by Emanuel Borges
3     05.19.2023
4    
5     This is the main UI for Gromulus, which is a catalog inventory for roms, games and maybe one day applications.
6     Like most of my GUI programs, this will import a separate library with my methods for doing the actual work.
7    
8     """
9    
10    
11     import sys, os, wx
12     #import wx.lib.buttons as buttons
13 nino.borges 979 import wx.lib.agw.pybusyinfo as PBI
14 nino.borges 795 import Gromulus_Lib
15    
16    
17    
18     class MyFrame(wx.Frame):
19     def __init__(self, parent, ID, title, pos=wx.DefaultPosition):
20 nino.borges 979 wx.Frame.__init__(self, parent, ID, title, pos, size = (1450,925))
21 nino.borges 909 self.db = Gromulus_Lib.DatabaseManager()
22 nino.borges 976 self.dbConnection = self.db.get_connection()
23 nino.borges 795 self.panel = wx.Panel(self,-1)
24 nino.borges 976 self.currentSystemKey = "SNES"
25     self.gamesListMatrix = Gromulus_Lib.GetGameListBySystem(self.currentSystemKey, self.dbConnection)
26 nino.borges 795 gamesList = list(self.gamesListMatrix.keys())
27     gamesList.sort()
28 nino.borges 806 self.gamesCount = len(gamesList)
29     unmatchedGamesList=[i for i in gamesList if "." in i]
30     self.unmatchedGamesCount = len(unmatchedGamesList)
31     print(f"{self.gamesCount} total games for this system. {self.gamesCount - self.unmatchedGamesCount} matched and {self.unmatchedGamesCount} unmatched.")
32 nino.borges 795
33     self.CreateSystemButtonSection()
34 nino.borges 979 self.gameSelectionListBox = wx.ListBox(self.panel, 60, (100, 50), (490, 500), gamesList , wx.LB_SINGLE|wx.LB_OWNERDRAW)
35 nino.borges 795 self.CreateFieldsFirstRow()
36 nino.borges 979 self.CreateArtworkPreviewSection()
37 nino.borges 795
38    
39    
40     mainSizer = wx.BoxSizer(wx.HORIZONTAL)
41     mainSizer.Add(self.buttonSizer, 0, wx.ALIGN_TOP|wx.LEFT|wx.TOP,25)
42     mainSizer.Add(self.gameSelectionListBox, 0, wx.ALIGN_TOP|wx.LEFT|wx.TOP,25)
43     mainSizer.Add(self.fieldsFirstRowSizer, 0, wx.ALIGN_TOP|wx.LEFT|wx.TOP,25)
44    
45 nino.borges 979 rootSizer = wx.BoxSizer(wx.VERTICAL)
46     rootSizer.Add(mainSizer, 0, wx.EXPAND)
47     rootSizer.AddStretchSpacer(1)
48     rootSizer.Add(self.artworkPreviewSizer, 0, wx.ALIGN_CENTER|wx.ALL, 15)
49     self.panel.SetSizer(rootSizer)
50 nino.borges 795
51 nino.borges 979
52 nino.borges 795 self.CreateStatusBar()
53     self.SetStatusText("Ready.")
54     self.CreateMenuBar()
55    
56     self.Bind(wx. EVT_TOGGLEBUTTON, self.OnSystemSelected, self.snesSystemButton)
57     self.Bind(wx. EVT_TOGGLEBUTTON, self.OnSystemSelected, self.nesSystemButton)
58     self.Bind(wx. EVT_TOGGLEBUTTON, self.OnSystemSelected, self.genesisSystemButton)
59     self.Bind(wx. EVT_TOGGLEBUTTON, self.OnSystemSelected, self.fz1SystemButton)
60     self.Bind(wx. EVT_TOGGLEBUTTON, self.OnSystemSelected, self.ps1SystemButton)
61    
62     self.Bind(wx.EVT_LISTBOX, self.OnGameSelected, self.gameSelectionListBox)
63    
64 nino.borges 976 def _set_text(self, text_ctrl, value):
65     text_ctrl.SetValue(value if value else "")
66 nino.borges 795
67 nino.borges 979 def _load_scaled_bitmap(self, image_path, width, height):
68     if not image_path or not os.path.isfile(image_path):
69     return wx.Bitmap(width, height)
70     image = wx.Image(image_path, wx.BITMAP_TYPE_ANY)
71     if not image.IsOk():
72     return wx.Bitmap(width, height)
73     image = image.Scale(width, height, wx.IMAGE_QUALITY_HIGH)
74     return wx.Bitmap(image)
75    
76     def _set_artwork_preview(self, bitmap_ctrl, image_path):
77     width, height = bitmap_ctrl.GetSize()
78     bitmap_ctrl.SetBitmap(self._load_scaled_bitmap(image_path, width, height))
79    
80     def _clear_artwork_previews(self):
81     self._set_artwork_preview(self.boxArtBitmap, None)
82     self._set_artwork_preview(self.titleArtBitmap, None)
83     self._set_artwork_preview(self.ingameArtBitmap, None)
84    
85     def _run_with_busy(self, message, func, *args, **kwargs):
86     busy = PBI.PyBusyInfo(message, parent=self, title="System Busy: Please Wait.")
87     wx.YieldIfNeeded()
88     try:
89     return func(*args, **kwargs)
90     finally:
91     del busy
92    
93 nino.borges 976 def _load_games_for_system(self, system_key):
94     self.currentSystemKey = system_key
95     self.gamesListMatrix = Gromulus_Lib.GetGameListBySystem(system_key, self.dbConnection)
96     gamesList = list(self.gamesListMatrix.keys())
97     gamesList.sort()
98    
99     self.gameSelectionListBox.Clear()
100     self.gameSelectionListBox.AppendItems(gamesList)
101    
102     self._set_text(self.gameNameTextCtrl, "")
103     self._set_text(self.gameHashTextCtrl, "")
104     self._set_text(self.gameFileNameTextCtrl, "")
105     self._set_text(self.gameFilePathTextCtrl, "")
106     self._set_text(self.noIntroGameNameTextCtrl, "")
107     self._set_text(self.noIntroSystemNameTextCtrl, "")
108 nino.borges 978 self._set_text(self.tosecGameNameTextCtrl, "")
109 nino.borges 979 self._clear_artwork_previews()
110 nino.borges 976
111 nino.borges 979 def _choose_system(self, prompt_title, only_with_roms=False):
112     systemMatrix = (
113     Gromulus_Lib.GetSystemListWithRoms(self.dbConnection)
114     if only_with_roms
115     else Gromulus_Lib.GetSystemList(self.dbConnection)
116     )
117 nino.borges 978 systemsList = list(systemMatrix.keys())
118     if not systemsList:
119 nino.borges 979 wx.MessageBox("No eligible systems found.", "System Selection", wx.OK | wx.ICON_WARNING)
120 nino.borges 978 return None
121 nino.borges 976
122 nino.borges 979 dlg = wx.SingleChoiceDialog(self, "Select system", prompt_title, systemsList)
123 nino.borges 978 if dlg.ShowModal() != wx.ID_OK:
124     return None
125     selected_name = dlg.GetStringSelection()
126     return systemMatrix[selected_name][0]
127    
128    
129 nino.borges 795 def CreateSystemButtonSection(self):
130     #systemsList = ['SNES','NES','Genisys']
131     #for system in systemsList:
132     #self.snesSystemButton = buttons.GenToggleButton(self.panel, -1, "SNES")
133     #self.nesSystemButton = buttons.GenToggleButton(self.panel, -1, "NES")
134     #self.genesisSystemButton = buttons.GenToggleButton(self.panel, -1, "Genesis")
135     #self.ps1SystemButton = buttons.GenToggleButton(self.panel, -1, "Playstation")
136    
137     ## Create a dictionary that holds the button instances by label text, so that you can toggle the other ones off in the bind event.
138     self.systemButtonDict = {}
139 nino.borges 976 self.buttonSystemMap = {}
140 nino.borges 795 self.snesSystemButton = wx.ToggleButton(self.panel, -1, "SNES")
141     self.systemButtonDict[self.snesSystemButton.GetLabelText()] = self.snesSystemButton
142 nino.borges 976 self.buttonSystemMap[self.snesSystemButton.GetLabelText()] = "SNES"
143 nino.borges 795
144     self.nesSystemButton = wx.ToggleButton(self.panel, -1, "NES")
145     self.systemButtonDict[self.nesSystemButton.GetLabelText()] = self.nesSystemButton
146 nino.borges 976 self.buttonSystemMap[self.nesSystemButton.GetLabelText()] = "NES"
147 nino.borges 795
148     self.genesisSystemButton = wx.ToggleButton(self.panel, -1, "Genesis")
149     self.systemButtonDict[self.genesisSystemButton.GetLabelText()] = self.genesisSystemButton
150 nino.borges 976 self.buttonSystemMap[self.genesisSystemButton.GetLabelText()] = "Genesis"
151 nino.borges 795
152     self.fz1SystemButton = wx.ToggleButton(self.panel, -1, "3DO")
153     self.systemButtonDict[self.fz1SystemButton.GetLabelText()] = self.fz1SystemButton
154 nino.borges 976 self.buttonSystemMap[self.fz1SystemButton.GetLabelText()] = "3DO"
155 nino.borges 795
156     self.ps1SystemButton = wx.ToggleButton(self.panel, -1, "Playstation")
157     self.systemButtonDict[self.ps1SystemButton.GetLabelText()] = self.ps1SystemButton
158 nino.borges 976 self.buttonSystemMap[self.ps1SystemButton.GetLabelText()] = "Playstation"
159     self.snesSystemButton.SetValue(True)
160 nino.borges 795
161    
162     self.buttonSizer = wx.BoxSizer(wx.VERTICAL)
163     self.buttonSizer.Add(self.snesSystemButton, 0, wx.ALL,10)
164     self.buttonSizer.Add(self.nesSystemButton,0,wx.ALL,10)
165     self.buttonSizer.Add(self.genesisSystemButton,0,wx.ALL,10)
166     self.buttonSizer.Add(self.fz1SystemButton,0,wx.ALL,10)
167     self.buttonSizer.Add(self.ps1SystemButton,0,wx.ALL,10)
168    
169     def CreateFieldsFirstRow(self):
170     self.gameNameStaticText = wx.StaticText(self.panel, -1, "Game Name:")
171     self.gameNameTextCtrl = wx.TextCtrl(self.panel, -1, size=(325, -1))
172    
173     self.gameHashStaticText = wx.StaticText(self.panel, -1, "Game Hash:")
174     self.gameHashTextCtrl = wx.TextCtrl(self.panel, -1, size=(325, -1))
175    
176     self.gameFileNameStaticText = wx.StaticText(self.panel, -1, "Game File Name:")
177     self.gameFileNameTextCtrl = wx.TextCtrl(self.panel, -1, size=(325, -1))
178    
179     self.gameFilePathStaticText = wx.StaticText(self.panel, -1, "Game File Path:")
180     self.gameFilePathTextCtrl = wx.TextCtrl(self.panel, -1, size=(425, -1))
181    
182     self.noIntroGameNameStaticText = wx.StaticText(self.panel, -1, "No Intro Game Name:")
183     self.noIntroGameNameTextCtrl = wx.TextCtrl(self.panel, -1, size=(325, -1))
184    
185     self.noIntroSystemNameStaticText = wx.StaticText(self.panel, -1, "No Intro System:")
186     self.noIntroSystemNameTextCtrl = wx.TextCtrl(self.panel, -1, size=(380, -1))
187    
188 nino.borges 976 self.tosecGameNameStaticText = wx.StaticText(self.panel, -1, "TOSEC Game Name:")
189     self.tosecGameNameTextCtrl = wx.TextCtrl(self.panel, -1, size=(325, -1))
190    
191 nino.borges 795 self.gameNameSizer = wx.BoxSizer(wx.HORIZONTAL)
192     self.gameNameSizer.Add(self.gameNameStaticText,0,wx.ALL, 10)
193     self.gameNameSizer.Add(self.gameNameTextCtrl,0,wx.ALL, 10)
194    
195     self.gameHashSizer = wx.BoxSizer(wx.HORIZONTAL)
196     self.gameHashSizer.Add(self.gameHashStaticText,0,wx.ALL, 10)
197     self.gameHashSizer.Add(self.gameHashTextCtrl,0,wx.ALL, 10)
198    
199     self.gameFileNameSizer = wx.BoxSizer(wx.HORIZONTAL)
200     self.gameFileNameSizer.Add(self.gameFileNameStaticText,0,wx.ALL, 10)
201     self.gameFileNameSizer.Add(self.gameFileNameTextCtrl,0,wx.ALL, 10)
202    
203     self.gameFilePathSizer = wx.BoxSizer(wx.HORIZONTAL)
204     self.gameFilePathSizer.Add(self.gameFilePathStaticText,0,wx.ALL, 10)
205     self.gameFilePathSizer.Add(self.gameFilePathTextCtrl,0,wx.ALL, 10)
206    
207     self.noIntroGameNameSizer = wx.BoxSizer(wx.HORIZONTAL)
208     self.noIntroGameNameSizer.Add(self.noIntroGameNameStaticText,0,wx.ALL, 10)
209     self.noIntroGameNameSizer.Add(self.noIntroGameNameTextCtrl,0,wx.ALL, 10)
210    
211     self.noIntroSystemNameSizer = wx.BoxSizer(wx.HORIZONTAL)
212     self.noIntroSystemNameSizer.Add(self.noIntroSystemNameStaticText,0,wx.ALL, 10)
213     self.noIntroSystemNameSizer.Add(self.noIntroSystemNameTextCtrl,0,wx.ALL, 10)
214    
215 nino.borges 976 self.tosecGameNameSizer = wx.BoxSizer(wx.HORIZONTAL)
216     self.tosecGameNameSizer.Add(self.tosecGameNameStaticText,0,wx.ALL, 10)
217     self.tosecGameNameSizer.Add(self.tosecGameNameTextCtrl,0,wx.ALL, 10)
218    
219 nino.borges 795 self.fieldsFirstRowSizer = wx.BoxSizer(wx.VERTICAL)
220     self.fieldsFirstRowSizer.Add(self.gameNameSizer,0,wx.ALL, 10)
221     self.fieldsFirstRowSizer.Add(self.gameHashSizer,0,wx.ALL, 10)
222     self.fieldsFirstRowSizer.Add(self.gameFileNameSizer,0,wx.ALL, 10)
223     self.fieldsFirstRowSizer.Add(self.gameFilePathSizer,0,wx.ALL, 10)
224     self.fieldsFirstRowSizer.Add(self.noIntroGameNameSizer,0,wx.ALL, 10)
225     self.fieldsFirstRowSizer.Add(self.noIntroSystemNameSizer,0,wx.ALL, 10)
226 nino.borges 976 self.fieldsFirstRowSizer.Add(self.tosecGameNameSizer,0,wx.ALL, 10)
227 nino.borges 795
228 nino.borges 979 def CreateArtworkPreviewSection(self):
229     preview_size = (300, 180)
230 nino.borges 795
231 nino.borges 979 boxSizer = wx.StaticBoxSizer(wx.VERTICAL, self.panel, "Box Art")
232     self.boxArtBitmap = wx.StaticBitmap(self.panel, -1, wx.Bitmap(*preview_size), size=preview_size)
233     boxSizer.Add(self.boxArtBitmap, 0, wx.ALL, 8)
234    
235     titleSizer = wx.StaticBoxSizer(wx.VERTICAL, self.panel, "Title Art")
236     self.titleArtBitmap = wx.StaticBitmap(self.panel, -1, wx.Bitmap(*preview_size), size=preview_size)
237     titleSizer.Add(self.titleArtBitmap, 0, wx.ALL, 8)
238    
239     ingameSizer = wx.StaticBoxSizer(wx.VERTICAL, self.panel, "Ingame Art")
240     self.ingameArtBitmap = wx.StaticBitmap(self.panel, -1, wx.Bitmap(*preview_size), size=preview_size)
241     ingameSizer.Add(self.ingameArtBitmap, 0, wx.ALL, 8)
242    
243     self.artworkPreviewSizer = wx.BoxSizer(wx.HORIZONTAL)
244     self.artworkPreviewSizer.Add(boxSizer, 0, wx.ALL, 10)
245     self.artworkPreviewSizer.Add(titleSizer, 0, wx.ALL, 10)
246     self.artworkPreviewSizer.Add(ingameSizer, 0, wx.ALL, 10)
247     self._clear_artwork_previews()
248    
249    
250 nino.borges 795 def MenuData(self):
251     return(("&Tools",
252 nino.borges 805 ("&Add Roms","Adds new Roms to a selected system.", self.OnAddNewRoms),
253 nino.borges 979 ("Add &Artwork","Adds artwork files to matched games for a selected system.", self.OnAddNewArtwork),
254 nino.borges 805 ("Import &NoIntro DAT", "Allows for the import of any of the No Intro DAT files.", self.OnImportNewNoIntroDat),
255 nino.borges 806 ("Import &TOSEC DAT", "Allows for the import of any of the TOSEC DAT files.", self.OnImportNewTosecDat)),
256 nino.borges 795 ("&Reports",
257     ("&Duplicates Report","Generates a report detailing duplicates that exist in your collection.", self.NothingYet)),
258     ("&Help",
259     ("&About", "Displays the About Window", self.NothingYet)))
260    
261     def CreateMenuBar(self):
262     menuBar = wx.MenuBar()
263     for eachMenuData in self.MenuData():
264     menuLabel = eachMenuData[0]
265     menuItems = eachMenuData[1:]
266     menuBar.Append(self.CreateMenu(menuItems), menuLabel)
267     self.SetMenuBar(menuBar)
268    
269     def CreateMenu(self, menuData):
270     menu = wx.Menu()
271     for eachLabel, eachStatus, eachHandler in menuData:
272     if not eachLabel:
273     menu.AppendSeparator()
274     continue
275     menuItem = menu.Append(-1, eachLabel, eachStatus)
276     self.Bind(wx.EVT_MENU, eachHandler, menuItem)
277     return menu
278    
279     def OnSystemSelected(self, evt):
280 nino.borges 976 if not evt.GetEventObject().GetValue():
281     evt.GetEventObject().SetValue(True)
282     return
283    
284 nino.borges 795 systemsButtonNamesList = list(self.systemButtonDict.keys())
285     systemsButtonNamesList.remove(evt.GetEventObject().GetLabelText())
286     for i in systemsButtonNamesList:
287     self.systemButtonDict[i].SetValue(False)
288    
289 nino.borges 976 system_key = self.buttonSystemMap.get(evt.GetEventObject().GetLabelText(), "SNES")
290 nino.borges 979 self._run_with_busy("Loading games for selected system...", self._load_games_for_system, system_key)
291 nino.borges 795
292    
293     def OnGameSelected(self, evt):
294     print(self.gameSelectionListBox.GetStringSelection())
295 nino.borges 976 selected = self.gameSelectionListBox.GetStringSelection()
296     if not selected:
297     return
298 nino.borges 795
299 nino.borges 976 gameData = Gromulus_Lib.GetSingleGameById(self.gamesListMatrix[selected], self.dbConnection)
300     if not gameData:
301     return
302 nino.borges 795
303 nino.borges 976 display_name = gameData.get("game_name") or gameData.get("no_intro_game") or gameData.get("tosec_game") or gameData.get("filename")
304     no_intro_name = gameData.get("no_intro_game")
305     no_intro_system = gameData.get("no_intro_system") or gameData.get("tosec_system")
306     tosec_name = gameData.get("tosec_game")
307    
308     self._set_text(self.gameNameTextCtrl, display_name)
309     self._set_text(self.gameHashTextCtrl, gameData.get("hash"))
310     self._set_text(self.gameFileNameTextCtrl, gameData.get("filename"))
311     self._set_text(self.gameFilePathTextCtrl, gameData.get("path"))
312     self._set_text(self.noIntroGameNameTextCtrl, no_intro_name)
313     self._set_text(self.noIntroSystemNameTextCtrl, no_intro_system)
314     self._set_text(self.tosecGameNameTextCtrl, tosec_name)
315 nino.borges 979 self._set_artwork_preview(self.boxArtBitmap, gameData.get("box_art_path"))
316     self._set_artwork_preview(self.titleArtBitmap, gameData.get("title_art_path"))
317     self._set_artwork_preview(self.ingameArtBitmap, gameData.get("ingame_art_path"))
318 nino.borges 976
319 nino.borges 795 def NothingYet(self, event):
320     diag = wx.MessageDialog(self, "Nothing here yet!", "Disabled...", wx.OK | wx.ICON_INFORMATION)
321     diag.ShowModal()
322     diag.Destroy()
323    
324 nino.borges 805 def OnImportNewNoIntroDat(self,event):
325 nino.borges 979 selected_system_id = self._choose_system("No-Intro DAT System Selection")
326 nino.borges 978 if not selected_system_id:
327     return
328    
329 nino.borges 805 dlg = wx.FileDialog(
330     self, message="Import New NoIntro DAT ...", defaultDir=os.getcwd(),
331     defaultFile="", wildcard="DAT files (*.dat)|*.dat", style=wx.FD_OPEN | wx.FD_CHANGE_DIR
332     )
333     if dlg.ShowModal() == wx.ID_OK:
334     datPath = dlg.GetPath()
335 nino.borges 978 Gromulus_Lib.ImportNewNoIntroDat(datPath, selected_system_id, self.dbConnection)
336 nino.borges 805 doneDiag = wx.MessageDialog(self, "NoIntro DAT imported","All Done!",wx.OK | wx.ICON_INFORMATION)
337     doneDiag.ShowModal()
338     doneDiag.Destroy()
339 nino.borges 795
340 nino.borges 806 def OnImportNewTosecDat(self,event):
341 nino.borges 979 selected_system_id = self._choose_system("TOSEC DAT System Selection")
342 nino.borges 978 if not selected_system_id:
343     return
344    
345 nino.borges 806 dlg = wx.FileDialog(
346     self, message="Import New Tosec DAT ...", defaultDir=os.getcwd(),
347     defaultFile="", wildcard="DAT files (*.dat)|*.dat", style=wx.FD_OPEN | wx.FD_CHANGE_DIR
348     )
349     if dlg.ShowModal() == wx.ID_OK:
350     datPath = dlg.GetPath()
351 nino.borges 978 Gromulus_Lib.ImportNewTosecDat(datPath, selected_system_id, self.dbConnection)
352 nino.borges 806 doneDiag = wx.MessageDialog(self, "Tosec DAT imported","All Done!",wx.OK | wx.ICON_INFORMATION)
353     doneDiag.ShowModal()
354     doneDiag.Destroy()
355    
356 nino.borges 979 def OnAddNewArtwork(self, event):
357     selected_system_id = self._choose_system("Artwork System Selection", only_with_roms=True)
358     if not selected_system_id:
359     return
360 nino.borges 806
361 nino.borges 979 artwork_type_map = {
362     "Game Box Art": "box",
363     "Game Title": "title",
364     "Game Ingame Screenshot": "ingame",
365     }
366     type_choices = list(artwork_type_map.keys())
367     type_dlg = wx.SingleChoiceDialog(
368     self,
369     "Select the type of artwork to ingest",
370     "Artwork Type",
371     type_choices,
372     )
373     if type_dlg.ShowModal() != wx.ID_OK:
374     return
375     selected_artwork_label = type_dlg.GetStringSelection()
376     artwork_type_key = artwork_type_map[selected_artwork_label]
377    
378     dry_run_dlg = wx.MessageDialog(
379     self,
380     "Run as dry run (preview only, no file copy and no DB updates)?",
381     "Artwork Import Mode",
382     wx.YES_NO | wx.ICON_QUESTION,
383     )
384     dry_run_mode = dry_run_dlg.ShowModal() == wx.ID_YES
385     dry_run_dlg.Destroy()
386    
387     dir_dlg = wx.DirDialog(
388     self,
389     f"Choose a directory containing {selected_artwork_label} artwork files:",
390     style=wx.DD_DEFAULT_STYLE,
391     )
392     if dir_dlg.ShowModal() != wx.ID_OK:
393     return
394     artwork_source_dir = dir_dlg.GetPath()
395    
396     matched_count, skipped_existing_count, unmatched_count, error_count = self._run_with_busy(
397     "Matching artwork files and updating game records...",
398     Gromulus_Lib.AddNewArtwork,
399     artwork_source_dir,
400     selected_system_id,
401     artwork_type_key,
402     self.dbConnection,
403     testOnly=dry_run_mode,
404     )
405     doneDiag = wx.MessageDialog(
406     self,
407     (
408     f"Mode: {'Dry Run (no files written)' if dry_run_mode else 'Import'}\n\n"
409     f"{matched_count} artwork files matched and imported.\n"
410     f"{skipped_existing_count} games skipped (artwork already set).\n"
411     f"{unmatched_count} games had no filename/title match.\n"
412     f"{error_count} files failed during copy/update."
413     ),
414     "Artwork import complete",
415     wx.OK | wx.ICON_INFORMATION,
416     )
417     doneDiag.ShowModal()
418     doneDiag.Destroy()
419    
420    
421 nino.borges 805 def OnAddNewRoms(self,event):
422 nino.borges 908 systemMatrix = Gromulus_Lib.GetSystemList(self.dbConnection)
423 nino.borges 805 systemsList = list(systemMatrix.keys())
424     dlg = wx.SingleChoiceDialog(self,
425     "Select system for new ROMs",
426     "List of systemss", systemsList)
427     if (dlg.ShowModal() == wx.ID_OK):
428     dlg2 = wx.DirDialog(self, "Choose a directory:",
429     style=wx.DD_DEFAULT_STYLE)
430     if (dlg2.ShowModal() == wx.ID_OK):
431     romsToImportPath = dlg2.GetPath()
432     systemID = systemMatrix[dlg.GetStringSelection()][0]
433 nino.borges 908 importedCount, alreadyExistsCount, errImportCount = Gromulus_Lib.AddNewRoms(romsToImportPath, systemID, self.dbConnection)#, testOnly=True)
434 nino.borges 805 #print(systemMatrix[dlg.GetStringSelection()])
435     #print(dlg2.GetPath())
436     doneDiag = wx.MessageDialog(self, f"{str(importedCount)} files imported.\n{str(alreadyExistsCount)} files not imported because you already have them.\n{str(errImportCount)} files imported because of errors.", "Rom import process complete",wx.OK | wx.ICON_INFORMATION)
437     doneDiag.ShowModal()
438     doneDiag.Destroy()
439    
440    
441    
442 nino.borges 795 class MyApp(wx.App):
443     def OnInit(self):
444 nino.borges 979 self.frame = MyFrame(None, -1, "Gromulus v1.3")
445 nino.borges 795 self.frame.Show(True)
446     self.SetTopWindow(self.frame)
447     return True
448 nino.borges 908 def OnExit(self):
449 nino.borges 910 if hasattr(self, "frame") and hasattr(self.frame, "db"):
450     self.frame.db.close()
451 nino.borges 908 print("Closed DB")
452     return 0
453 nino.borges 795
454    
455     if __name__ == '__main__':
456     app = MyApp(0)
457 nino.borges 976 app.MainLoop()