Пример #1
0
    def actPaste(self):
        c = []
        text = QtGui.QGuiApplication.clipboard().text()
        bad = False

        try:
            l = json.loads(text)
            if not isinstance(l, list):
                c = filterBS(parseBS(text))
            else:
                for s in l:
                    d = MapDataElement.fromdict(s)
                    if d and d.src and d.src.svgId:
                        c.append(d)
                    else:
                        bad = True
        except JSONDecodeError:
            c = filterBS(parseBS(text))

        if bad or len(c) == 0:
            QMessageBox(
                QMessageBox.Icon.Warning, TR("Paste"),
                c and TR("__paste_filter_invalid_blocks__").format(len(c))
                or TR("__paste_no_valid_blocks__")).exec_()
        len(c) and self.ghostHold(c)
Пример #2
0
 def mousePress(self, a0: QMouseEvent) -> bool:
     d, _ = self.parent.findCellUnder(a0)
     if not d:
         return False
     shift = a0.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier
     ctrl = a0.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier
     if a0.buttons() & QtCore.Qt.MouseButton.LeftButton:
         if a0.x() < Ruler.Width: # quick row selection
             self.parent.selector.addRowCol(self.parent.data, shift or ctrl, ctrl, y=d.y)
             return True
         if a0.y() < Ruler.Width: # quick column selection
             self.parent.selector.addRowCol(self.parent.data, shift or ctrl, ctrl, x=d.x)
             return True
     if a0.buttons() & QtCore.Qt.MouseButton.RightButton:
         if not (a0.x() < Ruler.Width or a0.y() < Ruler.Width):
             return False
         m = QMenu(self.parent)
         if a0.x() < Ruler.Width: # add hline
             if d.y in self.hlines:
                 a = m.addAction(TR('Remove Line'))
                 a.triggered.connect(lambda v: self.hlines.remove(d.y))
             else:
                 a = m.addAction(TR('Add Line'))
                 a.triggered.connect(lambda v: self.hlines.add(d.y))
         else:
             if d.x in self.vlines:
                 a = m.addAction(TR('Remove Line'))
                 a.triggered.connect(lambda v: self.vlines.remove(d.x))
             else:
                 a = m.addAction(TR('Add Line'))
                 a.triggered.connect(lambda v: self.vlines.add(d.x))
         m.popup(self.parent.mapToGlobal(a0.pos()))
         return True
     return False
Пример #3
0
    def __init__(self, parent: typing.Optional[QWidget], logfile) -> None:
        super().__init__(parent=parent)
        self.logfile = logfile

        self.setWindowTitle(APP_NAME)
        box = QVBoxLayout(self)
        self.log = QListWidget(self)
        self.log.addItems(LOGS)
        self.log.setFont(
            QtGui.QFontDatabase.systemFont(
                QtGui.QFontDatabase.SystemFont.FixedFont))
        self.log.doubleClicked.connect(self.open)
        box.addWidget(self.log)

        w = QWidget(self)
        hbox = QHBoxLayout(w)
        btn = QPushButton(TR('OK'), self)
        btn.clicked.connect(lambda: self.close())
        btn.setFixedSize(btn.sizeHint())
        hbox.addWidget(btn)
        btn = QPushButton(
            TR('Clear Logs') +
            ' ({:.2f}K)'.format(os.stat('logs.txt').st_size / 1024), self)
        btn.clicked.connect(lambda: self.clearLog())
        btn.setFixedSize(btn.sizeHint())
        hbox.addWidget(btn)
        btn = QPushButton(TR('Hide Debugs'), self)
        btn.clicked.connect(lambda: self.nonDebugLog())
        btn.setFixedSize(btn.sizeHint())
        hbox.addWidget(btn)

        box.addWidget(w)
        # self.setFixedSize(self.sizeHint())
        self.showMaximized()
        self.log.scrollToBottom()
