From dfe63375debde24f333abaae98ffcb3a09b35667 Mon Sep 17 00:00:00 2001 From: Taka Date: Mon, 1 May 2017 01:49:17 +0000 Subject: [PATCH] Allow keyboard accelerators to cycle through a nationality's cards. --- asl_cards/natinfo.py | 28 +++++++++++++--- asl_cards/natinfo/natinfo.json | 10 +++++- asl_cards/tests/natinfo-data/natinfo.json | 11 +++++-- asl_cards/tests/test_natinfo.py | 22 ++++++++++++- main_window.py | 40 +++++++++++++++++++++-- 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/asl_cards/natinfo.py b/asl_cards/natinfo.py index cbb07bd..cd6dec5 100644 --- a/asl_cards/natinfo.py +++ b/asl_cards/natinfo.py @@ -10,12 +10,16 @@ _base_dir = None # --------------------------------------------------------------------- -def _get_key( nat ) : +def _make_key( nat ) : """Get the JSON key for a nationality. We require the JSON keys to be lower-case, with no spaces (not strictly necessary, but not a bad idea :-/). """ - return nat.lower().replace( " " , "-" ) + return nat.lower().replace( " " , "-" ) if nat else None + +def display_name_from_key( key ) : + """Get the nationality display string from a JSON key.""" + return key.replace( "-" , " " ).capitalize() # --------------------------------------------------------------------- @@ -35,16 +39,32 @@ def dump() : for nat in sorted(_nat_info.keys()) : print( "{}:".format( nat ) ) print( "- flag = {}".format( get_flag(nat) ) ) + print( "- accel = {}".format( get_accel_for_nat(nat) ) ) def get_flag( nat ) : """Locate the flag image file for a nationality. These are set in the JSON data file at $/{nat}/flag. If there is no entry, the default is $/flags/${nat}.png """ - key = _get_key( nat ) + key = _make_key( nat ) try : fname = _nat_info[ key ][ "flag" ] except ( KeyError , TypeError ) : - fname = key + ".png" + fname = "{}.png".format( key ) fname = os.path.join( _base_dir , os.path.join("flags", fname) ) return fname if os.path.isfile(fname) else None + +def get_nats_for_accel( ch ) : + """Get the nationalities for an accelerator key.""" + if ch is None : return None + ch = ch.strip().lower() + if not ch : return None + return [ nat for nat,vals in _nat_info.items() if vals.get("accelerator","").lower() == ch ] + +def get_accel_for_nat( nat ) : + """Get the accelerator key for a nationality.""" + key = _make_key( nat ) + try : + return _nat_info[ key ][ "accelerator" ].lower() + except ( KeyError , TypeError ) : + return None diff --git a/asl_cards/natinfo/natinfo.json b/asl_cards/natinfo/natinfo.json index 0562f2b..0eca956 100644 --- a/asl_cards/natinfo/natinfo.json +++ b/asl_cards/natinfo/natinfo.json @@ -4,6 +4,7 @@ } , "american": { + "accelerator": "U" } , "axis-minor": { @@ -14,6 +15,7 @@ } , "british": { + "accelerator": "B" } , "bulgarian": { @@ -21,6 +23,7 @@ } , "chinese": { + "accelerator": "C" } , "croatian": { @@ -36,13 +39,16 @@ } , "finnish": { - "flag": "axis-minor.png" + "flag": "axis-minor.png" , + "accelerator": "F" } , "french": { + "accelerator": "F" } , "german": { + "accelerator": "G" } , "greek": { @@ -57,6 +63,7 @@ } , "japanese": { + "accelerator": "J" } , "polish": { @@ -68,6 +75,7 @@ } , "russian": { + "accelerator": "R" } , "slovakian": { diff --git a/asl_cards/tests/natinfo-data/natinfo.json b/asl_cards/tests/natinfo-data/natinfo.json index d6af812..5ab0b07 100644 --- a/asl_cards/tests/natinfo-data/natinfo.json +++ b/asl_cards/tests/natinfo-data/natinfo.json @@ -1,6 +1,7 @@ { "german": { - "_comment_": "this nationality has no flag" + "_comment_": "this nationality has no flag" , + "accelerator": "g" } , "russian": { @@ -8,7 +9,13 @@ } , "american" :{ - "_comment_": "this nationality has a default flag" + "_comment_": "this nationality has a default flag" , + "accelerator": "a" + } , + + "australian" :{ + "_comment_": "this nationality has the same accelerator as the americans" , + "accelerator": "A" } , "japanese" :{ diff --git a/asl_cards/tests/test_natinfo.py b/asl_cards/tests/test_natinfo.py index f2328be..58a35f2 100755 --- a/asl_cards/tests/test_natinfo.py +++ b/asl_cards/tests/test_natinfo.py @@ -21,10 +21,30 @@ class TestNatInfo( TestCaseBase ) : def test_flags( self ) : """Test locating the flag image files for each nationality.""" - self.assertIsNone( natinfo.get_flag( "xxx" ) ) self.assertIsNone( natinfo.get_flag( "german" ) ) self.assertTrue( natinfo.get_flag("american").endswith( "/flags/american.png" ) ) self.assertTrue( natinfo.get_flag("japanese").endswith( "/flags/japanese-flag.gif" ) ) + self.assertIsNone( natinfo.get_flag( "_unknown_" ) ) + self.assertIsNone( natinfo.get_flag( "" ) ) + self.assertIsNone( natinfo.get_flag( None ) ) + + def test_accelerators( self ) : + """Test nationality accelerators.""" + # test getting the nationalities that use a given accelerator key + self.assertEqual( natinfo.get_nats_for_accel("G") , ["german"] ) + self.assertEqual( natinfo.get_nats_for_accel("g") , ["german"] ) + self.assertEqual( natinfo.get_nats_for_accel("a") , ["american","australian"] ) + self.assertEqual( natinfo.get_nats_for_accel("russian") , [] ) + self.assertEqual( natinfo.get_nats_for_accel("_") , [] ) + self.assertIsNone( natinfo.get_nats_for_accel( "" ) ) + self.assertIsNone( natinfo.get_nats_for_accel( None ) ) + # test getting the accelerator key for a given nationality + self.assertEqual( natinfo.get_accel_for_nat("german") , "g" ) + self.assertEqual( natinfo.get_accel_for_nat("american") , "a" ) + self.assertEqual( natinfo.get_accel_for_nat("australian") , "a" ) + self.assertIsNone( natinfo.get_accel_for_nat( "" ) ) + self.assertIsNone( natinfo.get_accel_for_nat( None ) ) + # --------------------------------------------------------------------- diff --git a/main_window.py b/main_window.py index 85267d1..33ecc80 100644 --- a/main_window.py +++ b/main_window.py @@ -73,15 +73,51 @@ class MainWindow( QMainWindow ) : action.setStatusTip( "Close the program." ) action.triggered.connect( self.close ) file_menu.addAction( action ) - self.tab_widget = None - self._update_ui() + # initialize the menu + self.view_menu = menu_bar.addMenu( "&View" ) + self.view_menu.aboutToShow.connect( self.on_about_to_show_view_menu ) # load the window settings self.resize( globals.app_settings.value( MAINWINDOW_SIZE , QSize(500,300) ) ) self.move( globals.app_settings.value( MAINWINDOW_POSITION , QPoint(200,200) ) ) # show the startup form + self.tab_widget = None self.setCentralWidget( StartupWidget( db_fname , parent=self ) ) + self._update_ui() + + def on_about_to_show_view_menu( self ) : + # figure out what nationalities are currently open + nats = [] + for i in range(0,self.tab_widget.count()) : + widget = self.tab_widget.widget( i ) + if type(widget) is not AslCardWidget : continue + card = widget.card + if card.nationality not in nats : + nats.append( card.nationality ) + # rebuild the View menu + def cycle( nat ) : + # cycle to the nationality's next card + index = start_index = self.tab_widget.currentIndex() + while True : + index = (index + 1) % self.tab_widget.count() + if index == start_index : + break + card = self.tab_widget.widget( index ).card + if card.nationality == nat : + self.tab_widget.setCurrentIndex( index ) + break + self.view_menu.clear() + for nat in nats : + action = QAction( "Next {} card".format(nat) , self ) + fname = natinfo.get_flag( nat ) + if fname : + action.setIcon( QIcon(fname) ) + accel = natinfo.get_accel_for_nat( nat ) + if accel : + action.setShortcut( "Ctrl+{}".format( accel ) ) + action.triggered.connect( lambda qthack,nat=nat: cycle(nat) ) + self.view_menu.addAction( action ) def start_main_app( self , db_fname ) : """Start the main app."""