A tabbed viewer for Chris Edwards' ASL Vehicle/Gun data cards.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
asl-cards/main_window.py

253 lines
9.9 KiB

import sys
import os
from PyQt5.QtCore import Qt , QPoint , QSize
from PyQt5.QtWidgets import QApplication , QMainWindow , QVBoxLayout , QHBoxLayout , QWidget , QTabWidget , QLabel , QMenu
from PyQt5.QtWidgets import QMessageBox , QAction
from PyQt5.QtGui import QPainter , QPixmap , QIcon , QBrush
import asl_cards.db as db
from asl_cards import natinfo
from constants import *
import globals
from add_card_widget import AddCardWidget
from startup_widget import StartupWidget
from about_widget import AboutWidget
# ---------------------------------------------------------------------
class AslCardWidget( QWidget ) :
"""Simple widget that displays the image for an ASL Card."""
def __init__( self , card ) :
# initialize
super().__init__()
self.card = card
self.pixmap = QPixmap()
self.pixmap.loadFromData( card.card_image.image_data )
def paintEvent( self , evt ) :
qp = QPainter()
qp.begin( self )
qp_size = evt.rect().size()
# draw the AslCard image
qp.setRenderHint( QPainter.Antialiasing )
pixmap_size = self.pixmap.size()
pixmap_size.scale( qp_size , Qt.KeepAspectRatio )
pixmap = self.pixmap.scaled( pixmap_size , Qt.KeepAspectRatio , Qt.SmoothTransformation )
qp.drawPixmap(
(qp_size.width()-pixmap_size.width())/2 , (qp_size.height()-pixmap_size.height())/2 ,
pixmap
)
qp.end()
# ---------------------------------------------------------------------
class MainWindow( QMainWindow ) :
_instance = None
def __init__( self , db_fname ) :
# initialize
super().__init__()
assert MainWindow._instance is None
MainWindow._instance = self
# initialize the window
self.setWindowTitle( APP_NAME )
self.setWindowIcon( QIcon("resources/app.ico") )
self.setMinimumSize( 600 , 400 )
# initialize the menu
menu_bar = self.menuBar()
file_menu = menu_bar.addMenu( "&File" )
self.add_card_action = QAction( "&Add" , self )
self.add_card_action.setEnabled( False )
self.add_card_action.setShortcut( "Ctrl+A" )
self.add_card_action.setStatusTip( "Add an ASL Card." )
self.add_card_action.triggered.connect( self.on_add_card )
file_menu.addAction( self.add_card_action )
self.close_tab_action = QAction( "&Close" , self )
self.close_tab_action.setShortcut( "Ctrl+W" )
self.close_tab_action.setStatusTip( "Close the current tab." )
self.close_tab_action.triggered.connect( self.on_close_tab )
file_menu.addAction( self.close_tab_action )
action = QAction( "E&xit" , self )
action.setStatusTip( "Close the program." )
action.triggered.connect( self.close )
file_menu.addAction( action )
self.help_menu = menu_bar.addMenu( "&Help" )
about_action = QAction( "&About" , self )
about_action.setStatusTip( "About this program." )
about_action.triggered.connect( self.on_about )
self.help_menu.addAction( about_action )
# restore the window geometry
buf = globals.app_settings.value( MAINWINDOW_GEOMETRY )
if buf :
self.restoreGeometry( buf )
else :
self.resize( 1 , 1 ) # nb: the layout manager will set the correct size
# 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
widget = self.tab_widget.widget( index )
if type(widget) is AslCardWidget and widget.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."""
# we can now close the startup widget and replace it with the main tab widget
self.tab_widget = QTabWidget( self )
self.tab_widget.setTabsClosable( True )
self.tab_widget.setMovable( True )
self.tab_widget.tabCloseRequested.connect( self.on_tab_close_requested )
self.setCentralWidget( self.tab_widget )
# open the database
db.open_database( db_fname , False )
globals.cards = db.load_cards()
# show the View menu
self.view_menu = QMenu( "&View" )
self.menuBar().insertMenu( self.help_menu.menuAction() , self.view_menu )
self.view_menu.aboutToShow.connect( self.on_about_to_show_view_menu )
# ask the user to add the first card
self.add_card_action.setEnabled( True )
self.on_add_card()
@staticmethod
def show_info_msg( msg ) :
"""Show an informational message."""
QMessageBox.information( None , APP_NAME , msg )
@staticmethod
def show_error_msg( msg ) :
"""Show an error message."""
QMessageBox.warning( None , APP_NAME , msg )
@staticmethod
def ask( msg , buttons , default ) :
"""Ask the user a question."""
return QMessageBox.question( None , APP_NAME , msg , buttons , default )
def _update_ui( self ) :
"""Update the window's UI."""
self.close_tab_action.setEnabled( self.tab_widget.count() > 0 if self.tab_widget else False )
def _find_add_card_tab( self ) :
"""Find the "add card" tab."""
if self.tab_widget :
for i in range(0,self.tab_widget.count()) :
if type( self.tab_widget.widget(i) ) is AddCardWidget :
return i
return None
def on_tab_close_requested( self , index ) :
"""Handle the tab close request."""
self.tab_widget.removeTab( index )
def closeEvent( self , evt ) :
"""Handle window close."""
# confirm the close
widget = self.centralWidget()
if type(widget) is StartupWidget :
# don't allow this if we are analyzing files
if widget.analyze_thread :
MainWindow.show_info_msg( "Please cancel the analysis first." )
QApplication.beep()
evt.ignore()
return
else :
# check if we should confirm the exit
if globals.app_settings.value( CONFIRM_EXIT , True , type=bool ) :
rc = self.ask( "Do you want to the close the program?" ,
QMessageBox.Ok | QMessageBox.Cancel ,
QMessageBox.Cancel
)
if rc != QMessageBox.Ok :
evt.ignore()
# save the window settings
globals.app_settings.setValue( MAINWINDOW_GEOMETRY , self.saveGeometry() )
def keyPressEvent( self , evt ) :
"""Handle key-presses."""
if evt.key() == Qt.Key_Escape and globals.debug_settings.value("Debug/AllowEscapeToClose",type=bool) :
self.close()
def on_add_card( self ) :
"""Ask the user to select a new ASL card to show."""
# check if the "add card" tab is already open
index = self._find_add_card_tab()
if index is not None :
# yup - switch to it
self.tab_widget.setCurrentIndex( index )
return
# nope - open it
widget = AddCardWidget( self )
widget.accepted_signal.connect( self.on_add_card_accepted )
widget.cancelled_signal.connect( self.on_add_card_cancelled )
index = self.tab_widget.insertTab(
self.tab_widget.currentIndex() + 1 ,
widget , "(new card)"
)
self.tab_widget.setCurrentIndex( index )
self._update_ui()
widget.lb_cards.setFocus()
def on_add_card_accepted( self , card ) :
"""Handle the user's card selection."""
index = self._find_add_card_tab()
assert index is not None
self.tab_widget.removeTab( index )
fname = natinfo.get_flag( card.nationality )
if fname :
widget = self.tab_widget.insertTab( index , AslCardWidget(card) , QIcon(fname) , card.name )
else :
widget = self.tab_widget.insertTab( index , AslCardWidget(card) , card.name )
self.tab_widget.setCurrentIndex( index )
self._update_ui()
def on_add_card_cancelled( self ) :
"""Cancel the "add card" widget."""
index = self._find_add_card_tab()
assert index is not None
self.tab_widget.removeTab( index )
self._update_ui()
def on_close_tab( self ) :
"""Close the current tab."""
index = self.tab_widget.currentIndex()
if index < 0 :
QApplication.beep()
return
self.tab_widget.removeTab( index )
self._update_ui()
def on_about( self ) :
"""Show an About box."""
AboutWidget( self ).exec_()