Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

465 lines
17KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # JACK, A2J, LASH and LADISH Logs Viewer
  4. # Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the COPYING file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. if True:
  20. from PyQt5.QtCore import pyqtSlot, Qt, QFile, QIODevice, QMutex, QMutexLocker, QTextStream, QThread, QSettings
  21. from PyQt5.QtGui import QPalette, QSyntaxHighlighter
  22. from PyQt5.QtWidgets import QDialog
  23. else:
  24. from PyQt4.QtCore import pyqtSlot, Qt, QFile, QIODevice, QMutex, QMutexLocker, QTextStream, QThread, QSettings
  25. from PyQt4.QtGui import QPalette, QSyntaxHighlighter
  26. from PyQt4.QtGui import QDialog
  27. # ------------------------------------------------------------------------------------------------------------
  28. # Imports (Custom Stuff)
  29. import ui_logs
  30. from shared import *
  31. from shared_i18n import *
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Fix log text output (get rid of terminal colors stuff)
  34. def fixLogText(text):
  35. return text.replace("", "").replace("", "").replace("", "").replace("", "").replace("", "")
  36. # ------------------------------------------------------------------------------------------------------------
  37. # Syntax Highlighter for JACK
  38. class SyntaxHighlighter_JACK(QSyntaxHighlighter):
  39. def __init__(self, parent):
  40. QSyntaxHighlighter.__init__(self, parent)
  41. self.fPalette = parent.palette()
  42. def highlightBlock(self, text):
  43. if ": ERROR: " in text:
  44. self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
  45. elif ": WARNING: " in text:
  46. self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
  47. elif ": ------------------" in text:
  48. self.setFormat(text.find(" ------------------"), len(text), self.fPalette.color(QPalette.Active, QPalette.Mid))
  49. elif ": Connecting " in text:
  50. self.setFormat(text.find(" Connecting "), len(text), self.fPalette.color(QPalette.Active, QPalette.Link))
  51. elif ": Disconnecting " in text:
  52. self.setFormat(text.find(" Disconnecting "), len(text), self.fPalette.color(QPalette.Active, QPalette.LinkVisited))
  53. #elif (": New client " in text):
  54. #self.setFormat(text.find(" New client "), len(text), self.fPalette.color(QPalette.Active, QPalette.Link))
  55. # ------------------------------------------------------------------------------------------------------------
  56. # Syntax Highlighter for A2J
  57. class SyntaxHighlighter_A2J(QSyntaxHighlighter):
  58. def __init__(self, parent):
  59. QSyntaxHighlighter.__init__(self, parent)
  60. self.fPalette = parent.palette()
  61. def highlightBlock(self, text):
  62. if ": error: " in text:
  63. self.setFormat(text.find(" error: "), len(text), Qt.red)
  64. elif ": WARNING: " in text:
  65. self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
  66. elif ": ----------------------------" in text:
  67. self.setFormat(text.find("----------------------------"), len(text), self.fPalette.color(QPalette.Active, QPalette.Mid))
  68. elif ": port created: " in text:
  69. self.setFormat(text.find(" port created: "), len(text), self.fPalette.color(QPalette.Active, QPalette.Link))
  70. elif ": port deleted: " in text:
  71. self.setFormat(text.find(" port deleted: "), len(text), self.fPalette.color(QPalette.Active, QPalette.LinkVisited))
  72. # ------------------------------------------------------------------------------------------------------------
  73. # Syntax Highlighter for LASH
  74. class SyntaxHighlighter_LASH(QSyntaxHighlighter):
  75. def __init__(self, parent):
  76. QSyntaxHighlighter.__init__(self, parent)
  77. self.fPalette = parent.palette()
  78. def highlightBlock(self, text):
  79. if ": ERROR: " in text:
  80. self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
  81. elif ": WARNING: " in text:
  82. self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
  83. elif ": ------------------" in text:
  84. self.setFormat(text.find(" ------------------"), len(text), self.fPalette.color(QPalette.Active, QPalette.Mid))
  85. # ------------------------------------------------------------------------------------------------------------
  86. # Syntax Highlighter for LADISH
  87. class SyntaxHighlighter_LADISH(QSyntaxHighlighter):
  88. def __init__(self, parent):
  89. QSyntaxHighlighter.__init__(self, parent)
  90. self.fPalette = parent.palette()
  91. def highlightBlock(self, text):
  92. if ": ERROR: " in text:
  93. self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
  94. elif ": WARNING: " in text:
  95. self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
  96. elif ": -------" in text:
  97. self.setFormat(text.find(" -------"), len(text), self.fPalette.color(QPalette.Active, QPalette.Mid))
  98. # ------------------------------------------------------------------------------------------------------------
  99. # Lock-less file read thread
  100. class LogsReadThread(QThread):
  101. MAX_INITIAL_SIZE = 2*1024*1024 # 2Mb
  102. updateLogs = pyqtSignal()
  103. def __init__(self, parent):
  104. QThread.__init__(self, parent)
  105. self.fCloseNow = False
  106. self.fPurgeLogs = False
  107. self.fRealParent = parent
  108. # -------------------------------------------------------------
  109. # Take some values from Logs Window
  110. self.LOG_FILE_JACK = LogsW.LOG_FILE_JACK
  111. self.LOG_FILE_A2J = LogsW.LOG_FILE_A2J
  112. self.LOG_FILE_LASH = LogsW.LOG_FILE_LASH
  113. self.LOG_FILE_LADISH = LogsW.LOG_FILE_LADISH
  114. # -------------------------------------------------------------
  115. # Init logs
  116. if self.LOG_FILE_JACK is not None:
  117. self.fLogFileJACK = QFile(self.LOG_FILE_JACK)
  118. self.fLogFileJACK.open(QIODevice.ReadOnly)
  119. self.fLogStreamJACK = QTextStream(self.fLogFileJACK)
  120. self.fLogStreamJACK.setCodec("UTF-8")
  121. if self.fLogFileJACK.size() > self.MAX_INITIAL_SIZE:
  122. self.fLogStreamJACK.seek(self.fLogFileJACK.size() - self.MAX_INITIAL_SIZE)
  123. if self.LOG_FILE_A2J is not None:
  124. self.fLogFileA2J = QFile(self.LOG_FILE_A2J)
  125. self.fLogFileA2J.open(QIODevice.ReadOnly)
  126. self.fLogStreamA2J = QTextStream(self.fLogFileA2J)
  127. self.fLogStreamA2J.setCodec("UTF-8")
  128. if self.fLogFileA2J.size() > self.MAX_INITIAL_SIZE:
  129. self.fLogStreamA2J.seek(self.fLogFileA2J.size() - self.MAX_INITIAL_SIZE)
  130. if self.LOG_FILE_LASH is not None:
  131. self.fLogFileLASH = QFile(self.LOG_FILE_LASH)
  132. self.fLogFileLASH.open(QIODevice.ReadOnly)
  133. self.fLogStreamLASH = QTextStream(self.fLogFileLASH)
  134. self.fLogStreamLASH.setCodec("UTF-8")
  135. if self.fLogFileLASH.size() > self.MAX_INITIAL_SIZE:
  136. self.fLogStreamLASH.seek(self.fLogFileLASH.size() - self.MAX_INITIAL_SIZE)
  137. if self.LOG_FILE_LADISH is not None:
  138. self.fLogFileLADISH = QFile(self.LOG_FILE_LADISH)
  139. self.fLogFileLADISH.open(QIODevice.ReadOnly)
  140. self.fLogStreamLADISH = QTextStream(self.fLogFileLADISH)
  141. self.fLogStreamLADISH.setCodec("UTF-8")
  142. if self.fLogFileLADISH.size() > self.MAX_INITIAL_SIZE:
  143. self.fLogStreamLADISH.seek(self.fLogFileLADISH.size() - self.MAX_INITIAL_SIZE)
  144. def closeNow(self):
  145. self.fCloseNow = True
  146. def purgeLogs(self):
  147. self.fPurgeLogs = True
  148. def run(self):
  149. # -------------------------------------------------------------
  150. # Read logs and set text in main thread
  151. while not self.fCloseNow:
  152. if self.fPurgeLogs:
  153. if self.LOG_FILE_JACK:
  154. self.fLogStreamJACK.flush()
  155. self.fLogFileJACK.close()
  156. self.fLogFileJACK.open(QIODevice.WriteOnly)
  157. self.fLogFileJACK.close()
  158. self.fLogFileJACK.open(QIODevice.ReadOnly)
  159. if self.LOG_FILE_A2J:
  160. self.fLogStreamA2J.flush()
  161. self.fLogFileA2J.close()
  162. self.fLogFileA2J.open(QIODevice.WriteOnly)
  163. self.fLogFileA2J.close()
  164. self.fLogFileA2J.open(QIODevice.ReadOnly)
  165. if self.LOG_FILE_LASH:
  166. self.fLogStreamLASH.flush()
  167. self.fLogFileLASH.close()
  168. self.fLogFileLASH.open(QIODevice.WriteOnly)
  169. self.fLogFileLASH.close()
  170. self.fLogFileLASH.open(QIODevice.ReadOnly)
  171. if self.LOG_FILE_LADISH:
  172. self.fLogStreamLADISH.flush()
  173. self.fLogFileLADISH.close()
  174. self.fLogFileLADISH.open(QIODevice.WriteOnly)
  175. self.fLogFileLADISH.close()
  176. self.fLogFileLADISH.open(QIODevice.ReadOnly)
  177. self.fPurgeLogs = False
  178. else:
  179. if self.LOG_FILE_JACK:
  180. textJACK = fixLogText(self.fLogStreamJACK.readAll()).strip()
  181. else:
  182. textJACK = ""
  183. if self.LOG_FILE_A2J:
  184. textA2J = fixLogText(self.fLogStreamA2J.readAll()).strip()
  185. else:
  186. textA2J = ""
  187. if self.LOG_FILE_LASH:
  188. textLASH = fixLogText(self.fLogStreamLASH.readAll()).strip()
  189. else:
  190. textLASH = ""
  191. if self.LOG_FILE_LADISH:
  192. textLADISH = fixLogText(self.fLogStreamLADISH.readAll()).strip()
  193. else:
  194. textLADISH = ""
  195. self.fRealParent.setLogsText(textJACK, textA2J, textLASH, textLADISH)
  196. self.updateLogs.emit()
  197. if not self.fCloseNow:
  198. self.sleep(1)
  199. # -------------------------------------------------------------
  200. # Close logs before closing thread
  201. if self.LOG_FILE_JACK:
  202. self.fLogFileJACK.close()
  203. if self.LOG_FILE_A2J:
  204. self.fLogFileA2J.close()
  205. if self.LOG_FILE_LASH:
  206. self.fLogFileLASH.close()
  207. if self.LOG_FILE_LADISH:
  208. self.fLogFileLADISH.close()
  209. # ------------------------------------------------------------------------------------------------------------
  210. # Logs Window
  211. class LogsW(QDialog):
  212. LOG_PATH = os.path.join(HOME, ".log")
  213. LOG_FILE_JACK = os.path.join(LOG_PATH, "jack", "jackdbus.log")
  214. LOG_FILE_A2J = os.path.join(LOG_PATH, "a2j", "a2j.log")
  215. LOG_FILE_LASH = os.path.join(LOG_PATH, "lash", "lash.log")
  216. LOG_FILE_LADISH = os.path.join(LOG_PATH, "ladish", "ladish.log")
  217. if not os.path.exists(LOG_FILE_JACK):
  218. LOG_FILE_JACK = None
  219. if not os.path.exists(LOG_FILE_A2J):
  220. LOG_FILE_A2J = None
  221. if not os.path.exists(LOG_FILE_LASH):
  222. LOG_FILE_LASH = None
  223. if not os.path.exists(LOG_FILE_LADISH):
  224. LOG_FILE_LADISH = None
  225. SIGTERM = pyqtSignal()
  226. SIGUSR1 = pyqtSignal()
  227. SIGUSR2 = pyqtSignal()
  228. def __init__(self, parent):
  229. QDialog.__init__(self, parent)
  230. self.ui = ui_logs.Ui_LogsW()
  231. self.ui.setupUi(self)
  232. self.loadSettings()
  233. self.fFirstRun = True
  234. self.fTextLock = QMutex()
  235. self.fTextJACK = ""
  236. self.fTextA2J = ""
  237. self.fTextLASH = ""
  238. self.fTextLADISH = ""
  239. # -------------------------------------------------------------
  240. # Set-up GUI
  241. self.ui.b_close.setIcon(getIcon("window-close"))
  242. self.ui.b_purge.setIcon(getIcon("user-trash"))
  243. # -------------------------------------------------------------
  244. # Check for non-existing logs and remove tabs for those
  245. tabIndex = 0
  246. if self.LOG_FILE_JACK is None:
  247. self.ui.tabWidget.removeTab(0 - tabIndex)
  248. tabIndex += 1
  249. if self.LOG_FILE_A2J is None:
  250. self.ui.tabWidget.removeTab(1 - tabIndex)
  251. tabIndex += 1
  252. if self.LOG_FILE_LASH is None:
  253. self.ui.tabWidget.removeTab(2 - tabIndex)
  254. tabIndex += 1
  255. if self.LOG_FILE_LADISH is None:
  256. self.ui.tabWidget.removeTab(3 - tabIndex)
  257. tabIndex += 1
  258. # -------------------------------------------------------------
  259. # Init logs viewers
  260. if self.LOG_FILE_JACK:
  261. self.fSyntaxJACK = SyntaxHighlighter_JACK(self.ui.pte_jack)
  262. self.fSyntaxJACK.setDocument(self.ui.pte_jack.document())
  263. if self.LOG_FILE_A2J:
  264. self.fSyntaxA2J = SyntaxHighlighter_A2J(self.ui.pte_a2j)
  265. self.fSyntaxA2J.setDocument(self.ui.pte_a2j.document())
  266. if self.LOG_FILE_LASH:
  267. self.fSyntaxLASH = SyntaxHighlighter_LASH(self.ui.pte_lash)
  268. self.fSyntaxLASH.setDocument(self.ui.pte_lash.document())
  269. if self.LOG_FILE_LADISH:
  270. self.SyntaxLADISH = SyntaxHighlighter_LADISH(self.ui.pte_ladish)
  271. self.SyntaxLADISH.setDocument(self.ui.pte_ladish.document())
  272. # -------------------------------------------------------------
  273. # Init file read thread
  274. self.fReadThread = LogsReadThread(self)
  275. self.fReadThread.start(QThread.IdlePriority)
  276. # -------------------------------------------------------------
  277. # Set-up connections
  278. self.ui.b_purge.clicked.connect(self.slot_purgeLogs)
  279. self.fReadThread.updateLogs.connect(self.slot_updateLogs)
  280. # -------------------------------------------------------------
  281. def setLogsText(self, textJACK, textA2J, textLASH, textLADISH):
  282. QMutexLocker(self.fTextLock)
  283. self.fTextJACK = textJACK
  284. self.fTextA2J = textA2J
  285. self.fTextLASH = textLASH
  286. self.fTextLADISH = textLADISH
  287. @pyqtSlot()
  288. def slot_updateLogs(self):
  289. QMutexLocker(self.fTextLock)
  290. if self.fFirstRun:
  291. self.ui.pte_jack.clear()
  292. self.ui.pte_a2j.clear()
  293. self.ui.pte_lash.clear()
  294. self.ui.pte_ladish.clear()
  295. if self.LOG_FILE_JACK and self.fTextJACK:
  296. self.ui.pte_jack.appendPlainText(self.fTextJACK)
  297. if self.LOG_FILE_A2J and self.fTextA2J:
  298. self.ui.pte_a2j.appendPlainText(self.fTextA2J)
  299. if self.LOG_FILE_LASH and self.fTextLASH:
  300. self.ui.pte_lash.appendPlainText(self.fTextLASH)
  301. if self.LOG_FILE_LADISH and self.fTextLADISH:
  302. self.ui.pte_ladish.appendPlainText(self.fTextLADISH)
  303. if self.fFirstRun:
  304. self.ui.pte_jack.horizontalScrollBar().setValue(0)
  305. self.ui.pte_jack.verticalScrollBar().setValue(self.ui.pte_jack.verticalScrollBar().maximum())
  306. self.ui.pte_a2j.horizontalScrollBar().setValue(0)
  307. self.ui.pte_a2j.verticalScrollBar().setValue(self.ui.pte_a2j.verticalScrollBar().maximum())
  308. self.ui.pte_lash.horizontalScrollBar().setValue(0)
  309. self.ui.pte_lash.verticalScrollBar().setValue(self.ui.pte_lash.verticalScrollBar().maximum())
  310. self.ui.pte_ladish.horizontalScrollBar().setValue(0)
  311. self.ui.pte_ladish.verticalScrollBar().setValue(self.ui.pte_ladish.verticalScrollBar().maximum())
  312. self.fFirstRun = False
  313. @pyqtSlot()
  314. def slot_purgeLogs(self):
  315. self.fReadThread.purgeLogs()
  316. self.ui.pte_jack.clear()
  317. self.ui.pte_a2j.clear()
  318. self.ui.pte_lash.clear()
  319. self.ui.pte_ladish.clear()
  320. def loadSettings(self):
  321. settings = QSettings("Cadence", "Cadence-Logs")
  322. self.restoreGeometry(settings.value("Geometry", b""))
  323. def saveSettings(self):
  324. settings = QSettings("Cadence", "Cadence-Logs")
  325. settings.setValue("Geometry", self.saveGeometry())
  326. def closeEvent(self, event):
  327. self.saveSettings()
  328. if self.fReadThread.isRunning():
  329. self.fReadThread.closeNow()
  330. if not self.fReadThread.wait(2000):
  331. self.fReadThread.terminate()
  332. QDialog.closeEvent(self, event)
  333. def done(self, r):
  334. QDialog.done(self, r)
  335. self.close()
  336. # ------------------------------------------------------------------------------------------------------------
  337. # Allow to use this as a standalone app
  338. if __name__ == '__main__':
  339. # Additional imports
  340. from PyQt5.QtWidgets import QApplication
  341. # App initialization
  342. app = QApplication(sys.argv)
  343. app.setApplicationName("Cadence-Logs")
  344. app.setApplicationVersion(VERSION)
  345. app.setOrganizationName("Cadence")
  346. app.setWindowIcon(QIcon(":/scalable/cadence.svg"))
  347. setup_i18n()
  348. # Show GUI
  349. gui = LogsW(None)
  350. gui.show()
  351. setUpSignals(gui)
  352. # App-Loop
  353. sys.exit(app.exec_())