def UpdateFileList(self, baseFolder: Path, fileList: QtWidgets.QTreeWidget): assert (fileList) assert (baseFolder) asset_path = baseFolder / re_project._RE_DCC_APP fileList.clear() for pattern in self.sceneDCCExtensions: filelist_gen = asset_path.glob(pattern) file_list = list(filelist_gen) for file_path in file_list: rel_path: Path = file_path.relative_to(asset_path) #print(rel_path.as_posix()) file_stats: os.stat_result = file_path.stat() mtime = file_stats.st_mtime timestamp_str = datetime.datetime.fromtimestamp( mtime).strftime('%Y-%m-%d-%H:%M') fileList.addTopLevelItem( QtWidgets.QTreeWidgetItem([ rel_path.as_posix(), timestamp_str, ProjectManagerUI.convert_file_size(file_stats.st_size) ])) fileList.resizeColumnToContents(0) fileList.resizeColumnToContents(2)
class CollapsibleWidget(QWidget): """a dialog to which collapsible sections can be added; subclass and reimplement define_sections() to define sections and add them as (title, widget) tuples to self.sections """ def __init__(self): super().__init__() self.tree = QTreeWidget() self.tree.setHeaderHidden(True) self.tree.setPalette(get_verifone_color()) self.setStyleSheet("background-color: rgba(3, 169, 229, 0)") layout = QVBoxLayout() self.buttonGroup = QButtonGroup() layout.addWidget(self.tree) self.setLayout(layout) self.tree.setIndentation(0) self.setPalette(get_verifone_color()) self.sections = [] self.setFixedWidth(500) def get_tree(self): return self.tree def add_sections(self): """adds a collapsible sections for every (title, widget) tuple in self.sections """ for (title, widget) in self.sections: button1 = self.add_button(title) section1 = self.add_widget(button1, widget) button1.addChild(section1) def include_section(self, title, frame): """reimplement this to define all your sections and add them as (title, widget) tuples to self.sections """ self.sections.append((title, frame)) def add_button(self, title): """creates a QTreeWidgetItem containing a button to expand or collapse its section """ item = QTreeWidgetItem() item.setBackgroundColor(0, QColor(3, 169, 229)) self.tree.addTopLevelItem(item) self.tree.setItemWidget(item, 0, SectionExpandButton(item, text = title, group = self.buttonGroup)) return item def add_widget(self, button, widget): """creates a QWidgetItem containing the widget, as child of the button-QWidgetItem """ section = QTreeWidgetItem(button) section.setDisabled(True) section.setBackgroundColor(0, QColor(3, 169, 229)) self.tree.setItemWidget(section, 0, widget) return section
class CollapsibleDialog(QDialog): """a dialog to which collapsible sections can be added; subclass and reimplement define_sections() to define sections and add them as (title, widget) tuples to self.sections """ def __init__(self): super().__init__() self.tree = QTreeWidget() self.tree.setHeaderHidden(True) layout = QVBoxLayout() layout.addWidget(self.tree) self.setLayout(layout) self.tree.setIndentation(0) self.sections = [] self.define_sections() self.add_sections() def add_sections(self): """adds a collapsible sections for every (title, widget) tuple in self.sections """ for (title, widget) in self.sections: button1 = self.add_button(title) section1 = self.add_widget(button1, widget) button1.addChild(section1) def define_sections(self): """reimplement this to define all your sections and add them as (title, widget) tuples to self.sections """ widget = QFrame(self.tree) layout = QHBoxLayout(widget) layout.addWidget(QLabel("Bla")) layout.addWidget(QLabel("Blubb")) title = "Section 1" self.sections.append((title, widget)) def add_button(self, title): """creates a QTreeWidgetItem containing a button to expand or collapse its section """ item = QTreeWidgetItem() self.tree.addTopLevelItem(item) self.tree.setItemWidget(item, 0, SectionExpandButton(item, text=title)) return item def add_widget(self, button, widget): """creates a QWidgetItem containing the widget, as child of the button-QWidgetItem """ section = QTreeWidgetItem(button) section.setDisabled(True) self.tree.setItemWidget(section, 0, widget) return section
class SelectTiVoWidget(QDialog): """Displays the list of TiVos that were discovered on the network.""" # Signals connect_to_tivo = Signal(str, str) def __init__(self): super(SelectTiVoWidget, self).__init__() self.tivos_found = [] self.label = QLabel(self) self.label.setText('Below is a listing of all TiVos discovered on your' ' network, which will refresh every 5 seconds. If y' 'ou do not see your TiVo, ' '<a href="#specify_ip">click here to specify an IP' ' address.</a>') self.label.linkActivated.connect(self.specify_ip_address) self.tivo_listings = QTreeWidget(self) self.tivo_listings.itemDoubleClicked.connect(self.tivo_selected) self.tivo_listings.setHeaderLabels(["Name", "IP Address"]) self.layout = QVBoxLayout(self) self.layout.addWidget(self.label) self.layout.addWidget(self.tivo_listings) self.setWindowTitle("Select TiVo") self.resize(512, 384) self.setModal(True) def add_tivo(self, name, ip_address): item = QTreeWidgetItem() item.setText(0, name) item.setText(1, ip_address) self.tivo_listings.addTopLevelItem(item) self.tivos_found.append(item) def tivo_selected(self, item, column): self.connect_to_tivo.emit(item.text(0), item.text(1)) def specify_ip_address(self, link): text, ok = QInputDialog().getText(self, "Specify TiVO IP address", "IP address:", QLineEdit.Normal) if ok: self.connect_to_tivo.emit("unknown", text)
class ExifViewer(QMainWindow): WINDOW_SIZE = {'width': 900, 'height': 600} def __init__(self, parent=None): super().__init__(parent) self.qw_tree = None self.qw_stack = None self.tree_type_to_stack_idx = None self.exif_reader = None self.setWindowTitle("exif viewer") self.resize(self.WINDOW_SIZE['width'], self.WINDOW_SIZE['height']) def make_viewer(self, exif_reader): self.exif_reader = exif_reader ifds = self.exif_reader.get_exif() self.qw_tree = QTreeWidget(self) qt_util_init_tree(self.qw_tree, header_list=['ifd_name']) self.qw_stack = QStackedWidget(self) self.tree_type_to_stack_idx = {} for tree_item_type, (ifd_name, ifd) in enumerate(ifds.items()): # ----------------------- # treeにifd nameを書き込んでroot_treeに登録する. # ----------------------- qw_tree_item = QTreeWidgetItem(['{} ifd'.format(ifd_name), ''], type=tree_item_type) self.qw_tree.addTopLevelItem(qw_tree_item) # ----------------------- # text_editにexif情報を書き込んでstacked_widgetに登録する. # ----------------------- qw_text_edit = QTextEdit(self) qt_util_init_text_edit(qw_text_edit) qt_util_append_text_edit(qw_text_edit, None, head=True) for _, tag in ifd.items(): qt_util_append_text_edit(qw_text_edit, tag, head=False, display_max_len=50) qt_util_reset_text_edit_cursor(qw_text_edit) idx = self.qw_stack.addWidget(qw_text_edit) self.tree_type_to_stack_idx[tree_item_type] = idx else: self.qw_tree.itemClicked.connect(self._tree_item_clicked) # ----------------------- # widgetをlayoutに登録する. # ----------------------- widgets = [self.qw_tree, self.qw_stack] self._make_layout(widgets) self._make_toolbar() self.show() # widgetを表示する. def _make_toolbar(self): # action_to_text = QAction(QIcon(os.path.join('icon', 'text.png')), 'save', self) save_icon = QApplication.style().standardIcon( QStyle.SP_DialogSaveButton) save_action = QAction(icon=save_icon, text='save', parent=self) save_action.triggered.connect(self._save_text) exit_icon = QApplication.style().standardIcon( QStyle.SP_DialogCloseButton) exit_action = QAction(icon=exit_icon, text='exit', parent=self) exit_action.triggered.connect(self._quit) # ツールバー作成 self.toolbar = self.addToolBar('tool bar') self.toolbar.setIconSize(QSize(35, 35)) self.toolbar.setFixedHeight(35) self.toolbar.addAction(save_action) self.toolbar.addAction(exit_action) def _make_layout(self, widgets): qw_splitter = QSplitter(self) for widget in widgets: qw_splitter.addWidget(widget) qw_splitter.setSizes( [self.WINDOW_SIZE['width'] * 0.1, self.WINDOW_SIZE['width'] * 0.9]) # layout = QHBoxLayout() # layout.addWidget(qw_splitter) # qw = QWidget() # qw.setLayout(layout) # self.setCentralWidget(qw) # self.setWindowFlags(Qt.WindowStaysOnTopHint) # 常に手前に表示する. # MainWindowのCentral領域にWidgetを設定 self.setCentralWidget(qw_splitter) @Slot() def _tree_item_clicked(self, item, column): self.qw_stack.setCurrentIndex(self.tree_type_to_stack_idx[item.type()]) current_text_edit = self.qw_stack.currentWidget() qt_util_reset_text_edit_cursor(current_text_edit) @Slot() def _save_text(self): filename = QFileDialog.getSaveFileName(self, '名前を付けて保存') out_path = filename[0] if out_path: self.exif_reader.save_log(out_path) @Slot() def _quit(self): qApp.quit()
# --------------------------- # Treeにitemに子itemを追加する # --------------------------- import sys from PySide2.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem app = QApplication(sys.argv) qw_tree = QTreeWidget() qw_tree.setHeaderLabels(["name", "tel", "mail"]) qw_tree_parent_item = QTreeWidgetItem(['family']) qw_tree_parent_item.addChild( QTreeWidgetItem(['A', '111-111-111', '*****@*****.**'])) # Itemに子Itemを追加 qw_tree.addTopLevelItem(qw_tree_parent_item) qw_tree.expandAll() # TreeのItemを全て開く qw_tree.show() sys.exit(app.exec_())
qw_stack = QStackedWidget() qw_tree = QTreeWidget() qw_tree.setHeaderLabels(["test"]) def tree_item_clicked(item, column): print(item, column, item.type()) # TreeがクリックされたときにTextEditを切り替える qw_stack.setCurrentIndex(item.type()) for i in range(3): text = 'page No.' + str(i + 1) qw_tree_item = QTreeWidgetItem([text], type=i) qw_tree.addTopLevelItem(qw_tree_item) qw_text_edit = QTextEdit() qw_text_edit.append(text) stack_idx = qw_stack.addWidget(qw_text_edit) # print(stack_idx) # Treeがクリックされたときに呼ばれる関数を登録 qw_tree.itemClicked.connect(tree_item_clicked) qw_splitter = QSplitter() qw_splitter.addWidget(qw_tree) qw_splitter.addWidget(qw_stack) qw_splitter.show()
class ObjectsBrowserWindow(QWidget): def __init__(self, parent, title, person, unsorted_objects, autoshow=True, progress_indicator=None): super().__init__(None) act = QAction("close", self) act.triggered.connect(self._do_close) act.setShortcut(QKeySequence("escape")) self.addAction(act) layout = QGridLayout() bar = QMenuBar(self) self._object_actions = bar.addMenu(_("Object actions")) property_actions = bar.addMenu(_("Property actions")) self._create_item(property_actions, _("Copy property value"), "ctrl+c", self.on_copypropvalue_selected) self._create_item(property_actions, _("Copy property name"), "alt+c", self.on_copypropname_selected) self._create_item(property_actions, _("Copy property name and value"), "ctrl+alt+c", self.on_copypropline_selected) objects_label = QLabel(_("Objects"), self) layout.addWidget(objects_label, 0, 0) self._objects_list = QListWidget(self) self._objects_list.setAccessibleName(objects_label.text()) self._objects_list.setContextMenuPolicy(Qt.CustomContextMenu) self._objects_list.currentRowChanged.connect(self.on_objects_listbox) self._objects_list.customContextMenuRequested.connect( self._on_objects_list_menu) objects_label.setBuddy(self._objects_list) layout.addWidget(self._objects_list, 1, 0) props_label = QLabel(_("Object properties"), self) layout.addWidget(props_label, 0, 1) self._props = QTreeWidget(self) self._props.setAccessibleName(props_label.text()) props_label.setBuddy(self._props) layout.addWidget(self._props, 1, 1) goto_button = QPushButton(_("Go to"), self) goto_button.setDefault(True) goto_button.clicked.connect(self.on_goto_clicked) self._objects_list.itemActivated.connect(goto_button.click) layout.addWidget(goto_button, 2, 0) close_button = QPushButton(_("Close"), self) close_button.clicked.connect(self.close) layout.addWidget(close_button, 2, 1) self.setLayout(layout) self.setWindowTitle(title + _(" ({num_objects} objects shown)").format( num_objects=len(unsorted_objects))) self._person = person self._autoshow = autoshow self._progress_indicator = progress_indicator self._all_actions = [] for member in object_actions.__dict__.values(): if inspect.isclass(member) and issubclass(member, ObjectAction): self._all_actions.append(member) self._objects_list.setCurrentRow(0) self._sorter = ObjectsSorter(unsorted_objects, person) self._sorter.objects_sorted.connect(self._objects_sorted) self._sorter.start() def _objects_sorted(self, data): objects, item_data = data self._objects = objects for (desc, dist, rel_bearing) in item_data: self._objects_list.addItem( _("{object}: distance {distance} meters, {rel_bearing}"). format(object=desc, distance=dist, rel_bearing=rel_bearing)) if self._progress_indicator: self._progress_indicator.hide() self._progress_indicator.deleteLater() if self._autoshow: self._objects_list.setCurrentRow(0) self.show() def _create_item(self, menu, label, shortcut, callback): action = menu.addAction(label) action.triggered.connect(callback) action.setShortcut(QKeySequence(shortcut)) def on_goto_clicked(self, evt): self._person.move_to(self.selected_object[2], force=True) def _do_close(self): self.close() self.destroy() windows = QApplication.topLevelWidgets() other_browsers = [ w for w in windows if w is not self and isinstance(w, self.__class__) and w.isVisible() ] if other_browsers: other_browsers[-1].activateWindow() else: menu_service().ensure_key_capturer_focus() def on_objects_listbox(self, current_index): selected = self._objects[current_index][1] self._props.clear() common_item = QTreeWidgetItem([_("Common properties")]) specific_item = QTreeWidgetItem([_("Specific properties")]) other_item = QTreeWidgetItem([ _("Other properties - they can not be searched and are not processed in any way" ) ]) common_fields = list( EntityMetadata.for_discriminator("OSMEntity").fields.keys()) selected_metadata = EntityMetadata.for_discriminator( selected.discriminator) known_fields = selected_metadata.all_fields formatted_values = {} for field_name in selected.defined_field_names(): raw_value = selected.value_of_field(field_name) if field_name not in known_fields: # By the mere fact that the other fields have no defined order, we can add them there without losing anything. other_item.addChild( QTreeWidgetItem([ "%s: %s" % (underscored_to_words(field_name), raw_value) ])) else: value_str = "%s: %s" % ( underscored_to_words(field_name), format_field_value(raw_value, known_fields[field_name].type_name)) formatted_values[field_name] = value_str for common in common_fields: del known_fields[common] common_item.addChild(QTreeWidgetItem([formatted_values[common]])) for specific in known_fields.keys( ): # Because we deleted the common ones in the loop before this, only the specific remain. if specific in formatted_values: specific_item.addChild( QTreeWidgetItem([formatted_values[specific]])) # We add the entity ID mainly for debugging purposes, and that's the reason why it is added the last and so special in the first place. common_item.addChild( QTreeWidgetItem([_("Object id: {}").format(selected.id)])) self._props.addTopLevelItem(common_item) if specific_item.childCount() > 0: self._props.addTopLevelItem(specific_item) self._props.expandItem(specific_item) #self._props.setCurrentItem(specific_item) # Breaks focus behavior slightly, but annoingly enough. if other_item.childCount() > 0: self._props.addTopLevelItem(other_item) self._object_actions.clear() for action in self._all_actions: if action.executable(selected): mi = self._object_actions.addAction(action.label) mi.triggered.connect( action_execution_handler_factory(action, selected, self)) @property def selected_object(self): return self._objects[self._objects_list.currentRow()] def on_copypropvalue_selected(self, evt): prop = self._props.currentItem().text(0) val = prop.split(": ", 1)[1] QApplication.clipboard().setText(val) def on_copypropname_selected(self, evt): prop = self._props.currentItem().text(0) name = prop.split(": ", 1)[0] QApplication.clipboard().setText(name) def on_copypropline_selected(self, evt): prop = self._props.currentItem().text(0) QApplication.clipboard().setText(prop) def _on_objects_list_menu(self, point): self._object_actions.exec_(point)
def insert_list(tree: QTreeWidget, col0: str, col1: str): searched = tree.findItems(col1, Qt.MatchExactly, 1) if not searched: item = QTreeWidgetItem([col0, col1]) tree.addTopLevelItem(item)
# --------------------------- # Treeにitemを追加する # --------------------------- import sys from PySide2.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem app = QApplication(sys.argv) qw_tree = QTreeWidget() qw_tree.setHeaderLabels(["name", "tel", "mail"]) qw_tree_parent_item = QTreeWidgetItem(['family']) # Treeに追加するItemを生成 qw_tree.addTopLevelItem(qw_tree_parent_item) # TreeにItemを追加する qw_tree.show() sys.exit(app.exec_())
class DiscoverUi(QWidget): def __init__(self): super().__init__() self.discover = Discover() self.devices = self.discover.devices self.listWidget = None self.treeWidget = None self.initUI() self.mac_addr = None def initDevices(self): for device in self.devices: newItem = QListWidgetItem() newItem.setText(device.name + " (" + device.address + ")") self.listWidget.addItem(newItem) def initUI(self): self.listWidget = QListWidget() self.initDevices() self.treeWidget = QTreeWidget() self.treeWidget.itemPressed.connect(self.onItemPressed) self.treeWidget.setColumnCount(4) self.treeWidget.setColumnWidth(0, 250) self.treeWidget.setColumnWidth(1, 300) self.treeWidget.setColumnWidth(2, 300) self.treeWidget.setColumnWidth(3, 150) self.treeWidget.setHeaderLabels(["Service", "Service UUID", "Characteristic UUID", "Characteristic Property"]) btn = QPushButton("Read Services") btn.clicked.connect(self.onPushButton) groupDevices = QGroupBox("Devices") groupDevices.setMaximumWidth(300) vbox = QVBoxLayout() vbox.addWidget(self.listWidget) vbox.addWidget(btn) groupDevices.setLayout(vbox) self.btnR = QPushButton("Read") self.btnR.clicked.connect(self.onReadButton) self.btnW = QPushButton("Write") self.btnW.clicked.connect(self.onWriteButton) self.lneI = QLineEdit() self.chkN = QCheckBox("Notify") self.chkN.toggled.connect(self.onNotifyCheck) hbox = QHBoxLayout() hbox.addWidget(self.btnR) hbox.addWidget(self.btnW) hbox.addWidget(self.lneI) hbox.addWidget(self.chkN) groupProperty = QGroupBox("Property") #groupProperty.setLayout(vbox) groupProperty.setLayout(hbox) groupServices = QGroupBox("Services") vbox = QVBoxLayout() vbox.addWidget(self.treeWidget) vbox.addWidget(groupProperty) groupServices.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(groupDevices) hbox.addWidget(groupServices) self.setLayout(hbox) self.setGeometry(300, 300, 800, 600) self.setWindowTitle('BLE Discover') self.show() def onPushButton(self): try: self.mac_addr = self.devices[self.listWidget.currentRow()].address self.discover.getServices(self.mac_addr) except: print("Could not get GATT services.") else: svcs = self.discover.svcs for serviceKey, serviceValue in svcs.services.items(): item = QTreeWidgetItem(None, [serviceValue.description, serviceValue.uuid]) for characteristic in serviceValue.characteristics: for property in characteristic.properties: child = QTreeWidgetItem(["", "", characteristic.uuid, property]) item.addChild(child) self.treeWidget.addTopLevelItem(item) def onReadButton(self): byteArray = self.discover.readGattChar(self.mac_addr, self.chosenUuid) text = ''.join('{:02x}'.format(x) for x in byteArray) self.lneI.setText(text) def onWriteButton(self): text = self.lneI.text() print("onWriteButton") self.discover.writeGattChar(self.mac_addr, self.chosenUuid, bytes.fromhex(text)) def notifyCallback(self, sender, data): text = ''.join('{:02x}'.format(x) for x in data) self.lneI.textChanged.emit(text) def onNotifyCheck(self, checked): if checked: print("onNotifyCheck") self.discover.startNotify(self.mac_addr, self.chosenUuid, self.notifyCallback) else: print("onNotifyCheck else") def onItemPressed(self, item, column): if item.child(0) is None: print(item) print(item.text(2)) print(item.text(3)) self.chosenUuid = item.text(2) property = item.text(3) if property == "read": self.btnR.setEnabled(True) self.btnW.setEnabled(False) self.lneI.setEnabled(False) self.chkN.setEnabled(False) elif property == "write": self.btnR.setEnabled(True) self.btnW.setEnabled(True) self.lneI.setEnabled(True) self.chkN.setEnabled(False) elif property == "notify": self.btnR.setEnabled(False) self.btnW.setEnabled(False) self.lneI.setEnabled(False) self.chkN.setEnabled(True)
class ShareSelectionPage(QWizardPage): def __init__(self, parent=None, title="Auswahl LB", subtitle="Wählen Sie das Prüfungsmodul aus", button_name="Fertig"): super(ShareSelectionPage, self).__init__(parent) self.setTitle(title) self.setSubTitle(subtitle) self.validSelection = False self.setButtonText(QWizard.FinishButton, button_name) self.setButtonText(QWizard.CancelButton, "Abbrechen") self.setButtonText(QWizard.BackButton, "Zurück") def validatePage(self, *args, **kwargs): ''' test if only one tree view item is selected and if this item matches the naming conventions ''' if len(self.tree.selectedItems()) == 1: item = self.tree.selectedItems()[0] self.wizard().selectedPath = "//" + self.wizard( ).server.serverName + "/" + item.path if self.wizard().type == EcShareWizard.TYPE_LB_SELECTION: if re.compile("^M[1-9][0-9]{2}").match(item.name) == None: self.setSubTitle( "Bitte Prüfungsverzeichnis alá M101 oder M226A auswählen" ) return False else: if re.compile("^(UIFZ|IFZ|ICT)-").match(item.name) == None: self.setSubTitle( "Bitte Klassenverzeichnis alá UIFZ-926-001 oder IFZ-926-001 auswählen" ) return False self.setSubTitle("") return True self.wizard().selectedPath = None return False def initializePage(self): if self.layout() != None: # print("clean up previous entries") while self.layout().count() > 0: self.layout().removeItem(self.layout().itemAt(0)) self.tree = QTreeWidget() if self.wizard().defaultShare != None: self.tree.addTopLevelItem( MyTreeWidgetItem(self.tree, self.wizard().defaultShare)) else: shares = self.wizard().getShares() if shares == None: print("No shares found...") return print(",".join([x.name for x in shares])) for m in shares: if m.name not in [ "ADMIN$", "C$", "IPC$", "NETLOGON", "SYSVOL" ]: self.tree.addTopLevelItem(MyTreeWidgetItem(self.tree, m)) shareListBox = QWidget() shareListBoxLayout = QVBoxLayout() self.tree.setHeaderLabel("Freigaben") self.tree.itemClicked.connect(self.treeViewItemClicked) shareListBoxLayout.addWidget(self.tree) shareListBox.setLayout(shareListBoxLayout) self.folderList = QListWidget() folderListBox = QWidget() folderListBoxLayout = QVBoxLayout() folderListHeaderMessage = "Verzeichnisinhalt" folderListBoxLayout.addWidget(QLabel(folderListHeaderMessage)) folderListBoxLayout.addWidget(self.folderList) folderListBox.setLayout(folderListBoxLayout) layout = QHBoxLayout() layout.addWidget(shareListBox) layout.addWidget(folderListBox) self.setLayout(layout) def treeViewItemClicked(self, item): print("received treeview click on: " + str(item)) self.folderList.clear() self.validSelection = False hasChildren = item.childCount() > 0 # don't add children again try: content = self.wizard().getFolderContent(item.path) content = sorted(content, key=lambda entry: (not (entry.isDirectory), entry.filename)) for x in content: if not (x.filename.startswith(".")): newItemPath = item.path + "/" + x.filename icon = QStyle.SP_DirIcon if x.isDirectory else QStyle.SP_FileIcon self.folderList.addItem( MyListWidgetItem(self.style().standardIcon(icon), newItemPath, x.isDirectory, self.folderList)) if not (hasChildren) and x.isDirectory: item.addChild(MyTreeWidgetItem(self.tree, newItemPath)) except Exception as ex: # smb.smb_structs.OperationFailure ?? print(str(ex)) pass