Ejemplo n.º 1
0
class TextLogElement(object):
    def __init__(self,
                 maximum_block_count: int = 1000,
                 font_size_pt: int = 10,
                 font_family: str = "Courier",
                 title: str = "Log") -> None:
        # For nested layouts: (1) create everything, (2) lay out
        self.log_group = StyledQGroupBox(title)
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)

        font = self.log.font()
        font.setFamily(font_family)
        font.setPointSize(font_size_pt)

        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch(1)
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        self.log_group.setLayout(log_layout_1)

    def get_widget(self) -> QWidget:
        return self.log_group

    def add(self, msg: str) -> None:
        # http://stackoverflow.com/questions/16568451
        # self.log.moveCursor(QTextCursor.End)
        self.log.appendPlainText(msg)
        # ... will append it as a *paragraph*, i.e. no need to add a newline
        # self.scroll_to_end_of_log()

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)
Ejemplo n.º 2
0
class TextLogElement(object):
    def __init__(self,
                 maximum_block_count: int = 1000,
                 font_size_pt: int = 10,
                 font_family: str = "Courier",
                 title: str = "Log") -> None:
        # For nested layouts: (1) create everything, (2) lay out
        self.log_group = StyledQGroupBox(title)
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)

        font = self.log.font()
        font.setFamily(font_family)
        font.setPointSize(font_size_pt)

        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch(1)
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        self.log_group.setLayout(log_layout_1)

    def get_widget(self) -> QWidget:
        return self.log_group

    def add(self, msg: str) -> None:
        # http://stackoverflow.com/questions/16568451
        # self.log.moveCursor(QTextCursor.End)
        self.log.appendPlainText(msg)
        # ... will append it as a *paragraph*, i.e. no need to add a newline
        # self.scroll_to_end_of_log()

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)
Ejemplo n.º 3
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setGeometry(0, 0, 700, 400)
        self.setContentsMargins(6, 6, 6, 6)
        self.setStyleSheet(myStyleSheet(self))
        self.setWindowTitle("Radio Stations - searching with pyradios")
        self.genreList = genres.splitlines()
        self.findfield = QLineEdit()
        self.findfield.setFixedWidth(250)
        self.findfield.addAction(QIcon.fromTheme("edit-find"), 0)
        self.findfield.setPlaceholderText("type search term and press RETURN ")
        self.findfield.returnPressed.connect(self.findStations)
        self.findfield.setClearButtonEnabled(True)
        self.field = QPlainTextEdit()
        self.field.setContextMenuPolicy(Qt.CustomContextMenu)
        self.field.customContextMenuRequested.connect(
            self.contextMenuRequested)
        self.field.cursorPositionChanged.connect(self.selectLine)
        self.field.setWordWrapMode(QTextOption.NoWrap)
        ### volume slider
        self.volSlider = QSlider()
        self.volSlider.setFixedWidth(100)
        self.volSlider.setOrientation(Qt.Horizontal)
        self.volSlider.valueChanged.connect(self.setVolume)
        self.volSlider.setMinimum(0)
        self.volSlider.setMaximum(100)
        ### genre box
        self.combo = QComboBox()
        self.combo.currentIndexChanged.connect(self.comboSearch)
        self.combo.addItem("choose Genre")
        for m in self.genreList:
            self.combo.addItem(m)
        self.combo.addItem("Country")
        self.combo.setFixedWidth(150)
        ### toolbar ###
        self.tb = self.addToolBar("tools")
        self.tb.setContextMenuPolicy(Qt.PreventContextMenu)
        self.tb.setMovable(False)
        self.saveButton = QPushButton("Save as txt")
        self.saveButton.setIcon(QIcon.fromTheme("document-save"))
        self.saveButton.clicked.connect(self.saveStations)
        self.savePlaylistButton = QPushButton("Save as m3u")
        self.savePlaylistButton.setIcon(QIcon.fromTheme("document-save"))
        self.savePlaylistButton.clicked.connect(self.savePlaylist)
        self.setCentralWidget(self.field)
        self.tb.addWidget(self.findfield)
        self.tb.addWidget(self.saveButton)
        self.tb.addWidget(self.savePlaylistButton)
        self.tb.addSeparator()
        self.tb.addWidget(self.combo)
        ### player ###
        self.player = QMediaPlayer()
        self.player.metaDataChanged.connect(self.metaDataChanged)
        self.startButton = QPushButton("Play")
        self.startButton.setIcon(QIcon.fromTheme("media-playback-start"))
        self.startButton.clicked.connect(self.getURLtoPlay)
        self.stopButton = QPushButton("Stop")
        self.stopButton.setIcon(QIcon.fromTheme("media-playback-stop"))
        self.stopButton.clicked.connect(self.stopPlayer)
        self.statusBar().addPermanentWidget(self.volSlider)
        self.statusBar().addPermanentWidget(self.startButton)
        self.statusBar().addPermanentWidget(self.stopButton)
        ## actions
        self.getNameAction = QAction(QIcon.fromTheme("edit-copy"),
                                     "copy Station Name",
                                     self,
                                     triggered=self.getName)
        self.getUrlAction = QAction(QIcon.fromTheme("edit-copy"),
                                    "copy Station URL",
                                    self,
                                    triggered=self.getURL)
        self.getNameAndUrlAction = QAction(QIcon.fromTheme("edit-copy"),
                                           "copy Station Name,URL",
                                           self,
                                           triggered=self.getNameAndUrl)
        self.getURLtoPlayAction = QAction(
            QIcon.fromTheme("media-playback-start"),
            "play Station",
            self,
            shortcut="F6",
            triggered=self.getURLtoPlay)
        self.addAction(self.getURLtoPlayAction)
        self.stopPlayerAction = QAction(QIcon.fromTheme("media-playback-stop"),
                                        "stop playing",
                                        self,
                                        shortcut="F7",
                                        triggered=self.stopPlayer)
        self.addAction(self.stopPlayerAction)
        self.helpAction = QAction(QIcon.fromTheme("help-info"),
                                  "Help",
                                  self,
                                  shortcut="F1",
                                  triggered=self.showHelp)
        self.addAction(self.helpAction)

        self.getForWebAction = QAction(QIcon.fromTheme("browser"),
                                       "copy for WebPlayer",
                                       self,
                                       triggered=self.getForWeb)
        self.volSlider.setValue(60)
        self.statusBar().showMessage("Welcome", 0)

    def setVolume(self):
        self.player.setVolume(self.volSlider.value())

    def comboSearch(self):
        if self.combo.currentIndex() > 0:
            self.findfield.setText(self.combo.currentText())
            self.findStations()

    def getName(self):
        t = self.field.textCursor().selectedText().partition(",")[0]
        clip = QApplication.clipboard()
        clip.setText(t)

    def getURL(self):
        t = self.field.textCursor().selectedText().partition(",")[2]
        clip = QApplication.clipboard()
        clip.setText(t)

    def getNameAndUrl(self):
        t = self.field.textCursor().selectedText()
        clip = QApplication.clipboard()
        clip.setText(t)

    def getForWeb(self):
        t = self.field.textCursor().selectedText()
        name = t.partition(",")[0]
        url = t.partition(",")[2]
        result = f"<li><a class='chlist' href='{url}'>{name}</a></li>"
        clip = QApplication.clipboard()
        clip.setText(result)

    def selectLine(self):
        tc = self.field.textCursor()
        tc.select(QTextCursor.LineUnderCursor)
        tc.movePosition(QTextCursor.StartOfLine, QTextCursor.MoveAnchor)  ##,
        tc.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
        self.field.setTextCursor(tc)

    def showHelp(self):
        QMessageBox.information(
            self, "Information",
            "F6 -> play Station (from line where cursor is)\nF7 -> stop playing"
        )

    def stopPlayer(self):
        self.player.stop()
        self.statusBar().showMessage("Player stopped", 0)

    ### QPlainTextEdit contextMenu
    def contextMenuRequested(self, point):
        cmenu = QMenu()
        if not self.field.toPlainText() == "":
            cmenu.addAction(self.getNameAction)
            cmenu.addAction(self.getUrlAction)
            cmenu.addAction(self.getNameAndUrlAction)
            cmenu.addSeparator()
            cmenu.addAction(self.getURLtoPlayAction)
            cmenu.addAction(self.stopPlayerAction)
            cmenu.addSeparator()
            cmenu.addAction(self.helpAction)
            cmenu.addAction(self.getForWebAction)
        cmenu.exec_(self.field.mapToGlobal(point))

    def getURLtoPlay(self):
        url = ""
        tc = self.field.textCursor()
        rtext = tc.selectedText().partition(",")[2]
        stext = tc.selectedText().partition(",")[0]
        url = rtext
        print("stream url=", url)
        self.player.setMedia(QMediaContent(QUrl(url)))
        self.player.play()
        self.statusBar().showMessage("%s %s" % ("playing", stext), 0)

    def metaDataChanged(self):
        if self.player.isMetaDataAvailable():
            trackInfo = (self.player.metaData("Title"))
            trackInfo2 = (self.player.metaData("Comment"))
            if not trackInfo == None:
                self.statusBar().showMessage(trackInfo, 0)
                if not trackInfo2 == None:
                    self.statusBar().showMessage("%s %s" %
                                                 (trackInfo, trackInfo2))

    def findStations(self):
        self.field.setPlainText("")
        my_value = self.findfield.text()
        self.statusBar().showMessage("searching ...")
        base_url = "https://de1.api.radio-browser.info/xml/stations/byname/"
        url = f"{base_url}{my_value}"
        xml = requests.get(url).content.decode()
        if xml:
            root = ET.fromstring(xml)

            for child in root:
                ch_name = child.attrib["name"]
                ch_url = child.attrib["url"]
                self.field.appendPlainText(f"{ch_name},{ch_url}")
                self.copyToClipboard()

                tc = self.field.textCursor()
                tc.movePosition(QTextCursor.Start, QTextCursor.MoveAnchor)
                tc.select(QTextCursor.LineUnderCursor)
                tc.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor)
                self.field.setTextCursor(tc)

        else:
            self.statusBar().showMessage("nothing found", 0)

        self.field.verticalScrollBar().triggerAction(
            QScrollBar.SliderToMinimum)
        self.field.horizontalScrollBar().triggerAction(
            QScrollBar.SliderToMinimum)

    def saveStations(self):
        if not self.field.toPlainText() == "":
            path, _ = QFileDialog.getSaveFileName(
                None, "RadioStations",
                self.findfield.text() + ".txt", "Text Files (*.txt)")
            if path:
                s = self.field.toPlainText()
                with open(path, 'w') as f:
                    f.write(s)
                    f.close()
                    self.statusBar().showMessage("saved!", 0)

    def savePlaylist(self):
        if not self.field.toPlainText() == "":
            path, _ = QFileDialog.getSaveFileName(
                None, "RadioStations",
                self.findfield.text() + ".m3u", "Playlist Files (*.m3u)")
            if path:
                result = ""
                s = self.field.toPlainText()
                st = []
                for line in s.splitlines():
                    st.append(line)
                result += "#EXTM3U"
                result += '\n'
                for x in range(len(st)):
                    result += "#EXTINF:" + str(x) + "," + st[x].partition(
                        ",")[0]
                    result += '\n'
                    result += st[x].partition(",")[2]
                    result += '\n'
                with open(path, 'w') as f:
                    f.write(result)
                    f.close()
                    self.statusBar().showMessage("saved!", 0)

    def copyToClipboard(self):
        clip = QApplication.clipboard()
        if not self.field.toPlainText() == "":
            clip.setText(self.field.toPlainText())