Пример #4
0
    def __init__(self, parent: typing.Optional[QWidget]) -> None:
        super().__init__(parent=parent)
        box = self.box
        box.addWidget(QLabel('{} (v{})'.format(APP_NAME, APP_VERSION)))
        box.addWidget(QLabel(TR('__about__')))

        def _link(url, text=None):
            l = QLabel(self)
            l.setText("<a href=\"{}\">{}<a>".format(url, text or url))
            l.setTextFormat(QtCore.Qt.TextFormat.RichText)
            l.setTextInteractionFlags(
                QtCore.Qt.TextInteractionFlag.TextBrowserInteraction)
            l.setOpenExternalLinks(True)
            return l

        box.addWidget(_link("https://commons.wikimedia.org/wiki/BSicon/Guide"))
        box.addWidget(
            _link("https://github.com/coyove/RouteMaster",
                  "{} on Github".format(APP_NAME)))
        box.addWidget(_link("https://github.com/wisaly/qtbase_zh"))
        box.addWidget(_link("mailto:[email protected]", TR("Send Feedbacks")))
        box.addWidget(
            _link("https://github.com/coyove/RouteMaster/issues",
                  TR("File Issues on Github")))

        btn = QPushButton(TR('OK'), self)
        btn.clicked.connect(lambda: self.close())
        btn.setFixedSize(btn.sizeHint())
        box.addWidget(btn)  # alignment=QtCore.Qt.AlignmentFlag.AlignRight)
        self.setFixedSize(self.sizeHint())
Пример #5
0
 def actSelectByText(self):
     x, ok = QInputDialog.getText(self, TR('Select by Text'), TR('Text:'))
     if not ok or not x:
         return
     self.selector.clear()
     for k in self.data.data:
         d = self.data.data[k]
         if d.text.lower().count(x.lower()) > 0:
             self.selector.addSelection(d, propertyPanel=False)
     self.findMainWin().propertyPanel.update()
Пример #6
0
 def _askSave(self, thenQuit=False):
     if self.mapview.data.historyCap:
         ans = QMessageBox.question(self,
                                    TR("Save"),
                                    TR("Save current file?"),
                                    buttons=QMessageBox.StandardButton.Yes
                                    | QMessageBox.StandardButton.No
                                    | QMessageBox.StandardButton.Cancel)
         if ans == QMessageBox.StandardButton.Cancel:
             return False
         if ans == QMessageBox.StandardButton.Yes:
             self.doSave(True)
     thenQuit and QApplication.quit()
     return True
Пример #7
0
 def offsetChanged(self, t, v):
     if t == 'x' or t == 'y':
         self._foreach(lambda x: x.set(t == 'x' and 'textX' or 'textY', v))
     else:  # xo
         self.startXOffsetLabel.setText(
             TR('Overlay: Start X Percentage') + ' ' + str(int(v * 100)) +
             "%")
         self._foreach(lambda x: x.set("startXOffset", v))
Пример #8
0
 def doExportPngSvg(self, v=True, png=True):
     d = QFileDialog(self)
     fn, _ = d.getSaveFileName(
         filter='PNG File (*.png)' if png else 'SVG File (*.svg)')
     if not fn:
         return
     try:
         if png:
             exportMapDataPng(self, fn, self.mapview.data)
         else:
             exportMapDataSvg(self, fn, self.mapview.data)
         QMessageBox.information(self, TR('Export'),
                                 TR('__export_success__').format(fn))
     except Exception as e:
         QMessageBox(QMessageBox.Icon.Warning, TR('Export'),
                     TR('__export_fail__').format(fn)).exec_()
         QtCore.qDebug("{}".format(e).encode("utf-8"))
Пример #9
0
 def load(self, fn: str):
     d: MapData = self.mapview.data
     crashfn = fn.removesuffix(".bsm") + ".crash.bsm"
     delcrash = False
     if os.path.exists(crashfn):
         ans = QMessageBox.question(self, TR('Open'),
                                    TR('__open_crash__').format(crashfn))
         if ans == QMessageBox.StandardButton.Yes:
             if fn == "":
                 fn = crashfn
             else:
                 filename = os.path.basename(fn)
                 dir = os.path.dirname(fn)
                 shutil.copy2(fn, os.path.join(dir,
                                               "." + filename + ".old"))
                 shutil.copy2(crashfn, fn)
             delcrash = True
         else:
             os.remove(crashfn)
     if not os.path.exists(fn):
         return
     with open(fn, 'rb') as f:
         try:
             fd = json.load(f)
         except JSONDecodeError as e:
             QMessageBox(QMessageBox.Icon.Critical, TR('Open'),
                         TR('__open_fail__').format(fn)).exec_()
             QtCore.qDebug("{}".format(e).encode("utf-8"))
             return
         d.clearHistory()
         d.data = {}
         for c in fd['data']:
             el = MapDataElement.fromdict(c)
             if el:
                 d.data[(el.x, el.y)] = el
         # self.mapview.pan(0, 0)
         self.mapview.center()
         self._updateCurrentFile(fn)
         self.fileMeta["author"] = fd.get("author")
         self.fileMeta["desc"] = fd.get("desc")
         self.mapview.ruler.fromdict(fd.get("rulers"))
     if delcrash:
         os.remove(crashfn)
