コード例 #1
0
class App(QWidget):

    # 初期化
    def __init__(self):
        super().__init__()
        self.browser    = QWebEngineView()
        self.model      = QStandardItemModel()
        self.listWidget = QListView()
        self.init_ui()

    # ユーザーインターフェイスを初期化する
    def init_ui(self):
        self.listWidget.setModel(self.model)
        self.listWidget.setAlternatingRowColors(True)
        self.listWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.listWidget.setSelectionMode(
            QAbstractItemView.SingleSelection
        )
        self.listWidget.selectionModel().selectionChanged.connect(self.load_html)
        self.listWidget.setItemDelegate(ItemDelegate(self.listWidget))
        grid = QGridLayout()
        grid.setSpacing(10)
        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(self.listWidget)
        splitter.addWidget(self.browser)
        grid.addWidget(splitter,1,0)
        self.init_list_items()
        self.setLayout(grid)
        self.resize(1200, 800)
        self.listWidget.setMinimumWidth(600)
        self.browser.setMinimumWidth(600)
        self.show()

    # 記事本文をデータベースから取り出す(URLの)
    @classmethod #これはあってもなくても良い
    def fetch_contents(self, url):
        with closing(sqlite3.connect('articles.db')) as conn:
            c = conn.cursor()
            c.execute("SELECT contents FROM articles WHERE url=?;", [url])
            rows = c.fetchall()
            for row in rows:
                return row[0]

    # ブラウザに記事をセットする
    def load_html(self):
        for i in self.listWidget.selectedIndexes():
            html = '<h1>' + i.data()[1] + '</h1>' + self.fetch_contents(i.data()[0])
            self.browser.setHtml(html)

    # リストを初期化する
    def init_list_items(self):
        with closing(sqlite3.connect('articles.db')) as conn:
            c = conn.cursor()
            c.execute("SELECT url,title FROM articles;")
            rows = c.fetchall()
            for row in rows:
                item = QStandardItem()
                item.setData(row, Qt.DisplayRole)
                item.setSizeHint(QSize(20, 30))
                self.model.appendRow(item)
コード例 #2
0
class MdEditor(QSplitter):
    def __init__(self):
        super(MdEditor, self).__init__()
        self.writePart = QPlainTextEdit()
        self.showPart = QWebEngineView()
        self.showPart.setZoomFactor(1.5)
        self.writePart.setFont(QFont("", 13))
        self.showPart.setMinimumWidth(20)
        self.writePart.textChanged.connect(self.updateShowPart)
        self.addWidget(self.writePart)
        self.addWidget(self.showPart)

    def updateShowPart(self):
        text = self.writePart.toPlainText()
        html = md2html(text)
        self.showPart.setHtml(html)
