| @@ -7,6 +7,11 @@ | |||
| from abc import abstractmethod | |||
| import markdown | |||
| import subprocess | |||
| import shlex | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (PyQt) | |||
| @@ -14,7 +19,7 @@ from qt_compat import qt_config | |||
| if qt_config == 5: | |||
| from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QByteArray | |||
| from PyQt5.QtGui import QCursor, QIcon, QPalette, QPixmap | |||
| from PyQt5.QtGui import QCursor, QIcon, QPalette, QPixmap, QFont | |||
| from PyQt5.QtWidgets import ( | |||
| QDialog, | |||
| QFileDialog, | |||
| @@ -24,10 +29,13 @@ if qt_config == 5: | |||
| QScrollArea, | |||
| QVBoxLayout, | |||
| QWidget, | |||
| QGraphicsScene, | |||
| QGraphicsTextItem, | |||
| QGraphicsView, | |||
| ) | |||
| elif qt_config == 6: | |||
| from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QByteArray | |||
| from PyQt6.QtGui import QCursor, QIcon, QPalette, QPixmap | |||
| from PyQt6.QtGui import QCursor, QIcon, QPalette, QPixmap, QFont | |||
| from PyQt6.QtWidgets import ( | |||
| QDialog, | |||
| QFileDialog, | |||
| @@ -37,6 +45,9 @@ elif qt_config == 6: | |||
| QScrollArea, | |||
| QVBoxLayout, | |||
| QWidget, | |||
| QGraphicsScene, | |||
| QGraphicsTextItem, | |||
| QGraphicsView, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -981,6 +992,10 @@ class PluginEdit(QDialog): | |||
| self.ui.tabWidget.currentWidget().verticalScrollBar().setValue(scrollVal) | |||
| def reloadPrograms(self): | |||
| # jpka: That's wrong place fot it of course, please move to correct one. | |||
| self._createDescriptionWidgets(self.tr("Description")) | |||
| # Programs | |||
| self.ui.cb_programs.blockSignals(True) | |||
| self.ui.cb_programs.clear() | |||
| @@ -1678,6 +1693,92 @@ class PluginEdit(QDialog): | |||
| #------------------------------------------------------------------ | |||
| def _createDescriptionWidgets(self, tabPageName): | |||
| if self.fPluginInfo['type'] not in (PLUGIN_INTERNAL, PLUGIN_LV2, PLUGIN_SF2): | |||
| return | |||
| # jpka: To be filled from 'rdfs:comment' FIXME | |||
| strDescrFlatMdHtml = "To be filled from rdfs:comment. Test **bold** & _italic_ markdown." | |||
| # This one converts to html, i.e. 'unmarkdown's. | |||
| strDescrHtml = markdown.markdown(strDescrFlatMdHtml) | |||
| realPluginName = self.fPluginInfo['name'] or "(none)" | |||
| if self.fPluginInfo['type'] == PLUGIN_LV2: | |||
| strTemp = self.fPluginInfo['label'] | |||
| strURI = '<a href=' + strTemp + '>' + strTemp + '</a>' | |||
| elif self.fPluginInfo['type'] == PLUGIN_SF2: | |||
| strURI = '<b>' + self.fPluginInfo['maker'] + '</b> using ' +\ | |||
| '<b>' + self.fPluginInfo['label'] + '</b> sound font' | |||
| # if with autodetection of sf2dump: | |||
| # jpka: This adds 'libgig' dependency for 'sf2dump'. | |||
| # jpka: To be filled with real .sf2 filename, line below is tests only. FIXME | |||
| strSoundFontFile = '/usr/share/sounds/sf2/' + realPluginName + '.sf2' | |||
| strCmd = 'sf2dump ' + strSoundFontFile | |||
| result = subprocess.run(shlex.split | |||
| (f'sh -c "set -o pipefail ; timeout 0.2 ' + strCmd +\ | |||
| ' | sed /Samples/q"'), | |||
| stdout=subprocess.PIPE) | |||
| ret = result.returncode | |||
| strCmdHt = '<tt> <u>' + strCmd + '</u> </tt>' | |||
| if (ret == 0): | |||
| if MACOS: | |||
| # jpka: Please check it. FIXME | |||
| strEOL = '\r' | |||
| elif WINDOWS: | |||
| # jpka: Please check it. FIXME | |||
| strEOL = '\r\n' | |||
| else: | |||
| strEOL = '\n' | |||
| strResult = result.stdout.decode('utf-8')\ | |||
| .replace('\t', ' • ')\ | |||
| .replace(strEOL, '<br>') +\ | |||
| '<i>Please use ' + strCmdHt + ' for full info.</i>' | |||
| else: | |||
| strResult = 'Err ' + str(ret) + ' while run ' + strCmdHt + ' .' | |||
| strDescrHtml = strResult | |||
| # if no sf2dump: | |||
| # strDescrHtml = "<small>(No sf2dump support, sorry.)</small>" | |||
| else: | |||
| strURI = "Carla Internal Plugin" | |||
| if self.host.get_program_count(self.fPluginId) == 0: | |||
| strLoadState = "" | |||
| else: | |||
| strLoadState = '<div style="letter-spacing:1px"><br>'\ | |||
| '<b>Note: </b>This plugin collected some presets for you.<br>'\ | |||
| 'Use <i>Edit</i> tab, then <i>Load State</i> button.</div>' | |||
| scene = QGraphicsScene(self) | |||
| text = QGraphicsTextItem("",None) | |||
| text.setTextInteractionFlags(Qt.TextSelectableByMouse) | |||
| # jpka: Should be like this, but not work. FIXME | |||
| # text.setTextWidth(self.ui.tabWidget.geometry().width()); | |||
| # text.setTextWidth(self.ui.tabWidget.tabs.geometry().width()); | |||
| # text.setTextWidth(self.ui.tabWidget.widget(1).geometry().width()); | |||
| # text.setTextWidth(self.ui.tabWidget.size().width()); | |||
| text.setTextWidth(600); | |||
| # text.setFont(QFont("Arial, 16")) # NOTE: All Qt sizes are in Pt; real px~=4/3Pt. | |||
| text.setHtml('<body>' +\ | |||
| '<h1>' + realPluginName + '</h1><br>' +\ | |||
| strURI + '<br><br>' +\ | |||
| '<div style="line-height:1.25;">' + strDescrHtml + '</div>' +\ | |||
| strLoadState +\ | |||
| '<body>'); | |||
| scene.addItem(text) | |||
| view = QGraphicsView(scene, self) | |||
| self.ui.tabWidget.addTab(view, tabPageName) | |||
| #------------------------------------------------------------------ | |||
| def testTimer(self): | |||
| self.fIdleTimerId = self.startTimer(50) | |||