savestatemanager.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # $Id$
  2. from PyQt4 import QtGui, QtCore
  3. from qt_utils import connect
  4. from player import PlayState
  5. class SaveStateManager(object):
  6. def __init__(self, bridge, playState):
  7. self.__bridge = bridge
  8. self.__playState = playState
  9. self.__saveStateListWidget = None
  10. self.__newFileLineEdit = None
  11. self.__newFileWidget = None
  12. self.__deleteStateButton = None
  13. self.__saveStateButton = None
  14. self.__loadStateButton = None
  15. self.__cancelButton = None
  16. self.__saveStateDialog = dialog = QtGui.QDialog(
  17. None # TODO: find a way to get the real parent
  18. )
  19. from ui_savestatemanager import Ui_saveStateManager
  20. ui = Ui_saveStateManager()
  21. ui.setupUi(dialog)
  22. self.__saveStateListWidget = ui.saveStateListWidget
  23. self.__newFileLineEdit = ui.newFileLineEdit
  24. self.__newFileWidget = ui.newFileWidget
  25. self.__deleteStateButton = ui.deleteStateButton
  26. self.__saveStateButton = ui.saveStateButton
  27. self.__loadStateButton = ui.loadStateButton
  28. self.__cancelButton = ui.cancelButton
  29. # insert custom widget into generated gridlayout
  30. # (be careful, this may break if you change the layout...!)
  31. self.__imageView = ScaledImageView()
  32. self.__imageView.setText('No preview available...')
  33. ui.gridLayout.addWidget(self.__imageView, 0, 1, 1, 1)
  34. connect(self.__cancelButton, 'clicked()', lambda: dialog.reject())
  35. connect(self.__deleteStateButton, 'clicked()', self.__delete)
  36. connect(self.__loadStateButton, 'clicked()', self.__load)
  37. connect(self.__saveStateButton, 'clicked()', self.__save)
  38. connect(self.__newFileLineEdit, 'returnPressed()', self.__save)
  39. connect(self.__saveStateListWidget, 'itemSelectionChanged()',
  40. self.__updatePreview)
  41. def exec_(self, mode, parent = None):
  42. dialog = self.__saveStateDialog
  43. if mode == 'save':
  44. self.__loadStateButton.setVisible(False)
  45. self.__saveStateButton.setVisible(True)
  46. self.__newFileWidget.setVisible(True)
  47. self.__newFileWidget.setFocus()
  48. dialog.setWindowTitle('Save State')
  49. elif mode == 'load':
  50. self.__loadStateButton.setVisible(True)
  51. self.__saveStateButton.setVisible(False)
  52. self.__newFileWidget.setVisible(False)
  53. dialog.setWindowTitle('Load State')
  54. else:
  55. assert False, 'Invalid mode for dialog: ' + mode
  56. self.__newFileLineEdit.clear()
  57. self.__refreshList()
  58. self.__clearPreview()
  59. #dialog.setParent(parent) # why does this hang up the app?
  60. return dialog.exec_()
  61. def __refreshList(self):
  62. self.__bridge.command('list_savestates')(
  63. lambda *reply: self.__fill(*reply), None
  64. )
  65. def __fill(self, *saveStateList):
  66. self.__saveStateListWidget.clear()
  67. for saveState in saveStateList:
  68. self.__saveStateListWidget.addItem(saveState)
  69. def __delete(self):
  70. currentItem = self.__saveStateListWidget.currentItem()
  71. if currentItem == None:
  72. return
  73. selected = currentItem.text()
  74. if selected == '':
  75. return
  76. self.__bridge.command('delete_savestate',
  77. selected,
  78. )(
  79. lambda: self.__refreshList(),
  80. lambda message: self.__generalFailHandler(message, 'Problem deleting state')
  81. )
  82. def __load(self):
  83. currentItem = self.__saveStateListWidget.currentItem()
  84. if currentItem == None:
  85. return
  86. selected = currentItem.text()
  87. if selected == '':
  88. return
  89. # TODO: when loading fails, while state was STOP,
  90. # you see a nasty flicker (openMSX window becomes
  91. # visible for a short amount of time). Fix this!
  92. # save old play state
  93. state = self.__playState.getState()
  94. # set to play *before* loading the state
  95. self.__playState.setState(PlayState.play)
  96. self.__bridge.command('loadstate',
  97. selected
  98. )(
  99. lambda dummy: self.__saveStateDialog.accept(),
  100. lambda message: failHelper(message)
  101. )
  102. def failHelper(message):
  103. # failed, restore play state
  104. self.__playState.setState(state)
  105. self.__generalFailHandler(
  106. message, 'Problem loading state'
  107. )
  108. def __save(self):
  109. selected = self.__newFileLineEdit.text()
  110. if selected == '':
  111. currentItem = self.__saveStateListWidget.currentItem()
  112. if currentItem == None:
  113. return
  114. selected = currentItem.text()
  115. if selected == '':
  116. return
  117. reply = QtGui.QMessageBox.question(
  118. self.__saveStateDialog,
  119. 'Overwrite \"' + selected + '\"?',
  120. '<p>Overwrite save state \"' + selected + '\".</p><p>Are you sure?</p>',
  121. QtGui.QMessageBox.Yes,
  122. QtGui.QMessageBox.Cancel)
  123. if reply == QtGui.QMessageBox.Cancel:
  124. return
  125. self.__newFileLineEdit.clear()
  126. self.__bridge.command('savestate',
  127. selected
  128. )(
  129. lambda dummy: self.__refreshList(),
  130. lambda message: self.__generalFailHandler(message, 'Problem saving state')
  131. )
  132. def __generalFailHandler(self, message, title):
  133. messageBox = QtGui.QMessageBox(title, message,
  134. QtGui.QMessageBox.Warning, 0, 0, 0,
  135. self.__saveStateDialog
  136. )
  137. messageBox.show()
  138. def __updatePreview(self):
  139. currentItem = self.__saveStateListWidget.currentItem()
  140. if currentItem == None:
  141. self.__clearPreview()
  142. return
  143. selected = currentItem.text()
  144. # get filename from openMSX
  145. self.__bridge.command('return',
  146. '"$::env(OPENMSX_USER_DATA)/../savestates/' + selected + '.png"'
  147. )(self.__updatePreview2)
  148. def __updatePreview2(self, fileName):
  149. image = QtGui.QImage(fileName)
  150. if image.isNull():
  151. self.__clearPreview()
  152. else:
  153. self.__imageView.setImage(image)
  154. def __clearPreview(self):
  155. self.__imageView.setImage(None)
  156. class ScaledImageView(QtGui.QWidget):
  157. def __init__ (self, *args):
  158. QtGui.QWidget.__init__(self, *args)
  159. self.setSizePolicy(QtGui.QSizePolicy(
  160. QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding
  161. ))
  162. self.__image = None
  163. self.__scaledImage = None
  164. self.__text = ''
  165. def setImage(self, image):
  166. self.__image = image
  167. if image != None:
  168. self.__updateScaledImage()
  169. else:
  170. self.__scaledImage = None
  171. self.update()
  172. def setText(self, text):
  173. self.__text = text
  174. self.update()
  175. def sizeHint(self):
  176. if self.__scaledImage != None:
  177. return self.__scaledImage.size()
  178. else:
  179. if self.__image == None:
  180. return QtCore.QSize(320, 240)
  181. else:
  182. return self.__image.size()
  183. def resizeEvent(self, event):
  184. if self.__image == None:
  185. return
  186. self.__updateScaledImage()
  187. def __updateScaledImage(self):
  188. self.__scaledImage = self.__image.scaled(self.size(),
  189. QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
  190. def paintEvent(self, event):
  191. painter = QtGui.QPainter(self)
  192. if self.__scaledImage != None:
  193. xpos = (self.width() - self.__scaledImage.width()) / 2
  194. ypos = (self.height() - self.__scaledImage.height()) / 2
  195. # draw the image on the widget
  196. painter.drawImage(xpos, ypos, self.__scaledImage)
  197. else:
  198. painter.drawText(QtCore.QRect(0, 0, self.width(), self.height()),
  199. QtCore.Qt.AlignCenter, self.__text)