Ejemplo n.º 1
0
def exportMapDataSvg(parent, fn: str, data: MapData):
    bs = parent.mapview._blocksize()
    bounding = data.bbox(includeText=True)
    x0, y0 = bounding.x(), bounding.y()

    import re
    r = re.compile(r"<!.+>")
    buf = [
        '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{}" height="{}">'
        .format(bounding.width() * bs,
                bounding.height() * bs)
    ]
    for x, y in data.data:
        d = data.data[(x, y)]
        x = x - x0
        y = y - y0
        buf.append(
            '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="{}" y="{}" width="{}" height="{}" viewBox="0 0 500 500">'
            .format(x * bs, y * bs, bs, bs))
        s = r.sub('', d.src.source())
        buf.append(s.replace('<?', '<!--').replace('?>', '-->'))
        buf.append('</svg>')

    buf.append('</svg>')
    with open(fn, 'wb+') as f:
        f.write(''.join(buf).encode('utf-8'))
Ejemplo n.º 2
0
    def __init__(self, parent):
        super().__init__(parent)

        self.data = MapData(self)
        self.scale = 2

        if FLAGS["DEBUG_fill"]:
            sources = []
            for i in range(0, 10):
                sources.append(
                    SvgSource(
                        "id" + str(i), """
                    <svg height="48" width="48">
                        <text x="8" y="24" fill="red">{}</text>
                        <rect x="8" y="8" width="32" height="32" fill="transparent" stroke="#000"></rect>
                    </svg>""".format(i).encode('utf-8'), BS, BS))
            n = FLAGS["DEBUG_fill"]
            s = math.sqrt(n)
            for i in range(0, n):
                l = int(random.random() * s + 1)
                d = random.random() * math.pi * 2
                x, y = int(l * math.cos(d)), int(l * math.sin(d))
                el = MapDataElement(sources[random.randrange(0, len(sources))])
                if random.random() > 0.9:
                    el.cascades.append(sources[random.randrange(
                        0, len(sources))])
                if random.random() > 0.8:
                    el.text = "lazy\nfox jumps"
                self.data._put(x, y, el)

        self.selector = Selector(self)
        self.dragger = Dragger(self)
        self.hover = Hover(self)
        self.ruler = Ruler(self)
        self.boxRows = self.boxCols = 0
        self.viewOrigin = [0, 0]
        self.pan(0, 0)  # fill svgBoxes
        self.setMouseTracking(True)
        self.pressPos = None
        self.pressPosPath = []
        self.pressHoldSel = False
        self.showRuler = True
        self.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)
def get_maps_data_list(mapsDirectory):
    mapsList = get_maps_list(mapsDirectory)
    mapDataList = []
    for map in mapsList:
        input = gzip.GzipFile(map, 'rb')
        input.seek(5, 0)
        mapSize = ConvertByteToMapSize(input.read(1))
        mapSize = ConvertByteToMapSize(input.read(1))
        mapName = os.path.splitext(os.path.basename(map))[0]
        mapDataList.append(MapData(mapName, mapSize))
        input.close()

    print("Done reading HOMM3 maps names and sizes")
    return mapDataList
Ejemplo n.º 4
0
def exportMapDataPng(parent, fn: str, data: MapData):
    # QInputDialog.getInt(parent=parent, title='Block size', label='Pixels', value=32, min=16, max
    bs = parent.mapview._blocksize()

    bounding = data.bbox(includeText=True)
    x0, y0 = bounding.x(), bounding.y()

    img = QImage(bs * bounding.width(), bs * bounding.height(),
                 QImage.Format.Format_ARGB32)
    p = QPainter(img)
    for x, y in data.data:
        d = data.data[(x, y)]
        x = x - x0
        y = y - y0
        MapDataRenderer.paint(d, False, parent.mapview.scale, x * bs, y * bs,
                              p)
    p.end()
    img.save(fn)
from MapData import MapData
from Map import Map

first_floor_url = "./img/school_b/first_floor.png"
second_floor_url = "./img/school_b/second_floor.png"
first_map_y_delta = 185
second_map_y_delta = 185

first_floor_seal_locations = [(26, 159), (129, 139), (212, 88), (344, 240),
                              (167, 400), (319, 386), (117, 266), (108, 268)]

second_floor_seal_locations = [(41, 169), (140, 246), (211, 47), (281, 271),
                               (215, 339), (374, 385), (113, 59)]

