Color
@Color
Golang SWE, Cloud & DevOps

Как реализовать взаимодействие gui и программы, на примере PyQt и python?

Никогда не писал gui только cli, только хардкор, а вот теперь нужно написать. Есть несколько частных вопросов, которые как-бы часть одного:
Как обычно реализуется взаимодействие программы с графическим интерфейсом?
Как это реализуется?
Каким образом gui общается с программой?
Как должны запускаться модули программы (логика, интерфейс), кто должен кем управлять?

Читал про MVC/MVP и т.д., про оформление интерфейсов, а вот про то, как реализовать технически взаимодействие между ними (как написать стрелочки из диаграммы в коде) так ничего и не понял.

В этом вопросе я полный нуб, так что ссылки с описанием всякого и примерами, хорошие практики и правила хорошего тона, а также просто советы приветствуются :)

UPD: необходимо написать gui для существующего консольного приложения
  • Вопрос задан
  • 2017 просмотров
Пригласить эксперта
Ответы на вопрос 1
bobrovskyserg
@bobrovskyserg
Лови тетрис под Python3
import sys
from random import randint
from PyQt4 import QtCore, QtGui


class Tetris(QtGui.QMainWindow):
    def __init__(self):
        super(Tetris, self).__init__()
        self.tboard = Board(self)
        self.setCentralWidget(self.tboard)
        self.statusbar = self.statusBar()
        self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
        self.tboard.start()
        self.resize(180, 380)
        self.center()
        self.setWindowTitle('Tetris')
        self.show()

    def center(self):
        screen = QtGui.QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width() - size.width()) // 2, (screen.height() - size.height()) // 2)


