Esempio n. 1
0
class Ui:
    def __init__(self, app):
        self._app = app
        self._layout = QVBoxLayout(app)
        self._top_separator = Separator(parent=app)
        self._bottom_separator = Separator(parent=app)
        self._splitter = QSplitter(app)
        if use_mac_theme():
            self._splitter.setHandleWidth(0)

        # NOTE: 以位置命名的部件应该只用来组织界面布局,不要
        # 给其添加任何功能性的函数
        self.top_panel = TopPanel(app, app)
        self.bottom_panel = BottomPanel(app, app)
        self._left_panel_container = QScrollArea(app)
        self._left_panel_container.setWidgetResizable(True)
        self._left_panel_container.setFrameShape(QFrame.NoFrame)
        self.left_panel = LeftPanel(self._app, self._left_panel_container)
        self._left_panel_container.setWidget(self.left_panel)
        self.right_panel = RightPanel(self._app, self._splitter)
        self.mpv_widget = MpvOpenGLWidget(self._app)
        self.mpv_widget.hide()

        # alias
        self.magicbox = self.bottom_panel.magicbox
        self.pc_panel = self.top_panel.pc_panel
        self.table_container = self.right_panel.table_container
        self.songs_table = self.right_panel.table_container
        self.back_btn = self.bottom_panel.back_btn
        self.forward_btn = self.bottom_panel.forward_btn
        self.toggle_video_btn = self.pc_panel.toggle_video_btn

        # 对部件进行一些 UI 层面的初始化
        self._splitter.addWidget(self._left_panel_container)
        self._splitter.addWidget(self.right_panel)

        self.right_panel.setMinimumWidth(780)
        self._left_panel_container.setSizePolicy(QSizePolicy.Minimum,
                                                 QSizePolicy.Expanding)
        self.right_panel.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)

        self._layout.addWidget(self.bottom_panel)
        self._layout.addWidget(self._bottom_separator)
        self._layout.addWidget(self._splitter)
        self._layout.addWidget(self.mpv_widget)
        self._layout.addWidget(self._top_separator)
        self._layout.addWidget(self.top_panel)

        self._layout.setSpacing(0)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.top_panel.layout().setSpacing(0)
        self.top_panel.layout().setContentsMargins(0, 0, 0, 0)

        self.pc_panel.playlist_btn.clicked.connect(self.show_player_playlist)
        self.pc_panel.mv_btn.clicked.connect(self._play_mv)
        self.toggle_video_btn.clicked.connect(self._toggle_video_widget)
        self._app.player.video_format_changed.connect(
            self.on_video_format_changed)

        self._app.hotkey_mgr.registe(
            [QKeySequence('Ctrl+F'),
             QKeySequence(':'),
             QKeySequence('Alt+x')], self.magicbox.setFocus)

    def _play_mv(self):
        song = self._app.player.current_song
        url = song.mv.media.url_ahap
        self._app.player.play(url)
        self.show_video_widget()

    def show_player_playlist(self):
        songs = self._app.playlist.list()
        self.table_container.show_player_playlist(songs)

    def on_video_format_changed(self, vformat):
        if vformat is None:
            self.hide_video_widget()
            self.toggle_video_btn.hide()
        else:
            self.toggle_video_btn.show()

    def _toggle_video_widget(self):
        if self.mpv_widget.isVisible():
            self.hide_video_widget()
        else:
            self.show_video_widget()

    def hide_video_widget(self):
        self.mpv_widget.hide()
        self._splitter.show()
        self.bottom_panel.show()
        self._bottom_separator.show()
        self.pc_panel.toggle_video_btn.setText('△')

    def show_video_widget(self):
        self.bottom_panel.hide()
        self._bottom_separator.hide()
        self._splitter.hide()
        self.mpv_widget.show()
        self.pc_panel.toggle_video_btn.setText('▽')
Esempio n. 2
0
class Ui:
    def __init__(self, app):
        self._app = app
        self._layout = QVBoxLayout(app)
        self._top_separator = Separator(parent=app)
        self._splitter = QSplitter(app)

        # NOTE: 以位置命名的部件应该只用来组织界面布局,不要
        # 给其添加任何功能性的函数
        self.top_panel = TopPanel(app, app)
        self._left_panel_con = LeftPanel(self._app, )
        self.left_panel = self._left_panel_con.p
        self.right_panel = RightPanel(self._app, self._splitter)
        self.bottom_panel = self.right_panel.bottom_panel
        self.mpv_widget = MpvOpenGLWidget(self._app)

        # alias
        self.magicbox = self.bottom_panel.magicbox
        self.pc_panel = self.top_panel.pc_panel
        self.table_container = self.right_panel.table_container
        # backward compatible, old name is songs_table_container
        self.songs_table_container = self.table_container
        self.songs_table = self.table_container.songs_table
        self.back_btn = self.bottom_panel.back_btn
        self.forward_btn = self.bottom_panel.forward_btn
        self.toggle_video_btn = self.pc_panel.toggle_video_btn

        self.pc_panel.playlist_btn.clicked.connect(
            lambda: self._app.browser.goto(uri='/player_playlist'))
        self.pc_panel.mv_btn.clicked.connect(self._play_mv)
        self.toggle_video_btn.clicked.connect(self._toggle_video)
        self._app.player.video_format_changed.connect(
            self.on_video_format_changed, aioqueue=True)

        self.show_video_widget()
        self._app.initialized.connect(lambda app: self.hide_video_widget(),
                                      weak=False)
        self._setup_ui()

    def _setup_ui(self):
        self._app.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

        self._splitter.setHandleWidth(0)
        self._splitter.addWidget(self._left_panel_con)
        self._splitter.addWidget(self.right_panel)

        self.right_panel.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)

        # self._layout.addWidget(self.bottom_panel)
        self._layout.addWidget(self._splitter)
        self._layout.addWidget(self.mpv_widget)
        self._layout.addWidget(self._top_separator)
        self._layout.addWidget(self.top_panel)

        self._layout.setSpacing(0)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.top_panel.layout().setSpacing(0)
        self.top_panel.layout().setContentsMargins(0, 0, 0, 0)

        self._app.resize(880, 600)

    def _play_mv(self):
        song = self._app.player.current_song
        mv = song.mv if song else None
        if mv is not None:
            if mv.meta.support_multi_quality:
                media, _ = mv.select_media()
            else:
                media = mv.media
            self.toggle_video_btn.show()
            self.show_video_widget()
            self._app.player.play(media)

    def on_video_format_changed(self, vformat):
        """when video is available, show toggle_video_btn"""
        if vformat is None:
            self.hide_video_widget()
            self.toggle_video_btn.hide()
        else:
            self.toggle_video_btn.show()

    def _toggle_video(self):
        if self.mpv_widget.isVisible():
            self.hide_video_widget()
        else:
            self.show_video_widget()

    def hide_video_widget(self):
        self.mpv_widget.hide()
        self._splitter.show()
        self.bottom_panel.show()
        self.pc_panel.toggle_video_btn.setText('△')

    def show_video_widget(self):
        self.bottom_panel.hide()
        self._splitter.hide()
        self.mpv_widget.show()
        self.pc_panel.toggle_video_btn.setText('▽')
Esempio n. 3
0
class Ui:

    def __init__(self, app):
        self._app = app
        self._layout = QVBoxLayout(app)
        self._top_separator = Separator(parent=app)
        self._bottom_separator = Separator(parent=app)
        self._splitter = QSplitter(app)

        # NOTE: 以位置命名的部件应该只用来组织界面布局,不要
        # 给其添加任何功能性的函数
        self.top_panel = TopPanel(app, app)
        self.bottom_panel = BottomPanel(app, app)
        self._left_panel_con = LeftPanel(self._app,)
        self.left_panel = self._left_panel_con.p
        self.right_panel = RightPanel(self._app, self._splitter)
        self.mpv_widget = MpvOpenGLWidget(self._app)
        self.mpv_widget.hide()

        # alias
        self.magicbox = self.bottom_panel.magicbox
        self.pc_panel = self.top_panel.pc_panel
        self.table_container = self.right_panel.table_container
        # backward compatible, old name is songs_table_container
        self.songs_table_container = self.table_container
        self.songs_table = self.table_container.songs_table
        self.back_btn = self.bottom_panel.back_btn
        self.forward_btn = self.bottom_panel.forward_btn
        self.toggle_video_btn = self.pc_panel.toggle_video_btn

        self.pc_panel.playlist_btn.clicked.connect(self.show_player_playlist)
        self.pc_panel.mv_btn.clicked.connect(self._play_mv)
        self.toggle_video_btn.clicked.connect(self._toggle_video_widget)
        self._app.player.video_format_changed.connect(
            self.on_video_format_changed, aioqueue=True)

        self._app.hotkey_mgr.registe(
            [QKeySequence('Ctrl+F'), QKeySequence(':'), QKeySequence('Alt+x')],
            self.magicbox.setFocus
        )

        self._setup_ui()

    def _setup_ui(self):
        self._app.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

        self._splitter.setHandleWidth(0)
        self._splitter.addWidget(self._left_panel_con)
        self._splitter.addWidget(self.right_panel)

        self.right_panel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._left_panel_con.setMinimumWidth(200)

        self._layout.addWidget(self.bottom_panel)
        self._layout.addWidget(self._bottom_separator)
        self._layout.addWidget(self._splitter)
        self._layout.addWidget(self.mpv_widget)
        self._layout.addWidget(self._top_separator)
        self._layout.addWidget(self.top_panel)

        self._layout.setSpacing(0)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.top_panel.layout().setSpacing(0)
        self.top_panel.layout().setContentsMargins(0, 0, 0, 0)

    def _play_mv(self):
        song = self._app.player.current_song
        mv = song.mv if song else None
        if mv is not None:
            if mv.meta.support_multi_quality:
                media, _ = mv.select_media()
            else:
                media = mv.media
            self._app.player.play(media)
            self.show_video_widget()

    def show_player_playlist(self):
        self.table_container.show_player_playlist()

    def on_video_format_changed(self, vformat):
        if vformat is None:
            self.hide_video_widget()
            self.toggle_video_btn.hide()
        else:
            self.toggle_video_btn.show()

    def _toggle_video_widget(self):
        if self.mpv_widget.isVisible():
            self.hide_video_widget()
        else:
            self.show_video_widget()

    def hide_video_widget(self):
        self.mpv_widget.hide()
        self._splitter.show()
        self.bottom_panel.show()
        self._bottom_separator.show()
        self.pc_panel.toggle_video_btn.setText('△')

    def show_video_widget(self):
        self.bottom_panel.hide()
        self._bottom_separator.hide()
        self._splitter.hide()
        self.mpv_widget.show()
        self.pc_panel.toggle_video_btn.setText('▽')
Esempio n. 4
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)