File indexing completed on 2024-04-21 04:01:48
0001 # -*- coding: utf-8 -*- 0002 0003 """ 0004 Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de> 0005 0006 SPDX-License-Identifier: GPL-2.0 0007 0008 """ 0009 0010 # pylint: disable=invalid-name,W0611 0011 # invalid names, unused imports 0012 0013 import inspect 0014 0015 from twisted.internet.defer import Deferred, succeed 0016 0017 from kde import KMessageBox, KDialog 0018 0019 from qt import Qt, QDialog, QMessageBox, QWidget 0020 0021 from common import Options, Internal, isAlive, StrMixin 0022 0023 0024 class IgnoreEscape: 0025 0026 """as the name says. Use as a mixin for dialogs""" 0027 0028 def keyPressEvent(self, event): 0029 """catch and ignore the Escape key""" 0030 if event.key() == Qt.Key_Escape: 0031 event.ignore() 0032 else: 0033 # pass on to the first declared ancestor class which 0034 # currently is either KDialog or QDialog 0035 self.__class__.__mro__[1].keyPressEvent(self, event) 0036 0037 0038 class KDialogIgnoringEscape(KDialog, IgnoreEscape): 0039 0040 """as the name says""" 0041 0042 0043 class MustChooseKDialog(KDialogIgnoringEscape): 0044 0045 """this dialog can only be closed if a choice has been done. Currently, 0046 the self.chosen thing is not used, code removed. 0047 So this dialog can only be closed by calling accept() or reject()""" 0048 0049 def __init__(self): 0050 parent = Internal.mainWindow # default 0051 # if we are (maybe indirectly) called from a method belonging to a QWidget, take that as parent 0052 # this does probably not work for classmethod or staticmethod but it is 0053 # good enough right now 0054 for frametuple in inspect.getouterframes(inspect.currentframe())[1:]: 0055 if 'self' in frametuple[0].f_locals: 0056 obj = frametuple[0].f_locals['self'] 0057 if isinstance(obj, QWidget) and not isinstance(obj, QDialog) and isAlive(obj): 0058 parent = obj 0059 break 0060 if not isAlive(parent): 0061 parent = None 0062 KDialogIgnoringEscape.__init__(self, parent) 0063 0064 def closeEvent(self, event): # pylint: disable=no-self-use 0065 """self.chosen is currently not used, never allow this""" 0066 event.ignore() 0067 0068 0069 class Prompt(MustChooseKDialog, StrMixin): 0070 0071 """common code for things like QuestionYesNo, Information""" 0072 0073 def __init__(self, msg, icon=QMessageBox.Information, 0074 buttons=KDialog.Ok, caption=None, default=None): 0075 """buttons is button codes or-ed like KDialog.Ok | KDialog.Cancel. First one is default.""" 0076 if r'\n' in msg: 0077 print(r'*********************** Fix this! Prompt gets \n in', msg) 0078 msg = msg.replace(r'\n', '\n') 0079 self.msg = msg 0080 self.default = default 0081 if Options.gui: 0082 MustChooseKDialog.__init__(self) 0083 self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) 0084 self.setCaption(caption or '') 0085 KMessageBox.createKMessageBox( 0086 self, icon, msg, 0087 [], "", False, 0088 KMessageBox.Options(KMessageBox.NoExec | KMessageBox.AllowLink)) 0089 self.setButtons(KDialog.ButtonCode(buttons)) 0090 # buttons is either Yes/No or Ok 0091 defaultButton = KDialog.Yes if KDialog.Yes & buttons else KDialog.Ok 0092 assert defaultButton & buttons, buttons 0093 self.button(defaultButton).setFocus() 0094 0095 def returns(self, button=None): 0096 """the user answered""" 0097 if button is None: 0098 button = self.default 0099 return button in (KDialog.Yes, KDialog.Ok) 0100 0101 def __str__(self): 0102 return self.msg 0103 0104 0105 class DeferredDialog(Deferred): 0106 0107 """make dialogs usable as Deferred""" 0108 0109 def __init__(self, dlg, modal=True, always=False): 0110 Deferred.__init__(self) 0111 self.dlg = dlg 0112 self.modal = modal 0113 self.always = always 0114 if Options.gui: 0115 if hasattr(self.dlg, 'buttonClicked'): 0116 self.dlg.buttonClicked.connect(self.clicked) 0117 else: 0118 self.dlg.accepted.connect(self.clicked) 0119 self.dlg.rejected.connect(self.cancel) 0120 if Internal.reactor: 0121 Internal.reactor.callLater(0, self.__execute) 0122 else: 0123 # we do not yet have a reactor in initDb() 0124 self.__execute() 0125 0126 def __execute(self): 0127 """now do the actual action""" 0128 if self.dlg is None: 0129 return None 0130 scene = Internal.scene 0131 if not Options.gui or not isAlive(self.dlg): 0132 return self.clicked() 0133 autoPlay = scene and scene.game and scene.game.autoPlay 0134 autoAnswerDelayed = autoPlay and not self.always 0135 if self.modal and not autoAnswerDelayed: 0136 self.dlg.exec_() 0137 else: 0138 self.dlg.show() 0139 if autoAnswerDelayed: 0140 Internal.reactor.callLater( 0141 Internal.Preferences.animationDuration() / 500.0, 0142 self.clicked) 0143 return None 0144 0145 def clicked(self, button=None): 0146 """we got a reaction""" 0147 if self.dlg: 0148 result = self.dlg.returns(button) 0149 else: 0150 result = None 0151 self.__removeFromScene() 0152 self.callback(result) 0153 0154 def cancel(self): 0155 """we want no answer, just let the dialog disappear""" 0156 self.__removeFromScene() 0157 Deferred.cancel(self) 0158 0159 def __removeFromScene(self): 0160 """remove ourself""" 0161 if self.dlg and Internal.scene and isAlive(self.dlg): 0162 self.dlg.hide() 0163 self.dlg = None 0164 0165 0166 class QuestionYesNo(DeferredDialog): 0167 0168 """wrapper, see class Prompt""" 0169 0170 def __init__(self, msg, modal=True, always=False, caption=None): 0171 dialog = Prompt(msg, icon=QMessageBox.Question, 0172 buttons=KDialog.Yes | KDialog.No, default=KDialog.Yes, caption=caption) 0173 DeferredDialog.__init__(self, dialog, modal=modal, always=always) 0174 0175 0176 class WarningYesNo(DeferredDialog): 0177 0178 """wrapper, see class Prompt""" 0179 0180 def __init__(self, msg, modal=True, caption=None): 0181 dialog = Prompt(msg, icon=QMessageBox.Warning, 0182 buttons=KDialog.Yes | KDialog.No, default=KDialog.Yes, caption=caption) 0183 DeferredDialog.__init__(self, dialog, modal=modal) 0184 0185 0186 class Information(DeferredDialog): 0187 0188 """wrapper, see class Prompt""" 0189 0190 def __init__(self, msg, modal=True, caption=None): 0191 dialog = Prompt(msg, icon=QMessageBox.Information, 0192 buttons=KDialog.Ok, caption=caption) 0193 DeferredDialog.__init__(self, dialog, modal=modal) 0194 0195 0196 class Sorry(DeferredDialog): 0197 0198 """wrapper, see class Prompt""" 0199 0200 def __init__(self, msg, modal=True, caption=None, always=False): 0201 dialog = Prompt(msg, icon=QMessageBox.Information, 0202 buttons=KDialog.Ok, caption=caption or 'Sorry') 0203 DeferredDialog.__init__(self, dialog, modal=modal, always=always) 0204 0205 0206 def NoPrompt(unusedMsg): 0207 """we just want to be able to add callbacks even if non-interactive""" 0208 return succeed(None)