first_map_data = MapData(first_floor_url, first_map_y_delta,
                         first_floor_seal_locations)
second_map_data = MapData(second_floor_url, second_map_y_delta,
                          second_floor_seal_locations)

map_display = Map(first_map_data, second_map_data)
map_display.draw()
Ejemplo n.º 6
0
class Map(QWidget):
    Background = QtGui.QColor(255, 255, 255)

    import functools

    @functools.cache
    def BigFont():
        return QtGui.QFont(
            QtGui.QFontDatabase.systemFont(
                QtGui.QFontDatabase.SystemFont.FixedFont).family(), 16)

    def __init__(self, parent):
        super().__init__(parent)

        self.data = MapData(self)
        self.scale = 2

        if FLAGS["DEBUG_fill"]:
            sources = []
            for i in range(0, 10):
                sources.append(
                    SvgSource(
                        "id" + str(i), """
                    <svg height="48" width="48">
                        <text x="8" y="24" fill="red">{}</text>
                        <rect x="8" y="8" width="32" height="32" fill="transparent" stroke="#000"></rect>
                    </svg>""".format(i).encode('utf-8'), BS, BS))
            n = FLAGS["DEBUG_fill"]
            s = math.sqrt(n)
            for i in range(0, n):
                l = int(random.random() * s + 1)
                d = random.random() * math.pi * 2
                x, y = int(l * math.cos(d)), int(l * math.sin(d))
                el = MapDataElement(sources[random.randrange(0, len(sources))])
                if random.random() > 0.9:
                    el.cascades.append(sources[random.randrange(
                        0, len(sources))])
                if random.random() > 0.8:
                    el.text = "lazy\nfox jumps"
                self.data._put(x, y, el)

        self.selector = Selector(self)
        self.dragger = Dragger(self)
        self.hover = Hover(self)
        self.ruler = Ruler(self)
        self.boxRows = self.boxCols = 0
        self.viewOrigin = [0, 0]
        self.pan(0, 0)  # fill svgBoxes
        self.setMouseTracking(True)
        self.pressPos = None
        self.pressPosPath = []
        self.pressHoldSel = False
        self.showRuler = True
        self.setFocusPolicy(QtCore.Qt.FocusPolicy.ClickFocus)

    def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
        self.pan(0, 0)
        return super().resizeEvent(a0)

    def _getVirtualCell(self, x, y):
        bs = self._blocksize()
        return self.data.get(x - int(self.viewOrigin[0] / bs) - 1,
                             y - int(self.viewOrigin[1] / bs) - 1)

    def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
        p = QtGui.QPainter(self)
        p.setRenderHint(QtGui.QPainter.RenderHint.HighQualityAntialiasing)
        p.fillRect(0, 0, self.width(), self.height(), Map.Background)

        bs = self._blocksize()
        sx, sy = self.deltaxy()

        if self.boxRows * self.boxCols > len(self.data.data):
            for v in self.data.data.values():
                MapDataRenderer.paint(v, v in self.selector, self.scale,
                                      v.x * int(bs) + self.viewOrigin[0],
                                      v.y * int(bs) + self.viewOrigin[1], p)
        else:
            for y in range(self.boxRows):
                for x in range(self.boxCols):
                    tmp = self._getVirtualCell(x, y)
                    if tmp and tmp.valid():
                        MapDataRenderer.paint(tmp, tmp in self.selector,
                                              self.scale,
                                              x * int(bs) + sx - bs,
                                              y * int(bs) + sy - bs, p)

        self.selector.paint(p)
        self.dragger.paint(p)
        self.hover.paint(p)
        for i in range(0, len(self.pressPosPath)):
            s, e = self.pressPosPath[i], self.pressPosPath[
                i < len(self.pressPosPath) - 1 and i + 1 or 0]
            p.drawLine(s[0], s[1], e[0], e[1])

        w = 0
        if self.showRuler:
            w = Ruler.Width
            self.ruler.paint(p)

        if FLAGS.get('show_keys', False):
            vis = []
            QApplication.queryKeyboardModifiers(
            ) & QtCore.Qt.KeyboardModifier.ControlModifier and vis.append(
                '\u2318' if sys.platform == 'darwin' else 'Ctrl')
            QApplication.queryKeyboardModifiers(
            ) & QtCore.Qt.KeyboardModifier.ShiftModifier and vis.append(
                "Shift")
            if vis:
                p.setFont(Map.BigFont())
                p.drawText(w + 2, w + Map.BigFont().pointSize(), '-'.join(vis))

        p.end()
        return super().paintEvent(a0)

    def deltaxy(self):
        blockSize = int(BS * self.scale)
        sx = mod(self.viewOrigin[0], blockSize)
        sy = mod(self.viewOrigin[1], blockSize)
        return sx, sy

    def pan(self, dx, dy):
        blockSize = BS * self.scale
        offsetX = self.viewOrigin[0] = self.viewOrigin[0] + dx
        offsetY = self.viewOrigin[1] = self.viewOrigin[1] + dy

        self.boxRows = int(self.height() / blockSize) + 3
        self.boxCols = int(self.width() / blockSize) + 3

        self.findMainWin().barPosition.setText('x:{}({}) y:{}({})'.format(
            offsetX, int(offsetX / blockSize), offsetY,
            int(offsetY / blockSize)))
        self.findMainWin().barZoom.setText('{}%'.format(int(self.scale * 100)))

        self.selectionEvent()
        self.repaint()

    def findCellUnder(self, a0: QtGui.QMouseEvent, c: QtCore.QPoint = None):
        c = a0 and a0.pos() or c
        bs = self._blocksize()
        sx, sy = self.deltaxy()
        rr = math.floor
        pt = QtCore.QPoint(
            int((c.x() - sx) / bs) * bs + sx,
            int((c.y() - sy) / bs) * bs + sy)
        x_ = (c.x() - self.viewOrigin[0]) / bs  # xy of data
        y_ = (c.y() - self.viewOrigin[1]) / bs
        x, y = rr(x_), rr(y_)
        if x_ == x or y_ == y:  # special case: cursor on edge, no cell found
            return None, pt
        d = self.data.get(x, y) or MapDataElement(x=x, y=y)
        return d, pt

    def findMainWin(self):
        p = self.parent()
        while not isinstance(p, QMainWindow):
            p = p.parent()
        return p

    def selectionEvent(self, action=None, d=None):
        # print(d)
        self.findMainWin().barSelection.setText(self.selector.status())

    def keyReleaseEvent(self, a0: QtGui.QKeyEvent) -> None:
        self.repaint()
        return super().keyReleaseEvent(a0)

    def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
        if a0.key() == QtCore.Qt.Key.Key_Delete:
            self.actDelete()

        ctrl = QtGui.QGuiApplication.keyboardModifiers(
        ) & QtCore.Qt.KeyboardModifier.ControlModifier

        if ctrl and a0.key() == QtCore.Qt.Key.Key_C:
            self.actCopy()

        if ctrl and a0.key() == QtCore.Qt.Key.Key_X:
            self.actCut()

        if ctrl and a0.key() == QtCore.Qt.Key.Key_V:
            self.actPaste()

        if ctrl and (a0.key() == QtCore.Qt.Key.Key_Z
                     or a0.key() == QtCore.Qt.Key.Key_Y):
            self.actUndoRedo(a0.key() == QtCore.Qt.Key.Key_Y)

        if ctrl and a0.key() == QtCore.Qt.Key.Key_A:
            self.actSelectAll()

        if a0.key() == QtCore.Qt.Key.Key_Escape:
            self.selector.clear()
            self.hover.clear()
            self.dragger.reset()
            self.pressHoldSel = False
            self.pressPos = None
            self.pressPosPath.clear()
            self.pan(0, 0)
            self.findMainWin().searchResults.clearSelection()
            QApplication.restoreOverrideCursor()

        if a0.key() == QtCore.Qt.Key.Key_Home or a0.key(
        ) == QtCore.Qt.Key.Key_H:
            self.center()

        if a0.key() == QtCore.Qt.Key.Key_Space:
            edit: QLineEdit = self.findMainWin().searchBox
            edit.setFocus()
            edit.selectAll()

        if a0.key() == QtCore.Qt.Key.Key_Q:
            for l in self.hover.labels:
                l: MapDataElement
                r = SvgSource.tryRotate(l.src.svgId, q=True)
                if r:
                    l.src = SvgSource.getcreate(
                        r, SvgSource.Search.path + "/" + r, BS, BS)

        self.repaint()
        return super().keyPressEvent(a0)

    def actDelete(self):
        self.data.begin()
        for l in self.selector.labels:
            self.data.delete(l.data.x, l.data.y)
        self.selector.clear()
        self.pan(0, 0)

    def actSelectAll(self):
        self.selector.clear()
        for k in self.data.data:
            self.selector.addSelection(self.data.data[k], propertyPanel=False)
        self.findMainWin().propertyPanel.update()
        self.repaint()

    def actCopy(self):
        s = []
        for l in self.selector.labels:
            s.append(l.data.todict())
        QtGui.QGuiApplication.clipboard().setText(json.dumps(s))

    def actCut(self):
        self.actCopy()
        self.actDelete()

    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)

    def actUndoRedo(self, redo=False):
        self.selector.clear()
        if redo:
            self.data.forward()
        else:
            self.data.rewind()
        self.pan(0, 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()

    def ghostHold(self, c: typing.List[MapDataElement]):
        self.data.begin()
        self.selector.clear()
        self.hover.hold(c)
        self.dragger.start(0, 0, QtCore.QPoint(0, 0))
        self.dragger.visible = False
        self.pressHoldSel = True

    def center(self, selected=False, resetzoom=False):
        if resetzoom:
            self.scale = 1
        if len(self.data.data) == 0:
            self.viewOrigin = [0, 0]
            self.pan(0, 0)
            return
        if selected and len(self.selector.labels):
            cx, cy = self.selector.labels[0].datax, self.selector.labels[
                0].datay
        else:
            r = self.data.bbox()
            cx, cy = r.x() + r.width() // 2, r.y() + r.height() // 2
        x, y = cx * self._blocksize(), cy * self._blocksize()
        x = -x + self.width() // 2
        y = -y + self.height() // 2
        self.viewOrigin = [x, y]
        self.pan(0, 0)

    def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
        if FLAGS["DEBUG_crash"]:
            print(1 // 0)
        if self.showRuler and self.ruler.mousePress(a0):
            self.repaint()
            return super().mousePressEvent(a0)
        d, pt = self.findCellUnder(a0)
        if a0.buttons() & QtCore.Qt.MouseButton.MidButton:  # pan start
            self.pressPos = a0.pos()
            QApplication.setOverrideCursor(
                QtCore.Qt.CursorShape.DragMoveCursor)
        elif len(self.hover.labels) > 0:  # ghost hold, place them
            if a0.buttons() & QtCore.Qt.MouseButton.RightButton:
                self.hover.clear()
                self.pressHoldSel = False
                self.dragger.reset()
                return super().mousePressEvent(a0)

            old = self.hover.labels
            self.hover.end(a0.modifiers()
                           & QtCore.Qt.KeyboardModifier.ControlModifier)
            self.pressHoldSel = False
            self.dragger.reset()
            self.pan(0, 0)
            if a0.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier:
                self.ghostHold([x.dup() for x in old])  # continue holding
            else:
                self.findMainWin().searchResults.clearSelection()
        else:
            self.pressPos = None
            self.pressHoldSel = True
            if d:
                if a0.modifiers(
                ) & QtCore.Qt.KeyboardModifier.ShiftModifier:  # select
                    if not self.selector.addSelection(
                            d):  # begin circle select
                        self.pressPos = a0.pos()
                        self.pressPosPath.append((a0.x(), a0.y()))
                elif a0.modifiers(
                ) & QtCore.Qt.KeyboardModifier.ControlModifier:  # un-select
                    self.selector.delSelection(d)
                elif d.src:  # select-n-drag
                    if len(self.selector.labels) == 1:
                        self.selector.clear()
                    self.selector.addSelection(d)
                    self.dragger.start(a0.x(), a0.y(), pt)
                else:  # clear selections
                    self.selector.clear()
                    # let's take the slow way, find something visually selectable and select it for user
                    bs = self._blocksize()
                    d, _ = self.findCellUnder(
                        None, QtCore.QPoint(a0.x() - bs, a0.y()))
                    if d and d.src and d.calcActualWidthX(bs, bs) > bs:
                        # double width block consists of 2 std blocks (a, b), user clicked "b", we select "a"
                        self.selector.addSelection(d)
                    else:
                        # block with text, user clicked "text", we select the block
                        sx, sy = self.deltaxy()
                        for y in range(self.boxRows):
                            for x in range(self.boxCols):
                                cell = self._getVirtualCell(x, y)
                                if cell and cell.valid() and cell.text:
                                    posx, posy = x * bs + sx - bs, y * bs + sy - bs
                                    r = cell.textbbox(self.scale,
                                                      posx,
                                                      posy,
                                                      measure=True)
                                    if r.contains(a0.pos()):
                                        self.selector.addSelection(cell)
            self.repaint()
        return super().mousePressEvent(a0)

    def _blocksize(self):
        return int(self.scale * BS)

    def _appendPressPath(self):
        x, y = self.pressPos.x(), self.pressPos.y()
        if abs(x - self.pressPosPath[-1][0]) < 4 and abs(
                y - self.pressPosPath[-1][1]) < 4:
            return
        self.pressPosPath.append((x, y))

    def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
        d, pt = self.findCellUnder(a0)
        changed = False
        if d:  # update ruler
            xy = (d.x, d.y)
            changed = self.ruler.currentXY != xy and self.showRuler
            self.ruler.currentXY = xy

        if a0.buttons() & QtCore.Qt.MouseButton.MidButton:  # pan
            diff: QtCore.QPoint = a0.pos() - self.pressPos
            self.pressPos = a0.pos()
            self.pan(diff.x(), diff.y())
        elif a0.modifiers(
        ) & QtCore.Qt.KeyboardModifier.ShiftModifier and self.pressPosPath:  # circle select
            self._appendPressPath()
            self.pressPos = a0.pos()
            self.repaint()
        elif self.pressHoldSel:
            if d:
                if self.dragger.started:  # drag, show drag pointer
                    self.dragger.drag(a0.x(), a0.y(), pt)
                else:
                    if a0.modifiers(
                    ) & QtCore.Qt.KeyboardModifier.ShiftModifier:  # select
                        self.selector.addSelection(d)
                    elif a0.modifiers(
                    ) & QtCore.Qt.KeyboardModifier.ControlModifier:  # un-select
                        self.selector.delSelection(d)
            self.repaint()
        elif changed:
            self.repaint()
        return super().mouseMoveEvent(a0)

    def mouseReleaseEvent(self, a0: QtGui.QMouseEvent) -> None:
        self.pressPos = None

        if self.pressPosPath:  # circle select, calc how many blocks have been circled
            sx, sy = self.deltaxy()
            bs = self._blocksize()
            poly = QtGui.QPolygon()
            for x, y in self.pressPosPath:
                poly.append(QtCore.QPoint(x, y))
            for y in range(self.boxRows):
                for x in range(self.boxCols):
                    cell = self._getVirtualCell(x, y)
                    if cell and cell.valid():
                        posx, posy = x * bs + sx - bs, y * bs + sy - bs
                        p = QtCore.QPoint(posx + bs // 2, posy + bs // 2)
                        if poly.containsPoint(p,
                                              QtCore.Qt.FillRule.OddEvenFill):
                            self.selector.addSelection(cell,
                                                       propertyPanel=False)
            self.findMainWin().propertyPanel.update()
            self.pressPosPath.clear()

        if self.hover.labels:
            pass  # ghost hold depends on dragger, don't end() it here, call end() in mousePressEvent
        else:
            self.pressHoldSel = False
            dx, dy = self.dragger.end()
            if dx != 0 or dy != 0:
                self.selector.moveEnd(dx, dy)

        self.pan(0, 0)
        QApplication.restoreOverrideCursor()
        return super().mouseReleaseEvent(a0)

    def wheelEvent(self, a0: QtGui.QWheelEvent) -> None:
        if self.selector.labels and self.pressHoldSel:
            return super().wheelEvent(a0)

        if a0.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier:
            c = self.hover.cats()
            if len(c) == 1:
                v = a0.angleDelta().y() or a0.angleDelta().x()
                self.hover.incr() if v > 0 else self.hover.decr()
                self.repaint()
            return super().wheelEvent(a0)

        lastScale = self.scale
        if a0.angleDelta().y() > 0:
            self.scale = min(
                self.scale < 1 and self.scale + 0.125 or self.scale + 1, 32)
        else:
            self.scale = max(
                self.scale <= 1 and self.scale - 0.125 or self.scale - 1, 0.25)

        c = a0.pos()
        self.viewOrigin[0] = int((self.viewOrigin[0] - c.x()) * self.scale /
                                 lastScale + c.x())
        self.viewOrigin[1] = int((self.viewOrigin[1] - c.y()) * self.scale /
                                 lastScale + c.y())
        self.pan(0, 0)
        return super().wheelEvent(a0)