mainwindow.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. # -*- coding: utf-8 -*-
  2. #
  3. # AWL simulator - GUI main window
  4. #
  5. # Copyright 2012-2017 Michael Buesch <m@bues.ch>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. #
  21. from __future__ import division, absolute_import, print_function, unicode_literals
  22. from awlsim.common.compat import *
  23. import sys
  24. import os
  25. from awlsim.gui.util import *
  26. from awlsim.gui.editwidget import *
  27. from awlsim.gui.projectwidget import *
  28. from awlsim.gui.cpuwidget import *
  29. from awlsim.gui.guiconfig import *
  30. from awlsim.gui.cpuconfig import *
  31. from awlsim.gui.linkconfig import *
  32. from awlsim.gui.hwmodconfig import *
  33. from awlsim.gui.icons import *
  34. class CpuDockWidget(QDockWidget):
  35. def __init__(self, mainWidget, parent=None):
  36. QDockWidget.__init__(self, "", parent)
  37. self.mainWidget = mainWidget
  38. self.setFeatures(QDockWidget.DockWidgetMovable |
  39. QDockWidget.DockWidgetFloatable)
  40. self.setAllowedAreas(Qt.AllDockWidgetAreas)
  41. self.setWidget(CpuWidget(mainWidget))
  42. self.topLevelChanged.connect(self.__handleTopLevelChange)
  43. self.__handleTopLevelChange(self.isFloating())
  44. @property
  45. def cpuWidget(self):
  46. return self.widget()
  47. def __handleTopLevelChange(self, floating):
  48. if floating:
  49. self.setWindowTitle("%s - CPU view" %\
  50. self.mainWidget.mainWindow.TITLE)
  51. else:
  52. self.setWindowTitle("")
  53. class MainWidget(QWidget):
  54. # Signal: Project loaded
  55. projectLoaded = Signal(Project)
  56. # Signal: Dirty-status changed
  57. dirtyChanged = Signal(bool)
  58. # Signal: CPU run state changed
  59. runStateChanged = Signal(RunState)
  60. # Signal: Source text focus changed
  61. textFocusChanged = Signal(bool)
  62. # Signal: Selected project resource changed
  63. selResourceChanged = Signal(int)
  64. # Signal: UndoAvailable state changed
  65. undoAvailableChanged = Signal(bool)
  66. # Signal: RedoAvailable state changed
  67. redoAvailableChanged = Signal(bool)
  68. # Signal: CopyAvailable state changed
  69. copyAvailableChanged = Signal(bool)
  70. def __init__(self, mainWindow, parent=None):
  71. QWidget.__init__(self, parent)
  72. self.setLayout(QGridLayout(self))
  73. self.mainWindow = mainWindow
  74. self.simClient = GuiAwlSimClient()
  75. self.projectWidget = ProjectWidget(self)
  76. self.layout().addWidget(self.projectWidget, 0, 0)
  77. self.filename = None
  78. self.dirty = False
  79. self.projectWidget.codeChanged.connect(self.somethingChanged)
  80. self.projectWidget.fupChanged.connect(self.somethingChanged)
  81. self.projectWidget.kopChanged.connect(self.somethingChanged)
  82. self.projectWidget.symTabChanged.connect(self.somethingChanged)
  83. self.projectWidget.libTableChanged.connect(self.somethingChanged)
  84. self.projectWidget.textFocusChanged.connect(self.textFocusChanged)
  85. self.projectWidget.selResourceChanged.connect(self.selResourceChanged)
  86. self.projectWidget.undoAvailableChanged.connect(self.undoAvailableChanged)
  87. self.projectWidget.redoAvailableChanged.connect(self.redoAvailableChanged)
  88. self.projectWidget.copyAvailableChanged.connect(self.copyAvailableChanged)
  89. self.runStateChanged.connect(self.projectWidget.updateRunState)
  90. def isDirty(self):
  91. return self.dirty
  92. def setDirty(self, dirty, force=False):
  93. if dirty != self.dirty or force:
  94. self.dirty = dirty
  95. self.dirtyChanged.emit(self.dirty)
  96. def somethingChanged(self):
  97. self.setDirty(True)
  98. def getFilename(self):
  99. return self.filename
  100. def getSimClient(self):
  101. return self.simClient
  102. def getCpuWidget(self):
  103. return self.mainWindow.cpuWidget
  104. def getProject(self):
  105. return self.projectWidget.getProject()
  106. def newFile(self, filename=None):
  107. if isWinStandalone:
  108. executableName = "awlsim-gui.exe"
  109. else:
  110. executableName = sys.executable
  111. executable = findExecutable(executableName)
  112. if not executable:
  113. QMessageBox.critical(self,
  114. "Failed to find '%s'" % executableName,
  115. "Could not spawn a new instance.\n"
  116. "Failed to find '%s'" % executableName)
  117. return
  118. if isWinStandalone:
  119. argv = [ executable, ]
  120. else:
  121. argv = [ executable, "-m", "awlsim.gui.startup", ]
  122. if filename:
  123. argv.append(filename)
  124. try:
  125. PopenWrapper(argv, env=AwlSimEnv.getEnv())
  126. except OSError as e:
  127. QMessageBox.critical(self,
  128. "Failed to execute '%s'" % executableName,
  129. "Could not spawn a new instance.\n%s"
  130. "Failed to execute '%s'" % (
  131. str(e), executableName))
  132. return
  133. def loadFile(self, filename, newIfNotExist=False):
  134. if self.dirty:
  135. res = QMessageBox.question(self,
  136. "Unsaved project",
  137. "The current project is modified and contains unsaved changes.\n "
  138. "Do you want to:\n"
  139. "- Save the project, close it and open the new project\n"
  140. "- Open the new project in a new instance or\n"
  141. "- Discard the changes and open the new project\n"
  142. "- Cancel the operation",
  143. QMessageBox.Save | QMessageBox.Discard |\
  144. QMessageBox.Open | QMessageBox.Cancel,
  145. QMessageBox.Open)
  146. if res == QMessageBox.Save:
  147. if not self.save():
  148. return
  149. elif res == QMessageBox.Discard:
  150. pass
  151. elif res == QMessageBox.Open:
  152. self.newFile(filename)
  153. return
  154. elif res == QMessageBox.Cancel:
  155. return
  156. else:
  157. assert(0)
  158. self.getCpuWidget().goOffline()
  159. if not fileExists(filename) and newIfNotExist:
  160. # The file does not exist. We implicitly create it.
  161. # The actual file will be created when the project is saved.
  162. isNewProject = True
  163. self.projectWidget.reset()
  164. else:
  165. isNewProject = False
  166. try:
  167. self.projectWidget.loadProjectFile(filename)
  168. except AwlSimError as e:
  169. QMessageBox.critical(self,
  170. "Failed to load project file", str(e))
  171. return False
  172. self.filename = filename
  173. if isNewProject or not self.getProject().getProjectFile():
  174. self.setDirty(True, force=True)
  175. else:
  176. self.setDirty(False, force=True)
  177. self.projectLoaded.emit(self.getProject())
  178. return True
  179. def load(self):
  180. if isPyQt and isQt4:
  181. getOpenFileName = QFileDialog.getOpenFileNameAndFilter
  182. else:
  183. getOpenFileName = QFileDialog.getOpenFileName
  184. fn, fil = getOpenFileName(self,
  185. "Open project", "",
  186. "Awlsim project or AWL/STL source (*.awlpro *.awl);;"
  187. "Awlsim project (*.awlpro);;"
  188. "AWL source (*.awl);;"
  189. "All files (*)")
  190. if not fn:
  191. return
  192. self.loadFile(fn, newIfNotExist=False)
  193. def saveFile(self, filename):
  194. try:
  195. res = self.projectWidget.saveProjectFile(filename)
  196. if res == 0: # Failure
  197. return False
  198. elif res < 0: # Force save-as
  199. return self.save(newFile=True)
  200. except AwlSimError as e:
  201. QMessageBox.critical(self,
  202. "Failed to write project file", str(e))
  203. return False
  204. self.filename = filename
  205. self.setDirty(dirty = False, force = True)
  206. return True
  207. def save(self, newFile=False):
  208. if newFile or not self.filename:
  209. if isPyQt and isQt4:
  210. getSaveFileName = QFileDialog.getSaveFileNameAndFilter
  211. else:
  212. getSaveFileName = QFileDialog.getSaveFileName
  213. fn, fil = getSaveFileName(self,
  214. "Awlsim project save as", "",
  215. "Awlsim project (*.awlpro)",
  216. "*.awlpro")
  217. if not fn:
  218. return
  219. if not fn.endswith(".awlpro"):
  220. fn += ".awlpro"
  221. return self.saveFile(fn)
  222. else:
  223. return self.saveFile(self.filename)
  224. def guiConfig(self):
  225. dlg = GuiConfigDialog(self.getProject(), self)
  226. dlg.settingsChanged.connect(self.somethingChanged)
  227. if dlg.exec_() == dlg.Accepted:
  228. self.projectWidget.setSettings(self.getProject().getGuiSettings())
  229. dlg.deleteLater()
  230. def linkConfig(self):
  231. dlg = LinkConfigDialog(self.getProject(), self)
  232. dlg.settingsChanged.connect(self.somethingChanged)
  233. dlg.exec_()
  234. dlg.deleteLater()
  235. def cpuConfig(self):
  236. dlg = CpuConfigDialog(self.getProject(), self)
  237. dlg.settingsChanged.connect(self.somethingChanged)
  238. dlg.exec_()
  239. dlg.deleteLater()
  240. def hwmodConfig(self):
  241. dlg = HwmodConfigDialog(self.getProject(), self)
  242. dlg.settingsChanged.connect(self.somethingChanged)
  243. dlg.exec_()
  244. dlg.deleteLater()
  245. def insertOB(self):
  246. self.projectWidget.insertOB()
  247. def insertFC(self):
  248. self.projectWidget.insertFC()
  249. def insertFB(self):
  250. self.projectWidget.insertFB()
  251. def insertInstanceDB(self):
  252. self.projectWidget.insertInstanceDB()
  253. def insertGlobalDB(self):
  254. self.projectWidget.insertGlobalDB()
  255. def insertUDT(self):
  256. self.projectWidget.insertUDT()
  257. def insertFCcall(self):
  258. self.projectWidget.insertFCcall()
  259. def insertFBcall(self):
  260. self.projectWidget.insertFBcall()
  261. def openLibrary(self):
  262. self.projectWidget.openLibrary()
  263. def undo(self):
  264. self.projectWidget.undo()
  265. def redo(self):
  266. self.projectWidget.redo()
  267. def cut(self):
  268. self.projectWidget.clipboardCut()
  269. def copy(self):
  270. self.projectWidget.clipboardCopy()
  271. def paste(self):
  272. self.projectWidget.clipboardPaste()
  273. def findText(self):
  274. self.projectWidget.findText()
  275. def findReplaceText(self):
  276. self.projectWidget.findReplaceText()
  277. class MainWindow(QMainWindow):
  278. TITLE = "AWL/STL soft-PLC v%s" % VERSION_STRING
  279. @classmethod
  280. def start(cls,
  281. initialAwlSource = None):
  282. # Set basic qapp-details.
  283. # This is important for QSettings.
  284. QApplication.setOrganizationName("awlsim")
  285. QApplication.setApplicationName("Awlsim GUI")
  286. QApplication.setApplicationVersion(VERSION_STRING)
  287. mainwnd = cls(initialAwlSource)
  288. mainwnd.show()
  289. return mainwnd
  290. def __init__(self, awlSource=None, parent=None):
  291. QMainWindow.__init__(self, parent)
  292. self.setWindowIcon(getIcon("cpu"))
  293. self.mainWidget = MainWidget(self, self)
  294. self.cpuDockWidget = CpuDockWidget(self.mainWidget, self)
  295. self.setCentralWidget(self.mainWidget)
  296. self.addDockWidget(Qt.RightDockWidgetArea, self.cpuDockWidget)
  297. self.tb = QToolBar(self)
  298. self.tb.addAction(getIcon("new"), "New project", self.new)
  299. self.tb.addAction(getIcon("open"), "Open project", self.load)
  300. self.tbSaveAct = self.tb.addAction(getIcon("save"), "Save project", self.save)
  301. self.tb.addSeparator()
  302. self.tbUndoAct = self.tb.addAction(getIcon("undo"), "Undo last edit", self.undo)
  303. self.tbRedoAct = self.tb.addAction(getIcon("redo"), "Redo", self.redo)
  304. self.tb.addSeparator()
  305. self.tbCutAct = self.tb.addAction(getIcon("cut"), "Cut", self.cut)
  306. self.tbCopyAct = self.tb.addAction(getIcon("copy"), "Copy", self.copy)
  307. self.tbPasteAct = self.tb.addAction(getIcon("paste"), "Paste", self.paste)
  308. self.tb.addSeparator()
  309. self.tbFindAct = self.tb.addAction(getIcon("find"), "Find...", self.findText)
  310. self.tbFindReplaceAct = self.tb.addAction(getIcon("findreplace"),
  311. "Find and replace...", self.findReplaceText)
  312. self.tb.addSeparator()
  313. self.tbLibAct = self.tb.addAction(getIcon("stdlib"), "Standard library", self.openLibrary)
  314. self.addToolBar(Qt.TopToolBarArea, self.tb)
  315. self.ctrlTb = CpuControlToolBar(self)
  316. self.addToolBar(Qt.TopToolBarArea, self.ctrlTb)
  317. self.inspectTb = CpuInspectToolBar(self)
  318. self.addToolBar(Qt.RightToolBarArea, self.inspectTb)
  319. self.setMenuBar(QMenuBar(self))
  320. menu = QMenu("&File", self)
  321. menu.addAction(getIcon("new"), "&New project", self.new)
  322. menu.addAction(getIcon("open"), "&Open project...", self.load)
  323. self.saveAct = menu.addAction(getIcon("save"), "&Save project", self.save)
  324. menu.addAction(getIcon("save"), "&Save project as...", self.saveAs)
  325. menu.addSeparator()
  326. menu.addAction(getIcon("exit"), "&Exit...", self.close)
  327. self.menuBar().addMenu(menu)
  328. menu = QMenu("&Edit", self)
  329. self.undoAct = menu.addAction(getIcon("undo"), "&Undo", self.undo)
  330. self.redoAct = menu.addAction(getIcon("redo"), "&Redo", self.redo)
  331. menu.addSeparator()
  332. self.cutAct = menu.addAction(getIcon("cut"), "&Cut", self.cut)
  333. self.copyAct = menu.addAction(getIcon("copy"), "&Copy", self.copy)
  334. self.pasteAct = menu.addAction(getIcon("paste"), "&Paste", self.paste)
  335. menu.addSeparator()
  336. self.findAct = menu.addAction(getIcon("find"), "&Find...", self.findText)
  337. self.findReplaceAct = menu.addAction(getIcon("findreplace"),
  338. "Find and r&eplace...", self.findReplaceText)
  339. self.menuBar().addMenu(menu)
  340. menu = QMenu("&Library", self)
  341. menu.addAction(getIcon("textsource"), "Insert &OB template...", self.insertOB)
  342. menu.addAction(getIcon("textsource"), "Insert F&C template...", self.insertFC)
  343. menu.addAction(getIcon("textsource"), "Insert F&B template...", self.insertFB)
  344. menu.addAction(getIcon("textsource"), "Insert &instance-DB template...", self.insertInstanceDB)
  345. menu.addAction(getIcon("textsource"), "Insert &DB template...", self.insertGlobalDB)
  346. menu.addAction(getIcon("textsource"), "Insert &UDT template...", self.insertUDT)
  347. menu.addSeparator()
  348. menu.addAction(getIcon("textsource"), "Insert FC C&ALL template...", self.insertFCcall)
  349. menu.addAction(getIcon("textsource"), "Insert FB CA&LL template...", self.insertFBcall)
  350. menu.addSeparator()
  351. self.libAct = menu.addAction(getIcon("stdlib"), "&Standard library...", self.openLibrary)
  352. self.menuBar().addMenu(menu)
  353. menu = QMenu("&Settings", self)
  354. menu.addAction(getIcon("network"), "&Server connection...", self.linkConfig)
  355. menu.addAction(getIcon("cpu"), "&CPU config...", self.cpuConfig)
  356. menu.addAction(getIcon("hwmod"), "&Hardware modules...", self.hwmodConfig)
  357. menu.addAction(getIcon("prefs"), "&User interface...", self.guiConfig)
  358. self.menuBar().addMenu(menu)
  359. menu = QMenu("&CPU", self)
  360. menu.addAction(self.ctrlTb.onlineAction)
  361. menu.addAction(self.ctrlTb.downloadAction)
  362. menu.addAction(self.ctrlTb.downloadSingleAction)
  363. menu.addAction(self.ctrlTb.runAction)
  364. menu.addAction(self.ctrlTb.diagAction)
  365. menu.addSeparator()
  366. menu.addAction(self.inspectTb.blocksAction)
  367. menu.addAction(self.inspectTb.inputsAction)
  368. menu.addAction(self.inspectTb.outputsAction)
  369. menu.addAction(self.inspectTb.flagsAction)
  370. menu.addAction(self.inspectTb.dbAction)
  371. menu.addAction(self.inspectTb.timerAction)
  372. menu.addAction(self.inspectTb.counterAction)
  373. menu.addAction(self.inspectTb.cpuAction)
  374. menu.addAction(self.inspectTb.lcdAction)
  375. self.menuBar().addMenu(menu)
  376. menu = QMenu("&Help", self)
  377. menu.addAction(getIcon("browser"), "Awlsim &homepage...", self.awlsimHomepage)
  378. menu.addSeparator()
  379. menu.addAction(getIcon("cpu"), "&About...", self.about)
  380. self.menuBar().addMenu(menu)
  381. self.__sourceTextHasFocus = False
  382. self.__dirtyChanged(False)
  383. self.__selResourceChanged(ProjectWidget.RES_SOURCES)
  384. self.__textFocusChanged(False)
  385. self.__undoAvailableChanged(False)
  386. self.__redoAvailableChanged(False)
  387. self.__copyAvailableChanged(False)
  388. self.mainWidget.projectLoaded.connect(self.__handleProjectLoaded)
  389. self.mainWidget.dirtyChanged.connect(self.__dirtyChanged)
  390. self.mainWidget.textFocusChanged.connect(self.__textFocusChanged)
  391. self.mainWidget.selResourceChanged.connect(self.__selResourceChanged)
  392. self.mainWidget.undoAvailableChanged.connect(self.__undoAvailableChanged)
  393. self.mainWidget.redoAvailableChanged.connect(self.__redoAvailableChanged)
  394. self.mainWidget.copyAvailableChanged.connect(self.__copyAvailableChanged)
  395. self.ctrlTb.connectToCpuWidget(self.cpuWidget)
  396. self.inspectTb.connectToCpuWidget(self.cpuWidget)
  397. self.mainWidget.dirtyChanged.connect(self.cpuWidget.handleDirtyChange)
  398. self.mainWidget.projectWidget.visibleLinesChanged.connect(self.cpuWidget.updateVisibleLineRange)
  399. self.cpuWidget.runStateChanged.connect(self.mainWidget.runStateChanged)
  400. self.cpuWidget.onlineDiagChanged.connect(self.mainWidget.projectWidget.handleOnlineDiagChange)
  401. self.cpuWidget.haveInsnDump.connect(self.mainWidget.projectWidget.handleInsnDump)
  402. self.cpuWidget.haveIdentsMsg.connect(self.mainWidget.projectWidget.handleIdentsMsg)
  403. self.cpuWidget.configChanged.connect(self.mainWidget.somethingChanged)
  404. self.resize(1024, 768)
  405. if awlSource:
  406. self.mainWidget.loadFile(awlSource, newIfNotExist=True)
  407. @property
  408. def cpuWidget(self):
  409. return self.cpuDockWidget.cpuWidget
  410. def insertOB(self):
  411. self.mainWidget.insertOB()
  412. def insertFC(self):
  413. self.mainWidget.insertFC()
  414. def insertFB(self):
  415. self.mainWidget.insertFB()
  416. def insertInstanceDB(self):
  417. self.mainWidget.insertInstanceDB()
  418. def insertGlobalDB(self):
  419. self.mainWidget.insertGlobalDB()
  420. def insertUDT(self):
  421. self.mainWidget.insertUDT()
  422. def insertFCcall(self):
  423. self.mainWidget.insertFCcall()
  424. def insertFBcall(self):
  425. self.mainWidget.insertFBcall()
  426. def getSimClient(self):
  427. return self.mainWidget.getSimClient()
  428. def __dirtyChanged(self, isDirty):
  429. self.saveAct.setEnabled(isDirty)
  430. self.tbSaveAct.setEnabled(isDirty)
  431. filename = self.mainWidget.getFilename()
  432. if filename:
  433. postfix = " -- " + os.path.basename(filename)
  434. if isDirty:
  435. postfix += "*"
  436. else:
  437. postfix = ""
  438. self.setWindowTitle("%s%s" % (self.TITLE, postfix))
  439. def __handleProjectLoaded(self, project):
  440. self.__updateLibActions()
  441. self.__updateFindActions()
  442. def __updateFindActions(self):
  443. if self.__selProjectResource == ProjectWidget.RES_SOURCES:
  444. self.tbFindAct.setEnabled(True)
  445. self.tbFindReplaceAct.setEnabled(True)
  446. self.findAct.setEnabled(True)
  447. self.findReplaceAct.setEnabled(True)
  448. else:
  449. self.tbFindAct.setEnabled(False)
  450. self.tbFindReplaceAct.setEnabled(False)
  451. self.findAct.setEnabled(False)
  452. self.findReplaceAct.setEnabled(False)
  453. def __updateLibActions(self):
  454. # Enable/disable the library toolbar button.
  455. # The menu library button is always available on purpose.
  456. if self.tbLibAct.isEnabled():
  457. if self.__selProjectResource != ProjectWidget.RES_SOURCES:
  458. self.tbLibAct.setEnabled(False)
  459. self.tbLibAct.setToolTip("Standard library.\n"
  460. "Please click in the AWL/STL source "
  461. "code at the place where to paste the "
  462. "library call.")
  463. else:
  464. if self.__selProjectResource == ProjectWidget.RES_SOURCES and\
  465. self.__sourceTextHasFocus:
  466. self.tbLibAct.setEnabled(True)
  467. self.tbLibAct.setToolTip("Standard library")
  468. def __textFocusChanged(self, textHasFocus):
  469. self.__sourceTextHasFocus = textHasFocus
  470. self.__updateLibActions()
  471. self.__updateFindActions()
  472. def __selResourceChanged(self, resourceNumber):
  473. self.__selProjectResource = resourceNumber
  474. self.__updateLibActions()
  475. self.__updateFindActions()
  476. def __updateUndoActions(self):
  477. self.undoAct.setEnabled(
  478. self.__undoAvailable and
  479. self.__selProjectResource == ProjectWidget.RES_SOURCES
  480. )
  481. self.tbUndoAct.setEnabled(self.undoAct.isEnabled())
  482. def __undoAvailableChanged(self, undoAvailable):
  483. self.__undoAvailable = undoAvailable
  484. self.__updateUndoActions()
  485. def __updateRedoActions(self):
  486. self.redoAct.setEnabled(
  487. self.__redoAvailable and
  488. self.__selProjectResource == ProjectWidget.RES_SOURCES
  489. )
  490. self.tbRedoAct.setEnabled(self.redoAct.isEnabled())
  491. def __redoAvailableChanged(self, redoAvailable):
  492. self.__redoAvailable = redoAvailable
  493. self.__updateRedoActions()
  494. def __updateClipboardActions(self):
  495. self.cutAct.setEnabled(
  496. self.__copyAvailable and
  497. self.__selProjectResource == ProjectWidget.RES_SOURCES
  498. )
  499. self.tbCutAct.setEnabled(self.cutAct.isEnabled())
  500. self.copyAct.setEnabled(self.cutAct.isEnabled())
  501. self.tbCopyAct.setEnabled(self.copyAct.isEnabled())
  502. def __copyAvailableChanged(self, copyAvailable):
  503. self.__copyAvailable = copyAvailable
  504. self.__updateClipboardActions()
  505. def closeEvent(self, ev):
  506. if self.mainWidget.isDirty():
  507. res = QMessageBox.question(self,
  508. "Unsaved AWL/STL code",
  509. "The editor contains unsaved AWL/STL code.\n"
  510. "AWL/STL code will be lost by exiting without saving.",
  511. QMessageBox.Discard | QMessageBox.Save | QMessageBox.Cancel,
  512. QMessageBox.Cancel)
  513. if res == QMessageBox.Save:
  514. if not self.mainWidget.save():
  515. ev.ignore()
  516. return
  517. elif res == QMessageBox.Cancel:
  518. ev.ignore()
  519. return
  520. self.mainWidget.getSimClient().shutdown()
  521. ev.accept()
  522. QMainWindow.closeEvent(self, ev)
  523. def awlsimHomepage(self):
  524. QDesktopServices.openUrl(QUrl(AWLSIM_HOME_URL, QUrl.StrictMode))
  525. def about(self):
  526. QMessageBox.about(self, "About AWL/STL soft-PLC",
  527. "Awlsim soft-PLC version %s\n"
  528. "\n"
  529. "Copyright 2012-2018 Michael Büsch <m@bues.ch>\n"
  530. "\n"
  531. "Project home: %s\n"
  532. "\n"
  533. "\n"
  534. "This program is free software; you can redistribute it and/or modify "
  535. "it under the terms of the GNU General Public License as published by "
  536. "the Free Software Foundation; either version 2 of the License, or "
  537. "(at your option) any later version.\n"
  538. "\n"
  539. "This program is distributed in the hope that it will be useful, "
  540. "but WITHOUT ANY WARRANTY; without even the implied warranty of "
  541. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
  542. "GNU General Public License for more details.\n"
  543. "\n"
  544. "You should have received a copy of the GNU General Public License along "
  545. "with this program; if not, write to the Free Software Foundation, Inc., "
  546. "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA." %\
  547. (VERSION_STRING, AWLSIM_HOME_URL))
  548. def new(self):
  549. self.mainWidget.newFile()
  550. def load(self):
  551. self.mainWidget.load()
  552. def save(self):
  553. self.mainWidget.save()
  554. def saveAs(self):
  555. self.mainWidget.save(True)
  556. def guiConfig(self):
  557. self.mainWidget.guiConfig()
  558. def hwmodConfig(self):
  559. self.mainWidget.hwmodConfig()
  560. def linkConfig(self):
  561. self.mainWidget.linkConfig()
  562. def cpuConfig(self):
  563. self.mainWidget.cpuConfig()
  564. def openLibrary(self):
  565. self.mainWidget.openLibrary()
  566. def undo(self):
  567. self.mainWidget.undo()
  568. def redo(self):
  569. self.mainWidget.redo()
  570. def cut(self):
  571. self.mainWidget.cut()
  572. def copy(self):
  573. self.mainWidget.copy()
  574. def paste(self):
  575. self.mainWidget.paste()
  576. def findText(self):
  577. self.mainWidget.findText()
  578. def findReplaceText(self):
  579. self.mainWidget.findReplaceText()