File indexing completed on 2024-04-21 04:01:50

0001 #!/usr/bin/env python3
0002 # -*- coding: utf-8 -*-
0003 
0004 """
0005 Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de>
0006 
0007 SPDX-License-Identifier: GPL-2.0
0008 
0009 """
0010 
0011 # pylint: disable=wrong-import-position
0012 
0013 # keyboardinterrupt should simply terminate
0014 # import signal
0015 # signal.signal(signal.SIGINT, signal.SIG_DFL)
0016 import sys
0017 import os
0018 import logging
0019 
0020 from qt import QObject, QCommandLineParser, QCommandLineOption, Qt, QGuiApplication
0021 from qtpy import QT5
0022 from kde import KApplication
0023 from mi18n import i18n, MLocale
0024 
0025 from common import Options, SingleshotOptions, Internal, Debug
0026 # do not import modules using twisted before our reactor is running
0027 
0028 def initRulesets():
0029     """exits if user only wanted to see available rulesets"""
0030     import predefined
0031     predefined.load()
0032     if Options.showRulesets or Options.rulesetName:
0033         from rule import Ruleset
0034         rulesets = {x.name: x for x in Ruleset.selectableRulesets()}
0035         if Options.showRulesets:
0036             for name in rulesets:
0037                 print(name)
0038             Internal.db.close()
0039             sys.exit(0)
0040         elif Options.rulesetName in rulesets:
0041             # we have an exact match
0042             Options.ruleset = rulesets[Options.rulesetName]
0043         else:
0044             matches = [x for x in rulesets if Options.rulesetName in x]
0045             if len(matches) != 1:
0046                 if not matches:
0047                     msg = 'Ruleset %s is unknown' % Options.rulesetName
0048                 else:
0049                     msg = 'Ruleset %s is ambiguous: %s' % (
0050                         Options.rulesetName,
0051                         ', '.join(matches))
0052                 Internal.db.close()
0053                 raise SystemExit(msg)
0054             Options.ruleset = rulesets[matches[0]]
0055 
0056 
0057 class CommandLineOption(QCommandLineOption):
0058     """add some helping attributes"""
0059     def __init__(self, name, description, valueName=None,
0060         defaultValue=None, optName=None, argType=None, singleshot=False):
0061         QCommandLineOption.__init__(self, [name], description, valueName, defaultValue)
0062         if argType is None:
0063             if valueName is None:
0064                 argType = bool
0065             else:
0066                 argType = str
0067         self.argType = argType
0068         self.optName = optName or name
0069         self.singleshot = singleshot
0070 
0071 def defineOptions():
0072     """define command line options"""
0073     parser = QCommandLineParser()
0074     options = []
0075     def option(*args, optName=None, argType=None, singleshot=False):
0076         """helper"""
0077         opt = CommandLineOption(*args, optName=optName, argType=argType, singleshot=singleshot)
0078         options.append(opt)
0079         parser.addOption(opt)
0080 
0081     parser.setApplicationDescription(i18n('Mah Jongg - the ancient Chinese board game for 4 players'))
0082 
0083     parser.addHelpOption()
0084     parser.addVersionOption()
0085     option('playopen', i18n('all robots play with visible concealed tiles'), optName='playOpen')
0086     option('demo', i18n('start with demo mode'))
0087     option('host', i18n("login to HOST"), 'HOST', '')
0088     option('table', i18n('start new TABLE'), 'TABLE', '1', argType=int, singleshot=True)
0089     option('join', i18n('join TABLE'), 'TABLE', '1', argType=int, singleshot=True)
0090     option('ruleset', i18n('use RULESET without asking'), 'RULESET', '', optName='rulesetName')
0091     option('rounds', i18n('play only ROUNDS rounds per game. Only for debugging!'), 'ROUNDS', '4', argType=int)
0092     option('player', i18n('prefer PLAYER for next login'), 'PLAYER', '')
0093     option('ai', i18n('use AI variant for human player in demo mode'), 'AI', '', optName='AI')
0094     option('csv', i18n('write statistics to CSV'), 'CSV', '')
0095     option('rulesets', i18n('show all available rulesets'), optName='showRulesets')
0096     option('game', i18n('for testing purposes: Initializes the random generator'),
0097            'seed(/firsthand)(..(lasthand))', '0')
0098     option('nogui', i18n('show no graphical user interface. Intended only for testing'), optName='gui')
0099     option('socket', i18n('use a dedicated server listening on SOCKET. Intended only for testing'), 'SOCKET', '')
0100     option('port', i18n('use a dedicated server listening on PORT. Intended only for testing'), 'PORT', '')
0101     option('debug', Debug.help(), 'DEBUG', '')
0102     return parser, options
0103 
0104 def parseOptions():
0105     """parse command line options and save the values"""
0106     Options.gui = True
0107     parser, options = defineOptions()
0108     parser.process(Internal.app)
0109     for option in options:
0110         if parser.isSet(option):
0111             value = parser.value(option)
0112             if option.optName == 'debug':
0113                 msg = Debug.setOptions(value)
0114                 if msg:
0115                     Internal.logger.debug(msg)
0116                     logging.shutdown()
0117                     sys.exit(2)
0118                 continue
0119             if option.optName in SingleshotOptions.__dict__:
0120                 target = SingleshotOptions
0121             else:
0122                 target = Options
0123             if option.argType is bool:
0124                 setattr(target, option.optName, not option.names()[0].startswith('no'))
0125             elif option.argType is int:
0126                 setattr(target, option.optName, int(value))
0127             else:
0128                 setattr(target, option.optName, value)
0129 
0130     Options.demo |= not Options.gui
0131     Internal.autoPlay = Options.demo
0132 
0133     from query import initDb
0134     if not initDb():
0135         raise SystemExit('Cannot initialize database')
0136     initRulesets()
0137     Options.fixed = True  # may not be changed anymore
0138 
0139 
0140 class EvHandler(QObject):
0141 
0142     """an application wide event handler"""
0143 
0144     def eventFilter(self, receiver, event):
0145         """will be called for all events"""
0146         from log import EventData
0147         EventData(receiver, event)
0148         return QObject.eventFilter(self, receiver, event)
0149 
0150 from util import gitHead
0151 
0152 if os.name == 'nt':
0153     _ = os.path.dirname(os.path.realpath(__file__))
0154     if _.endswith('.zip'):
0155         # cx_freeze
0156         os.chdir(os.path.dirname(_))
0157 
0158 QGuiApplication.setAttribute(Qt.AA_ShareOpenGLContexts, True)
0159 Internal.app = KApplication()
0160 parseOptions()
0161 
0162 if hasattr(QGuiApplication, 'setDesktopFileName'):
0163     QGuiApplication.setDesktopFileName('org.kde.kajongg')
0164 
0165 if Debug.neutral:
0166     MLocale.translation = None
0167 
0168 if Debug.locate:
0169     # this has been read before Debug.locate is set
0170     Internal.logger.debug('Configuration in %s', Internal.kajonggrc.path)
0171 
0172 if Debug.events:
0173     EVHANDLER = EvHandler()
0174     Internal.app.installEventFilter(EVHANDLER)
0175 
0176 from config import SetupPreferences
0177 SetupPreferences()
0178 
0179 if Options.csv:
0180     if gitHead() == 'current':
0181         Internal.logger.debug(
0182             'You cannot write to %s with changes uncommitted to git',
0183             Options.csv)
0184         sys.exit(2)
0185 from mainwindow import MainWindow
0186 if QT5:
0187     QGuiApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
0188 MainWindow()
0189 Internal.app.exec_()