class Board(QtGui.QFrame):
    msg2Statusbar = QtCore.pyqtSignal(str)
    BoardWidth, BoardHeight, Speed = 10, 22, 300
    EmptyLine = [0] * BoardWidth
    geometry = (None,
                (((0, 0), (-1, -1), (0, -1), (-1, -2)), ((-1, 0), (0, 0), (0, -1), (1, -1)),
                 ((0, 0), (-1, -1), (0, -1), (-1, -2)), ((-1, 0), (0, 0), (0, -1), (1, -1))),
                (((-1, 0), (-1, -1), (0, -1), (0, -2)), ((0, 0), (1, 0), (-1, -1), (0, -1)),
                 ((-1, 0), (-1, -1), (0, -1), (0, -2)), ((0, 0), (1, 0), (-1, -1), (0, -1))),
                (((0, 0), (0, -1), (0, -2), (0, -3)), ((-2, -1), (-1, -1), (0, -1), (1, -1)),
                 ((0, 0), (0, -1), (0, -2), (0, -3)), ((-2, -1), (-1, -1), (0, -1), (1, -1))),
                (((-1, 0), (0, 0), (1, 0), (0, -1)), ((1, 0), (0, -1), (1, -1), (1, -2)),
                 ((0, 0), (-1, -1), (0, -1), (1, -1)), ((-1, 0), (-1, -1), (0, -1), (-1, -2))),
                (((-1, 0), (0, 0), (-1, -1), (0, -1)), ((-1, 0), (0, 0), (-1, -1), (0, -1)),
                 ((-1, 0), (0, 0), (-1, -1), (0, -1)), ((-1, 0), (0, 0), (-1, -1), (0, -1))),
                (((0, 0), (1, 0), (0, -1), (0, -2)), ((-1, 0), (0, 0), (1, 0), (1, -1)),
                 ((0, 0), (0, -1), (-1, -2), (0, -2)), ((-1, 0), (-1, -1), (0, -1), (1, -1))),
                (((-1, 0), (0, 0), (0, -1), (0, -2)), ((1, 0), (-1, -1), (0, -1), (1, -1)),
                 ((0, 0), (0, -1), (0, -2), (1, -2)), ((-1, 0), (0, 0), (1, 0), (-1, -1))))

    def __init__(self, parent):
        super(Board, self).__init__(parent)
        self.timer = QtCore.QBasicTimer()
        self.isWaitingAfterLine = self.isStarted = self.isPaused = False
        self.numLinesRemoved = 0
        self.setFocusPolicy(QtCore.Qt.StrongFocus)

    def start(self):
        if self.isPaused:
            return
        self.isStarted = True
        self.isWaitingAfterLine = False
        self.numLinesRemoved = 0
        self.board = [Board.EmptyLine.copy() for _ in range(Board.BoardHeight)]
        self.msg2Statusbar.emit(str(self.numLinesRemoved))
        self.newPiece()
        self.timer.start(Board.Speed, self)

    def paintEvent(self, event):
        glass = [l.copy() for l in self.board]
        if self.shape:
            s, a, X, Y = self.shape
            for x, y in self.geometry[s][a]:
                glass[y + Y][x + X] = s
        h, w = self.contentsRect().height() // Board.BoardHeight, self.contentsRect().width() // Board.BoardWidth
        painter = QtGui.QPainter(self)
        for y, l in enumerate(glass):
            y = self.contentsRect().bottom() - (y + 1) * h
            for x, s in enumerate(l):
                if s:
                    x *= w
                    color = QtGui.QColor(
                        (None, 0xCC6666, 0x66CC66, 0x6666CC, 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00)[s])
                    painter.fillRect(x + 1, y + 1, w - 2, h - 2, color)
                    painter.setPen(color.light())
                    painter.drawLine(x, y + h - 1, x, y)
                    painter.drawLine(x, y, x + w - 1, y)
                    painter.setPen(color.dark())
                    painter.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1)
                    painter.drawLine(x + w - 1, y + h - 1, x + w - 1, y + 1)


    def keyPressEvent(self, event):
        if not (self.isStarted and self.shape):
            super(Board, self).keyPressEvent(event)
            return
        key = event.key()
        if key == QtCore.Qt.Key_P:
            self.isPaused ^= True
            if self.isPaused:
                self.timer.stop()
                self.msg2Statusbar.emit('paused')
            else:
                self.timer.start(Board.Speed, self)
                self.msg2Statusbar.emit(str(self.numLinesRemoved))
        elif key == QtCore.Qt.Key_Left:
            self.nextshape[2] -= 1
            self.tryMove()
        elif key == QtCore.Qt.Key_Right:
            self.nextshape[2] += 1
            self.tryMove()
        elif key == QtCore.Qt.Key_Down:
            self.nextshape[1] = (self.shape[1] + 1) % 4
            self.tryMove()
        elif key == QtCore.Qt.Key_Up:
            self.nextshape[1] = (self.shape[1] + 3) % 4
            self.tryMove()
        elif key == QtCore.Qt.Key_Space:
            self.dropDown()
        elif key == QtCore.Qt.Key_D:
            self.oneLineDown()
        else:
            super(Board, self).keyPressEvent(event)

    def timerEvent(self, event):
        if event.timerId() == self.timer.timerId():
            if self.isWaitingAfterLine:
                self.isWaitingAfterLine = False
                self.newPiece()
            else:
                self.oneLineDown()
        else:
            super(Board, self).timerEvent(event)

    def dropDown(self):
        while self.tryMove():
            self.nextshape[3] -= 1
        self.pieceDropped()

    def oneLineDown(self):
        self.nextshape[3] -= 1
        if not self.tryMove():
            self.pieceDropped()

    def pieceDropped(self):
        s, a, X, Y = self.shape
        for x, y in self.geometry[s][a]:
            self.board[y + Y][x + X] = s
        self.board = [l for l in self.board if not all(l)]
        numFullLines = Board.BoardHeight - len(self.board)
        if numFullLines:
            for i in range(numFullLines):
                self.board.append(Board.EmptyLine.copy())
            self.numLinesRemoved += numFullLines
            self.msg2Statusbar.emit(str(self.numLinesRemoved))
            self.isWaitingAfterLine = True
            self.shape = None
            self.update()
        if not self.isWaitingAfterLine:
            self.newPiece()

    def newPiece(self):
        self.nextshape = [randint(1, 7), 0, Board.BoardWidth // 2, Board.BoardHeight - 1]
        if not self.tryMove():
            self.shape = None
            self.timer.stop()
            self.isStarted = False
            self.msg2Statusbar.emit("Game over")

    def tryMove(self):
        s, a, X, Y = self.nextshape
        for x, y in self.geometry[s][a]:
            x += X
            y += Y
            if not (0 <= x < Board.BoardWidth and 0 <= y) or self.board[y][x]:
                self.nextshape = self.shape.copy()
                return False
        self.shape = self.nextshape.copy()
        self.update()
        return True


if __name__ == '__main__':
    app = QtGui.QApplication([])
    tetris = Tetris()
    sys.exit(app.exec_())
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы