def wifi_connect_done(client: NM.Client, res: Gio.AsyncResult, button: Gtk.Button): created = client.add_connection_finish(res) logger.debug('NetworkManager created connection: {}', created) if created: button.set_label(_('Saved')) button.set_sensitive(False)
def on_keyfile_select_button_clicked(self, button: Gtk.Button) -> None: self.unlocked_database.start_database_lock_timer() # We reset the button if we previously failed. if button.props.icon_name == "edit-delete-symbolic": button.props.icon_name = "document-open-symbolic" button.remove_css_class("destructive-action") self.current_keyfile_path = None self.current_keyfile_hash = None return select_dialog = Gtk.FileChooserNative.new( # NOTE: Filechooser title for choosing current used keyfile _("Select Current Keyfile"), self, Gtk.FileChooserAction.OPEN, None, None, ) select_dialog.set_modal(True) ffilter = KeyFileFilter().file_filter select_dialog.add_filter(ffilter) select_dialog.connect("response", self._on_select_filechooser_response, select_dialog) select_dialog.show()
def _setButtons(self) -> None: """ Sets up the cancel and generate buttons. :return: """ box: Box = Box() self._main_box.pack_end(box, False, True, 0) box.set_halign(Align.CENTER) box.set_orientation(Orientation.HORIZONTAL) setMargin(box, 5) self._cancel_btn = Button(label='Cancel') box.pack_start(self._cancel_btn, False, True, 0) setMargin(self._cancel_btn, 5, 5, 100, 5) self._cancel_btn.connect('clicked', self._onCancelBtnClicked) self._generate_btn = Button(label='Generate') box.pack_end(self._generate_btn, False, True, 0) setMargin(self._generate_btn, 100, 5, 5, 5) self._generate_btn.connect('clicked', self._onGenerateBtnClicked) timeout_add(300, self._setSensitiveGenerateBtn)
def on_btn_copy_clicked(self, button: Gtk.Button): clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) begin = self.raw_result_buffer.get_start_iter() end = self.raw_result_buffer.get_end_iter() self.raw_result_buffer.select_range(begin, end) self.raw_result_buffer.copy_clipboard(clipboard) button.set_tooltip_text(_('Copied')) GLib.timeout_add_seconds(3, remove_tooltip, button)
def usb_entry_check(self: Gtk.Entry, button: Gtk.Button): usbpattern = re.compile(r'^[a-f\d]{4}:[a-f\d]{4}$') if usbpattern.match(self.get_text()): self.set_name('validEntry') button.set_sensitive(True) else: self.set_name('invalidEntry') button.set_sensitive(False)
def set_button_string(button: Gtk.Button, text: str) -> None: """ Sets the displayed text of a button widget :param button: the button widget to be modified :param text: the text to be displayed :return: void """ button.set_label(text)
def on_execute(self, button: Gtk.Button): if button.get_label() == 'Execute': pinholesizes = [float(x[0]) for x in self.builder.get_object('pinhole_store') if x[0]] spacers = [float(x[0]) for x in self.builder.get_object('spacers_store') if x[0]] self.instrument.config['gui']['optimizegeometry']['pinholes'] = pinholesizes self.instrument.config['gui']['optimizegeometry']['spacers'] = spacers mindist_l1 = self.builder.get_object('l1baselength_adjustment').get_value() mindist_l2 = self.builder.get_object('l2baselength_adjustment').get_value() wavelength = self.builder.get_object('wavelength_adjustment').get_value() sealringwidth = self.builder.get_object('sealingringwidth_adjustment').get_value() sd = self.builder.get_object('distance_sd_adjustment').get_value() lbs = self.builder.get_object('distance_dbs_adjustment').get_value() ls = self.builder.get_object('distance_ph3s_adjustment').get_value() self.limits_samplesize = ( self.builder.get_object('diameter_sample_min_adjustment').get_value(), self.builder.get_object('diameter_sample_max_adjustment').get_value() ) self.limits_beamstopsize = ( self.builder.get_object('diameter_beamstop_min_adjustment').get_value(), self.builder.get_object('diameter_beamstop_max_adjustment').get_value() ) self.limit_l1min = self.builder.get_object('min_l1_adjustment').get_value() self.limit_l2min = self.builder.get_object('min_l2_adjustment').get_value() self.pinholegenerator = PinholeConfiguration.enumerate( spacers, pinholesizes, ls, lbs, sd, mindist_l1, mindist_l2, sealringwidth, wavelength) self.builder.get_object('results_store').clear() button.set_label('Stop') button.get_image().set_from_icon_name('gtk-stop', Gtk.IconSize.BUTTON) self.set_sensitive(False, 'Filtering possible pinhole configurations', ['inputframe']) self.builder.get_object('work_progress').show() self.builder.get_object('work_progress').set_fraction(0) self.workdone = 0 self.worksize = self._executor.submit(estimate_worksize_C, spacers, pinholesizes, sealringwidth) state = {'pinholesizes': pinholesizes, 'spacers': spacers, 'mindist_l1': mindist_l1, 'mindist_l2': mindist_l2, 'wavelength': wavelength, 'sealringwidth': sealringwidth, 'sd': sd, 'lbs': lbs, 'ls': ls, 'limits_samplesize': self.limits_samplesize, 'limits_beamstopsize': self.limits_beamstopsize, 'limit_l1min': self.limit_l1min, 'limit_l2min': self.limit_l2min } self.instrument.config['gui']['optimizegeometry'].update(state) self._idle_handler = GLib.idle_add(self._idle_func) else: self.end_work()
def on_command_execute(self, button: Gtk.Button): if button.get_label() == 'Execute': cmd = self.builder.get_object('command_entry').get_text() try: self.instrument.services['interpreter'].execute_command(cmd) except CommandError as ce: error_message(self.widget, 'Cannot execute command', str(ce)) else: button.set_label('Stop') if (not self._commandhistory) or (self._commandhistory and self._commandhistory[-1] != cmd): self._commandhistory.append(self.builder.get_object('command_entry').get_text()) elif button.get_label() == 'Stop': self.instrument.services['interpreter'].kill() else: raise ValueError(button.get_label())
def __init__(self, prop, *args, **kwargs): Button.__init__(self, *args, **kwargs) # Styling context = self.get_style_context() provider = CssProvider() resource = 'ml/prevete/Daty/gtk/property.css' provider.load_from_resource(resource) context.add_provider(provider, STYLE_PROVIDER_PRIORITY_APPLICATION) context = self.description.get_style_context() set_style(context, resource, 'popover_description', True) self.set_label(prop["Label"], prop["Description"]) self.description.set_text(prop['Description']) self.description.set_line_wrap(True)
def sousFenêtreFichier(self, bouton: Gtk.Button, numFichier: int) -> None: """ Callback utilisé pour lancer une fenêtre sélecteur de fichier. Modifie le texte du bouton une fois le fichier sélectionné. """ dialogue = Gtk.FileChooserDialog( "Choisissez un fichier", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) réponse = dialogue.run() if réponse == Gtk.ResponseType.OK: self.fichiers[numFichier] = dialogue.get_filename() dialogue.destroy() nomFichier = self.fichiers[numFichier].split("/")[-1] bouton.set_label(nomFichier)
def on_console_button_toggled(self, button: Gtk.Button) -> None: if button.get_active(): console = ctypes.windll.kernel32.GetConsoleWindow() ctypes.windll.user32.ShowWindow(console, 1) ctypes.windll.kernel32.CloseHandle(console) else: console = ctypes.windll.kernel32.GetConsoleWindow() ctypes.windll.user32.ShowWindow(console, 0) ctypes.windll.kernel32.CloseHandle(console)
def on_show_close_button_toggled(self, button: Gtk.Button) -> None: current_value = button.get_active() if current_value: self.parent_window.set_deletable(True) else: self.parent_window.set_deletable(False) config.new('general', 'show_close_button', current_value)
def on_power_clicked(self, button: Gtk.Button) -> None: serial_message = 'fl' status = str(int(button.get_active())) if self.fan_index == -1: config.new("front", "power_status", status * 3) else: config.set_to_index("front", "power_status", status, self.fan_index) serial_message += config.parser.get("front", "power_status") self.application.send_serial(serial_message)
def on_yes_button_clicked(self, button: Gtk.Button) -> None: button.hide() self.no_button.hide() with contextlib.suppress(AttributeError): self.give_label.hide() self.give_tree.hide() self.arrow.hide() self.receive_tree.hide() self.set_size_request(0, 0) self.header_bar.set_show_close_button(False) self.parent_window.text_tree_lock = True task: asyncio.Future[Union[Dict[str, Any], List[Tuple[Any, Dict[str, Any]]]]] if self.iter: task = asyncio.ensure_future(self.finalize()) else: task = asyncio.ensure_future(self.batch_finalize()) task.add_done_callback(self.on_task_finish)
def __init__(self, *args, image=None, tooltip=None, callback=None, cb_args={}, **kwargs): Button.__init__(self, *args, **kwargs) # Styling context = self.get_style_context() provider = CssProvider() provider.load_from_resource('/ml/prevete/Daty/gtk/roundedbutton.css') context.add_provider(provider, STYLE_PROVIDER_PRIORITY_APPLICATION) if callback: self.connect("clicked", callback, *cb_args) if image: self.image.set_from_icon_name(image, IconSize.BUTTON) if tooltip: self.set_tooltip_text(tooltip)
def _destination_file_chooser_response_cb( self, native: Gtk.FileChooserNative, response: int, button: Gtk.Button, ): if response == Gtk.ResponseType.ACCEPT: file: Gio.File = native.get_file() filename: str = file.get_path() if (filename and os.path.isdir(filename) and os.access(filename, os.W_OK)): label: Gtk.Label = button.get_child() label.set_text(filename) self._destination = filename else: self._destination = None self._update_buttons() native.destroy()
def on_layout_btn_clicked(button: Gtk.Button, *args): """ @brief Change desktop layout on layout button click. @details In the layout regarding button must be named after the target layout name int LAYOUT_COMMANDS dictionary. @param button Gtk.Button button widget that recieved clicked event. @param args place holder list @return None """ name: str = button.get_name() print("Layout Name: ", name) commands = LAYOUT_COMMANDS[name] for cmd in commands: cmd.execute()
def _on_start(self, widget: Gtk.Button, event: Gdk.EventButton) -> bool: if self.going: return True self.going = True self.spin.set_range(self.iterc, self.iterc) self.progr1.set_opacity(1) self.progr2.set_opacity(1) self.set_progress(0, True) self.set_progress(0, False) timings = TIMINGS[widget.get_label()] cache = {timing[0]: [] for timing in timings} iterc = self.iterc for i in range(1, iterc + 1): for name, val in zip(*self.measure(timings)): cache[name].append(val) self.set_bars(cache) self.set_progress(i / iterc, True) self.progr1.set_opacity(0) self.progr2.set_opacity(0) self.going = False self.spin.set_range(1, 2**16) return True
async def on_add_authenticator_clicked(self, button: Gtk.Button) -> None: self.status.info(_("Retrieving user data")) button.set_sensitive(False) self.user_details_section.hide() self.set_size_request(0, 0) if not self.oauth_token or not self.steamid: self.status.error(_( "Some login data is missing. If the problem persists, go to:\n" "Settings -> Login -> Advanced -> and click on RESET Everything." )) return deviceid = universe.generate_device_id(token=self.oauth_token) oauth = {'steamid': self.steamid, 'oauth_token': self.oauth_token} login_data = login.LoginData(auth={}, oauth=oauth) if not self._login_data or not self.sms_code: try: self._login_data = await self.application.webapi_session.add_authenticator(login_data, deviceid) except aiohttp.ClientError: self.status.error(_("Check your connection. (server down?)")) except webapi.AuthenticatorExists: self.status.error(_( "There's already an authenticator active for that account.\n" "Remove your current steam authenticator and try again." )) except webapi.PhoneNotRegistered: self.status.error(_( "You must have a phone registered on your steam account to proceed.\n" "Go to your Steam Account Settings, add a Phone Number, and try again." )) except NotImplementedError as exception: import sys, traceback utils.fatal_error_dialog(exception, traceback.print_tb(sys.exc_info), self.parent_window) self.application.on_exit_activate() else: self.status.info(_("Enter bellow the code received by SMS\nand click on 'Add Authenticator' button")) self.user_details_section.show_all() self.sms_code_item.set_text('') self.sms_code_item.grab_focus() finally: button.set_sensitive(True) return self.status.info(_("Adding authenticator")) try: await self.application.webapi_session.finalize_add_authenticator( self._login_data, self.sms_code, time_offset=self.application.time_offset, ) except webapi.SMSCodeError: self.status.info(_("Invalid SMS Code. Please,\ncheck the code and try again.")) self.user_details_section.show_all() self.sms_code_item.set_text('') self.sms_code_item.grab_focus() except aiohttp.ClientError: self.status.error(_("Check your connection. (server down?)")) self.user_details_section.show_all() self.sms_code_item.set_text('') self.sms_code_item.grab_focus() except Exception as exception: import sys, traceback utils.fatal_error_dialog(exception, traceback.print_tb(sys.exc_info), self.parent_window) self.application.on_exit_activate() else: self.status.info(_("Saving new secrets")) config.new("login", "shared_secret", self._login_data.auth['shared_secret']) config.new("login", "identity_secret", self._login_data.auth['identity_secret']) config.new("plugins", "steamguard", True) config.new("plguins", "confirmations", True) self.status.info(_( "RECOVERY CODE\n\n" "You will need this code to recovery your Steam Account\n" "if you lose access to STNG Authenticator. So, write" "down this recovery code.\n\n" "YOU WILL NOT ABLE TO VIEW IT AGAIN!\n" )) revocation_code = self._login_data.auth['revocation_code'] self.add_authenticator_button.hide() revocation_status = utils.Status(6, _("Recovery Code")) revocation_status.set_pausable(False) revocation_status.set_display(revocation_code) revocation_status.set_status('') self.content_area.add(revocation_status) revocation_status.show_all() self.set_deletable(False) max_value = 30 * 3 for offset in range(max_value): revocation_status.set_level(offset, max_value) await asyncio.sleep(0.3) self.set_deletable(True) finally: button.set_sensitive(True)
def _setButtonsSignals(self) -> None: """ Set up the buttons and their respective signals for both grids. :param win: A window. :param fst_grid: The first grid container where the buttons goes in. :param box: The box container where the first grid goes in. :param fc_s: A list with file choosers. :return: """ del_img: Optional[Pixbuf] img_path: str = path.abspath('asts/Icons/delete.png') try: del_img = Pixbuf().new_from_file_at_scale(img_path, 20, 20, False) except GLib_Error: exit(f'{img_path} file not found. Failed to create pixbuf.') icons: List[Optional[Image]] = [ Image().new_from_pixbuf(del_img) for _ in range(4) ] btns: List[Button] = [Button() for _ in range(4)] for (idx, btn) in enumerate(btns): btn.set_image(icons[idx]) setMargin(btn, 0, 5, 0, 5) btn.set_halign(Align.END) self._fst_grid.attach(btn, 2, idx, 1, 1) btns[FileType.ANKI2_COLLECTION].connect( 'clicked', lambda _: self._fc_s[FileType.ANKI2_COLLECTION].unselect_all()) btns[FileType.VIDEO].connect( 'clicked', lambda _: self._fc_s[FileType.VIDEO].unselect_all()) btns[FileType.SUBTITLE].connect( 'clicked', lambda _: self._fc_s[FileType.SUBTITLE].unselect_all()) btns[FileType.O_SUBTITLE].connect( 'clicked', lambda _: self._fc_s[FileType.O_SUBTITLE].unselect_all()) box: Box = Box() self._box.pack_end(box, False, True, 0) box.set_orientation(Orientation.HORIZONTAL) box.set_halign(Align.CENTER) cancel_btn: Button = Button(label='Cancel') box.pack_start(cancel_btn, False, True, 0) cancel_btn.set_margin_bottom(10) cancel_btn.connect('clicked', lambda _: self.close()) self._next_btn = Button(label='Next') box.pack_end(self._next_btn, False, True, 0) setMargin(self._next_btn, 200, 10, 0, 0) self._next_btn.connect('clicked', self._onNextBtnClicked) timeout_add(300, self._setSensitiveNextBtn) timeout_add(300, self._checkInvalidSelection)
class FilesChooser(Window): def __init__(self, app: Application): super().__init__(title='Asts', application=app) self._app: Application = app self.set_default_size(1000, 700) self.set_keep_above(True) self.set_resizable(False) self._box: Box = Box() self._box.set_orientation(Orientation.VERTICAL) setBgColor(widget=self, alpha=0.93) setMargin(self._box, 10) self._fst_grid: Grid = Grid() # labels self._setLabels() # file choosers button # _fc_s[0] = anki2.collection file # _fc_s[1] = video file # _fc_s[2] = subtitle file # _fc_s[3] = optional subtitle file self._fc_s: List[FileChooser] = self._setFileChoosers() # text entry self._entry: Entry self._setTextEntry() self._fillCachedFile() # filters self._setFilters() # buttons self._next_btn: Button self._setButtonsSignals() # box.pack_(child, expand, fill, padding) self._box.pack_start(self._fst_grid, False, True, 0) self.add(self._box) def _setLabels(self) -> None: """ Setup the labels for the grid. :return: """ labels: List[Label] = [ Label(label='collection.anki2 File (Required):'), Label(label='Video (Required):'), Label(label='Subtitle File with The Target Language (Required):'), Label(label='Subtitle File with Translation (Optional):'), Label(label='Deck Name (Required):') ] for (idx, lbl) in enumerate(labels): lbl.set_halign(Align.START) # Grid.attach(child, left, top, width, height) self._fst_grid.attach(lbl, 0, idx, 1, 1) def _setFileChoosers(self) -> List[FileChooser]: """ Set up the file choosers for the grid. :return: The file choosers created. """ f_cs: List[FileChooser] = [FileChooserButton() for _ in range(4)] for (idx, f_c) in enumerate(f_cs): f_c.set_hexpand(True) setMargin(f_c, 10, 5, 10, 5) f_c.set_halign(Align.FILL) self._fst_grid.attach(f_c, 1, idx, 1, 1) return f_cs def _setFilters(self) -> None: """ Set a filter for each file chooser. :return: """ ff1: FileFilter = FileFilter() ff1.set_name('collection.anki2') ff1.add_pattern('*.anki2') self._fc_s[FileType.ANKI2_COLLECTION].add_filter(ff1) ff2: FileFilter = FileFilter() ff2.set_name('Video File') ff2.add_mime_type('video/mp4') ff2.add_mime_type('video/wmv') ff2.add_mime_type('video/avi') ff2.add_mime_type('video/mkv') ff2.add_mime_type('video/webm') ff2.add_pattern('*.mp4') ff2.add_pattern('*.wmv') ff2.add_pattern('*.avi') ff2.add_pattern('*.mkv') ff2.add_pattern('*.webm') self._fc_s[FileType.VIDEO].add_filter(ff2) ff3: FileFilter = FileFilter() ff3.set_name('Subtitles (ASS/SRT)') ff3.add_pattern('*.srt') ff3.add_pattern('*.ass') self._fc_s[FileType.SUBTITLE].add_filter(ff3) self._fc_s[FileType.O_SUBTITLE].add_filter(ff3) def _fillCachedFile(self) -> None: """ Fills up collection_file and deck_name filename. :return: """ try: cache_dir: Filepath = path.abspath('data/cache') cached_usage: Filename = path.join(cache_dir + '/' + 'cached_usage.txt') with open(cached_usage, 'r') as f: list_cache: List[str] = f.read().split('\n') self._fc_s[FileType.ANKI2_COLLECTION].set_filename( list_cache[0]) self._entry.set_text(list_cache[1]) # it's safe to pass here # it means that there's no filename cached to be used except FileNotFoundError: pass def _setButtonsSignals(self) -> None: """ Set up the buttons and their respective signals for both grids. :param win: A window. :param fst_grid: The first grid container where the buttons goes in. :param box: The box container where the first grid goes in. :param fc_s: A list with file choosers. :return: """ del_img: Optional[Pixbuf] img_path: str = path.abspath('asts/Icons/delete.png') try: del_img = Pixbuf().new_from_file_at_scale(img_path, 20, 20, False) except GLib_Error: exit(f'{img_path} file not found. Failed to create pixbuf.') icons: List[Optional[Image]] = [ Image().new_from_pixbuf(del_img) for _ in range(4) ] btns: List[Button] = [Button() for _ in range(4)] for (idx, btn) in enumerate(btns): btn.set_image(icons[idx]) setMargin(btn, 0, 5, 0, 5) btn.set_halign(Align.END) self._fst_grid.attach(btn, 2, idx, 1, 1) btns[FileType.ANKI2_COLLECTION].connect( 'clicked', lambda _: self._fc_s[FileType.ANKI2_COLLECTION].unselect_all()) btns[FileType.VIDEO].connect( 'clicked', lambda _: self._fc_s[FileType.VIDEO].unselect_all()) btns[FileType.SUBTITLE].connect( 'clicked', lambda _: self._fc_s[FileType.SUBTITLE].unselect_all()) btns[FileType.O_SUBTITLE].connect( 'clicked', lambda _: self._fc_s[FileType.O_SUBTITLE].unselect_all()) box: Box = Box() self._box.pack_end(box, False, True, 0) box.set_orientation(Orientation.HORIZONTAL) box.set_halign(Align.CENTER) cancel_btn: Button = Button(label='Cancel') box.pack_start(cancel_btn, False, True, 0) cancel_btn.set_margin_bottom(10) cancel_btn.connect('clicked', lambda _: self.close()) self._next_btn = Button(label='Next') box.pack_end(self._next_btn, False, True, 0) setMargin(self._next_btn, 200, 10, 0, 0) self._next_btn.connect('clicked', self._onNextBtnClicked) timeout_add(300, self._setSensitiveNextBtn) timeout_add(300, self._checkInvalidSelection) def _setTextEntry(self) -> None: """ Sets the text entry for deck name. :return: """ self._entry = Entry(placeholder_text='Deck name...') setMargin(self._entry, 10, 5, 10, 5) self._fst_grid.attach(self._entry, 1, 4, 1, 1) def _getFilename(self, f_type: FileType) -> Optional[str]: """ Return the name of the file. :param f_type: The type of file. :return: The name of the file. """ try: return self._fc_s[f_type].get_filename() except IndexError: return None def _getDeckName(self) -> str: """ Gets the deck name. :return: The deck name. """ return self._entry.get_text() def _checkInvalidSelection(self) -> bool: """ Unselects any invalid filename selected through file choosers. :return: True to keep timeout_add running. """ col: Optional[str] = self._getFilename(FileType.ANKI2_COLLECTION) vid: Optional[str] = self._getFilename(FileType.VIDEO) sub: Optional[str] = self._getFilename(FileType.SUBTITLE) o_sub: Optional[str] = self._getFilename(FileType.O_SUBTITLE) if col != None and not isCollection(col): self._fc_s[0].unselect_all() if vid != None and not isVideo(vid): self._fc_s[1].unselect_all() if sub != None and not isSub(sub): self._fc_s[2].unselect_all() if o_sub != None and not isSub(o_sub): self._fc_s[3].unselect_all() return True def _setSensitiveNextBtn(self) -> bool: """ Set if a button is clickable or not. :return: True to keep timeout_add running. """ col: bool = isCollection(self._getFilename(FileType.ANKI2_COLLECTION)) vid: bool = isVideo(self._getFilename(FileType.VIDEO)) sub: bool = isSub(self._getFilename(FileType.SUBTITLE)) deck_name: str = self._getDeckName() if all((col, vid, sub, deck_name)): self._next_btn.set_sensitive(True) else: self._next_btn.set_sensitive(False) return True def _onNextBtnClicked(self, _) -> None: """ Opens the generator of anki cards. :return: """ createCacheDirIfItNotExists() clearCachedFiles() recentUsedFiles(self._getFilename(FileType.ANKI2_COLLECTION), self._getDeckName()) CardsGenerator(self, self._app, self._getFilename(FileType.ANKI2_COLLECTION), self._getFilename(FileType.VIDEO), self._getFilename(FileType.SUBTITLE), self._getFilename(FileType.O_SUBTITLE), self._getDeckName()).showAll()
def remove_tooltip(button: Gtk.Button): button.set_has_tooltip(False) return False
def on_save_clicked(self, button: Gtk.Button) -> None: serial_message = 'ms' self.application.send_serial(serial_message) button.set_label("Saved!")
def on_advanced_button_toggled(self, button: Gtk.Button) -> None: if button.get_active(): advanced_settings = advanced.AdvancedSettingsDialog( self, self.application, button) advanced_settings.show_all()
class CardsGenerator(Window): def __init__(self, parent: Window, app: Application, col_filename: Filename, vid_filename: Filename, sub_filename: Filename, opt_sub_filename: OptFilename, deck_name: str): super().__init__(title='Asts - Anki Card Generator', application=app, transient_for=parent) self.set_default_size(width=1000, height=700) self.set_keep_above(True) self.set_modal(True) self.set_resizable(False) setBgColor(widget=self, alpha=0.93) self._main_box: Box = Box() self._main_box.set_orientation(Orientation.VERTICAL) setMargin(self._main_box, 10) self.add(self._main_box) self._subtitles_grid: Grid = Grid() setMargin(self._subtitles_grid, 5) # box.pack_(expand, fill, padding) self._main_box.pack_start(self._subtitles_grid, False, True, 0) self._collection_filename: Filename = col_filename self._video_filename: Filename = vid_filename self._subtitles_filename: Filename = sub_filename self._opt_subtitles_filename: OptFilename = opt_sub_filename self._deck_name: str = deck_name self._any_media_toggled: bool = False self._dict_any_media: Dict[str, bool] self._dict_any_change_front: Dict[str, bytes] self._dict_any_change_back: Dict[str, bytes] self._textview_front: TextView self._textview_back: TextView self._textbuffer_front: TextBuffer self._textbuffer_back: TextBuffer self._subtitles_liststore: ListStore self._subtitles_liststore_back: ListStore self._subtitles_treeview: TreeView self._selected_row: TreeSelection self._progress_bar: ProgressBar self._cancel_btn: Button self._generate_btn: Button self._cur_progress: int self._max_tasks: int self._cancel_task: bool self._list_of_sentences: ListSentences self._list_info_medias: List[List[Info]] self._color_tag_names: List[str] # TheadingHandler will utilize these # updating the status for each task tasks # also the sensitive and progress of the progress bar # depends on these. self._futures_list: List[Future] def showAll(self) -> None: """ Draws the cards generator window and it's respective widgets. :return: """ # subtitles tree view self._setSubtitleTreeView() # indice and dialogue cells self._setDialogCells() # start and end timer cells self._setTimerCells() # video, audio and image cells self._setMediasCells() # fills both tree view with the subtitles self._populateListStore() # setting the model after and initializing _selected_row and _dict_any_media # after the liststore being complete initialized self._subtitles_treeview.set_model(self._subtitles_liststore) self._selected_row = self._subtitles_treeview.get_selection() self._selected_row.connect('changed', self._itemSelected) self._dict_any_media = { str(key): False for key in enumerate(self._subtitles_liststore) } # search entry self._setSearchEntry() # sets up the sentence editing related (e.g toolbar, tagging, etc) self._setSentenceRelated() # all color tags are named as it's respective values self._color_tag_names = [ '#9999c1c1f1f1', '#6262a0a0eaea', '#35358484e4e4', '#1c1c7171d8d8', '#1a1a5f5fb4b4', '#8f8ff0f0a4a4', '#5757e3e38989', '#3333d1d17a7a', '#2e2ec2c27e7e', '#2626a2a26969', '#f9f9f0f06b6b', '#f8f8e4e45c5c', '#f6f6d3d32d2d', '#f5f5c2c21111', '#e5e5a5a50a0a', '#ffffbebe6f6f', '#ffffa3a34848', '#ffff78780000', '#e6e661610000', '#c6c646460000', '#f6f661615151', '#eded33333b3b', '#e0e01b1b2424', '#c0c01c1c2828', '#a5a51d1d2d2d', '#dcdc8a8adddd', '#c0c06161cbcb', '#91914141acac', '#81813d3d9c9c', '#616135358383', '#cdcdabab8f8f', '#b5b583835a5a', '#98986a6a4444', '#86865e5e3c3c', '#636345452c2c', '#ffffffffffff', '#f6f6f5f5f4f4', '#dededddddada', '#c0c0bfbfbcbc', '#9a9a99999696', '#777776767b7b', '#5e5e5c5c6464', '#3d3d38384646', '#24241f1f3131', '#000000000000', ] # sets up dictionary used to track the tags used self._initDictionariesTag() # sets up the buttons to select all sentences self._setSelectAll() # sets up the progress bar self._setProgressBar() # cancel and generate buttonsj self._resetFuturesLists() self._setButtons() self.show_all() def _resetFuturesLists(self) -> None: """ Assign a empty list of both lists of futures (futures_setences and futures_medias). :return: """ self._futures_list = [] def _setSearchEntry(self) -> None: """ Connect the changed event for the search_entry object. :return: """ search_entry: SearchEntry = SearchEntry() search_entry.set_halign(Align.END) setMargin(search_entry, 0, 5, 0, 5) self._subtitles_grid.attach(search_entry, 0, 0, 1, 1) search_entry.connect('changed', self.searchIt) def searchIt(self, search_entry: SearchEntry) -> None: """ Searchs over the _subtitles_liststore. :return: """ term_searched: str = search_entry.get_text() for i, term in enumerate(self._subtitles_liststore): if term_searched and term_searched in term[1].lower(): self._subtitles_treeview.set_cursor(i) break def _setSelectAll(self) -> None: """ Sets up widgets to select all sentences. :return: """ grid: Grid = Grid() grid.set_halign(Align.END) self._subtitles_grid.attach(grid, 0, 2, 1, 1) lbl: Label = Label(label='Select all') setMargin(lbl, 5) grid.attach(lbl, 0, 0, 1, 1) all_vid_toggle: CheckButton = CheckButton() all_vid_toggle.set_halign(Align.CENTER) all_vid_toggle.connect('toggled', self._onAllVideosToggled) setMargin(all_vid_toggle, 5) grid.attach(all_vid_toggle, 1, 0, 1, 1) lbl2: Label = Label(label='Videos') setMargin(lbl2, 5) grid.attach(lbl2, 1, 1, 1, 1) all_audio_toggle: CheckButton = CheckButton() all_audio_toggle.set_halign(Align.CENTER) all_audio_toggle.connect('toggled', self._onAllAudiosToggled, all_vid_toggle) setMargin(all_audio_toggle, 5) grid.attach(all_audio_toggle, 2, 0, 1, 1) lbl3: Label = Label(label='Audios') setMargin(lbl3, 5) grid.attach(lbl3, 2, 1, 1, 1) all_img_toggle: CheckButton = CheckButton() all_img_toggle.set_halign(Align.CENTER) all_img_toggle.connect('toggled', self._onAllImagesToggled) setMargin(all_img_toggle, 5) grid.attach(all_img_toggle, 3, 0, 1, 1) lbl4: Label = Label(label='Snapshot') setMargin(lbl4, 5) grid.attach(lbl4, 3, 1, 1, 1) def _onAllVideosToggled(self, _) -> None: """ Handle the toggled event for the ToggleButton object. :param widget: ToggleButton object. :return: """ for i in range(len(self._subtitles_liststore)): if self._subtitles_liststore[i][5]: self._subtitles_liststore[i][ 5] = not self._subtitles_liststore[i][5] self._subtitles_liststore[i][ 4] = not self._subtitles_liststore[i][4] self._dict_any_media[str(i)] = self._subtitles_liststore[i][4] elif self._subtitles_liststore[i][6]: self._subtitles_liststore[i][ 6] = not self._subtitles_liststore[i][6] self._subtitles_liststore[i][ 4] = not self._subtitles_liststore[i][4] self._dict_any_media[str(i)] = self._subtitles_liststore[i][4] else: self._subtitles_liststore[i][ 4] = not self._subtitles_liststore[i][4] self._dict_any_media[str(i)] = self._subtitles_liststore[i][4] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _onAllAudiosToggled(self, _) -> None: """ Handle the toggled event for the ToggleButton object. :param widget: ToggleButton object. :return: """ for i in range(len(self._subtitles_liststore)): if self._subtitles_liststore[i][4]: self._subtitles_liststore[i][ 4] = not self._subtitles_liststore[i][4] self._subtitles_liststore[i][ 5] = not self._subtitles_liststore[i][5] self._dict_any_media[str(i)] = self._subtitles_liststore[i][5] elif self._subtitles_liststore[i][5] and self._subtitles_liststore[ i][6]: self._subtitles_liststore[i][ 5] = not self._subtitles_liststore[i][5] self._dict_any_media[str(i)] = self._subtitles_liststore[i][6] else: self._subtitles_liststore[i][ 5] = not self._subtitles_liststore[i][5] self._dict_any_media[str(i)] = self._subtitles_liststore[i][5] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _onAllImagesToggled(self, _) -> None: """ Handle the toggled event for the ToggleButton object. :param widget: ToggleButton object. :return: """ for i in range(len(self._subtitles_liststore)): if self._subtitles_liststore[i][4]: self._subtitles_liststore[i][ 4] = not self._subtitles_liststore[i][4] self._subtitles_liststore[i][ 6] = not self._subtitles_liststore[i][6] self._dict_any_media[str(i)] = self._subtitles_liststore[i][6] else: self._subtitles_liststore[i][ 6] = not self._subtitles_liststore[i][6] self._dict_any_media[str(i)] = self._subtitles_liststore[i][6] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _initDictionariesTag(self) -> None: """ Init the default values for the used tags. :return: """ # dictionaries to track the tags self._dict_any_change_front = ({ str(key): serializeIt(text_buffer=self._textbuffer_front, tmp_string=value[1]) for key, value in enumerate(self._subtitles_liststore) }) self._dict_any_change_back = ({ str(key): serializeIt(text_buffer=self._textbuffer_back, tmp_string=value[1]) for key, value in enumerate(self._subtitles_liststore_back) }) def _populateListStore(self) -> None: """ Fills both list store (front and back) with subtitles. :return: """ self._subtitles_liststore = ListStore( int, # indice str, # dialogue str, # start timer str, # end timer bool, # whether video is selected bool, # whether audio is selected bool # whether image is selected ) # only the first two values are important here self._subtitles_liststore_back = ListStore(int, str, str, str, bool, bool, bool) dialogues_list: List[List[Info]] = extractAllDialogues( self._subtitles_filename) for dialogue in dialogues_list: self._subtitles_liststore.append(dialogue) if self._opt_subtitles_filename: opt_dialogues_list: List[List[Info]] = extractAllDialogues( self._opt_subtitles_filename) # the subtitles and their respective translations # may or may not be of same lenght # in that case fill the list with dummy values for i in range(len(dialogues_list)): try: self._subtitles_liststore_back.append( opt_dialogues_list[i]) except IndexError: self._subtitles_liststore_back.append( (i, '', '', '', False, False, False)) else: # in case no subtitles was selected for the back list store # fill it with dummy values for i in range(len(dialogues_list)): self._subtitles_liststore_back.append( (i, '', '', '', False, False, False)) def _setTimerCells(self) -> None: """ Arrange the start and end timer cells. :return: """ # Making some cells editable 'Start' and 'End' respectivily editable_start_field: CellRendererText = CellRendererText() editable_end_field: CellRendererText = CellRendererText() editable_start_field.set_property('editable', True) editable_end_field.set_property('editable', True) self._subtitles_treeview.append_column( TreeViewColumn(title='Start', cell_renderer=editable_start_field, text=2)) self._subtitles_treeview.append_column( TreeViewColumn(title='End', cell_renderer=editable_end_field, text=3)) editable_start_field.connect('edited', self._startFieldEdited) editable_end_field.connect('edited', self._endFieldEdited) def _startFieldEdited(self, _, path: TreePath, text: str) -> None: """ Handle the edited event for the start timer field cell. :widget: CellRendererText object. :path: TreePath object. :text: A string to be assigned to subtitles_liststore. :return: """ from re import compile, Pattern regex_timer: Pattern[str] = compile( r'([0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9])') result = regex_timer.findall(text) if result: self._subtitles_liststore[path][2] = result[0] def _endFieldEdited(self, _, path: TreePath, text: str) -> None: """ Handle the edited event for the end timer field cell. :widget: CellRendererText object. :path: TreePath object. :text: A string to be assigned to subtitles_liststore. :return: """ from re import compile, Pattern regex_timer: Pattern[str] = compile( r'([0-9]?[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9])') result: List[str] = regex_timer.findall(text) if result: self._subtitles_liststore[path][3] = result[0] def _setDialogCells(self) -> None: """ Arrange the dialogue and indice cell at the treeview. :return: """ for i, title in enumerate(['Indice', 'Dialog']): renderer: CellRendererText = CellRendererText() path_column: TreeViewColumn = TreeViewColumn( title=title, cell_renderer=renderer, text=i) if title == 'Dialog': path_column.set_sizing(TreeViewColumnSizing.FIXED) path_column.set_fixed_width(520) path_column.set_min_width(520) self._subtitles_treeview.append_column(path_column) def _setMediasCells(self) -> None: """ Arrange the video, audio and snapshot cells. :return: """ # cell video, audio and snapshot to toggle renderer_video_toggle: CellRendererToggle = CellRendererToggle() column_toggle = TreeViewColumn(title='Video', cell_renderer=renderer_video_toggle, active=4) self._subtitles_treeview.append_column(column_toggle) renderer_video_toggle.connect("toggled", self._onCellVideoToggled) renderer_audio_toggle: CellRendererToggle = CellRendererToggle() column_toggle = TreeViewColumn(title='Audio', cell_renderer=renderer_audio_toggle, active=5) self._subtitles_treeview.append_column(column_toggle) renderer_audio_toggle.connect("toggled", self._onCellAudioToggled) renderer_snapshot_toggle: CellRendererToggle = CellRendererToggle() column_toggle = TreeViewColumn(title='Snapshot', cell_renderer=renderer_snapshot_toggle, active=6) self._subtitles_treeview.append_column(column_toggle) renderer_snapshot_toggle.connect("toggled", self._onCellImageToggled) def _onCellVideoToggled(self, _, path) -> None: """ Handles the toggled event for the CellRendererToggle object. :param widget: CellRendererToggle object. :path path: TreePath object. :return: """ if self._subtitles_liststore[path][5]: self._subtitles_liststore[path][ 5] = not self._subtitles_liststore[path][5] self._subtitles_liststore[path][ 4] = not self._subtitles_liststore[path][4] self._dict_any_media[path] = self._subtitles_liststore[path][4] elif self._subtitles_liststore[path][6]: self._subtitles_liststore[path][ 6] = not self._subtitles_liststore[path][6] self._subtitles_liststore[path][ 4] = not self._subtitles_liststore[path][4] self._dict_any_media[path] = self._subtitles_liststore[path][4] else: self._subtitles_liststore[path][ 4] = not self._subtitles_liststore[path][4] self._dict_any_media[path] = self._subtitles_liststore[path][4] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _onCellAudioToggled(self, _, path: str) -> None: """ Handles the toggled event for the CellRendererToggle object. :param widget: CellRendererToggle object. :path path: TreePath object. :return: """ if self._subtitles_liststore[path][4]: self._subtitles_liststore[path][ 4] = not self._subtitles_liststore[path][4] self._subtitles_liststore[path][ 5] = not self._subtitles_liststore[path][5] self._dict_any_media[path] = self._subtitles_liststore[path][5] elif self._subtitles_liststore[path][5] and self._subtitles_liststore[ path][6]: self._subtitles_liststore[path][ 5] = not self._subtitles_liststore[path][5] self._dict_any_media[path] = self._subtitles_liststore[path][6] else: self._subtitles_liststore[path][ 5] = not self._subtitles_liststore[path][5] self._dict_any_media[path] = self._subtitles_liststore[path][5] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _onCellImageToggled(self, _, path: str) -> None: """ Handles the toggled event for the CellRendererToggle object. :param widget: CellRendererToggle object. :path path: TreePath object. :return: """ if self._subtitles_liststore[path][4]: self._subtitles_liststore[path][ 4] = not self._subtitles_liststore[path][4] self._subtitles_liststore[path][ 6] = not self._subtitles_liststore[path][6] self._dict_any_media[path] = self._subtitles_liststore[path][6] elif self._subtitles_liststore[path][6] and self._subtitles_liststore[ path][5]: self._subtitles_liststore[path][ 6] = not self._subtitles_liststore[path][6] self._dict_any_media[path] = self._subtitles_liststore[path][5] else: self._subtitles_liststore[path][ 6] = not self._subtitles_liststore[path][6] self._dict_any_media[path] = self._subtitles_liststore[path][6] if True in self._dict_any_media.values(): self._any_media_toggled = True else: self._any_media_toggled = False def _setSubtitleTreeView(self) -> None: """ Sets the scrolled window and a tree view for subtitles info. :return: """ self._subtitles_treeview = TreeView() self._subtitles_treeview.set_grid_lines(TreeViewGridLines.BOTH) scrl_wnd: ScrolledWindow = ScrolledWindow() scrl_wnd.set_hexpand(True) scrl_wnd.set_vexpand(True) scrl_wnd.add(self._subtitles_treeview) self._subtitles_grid.attach(scrl_wnd, 0, 1, 1, 1) def _itemSelected(self, _) -> None: """ Keeps tracks of selections change at the treeview object. :return: """ path: str = self._selected_row.get_selected_rows()[1][0].to_string() deserializeIt(self._textbuffer_front, self._dict_any_change_front[path]) deserializeIt(self._textbuffer_back, self._dict_any_change_back[path]) self._textbuffer_front.connect('changed', self._editingCard) self._textbuffer_back.connect('changed', self._editingCardBack) def _editingCard(self, textbuffer_front: TextBuffer) -> None: """ Keeps track of changes at the text_buffer_front. :param text_buffer_front: TextBuffer object. :return: """ path: TreePath = self._selected_row.get_selected_rows()[1][0] start_iter_front: TextIter = textbuffer_front.get_start_iter() end_iter_front: TextIter = textbuffer_front.get_end_iter() self._subtitles_liststore[path][1] = textbuffer_front.get_text( start_iter_front, end_iter_front, True) self._dict_any_change_front[path.to_string()] = serializeIt( text_buffer=textbuffer_front) def _editingCardBack(self, textbuffer_back: TextBuffer) -> None: """ Keeps track of changes at the text_buffer_back. :param text_buffer_back: TextBuffer object. :return: """ path: TreePath = self._selected_row.get_selected_rows()[1][0] start_iter_back: TextIter = textbuffer_back.get_start_iter() end_iter_back: TextIter = textbuffer_back.get_end_iter() self._subtitles_liststore_back[path][1] = textbuffer_back.get_text( start_iter_back, end_iter_back, True) self._dict_any_change_back[path.to_string()] = serializeIt( text_buffer=textbuffer_back) def _setSentenceRelated(self) -> None: """ Sets up the sentence editing widgets related. Also initialize both text buffers. :return: """ box: Box = Box() self._main_box.pack_start(box, False, True, 0) box.set_orientation(Orientation.VERTICAL) setMargin(box, 5) toolbar: Toolbar = Toolbar() box.pack_start(toolbar, False, True, 0) toolbar.set_halign(Align.END) setMargin(toolbar, 5) lbl: Label = Label() lbl.set_markup('<i><b>Front</b></i>') box.pack_start(lbl, False, True, 0) lbl.set_halign(Align.START) setMargin(lbl, 5) scrl_wnd: ScrolledWindow = ScrolledWindow() scrl_wnd.set_hexpand(True) scrl_wnd.set_vexpand(True) textview: TextView = TextView() scrl_wnd.add(textview) box.pack_start(scrl_wnd, False, True, 0) self._textbuffer_front = textview.get_buffer() lbl2: Label = Label() lbl2.set_halign(Align.START) lbl2.set_markup('<i><b>Back</b></i>') box.pack_start(lbl2, False, True, 0) setMargin(lbl2, 5) scrl_wnd2: ScrolledWindow = ScrolledWindow() scrl_wnd2.set_hexpand(True) scrl_wnd2.set_vexpand(True) textview2: TextView = TextView() scrl_wnd2.add(textview2) box.pack_end(scrl_wnd2, False, True, 0) self._textbuffer_back = textview2.get_buffer() # this depends on the text buffer to be initialized self._setToolbarColorButton(toolbar) toolbar.insert(SeparatorToolItem(), 3) self._setToolbarUnderlineButton(toolbar) self._setToolbarBoldButton(toolbar) self._setToolbarItalicButton(toolbar) toolbar.insert(SeparatorToolItem(), 7) self._setToolbarTagRemoverButton(toolbar) def _setToolbarColorButton(self, toolbar: Toolbar) -> None: """ Sets up the color button from the toolbar. :param toolbar: Toolbar object :return: """ set_color_button: ToolButton = ToolButton() set_color_button.set_icon_name('gtk-select-color') toolbar.insert(set_color_button, 1) tool_item_color_button: ToolItem = ToolItem() color_button = ColorButton() tool_item_color_button.add(color_button) toolbar.insert(tool_item_color_button, 2) set_color_button.connect('clicked', self._onToolbarColorBtnClicked, color_button) def _setToolbarUnderlineButton(self, toolbar: Toolbar) -> None: """ Sets up the underline button from the toolbar. :param toolbar: Toolbar object :return: """ tag_underline_front: TextTag = self._textbuffer_front.create_tag( 'underline', underline=Underline.SINGLE) tag_underline_back: TextTag = self._textbuffer_back.create_tag( 'underline', underline=Underline.SINGLE) button_underline: ToolButton = ToolButton() button_underline.set_icon_name('format-text-underline-symbolic') toolbar.insert(button_underline, 4) button_underline.connect('clicked', self._onToolbarTagBtnClicked, tag_underline_front, tag_underline_back) def _setToolbarBoldButton(self, toolbar: Toolbar) -> None: """ Sets up the bold button from the toolbar. :param toolbar: Toolbar object :return: """ tag_bold_front: TextTag = self._textbuffer_front.create_tag( 'bold', weight=Weight.BOLD) tag_bold_back: TextTag = self._textbuffer_back.create_tag( 'bold', weight=Weight.BOLD) button_bold: ToolButton = ToolButton() button_bold.set_icon_name('format-text-bold-symbolic') toolbar.insert(button_bold, 5) button_bold.connect('clicked', self._onToolbarTagBtnClicked, tag_bold_front, tag_bold_back) def _setToolbarItalicButton(self, toolbar: Toolbar) -> None: """ Sets up the italic button from the toolbar. :param toolbar: Toolbar object :return: """ tag_italic_front: TextTag = self._textbuffer_front.create_tag( 'italic', style=Style.ITALIC) tag_italic_back: TextTag = self._textbuffer_back.create_tag( 'italic', style=Style.ITALIC) button_italic: ToolButton = ToolButton() button_italic.set_icon_name('format-text-italic-symbolic') toolbar.insert(button_italic, 6) button_italic.connect('clicked', self._onToolbarTagBtnClicked, tag_italic_front, tag_italic_back) def _setToolbarTagRemoverButton(self, toolbar: Toolbar) -> None: """ Sets up the tag remover button from the toolbar. :param toolbar: Toolbar object. :return: """ button_remove_all_tags: ToolButton = ToolButton() button_remove_all_tags.set_icon_name('edit-clear-symbolic') toolbar.insert(button_remove_all_tags, 8) button_remove_all_tags.connect( 'clicked', lambda _: self._removeAllTagsFromSelection()) def _getBounds(self) -> Tuple[TextMark, TextMark, Optional[str]]: """ Returns the selection of the text in the text buffer. :return: A tuple with the textiter of the selection and the path string. """ path: Optional[str] # if no row is selected # a IndexError will be raised try: path = self._selected_row.get_selected_rows()[1][0].to_string() except IndexError: path = None bounds_front: TextMark = self._textbuffer_front.get_selection_bounds() bounds_back: TextMark = self._textbuffer_back.get_selection_bounds() return (bounds_front, bounds_back, path) def _onToolbarColorBtnClicked(self, _, color_button: ColorButton) -> None: """ Handles the clicked event for the tool_item_color_button. :param set_color_button: ToolButton object. :param color_button: ColorButton object. :return: """ start: TextIter end: TextIter bounds_front: TextMark bounds_back: TextMark path: Optional[str] color: str = color_button.get_color().to_string() tag_table_front: TextTagTable = self._textbuffer_front.get_tag_table() tag_table_back: TextTagTable = self._textbuffer_back.get_tag_table() (bounds_front, bounds_back, path) = self._getBounds() # no selected row so there's nothing to do if not path: return ##### FRONT if bounds_front: (start, end) = bounds_front # only the first color applied to the selection # will be present at the final card # so remove all color previously applied to the current selected text. self._removeAllTagsFromSelection(color_tags=True) if not tag_table_front.lookup(color): tag_front: TextTag = self._textbuffer_front.create_tag( color, foreground=color) self._textbuffer_front.apply_tag(tag_front, start, end) else: self._textbuffer_front.apply_tag_by_name(color, start, end) self._dict_any_change_front[path] = serializeIt( text_buffer=self._textbuffer_front) ###### BACK if bounds_back: (start, end) = bounds_back # only the first color applied to the selected text # will be present at the final card # so remove all color previously applied to the current selected text. self._removeAllTagsFromSelection(color_tags=True) if not tag_table_back.lookup(color): tag_back = self._textbuffer_back.create_tag(color, foreground=color) self._textbuffer_back.apply_tag(tag_back, start, end) else: self._textbuffer_back.apply_tag_by_name(color, start, end) self._dict_any_change_back[path] = serializeIt( text_buffer=self._textbuffer_back) def _onToolbarTagBtnClicked(self, _, tag_front: TextTag, tag_back: TextTag) -> None: """ Handles the clicked event for the tool button. :param widget: ToolButton object. :param tag_front: TextTag object. :param tag_back: TextTag object. :return: """ start: TextIter end: TextIter bounds_front: TextMark bounds_back: TextMark path: Optional[str] (bounds_front, bounds_back, path) = self._getBounds() # no selected row so there's nothing to do if not path: return ##### FRONT if bounds_front: (start, end) = bounds_front self._textbuffer_front.apply_tag(tag_front, start, end) self._dict_any_change_front[path] = serializeIt( text_buffer=self._textbuffer_front) ###### BACK if bounds_back: (start, end) = bounds_back self._textbuffer_back.apply_tag(tag_back, start, end) self._dict_any_change_back[path] = serializeIt( text_buffer=self._textbuffer_back) def _removeAllTagsFromSelection(self, color_tags: bool = False) -> None: """ Remove all tags from the current selected text. :param color_tags: If true only removes color tags. :return: """ start: TextIter end: TextIter bounds_front: TextMark bounds_back: TextMark path: Optional[str] tag_table_front: TextTagTable = self._textbuffer_front.get_tag_table() tag_table_back: TextTagTable = self._textbuffer_back.get_tag_table() (bounds_front, bounds_back, path) = self._getBounds() # no selected row so there's nothing to do if not path: return ### FRONT if bounds_front: (start, end) = bounds_front if color_tags: for c in self._color_tag_names: if tag_table_front.lookup(c): self._textbuffer_front.remove_tag_by_name( c, start, end) else: self._textbuffer_front.remove_all_tags(start, end) self._dict_any_change_front[path] = serializeIt( text_buffer=self._textbuffer_front) ### BACK if bounds_back: (start, end) = bounds_back if color_tags: for c in self._color_tag_names: if tag_table_back.lookup(c): self._textbuffer_back.remove_tag_by_name(c, start, end) else: self._textbuffer_back.remove_all_tags(start, end) self._dict_any_change_back[path] = serializeIt( text_buffer=self._textbuffer_back) def _setProgressBar(self) -> None: """ Sets up the progress bar. :return: """ self._cur_progress = 0 self._progress_bar = ProgressBar() setMargin(self._progress_bar, 5) self._main_box.pack_start(self._progress_bar, False, True, 0) def _setButtons(self) -> None: """ Sets up the cancel and generate buttons. :return: """ box: Box = Box() self._main_box.pack_end(box, False, True, 0) box.set_halign(Align.CENTER) box.set_orientation(Orientation.HORIZONTAL) setMargin(box, 5) self._cancel_btn = Button(label='Cancel') box.pack_start(self._cancel_btn, False, True, 0) setMargin(self._cancel_btn, 5, 5, 100, 5) self._cancel_btn.connect('clicked', self._onCancelBtnClicked) self._generate_btn = Button(label='Generate') box.pack_end(self._generate_btn, False, True, 0) setMargin(self._generate_btn, 100, 5, 5, 5) self._generate_btn.connect('clicked', self._onGenerateBtnClicked) timeout_add(300, self._setSensitiveGenerateBtn) def _setSensitiveGenerateBtn(self) -> bool: """ Set the senstive for the generate_btn. :return: A boolean to signal whether idle_add should remove it from list event. """ if self._cur_progress or not self._allFuturesDone(): self._generate_btn.set_sensitive(False) elif not self._any_media_toggled: self._generate_btn.set_sensitive(False) else: self._generate_btn.set_sensitive(True) return True def _allFuturesDone(self) -> bool: """ Check for the status of futures. :return: Return true if all futures are done. """ for f in self._futures_list: if not f.done(): return False return True def _updateProgress(self) -> bool: """ Keep track of the objects yet to be completed. Updates the progress bar. :param future: Parameter passed by add_done_callback. :return: a boolean to signal whether idle_add should remove it from list event. """ if not self.getCancelTaskStatus(): self._cur_progress += 1 self._progress_bar.set_fraction(self._cur_progress / self._max_tasks) self._progress_bar.set_text(None) self._progress_bar.set_show_text(True) if self._cur_progress == self._max_tasks: self._cur_progress = 0 self._progress_bar.set_text('Done!') self._progress_bar.set_show_text(True) return False def resetProgressbar(self) -> None: """ Resets the progress bar back to zero. :return: """ self._cur_progress = 0 self._progress_bar.set_fraction(self._cur_progress) self._progress_bar.set_show_text(False) def idleaddUpdateProgress(self, _) -> None: """ Call idle_add to call updateProgress. :param future: Optional future object. :return: """ idle_add(self._updateProgress) def getCancelTaskStatus(self) -> bool: """ Get the status for the cancel_task. :return: Return true if the task should be cancelled. """ return self._cancel_task def setCancelTaskStatus(self, status: bool) -> None: """ Set the status for the cancel_task. :return: """ self._cancel_task = status def _idleaddUpdateProgress(self, _) -> None: """ Call idle_add to call updateProgress. :param future: Optional future object. :return: """ idle_add(self._updateProgress) def _setSensitiveCancelBtn(self) -> bool: """ Set the sensitive for snd_cancel_button. :return: """ if self._allFuturesDone(): self._progress_bar.set_text('Canceled!') self._cancel_btn.set_sensitive(True) return False else: self._progress_bar.set_text('Cancelling please wait...') self._cancel_btn.set_sensitive(False) return True def _onCancelBtnClicked(self, _) -> None: """ Handle the clicked event for the second_cancel_button button. :param widget: Button object. :return: """ if not self._cur_progress: self._generate_btn.set_sensitive(True) self.close() else: self.setCancelTaskStatus(True) self._cur_progress = 0 self._progress_bar.set_fraction(self._cur_progress) self._progress_bar.set_show_text(True) timeout_add(300, self._setSensitiveCancelBtn) self._cur_progress = 0 self._progress_bar.set_fraction(self._cur_progress) def _onGenerateBtnClicked(self, _) -> None: """ Handle the click event for the generate_btn. :return: """ from asts.Threading import ThreadingHandler self._listMediasSentences() ThreadingHandler(self) def _listMediasSentences(self) -> None: """ Create two lists and fill them with filenames and sentences. :return: """ from uuid import uuid1 from asts.Utils import PangoToHtml # case other tasks already had been scheduled self._resetFuturesLists() self._list_of_sentences = [] self._list_info_medias = [] p: PangoToHtml = PangoToHtml() for i in range(len(self._subtitles_liststore)): if self._subtitles_liststore[i][4] or self._subtitles_liststore[i][ 5] or self._subtitles_liststore[i][6]: # a unique id for each media, some images will conflict if it has the same name as a image # on anki media collection uuid_media = uuid1().int text_front: str = p.feed(self._dict_any_change_front[str(i)]) text_back: str = p.feed(self._dict_any_change_back[str(i)]) self._list_info_medias.append( [str(uuid_media)] + (self._subtitles_liststore[i][1:])) if self._subtitles_liststore[i][ 4] and not self._subtitles_liststore[i][6]: self._list_of_sentences.append( (text_front, text_back, f'{uuid_media}.mp4', None, None)) elif self._subtitles_liststore[i][ 5] and self._subtitles_liststore[i][6]: self._list_of_sentences.append( (text_front, text_back, None, f'{uuid_media}.mp3', f'{uuid_media}.bmp')) elif self._subtitles_liststore[i][ 5] and not self._subtitles_liststore[i][6]: self._list_of_sentences.append( (text_front, text_back, None, f'{uuid_media}.mp3', None)) else: self._list_of_sentences.append( (text_front, text_back, None, None, f'{uuid_media}.bmp')) self._max_tasks = len(self._list_info_medias) + len( self._list_of_sentences) def getCollection(self) -> Filename: """ Returns the filename for the anki2.collection. :return: Filename of the anki2.collection. """ return self._collection_filename def getDeckName(self) -> str: """ Returns the deck name. :return: Deck name. """ return self._deck_name def getVideoFilename(self) -> Filename: """ Returns the name of the video file. :return: Video filename. """ return self._video_filename def getListInfoMedias(self) -> List[List[Info]]: """ Returns a list with information about each media to be used at creating cards. :return: A list with information about each media. """ return self._list_info_medias def getListOfSentences(self) -> ListSentences: """ Returns a List with information about each sentence to be used at creating cards. :return: A list with information about each sentence. """ return self._list_of_sentences def appendFuture(self, future: Future) -> None: """ Append the future to _futures_list. :return: """ self._futures_list.append(future)
def on_selection_changed(self, icon_view, album=None): popup = Popover.new(self.albumview) popup.set_size_request(810, 240) if album is None: selection = icon_view.get_selected_items() if len(selection) != 1: return path = selection[0] treeiter = self.albumfilter.get_iter(path) isset, path, cell = icon_view.get_cursor() isset, rect = icon_view.get_cell_rect(path, cell) popup.set_pointing_to(rect) album_id = self.albumfilter.get_value(treeiter, 4) album_obj = self.albums[album_id] else: album_obj = album popup.set_relative_to(self.search_entry) # Handle double clicks def empty_dblclick(): self.dblclick = None if self.dblclick is None: self.dblclick = album_obj timeout_add(1000, empty_dblclick) elif self.dblclick == album_obj: self.play(album_obj) return album = album_obj.name artist = album_obj.artist glade_album = join(self.functions.datadir, 'glade', 'albumview.ui') box = gtk_builder() box.set_translation_domain('bluemindo') box.add_from_file(glade_album) popup.add(box.get_object('box1')) box.get_object('label_album').set_text(album) box.get_object('label_artist').set_text(artist) bdir = join(self.userconf.datadir, 'modules', 'player', 'covers') cover = join(bdir, self.functions.get_hash(album, artist)) if isfile(cover): cover_px = Pixbuf.new_from_file_at_scale(cover, 180, 180, True) else: cover_px = Pixbuf.new_from_file(join(self.functions.datadir, 'image', 'logo_head_big.png')) box.get_object('album_cover').set_from_pixbuf(cover_px) def play_album(wdg, album): self.play(album) def queue_album(wdg, album): self.queue(album) def change_cover(wdg, ka, album): artist_name = album.artist album_name = album.name fcdialog = FileChooserDialog( title=_('Change the cover picture for this album'), buttons=(_('Select'), ResponseType.OK)) fcdialog.set_transient_for(self.widgets[0][11]) response = fcdialog.run() if response == ResponseType.OK: filename = fcdialog.get_filename() datadir = self.userconf.datadir hash_a = self.functions.get_hash(album_name, artist_name) pictures_dir = join(datadir, 'modules', 'player', 'covers') album_file = join(pictures_dir, hash_a) copyfile(filename, album_file) new = Pixbuf.new_from_file_at_scale(album_file, 180, 180, True) box.get_object('album_cover').set_from_pixbuf(new) fcdialog.destroy() box.get_object('button_play').connect('clicked', play_album, album_obj) box.get_object('button_add').connect('clicked', queue_album, album_obj) box.get_object('coverevent').connect('button-press-event', change_cover, album_obj) i = 0 a = -1 previous_column = 0 grid_songs = box.get_object('grid_songs') grid_songs.set_size_request(-1, 200) grid_songs.set_column_spacing(5) try: kids = grid_songs.get_children() for kid in kids: grid_songs.remove(kid) except IndexError: pass for song in album_obj.tracks: i += 1 a += 1 def queue(wdg, song): self.queue(song) def play(wdg, song): self.play(song) song_wdg = Box(spacing=0) song_btr = Button() song_btr.connect('clicked', play, song) song_btr.set_relief(ReliefStyle.NONE) song_btr_content = Box(spacing=0) song_btr.add(song_btr_content) song_tr = Label() song_tr.set_markup('<span foreground="grey">' + str(song.track) + '</span>') song_tr.set_width_chars(3) song_btr_content.pack_start(song_tr, False, True, 0) song_ti = Label() song_ti.set_markup('<b>' + self.functions.view_encode(song.title, 22) + '</b>') song_ti.set_alignment(0.0, 0.5) song_ti.set_size_request(190, -1) song_btr_content.pack_start(song_ti, False, False, 0) length = self.functions.human_length(song.length) song_le = Label() song_le.set_markup('<span foreground="grey">' + length + '</span>') song_le.set_width_chars(5) song_btr_content.pack_start(song_le, False, True, 0) song_wdg.pack_start(song_btr, False, False, 0) song_add = Button.new_from_icon_name('list-add-symbolic', 0) song_add.set_property('relief', 2) song_add.set_size_request(14, 14) song_add.connect('clicked', queue, song) song_wdg.pack_start(song_add, False, False, 0) if i <= len(album_obj.tracks)/2: column = 0 previous_column = 0 row = a else: if previous_column == 0: a = 0 column = 1 previous_column = 1 row = a grid_songs.attach(song_wdg, column, row, 1, 1) popup.show_all()
def __init__(self, extensions): # Start threads threads_init() self.extensions = extensions # Create the main Bluemindo window self.main_window = Window() functions.open_bluemindo(self.main_window) # Handling close button def close_window(wdg, ka): functions.close_bluemindo(self.main_window, True) self.main_window.connect('delete_event', close_window) # Create the whole Header Bar box = HeaderBar() box.set_show_close_button(True) box.props.title = 'Bluemindo' self.main_window.set_titlebar(box) # Add an icon to the window icon_file = join(functions.datadir, 'image', 'logo_head_small.png') pixbuf = Pixbuf.new_from_file(icon_file) self.main_window.set_icon(pixbuf) # Add the about button about_button = Button(relief=2) about_button.add( Image.new_from_gicon(ThemedIcon(name='help-about-symbolic'), IconSize.BUTTON)) box.pack_end(about_button) # Add the reload button refresh_button = Button(relief=2) refresh_button.add( Image.new_from_gicon(ThemedIcon(name='view-refresh-symbolic'), IconSize.BUTTON)) box.pack_end(refresh_button) # Add PREVIOUS/STOP/PLAYPAUSE/NEXT buttons player_box = Box(orientation=Orientation.HORIZONTAL) StyleContext.add_class(player_box.get_style_context(), 'linked') previous_b = Button() previous_b.set_size_request(42, -1) previous_b.add( Image.new_from_gicon( ThemedIcon(name='media-skip-backward-symbolic'), IconSize.BUTTON)) player_box.add(previous_b) stop_b = Button() stop_b.set_size_request(42, -1) stop_b.add( Image.new_from_gicon( ThemedIcon(name='media-playback-stop-symbolic'), IconSize.BUTTON)) player_box.add(stop_b) playpause_b = Button() playpause_b.set_size_request(55, -1) playpause_b.add( Image.new_from_gicon( ThemedIcon(name='media-playback-start-symbolic'), IconSize.BUTTON)) player_box.add(playpause_b) next_b = Button() next_b.set_size_request(42, -1) next_b.add( Image.new_from_gicon( ThemedIcon(name='media-skip-forward-symbolic'), IconSize.BUTTON)) player_box.add(next_b) box.pack_start(player_box) # Create the main window glade_main = join(functions.datadir, 'glade', 'mainwindow.ui') win = gtk_builder() win.set_translation_domain('bluemindo') win.add_from_file(glade_main) self.main_window.add(win.get_object('box1')) # Connect to the about button def show_dialog(wdg): dialog = AboutDialog() dialog.set_transient_for(self.main_window) dialog.set_artists(['Thomas Julien <*****@*****.**>']) dialog.set_authors([ 'Erwan Briand <*****@*****.**>', 'Vincent Berset <*****@*****.**>', 'Thibaut Girka <*****@*****.**>', 'Ľubomír Remák <*****@*****.**>', 'Anaël Verrier <*****@*****.**>' ]) dialog.set_translator_credits( 'Bruno Conde <*****@*****.**>\n' + 'Niklas Grahn <*****@*****.**>\n' + 'Ľubomír Remák <*****@*****.**>\n' + 'Salvatore Tomarchio <*****@*****.**>\n' + 'Shang Yuanchun <*****@*****.**>') dialog.set_copyright('Copyright © 2007-2016 Erwan Briand ' + '<*****@*****.**>') dialog.set_comments( _('Ergonomic and modern music player ' + 'designed for audiophiles.')) dialog.set_license('GNU General Public License (v3)') dialog.set_license_type(10) dialog.set_program_name('Bluemindo') dialog.set_version('1.0RC1') dialog.set_website('http://bluemindo.codingteam.net') pxbf = Pixbuf.new_from_file_at_scale( join(functions.datadir, 'image', 'logo_head_big.png'), 60, 60, True) dialog.set_logo(pxbf) dialog.show_all() about_button.connect('clicked', show_dialog) # Start main handler headerbar_wdg = [ box, None, about_button, refresh_button, player_box, previous_b, stop_b, playpause_b, next_b, None, win.get_object('box1'), self.main_window ] self.wdg = [headerbar_wdg, win]
def get_string_from_button(button: Gtk.Button) -> str: """ Returns the displayed string from a button :return: the button string """ return button.get_label()