1616import webbrowser
1717from datetime import datetime as dt
1818from pathlib import Path
19- from queue import Empty , Queue
19+ from queue import Queue
2020from typing import Optional
2121
2222from PIL import Image
4646)
4747from humanfriendly import format_timespan
4848
49+ from src .core .enums import SettingItems
4950from src .core .library import ItemType
5051from src .core .ts_core import (
5152 PLAINTEXT_TYPES ,
8889from src .qt .modals .fix_unlinked import FixUnlinkedEntriesModal
8990from src .qt .modals .fix_dupes import FixDupeFilesModal
9091from src .qt .modals .folders_to_tags import FoldersToTagsModal
91- import src .qt .resources_rc
92+
93+ # this import has side-effect of import PySide resources
94+ import src .qt .resources_rc # pylint: disable=unused-import
9295
9396# SIGQUIT is not defined on Windows
9497if sys .platform == "win32" :
@@ -282,6 +285,7 @@ def start(self):
282285 edit_menu = QMenu ("&Edit" , menu_bar )
283286 tools_menu = QMenu ("&Tools" , menu_bar )
284287 macros_menu = QMenu ("&Macros" , menu_bar )
288+ window_menu = QMenu ("&Window" , menu_bar )
285289 help_menu = QMenu ("&Help" , menu_bar )
286290
287291 # File Menu ============================================================
@@ -376,6 +380,18 @@ def start(self):
376380 tag_database_action .triggered .connect (lambda : self .show_tag_database ())
377381 edit_menu .addAction (tag_database_action )
378382
383+ check_action = QAction ("Open library on start" , self )
384+ check_action .setCheckable (True )
385+ check_action .setChecked (
386+ self .settings .value (SettingItems .START_LOAD_LAST , True , type = bool )
387+ )
388+ check_action .triggered .connect (
389+ lambda checked : self .settings .setValue (
390+ SettingItems .START_LOAD_LAST , checked
391+ )
392+ )
393+ window_menu .addAction (check_action )
394+
379395 # Tools Menu ===========================================================
380396 fix_unlinked_entries_action = QAction ("Fix &Unlinked Entries" , menu_bar )
381397 fue_modal = FixUnlinkedEntriesModal (self .lib , self )
@@ -423,6 +439,20 @@ def start(self):
423439 macros_menu .addAction (self .sort_fields_action )
424440
425441 folders_to_tags_action = QAction ("Create Tags From Folders" , menu_bar )
442+ show_libs_list_action = QAction ("Show Recent Libraries" , menu_bar )
443+ show_libs_list_action .setCheckable (True )
444+ show_libs_list_action .setChecked (
445+ self .settings .value (SettingItems .WINDOW_SHOW_LIBS , True , type = bool )
446+ )
447+ show_libs_list_action .triggered .connect (
448+ lambda checked : (
449+ self .settings .setValue (SettingItems .WINDOW_SHOW_LIBS , checked ),
450+ self .toggle_libs_list (checked ),
451+ )
452+ )
453+ window_menu .addAction (show_libs_list_action )
454+
455+ folders_to_tags_action = QAction ("Folders to Tags" , menu_bar )
426456 ftt_modal = FoldersToTagsModal (self .lib , self )
427457 folders_to_tags_action .triggered .connect (lambda : ftt_modal .show ())
428458 macros_menu .addAction (folders_to_tags_action )
@@ -440,6 +470,7 @@ def start(self):
440470 menu_bar .addMenu (edit_menu )
441471 menu_bar .addMenu (tools_menu )
442472 menu_bar .addMenu (macros_menu )
473+ menu_bar .addMenu (window_menu )
443474 menu_bar .addMenu (help_menu )
444475
445476 self .preview_panel = PreviewPanel (self .lib , self )
@@ -458,6 +489,31 @@ def start(self):
458489 self .thumb_renderers : list [ThumbRenderer ] = []
459490 self .collation_thumb_size = math .ceil (self .thumb_size * 2 )
460491
492+ self .init_library_window ()
493+
494+ lib = None
495+ if self .args .open :
496+ lib = self .args .open
497+ elif self .settings .value (SettingItems .START_LOAD_LAST , True , type = bool ):
498+ lib = self .settings .value (SettingItems .LAST_LIBRARY )
499+
500+ if lib :
501+ self .splash .showMessage (
502+ f'Opening Library "{ lib } "...' ,
503+ int (Qt .AlignmentFlag .AlignBottom | Qt .AlignmentFlag .AlignHCenter ),
504+ QColor ("#9782ff" ),
505+ )
506+ self .open_library (lib )
507+
508+ if self .args .ci :
509+ # gracefully terminate the app in CI environment
510+ self .thumb_job_queue .put ((self .SIGTERM .emit , []))
511+
512+ app .exec ()
513+
514+ self .shutdown ()
515+
516+ def init_library_window (self ):
461517 self ._init_thumb_grid ()
462518
463519 # TODO: Put this into its own method that copies the font file(s) into memory
@@ -510,31 +566,12 @@ def start(self):
510566 self .splash .finish (self .main_window )
511567 self .preview_panel .update_widgets ()
512568
513- # Check if a library should be opened on startup, args should override last_library
514- # TODO: check for behavior (open last, open default, start empty)
515- if (
516- self .args .open
517- or self .settings .contains ("last_library" )
518- and os .path .isdir (self .settings .value ("last_library" ))
519- ):
520- if self .args .open :
521- lib = self .args .open
522- elif self .settings .value ("last_library" ):
523- lib = self .settings .value ("last_library" )
524- self .splash .showMessage (
525- f'Opening Library "{ lib } "...' ,
526- int (Qt .AlignmentFlag .AlignBottom | Qt .AlignmentFlag .AlignHCenter ),
527- QColor ("#9782ff" ),
528- )
529- self .open_library (lib )
530-
531- if self .args .ci :
532- # gracefully terminate the app in CI environment
533- self .thumb_job_queue .put ((self .SIGTERM .emit , []))
534-
535- app .exec ()
536-
537- self .shutdown ()
569+ def toggle_libs_list (self , value : bool ):
570+ if value :
571+ self .preview_panel .libs_flow_container .show ()
572+ else :
573+ self .preview_panel .libs_flow_container .hide ()
574+ self .preview_panel .update ()
538575
539576 def callback_library_needed_check (self , func ):
540577 """Check if loaded library has valid path before executing the button function"""
@@ -548,7 +585,7 @@ def shutdown(self):
548585 """Save Library on Application Exit"""
549586 if self .lib .library_dir :
550587 self .save_library ()
551- self .settings .setValue ("last_library" , self .lib .library_dir )
588+ self .settings .setValue (SettingItems . LAST_LIBRARY , self .lib .library_dir )
552589 self .settings .sync ()
553590 logging .info ("[SHUTDOWN] Ending Thumbnail Threads..." )
554591 for _ in self .thumb_threads :
@@ -597,7 +634,7 @@ def close_library(self):
597634 self .main_window .statusbar .showMessage (f"Closing & Saving Library..." )
598635 start_time = time .time ()
599636 self .save_library (show_status = False )
600- self .settings .setValue ("last_library" , self .lib .library_dir )
637+ self .settings .setValue (SettingItems . LAST_LIBRARY , self .lib .library_dir )
601638 self .settings .sync ()
602639
603640 self .lib .clear_internal_vars ()
@@ -709,7 +746,7 @@ def add_new_files_callback(self):
709746 iterator .value .connect (lambda x : pw .update_progress (x + 1 ))
710747 iterator .value .connect (
711748 lambda x : pw .update_label (
712- f'Scanning Directories for New Files...\n { x + 1 } File{ "s" if x + 1 != 1 else "" } Searched, { len (self .lib .files_not_in_library )} New Files Found'
749+ f'Scanning Directories for New Files...\n { x + 1 } File{ "s" if x + 1 != 1 else "" } Searched, { len (self .lib .files_not_in_library )} New Files Found'
713750 )
714751 )
715752 r = CustomRunnable (lambda : iterator .run ())
@@ -760,7 +797,7 @@ def add_new_files_runnable(self):
760797 iterator .value .connect (lambda x : pw .update_progress (x + 1 ))
761798 iterator .value .connect (
762799 lambda x : pw .update_label (
763- f"Running Configured Macros on { x + 1 } /{ len (new_ids )} New Entries"
800+ f"Running Configured Macros on { x + 1 } /{ len (new_ids )} New Entries"
764801 )
765802 )
766803 r = CustomRunnable (lambda : iterator .run ())
@@ -1297,6 +1334,38 @@ def filter_items(self, query=""):
12971334
12981335 # self.update_thumbs()
12991336
1337+ def remove_recent_library (self , item_key : str ):
1338+ self .settings .beginGroup (SettingItems .LIBS_LIST )
1339+ self .settings .remove (item_key )
1340+ self .settings .endGroup ()
1341+ self .settings .sync ()
1342+
1343+ def update_libs_list (self , path : str | Path ):
1344+ """add library to list in SettingItems.LIBS_LIST"""
1345+ ITEMS_LIMIT = 5
1346+ path = Path (path )
1347+
1348+ self .settings .beginGroup (SettingItems .LIBS_LIST )
1349+
1350+ all_libs = {str (time .time ()): str (path )}
1351+
1352+ for item_key in self .settings .allKeys ():
1353+ item_path = self .settings .value (item_key )
1354+ if Path (item_path ) != path :
1355+ all_libs [item_key ] = item_path
1356+
1357+ # sort items, most recent first
1358+ all_libs = sorted (all_libs .items (), key = lambda item : item [0 ], reverse = True )
1359+
1360+ # remove previously saved items
1361+ self .settings .clear ()
1362+
1363+ for item_key , item_value in all_libs [:ITEMS_LIMIT ]:
1364+ self .settings .setValue (item_key , item_value )
1365+
1366+ self .settings .endGroup ()
1367+ self .settings .sync ()
1368+
13001369 def open_library (self , path ):
13011370 """Opens a TagStudio library."""
13021371 if self .lib .library_dir :
@@ -1314,7 +1383,7 @@ def open_library(self, path):
13141383 # self.lib.refresh_missing_files()
13151384 # title_text = f'{self.base_title} - Library \'{self.lib.library_dir}\''
13161385 # self.main_window.setWindowTitle(title_text)
1317- pass
1386+ self . update_libs_list ( path )
13181387
13191388 else :
13201389 logging .info (
0 commit comments