コード例 #3
0
ファイル: Window.py プロジェクト: JX25/LwH-1
class App(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.actions = []
        self.project = Project()
        self.create_window()

    def create_window(self):
        # labels
        nameLabel = QLabel("Nazwa akcji", self)
        nameLabel.setFixedWidth(100)
        durationLabel = QLabel("Czas trwania akcji", self)
        durationLabel.setFixedWidth(100)
        predLabel = QLabel("Poprzednicy [;]", self)
        predLabel.setFixedWidth(100)
        self.criticalPathLabel = QLabel("Ścieżka krytyczna: ", self)
        self.timeLabel = QLabel("Czas trwania: ", self)
        self.messageLabel = QLabel("", self)
        self.graphLabel = QLabel(self)

        pixmap2 = QPixmap('images/graph.png')

        self.graphLabel.setPixmap(pixmap2)
        # entries
        self.nameEntry = QLineEdit()
        self.nameEntry.setFixedWidth(150)
        self.durationEntry = QLineEdit()
        self.durationEntry.setFixedWidth(150)
        self.predEntry = QLineEdit()
        self.predEntry.setFixedWidth(150)

        # buttons
        addButton = QPushButton("Dodaj czynność", self)
        addButton.setFixedWidth(100)
        computeButton = QPushButton("Oblicz", self)
        computeButton.setFixedWidth(100)

        # buttons action
        addButton.clicked.connect(self.add_action)
        computeButton.clicked.connect(self.compute_task)

        # web view
        self.view = QWebEngineView(self)
        self.view.setMinimumWidth(800)
        self.view.setMinimumHeight(600)
        url = QtCore.QUrl.fromLocalFile(r"/temp-plot.html")
        self.view.load(url)
        # grid

        hboxRow1 = QHBoxLayout()
        hboxRow1.addWidget(nameLabel)
        hboxRow1.addWidget(self.nameEntry)
        hboxRow1.addWidget(addButton)
        hboxRow1.addStretch(1)

        hboxRow2 = QHBoxLayout()
        hboxRow2.addWidget(durationLabel)
        hboxRow2.addWidget(self.durationEntry)
        hboxRow2.addWidget(computeButton)
        hboxRow2.addStretch(1)

        hboxRow3 = QHBoxLayout()
        hboxRow3.addWidget(predLabel)
        hboxRow3.addWidget(self.predEntry)
        hboxRow3.addStretch(1)

        vboxRowLast = QVBoxLayout()
        vboxRowLast.addWidget(self.criticalPathLabel)
        vboxRowLast.addWidget(self.timeLabel)

        vboxEntryData = QVBoxLayout()
        vboxEntryData.addLayout(hboxRow1)
        vboxEntryData.addLayout(hboxRow2)
        vboxEntryData.addLayout(hboxRow3)
        vboxEntryData.addLayout(vboxRowLast)

        vboxGraphInfo = QVBoxLayout()
        vboxGraphInfo.addWidget(self.graphLabel)
        vboxGraphInfo.addStretch(1)
        vboxGraphInfo.addWidget(self.messageLabel)

        vboxLeftSide = QVBoxLayout()
        vboxLeftSide.addLayout(vboxEntryData)
        vboxLeftSide.addStretch(1)
        vboxLeftSide.addLayout(vboxGraphInfo)

        hboxFinal = QHBoxLayout()
        hboxFinal.addLayout(vboxLeftSide)
        hboxFinal.addStretch(1)
        hboxFinal.addWidget(self.view)

        self.setLayout(hboxFinal)
        self.resize(500, 500)
        self.setWindowTitle("App")
        self.show()

    def add_action(self):
        name = self.nameEntry.text()
        duration = self.durationEntry.text()
        if self.predEntry.text() == '':
            pred = []
        else:
            pred = self.predEntry.text().split(";")
        try:  #s
            self.actions.append(Action(name, duration, pred))
            self.messageLabel.setText("Dodano czynność: " + name)
        except Exception as error:
            self.messageLabel.setText(error.args[0])
            self.actions = []
        for task in self.actions:
            print(task.name + " " + str(task.duration) + " " +
                  str(task.predecessors))

    def compute_task(self):
        if self.actions != []:
            self.project.create_network(self.actions)
            cp = self.project.get_critical_path()
            dr = self.project.get_duration()
            create_graph_image(self.actions, cp)
            self.graphLabel.setPixmap(QPixmap('images/graph.png'))
            create_gantt_chart(self.actions, cp)
            url = QtCore.QUrl.fromLocalFile(r"/temp-plot.html")
            self.view.load(url)
            # self.ganttLabel.setPixmap(QPixmap('images/gantt.png'))
            self.timeLabel.setText("Czas trwania " + str(dr) + "h")
            string_cp = str(cp[0])
            for node in cp[1:]:
                string_cp += " -> " + str(node)
            self.criticalPathLabel.setText(string_cp)
            self.actions = []
            self.project = Project()
        else:
            self.messageLabel.setText("Brak danych!")
            self.actions = []
            self.project = Project()
コード例 #4
0
class OWPlotlyViewer(SparkEnvironment, widget.OWWidget):
    # --------------- Widget metadata protocol ---------------
    priority = 2

    name = "Plotly"
    description = "A plotly canvas"
    icon = "../assets/DataFrameViewer.svg"

    input_figure: go.Figure = None

    class Inputs:
        figure = widget.Input("Figure", go.Figure)

    class Outputs:
        pass
        #data_frame = widget.Output("DataFrame", pyspark.sql.DataFrame)

    want_control_area = True

    def __init__(self):
        super().__init__()
        self.controlArea.setMinimumWidth(250)
        self.v_info_box = gui.vBox(self.controlArea, 'Info')
        self.v_info = gui.label(self.v_info_box, self, '')
        self.v_info.setAlignment(QtCore.Qt.AlignTop)

        self.mainArea.setMinimumWidth(800)
        self.mainArea.setMinimumHeight(600)

        self.v_webview = QWebEngineView(self.mainArea)
        self.v_webview.setMinimumWidth(800)
        self.v_webview.setMinimumHeight(600)

    @Inputs.figure
    def set_input_figure(self, figure):
        self.input_figure = figure

    # called after received all inputs
    def handleNewSignals(self):
        self.apply()

    def _check_input(self):
        if self.input_figure is None:
            self.warning('Input figure does not exist')
            return False
        else:
            return True

    # this is the logic: computation, update UI, send outputs. ..
    def apply(self):
        if not self._check_input():
            return

        self.clear_messages()

        filename = tempfile.NamedTemporaryFile(suffix='.html').name
        print('Plot file path: %s' % filename)
        plotly.offline.plot(self.input_figure,
                            output_type='file',
                            filename=filename,
                            auto_open=False,
                            show_link=False)  # , include_plotlyjs=True)

        self.v_webview.load(QUrl.fromLocalFile(filename))
コード例 #5
0
class MainWindow(QMainWindow):
    htmlReady = pyqtSignal(str)

    def __init__(self, app):
        QMainWindow.__init__(self)
        self.install_directory = os.getcwd()

        self.app = app
        self.book = None
        self.last_book = ""
        self.filename = ""
        self._part_is_new = False
        self.tread_running = False
        self.initTheme()
        self.createUi()
        self.loadPlugins()
        self.createMenus()
        self.createStatusBar()
        self.readSettings()

        self.text_edit.textChanged.connect(self.textChanged)

    def initTheme(self):
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                             QCoreApplication.organizationName(),
                             QCoreApplication.applicationName())
        self.theme = settings.value("theme", "DarkFusion")
        hilite_color = settings.value(
            "hiliteColor",
            self.palette().highlight().color().name())
        self.changeStyle(self.theme, hilite_color)

    def showEvent(self, event):
        if self.last_book:
            self.loadBook(self.last_book)

    def changeStyle(self, theme, hilite_color):
        self.theme = theme
        if theme == "DarkFusion":
            QApplication.setStyle(DarkFusion(hilite_color))
        else:
            QApplication.setStyle(QStyleFactory.create(theme))
            pal = self.app.palette()
            pal.setColor(QPalette.Highlight, QColor(hilite_color))
            self.app.setPalette(pal)

    def createUi(self):
        self.content = Expander("Content", ":/images/parts.svg")
        self.images = Expander("Images", ":/images/images.svg")
        self.settings = Expander("Settings", ":/images/settings.svg")

        self.setWindowTitle(QCoreApplication.applicationName() + " " +
                            QCoreApplication.applicationVersion())
        vbox = QVBoxLayout()
        vbox.addWidget(self.content)
        vbox.addWidget(self.images)
        vbox.addWidget(self.settings)
        vbox.addStretch()

        self.content_list = QListWidget()
        self.content_list.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
        content_box = QVBoxLayout()
        content_box.addWidget(self.content_list)
        self.item_edit = QLineEdit()
        self.item_edit.setMaximumHeight(0)
        self.item_edit.editingFinished.connect(self.editItemFinished)
        self.item_anim = QPropertyAnimation(self.item_edit,
                                            "maximumHeight".encode("utf-8"))
        content_box.addWidget(self.item_edit)
        button_layout = QHBoxLayout()
        plus_button = FlatButton(":/images/plus.svg")
        self.edit_button = FlatButton(":/images/edit.svg")
        self.trash_button = FlatButton(":/images/trash.svg")
        self.up_button = FlatButton(":/images/up.svg")
        self.down_button = FlatButton(":/images/down.svg")
        self.trash_button.enabled = False
        self.up_button.enabled = False
        self.down_button.enabled = False
        button_layout.addWidget(plus_button)
        button_layout.addWidget(self.up_button)
        button_layout.addWidget(self.down_button)
        button_layout.addWidget(self.edit_button)
        button_layout.addWidget(self.trash_button)
        content_box.addLayout(button_layout)
        self.content.addLayout(content_box)
        plus_button.clicked.connect(self.addPart)
        self.trash_button.clicked.connect(self.dropPart)
        self.up_button.clicked.connect(self.partUp)
        self.down_button.clicked.connect(self.partDown)
        self.edit_button.clicked.connect(self.editPart)

        self.image_list = QListWidget()
        self.image_list.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
        image_box = QVBoxLayout()
        image_box.addWidget(self.image_list)
        image_button_layout = QHBoxLayout()
        image_plus_button = FlatButton(":/images/plus.svg")
        self.image_trash_button = FlatButton(":/images/trash.svg")
        self.image_trash_button.enabled = False
        image_button_layout.addWidget(image_plus_button)
        image_button_layout.addWidget(self.image_trash_button)
        image_box.addLayout(image_button_layout)
        self.images.addLayout(image_box)
        image_plus_button.clicked.connect(self.addImage)
        self.image_trash_button.clicked.connect(self.dropImage)

        scroll_content = QWidget()
        scroll_content.setLayout(vbox)
        scroll = QScrollArea()
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setWidget(scroll_content)
        scroll.setWidgetResizable(True)
        scroll.setMaximumWidth(200)
        scroll.setMinimumWidth(200)

        self.navigationdock = QDockWidget("Navigation", self)
        self.navigationdock.setAllowedAreas(Qt.LeftDockWidgetArea
                                            | Qt.RightDockWidgetArea)
        self.navigationdock.setWidget(scroll)
        self.navigationdock.setObjectName("Navigation")
        self.addDockWidget(Qt.LeftDockWidgetArea, self.navigationdock)

        self.splitter = QSplitter()
        self.text_edit = MarkdownEdit()
        self.text_edit.setFont(QFont("Courier", 15))  # 11 on Linux
        self.preview = QWebEngineView()
        self.preview.setMinimumWidth(300)
        self.setWindowTitle(QCoreApplication.applicationName())

        self.splitter.addWidget(self.text_edit)
        self.splitter.addWidget(self.preview)
        self.setCentralWidget(self.splitter)

        self.content.expanded.connect(self.contentExpanded)
        self.images.expanded.connect(self.imagesExpanded)
        self.settings.expanded.connect(self.settingsExpanded)
        self.settings.clicked.connect(self.openSettings)
        self.content_list.currentItemChanged.connect(self.partSelectionChanged)
        self.image_list.currentItemChanged.connect(self.imageSelectionChanged)
        self.image_list.itemDoubleClicked.connect(self.insertImage)

        self.text_edit.undoAvailable.connect(self.undoAvailable)
        self.text_edit.redoAvailable.connect(self.redoAvailable)
        self.text_edit.copyAvailable.connect(self.copyAvailable)

        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)

    def undoAvailable(self, value):
        self.undo_act.setEnabled(value)

    def redoAvailable(self, value):
        self.redo_act.setEnabled(value)

    def copyAvailable(self, value):
        self.copy_act.setEnabled(value)
        self.cut_act.setEnabled(value)

    def clipboardDataChanged(self):
        md = QApplication.clipboard().mimeData()
        self.paste_act.setEnabled(md.hasText())

    def openSettings(self):
        dlg = Settings(self.book, self.install_directory)
        dlg.exec()
        if dlg.saved:
            self.setWindowTitle(QCoreApplication.applicationName() + " - " +
                                self.book.name)

    def addPart(self):
        self.item_edit.setText("")
        self.item_edit.setFocus()
        self.item_anim.setStartValue(0)
        self.item_anim.setEndValue(23)
        self.item_anim.start()
        self._part_is_new = True

    def addItem(self):
        text = self.item_edit.text()
        if text:
            if not self.book.getPart(text):
                self.book.addPart(text)
                self.loadBook(self.last_book)

    def updateItem(self):
        text = self.item_edit.text()
        if text:
            if not self.book.getPart(text):
                self.book.updatePart(
                    self.content_list.currentItem().data(1).name, text)
                self.loadBook(self.last_book)

    def editItemFinished(self):
        if self._part_is_new:
            self.addItem()
        else:
            self.updateItem()
        self.item_anim.setStartValue(23)
        self.item_anim.setEndValue(0)
        self.item_anim.start()

    def editPart(self):
        item = self.content_list.currentItem().data(1).name
        self.item_edit.setText(item)
        self.item_edit.setFocus()
        self.item_anim.setStartValue(0)
        self.item_anim.setEndValue(23)
        self.item_anim.start()
        self._part_is_new = False

    def dropPart(self):
        item = self.content_list.currentItem().data(1).name
        msgBox = QMessageBox()
        msgBox.setText("You are about to delete the part <i>" + item + "</i>")
        msgBox.setInformativeText("Do you really want to delete the item?")
        msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
        msgBox.setDefaultButton(QMessageBox.Cancel)
        ret = msgBox.exec()
        if ret == QMessageBox.Yes:
            self.book.dropPart(item)
            self.loadBook(self.last_book)

    def addImage(self):
        fileName = ""
        dialog = QFileDialog()
        dialog.setFileMode(QFileDialog.AnyFile)
        dialog.setNameFilter("Image Files(*.png *.jpg *.bmp *.gif);;All (*)")
        dialog.setWindowTitle("Load Image")
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        if dialog.exec_():
            fileName = dialog.selectedFiles()[0]
        del dialog
        if not fileName:
            return

        base = os.path.basename(fileName)
        if not os.path.exists(
                os.path.join(self.book.source_path, "images", base)):
            copy(fileName, os.path.join(self.book.source_path, "images"))
        item = QListWidgetItem()
        item.setText(Path(fileName).name)
        item.setData(
            1,
            os.path.join(self.book.source_path, "images",
                         Path(fileName).name))
        self.image_list.addItem(item)

    def dropImage(self):
        item = self.image_list.currentItem()
        image = item.data(1)
        filename = os.path.join(self.book.source_path, "parts", image)
        os.remove(filename)
        self.loadImages()

    def loadImages(self):
        self.image_list.clear()
        for root, dir, files in os.walk(
                os.path.join(self.book.source_path, "images")):
            for file in files:
                filename = os.path.join(self.book.source_path, "images",
                                        Path(file).name)
                item = QListWidgetItem()
                item.setToolTip("Doubleclick image to insert into text")
                item.setText(Path(file).name)
                item.setData(1, filename)
                self.image_list.addItem(item)

    def partUp(self):
        pos = self.content_list.currentRow()
        item = self.content_list.takeItem(pos)
        self.content_list.insertItem(pos - 1, item)
        self.content_list.setCurrentRow(pos - 1)
        self.book.partUp(item.data(1).name)

    def partDown(self):
        pos = self.content_list.currentRow()
        item = self.content_list.takeItem(pos)
        self.content_list.insertItem(pos + 1, item)
        self.content_list.setCurrentRow(pos + 1)
        self.book.partDown(item.data(1).name)

    def partSelectionChanged(self, item):
        if item:
            part = item.data(1)
            self.filename = os.path.join(self.book.source_path, "parts",
                                         part.src)
            with open(self.filename, "r") as f:
                t = f.read()
                self.text_edit.setPlainText(t)
            self.trash_button.enabled = True
            self.up_button.enabled = self.content_list.currentRow() > 0
            self.down_button.enabled = self.content_list.currentRow(
            ) < self.content_list.count() - 1
            self.edit_button.enabled = True
        else:
            self.text_edit.setText("")
            self.trash_button.enabled = False
            self.up_button.enabled = False
            self.down_button.enabled = False
            self.edit_button.enabled = False

    def imageSelectionChanged(self, item):
        if item:
            self.image_trash_button.enabled = True
        else:
            self.image_trash_button.enabled = False

    def contentExpanded(self, value):
        if value:
            self.images.setExpanded(False)
            self.settings.setExpanded(False)

    def imagesExpanded(self, value):
        if value:
            self.content.setExpanded(False)
            self.settings.setExpanded(False)

    def appearanceExpanded(self, value):
        if value:
            self.content.setExpanded(False)
            self.images.setExpanded(False)
            self.settings.setExpanded(False)

    def settingsExpanded(self, value):
        if value:
            self.content.setExpanded(False)
            self.images.setExpanded(False)

    def closeEvent(self, event):
        self.writeSettings()
        event.accept()

    def createMenus(self):
        new_icon = QIcon(QPixmap(":/images/new.svg"))
        open_icon = QIcon(QPixmap(":/images/open.svg"))
        book_icon = QIcon(QPixmap(":/images/book.svg"))
        bold_icon = QIcon(QPixmap(":/images/bold.svg"))
        italic_icon = QIcon(QPixmap(":/images/italic.svg"))
        image_icon = QIcon(QPixmap(":/images/image.svg"))
        table_icon = QIcon(QPixmap(":/images/table.svg"))
        á_icon = QIcon(QPixmap(":/images/á.svg"))
        ã_icon = QIcon(QPixmap(":/images/ã.svg"))
        é_icon = QIcon(QPixmap(":/images/é.svg"))
        ê_icon = QIcon(QPixmap(":/images/ê.svg"))
        ó_icon = QIcon(QPixmap(":/images/ó.svg"))

        new_act = QAction(new_icon, "&New", self)
        new_act.setShortcuts(QKeySequence.New)
        new_act.setStatusTip("Create a new ebook project")
        new_act.triggered.connect(self.newFile)
        new_act.setToolTip("Create new ebook project")

        open_act = QAction(open_icon, "&Open", self)
        open_act.setShortcuts(QKeySequence.Open)
        open_act.setStatusTip("Open an existing ebook project")
        open_act.triggered.connect(self.open)
        open_act.setToolTip("Open an existing ebook project")

        book_act = QAction(book_icon, "&Create Book", self)
        book_act.setShortcuts(QKeySequence.SaveAs)
        book_act.setStatusTip("Create an ebook")
        book_act.triggered.connect(self.create)
        book_act.setToolTip("Create an ebook")

        pdf_act = QAction("Create &PDF", self)
        pdf_act.setStatusTip("Create PDF")
        pdf_act.setToolTip("Create PDF")
        pdf_act.triggered.connect(self.pdfExport)

        settings_act = QAction("&Settings", self)
        settings_act.setStatusTip("Open settings dialog")
        settings_act.triggered.connect(self.settingsDialog)
        settings_act.setToolTip("Open settings dialog")

        exit_act = QAction("E&xit", self)
        exit_act.setShortcuts(QKeySequence.Quit)
        exit_act.setStatusTip("Exit the application")
        exit_act.triggered.connect(self.close)

        self.undo_act = QAction("Undo", self)
        self.undo_act.setShortcut(QKeySequence.Undo)
        self.undo_act.setEnabled(False)
        self.undo_act.triggered.connect(self.doUndo)

        self.redo_act = QAction("Redo", self)
        self.redo_act.setShortcut(QKeySequence.Redo)
        self.redo_act.setEnabled(False)
        self.undo_act.triggered.connect(self.doRedo)

        self.cut_act = QAction("Cu&t", self)
        self.cut_act.setShortcut(QKeySequence.Cut)
        self.cut_act.triggered.connect(self.doCut)
        self.cut_act.setEnabled(False)

        self.copy_act = QAction("&Copy", self)
        self.copy_act.setShortcut(QKeySequence.Copy)
        self.copy_act.triggered.connect(self.doCopy)
        self.copy_act.setEnabled(False)

        self.paste_act = QAction("&Paste", self)
        self.paste_act.setShortcut(QKeySequence.Paste)
        self.paste_act.triggered.connect(self.doPaste)
        self.paste_act.setEnabled(False)

        bold_act = QAction(bold_icon, "Bold", self)
        bold_act.setShortcut(Qt.CTRL + Qt.Key_B)
        bold_act.triggered.connect(self.bold)

        italic_act = QAction(italic_icon, "Italic", self)
        italic_act.setShortcut(Qt.CTRL + Qt.Key_I)
        italic_act.triggered.connect(self.italic)

        image_act = QAction(image_icon, "Image", self)
        image_act.setShortcut(Qt.CTRL + Qt.Key_G)
        image_act.triggered.connect(self.insertImage)
        image_act.setToolTip("Insert an image")

        table_act = QAction(table_icon, "Table", self)
        table_act.setShortcut(Qt.CTRL + Qt.Key_T)
        table_act.triggered.connect(self.insertTable)
        table_act.setToolTip("Insert a table")

        á_act = QAction(á_icon, "á", self)
        á_act.triggered.connect(self.insertLetterA1)
        á_act.setToolTip("Insert letter á")

        ã_act = QAction(ã_icon, "ã", self)
        ã_act.triggered.connect(self.insertLetterA2)
        ã_act.setToolTip("Insert letter ã")

        é_act = QAction(é_icon, "é", self)
        é_act.triggered.connect(self.insertLetterE1)
        é_act.setToolTip("Insert letter é")

        ê_act = QAction(ê_icon, "ê", self)
        ê_act.triggered.connect(self.insertLetterE2)
        ê_act.setToolTip("Insert letter ê")

        ó_act = QAction(ó_icon, "ó", self)
        ó_act.triggered.connect(self.insertLetterO1)
        ó_act.setToolTip("Insert letter ó")

        about_act = QAction("&About", self)
        about_act.triggered.connect(self.about)
        about_act.setStatusTip("Show the application's About box")

        spell_act = QAction("&Spellcheck", self)
        spell_act.setShortcut(Qt.CTRL + Qt.Key_P)
        spell_act.triggered.connect(self.spellCheck)
        spell_act.setStatusTip("Spellcheck")

        file_menu = self.menuBar().addMenu("&File")
        file_menu.addAction(new_act)
        file_menu.addAction(open_act)
        file_menu.addAction(book_act)
        file_menu.addAction(pdf_act)
        file_menu.addSeparator()
        file_menu.addAction(settings_act)
        file_menu.addSeparator()
        file_menu.addAction(exit_act)

        edit_menu = self.menuBar().addMenu("&Edit")
        edit_menu.addAction(self.undo_act)
        edit_menu.addAction(self.redo_act)
        edit_menu.addSeparator()
        edit_menu.addAction(self.cut_act)
        edit_menu.addAction(self.copy_act)
        edit_menu.addAction(self.paste_act)

        format_menu = self.menuBar().addMenu("&Format")
        format_menu.addAction(bold_act)
        format_menu.addAction(italic_act)

        insert_menu = self.menuBar().addMenu("&Insert")
        insert_menu.addAction(image_act)
        insert_menu.addAction(table_act)

        for key in Plugins.generatorPluginNames():
            gen = Plugins.getGeneratorPlugin(key)
            if gen:
                act = QAction(gen.display_name, self)
                #act.triggered.connect(self.insertTable)
                #act.setToolTip("Insert a table")
                insert_menu.addAction(act)
                act.triggered.connect(gen.menu_action)

        help_menu = self.menuBar().addMenu("&Help")
        help_menu.addAction(about_act)
        help_menu.addAction(spell_act)

        file_tool_bar = self.addToolBar("File")
        file_tool_bar.addAction(new_act)
        file_tool_bar.addAction(open_act)
        file_tool_bar.addAction(book_act)

        format_tool_bar = self.addToolBar("Format")
        format_tool_bar.addAction(bold_act)
        format_tool_bar.addAction(italic_act)

        insert_toolbar = self.addToolBar("Insert")
        insert_toolbar.addAction(image_act)
        insert_toolbar.addAction(table_act)
        insert_toolbar.addAction(á_act)
        insert_toolbar.addAction(ã_act)
        insert_toolbar.addAction(é_act)
        insert_toolbar.addAction(ê_act)
        insert_toolbar.addAction(ó_act)

    def doUndo(self):
        self.text_edit.undo()

    def doRedo(self):
        self.text_edit.redo()

    def doCut(self):
        self.text_edit.cut()

    def doCopy(self):
        self.text_edit.copy()

    def doPaste(self):
        self.text_edit.paste()

    def insertImage(self):
        if not self.book:
            QMessageBox.warning(self, QCoreApplication.applicationName(),
                                "You have to load or create a book first!")
            return
        if not self.filename:
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to select part from the book content first!")
            return
        if self.image_list.count() == 0:
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to add an image to the image list first!")
            return
        if not self.image_list.currentItem():
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to select an image from the image list first!")
            return

        item = self.image_list.currentItem()
        filename = item.text()
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        base = filename.split(".")[0].replace("_", "-")
        cursor.insertText("![" + base + "](../images/" + filename + " \"" +
                          base + "\")")
        cursor.setPosition(pos)
        self.text_edit.setTextCursor(cursor)

    def insertTable(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText(
            "| alignLeft | alignCenter | unAligned | alignRight |\n"
            "|  :---     |   :---:     |   ---     |   ---:     |\n"
            "|  cell a   |   cell b    |   cell c  |   cell d   |\n"
            "|  cell e   |   cell f    |   cell g  |   cell h   |\n")
        cursor.setPosition(pos)
        self.text_edit.setTextCursor(cursor)

    def insertLetterA1(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText("á")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def insertLetterA2(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText("ã")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def insertLetterE1(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText("é")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def insertLetterE2(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText("ê")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def insertLetterO1(self):
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        cursor.insertText("ó")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def createStatusBar(self):
        self.statusBar().showMessage("Ready")

    def about(self):
        QMessageBox.about(
            self, "About " + QCoreApplication.applicationName(),
            "EbookCreator\nVersion: " + QCoreApplication.applicationVersion() +
            "\n(C) Copyright 2020 CrowdWare. All rights reserved.\n\nThis program is provided AS IS with NO\nWARRANTY OF ANY KIND, INCLUDING THE\nWARRANTY OF DESIGN, MERCHANTABILITY AND\nFITNESS FOR A PATICULAR PURPOSE."
        )

    def newFile(self):
        dlg = ProjectWizard(self.install_directory, parent=self)
        dlg.loadBook.connect(self.loadBook)
        dlg.show()

    def open(self):
        fileName = ""
        dialog = QFileDialog()
        dialog.setFileMode(QFileDialog.AnyFile)
        dialog.setNameFilter("EbookCreator (book.qml);;All (*)")
        dialog.setWindowTitle("Load Ebook")
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        dialog.setDirectory(os.path.join(self.install_directory, "sources"))
        if dialog.exec_():
            fileName = dialog.selectedFiles()[0]
        del dialog
        if not fileName:
            return
        self.loadBook(fileName)

    def writeSettings(self):
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                             QCoreApplication.organizationName(),
                             QCoreApplication.applicationName())
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("lastBook", self.last_book)

    def readSettings(self):
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                             QCoreApplication.organizationName(),
                             QCoreApplication.applicationName())
        geometry = settings.value("geometry", QByteArray())
        self.last_book = settings.value("lastBook")
        if not geometry:
            availableGeometry = QApplication.desktop().availableGeometry(self)
            self.resize(availableGeometry.width() / 3,
                        availableGeometry.height() / 2)
            self.move((availableGeometry.width() - self.width()) / 2,
                      (availableGeometry.height() - self.height()) / 2)
        else:
            self.restoreGeometry(geometry)

    def bold(self):
        if not self.filename:
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to select part from the book content first!")
            return
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        if not cursor.hasSelection():
            cursor.select(QTextCursor.WordUnderCursor)
        cursor.insertText("**" + cursor.selectedText() + "**")
        cursor.setPosition(pos + 2)
        self.text_edit.setTextCursor(cursor)

    def italic(self):
        if not self.filename:
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to select part from the book content first!")
            return
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        if not cursor.hasSelection():
            cursor.select(QTextCursor.WordUnderCursor)
        cursor.insertText("*" + cursor.selectedText() + "*")
        cursor.setPosition(pos + 1)
        self.text_edit.setTextCursor(cursor)

    def create(self):
        filename = ""
        dialog = QFileDialog()
        dialog.setFileMode(QFileDialog.AnyFile)
        dialog.setNameFilter("ePub3 (*.epub);;All (*)")
        dialog.setWindowTitle("Create Ebook")
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setDirectory(self.book.source_path)
        dialog.setDefaultSuffix("epub")
        if dialog.exec_():
            filename = dialog.selectedFiles()[0]
        del dialog
        if not filename:
            return
        QApplication.setOverrideCursor(Qt.WaitCursor)
        createEpub(filename, self.book, self)
        QApplication.restoreOverrideCursor()

    def loadStatusChanged(self, status):
        if status == 1:
            self.book = self.component.create()
            if self.book is not None:
                self.book.setFilename(self.last_book)
                self.book.setWindow(self)
            else:
                for error in self.component.errors():
                    print(error.toString())
                return

            self.content_list.clear()
            for part in self.book.parts:
                item = QListWidgetItem()
                item.setText(part.name)
                item.setData(1, part)
                self.content_list.addItem(item)

            self.loadImages()
            self.setWindowTitle(QCoreApplication.applicationName() + " - " +
                                self.book.name)

            self.content.setExpanded(True)
            self.content_list.setCurrentRow(0)
        elif status == 3:
            for error in self.component.errors():
                print(error.toString())
            return

    def loadBook(self, filename):
        self.last_book = filename
        self.filename = ""
        engine = QQmlEngine()
        self.component = QQmlComponent(engine)
        self.component.statusChanged.connect(self.loadStatusChanged)
        self.component.loadUrl(QUrl.fromLocalFile(filename))

    def settingsDialog(self):
        dlg = SettingsDialog(self.theme,
                             self.palette().highlight().color().name(),
                             parent=self)
        dlg.exec()
        if dlg.theme != self.theme or dlg.hilite_color != self.palette(
        ).highlight().color().name():
            settings = QSettings(QSettings.IniFormat, QSettings.UserScope,
                                 QCoreApplication.organizationName(),
                                 QCoreApplication.applicationName())
            settings.setValue("theme", dlg.theme)
            settings.setValue("hiliteColor", dlg.hilite_color)

            msgBox = QMessageBox()
            msgBox.setText("Please restart the app to change the theme!")
            msgBox.exec()

    def textChanged(self):
        text = self.text_edit.toPlainText()
        if self.filename:
            with open(self.filename, "w") as f:
                f.write(text)

        self.lock = Lock()
        with self.lock:
            if not self.tread_running:
                self.tread_running = True
                self.htmlReady.connect(self.previewReady)
                thread = Thread(target=self.createHtml, args=(text, ))
                thread.daemon = True
                thread.start()

    def previewReady(self, html):
        self.preview.setHtml(
            html,
            baseUrl=QUrl(
                Path(os.path.join(self.book.source_path, "parts",
                                  "index.html")).as_uri()))
        self.htmlReady.disconnect()
        with self.lock:
            self.tread_running = False

    def createHtml(self, text):
        html = '<html>\n<head>\n'
        html += '<link href="../css/pastie.css" rel="stylesheet" type="text/css"/>\n'
        html += '<link href="../css/stylesheet.css" rel="stylesheet" type="text/css"/>\n'
        html += '</head>\n<body>\n'
        html += markdown(text,
                         html4tags=False,
                         extras=[
                             "fenced-code-blocks", "wiki-tables", "tables",
                             "header-ids"
                         ])
        html += '\n</body>\n</html>'
        html = addLineNumbers(html)
        self.htmlReady.emit(html)

    def pdfExport(self):
        p = PdfExport(self.book, self.statusBar())

    def spellCheck(self):
        if not self.filename:
            QMessageBox.warning(
                self, QCoreApplication.applicationName(),
                "You have to select part from the book content first!")
            return
        cursor = self.text_edit.textCursor()
        pos = cursor.position()
        if not cursor.hasSelection():
            cursor.select(QTextCursor.WordUnderCursor)
        spell = Speller(lang='en')
        changed = spell(cursor.selectedText())
        if changed != cursor.selectedText():
            cursor.insertText(changed)
            self.text_edit.setTextCursor(cursor)

    def loadPlugins(self):
        # check if we are running in a frozen environment (pyinstaller --onefile)
        if getattr(sys, "frozen", False):
            bundle_dir = sys._MEIPASS
            # if we are running in a onefile environment, then copy all plugin to /tmp/...
            if bundle_dir != os.getcwd():
                os.mkdir(os.path.join(bundle_dir, "plugins"))
                for root, dirs, files in os.walk(
                        os.path.join(os.getcwd(), "plugins")):
                    for file in files:
                        shutil.copy(os.path.join(root, file),
                                    os.path.join(bundle_dir, "plugins"))
                        print("copy", file)
                    break  # do not copy __pycache__
        else:
            bundle_dir = os.getcwd()

        plugins_dir = os.path.join(bundle_dir, "plugins")
        for root, dirs, files in os.walk(plugins_dir):
            for file in files:
                modulename, ext = os.path.splitext(file)
                if ext == ".py":
                    module = import_module("plugins." + modulename)
                    for name, klass in inspect.getmembers(
                            module, inspect.isclass):
                        if klass.__module__ == "plugins." + modulename:
                            instance = klass()
                            if isinstance(instance, GeneratorInterface):
                                Plugins.addGeneratorPlugin(name, instance)
                                instance.setTextEdit(self.text_edit)
                                #instance.registerContenType()
            break  # not to list __pycache__
コード例 #6
0
# loop.exec_()
# html = webview.page().mainFrame().toHtml()
# tree = lxml.html.fromstring(html)
# content = tree.cssselect('#result')[0].text
# print(content)

## 新版本使用
# pip install PySide2==5.12.0
# pip install PyQt5==5.11.3
# try:
#     from PySide2.QtCore import QUrl
#     from PySide2.QtWidgets import QApplication
#     from PySide2.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
# except:
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineView
# 弹窗显示
url = 'http://example.webscraping.com/places/default/search'
app = QApplication([])
view = QWebEngineView()
view.setWindowTitle("阿尔法科技有限公司")
view.setMinimumWidth(1000)
view.setMinimumHeight(800)
view.load(QUrl(url))
view.show()
app.exec()

# 基于webkit的解决方案
# Ghost是Selenium 与PhantomJS 的结合体,安装时会携带PySide,PyQt4
# pip install Ghost==0.5.0
コード例 #7
0
class POSM(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setLocale(QLocale(QLocale.English))
        self.initUI()
        self.setAttribute(Qt.WA_AlwaysShowToolTips)
        sizegrip = QtWidgets.QSizeGrip(self)
        self.layout.addWidget(sizegrip, 0,
                              QtCore.Qt.AlignBottom | QtCore.Qt.AlignRight)

        self.record = [None]
        recordAction = QAction(datetime.datetime.now().strftime('%H:%M:%S'),
                               self)
        recordAction.triggered.connect(lambda: self.changeMap(0))
        self.recordMenu.addAction(recordAction)

    def initUI(self):
        self.layout = QHBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.console = InformationalConsole(app)

        self.horSplitter = QSplitter(Qt.Horizontal)
        self.horSplitter.setChildrenCollapsible(False)
        self.editionSplitter = QSplitter(Qt.Vertical)
        self.editionSplitter.setChildrenCollapsible(False)

        self.queryUI = QueryUI()
        self.queryUI.setOnRequestChanged(self.changeCurrentMap)
        self.editionSplitter.addWidget(self.queryUI)

        self.queryWidget = QWidget()
        self.queryWidget.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Maximum)
        self.queryWidget.setLayout(QVBoxLayout())
        self.queryWidget.layout().setContentsMargins(0, 0, 0, 0)
        self.queryWidget.layout().setSpacing(0)

        self.queryHeader = QLabel("Query")
        self.queryHeader.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Fixed)
        self.queryHeader.setFixedHeight(self.queryHeader.sizeHint().height() +
                                        10)
        self.queryHeader.setContentsMargins(5, 5, 0, 5)
        self.queryWidget.layout().addWidget(self.queryHeader)

        self.queryText = CodeEditor()
        self.qlHighlighter = OverpassQLHighlighter(self.queryText.document())
        self.queryText.setReadOnly(True)
        self.queryWidget.layout().addWidget(self.queryText)

        self.editionSplitter.addWidget(self.queryWidget)

        self.horSplitter.addWidget(self.editionSplitter)

        self.emptyMapPage = QWebEnginePage()
        self.emptyMapPage.setHtml(EMPTY_HTML)

        self.manualModePage = QWebEnginePage()
        soup = bs4.BeautifulSoup(EMPTY_HTML, features="html.parser")
        js = soup.new_tag("script")
        js.string = (MANUAL_MODE_JS_SCRIPT % (str([])))
        soup.append(js)

        self.manualModePage.setHtml(str(soup))

        self.mapRenderer = QWebEngineView()
        self.mapRenderer.setMinimumWidth(500)
        self.mapRenderer.setPage(self.emptyMapPage)

        self.consoleSplitter = QSplitter(Qt.Vertical)
        self.consoleSplitter.setChildrenCollapsible(False)
        self.consoleSplitter.addWidget(self.mapRenderer)

        self.consoleWidget = QWidget()
        self.consoleWidget.setLayout(QVBoxLayout())
        self.consoleWidget.layout().setContentsMargins(0, 0, 0, 0)
        self.consoleWidget.layout().setSpacing(0)

        self.consoleHeader = QLabel("Console")
        self.consoleHeader.setSizePolicy(QSizePolicy.Expanding,
                                         QSizePolicy.Fixed)
        self.consoleHeader.setContentsMargins(5, 5, 0, 5)
        self.consoleWidget.layout().addWidget(self.consoleHeader)
        self.consoleWidget.layout().addWidget(self.console)

        self.consoleSplitter.addWidget(self.consoleWidget)

        self.horSplitter.addWidget(self.consoleSplitter)

        self.layout.addWidget(self.horSplitter)

        self.initMenuBar()

        centralWidget = QWidget(self)
        centralWidget.setLayout(self.layout)
        self.setCentralWidget(centralWidget)

        self.setWindowTitle('Python Open Street Map')

    def initMenuBar(self):
        menubar = self.menuBar()

        fileMenu = menubar.addMenu('File')

        openAct = QAction('Open netedit', self)
        openAct.triggered.connect(self.openNet)
        fileMenu.addAction(openAct)

        saveMenu = fileMenu.addMenu("Save")

        saveOutputAct = QAction('output', self)
        saveOutputAct.triggered.connect(self.saveNet)
        saveOutputAct.setShortcut('Ctrl+S')
        saveMenu.addAction(saveOutputAct)

        saveQueryAct = QAction('query', self)
        saveQueryAct.triggered.connect(self.saveQuery)
        saveQueryAct.setShortcut('Ctrl+Shift+S')
        saveMenu.addAction(saveQueryAct)

        saveInteractiveModeAct = QAction('interactive mode', self)
        saveInteractiveModeAct.triggered.connect(self.saveInteractiveQuery)
        saveMenu.addAction(saveInteractiveModeAct)

        openMenu = fileMenu.addMenu("Open")

        openQuery = QAction('query', self)
        openQuery.triggered.connect(self.openQuery)
        openQuery.setShortcut('Ctrl+O')
        openMenu.addAction(openQuery)

        openInteractiveMode = QAction('interactive mode', self)
        openInteractiveMode.triggered.connect(self.openInteractiveQuery)
        openMenu.addAction(openInteractiveMode)

        self.recordMenu = openMenu.addMenu("record")

        runMenu = menubar.addMenu('Run')

        playAct = QAction('Play', self)
        playAct.triggered.connect(self.playQuery)
        playAct.setShortcut('Ctrl+P')
        runMenu.addAction(playAct)

        playTableRowAct = QAction('Play row selection', self)
        playTableRowAct.triggered.connect(self.playTableRow)
        playTableRowAct.setShortcut('Ctrl+T')
        runMenu.addAction(playTableRowAct)

        self.requestMenu = menubar.addMenu('Request')

        addRequestAct = QAction('Add request', self)
        addRequestAct.triggered.connect(lambda b: self.addRequest())
        addRequestAct.setShortcut('Ctrl+A')
        self.requestMenu.addAction(addRequestAct)

        templatesMenu = self.requestMenu.addMenu("Add template")

        addRoadAct = QAction('Roads', self)
        addRoadAct.triggered.connect(lambda: self.addTemplate([
            OverpassFilter("highway", TagComparison.EQUAL, "", False, True),
            OverpassFilter("name", TagComparison.EQUAL, "", False, True),
            OverpassFilter("ref", TagComparison.EQUAL, "", False, True),
            OverpassFilter("maxspeed", TagComparison.AT_MOST, "120", False,
                           False),
            OverpassFilter("lanes", TagComparison.EQUAL, "", False, True),
            OverpassFilter("oneway", TagComparison.EQUAL, "", False, True)
        ]))
        templatesMenu.addAction(addRoadAct)

        addMainRoadAct = QAction('Main roads', self)
        mainHighways = "^(motorway|trunk|primary|secondary|residential)(_link)?$"
        everythingButYes = "^(y(e([^s]|$|s.)|[^e]|$)|[^y]|$).*"
        addMainRoadAct.triggered.connect(lambda: self.addTemplate([
            OverpassFilter("highway", TagComparison.EQUAL, mainHighways, False,
                           False),
            OverpassFilter("construction", TagComparison.HAS_NOT_KEY, "",
                           False, False),
            OverpassFilter("noexit", TagComparison.EQUAL, "yes", True, True),
            OverpassFilter("access", TagComparison.EQUAL, everythingButYes,
                           True, False)
        ]))
        templatesMenu.addAction(addMainRoadAct)

        addParkingAct = QAction('Parking', self)
        addParkingAct.triggered.connect(lambda: self.addTemplate([
            OverpassFilter("service", TagComparison.EQUAL, "parking", False,
                           False),
            OverpassFilter("highway", TagComparison.HAS_KEY, "", False, True)
        ]))
        templatesMenu.addAction(addParkingAct)

        addPedestriansAct = QAction('Pedestrians', self)
        pedestrianHighway = [
            "pedestrian", "footway", "path", "cycleway", "bridleway", "steps",
            "crossing"
        ]
        addPedestriansAct.triggered.connect(lambda: self.addTemplate([
            OverpassFilter("highway", TagComparison.IS_ONE_OF,
                           pedestrianHighway, False, True)
        ]))
        templatesMenu.addAction(addPedestriansAct)

        removeRequestAct = QAction('Remove current request', self)
        removeRequestAct.triggered.connect(self.removeRequest)
        removeRequestAct.setShortcut('Ctrl+R')
        self.requestMenu.addAction(removeRequestAct)

        self.manualModeAct = QAction(
            'Switch between interactive and manual mode', self)
        self.manualModeAct.triggered.connect(self.switchManualMode)
        self.requestMenu.addAction(self.manualModeAct)

        self.manualModeMenu = menubar.addMenu('Manual mode')
        self.manualModeMenu.setEnabled(False)

        manualModeCleanPolygonAct = QAction('Clean polygon', self)
        manualModeCleanPolygonAct.triggered.connect(
            self.cleanManualModePolygon)
        self.manualModeMenu.addAction(manualModeCleanPolygonAct)

        manualModeGetPolygonAct = QAction('Polygon coordinates', self)
        manualModeGetPolygonAct.triggered.connect(
            lambda: self.manualModePage.runJavaScript(
                "getPolygons();", self.logManualModePolygonCoords))
        self.manualModeMenu.addAction(manualModeGetPolygonAct)

        windowsMenu = menubar.addMenu('Windows')

        cleanMapAct = QAction('Clean map', self)
        cleanMapAct.triggered.connect(self.cleanMap)
        windowsMenu.addAction(cleanMapAct)

        self.showHideInteractiveModeAct = QAction('Interactive mode', self)
        self.showHideInteractiveModeAct.triggered.connect(
            self.showHideInteractiveMode)
        windowsMenu.addAction(self.showHideInteractiveModeAct)

        showHideConsole = QAction('Console', self)
        showHideConsole.triggered.connect(self.showHideConsole)
        windowsMenu.addAction(showHideConsole)

        showHideQuery = QAction('Query', self)
        showHideQuery.triggered.connect(self.showHideQuery)
        windowsMenu.addAction(showHideQuery)

    # ACTIONS
    def cleanMap(self):
        if self.queryText.isReadOnly():
            if self.queryUI.getCurrentMap() is not None:
                self.mapRenderer.setPage(self.queryUI.updateMaps(EMPTY_HTML))
        else:
            soup = bs4.BeautifulSoup(EMPTY_HTML, features="html.parser")
            js = soup.new_tag("script")
            js.string = (MANUAL_MODE_JS_SCRIPT % (str([])))
            soup.append(js)
            self.manualModePage.setHtml(str(soup))

        logging.info("Cleaning map")

    def changeMap(self, i):
        if i == 0:
            if not self.queryText.isReadOnly():
                self.switchManualMode()
            if self.queryText.isReadOnly():
                self.queryUI.reset()
                self.queryText.clear()
                self.mapRenderer.setPage(self.emptyMapPage)
                self.queryUI.updateMaps(EMPTY_HTML)
        elif self.record[i]["interactiveMode"]:
            if not self.queryText.isReadOnly():
                self.switchManualMode()
            if self.queryText.isReadOnly():
                self.queryUI.setQuery(self.record[i]["query"])
                self.queryText.setPlainText(self.record[i]["query"].getQL())
                self.mapRenderer.setPage(
                    self.queryUI.updateMaps(self.record[i]["html"]))
        else:
            if self.queryText.isReadOnly():
                self.switchManualMode()
            if not self.queryText.isReadOnly():
                self.queryUI.reset()
                self.queryText.setPlainText(self.record[i]["query"])
                self.manualModePage.setHtml(self.record[i]["html"])
                self.mapRenderer.setPage(self.manualModePage)

    def logManualModePolygonCoords(self, coords):
        coordsString = " ".join([str(c) for point in coords for c in point])
        logging.info("Polygon coordinates:\"{}\"".format(coordsString))
        pyperclip.copy(coordsString)
        logging.debug("LINE")

    def cleanManualModePolygon(self):
        logging.info("Cleaning polygon.")
        self.manualModePage.runJavaScript(
            "cleanPolygon();", lambda returnValue: logging.debug("LINE"))

    def showHideInteractiveMode(self):
        if self.queryUI.isHidden():
            if self.editionSplitter.isHidden():
                self.editionSplitter.show()
                self.queryText.hide()
            self.queryUI.show()
            logging.info("Showing 'Interactive mode' window.")
        else:
            if self.queryText.isHidden():
                self.editionSplitter.hide()
            self.queryUI.hide()
            logging.info("Hiding 'Interactive mode' window.")
        logging.debug("LINE")

    def showHideConsole(self):
        if self.console.isHidden():
            self.console.show()
            logging.info("Showing 'Console' window.")
            self.consoleWidget.setMaximumHeight(QWIDGETSIZE_MAX)
        else:
            self.console.hide()
            self.consoleWidget.setMaximumHeight(
                self.queryHeader.sizeHint().height())
            logging.info("Hiding 'Console' window.")
        logging.debug("LINE")

    def showHideQuery(self):
        if self.queryText.isHidden():
            if self.editionSplitter.isHidden():
                self.editionSplitter.show()
                self.queryUI.hide()
            self.queryText.show()
            logging.info("Showing 'Query' window.")
            self.queryWidget.setMaximumHeight(QWIDGETSIZE_MAX)
        else:
            if self.queryUI.isHidden():
                self.editionSplitter.hide()
            self.queryText.hide()
            self.queryWidget.setMaximumHeight(
                self.queryHeader.sizeHint().height())
            logging.info("Hiding 'Query' window.")
        logging.debug("LINE")

    def switchManualMode(self):
        if self.queryText.isReadOnly():
            reply = QMessageBox.question(
                self, "Manual mode",
                "Are you sure?\nThe interactive mode will remain as it is now."
            )

            if reply == QMessageBox.Yes:
                self.queryText.setReadOnly(False)

                self.queryUI.hide()
                for action in self.requestMenu.actions():
                    action.setEnabled(False)
                self.manualModeAct.setEnabled(True)
                self.manualModeMenu.setEnabled(True)
                self.showHideInteractiveModeAct.setEnabled(False)
                self.mapRenderer.setPage(self.manualModePage)

                logging.info("Switching to manual mode.")
            else:
                logging.info(
                    "'Switch between interactive and manual mode' cancelled.")
        else:
            reply = QMessageBox.question(
                self, "Interactive mode",
                "Are you sure?\nThe current query will be removed.")

            if reply == QMessageBox.Yes:
                try:
                    self.queryText.clear()
                    self.queryText.setPlainText(
                        self.queryUI.getQuery().getQL())
                except BadFilterAttributes as e:
                    logging.error(str(e))
                except RuntimeError:
                    logging.warning("Failed to write query.")
                    self.queryText.clear()
                    self.queryText.setPlainText("")

                self.queryText.setReadOnly(True)

                self.queryUI.show()
                for action in self.requestMenu.actions():
                    action.setEnabled(True)
                self.manualModeMenu.setEnabled(False)
                self.showHideInteractiveModeAct.setEnabled(True)
                self.changeCurrentMap(0)

                logging.info("Switching to interactive mode.")
            else:
                logging.info(
                    "'Switch between interactive and manual mode' cancelled.")

        logging.info("Showing 'manual mode' polygon.")

    def addRequest(self, filters=None):
        self.queryUI.addRequestByFilters(filters)
        logging.info("Request added.")
        logging.debug("LINE")

    def addTemplate(self, filters):
        logging.info("Template applied.")
        self.queryUI.addRequestByFilters(filters)

    def removeRequest(self):
        reply = QMessageBox.question(
            self, "Remove current request",
            "Are you sure? This option is not undoable.")

        if reply == QMessageBox.Yes:
            self.queryUI.removeRequest()
            logging.info("'Remove request' successfully executed.")
        else:
            logging.info("'Remove request' cancelled.")
        logging.debug("LINE")

    def saveQuery(self):
        filename, selectedFilter = QFileDialog.getSaveFileName(
            self, 'Save query', expanduser("~/filename.txt"),
            "Text files (*.txt)")

        if filename != "":
            if self.queryText.isReadOnly():
                try:
                    query = self.queryUI.getQuery().getQL()
                    f = open(filename, "w+")
                    f.seek(0)
                    f.truncate()
                    f.write(query)
                    f.close()

                    logging.info("Query saved successfully.")
                except (RuntimeError, BadFilterAttributes) as e:
                    logging.error(str(e))
                except OSError:
                    logging.error(
                        "There was a problem creating the file with the query."
                    )
            else:
                try:
                    f = open(filename, "w+")
                    f.seek(0)
                    f.truncate()
                    f.write(self.queryText.toPlainText())
                    f.close()

                    logging.info("Query saved successfully.")
                except OSError:
                    logging.error(
                        "There was a problem creating the file with the query."
                    )
        else:
            logging.info("\"Save query\" canceled.")

        logging.debug("LINE")

    def openQuery(self):
        filename, selectedFilter = QFileDialog.getOpenFileName(
            self, 'Open query', expanduser("~/filename.txt"))

        if filename != "":
            try:
                if self.queryText.isReadOnly():
                    self.switchManualMode()

                f = open(filename, "r")
                self.queryText.clear()
                self.queryText.setPlainText(f.read())
                f.close()

                logging.info("File read successfully.")
            except UnicodeDecodeError:
                logging.error("The given file is not readable as text.")
            except OSError:
                logging.error("There was a problem opening the query file.")
        else:
            logging.info("\"Open query\" canceled.")

        logging.debug("LINE")

    def saveInteractiveQuery(self):
        filename, selectedFilter = QFileDialog.getSaveFileName(
            self, 'Save query', expanduser("~/filename.json"),
            "JSON files (*.json)")

        if filename != "":
            try:
                query = self.queryUI.getQuery()
                query.saveToFile(filename)
                logging.info("Query saved successfully.")
            except (RuntimeError, BadFilterAttributes) as e:
                logging.error(str(e))
            except OSError:
                logging.error(
                    "There was a problem creating the file with the query.")
        else:
            logging.info("\"Save query\" canceled.")

        logging.debug("LINE")

    def openInteractiveQuery(self):
        filename, selectedFilter = QFileDialog.getOpenFileName(
            self, 'Open query', expanduser("~/filename.json"))

        if filename != "":
            try:
                self.queryUI.setQuery(OverpassQuery.getFromFile(filename))

                if not self.queryText.isReadOnly():
                    self.switchManualMode()
            except json.decoder.JSONDecodeError:
                logging.error(
                    "The given file has not the right format (json). The file could not be opened."
                )
            except UnicodeDecodeError:
                logging.error(
                    "The given file is not readable as text. The file could not be opened."
                )
            except (TypeError, KeyError):
                logging.error(
                    "Fields are missing from the file or there are fields with the wrong data type. "
                    "The file could not be opened.")
            except OSError:
                logging.error(
                    "There was a problem opening the query file. The file could not be opened."
                )
        else:
            logging.info("\"Open query\" canceled.")

        logging.debug("LINE")

    def saveNet(self):
        filename, selectedFilter = QFileDialog.getSaveFileName(
            self, 'Save File', expanduser("~/filenameWithoutExtension"))
        if filename != "":
            buildNet(filename)
        else:
            logging.info("\"Save File\" canceled.")
        logging.debug("LINE")
        return filename

    def openNet(self):
        try:
            filename = self.saveNet()
            if filename == "":
                logging.error("Can't open NETEDIT without a file.")
            else:
                openNetedit(filename + ".net.xml")
                logging.info("Opening NETEDIT.")
                logging.warning(
                    "If NETEDIT is not open in ten seconds, there was an unhandled problem."
                )
                logging.debug("LINE")
        except OSError:
            logging.error("Can't find NETEDIT.")
        except Exception:
            logging.error(traceback.format_exc())

    # POLYGONS
    def changeCurrentMap(self, i):
        if self.queryUI.getCurrentMap() is None:
            self.mapRenderer.setPage(self.emptyMapPage)
        else:
            self.mapRenderer.setPage(self.queryUI.getCurrentMap())

    def playQuery(self):
        newRecord = {
            "interactiveMode": self.queryText.isReadOnly(),
            "query": self.queryText.toPlainText(),
            "html": ""
        }

        if self.queryText.isReadOnly():
            try:
                query = self.queryUI.getQuery()
                newRecord["query"] = query
                self.queryText.setPlainText(query.getQL())
            except (RuntimeError, BadFilterAttributes) as e:
                logging.error(str(e))
                return
        try:
            html = buildHTMLWithQuery(self.queryText.toPlainText())
            if self.queryText.isReadOnly():
                self.mapRenderer.setPage(self.queryUI.updateMaps(html))
                newRecord["html"] = html
            else:
                soup = bs4.BeautifulSoup(html, features="html.parser")
                js = soup.new_tag("script")
                js.string = (MANUAL_MODE_JS_SCRIPT % (str([])))
                soup.append(js)

                self.manualModePage.setHtml(str(soup))
                newRecord["html"] = str(soup)
                self.mapRenderer.setPage(self.manualModePage)
            logging.info("Query drawn.")
            logging.debug("LINE")
            self.addRecord(newRecord)
        except (OverpassRequestException, OsmnxException) as e:
            logging.error(str(e))
        except ox.EmptyOverpassResponse:
            logging.error("There are no elements with the given query.")
        except OSError:
            logging.error(
                "There was a problem creating the file with the request response."
            )
        except Exception:
            logging.error(traceback.format_exc())

    def addRecord(self, newRecord):
        self.record.append(newRecord)
        index = len(self.record) - 1

        recordAction = QAction(datetime.datetime.now().strftime('%H:%M:%S'),
                               self)
        recordAction.triggered.connect(lambda: self.changeMap(index))
        self.recordMenu.addAction(recordAction)

    def playTableRow(self):
        try:
            self.mapRenderer.setPage(self.queryUI.updateMapFromRow())
        except (OverpassRequestException, OsmnxException) as e:
            logging.error(str(e))
            logging.warning(
                "Before open NETEDIT you must run a query with the row filters applied."
            )
        except ox.EmptyOverpassResponse:
            logging.error("There are no elements with the given row.")
        except OSError:
            logging.error(
                "There was a problem creating the file with the row selection."
            )
        except RuntimeError as e:
            logging.error(str(e))
        except Exception:
            logging.error(traceback.format_exc())
        logging.debug("LINE")

    # EVENTS
    def closeEvent(self, event):
        for f in os.listdir(tempDir):
            os.remove(os.path.join(tempDir, f))
        QMainWindow.closeEvent(self, event)
コード例 #8
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        # Init a state dict
        self.state = {
            "internet": {
                "connected": False,
            },
            "serial": {
                "client": serial.Serial(),
                "connected": False,
                "device": "",
                "baud": 0,
            },
            "mqtt": {
                "client": mqtt.Client(),
                "connected": False,
                "hostname": "",
                "port": "",
                "username": "",
            }
        }

        # Grab config
        self.config = configparser.ConfigParser()
        self.config.read('config.ini')

        # Setup MQTT Callbacks
        self.state["mqtt"]["client"].on_connect = self.mqtt_on_connect
        self.state["mqtt"]["client"].on_disconnect = self.mqtt_on_disconnect
        self.state["mqtt"]["client"].on_subscribe = self.mqtt_on_subscribe
        self.state["mqtt"]["client"].on_unsubscribe = self.mqtt_on_unsubscribe
        self.state["mqtt"]["client"].on_publish = self.mqtt_on_publish
        self.state["mqtt"]["client"].on_message = self.mqtt_on_message
        self.state["mqtt"]["client"].on_log = self.mqtt_on_log

        # Setup support things
        self.create_icons()

        # Setup main window
        self.layout_main_window = QGridLayout()

        self.setWindowTitle("Icarus GCS")
        self.setWindowIcon(QIcon('assets/balloon_map_icon.png'))

        self.create_toolbar_interface()
        self.create_map_interface()
        self.create_serial_interface()
        self.create_mqtt_interface()

        self.layout_main_window.addWidget(self.toolbar_status, 0, 1, 1, -1)
        self.layout_main_window.addWidget(self.webengine_map, 1, 1, -1, -1)
        self.layout_main_window.addWidget(self.groupbox_serial_interface, 0, 0,
                                          2, 1)
        self.layout_main_window.addWidget(self.groupbox_mqtt_interface, 2, 0)

        self.setLayout(self.layout_main_window)

        # Setup timers for events
        self.timer_internet_status = QTimer(self)
        self.timer_internet_status.timeout.connect(
            self.on_timer_internet_status)
        self.timer_internet_status.start(5 * 1000)

        # Check timed events once at the start
        # self.on_timer_internet_status()

        # Let's do this!
        self.show()

    def create_toolbar_interface(self):
        self.toolbar_status = QToolBar()
        self.toolbar_status.setFloatable(False)
        self.toolbar_status.setMovable(False)
        self.toolbar_status.setIconSize(QSize(24, 24))

        self.toolbutton_internet_status = QToolButton()
        self.toolbutton_internet_status.setIcon(self.icons["cloud-line"])
        self.toolbutton_internet_status.setEnabled(False)

        self.toolbutton_mqtt_status = QToolButton()
        self.toolbutton_mqtt_status.setIcon(self.icons["server-line"])
        self.toolbutton_mqtt_status.setEnabled(False)

        self.toolbutton_radio_status = QToolButton()
        self.toolbutton_radio_status.setIcon(self.icons["wifi-line"])
        self.toolbutton_radio_status.setEnabled(False)

        self.toolbutton_cellular_status = QToolButton()
        self.toolbutton_cellular_status.setIcon(self.icons["sim-card-line"])
        self.toolbutton_cellular_status.setEnabled(False)

        self.toolbutton_heartbeat_status = QToolButton()
        self.toolbutton_heartbeat_status.setIcon(self.icons["heart-line"])
        self.toolbutton_heartbeat_status.setEnabled(False)

        self.toolbar_status.addWidget(self.toolbutton_internet_status)
        self.toolbar_status.addWidget(self.toolbutton_mqtt_status)
        self.toolbar_status.addWidget(self.toolbutton_radio_status)
        self.toolbar_status.addWidget(self.toolbutton_cellular_status)
        self.toolbar_status.addWidget(self.toolbutton_heartbeat_status)

    def create_map_interface(self):
        self.webengine_map = QWebEngineView()

        self.map_wrapper = MapWrapper(
            self.webengine_map, {
                "home_latitude": self.config["Map"]["Home Latitude"],
                "home_longitude": self.config["Map"]["Home Longitude"],
                "home_zoom": self.config["Map"]["Zoom Level"],
                "max_zoom": self.config["Map"]["Max Zoom Level"],
                "min_zoom": self.config["Map"]["Min Zoom Level"],
            })

        self.map_view_webchannel = QWebChannel()
        self.map_view_webchannel.registerObject("python_link",
                                                self.map_wrapper)
        self.webengine_map.page().setWebChannel(self.map_view_webchannel)
        self.webengine_map.load(
            QtCore.QUrl.fromLocalFile(
                QtCore.QDir.current().filePath("static/map.html")))
        self.webengine_map.setMinimumWidth(1024)
        self.webengine_map.setMinimumHeight(768)

    def create_serial_interface(self):
        self.groupbox_serial_interface = QGroupBox("Serial")
        self.groupbox_serial_interface.setMinimumWidth(320)
        self.groupbox_serial_interface.setMaximumWidth(320)
        self.layout_serial_interface = QGridLayout()
        self.groupbox_serial_interface.setLayout(self.layout_serial_interface)

        self.label_serial_port = QLabel(self)
        self.label_serial_port.setText("Device:")
        self.label_serial_port.setMinimumWidth(100)
        self.label_serial_port.setMaximumWidth(100)

        self.combo_serial_port = QComboBox(self)
        devices = self.serial_list_ports()
        for device in devices:
            self.combo_serial_port.addItem(device)

        self.label_serial_baud_rate = QLabel(self)
        self.label_serial_baud_rate.setText("Baud Rate:")

        self.line_edit_serial_baud_rate = QLineEdit(self)
        self.line_edit_serial_baud_rate.setText("115200")

        self.button_serial_connect = QPushButton(self)
        self.button_serial_connect.setText("Connect")
        self.button_serial_connect.clicked.connect(
            self.button_serial_connect_clicked)

        self.layout_serial_interface.addWidget(self.label_serial_port, 0, 0)
        self.layout_serial_interface.addWidget(self.combo_serial_port, 0, 1)
        self.layout_serial_interface.addWidget(self.label_serial_baud_rate, 1,
                                               0)
        self.layout_serial_interface.addWidget(self.line_edit_serial_baud_rate,
                                               1, 1)
        self.layout_serial_interface.addWidget(self.button_serial_connect, 2,
                                               0, 1, 2)

    def lock_serial_interface(self):
        self.combo_serial_port.setReadOnly(True)
        self.line_edit_serial_baud_rate.setReadOnly(True)

        self.button_serial_connect.setText("Disconnect")

    def unlock_serial_interface(self):
        self.combo_serial_port.setReadOnly(False)
        self.line_edit_serial_baud_rate.setReadOnly(False)

        self.button_serial_connect.setText("Connect")

    def serial_list_ports(self):
        ports = list(serial.tools.list_ports.comports(False))
        devices = []

        for port in ports:
            devices.append(str(port.description) + " (" + port.device + ")")

        return devices

    def button_serial_connect_clicked(self):
        if not self.state["serial"]["connected"]:
            selected_serial_port = self.combo_serial_port.currentText().split(
                "(")[1].replace(")", "")

            try:
                self.state["serial"]["client"].port = selected_serial_port
                self.state["serial"]["client"].baudrate = int(
                    self.line_edit_serial_baud_rate.text())
                self.state["serial"]["client"].open()

                self.state["serial"]["connected"] = True
                self.state["serial"]["device"] = selected_serial_port
                self.state["serial"][
                    "baud"] = self.line_edit_serial_baud_rate.text()

                self.lock_serial_interface()

                print("Connected to: " + self.combo_serial_port.currentText())
            except serial.SerialException as e:
                print("Failed to connect to Serial Port: " +
                      self.combo_serial_port.currentText())
                print(e)
        else:
            try:
                self.serial_port.close()

                self.state["serial"]["connected"] = False
                self.state["serial"]["device"] = ""
                self.state["serial"]["baud"] = 0

                self.unlock_serial_interface()

                print("Disconnected from: " + self.state["serial"]["device"])
            except serial.SerialException as e:
                print("Failed to disconnect from: " +
                      self.state["serial"]["device"])
                print(e)

    def create_mqtt_interface(self):
        self.groupbox_mqtt_interface = QGroupBox("MQTT")
        self.groupbox_mqtt_interface.setMinimumWidth(320)
        self.groupbox_mqtt_interface.setMaximumWidth(320)
        self.layout_mqtt_interface = QGridLayout()
        self.groupbox_mqtt_interface.setLayout(self.layout_mqtt_interface)

        self.label_internet_status = QLabel(self)
        self.label_internet_status.setText("Internet:")
        self.label_internet_status.setMinimumWidth(100)
        self.label_internet_status.setMaximumWidth(100)

        self.line_edit_internet_status = QLineEdit(self)
        self.line_edit_internet_status.setText("Disconnected")
        self.line_edit_internet_status.setReadOnly(True)

        self.label_mqtt_hostname = QLabel(self)
        self.label_mqtt_hostname.setText("Hostname:")

        self.line_edit_mqtt_hostname = QLineEdit(self)
        self.line_edit_mqtt_hostname.setText(self.config["MQTT"]["Hostname"])

        self.label_mqtt_port = QLabel(self)
        self.label_mqtt_port.setText("Port: ")

        self.line_edit_mqtt_port = QLineEdit(self)
        self.line_edit_mqtt_port.setText(self.config["MQTT"]["Port"])

        self.label_mqtt_username = QLabel(self)
        self.label_mqtt_username.setText("Username: "******"MQTT"]["Username"])

        self.label_mqtt_password = QLabel(self)
        self.label_mqtt_password.setText("Password: "******"MQTT"]["Password"])
        self.line_edit_mqtt_password.setEchoMode(QLineEdit.Password)

        self.button_mqtt_connect = QPushButton(self)
        self.button_mqtt_connect.setText("Connect")
        self.button_mqtt_connect.clicked.connect(
            self.button_mqtt_connect_clicked)

        self.label_mqtt_topic = QLabel(self)
        self.label_mqtt_topic.setText("Topic:")

        self.line_edit_mqtt_topic = QLineEdit(self)

        self.button_mqtt_subscribe = QPushButton(self)
        self.button_mqtt_subscribe.setText("Subscribe")
        #self.button_mqtt_subscribe.clicked.connect(self.button_mqtt_subscribe_clicked)

        self.list_box_mqtt_subscriptions = QListWidget(self)
        self.list_box_mqtt_subscriptions.setMaximumHeight(100)

        self.button_mqtt_unsubscribe = QPushButton(self)
        self.button_mqtt_unsubscribe.setText("Unsubscribe")
        #self.button_mqtt_unsubscribe.clicked.connect(self.button_mqtt_unsubscribe_clicked)

        self.layout_mqtt_interface.addWidget(self.label_internet_status, 0, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_internet_status, 0,
                                             1)
        self.layout_mqtt_interface.addWidget(self.label_mqtt_hostname, 1, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_mqtt_hostname, 1,
                                             1)
        self.layout_mqtt_interface.addWidget(self.label_mqtt_port, 2, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_mqtt_port, 2, 1)
        self.layout_mqtt_interface.addWidget(self.label_mqtt_username, 3, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_mqtt_username, 3,
                                             1)
        self.layout_mqtt_interface.addWidget(self.label_mqtt_password, 4, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_mqtt_password, 4,
                                             1)
        self.layout_mqtt_interface.addWidget(self.button_mqtt_connect, 5, 0, 1,
                                             2)
        self.layout_mqtt_interface.addWidget(self.label_mqtt_topic, 6, 0)
        self.layout_mqtt_interface.addWidget(self.line_edit_mqtt_topic, 6, 1)
        self.layout_mqtt_interface.addWidget(self.button_mqtt_subscribe, 7, 0,
                                             1, 2)
        self.layout_mqtt_interface.addWidget(self.list_box_mqtt_subscriptions,
                                             8, 0, 1, 2)
        self.layout_mqtt_interface.addWidget(self.button_mqtt_unsubscribe, 9,
                                             0, 1, 2)

    def lock_mqtt_interface(self):
        self.line_edit_mqtt_hostname.setReadOnly(True)
        self.line_edit_mqtt_port.setReadOnly(True)
        self.line_edit_mqtt_username.setReadOnly(True)
        self.line_edit_mqtt_password.setReadOnly(True)

        self.button_mqtt_connect.setText("Disconnect")

    def unlock_mqtt_interface(self):
        self.line_edit_mqtt_hostname.setReadOnly(False)
        self.line_edit_mqtt_port.setReadOnly(False)
        self.line_edit_mqtt_username.setReadOnly(False)
        self.line_edit_mqtt_password.setReadOnly(False)

        self.button_mqtt_connect.setText("Connect")

    def button_mqtt_connect_clicked(self):
        if not self.state["mqtt"]["connected"]:
            self.state["mqtt"]["client"].username_pw_set(
                self.line_edit_mqtt_username.text(),
                self.line_edit_mqtt_password.text())
            self.state["mqtt"]["client"].connect(
                self.line_edit_mqtt_hostname.text(),
                int(self.line_edit_mqtt_port.text()), 30)
            self.state["mqtt"]["client"].loop_start()
        else:
            self.state["mqtt"]["client"].loop_stop()
            self.state["mqtt"]["client"].disconnect()

    def create_chart_interface(self):
        fig = Figure(figsize=(100, 100), dpi=100)
        self.axes = fig.add_subplot(111)
        fig.axes.plot([0, 1, 2, 3, 4], [10, 1, 20, 3, 40])

        self.wid
        self.layout_chart_interface = QGridLayout()

    def on_timer_internet_status(self):
        try:
            socket.create_connection(("1.1.1.1", 53))
            self.state["internet"]["connected"] = True
            self.line_edit_internet_status.setText("Connected")
            self.toolbutton_internet_status.setIcon(self.icons["cloud"])
            return True
        except OSError:
            pass

        self.state["internet"]["connected"] = False
        self.line_edit_internet_status.setText("Disconnected")
        self.toolbutton_internet_status.setIcon(self.icons["cloud-line"])
        return False

    def mqtt_on_connect(self, client, userdata, flags, return_code):
        if return_code == 0:
            print("Connected to MQTT broker at: " +
                  self.line_edit_mqtt_hostname.text())
            self.state["mqtt"]["connected"] = True
            self.state["mqtt"]["hostname"] = self.line_edit_mqtt_hostname.text(
            )
            self.state["mqtt"]["port"] = self.line_edit_mqtt_port.text()
            self.state["mqtt"]["username"] = self.line_edit_mqtt_username.text(
            )
            self.toolbutton_mqtt_status.setIcon(self.icons["server"])
            self.lock_mqtt_interface()
        else:
            print("Failed to connect to MQTT broker at: " +
                  self.line_edit_mqtt_hostname.text())

    def mqtt_on_disconnect(self, client, userdata, return_code):
        if return_code == 0:
            print("Disconnected from MQTT broker")
        else:
            print(
                "Failed to cleanly disconnect from MQTT broker, already disconnected"
            )

        self.state["mqtt"]["connected"] = False
        self.state["mqtt"]["hostname"] = ""
        self.state["mqtt"]["port"] = ""
        self.state["mqtt"]["username"] = ""
        self.toolbutton_mqtt_status.setIcon(self.icons["server-line"])
        self.unlock_mqtt_interface()

    def mqtt_on_subscribe(self, client, userdata, message_id, granted_qos):
        print("test")

    def mqtt_on_unsubscribe(self, client, userdata, message_id):
        print("test")

    def mqtt_on_publish(self, client, userdata, message_id):
        print("test")

    def mqtt_on_message(self, client, userdata, message):
        print("test")

    def mqtt_on_log(self, client, userdata, level, string):
        print(level + ", " + string)

    def create_icons(self):
        self.icons = {}

        icon_path = "assets/remix/icons"

        self.icons["cloud"] = QIcon(icon_path + "/Business/cloud-fill.svg")
        self.icons["cloud-line"] = QIcon(icon_path +
                                         "/Business/cloud-line.svg")
        self.icons["cloud-off"] = QIcon(icon_path +
                                        "/Business/cloud-off-fill.svg")
        self.icons["wifi"] = QIcon(icon_path + "/Device/signal-wifi-fill.svg")
        self.icons["wifi-line"] = QIcon(icon_path +
                                        "/Device/signal-wifi-line.svg")
        self.icons["server"] = QIcon(icon_path + "/Device/server-fill.svg")
        self.icons["server-line"] = QIcon(icon_path +
                                          "/Device/server-line.svg")
        self.icons["wifi-off"] = QIcon(icon_path +
                                       "/Device/signal-wifi-off-fill.svg")
        self.icons["radio"] = QIcon(icon_path + "/Device/wifi-fill.svg")
        self.icons["radio-off"] = QIcon(icon_path +
                                        "/Device/wifi-off-fill.svg")
        self.icons["sim-card"] = QIcon(icon_path +
                                       "/Device/sim-card-2-fill.svg")
        self.icons["sim-card-line"] = QIcon(icon_path +
                                            "/Device/sim-card-2-line.svg")
        self.icons["takeoff"] = QIcon(icon_path +
                                      "/Map/flight-takeoff-fill.svg")
        self.icons["land"] = QIcon(icon_path + "/Map/flight-land-fill.svg")
        self.icons["heart"] = QIcon(icon_path + "/Health/heart-3-fill.svg")
        self.icons["heart-line"] = QIcon(icon_path +
                                         "/Health/heart-3-line.svg")
        self.icons["heart-off"] = QIcon(icon_path + "/Health/dislike-fill.svg")
        self.icons["heart-pulse"] = QIcon(icon_path +
                                          "/Health/heart-pulse-fill.svg")
