def sizeHint(self): """Set prefered size. """ # default in Qt is 23 height & about 10 * numDigits size = QLCDNumber.sizeHint(self) return QSize(int(size.width() * self.sizeFactor), int(size.height() * self.sizeFactor))
class WheelBox(QWidget): valueChanged = pyqtSignal('double', name="valueChanged") def __init__(self, title, min, max, stepSize, parent): super().__init__(parent) self.__d_number = QLCDNumber(self) self.__d_number.setSegmentStyle(QLCDNumber.Filled) self.__d_number.setAutoFillBackground(True) self.__d_number.setFixedHeight(self.__d_number.sizeHint().height() * 2) self.__d_number.setFocusPolicy(Qt.WheelFocus) pal = QPalette(Qt.black) pal.setColor(QPalette.WindowText, Qt.green) self.__d_number.setPalette(pal) self.__d_wheel = Wheel(self) self.__d_wheel.setOrientation(Qt.Vertical) self.__d_wheel.setInverted(True) self.__d_wheel.setRange(min, max) self.__d_wheel.setSingleStep(stepSize) self.__d_wheel.setPageStepCount(5) self.__d_wheel.setFixedHeight(self.__d_number.height()) self.__d_number.setFocusProxy(self.__d_wheel) font = QFont("Helvetica", 10) font.setBold(True) self.__d_label = QLabel(title, self) self.__d_label.setFont(font) hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 0) hLayout.setSpacing(2) hLayout.addWidget(self.__d_number, 10) hLayout.addWidget(self.__d_wheel) vLayout = QVBoxLayout(self) vLayout.addLayout(hLayout, 10) vLayout.addWidget(self.__d_label, 0, Qt.AlignTop | Qt.AlignHCenter) self.__d_wheel.valueChanged.connect(self.display) self.__d_wheel.valueChanged.connect(self.valueChanged) def display(self, value): self.__d_number.display(value) def theme(self): return self.__d_wheel.palette().color(QPalette.Window) def setTheme(self, color): self.__d_wheel.setPalette(QPalette(color)) def setValue(self, value): self.__d_wheel.setValue(value) self.__d_number.display(value) def value(self): return self.__d_wheel.value() theme = pyqtProperty(QColor, theme, setTheme)
class Klotski(QMainWindow): def __init__(self, maps: Dict[int, KLMap], firstBoard: Optional[int] = None) -> None: QMainWindow.__init__(self) self.klmap = None # type: Optional[KLMap] self.moves = 0 self.move_list = [] # type: List[ Tuple[str, Tuple[int, int]]] self.move_index = -1 self.mini_maps_dict = {} # type: Dict[int, QPixmap] self.levels_by_id = maps self.init_misc_gui() self.board.load_tiles() self.board.sig_move.connect(self.move_tile) self.generate_mini_maps() self.board_chooser = KLBoardChooser( KlMinimapProvider(self.levels_by_id, self.mini_maps_dict), self) self.board_chooser.hide() # needs self.board self.init_menu() self.setWindowTitle("Klotski") self.title_label.setText("Klotski") if firstBoard: self.new_level(self.levels_by_id[int(firstBoard)]) self.move_enabled = True else: self.new_level(self.levels_by_id[ID_SPLASH_SCREEN]) self.move_enabled = False def init_misc_gui(self) -> None: w = QWidget(self) self.setCentralWidget(w) ly = QVBoxLayout() w.setLayout(ly) ly.setSpacing(10) self.title_label = QLabel("Klotski", w) ft = self.title_label.font() ft.setPointSize(20) self.title_label.setFont(ft) self.title_label.setAlignment(Qt.AlignCenter) self.title_label.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) ly.addWidget(self.title_label) hb = QHBoxLayout() self.board = KLBoard(w) hb.addWidget(self.board) hb.setStretchFactor(self.board, 100) ly.addLayout(hb) # horizontal bottom hbox [ QLabel | QLcdNumber ] hb = QHBoxLayout() # hb.setSpacing( 5 ) # hb.setMargin( 5 ) ml = QLabel("Moves : ", w) ml.setFont(ft) ml.setAlignment(Qt.AlignRight) hb.addWidget(ml) self.move_lcd_nb = QLCDNumber(4, w) self.move_lcd_nb.adjustSize() self.move_lcd_nb.setFixedSize(self.move_lcd_nb.width(), 30) hb.addWidget(self.move_lcd_nb) # hb.setFixedHeight( hb.height() ) ly.addLayout(hb) self.adjustSize() def generate_mini_maps(self) -> None: self.mini_maps_dict = {} for i in self.levels_by_id.keys(): self.mini_maps_dict[i] = self.board.generate_mini_map( self.levels_by_id[i]) def new_level(self, m: KLMap) -> None: '''Display a new map into the main window''' if m.name != NAME_SPLASH_SCREEN: # display the name of the board self.setWindowTitle("Klotski - " + m.name) self.title_label.setText(m.name) else: # display the splash screen self.setWindowTitle("Klotski") self.title_label.setText('') self.klmap = m w = max(m.w * TILE_SIZE + 10 + 40, self.title_label.sizeHint().width() + 20) h = (m.h * TILE_SIZE + 10 + 80 + self.title_label.sizeHint().height() + max(self.move_lcd_nb.sizeHint().height(), self.title_label.sizeHint().height())) center = self.frameGeometry().center() self.resize(w, h) r = self.frameGeometry() r.moveCenter(center) desktop = QApplication.desktop() if r.x() + w > desktop.width() or r.x( ) < 0 or r.y() + h > desktop.height() or r.y() < 0: r.moveCenter(desktop.rect().center()) if r.x() < 0: r.setX(0) if r.y() < 0: r.setY(0) if r.x() + w > desktop.width(): r.setX(0) w = desktop.width() - 20 - r.x() if r.y() + h > desktop.height(): r.setY(0) h = desktop.height() - 20 - r.y() self.move(r.topLeft()) self.resize(w, h) self.reset() def choose_board(self) -> None: self.board_chooser.exec() board_id = self.board_chooser.result() if board_id in self.levels_by_id: self.new_level(self.levels_by_id[board_id]) def init_menu(self) -> None: file_menu = QMenu('Game', self) file_menu.addAction("Boards", self.choose_board, Qt.CTRL + Qt.Key_B) file_menu.addAction("Quit", self.close, Qt.CTRL + Qt.Key_X) # type: ignore file_menu.addAction("About Klotski", self.about) move_menu = QMenu('Moves', self) move_menu.addAction("Reset", self.reset) move_menu.addAction("Undo", self.undo, Qt.CTRL + Qt.Key_U) move_menu.addAction("Redo", self.redo, Qt.CTRL + Qt.Key_R) main_menu = self.menuBar() main_menu.addMenu(file_menu) main_menu.addMenu(move_menu) def set_move_nb(self, m: int) -> None: self.move_lcd_nb.display(m) self.moves = m def move_tile(self, move_info: Tuple[str, Tuple[int, int]]) -> None: pid, delta = move_info if not self.move_enabled: return assert self.klmap self.klmap.move_piece(pid, delta) self.board.move_piece(pid, delta) if self.move_index < 0: self.move_list = [(pid, (0, 0))] self.move_index = 0 self.set_move_nb(self.moves + 1) last_move_pid, last_move = self.move_list[self.move_index] if pid != last_move_pid: self.move_index = self.move_index + 1 self.move_list[self.move_index:] = [(pid, delta)] self.set_move_nb(self.moves + 1) else: self.move_list[self.move_index:] = [ (pid, (last_move[0] + delta[0], last_move[1] + delta[1])) ] if self.klmap.is_game_won(): QMessageBox.information( self, "Congratulation", "Congratulations!!!\nYou completed this level in %d moves" % self.moves) self.move_enabled = False self.klmap.reset() def reset(self) -> None: assert self.klmap self.klmap.reset() self.board.set_map(self.klmap) self.set_move_nb(0) self.move_enabled = True self.move_list = [] self.move_index = -1 def undo(self) -> None: if self.move_index < 0: return if not self.move_enabled: return pid = self.move_list[self.move_index][0] d = reverse_move(self.move_list[self.move_index][1]) assert self.klmap assert self.board self.klmap.move_piece(pid, d) self.board.move_piece(pid, d) self.set_move_nb(self.moves - 1) self.move_index = self.move_index - 1 def redo(self) -> None: if self.move_index + 1 >= len(self.move_list): return if not self.move_enabled: return self.move_index = self.move_index + 1 pid, d = self.move_list[self.move_index] assert self.klmap assert self.board self.klmap.move_piece(pid, d) self.board.move_piece(pid, d) self.set_move_nb(self.moves + 1) def about(self) -> None: QMessageBox.about(self, 'About Klotski', MSG_ABOUT) def print_state(self) -> None: print(self.klmap) print("Move list : ", self.move_list) print("move list index : ", self.move_index)