Пример #10
0
 def doSaveAs(self, v):
     d = QFileDialog(self)
     fn, _ = d.getSaveFileName(filter='BSM Files (*.bsm)')
     if not fn:
         return
     try:
         self.save(fn)
     except Exception as e:
         QMessageBox(QMessageBox.Icon.Warning, TR('Save'),
                     'Failed to save {}: {}'.format(fn, e)).exec_()
Пример #11
0
 def doOpen(self, v):
     if not self._askSave():
         return
     d = QFileDialog(self)
     fn, _ = d.getOpenFileName(filter='BSM Files (*.bsm)')
     if not fn:
         return
     try:
         self.load(fn)
     except Exception as e:
         QMessageBox(QMessageBox.Icon.Warning, TR('Open'),
                     'Failed to open {}: {}'.format(fn, e)).exec_()
Пример #12
0
    def __init__(self):
        super().__init__(flags=QtCore.Qt.WindowType.WindowCloseButtonHint
                         | QtCore.Qt.WindowType.WindowStaysOnTopHint)
        self.setWindowTitle(APP_NAME)
        self.setMaximumWidth(WIN_WIDTH * 2)
        self.show()

        w = QWidget(self)
        box = QVBoxLayout()
        w.setLayout(box)

        self.bar = QProgressBar(w)
        self.bar.show()
        self.bar.setRange(0, 0)

        box.addWidget(QLabel(TR("__download_icons__")), 1)
        self.progress = QLabel('0')
        box.addWidget(self.progress)
        box.addWidget(self.bar, 1)

        self.setCentralWidget(w)
        self.setVisible(False)
        self.tasks = set()
        Loader.Single = self
Пример #13
0
    def __init__(self, parent: typing.Optional[QWidget], meta: dict) -> None:
        super().__init__(parent=parent)
        self.setWindowTitle(APP_NAME)
        self.meta = meta
        box = QVBoxLayout(self)
        box.addWidget(QLabel(TR('Author')))
        self.author = QLineEdit(self)
        self.author.setText(meta.get("author"))
        box.addWidget(self.author)

        self.desc = QTextEdit(self)
        self.desc.setText(meta.get("desc"))
        box.addWidget(QLabel(TR('Description')))
        box.addWidget(self.desc, 5)

        data: MapData = parent.mapview.data
        total, dedup, missings, polyfills = 0, set(), set(), set()
        for v in data.data.values():
            if not v.src.svgId.lower() in SvgSource.Search.files:
                missings.add(v.src.svgId)
            if ispngployfill(v.src.svgId):
                polyfills.add(v.src.svgId)
            dedup.add(v.src.svgId)
            total = total + 1
            for s in v.cascades:
                if not s.svgId.lower() in SvgSource.Search.files:
                    missings.add(s.svgId)
                if ispngployfill(s.svgId):
                    polyfills.add(s.svgId)
                dedup.add(s.svgId)
                total = total + 1

        tabs = QTabWidget(self)

        def rectStr(r: QtCore.QRect):
            return "({}, {})-({}, {})".format(r.x(), r.y(),
                                              r.width() + r.x(),
                                              r.height() + r.y())

        overview = QListWidget(self)
        overview.addItem(TR('Total Blocks') + ": " + str(total))
        overview.addItem(TR('Bounding') + ": " + rectStr(data.bbox()))
        overview.addItem(
            TR('Text Bounding') + ": " + rectStr(data.bbox(includeText=True)))
        tabs.addTab(overview, TR('Overview'))

        self.all = QTextEdit(self)
        self.all.setText('\n'.join(dedup))
        self.all.setReadOnly(True)
        tabs.addTab(self.all, TR('All Blocks'))

        self.missing = QTextEdit(self)
        self.missing.setText('\n'.join(missings))
        self.missing.setReadOnly(True)
        tabs.addTab(self.missing, TR('Missings'))

        self.polyfills = QTextEdit(self)
        self.polyfills.setText('\n'.join(polyfills))
        self.missing.setReadOnly(True)
        tabs.addTab(self.polyfills, TR('Polyfills'))

        box.addWidget(tabs, 5)

        self.ok = QPushButton(TR("OK"), self)
        self.ok.clicked.connect(self.onOK)
        self.ok.setFixedSize(self.ok.sizeHint())
        box.addWidget(self.ok)

        self.setFixedWidth(WIN_WIDTH)