Ejemplo n.º 4
0
class LogWindow(QMainWindow):
    emit_msg = pyqtSignal(str)

    def __init__(self,
                 level: int = logging.INFO,
                 window_title: str = "Python log",
                 logger: logging.Logger = None,
                 min_width: int = 800,
                 min_height: int = 400,
                 maximum_block_count: int = 1000) -> None:
        super().__init__()
        self.setStyleSheet(LOGEDIT_STYLESHEET)

        self.handler = HtmlColorHandler(self.log_message, level)
        self.may_close = False
        self.set_may_close(self.may_close)

        self.setWindowTitle(window_title)
        if min_width:
            self.setMinimumWidth(min_width)
        if min_height:
            self.setMinimumHeight(min_height)

        log_group = StyledQGroupBox("Log")
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        # QPlainTextEdit better than QTextEdit because it supports
        # maximumBlockCount while still allowing HTML (via appendHtml,
        # not insertHtml).
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)
        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch()
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        log_group.setLayout(log_layout_1)

        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.addWidget(log_group)

        self.emit_msg.connect(self.log_internal)

        if logger:
            logger.addHandler(self.get_handler())

    def get_handler(self) -> logging.Handler:
        return self.handler

    def set_may_close(self, may_close: bool) -> None:
        # log.debug("LogWindow: may_close({})".format(may_close))
        self.may_close = may_close
        # return
        if may_close:
            self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.show()
        # ... or it will be hidden (in a logical not a real way!) by
        # setWindowFlags(), and thus mess up the logic for the whole Qt app
        # exiting (since qt_app.exec_() runs until there are no more windows
        # being shown).

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)

    # noinspection PyPep8Naming
    def closeEvent(self, event: QCloseEvent) -> None:
        """Trap exit."""
        if not self.may_close:
            # log.debug("LogWindow: ignore closeEvent")
            event.ignore()
        else:
            # log.debug("LogWindow: accept closeEvent")
            event.accept()

    def log_message(self, html: str) -> None:
        # Jump threads via a signal
        self.emit_msg.emit(html)

    @pyqtSlot(str)
    def log_internal(self, html: str) -> None:
        # self.log.moveCursor(QTextCursor.End)
        # self.log.insertHtml(html)
        self.log.appendHtml(html)
        # self.scroll_to_end_of_log()
        # ... unnecessary; if you're at the end, it scrolls, and if you're at
        # the top, it doesn't bug you.

    @pyqtSlot()
    def exit(self) -> None:
        # log.debug("LogWindow: exit")
        self.may_close = True
        # closed = QMainWindow.close(self)
        # log.debug("closed: {}".format(closed))
        QMainWindow.close(self)

    @pyqtSlot()
    def may_exit(self) -> None:
        # log.debug("LogWindow: may_exit")
        self.set_may_close(True)
