def __init__(self, playtest=False): common.MainWindow.__init__(self) if not playtest: self.resize(1280, 720) self.setWindowIcon(QIcon(here('resources', 'player.ico'))) self.scene = Scene() self.central_widget = QWidget() self.setCentralWidget(self.central_widget) layout = QVBoxLayout() layout.setContentsMargins(QMargins()) layout.setSpacing(0) self.central_widget.setLayout(layout) self.levels_bar = QTabBar() layout.addWidget(self.levels_bar) self.levels_bar.currentChanged.connect(self.level_change) top_layout = QHBoxLayout() layout.addLayout(top_layout) self.author_align_label = QLabel() self.author_align_label.setStyleSheet('color: rgba(0,0,0,0%)') top_layout.addWidget(self.author_align_label, 0) self.title_label = QLabel() self.title_label.setAlignment(qt.AlignHCenter) update_font(self.title_label, lambda f: multiply_font_size(f, 1.8)) top_layout.addWidget(self.title_label, 1) self.author_label = QLabel() self.author_label.setAlignment(qt.AlignRight) top_layout.addWidget(self.author_label, 0) self.view = View(self.scene) layout.addWidget(self.view, 1) self.information_label = QLabel() self.information_label.setAlignment(qt.AlignHCenter) self.information_label.setWordWrap(True) self.information_label.setContentsMargins(5, 5, 5, 5) update_font(self.information_label, lambda f: multiply_font_size(f, 1.5)) layout.addWidget(self.information_label) self.scene.playtest = self.playtest = playtest menu = self.menuBar().addMenu("&File") if not playtest: action = menu.addAction("&Open...", self.load_file, QKeySequence.Open) menu.addSeparator() self.copy_action = action = menu.addAction("&Copy State to Clipboard", lambda: self.copy(display=True), QKeySequence('Ctrl+C')) action.setStatusTip("Copy the current state of the level into clipboard, in a text-based .hexcells format, padded with Tab characters.") if not playtest: action = menu.addAction("&Paste from Clipboard", self.paste, QKeySequence('Ctrl+V')) action.setStatusTip("Load a level in text-based .hexcells format that is currently in the clipboard.") menu.addSeparator() action = menu.addAction("&Quit", self.close, QKeySequence('Tab') if playtest else QKeySequence.Quit) if playtest: QShortcut(QKeySequence.Quit, self, action.trigger) menu = self.menuBar().addMenu("&Edit") action = menu.addAction("&Undo", self.scene.undo, QKeySequence.Undo) QShortcut(QKeySequence('Z'), self, action.trigger) action.setStatusTip("Cover the last uncovered cell.") action = menu.addAction("Clear &Progress", self.clear_progress) menu.addSeparator() menu.addAction("&Clear Annotations", self.scene.clear_guesses, QKeySequence("X")) menu.addAction("Con&firm Annotated Guesses", self.scene.confirm_guesses, QKeySequence("C")) menu.addAction("&Deny Annotated Guesses", self.scene.confirm_opposite_guesses, QKeySequence("D")) menu.addSeparator() menu.addAction("Highlight All C&olumn Hints", self.scene.highlight_all_columns) menu.addAction("Highlight All F&lower Hints", self.scene.highlight_all_flowers) menu = self.menuBar().addMenu("&Solve") menu.setEnabled(solve is not None) menu.addAction("&One Step", self.scene.solve_step, QKeySequence("S")) action = menu.addAction("Con&firm Solved", self.scene.confirm_guesses, QKeySequence("C")) action.setShortcutContext(qt.WidgetWithChildrenShortcut) # To prevent "ambiguous shortcut" action = menu.addAction("&Clear Solved", self.scene.clear_guesses, QKeySequence("X")) action.setShortcutContext(qt.WidgetWithChildrenShortcut) menu.addSeparator() menu.addAction("&Solve Completely", self.scene.solve_complete) menu = self.menuBar().addMenu("&Preferences") self.swap_buttons_action = action = make_check_action("&Swap Buttons", self, self.scene, 'swap_buttons') menu.addAction(action) menu = self.menuBar().addMenu("&Help") action = menu.addAction("&Instructions", self.help, QKeySequence.HelpContents) action = menu.addAction("&About", self.about) self.last_used_folder = None self.close_file() load_config_from_file(self, self.config_format, 'sixcells', 'player.cfg')
class MainWindow(common.MainWindow): title = "SixCells Player" Cell = Cell Column = Column def __init__(self, playtest=False): common.MainWindow.__init__(self) if not playtest: self.resize(1280, 720) self.setWindowIcon(QIcon(here('resources', 'player.ico'))) self.scene = Scene() self.central_widget = QWidget() self.setCentralWidget(self.central_widget) layout = QVBoxLayout() layout.setContentsMargins(QMargins()) layout.setSpacing(0) self.central_widget.setLayout(layout) self.levels_bar = QTabBar() layout.addWidget(self.levels_bar) self.levels_bar.currentChanged.connect(self.level_change) top_layout = QHBoxLayout() layout.addLayout(top_layout) self.author_align_label = QLabel() self.author_align_label.setStyleSheet('color: rgba(0,0,0,0%)') top_layout.addWidget(self.author_align_label, 0) self.title_label = QLabel() self.title_label.setAlignment(qt.AlignHCenter) update_font(self.title_label, lambda f: multiply_font_size(f, 1.8)) top_layout.addWidget(self.title_label, 1) self.author_label = QLabel() self.author_label.setAlignment(qt.AlignRight) top_layout.addWidget(self.author_label, 0) self.view = View(self.scene) layout.addWidget(self.view, 1) self.information_label = QLabel() self.information_label.setAlignment(qt.AlignHCenter) self.information_label.setWordWrap(True) self.information_label.setContentsMargins(5, 5, 5, 5) update_font(self.information_label, lambda f: multiply_font_size(f, 1.5)) layout.addWidget(self.information_label) self.scene.playtest = self.playtest = playtest menu = self.menuBar().addMenu("&File") if not playtest: action = menu.addAction("&Open...", self.load_file, QKeySequence.Open) menu.addSeparator() self.copy_action = action = menu.addAction("&Copy State to Clipboard", lambda: self.copy(display=True), QKeySequence('Ctrl+C')) action.setStatusTip("Copy the current state of the level into clipboard, in a text-based .hexcells format, padded with Tab characters.") if not playtest: action = menu.addAction("&Paste from Clipboard", self.paste, QKeySequence('Ctrl+V')) action.setStatusTip("Load a level in text-based .hexcells format that is currently in the clipboard.") menu.addSeparator() action = menu.addAction("&Quit", self.close, QKeySequence('Tab') if playtest else QKeySequence.Quit) if playtest: QShortcut(QKeySequence.Quit, self, action.trigger) menu = self.menuBar().addMenu("&Edit") action = menu.addAction("&Undo", self.scene.undo, QKeySequence.Undo) QShortcut(QKeySequence('Z'), self, action.trigger) action.setStatusTip("Cover the last uncovered cell.") action = menu.addAction("Clear &Progress", self.clear_progress) menu.addSeparator() menu.addAction("&Clear Annotations", self.scene.clear_guesses, QKeySequence("X")) menu.addAction("Con&firm Annotated Guesses", self.scene.confirm_guesses, QKeySequence("C")) menu.addAction("&Deny Annotated Guesses", self.scene.confirm_opposite_guesses, QKeySequence("D")) menu.addSeparator() menu.addAction("Highlight All C&olumn Hints", self.scene.highlight_all_columns) menu.addAction("Highlight All F&lower Hints", self.scene.highlight_all_flowers) menu = self.menuBar().addMenu("&Solve") menu.setEnabled(solve is not None) menu.addAction("&One Step", self.scene.solve_step, QKeySequence("S")) action = menu.addAction("Con&firm Solved", self.scene.confirm_guesses, QKeySequence("C")) action.setShortcutContext(qt.WidgetWithChildrenShortcut) # To prevent "ambiguous shortcut" action = menu.addAction("&Clear Solved", self.scene.clear_guesses, QKeySequence("X")) action.setShortcutContext(qt.WidgetWithChildrenShortcut) menu.addSeparator() menu.addAction("&Solve Completely", self.scene.solve_complete) menu = self.menuBar().addMenu("&Preferences") self.swap_buttons_action = action = make_check_action("&Swap Buttons", self, self.scene, 'swap_buttons') menu.addAction(action) menu = self.menuBar().addMenu("&Help") action = menu.addAction("&Instructions", self.help, QKeySequence.HelpContents) action = menu.addAction("&About", self.about) self.last_used_folder = None self.close_file() load_config_from_file(self, self.config_format, 'sixcells', 'player.cfg') config_format = ''' swap_buttons = swap_buttons_action.isChecked(); swap_buttons_action.setChecked(v) antialiasing = view.antialiasing; view.antialiasing = v last_used_folder window_geometry_qt = save_geometry_qt(); restore_geometry_qt(v) ''' def close_file(self): if not self.playtest: total = 0 revealed = 0 for cell in self.scene.all(Cell): if not cell.revealed: total += 1 if cell.display is not Cell.unknown: revealed += 1 clearing = hasattr(self, 'clearing') if 0 < revealed < total or clearing: try: saved = save(self.scene, display=True, padding=False) do_save = self.original_level != saved if do_save: if not clearing: msg = "Would you like to save your progress for this level?" btn = QMessageBox.warning(self, "Unsaved progress", msg, QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save) else: msg = "Are you sure you want to clear progress for this level?" btn = QMessageBox.warning(self, "Clear progress", msg, QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Discard) if btn == QMessageBox.Discard: do_save = False elif btn == QMessageBox.Cancel: return with db_connection('sixcells.sqlite3') as con: with con: con.execute('CREATE TABLE IF NOT EXISTS `saves` (`level` TEXT PRIMARY KEY, `save` TEXT, `mistakes` INT)') con.execute('DELETE FROM `saves` WHERE `level` = ?', (self.original_level,)) if do_save: con.execute('INSERT INTO `saves` (`level`, `save`, `mistakes`) VALUES (?, ?, ?)', (self.original_level, saved, self.scene.mistakes)) except Exception as e: pass self.current_file = None self.scene.clear() self.scene.remaining = 0 self.scene.mistakes = 0 self.scene.reset_cache() for it in [self.title_label, self.author_align_label, self.author_label, self.information_label]: it.hide() self.copy_action.setEnabled(False) self.undo_history = [] self.view.progress_loaded_timer.stop() self.view.viewport().repaint() return True def clear_progress(self): self.clearing = True self.load_one(self.original_level) delattr(self, 'clearing') @event_property def current_file(self): title = self.title if self.current_file: title = os.path.basename(self.current_file) + ' - ' + title self.setWindowTitle(("Playtest" + ' - ' if self.playtest else '') + title) def prepare(self): if not self.playtest: self.view.fit() remaining = 0 for i, cell in enumerate(self.scene.all(Cell)): cell.id = i if cell.kind is Cell.full and not cell.revealed: remaining += 1 cell._display = cell.kind if cell.revealed else Cell.unknown for i, col in enumerate(self.scene.all(Column)): col.id = i self.scene.remaining = remaining self.scene.mistakes = 0 author_text = ("by {}" if self.scene.author else "").format(self.scene.author) for txt, it in [ (self.scene.title, self.title_label), (author_text, self.author_label), (author_text, self.author_align_label), (self.scene.information, self.information_label), ]: if txt: it.setText(txt) it.show() else: it.hide() self.scene.full_upd() self.copy_action.setEnabled(True) def load_one(self, level): if common.MainWindow.load(self, level): self.original_level = save(self.scene, padding=False) try: with db_connection('sixcells.sqlite3') as con: with con: [(saved, mistakes)] = con.execute('SELECT `save`, `mistakes` FROM `saves` WHERE `level` = ?', (self.original_level,)) common.MainWindow.load(self, saved) self.scene.mistakes = mistakes self.view.progress_loaded_timer.start() self.view.viewport().update() except Exception: pass self.view.setFocus() return True def load(self, level): while self.levels_bar.count(): self.levels_bar.removeTab(0) self.levels_bar.hide() levels = [] lines = level.splitlines() start = None skip = 0 for i, line in enumerate(lines + [None]): if skip: skip -= 1 continue if line is None or line.strip() == 'Hexcells level v1': if start is not None: level_lines = lines[start:i] levels.append(('\n'.join(level_lines), level_lines[1])) start = i skip = 4 self.current_level = 0 if len(levels) > 1: self.levels_bar.show() self.load_one(levels[0][0]) for level, title in levels: self.levels_bar.addTab(title) self.levels_bar.setTabData(self.levels_bar.count()-1, level) else: self.load_one(level) def level_change(self, index): if index >= 0 and index != self.current_level: level = self.levels_bar.tabData(index) if level: if self.load_one(level): self.current_level = index else: self.levels_bar.setCurrentIndex(self.current_level) def closeEvent(self, e): if not self.close_file(): e.ignore() return self.scene.solving = 0 save_config_to_file(self, self.config_format, 'sixcells', 'player.cfg')