softwaredb.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. # $Id$
  2. from PyQt4 import QtCore, QtGui
  3. from qt_utils import connect
  4. import os
  5. class SoftwareDB(object):
  6. def __init__(self, bridge):
  7. self.__dmDialog = None
  8. self.__ui = None
  9. self.__cursor = None
  10. self.__bridge = bridge
  11. self.__selectedgameid = []
  12. self.__currentshell = ''
  13. self.__commandshell = ''
  14. self.__destroyshell = False
  15. def show(self):
  16. dialog = self.__dmDialog
  17. if dialog is None:
  18. self.__dmDialog = dialog = QtGui.QDialog(
  19. None, # TODO: find a way to get the real parent
  20. QtCore.Qt.Dialog
  21. | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowSystemMenuHint
  22. )
  23. # Setup UI made in Qt Designer.
  24. from ui_softwaredb import Ui_SoftwareDB
  25. ui = Ui_SoftwareDB()
  26. ui.setupUi(dialog)
  27. self.__ui = ui
  28. # Connect signals.
  29. #connect(ui.dirUpButton, 'clicked()', self.updir)
  30. # Fill the components with values.
  31. # Since Python 2.5 SQLite is part of the standard library;
  32. # in case of python 2.4 we try to fall back to the external pysqlite2 module
  33. try:
  34. import sqlite3 as sqlite
  35. except:
  36. from pysqlite2 import dbapi2 as sqlite
  37. cursor = self.__cursor
  38. if cursor is None:
  39. connection = sqlite.connect('softdb.db')
  40. self.__cursor = cursor = connection.cursor()
  41. # First the Company
  42. ui.companyComboBox.addItem(QtCore.QString('Select...'))
  43. cursor.execute(
  44. 'SELECT DISTINCT Company FROM software ORDER BY Company'
  45. )
  46. for row in cursor:
  47. ui.companyComboBox.addItem(QtCore.QString(row[0]))
  48. # The Genre
  49. ui.genreComboBox.addItem(QtCore.QString('Select...'))
  50. cursor.execute('SELECT DISTINCT Genre FROM software ORDER BY Genre')
  51. for row in cursor:
  52. ui.genreComboBox.addItem(QtCore.QString(row[0]))
  53. # The Machine
  54. ui.machineComboBox.addItem(QtCore.QString('Select...'))
  55. cursor.execute(
  56. 'SELECT DISTINCT Machine FROM software ORDER BY Machine'
  57. )
  58. for row in cursor:
  59. ui.machineComboBox.addItem(QtCore.QString(row[0]))
  60. # The Patched
  61. ui.patchedComboBox.addItem(QtCore.QString('Select...'))
  62. cursor.execute(
  63. 'SELECT DISTINCT Patched FROM software ORDER BY Patched'
  64. )
  65. for row in cursor:
  66. ui.patchedComboBox.addItem(QtCore.QString(row[0]))
  67. # The Type
  68. ui.typeComboBox.addItem(QtCore.QString('Select...'))
  69. cursor.execute('SELECT DISTINCT Type FROM software ORDER BY Type')
  70. for row in cursor:
  71. ui.typeComboBox.addItem(QtCore.QString(row[0]))
  72. # The Year
  73. ui.yearComboBox.addItem(QtCore.QString('Select...'))
  74. cursor.execute('SELECT DISTINCT Year FROM software ORDER BY Year')
  75. for row in cursor:
  76. ui.yearComboBox.addItem(QtCore.QString(row[0]))
  77. # Connect all dropdown boxes to the update counter method.
  78. for combox in (
  79. ui.companyComboBox,
  80. ui.genreComboBox,
  81. ui.machineComboBox,
  82. ui.patchedComboBox,
  83. ui.typeComboBox,
  84. ui.yearComboBox
  85. ):
  86. connect(
  87. combox,
  88. 'currentIndexChanged(int)',
  89. self.findMatches
  90. )
  91. # connect regular buttons
  92. connect(
  93. ui.nextPushButton,
  94. 'clicked()',
  95. self.on_nextPushButton_clicked
  96. )
  97. connect(
  98. ui.nextPushButton_2,
  99. 'clicked()',
  100. self.on_nextPushButton_2_clicked
  101. )
  102. connect(
  103. ui.previousPushButton,
  104. 'clicked()',
  105. self.on_previousPushButton_clicked
  106. )
  107. connect(
  108. ui.previousPushButton_2,
  109. 'clicked()',
  110. self.on_previousPushButton_2_clicked
  111. )
  112. connect(
  113. ui.applyPushButton,
  114. 'clicked()',
  115. self.on_applyPushButton_clicked
  116. )
  117. connect(
  118. ui.gamelistView,
  119. 'cellClicked(int,int)',
  120. self.gamelistView_cellClicked
  121. )
  122. self.__ui.gamelistView.setSortingEnabled(0)
  123. self.__ui.gamelistView.horizontalHeader().setResizeMode(
  124. 0, QtGui.QHeaderView.Stretch
  125. )
  126. self.__ui.gamelistView.horizontalHeader().hide()
  127. self.__ui.gamelistView.verticalHeader().hide()
  128. self.__ui.stackedPages.setCurrentIndex(0)
  129. self.findMatches()
  130. dialog.show()
  131. dialog.raise_()
  132. dialog.activateWindow()
  133. def findMatches(self):
  134. query = 'SELECT count(*) ' + self.constructFromPartQuery()
  135. cursor = self.__cursor
  136. cursor.execute(query)
  137. for row in cursor:
  138. print row
  139. self.__ui.numbermatchesLabel.setText(str(row[0]))
  140. def constructFromPartQuery(self):
  141. ui = self.__ui
  142. #construct the query
  143. query = 'FROM software'
  144. where = []
  145. for gui, sqlstatement in (
  146. (ui.companyComboBox, 'Company'),
  147. (ui.genreComboBox, 'Genre'),
  148. (ui.machineComboBox, 'Machine'),
  149. (ui.patchedComboBox, 'Patched'),
  150. (ui.typeComboBox, 'Type'),
  151. (ui.yearComboBox, 'Year')
  152. ):
  153. if gui.currentIndex() != 0:
  154. where.append( str(sqlstatement) + " = '"
  155. + str(gui.currentText() ) + "'" )
  156. for gui, sqlstatement in (
  157. (ui.nameinfoLabel, 'Info'),
  158. (ui.extentionsinfoLabel, 'HardwareExtension')
  159. ):
  160. if gui.text() != 'not specified':
  161. where.append( sqlstatement + " like '%" + str(gui.text()) +"%'")
  162. if len(where) > 0:
  163. query += " WHERE "
  164. query += " AND ".join(where)
  165. #print query
  166. return query
  167. # Slots:
  168. @QtCore.pyqtSignature('')
  169. def on_previousPushButton_clicked(self):
  170. self.__ui.stackedPages.setCurrentIndex(0)
  171. @QtCore.pyqtSignature('')
  172. def on_previousPushButton_2_clicked(self):
  173. self.__ui.stackedPages.setCurrentIndex(1)
  174. @QtCore.pyqtSignature('')
  175. def on_nextPushButton_clicked(self):
  176. self.__ui.stackedPages.setCurrentIndex(1)
  177. query = 'SELECT * ' + self.constructFromPartQuery() + ' ORDER BY Info'
  178. cursor = self.__cursor
  179. cursor.execute(query)
  180. self.__selectedgameid = []
  181. index = 0
  182. for row in cursor:
  183. print row
  184. self.__ui.gamelistView.setRowCount(index + 1)
  185. founditem = QtGui.QTableWidgetItem(QtCore.QString(row[8])) # 'Info'
  186. founditem.setFlags(
  187. QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
  188. )
  189. self.__ui.gamelistView.setItem(index, 0, founditem)
  190. #item = QtGui.QTableWidgetItem(row[0]) # 'id'
  191. #item.setFlags(QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable)
  192. #self.__ui.gamelistView.setItem(index, 1, item)
  193. self.__selectedgameid.append( row[0] )
  194. index += 1
  195. self.__ui.gamelistView.setRowCount(index + 1)
  196. self.showGameinfo( self.__selectedgameid[0] )
  197. @QtCore.pyqtSignature('')
  198. def on_nextPushButton_2_clicked(self):
  199. ui = self.__ui
  200. ui.stackedPages.setCurrentIndex(2)
  201. ui.gamenameLabel.setText(ui.label_name.text())
  202. for gui in (
  203. ui.diskaLabel,
  204. ui.diskbLabel,
  205. ui.cartaLabel,
  206. ui.cartbLabel,
  207. ui.machineLabel,
  208. ui.extensionsLabel
  209. ):
  210. gui.setText('empty')
  211. for gui in (
  212. ui.diskaCheckBox,
  213. ui.diskbCheckBox,
  214. ui.cartaCheckBox,
  215. ui.cartbCheckBox,
  216. ui.machineCheckBox,
  217. ui.extensionsCheckBox
  218. ):
  219. gui.setEnabled(True)
  220. query = 'SELECT * FROM software WHERE id=' + \
  221. str(self.__selectedgameid[ui.gamelistView.currentRow()]) + \
  222. ' ORDER BY Info'
  223. cursor = self.__cursor
  224. cursor.execute(query)
  225. for row in cursor:
  226. print row
  227. # machine
  228. ui.machineLabel.setText(row[6])
  229. #extensions
  230. #split of the 'CTRL' fake extension
  231. ui.ctrlCheckBox.setChecked(False)
  232. extlist = []
  233. for item in str(row[5]).split(','):
  234. if item == 'CTRL':
  235. ui.ctrlCheckBox.setChecked(True)
  236. else:
  237. extlist.append(item)
  238. if len(extlist) == 0:
  239. ui.extensionsLabel.setText('None')
  240. else :
  241. ui.extensionsLabel.setText(','.join(extlist))
  242. #media
  243. for media, gui in (
  244. ('disk', ui.diskaLabel),
  245. ('diska', ui.diskaLabel),
  246. ('diskb', ui.diskbLabel),
  247. ('cart', ui.cartaLabel),
  248. ('carta', ui.cartaLabel),
  249. ('cartb', ui.cartbLabel),
  250. ):
  251. if str(row[1]).lower()==media:
  252. gui.setText(row[9])
  253. @QtCore.pyqtSignature('')
  254. def on_applyPushButton_clicked(self):
  255. ui = self.__ui
  256. # First of all change machine if needed.
  257. #
  258. # The code originally used the 'machine' command and in the
  259. # callback fucntions the 'diska' commands were launched
  260. #
  261. # Unfortunately these disks were then still inserted in the old
  262. # msx shell which was about to be destroyed after the switch
  263. # was made.
  264. #
  265. # The 'Ok'-reply from the machine command seems to mean:
  266. # "machine command accept and will start in the near future"
  267. # and not "machine is switched"
  268. #
  269. # Now we will create the msx shell ourself and delete the
  270. # current shell afterwards
  271. #
  272. self.__bridge.command('machine')(
  273. self.__setCurrentshell,
  274. self.__machineChangeErrorHandler
  275. )
  276. if ui.machineCheckBox.isChecked():
  277. # Request machine change from openMSX.
  278. # TODO: Skip change if openMSX is running with the correct machine.
  279. self.__destroyshell = True
  280. self.__bridge.command('create_machine')(
  281. self.__setCommandshell,
  282. self.__machineChangeErrorHandler
  283. )
  284. else:
  285. self.__destroyshell = False
  286. self.__bridge.command('machine', ui.machineLabel.text())(
  287. self.__setCommandshell,
  288. self.__machineChangeErrorHandler
  289. )
  290. def __setCurrentshell(self, message):
  291. self.__currentshell = message
  292. def __setCommandshell(self, message):
  293. self.__commandshell = message
  294. if self.__destroyshell:
  295. #load the new config for the new machine
  296. self.__bridge.command(str(self.__commandshell) +
  297. '::load_machine', self.__ui.machineLabel.text())(
  298. self.__applyMedia,
  299. self.__machineChangeErrorHandler
  300. )
  301. else:
  302. #in correct machine config so resume with extensions
  303. self.__applyMedia(message)
  304. def __applyMedia(self, message):
  305. # Insert(/eject) the media if requested
  306. ui = self.__ui
  307. filename = ''
  308. for check, label, media in (
  309. ( ui.diskaCheckBox, ui.diskaLabel, '::diska' ),
  310. ( ui.diskbCheckBox, ui.diskbLabel, '::diskb' ),
  311. ( ui.cartaCheckBox, ui.cartaLabel, '::carta' ),
  312. ( ui.cartbCheckBox, ui.cartbLabel, '::cartb' )
  313. ):
  314. setmedia = str(self.__commandshell) + str(media)
  315. if check.isChecked():
  316. if label.text() == 'empty':
  317. self.__bridge.command( setmedia, 'eject' )()
  318. print setmedia + " eject"
  319. else:
  320. self.__bridge.command( setmedia, label.text() )()
  321. print setmedia + " " + str(label.text())
  322. filename = str(label.text())
  323. else:
  324. print "yyyyyyyyyyyyyyyyy not checked"
  325. #
  326. # Then the needed extension.
  327. # We do this after the media because the eject of the cartx might
  328. # eject an extension otherwise (fi. an fmpac)
  329. #
  330. # TODO: if the machine isn't switched then we need to prevent inserting
  331. # extension if they are already available, but then we need to make the
  332. # 'cartx eject' more inteligent also...
  333. if ui.extensionsCheckBox.isChecked():
  334. setext = str(self.__commandshell) + str('::ext')
  335. for item in (ui.extensionsLabel.text()).split(','):
  336. self.__bridge.command(setext, item)()
  337. #
  338. # Switch machine if needed
  339. #
  340. if self.__destroyshell:
  341. #TODO: find out in openMSX itself why this doesn't work
  342. #if you first activate the new one and then delte the
  343. #current one, aka switch the two lines
  344. self.__bridge.command( 'delete_machine' , self.__currentshell )()
  345. self.__bridge.command( 'activate_machine' , self.__commandshell )()
  346. #in the new active machine press CTRL for ten emutime-seconds if requested
  347. if ui.ctrlCheckBox.isChecked():
  348. self.__bridge.command( 'keymatrixdown', '6', '0x02' )()
  349. self.__bridge.command( 'after', 'time', '10', 'keymatrixup', '6', '0x02' )()
  350. #show the readme first for this piece of software
  351. if filename != '':
  352. if filename.lower().endswith(".gz") :
  353. filename = filename[:len(filename)-3]
  354. if os.access(filename+str('.readme.1st'), os.F_OK):
  355. readmefile = open(filename+str('.readme.1st'),'r')
  356. msg = ''
  357. for line in readmefile:
  358. msg += line
  359. messageBox = QtGui.QMessageBox(
  360. 'Read me!', msg,
  361. QtGui.QMessageBox.Information, 0, 0, 0,
  362. self.__dmDialog
  363. )
  364. messageBox.show()
  365. # TODO: Do we actually want to close this dialog once a software is
  366. # chosen?
  367. self.__dmDialog.hide()
  368. def __machineChangeErrorHandler(self, message):
  369. messageBox = QtGui.QMessageBox(
  370. 'Problem changing machine:', message,
  371. QtGui.QMessageBox.Warning, 0, 0, 0,
  372. self.__dmDialog
  373. )
  374. messageBox.show()
  375. def showGameinfo( self , softid ):
  376. query = "SELECT * FROM software WHERE id = '" + str(softid) + "'"
  377. print query
  378. cursor = self.__cursor
  379. cursor.execute(query)
  380. for row in cursor:
  381. #print row
  382. #info on page 2
  383. self.__ui.label_name.setText(QtCore.QString(row[8]))
  384. self.__ui.label_company.setText(QtCore.QString(row[4]))
  385. self.__ui.label_year.setText(QtCore.QString(row[2]))
  386. self.__ui.label_machine.setText(QtCore.QString(row[6]))
  387. self.__ui.label_genre.setText(QtCore.QString(row[7]))
  388. def gamelistView_cellClicked(self, row, column): # pylint: disable-msg=W0613
  389. self.showGameinfo(self.__selectedgameid[row])
  390. #def __updateAll(self):
  391. # TODO: The idea of the name "updateAll" was to be able to deal with
  392. # openMSX crashes. So, we should go back to knowing nothing about
  393. # the openMSX state.