Ejemplo n.º 5
0
class LogWindow(QMainWindow):
    emit_msg = pyqtSignal(str)

    def __init__(self,
                 level: int = logging.INFO,
                 window_title: str = "Python log",
                 logger: logging.Logger = None,
                 min_width: int = 800,
                 min_height: int = 400,
                 maximum_block_count: int = 1000) -> None:
        super().__init__()
        self.setStyleSheet(LOGEDIT_STYLESHEET)

        self.handler = HtmlColorHandler(self.log_message, level)
        self.may_close = False
        self.set_may_close(self.may_close)

        self.setWindowTitle(window_title)
        if min_width:
            self.setMinimumWidth(min_width)
        if min_height:
            self.setMinimumHeight(min_height)

        log_group = StyledQGroupBox("Log")
        log_layout_1 = QVBoxLayout()
        log_layout_2 = QHBoxLayout()
        self.log = QPlainTextEdit()
        # QPlainTextEdit better than QTextEdit because it supports
        # maximumBlockCount while still allowing HTML (via appendHtml,
        # not insertHtml).
        self.log.setReadOnly(True)
        self.log.setLineWrapMode(QPlainTextEdit.NoWrap)
        self.log.setMaximumBlockCount(maximum_block_count)
        log_clear_button = QPushButton('Clear log')
        log_clear_button.clicked.connect(self.log.clear)
        log_copy_button = QPushButton('Copy to clipboard')
        log_copy_button.clicked.connect(self.copy_whole_log)
        log_layout_2.addWidget(log_clear_button)
        log_layout_2.addWidget(log_copy_button)
        log_layout_2.addStretch()
        log_layout_1.addWidget(self.log)
        log_layout_1.addLayout(log_layout_2)
        log_group.setLayout(log_layout_1)

        main_widget = QWidget(self)
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        main_layout.addWidget(log_group)

        self.emit_msg.connect(self.log_internal)

        if logger:
            logger.addHandler(self.get_handler())

    def get_handler(self) -> logging.Handler:
        return self.handler

    def set_may_close(self, may_close: bool) -> None:
        # log.debug("LogWindow: may_close({})".format(may_close))
        self.may_close = may_close
        # return
        if may_close:
            self.setWindowFlags(self.windowFlags() | Qt.WindowCloseButtonHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
        self.show()
        # ... or it will be hidden (in a logical not a real way!) by
        # setWindowFlags(), and thus mess up the logic for the whole Qt app
        # exiting (since qt_app.exec_() runs until there are no more windows
        # being shown).

    def copy_whole_log(self) -> None:
        # Ctrl-C will copy the selected parts.
        # log.copy() will copy the selected parts.
        self.log.selectAll()
        self.log.copy()
        self.log.moveCursor(QTextCursor.End)
        self.scroll_to_end_of_log()

    def scroll_to_end_of_log(self) -> None:
        vsb = self.log.verticalScrollBar()
        vsb.setValue(vsb.maximum())
        hsb = self.log.horizontalScrollBar()
        hsb.setValue(0)

    # noinspection PyPep8Naming
    def closeEvent(self, event: QCloseEvent) -> None:
        """Trap exit."""
        if not self.may_close:
            # log.debug("LogWindow: ignore closeEvent")
            event.ignore()
        else:
            # log.debug("LogWindow: accept closeEvent")
            event.accept()

    def log_message(self, html: str) -> None:
        # Jump threads via a signal
        self.emit_msg.emit(html)

    @pyqtSlot(str)
    def log_internal(self, html: str) -> None:
        # self.log.moveCursor(QTextCursor.End)
        # self.log.insertHtml(html)
        self.log.appendHtml(html)
        # self.scroll_to_end_of_log()
        # ... unnecessary; if you're at the end, it scrolls, and if you're at
        # the top, it doesn't bug you.

    @pyqtSlot()
    def exit(self) -> None:
        # log.debug("LogWindow: exit")
        self.may_close = True
        # closed = QMainWindow.close(self)
        # log.debug("closed: {}".format(closed))
        QMainWindow.close(self)

    @pyqtSlot()
    def may_exit(self) -> None:
        # log.debug("LogWindow: may_exit")
        self.set_may_close(True)