Пример #14
0
    def __init__(self, parent: typing.Optional['QWidget']):
        super().__init__(parent=parent)
        self.scrollView = QScrollArea(self)
        self.scrollView.setWidgetResizable(True)
        self.scrollView.setHorizontalScrollBarPolicy(
            QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        self.textAttr = QWidget(self)
        self.textAttr.setSizePolicy(QSizePolicy.Policy.Preferred,
                                    QSizePolicy.Policy.Preferred)
        self.textAttrBox = QVBoxLayout()
        self.textAttr.setLayout(self.textAttrBox)

        self.cascades = SvgBar(self)
        self.cascades.setVisible(False)
        self.cascades.onDelete = self.deleteCascade
        self.cascades.onDrag = self.resortCascade
        self.cascades.onCopy = self.onCopy
        self.textAttrBox.addWidget(self.cascades)

        self.svgId = QLabel('N/A', self)
        self.textAttrBox.addWidget(self._title(TR('Type')))
        self.textAttrBox.addWidget(self.svgId)

        self.startXOffsetLabel = self._title(TR('Overlay: Start X Percentage'))
        self.textAttrBox.addWidget(self.startXOffsetLabel)
        self.startXOffset = QSlider(self)
        self.startXOffset.setOrientation(QtCore.Qt.Orientation.Horizontal)
        self.startXOffset.setMinimum(0)
        self.startXOffset.setMaximum(3)
        self.startXOffset.valueChanged.connect(
            lambda e: self.offsetChanged('xo',
                                         self.startXOffset.value() * 0.25))
        self.textAttrBox.addWidget(self.startXOffset)

        self.textAttrBox.addWidget(self._title(TR('Text')))
        self.text = QTextEdit(self)
        self.text.textChanged.connect(self.textChanged)
        self.text.installEventFilter(self)
        self.textAttrBox.addWidget(self.text)

        self.textFont = QComboBox(self)
        fontFamilies = QFontDatabase()
        for s in fontFamilies.families():
            self.textFont.addItem(s)
        self.textFont.currentIndexChanged.connect(self.fontChanged)
        self.textFont.setEditable(True)

        self.textSize = QComboBox(self)
        for i in range(8, 150, 1):
            if i <= 32:
                self.textSize.addItem(str(i))
            elif i <= 80 and i % 2 == 0:
                self.textSize.addItem(str(i))
            elif i % 10 == 0:
                self.textSize.addItem(str(i))
        self.textSize.currentIndexChanged.connect(self.sizeChanged)
        self.textSize.setEditable(True)
        self._addBiBoxInTextAttrBox(self._title(TR("Font Family")),
                                    self.textFont,
                                    self._title(TR("Font Size")),
                                    self.textSize)

        self.textAlign = QComboBox(self)
        self.textPlace = QComboBox(self)
        for c in [self.textAlign, self.textPlace]:
            c.addItem(TR('Center'), 'c')
            c.addItem(TR('Top'), 't')
            c.addItem(TR('Bottom'), 'b')
            c.addItem(TR('Left'), 'l')
            c.addItem(TR('Right'), 'r')
        self.textAlign.currentIndexChanged.connect(self.alignChanged)
        self.textPlace.currentIndexChanged.connect(self.placeChanged)
        self._addBiBoxInTextAttrBox(self._title(TR("Alignment")),
                                    self.textAlign,
                                    self._title(TR("Placement")),
                                    self.textPlace)

        self.textX = QSpinBox(self)
        self.textY = QSpinBox(self)
        for c in [self.textX, self.textY]:
            c.setValue(0)
            c.setMinimum(-1e5)
            c.setMaximum(1e5)
        self.textX.valueChanged.connect(lambda e: self.offsetChanged('x', e))
        self.textY.valueChanged.connect(lambda e: self.offsetChanged('y', e))
        self._addBiBoxInTextAttrBox(self._title(TR("Offset X")), self.textX,
                                    self._title(TR("Offset Y")), self.textY)

        # self.setLayout(self.textAttrBox)
        self.scrollView.setWidget(self.textAttr)
        box = QVBoxLayout()
        box.addWidget(self.scrollView)
        box.setContentsMargins(0, 0, 0, 0)
        self.setLayout(box)

        self.show()
        self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding,
                           QSizePolicy.Policy.MinimumExpanding)

        self.updating = False
