def __init__( self, editable: t.Union[Editable, t.Mapping[str, t.Any]], undo_stack: t.Optional[QUndoStack] = None, ) -> None: super().__init__( Context.get_undo_stack() if undo_stack is None else undo_stack) self._loading_lock = threading.Lock() self._loading_event = threading.Event() self._loading = False if isinstance(editable, Editable): self._editable = editable self._serialized = None else: self._editable = None self._serialized = editable self._layout = QtWidgets.QVBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._loading_label = None if self._editable is None: self._loading_label = QtWidgets.QLabel('Loading...') self._loading_label.setAlignment(QtCore.Qt.AlignCenter) self._layout.addWidget(self._loading_label) self.editable_loaded.connect(self._on_loaded) else: self._layout.addWidget(self._editable) self._editable.tab = self self._editable.show()
def add_editable(self, editable: t.Union[Editable, t.Mapping[str, t.Any]], meta: TabMeta) -> Tab: tab = EditorTab( editable, editable.undo_stack if isinstance(editable, Editable) else Context.get_undo_stack(), ) self.addTab(tab, meta.truncated_name) self._metas[tab] = meta return tab
def new_draft(self, draft_id: str) -> None: for editable, meta in self._metas.items(): if meta.key == draft_id: self.setCurrentWidget(editable) return saved_draft = Context.saved_drafts.pop(draft_id, None) self.setCurrentWidget( self.add_editable( (DraftView.load(saved_draft, Context.get_undo_stack()) if saved_draft is not None else DraftView( DraftModel(draft_id), Context.get_undo_stack(), )), TabMeta( 'some draft', key=draft_id, ), ))
def __init__(self, draft_model: DraftModel): super().__init__() self._undo_stack = Context.get_undo_stack() self._draft_model = draft_model self._latest_meta_info = PickMetaInfo(draft_model) self._head_meta_info = PickMetaInfo(draft_model) self._head_meta_info.hide() self._countdown = Countdown() self._countdown.hide() self._picking_info = QtWidgets.QLabel('') self._picking_info.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self._booster_scene = CubeScene( mode=CubeEditMode.CLOSED, scene_type=SceneType.BOOSTER, ) self._booster_view = CubeView( scene=self._booster_scene, undo_stack=self._undo_stack, cube_image_view=BoosterImageView( self._undo_stack, self._booster_scene, self._draft_model, ), ) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) info_bar_layout = QtWidgets.QHBoxLayout() info_bar_layout.setContentsMargins(0, 0, 0, 0) info_bar_layout.addWidget(self._latest_meta_info) info_bar_layout.addWidget(self._head_meta_info) info_bar_layout.addStretch() info_bar_layout.addWidget(self._picking_info) info_bar_layout.addWidget(self._countdown) layout.addLayout(info_bar_layout) layout.addWidget(self._booster_view) self._draft_model.new_head.connect(self._set_pick_point) self._draft_model.picked.connect(self._on_picked) self._draft_model.received_booster.connect(self._on_receive_booster) self._draft_model.boosters_changed.connect(self._on_boosters_changed) self._booster_view.cube_image_view.card_double_clicked.connect( self._on_card_double_clicked)
def new_limited_deck(self, deck: LimitedDeck) -> None: key = f'pool-deck-{deck.id}' if self._check_for_key(key): return tab = self.add_editable( DeckView( DeckModel( list(map(PhysicalCard.from_cubeable, deck.deck.maindeck)), list(map(PhysicalCard.from_cubeable, deck.deck.sideboard)), ), undo_stack=Context.get_undo_stack(), ), TabMeta( deck.name, key=key, ), ) self.setCurrentWidget(tab) self._sort_opened_view(tab.editable)
def new_pool(self, pool: Cube, infinites: Infinites, key: str) -> None: for tab, meta in self._metas.items(): if meta.key == key: self.setCurrentWidget(tab) return tab = self.add_editable( PoolView( PoolModel( list(map(PhysicalCard.from_cubeable, pool)), infinites=infinites, ), undo_stack=Context.get_undo_stack(), ), TabMeta( key, key=key, ), ) self.setCurrentWidget(tab) self._sort_opened_view(tab.editable)
def __init__(self, deck_scene: CubeScene, *, parent: t.Optional[QtWidgets.QWidget] = None) -> None: super().__init__(parent) self._deck_scene = deck_scene self._undo_stack = Context.get_undo_stack() self._hand_scene = CubeScene( aligner_type=GridAligner, mode=CubeEditMode.CLOSED, scene_type=SceneType.SAMPLE_HAND, ) self._hand_view = CubeView(self._hand_scene, self._undo_stack) self._draw_hand_button = QtWidgets.QPushButton('Draw Hand') self._draw_hand_button.clicked.connect(self.refresh) self._create_shortcut(self.refresh, 'h') self._draw_card_button = QtWidgets.QPushButton('Draw Card') self._draw_card_button.clicked.connect(lambda: self.add_cubeables(1)) self._create_shortcut(self.add_cubeables, 'd') layout = QtWidgets.QVBoxLayout(self) control_bar = QtWidgets.QHBoxLayout() control_bar.addWidget(self._draw_hand_button) control_bar.addWidget(self._draw_card_button) layout.addWidget(self._hand_view) layout.addLayout(control_bar)
def __init__(self): super().__init__() self._match: t.Optional[ScheduledMatch] = None self._participants_model = ListTableModel(ParticipantsSchema()) self._participants_view: ReadOnlyListTableView[ TournamentParticipant] = ReadOnlyListTableView() self.setFocusProxy(self._participants_view) self._participants_view.setModel(self._participants_model) self._participants_view.item_selected.connect( lambda p: Context.editor.open_limited_deck(p.deck.id)) self._name_label = QtWidgets.QLabel() self._name_label.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self._round_label = QtWidgets.QLabel() self._round_label.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self._deck_cache: t.MutableMapping[int, t.Optional[LimitedDeck]] = {} self._deck_preview_model = DeckModel(mode=CubeEditMode.CLOSED) self._deck_preview = DeckView( self._deck_preview_model, Context.get_undo_stack(), ) self._no_preview_view = QtWidgets.QLabel('No preview selected') self._no_preview_view.setAlignment(Qt.AlignCenter) self._preview_unavailable_view = QtWidgets.QLabel( 'Preview unavailable') self._preview_unavailable_view.setAlignment(Qt.AlignCenter) self._preview_stack = QtWidgets.QStackedWidget() self._preview_stack.addWidget(self._deck_preview) self._preview_stack.addWidget(self._preview_unavailable_view) self._preview_stack.addWidget(self._no_preview_view) self._preview_stack.setCurrentWidget(self._no_preview_view) self.preview_fetched.connect(self._on_retrieved_deck) self._participants_view.current_item_changed.connect( self._on_participant_selected) self._open_button = QtWidgets.QPushButton('Open') self._open_button.clicked.connect(self._on_open_clicked) self._open_all_button = QtWidgets.QPushButton('Open All') self._open_all_button.clicked.connect(self._on_open_all_clicked) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) top_bar = QtWidgets.QHBoxLayout() top_bar.setContentsMargins(5, 0, 5, 0) top_bar.addWidget(self._name_label) top_bar.addStretch() top_bar.addWidget(self._round_label) splitter = QtWidgets.QSplitter(Qt.Horizontal) participants_view = QtWidgets.QWidget() participants_layout = QtWidgets.QVBoxLayout(participants_view) participants_layout.setContentsMargins(0, 0, 0, 0) participants_control_bar_layout = QtWidgets.QHBoxLayout() participants_control_bar_layout.setContentsMargins(0, 0, 0, 0) participants_control_bar_layout.addWidget(self._open_button) participants_control_bar_layout.addWidget(self._open_all_button) participants_layout.addWidget(self._participants_view) participants_layout.addLayout(participants_control_bar_layout) splitter.addWidget(participants_view) splitter.addWidget(self._preview_stack) layout.addLayout(top_bar) layout.addWidget(splitter)
def load_editable(serialized: t.Mapping[str, t.Any], undo_stack: t.Optional[QUndoStack] = None) -> Editable: return (PoolView if serialized['tab_type'] == TabType.POOL else DeckView).load(serialized, undo_stack or Context.get_undo_stack())
def open_file(self, path: str, target: t.Type[TabModel] = Deck) -> None: for editable, meta in self._metas.items(): if meta.path == path: self.setCurrentWidget(editable) return file_name = os.path.split(path)[1] name, extension = os.path.splitext(file_name) extension = extension[1:] meta = TabMeta(file_name, path) editable = None if extension.lower() == 'embd': with open(path, 'rb') as f: try: tab = self.load_file(pickle.load(f), meta) except UnpicklingError: raise FileOpenException('corrupt file') elif extension.lower() == 'embp': with open(path, 'rb') as f: try: tab = self.load_file(pickle.load(f), meta) except UnpicklingError: raise FileOpenException('corrupt file') else: with open(path, 'r') as f: try: serializer: TabModelSerializer[t.Union[ Deck, Pool]] = TabModelSerializer.extension_to_serializer[( extension, target)] except KeyError: raise FileOpenException( 'unsupported file type "{}"'.format(extension)) try: tab_model = serializer.deserialize(f.read()) except SerializationException: raise FileOpenException() if target == Deck: editable = DeckView( DeckModel( list( map(PhysicalCard.from_cubeable, tab_model.maindeck)), list( map(PhysicalCard.from_cubeable, tab_model.sideboard)), ), Context.get_undo_stack(), ) elif target == Pool: editable = PoolView( PoolModel(list(map(PhysicalCard.from_cubeable, tab_model)), ), Context.get_undo_stack(), ) else: raise FileOpenException( 'invalid load target "{}"'.format(target)) tab = self.add_editable(editable, meta) self.setCurrentWidget(tab) if editable is not None: self._sort_opened_view(editable) if not Context.main_window.isActiveWindow() and Context.settings.value( 'focus_on_open_file', True, bool): Context.main_window.raise_() Context.main_window.show() Context.main_window.activateWindow()
def new_deck(self, model: DeckModel) -> Tab: return self.add_editable( DeckView(model, Context.get_undo_stack()), TabMeta('untitled deck'), )
def run(): sys.excepthook = _get_exception_hook() arg_parser = argparse.ArgumentParser(description = 'Edit decks') arg_parser.add_argument( '-v', '--version', action = 'store_true', help = 'show version', ) arg_parser.add_argument( '-m', '--multi-instance', action = 'store_true', help = 'allow running in parallel with other instances of Embargo Edit', ) arg_parser.add_argument( '-d', '--debug', action = 'store_true', help = 'debug mode', ) arg_parser.add_argument( '-l', '--log-level', action = 'store', help = 'logging level', default = 'debug', choices = values.LOGGING_LEVEL_MAP.keys(), ) arg_parser.add_argument( '--echo-sql', action = 'store_true', help = 'Echo sql queries', ) arg_parser.add_argument( '-n', '--no-server', action = 'store_true', help = 'dont start server', ) arg_parser.add_argument( '--no-ssl-verify', action = 'store_true', help = 'disable ssl certificate verification for remote connections.', ) arg_parser.add_argument( '--db-type', type = str, nargs = '?', choices = ['sql', 'pickle', 'default'], default = 'default', help = 'what type of server to use. "default" means use the one defined in application settings', ) arg_parser.add_argument( '--port', metavar = 'P', type = int, nargs = '?', default = 7777, help = 'server port', ) arg_parser.add_argument( '--host', metavar = 'H', type = str, nargs = '?', default = 'localhost', help = 'server host', ) arg_parser.add_argument('files', metavar = 'F', type = str, nargs = '*', help = 'paths of files to open') args = arg_parser.parse_args() for k, v in logging.Logger.manager.loggerDict.items(): if isinstance(v, logging.Logger): v.handlers[:] = [] logging.basicConfig( format = '%(levelname)s %(message)s', level = values.LOGGING_LEVEL_MAP[args.log_level], stream = sys.stdout, ) logging.getLogger('PIL.PngImagePlugin').addFilter(FilterAll()) if args.version: print(version_formatted()) return if not args.multi_instance: client = EmbargoClient(host = args.host, port = args.port) if client.check(): logging.info('instance already running') if args.files: for file in args.files: client.open_file(os.path.abspath(file)) return init_aligners() app = EmbargoApp.init(sys.argv) app.setQuitOnLastWindowClosed(True) compiled = __file__ == os.path.split(__file__)[-1] db_type = DbType(args.db_type) if args.no_ssl_verify: logging.warning('Running without ssl verification!') requests.packages.urllib3.disable_warnings() init_args = { 'compiled': compiled, 'debug': args.debug, 'db_type': db_type, 'echo_sql': args.echo_sql, 'no_ssl_verify': args.no_ssl_verify, } try: Context.init(app, **init_args) except Exception: if not DBUpdateDialog().exec_() == QDialog.Accepted: return Context.init(app, **init_args) EDB.init(echo = args.echo_sql) models.create(EDB.engine) init_deck_serializers() main_window = MainWindow() Context.main_window = main_window sys.excepthook = _get_exception_hook(main_window) if not args.no_server: Context.embargo_server = EmbargoServer(host = args.host, port = args.port) Context.embargo_server.start() main_window.showMaximized() if not args.multi_instance: save_state_timer = QtCore.QTimer() save_state_timer.timeout.connect(main_window.save_state) save_state_timer.start(1000 * 60 * 3) if settings.AUTO_LOGIN.get_value(): LOGIN_CONTROLLER.re_login() if args.files: for path in args.files: Context.open_file.emit(path) sys.exit(app.exec_())