コード例 #9
0
class EmailViewerPageView(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.c = EmailViewerPageController()
        self.c.on_viewemail.connect(self.update_content)
        self.c.on_clearview.connect(self.clear_content)

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        self.splitter = QSplitter()

        self._web_engine = QWebEngineView(self)
        self._web_engine.setMinimumWidth(330)
        self._web_engine.setSizePolicy(QSizePolicy.MinimumExpanding,
                                       QSizePolicy.Expanding)
        self.email_page = self._web_engine.page()
        self.splitter.addWidget(self._web_engine)

        attachment_model = AttachmentListModel()
        self.attachments = AttachmentsView(self)
        self.attachments.setMaximumWidth(300)
        self.attachments.set_model(attachment_model)
        self._attachments_collapsed = False
        self.splitter.addWidget(self.attachments)

        layout.addWidget(self.splitter)
        self.setLayout(layout)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        # TODO: Fix the splitter not resizing when _web_engine gets smaller than minimumSize.
        #  Inspect sizes() for that, check if _web_engines size is less than minimumSize and
        #  somehow update the splitter's handle, it just has to be touched in order for it to resize
        #  to the proper size.
        #  !!! NOTE: First call to sizes returns [0, 0].
        page_size, attachments_size = self.splitter.sizes()
        if page_size == 0 and attachments_size == 0:
            return
        elif page_size < self._web_engine.minimumWidth(
        ) and not self._attachments_collapsed:
            self.splitter.setSizes((self.splitter.width(), 0))
            self._attachments_collapsed = True
        elif self._attachments_collapsed:
            splitter_width = self.splitter.width()
            size1 = self._web_engine.minimumWidth()
            size2 = self.attachments.sizeHint().width()
            if splitter_width > size1 + size2:
                self.splitter.setSizes((splitter_width - size2, size2))
                self._attachments_collapsed = False

    def update_content(self, body, attachments, error=None):
        if error:
            err_dialog = ErrorReportDialog(error)
            err_dialog.exec_()
            return

        self.attachments.clear_attachments()
        self.email_page.runJavaScript(
            f'document.open(); document.write(""); document.write(`{body}`); document.close();'
        )

        self.attachments.append_attachments(attachments)

    def clear_content(self, flag):
        self.attachments.clear_attachments()
        self.email_page.runJavaScript(
            'document.open(); document.write(""); document.close();')
コード例 #10
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.cur_file = ""
        self.splitter = QSplitter()
        self.text_edit = QTextEdit("")
        self.preview = QWebEngineView()
        self.preview.setMinimumWidth(300)
        self.setWindowTitle("Markdown [*]")
        self.splitter.addWidget(self.text_edit)
        self.splitter.addWidget(self.preview)
        self.setCentralWidget(self.splitter)
        self.createMenus()
        self.createStatusBar()
        self.readSettings()
        self.text_edit.document().contentsChanged.connect(
            self.documentWasModified)
        self.text_edit.textChanged.connect(self.textChanged)

    def closeEvent(self, event):
        if self.maybeSave():
            self.writeSettings()
            event.accept()
        else:
            event.ignore()

    def documentWasModified(self):
        self.setWindowModified(self.text_edit.document().isModified())

    def createMenus(self):
        new_icon = QIcon("./assets/new.png")
        open_icon = QIcon("./assets/open.png")
        save_icon = QIcon("./assets/save.png")
        save_as_icon = QIcon("./assets/save_as.png")
        exit_icon = QIcon("./assets/exit.png")

        new_act = QAction(new_icon, "&New", self)
        new_act.setShortcuts(QKeySequence.New)
        new_act.setStatusTip("Create a new file")
        new_act.triggered.connect(self.newFile)

        open_act = QAction(open_icon, "&Open", self)
        open_act.setShortcuts(QKeySequence.Open)
        open_act.setStatusTip("Open an existing file")
        open_act.triggered.connect(self.open)

        save_act = QAction(save_icon, "&Save", self)
        save_act.setShortcuts(QKeySequence.Save)
        save_act.setStatusTip("Save the document to disk")
        save_act.triggered.connect(self.save)

        save_as_act = QAction(save_as_icon, "Save &As...", self)
        save_as_act.setShortcuts(QKeySequence.SaveAs)
        save_as_act.setStatusTip("Save the document under a new name")
        save_as_act.triggered.connect(self.saveAs)

        exit_act = QAction(exit_icon, "E&xit", self)
        exit_act.setShortcuts(QKeySequence.Quit)
        exit_act.setStatusTip("Exit the application")
        exit_act.triggered.connect(self.close)

        about_act = QAction("&About", self)
        about_act.triggered.connect(self.about)
        about_act.setStatusTip("Show the application's About box")

        file_menu = self.menuBar().addMenu("&File")
        file_menu.addAction(new_act)
        file_menu.addAction(open_act)
        file_menu.addAction(save_act)
        file_menu.addAction(save_as_act)
        file_menu.addSeparator()
        file_menu.addAction(exit_act)

        help_menu = self.menuBar().addMenu("&Help")
        help_menu.addAction(about_act)

        file_tool_bar = self.addToolBar("File")
        file_tool_bar.addAction(new_act)
        file_tool_bar.addAction(open_act)
        file_tool_bar.addAction(save_act)

    def createStatusBar(self):
        self.statusBar().showMessage("Ready")

    def about(self):
        QMessageBox.about(
            self, "About Markdown", "This app demonstrates how to "
            "write modern GUI applications using Qt, with a menu bar, "
            "toolbars, and a status bar.")

    def newFile(self):
        if self.maybeSave():
            self.text_edit.clear()
        self.setCurrentFile("")

    def open(self):
        if self.maybeSave():
            fileName = QFileDialog.getOpenFileName(self)[0]
        if fileName:
            self.loadFile(fileName)

    def save(self):
        if not self.cur_file:
            return self.saveAs()
        else:
            return self.saveFile(self.cur_file)

    def saveAs(self):
        dialog = QFileDialog(self)
        dialog.setWindowModality(Qt.WindowModal)
        dialog.setAcceptMode(QFileDialog.AcceptSave)
        if dialog.exec() != QDialog.Accepted:
            return False
        return self.saveFile(dialog.selectedFiles()[0])

    def maybeSave(self):
        if not self.text_edit.document().isModified():
            return True
        ret = QMessageBox.warning(
            self, "Qt Demo", "The document has been modified.\n"
            "Do you want to save your changes?",
            QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
        if ret == QMessageBox.Save:
            return self.save()
        elif ret == QMessageBox.Cancel:
            return False
        return True

    def loadFile(self, fileName):
        with open(fileName, mode="r") as f:
            text = f.read()

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.setCurrentFile(fileName)
        self.text_edit.setPlainText(text)
        self.text_edit.document().setModified(False)
        self.setWindowModified(False)
        QApplication.restoreOverrideCursor()
        self.statusBar().showMessage("File loaded", 2000)

    def saveFile(self, fileName):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        with open(fileName, "w") as f:
            f.write(self.text_edit.toPlainText())
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName)
        self.text_edit.document().setModified(False)
        self.setWindowModified(False)
        self.statusBar().showMessage("File saved", 2000)

    def setCurrentFile(self, fileName):
        self.cur_file = fileName
        shown_name = self.cur_file
        if not self.cur_file:
            shown_name = "untitled.txt"
        self.setWindowFilePath(shown_name)

    def writeSettings(self):
        settings = QSettings(QCoreApplication.organizationName(),
                             QCoreApplication.applicationName())
        settings.setValue("geometry", self.saveGeometry())

    def readSettings(self):
        settings = QSettings(QCoreApplication.organizationName(),
                             QCoreApplication.applicationName())
        geometry = settings.value("geometry", QByteArray())
        if not geometry:
            availableGeometry = QApplication.desktop().availableGeometry(self)
            self.resize(availableGeometry.width() / 3,
                        availableGeometry.height() / 2)
            self.move((availableGeometry.width() - self.width()) / 2,
                      (availableGeometry.height() - self.height()) / 2)
        else:
            self.restoreGeometry(geometry)

    def textChanged(self):
        path = os.getcwd()
        html = "<html><head><link href=\"assets/pastie.css\" rel=\"stylesheet\" type=\"text/css\"/></head><body>"
        html += markdown2.markdown(self.text_edit.toPlainText(), ...,
                                   extras=["fenced-code-blocks"])
        html += "</body></html>"
        self.preview.setHtml(html, baseUrl=QUrl("file://" + path + "/"))
コード例 #11
0
class App(QWidget):

    # 初期化
    def __init__(self):
        self.connection_pool = mysql.connector.pooling.MySQLConnectionPool(
            pool_name="pynative_pool",
            pool_size=1,
            pool_reset_session=True,
            host='news-recorder.cnfoeuugi3ju.ap-northeast-1.rds.amazonaws.com',
            database='newsrecorder',
            user='******',
            password=os.environ['NEWS_PASS'],
            ssl_ca='rds-combined-ca-bundle.pem',
            charset='utf8')
        super().__init__()
        self.browser = QWebEngineView()
        self.model = QStandardItemModel()
        self.listWidget = QListView()
        self.init_ui()

    # ユーザーインターフェイスを初期化する
    def init_ui(self):
        self.listWidget.setModel(self.model)
        self.listWidget.setAlternatingRowColors(True)
        self.listWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.listWidget.setSelectionMode(QAbstractItemView.SingleSelection)
        self.listWidget.selectionModel().selectionChanged.connect(
            self.load_html)
        self.listWidget.setItemDelegate(ItemDelegate(self.listWidget))
        grid = QGridLayout()
        grid.setSpacing(10)
        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(self.listWidget)
        splitter.addWidget(self.browser)
        grid.addWidget(splitter, 1, 0)
        self.init_list_items()
        self.setLayout(grid)
        self.resize(1200, 800)
        self.listWidget.setMinimumWidth(600)
        self.browser.setMinimumWidth(600)
        self.show()

    # 記事本文をデータベースから取り出す(URLの)
    def fetch_contents(self, url):
        with closing(self.connection_pool.get_connection()) as conn:
            c = conn.cursor()
            c.execute("SELECT contents FROM articles WHERE url=%s;", [url])
            rows = c.fetchall()
            for row in rows:
                return row[0]

    # ブラウザに記事をセットする
    def load_html(self):
        for i in self.listWidget.selectedIndexes():
            html = '<h1>' + i.data()[1] + '</h1>' + self.fetch_contents(
                i.data()[0])
            self.browser.setHtml(html)

    # リストを初期化する
    def init_list_items(self):
        with closing(self.connection_pool.get_connection()) as conn:
            c = conn.cursor()
            c.execute("SELECT url,title FROM articles;")
            rows = c.fetchall()
            for row in rows:
                item = QStandardItem()
                item.setData(row, Qt.DisplayRole)
                item.setSizeHint(QSize(20, 30))
                self.model.appendRow(item)