Пример #15
0
 def actDownloadBlocks(self, v):
     ss, _ = QInputDialog.getText(self, APP_NAME, TR('Block Name:'))
     for s in ss.split():
         s = s.strip()
         s and self.loader.addTask(s)
Пример #16
0
 def _updateCurrentFile(self, fn):
     self.currentFile = fn
     self.setWindowTitle(APP_NAME + " - " +
                         (self.currentFile or TR('[Untitled]')))
Пример #17
0
    def __init__(self):
        super().__init__()

        self.searcher = SvgSearch(BLOCK_DIR)
        SvgSource.Search = self.searcher
        SvgSource.Parent = self

        self.loader = Loader()
        # self.loader.addTask("MFADEf")

        self.setWindowTitle(APP_NAME)
        self.setGeometry(0, 0, 800, 500)

        bar = QStatusBar(self)
        self.barPosition = QLabel(bar)
        bar.addWidget(self.barPosition, 1)
        self.barSelection = QLabel(bar)
        bar.addWidget(self.barSelection, 1)
        self.barZoom = QLabel(bar)
        bar.addWidget(self.barZoom, 1)
        self.barCursor = QLabel(bar)
        bar.addWidget(self.barCursor, 1)
        self.barHistory = QLabel(bar)
        bar.addWidget(self.barHistory, 1)
        self.setStatusBar(bar)

        main = QWidget(self)
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)

        self.propertyPanel = Property(main)

        self.searchBox = QLineEdit(self)
        self.searchBox.setPlaceholderText(TR('Search blocks'))
        self.searchBox.textChanged.connect(self.updateSearches)
        # self.searchBox.returnPressed.connect(self.searchBlocks)
        self.searchResults = SvgBar(self)
        vbox.addWidget(
            Property._genVBox(self, self.searchBox, self.searchResults, 8))

        main.setLayout(vbox)
        self.setCentralWidget(main)

        splitter = QSplitter(QtCore.Qt.Orientation.Horizontal)
        splitter.addWidget(self.propertyPanel)

        self.mapview = Map(main)
        self._updateCurrentFile('')
        splitter.addWidget(self.mapview)

        splitter.setStretchFactor(1, 2)
        splitter.setSizes([100, 100])
        vbox.addWidget(splitter, 255)

        self.topMenus = {}
        self._addMenu(TR('&File'), TR('&New'), 'Ctrl+N', self.doNew)
        self._addMenu(TR('&File'), TR('&New Window'), 'Ctrl+Shift+N',
                      lambda x: startNew())
        self._addMenu(TR('&File'), '-')
        self._addMenu(TR('&File'), TR('&Open'), 'Ctrl+O', self.doOpen)
        self._addMenu(TR('&File'), '-')
        self._addMenu(TR('&File'), TR('&Save'), 'Ctrl+S', self.doSave)
        self._addMenu(TR('&File'), TR('&Save As...'), 'Ctrl+Shift+S',
                      self.doSaveAs)
        self._addMenu(TR('&File'), '-')
        self._addMenu(TR('&File'), TR('&Export PNG...'), '',
                      lambda x: self.doExportPngSvg(png=True))
        self._addMenu(TR('&File'), TR('E&xport SVG...'), '',
                      lambda x: self.doExportPngSvg(png=False))
        self._addMenu(TR('&File'), '-')
        self._addMenu(TR('&File'), TR('&File Properties...'), 'F3',
                      lambda x: FileProperty(self, self.fileMeta).exec_())
        self._addMenu(TR('&File'), '-')
        self._addMenu(
            TR('&File'), TR('&Open Data Folder'), '',
            lambda x: QtGui.QDesktopServices.openUrl(
                QtCore.QUrl.fromLocalFile(BLOCK_DIR)))
        self._addMenu(TR('&File'), TR('&Download SVG Blocks'), '',
                      self.actDownloadBlocks)
        self._addMenu(TR('&File'), '-')
        self._addMenu(TR('&File'), TR('&Quit'), '',
                      lambda x: self._askSave(thenQuit=True))

        self._addMenu(TR('&Edit'), TR('&Undo'), '',
                      lambda x: self.mapview.actUndoRedo())
        self._addMenu(TR('&Edit'), TR('&Redo'), '',
                      lambda x: self.mapview.actUndoRedo(redo=True))
        self._addMenu(TR('&Edit'), '-')
        self._addMenu(TR('&Edit'), TR('&Cut'), '',
                      lambda x: self.mapview.actCut())
        self._addMenu(TR('&Edit'), TR('&Copy'), '',
                      lambda x: self.mapview.actCopy())
        self._addMenu(TR('&Edit'), TR('&Paste'), '',
                      lambda x: self.mapview.actPaste())
        self._addMenu(TR('&Edit'), '-')
        self._addMenu(TR('&Edit'), TR('&Clear History'), 'Ctrl+K',
                      lambda x: self.mapview.data.clearHistory())
        self._addMenu(TR('&Edit'), '-')
        self._addMenu(TR('&Edit'), TR('&Select by Text'), 'Ctrl+F',
                      lambda x: self.mapview.actSelectByText())

        self._addMenu(TR('&View'), TR('&Center'), '',
                      lambda x: self.mapview.center())
        self._addMenu(TR('&View'), TR('Center &Selected'), 'Ctrl+Shift+H',
                      lambda x: self.mapview.center(selected=True))
        self._addMenu(TR('&View'), TR('100% &Zoom'), '',
                      lambda x: self.mapview.center(resetzoom=True))
        self._addMenu(TR('&View'), '-')
        self.showRuler = self._addMenu(TR('&View'), TR('&Ruler'), '',
                                       self.actShowRuler)
        self.showRuler.setCheckable(True)
        self.mapview.showRuler = not FLAGS["hide_ruler"]
        self.showRuler.setChecked(self.mapview.showRuler)
        self._addMenu(TR('&View'), '-')
        self._addMenu(TR('&View'), TR('&Logs'), '',
                      lambda x: Logger(self, logfile).exec_())

        self._addMenu(TR('&Help'), TR('&About'), '',
                      lambda x: About(self).exec_()).setMenuRole(
                          QAction.MenuRole.AboutRole)

        self.propertyPanel.update()
        self.showMaximized()

        self.fileMeta = {}
        import socket
        self.resetFileMeta = lambda: self.__setattr__(
            "fileMeta", {
                "author": socket.gethostname(),
                "desc": APP_NAME
            })
        self.resetFileMeta()

        self.load(args.file or '')

        if args.convert:
            self.setVisible(False)
            if args.convert.endswith('.svg'):
                exportMapDataSvg(self, args.convert, self.mapview.data)
            else:
                self.mapview.scale = float(args.png_scale)
                exportMapDataPng(self, args.convert, self.mapview.data)
            print('Successfully export to {}'.format(args.convert))
            sys.exit(0)
Пример #18
0
win = Window()


def excepthook(exc_type, exc_value, exc_tb):
    import traceback
    msg = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
    QtCore.qDebug(("exception:\n" + msg).encode('utf-8'))
    print(msg)
    global win
    if win:
        w = win
        win = None  # prevent dead loop
        try:
            w.save(
                w.currentFile.removesuffix(".bsm") +
                '.crash.bsm' if w.currentFile else '.crash.bsm')
        except Exception as e:
            QtCore.qDebug(
                ("double exception in hook: {}".format(e)).encode('utf-8'))
    app.quit()


sys.excepthook = excepthook

if not os.path.exists(BLOCK_DIR) or len(SvgSource.Search.files) == 0:
    QMessageBox(QMessageBox.Icon.Warning, APP_NAME,
                TR("__block_dir__").format(BLOCK_DIR)).exec_()

app.exec_()