Example #1
0
    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')
Example #2
0
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')