Пример #1
0
    def __init__(self):

        ui_file_name = "record_frame.ui"
        ui_file = QFile(ui_file_name)
        if not ui_file.open(QIODevice.ReadOnly):
            print("can not open file " + ui_file_name)
            sys.exit(-1)
        self.window = QUiLoader().load(ui_file)
        ui_file.close()
        self._component_bind()
        self.window.show()
        self.status = "Ready"
        self.record = []
        self.current_mill_time = current_time()
        self.delay = int(self.window.record_time_stepper.value())
        self.run_times = int(self.window.run_time_stepper.value())
        self.start_hot_key = self.window.run_hot_key.currentText()
        self.stop_hot_key = self.window.stop_hot_key.currentText()
        # self.window.record_script
        self.window.run_hot_key.addItems(self.HOT_KEYS)
        self.window.run_hot_key.setCurrentIndex(8)
        self.window.stop_hot_key.addItems(self.HOT_KEYS)
        self.window.stop_hot_key.setCurrentIndex(9)
        self._refresh_scripts()
        self.window.status.setText(self.status)

        self.hookManager = pyWinhook.HookManager()
        self.hookManager.MouseAll = self._mouse_move_handler
        self.hookManager.KeyAll = self._keyboard_click_handler
        self.hookManager.HookKeyboard()
        self.hookManager.HookMouse()
Пример #2
0
    def __init__(self, parent=None):
        super(DragWidget, self).__init__(parent)

        dictionaryFile = QFile(':/dictionary/words.txt')
        dictionaryFile.open(QIODevice.ReadOnly)

        x = 5
        y = 5

        for word in QTextStream(dictionaryFile).readAll().split():
            wordLabel = DragLabel(word, self)
            wordLabel.move(x, y)
            wordLabel.show()
            x += wordLabel.width() + 2
            if x >= 195:
                x = 5
                y += wordLabel.height() + 2

        newPalette = self.palette()
        newPalette.setColor(QPalette.Window, Qt.white)
        self.setPalette(newPalette)

        self.setAcceptDrops(True)
        self.setMinimumSize(400, max(200, y))
        self.setWindowTitle("Draggable Text")
Пример #3
0
    def __init__(self, parent=None):
        #load ui
        ui_file = QFile("mainwindow.ui")
        ui_file.open(QFile.ReadOnly)
        loader = QUiLoader()
        self.window = loader.load(ui_file)

        #init game
        self.gameInit()
        self.window.actionrule.triggered.connect(self.helpText)
        self.window.btnCheck.clicked.connect(self.CheckNum)
        self.window.btnRetry.clicked.connect(self.gameInit)
        self.window.show()
    def open_file(self, path=""):
        file_name = path

        if not file_name:
            file_name, _ = QFileDialog.getOpenFileName(
                self, self.tr("Open File"), "",
                "qmake Files (*.pro *.prf *.pri)")

        if file_name:
            inFile = QFile(file_name)
            if inFile.open(QFile.ReadOnly | QFile.Text):
                stream = QTextStream(inFile)
                self._editor.setPlainText(stream.readAll())
Пример #5
0
    def loadFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly | QFile.Text):
            QMessageBox.warning(
                self, "MateWriter",
                "Cannot read file %s:\n%s." % (fileName, file.errorString()))
            return

        inf = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.textEdit.setPlainText(inf.readAll())
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName)
        self.statusBar().showMessage("File loaded", 2000)
Пример #6
0
    def saveFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(
                self, "MateWriter",
                "Cannot write file %s:\n%s." % (fileName, file.errorString()))
            return False

        outf = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        outf << self.textEdit.toPlainText()
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName)
        self.statusBar().showMessage("File saved", 2000)
        return True
Пример #7
0
    def run(self):
        """
        Main logic. Pastes the files from the clipboard to the target path.
        """
        if self.clipboard.mimeData().hasUrls():
            self.signals.started.emit()

            path_list = self.get_paths_from_clipboard()

            cut = (collections.Counter(path_list) == collections.Counter(
                self.marked_to_cut))

            path_list = self.recurse_dirs(path_list)

            files_copied = 0
            maximum = self.count_files(path_list)
            self.signals.ready.emit(maximum)

            base_path = self.get_base_path(path_list)

            # copy files to new location
            for path in path_list:
                if self.is_canceled:
                    break

                new_path = os.path.join(self.target_path,
                                        path.replace(base_path, ''))
                if (os.path.isdir(path) and not QDir().exists(new_path)):
                    QDir().mkpath(new_path)
                elif (QFile().exists(path) and not QFile().exists(new_path)):
                    if (QFile().copy(path, new_path)):
                        files_copied += 1
                        self.signals.progress.emit(files_copied)
                        self.signals.progress_message.emit('Pasting file ' +
                                                           str(files_copied) +
                                                           ' of ' +
                                                           str(maximum) +
                                                           '...')
                    else:
                        raise OSError
            # removed cut files
            if cut:
                for file_path in path_list:
                    if (not QFile().remove(file_path)):
                        raise OSError
        self.signals.finished.emit()
Пример #8
0
    def loadFile(self):
        file = QFile(self.filename)

        if not file.open(QIODevice.ReadOnly):
            print("Can't open file:\n" + self.filename)
            return

        # Prepare text stream
        textStream = QTextStream(file)

        # Read lines
        data = []
        while not textStream.atEnd():
            data.append(textStream.readLine())

        # Load lines
        self.loadData(data)
Пример #9
0
    def __init__(self):
        super().__init__()

        self.setGeometry(50, 50, 200, 200)
        self.setWindowTitle('Awesome File Manager')

        ui_file = QFile("mainwindow.ui")
        ui_file.open(QFile.ReadOnly)

        loader = QUiLoader()
        self.window = loader.load(ui_file)

        self.window.comboBox.setItemText(0, 'first filter')

        files = QFileDialog.getOpenFileNames(self)
        btn_file = QPushButton('Open', self)
        for file in files[0]:
            btn_file.clicked.connect(main(file))
Пример #10
0
    def loadFile(self, fileName):
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly | QFile.Text):
            QMessageBox.warning(
                self, "MDI",
                "Cannot read file %s:\n%s." % (fileName, file.errorString()))
            return False

        instr = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.setPlainText(instr.readAll())
        QApplication.restoreOverrideCursor()

        self.setCurrentFile(fileName)

        self.document().contentsChanged.connect(self.documentWasModified)

        return True
Пример #11
0
    def save(self):
        filename, _ = QFileDialog.getSaveFileName(self, "Choose a file name",
                                                  '.', "HTML (*.html *.htm)")
        if not filename:
            return

        file = QFile(filename)
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(
                self, "Dock Widgets",
                "Cannot write file %s:\n%s." % (filename, file.errorString()))
            return

        out = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        out << self.textEdit.toHtml()
        QApplication.restoreOverrideCursor()

        self.statusBar().showMessage("Saved '%s'" % filename, 2000)
Пример #12
0
    def initUI(self, uiname, mode):
        # release mode
        # TODO: convert .ui to py code
        app = QApplication(sys.argv)

        # debug mode
        if mode == 'debug':
            ui_file_name = uiname
            ui_file = QFile(ui_file_name)
            if not ui_file.open(QIODevice.ReadOnly):
                print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
                sys.exit(-1)
            loader = QUiLoader()
            window = loader.load(ui_file)
            ui_file.close()
            if not window:
                print(loader.errorString())
                sys.exit(-1)
            window.show()

        return app, window
Пример #13
0
    def loadFile(self, fileName):
        file = QFile(fileName)

        if not file.open(QIODevice.ReadOnly):
            QMessageBox.critical(self, self.windowTitle(),
                                 "Can't open file:\n" + fileName)
            return

        # Set filename
        self.m_programFileName = fileName

        # Prepare text stream
        textStream = QTextStream(file)

        # Read lines
        data = []
        while not textStream.atEnd():
            data.append(textStream.readLine())

        # Load lines
        self.loadData(data)
Пример #14
0
 def load_ui(self):
     loader = QUiLoader()
     path = os.fspath(Path(__file__).resolve().parent / "form.ui")
     ui_file = QFile(path)
     ui_file.open(QFile.ReadOnly)
     loader.load(ui_file, self)
     ui_file.close()
 def load_items(self, file_name: str, gim):
     input_file = QFile(file_name)
     if input_file.open(QIODevice.ReadOnly):
         input_stream = QDataStream(input_file)
         while not input_stream.atEnd():
             item_type = input_stream.readInt8()
             item_name = input_stream.readString()
             if item_type == QGraphicsRectItem.type(QGraphicsRectItem()):
                 rect = QGraphicsRectItem()
                 self.input_to_rect_item(input_stream, rect)
                 self.scene.addItem(rect)
                 gim.append_shape(rect, rect.type(), rect.pen(),
                                  rect.brush())
                 print("Rectangle loaded")
             elif item_type == QGraphicsEllipseItem.type(
                     QGraphicsEllipseItem()):
                 ellipse = QGraphicsEllipseItem()
                 self.input_to_rect_item(input_stream, ellipse)
                 self.scene.addItem(ellipse)
                 gim.append_shape(ellipse, ellipse.type(), ellipse.pen(),
                                  ellipse.brush())
                 print("Ellipse loaded")
             elif item_type == QGraphicsPolygonItem.type(
                     QGraphicsPolygonItem()):
                 polygon = QGraphicsPolygonItem()
                 self.input_to_polygon_item(input_stream, polygon)
                 self.scene.addItem(polygon)
                 gim.append_shape(polygon, polygon.type(), polygon.pen(),
                                  polygon.brush())
                 print("l polygon")
             elif item_type == QGraphicsLineItem.type(QGraphicsLineItem()):
                 line = QGraphicsLineItem()
                 self.input_to_line_item(input_stream, line)
                 self.scene.addItem(line)
                 gim.append_shape(line, line.type(), line.pen(),
                                  QBrush(Qt.black))
                 print("Line loaded")
             gim.return_shapes()[-1].name = item_name
             ItemsInputOutput.set_item_flags(self.scene)
         input_file.close()
Пример #16
0
def importXmlDOM(filename):
    '''
	The QDomDocument class represents the entire XML document
	The DOM classes that will be used most often are QDomNode , QDomDocument , QDomElement and QDomText
	The DOM tree might end up reserving a lot of memory if the XML document is big. For such documents,
	the QXmlStreamReaderor the QXmlQuery classes might be better solutions.
	:param filename: filename_path
	:return: document
	'''

    document = QDomDocument()
    error = None
    file = None
    try:
        # instantiate a device(QFile) specified by filename
        file = QFile(filename)
        if not file.open(QIODevice.ReadOnly):
            raise IOError(str(file.errorString()))
        # setContent parses an XML file and creates the DOM tree that represents the document
        if not document.setContent(file):
            raise ValueError("could not parse XML")
    except (IOError, OSError, ValueError) as e:
        error = "Failed to import: {0}".format(e)
    finally:
        if file is not None:
            file.close()
        if error is not None:
            print(error)
        return document
 def save_items(self, file_name: str, gim):
     output_file = QFile(file_name)
     if output_file.open(QIODevice.WriteOnly):
         output_stream = QDataStream(output_file)
         scene_items_reverse = self.scene.items()
         scene_items_reverse.reverse()
         for item in scene_items_reverse:
             output_stream.writeInt8(item.type())
             output_stream.writeString(gim.get_name_from_reference(item))
             if type(item) == QGraphicsRectItem:
                 self.output_from_rect_item(output_stream, item)
                 print("rect saved!")
             elif type(item) == QGraphicsEllipseItem:
                 self.output_from_rect_item(output_stream, item)
                 print("ellipse saved!")
             elif type(item) == QGraphicsPolygonItem:
                 self.output_from_polygon_item(output_stream, item)
                 print("polygon saved!")
             elif type(item) == QGraphicsLineItem:
                 self.output_from_line_item(output_stream, item)
                 print("line saved!")
         output_file.close()
Пример #18
0
def load_ui(ui_file, parent=None):
    loader = QUiLoader()
    file = QFile(ui_file)
    file.open(QFile.ReadOnly)
    myWidget = loader.load(file, None)
    myWidget.show()
    file.close()
    myWidget.show()
    return myWidget
Пример #19
0
    def setPersepolisColorScheme(self, color_scheme):
        self.persepolis_color_scheme = color_scheme
        if color_scheme == 'Dark Fusion':
            dark_fusion = DarkFusionPalette()
            self.setPalette(dark_fusion)
            file = QFile(":/dark_style.qss")
            file.open(QFile.ReadOnly | QFile.Text)
            stream = QTextStream(file)
            self.setStyleSheet(stream.readAll())

        elif color_scheme == 'Light Fusion':
            light_fusion = LightFusionPalette()
            self.setPalette(light_fusion)
            file = QFile(":/light_style.qss")
            file.open(QFile.ReadOnly | QFile.Text)
            stream = QTextStream(file)
            self.setStyleSheet(stream.readAll())
Пример #20
0
 def inflate(self, ui_path: Optional[str] = None) -> MainWindow:
     super().inflate(ui_path)
     with importlib.resources.path(__package__,
                                   'main_window.ui') as main_window_ui:
         main_window_ui_qfile: QFile = QFile(str(main_window_ui))
         if not main_window_ui_qfile.open(QIODevice.ReadOnly):
             raise RuntimeError(
                 f"Cannot open {main_window_ui}: {main_window_ui_qfile.errorString()}"
             )
         qui_loader: QUiLoader = QUiLoader()
         self.main_window = qui_loader.load(main_window_ui_qfile)
         main_window_ui_qfile.close()
         if not self.main_window:
             raise RuntimeError(qui_loader.errorString())
     return self
Пример #21
0
 def action_rename_event(self):
     """
     Prompts the user to enter a new name and changes the selected file's
     name.
     """
     file_name = self.table_view.currentIndex().data()
     new_file_name, ok = QInputDialog().getText(self,
                                                'Rename ' + file_name,
                                                'New name:')
     if (ok):
         if (new_file_name):
             QFile().rename(os.path.join(self.current_path, file_name),
                            os.path.join(self.current_path, new_file_name))
         else:
             dialog = utility.message_dialog('Please enter the new name '
                                             + 'for the file.',
                                             QMessageBox.Warning)
             dialog.exec()
Пример #22
0
def load_ui(name, custom_widgets=[], parent=None):
    loader = QUiLoader()
    for cw in custom_widgets:
        loader.registerCustomWidget(cw)
    path = os.path.join(os.path.dirname(__file__), "ui", name)
    ui_file = QFile(path)
    if not ui_file.open(QFile.ReadOnly):
        logging.critical("Cannot open {}: {}".format(path,
                                                     ui_file.errorString()))
        sys.exit(-1)
    ui = loader.load(ui_file, parent)
    ui_file.close()
    return ui
    def inflate(self, ui_path: Optional[str] = None) -> MainWindow:
        super().inflate(ui_path)
        with importlib.resources.path(__package__, 'main_window.ui') as main_window_ui:
            main_window_ui_qfile: QFile = QFile(str(main_window_ui))
            if not main_window_ui_qfile.open(QIODevice.ReadOnly):
                raise RuntimeError(f"Cannot open {main_window_ui}: {main_window_ui_qfile.errorString()}")
            qui_loader: QUiLoader = QUiLoader()
            self.main_window = qui_loader.load(main_window_ui_qfile)
            main_window_ui_qfile.close()
            if not self.main_window:
                raise RuntimeError(qui_loader.errorString())

        self.add_push_button = getattr(self.main_window, 'push_button_add')
        self.remove_push_button = getattr(self.main_window, 'push_button_remove')
        self.save_push_button = getattr(self.main_window, 'push_button_save')
        self.datum_list_widget = getattr(self.main_window, 'list_widget_datum_list')
        self.record_no_line_edit = getattr(self.main_window, 'line_edit_record_no')
        self.lab_no_line_edit = getattr(self.main_window, 'line_edit_lab_no')
        self.mcv_line_edit = getattr(self.main_window, 'line_edit_mcv')
        self.wbc_line_edit = getattr(self.main_window, 'line_edit_wbc')
        self.hb_line_edit = getattr(self.main_window, 'line_edit_hb')
        self.hct_line_edit = getattr(self.main_window, 'line_edit_hct')
        return self
Пример #24
0
    def __init__(self, persepolis_setting):
        super().__init__(persepolis_setting)

        self.persepolis_setting = persepolis_setting

        # setting window size and position
        size = self.persepolis_setting.value('AboutWindow/size',
                                             QSize(545, 375))
        position = self.persepolis_setting.value('AboutWindow/position',
                                                 QPoint(300, 300))

        # read translators.txt files.
        # this file contains all translators.
        f = QFile(':/translators.txt')

        f.open(QIODevice.ReadOnly | QFile.Text)
        f_text = QTextStream(f).readAll()
        f.close()

        self.translators_textEdit.insertPlainText(f_text)

        self.resize(size)
        self.move(position)
Пример #25
0
    def __init__(self):
        super(MainWindow, self).__init__()
        loader = QUiLoader()
        path = os.path.join(os.path.dirname(__file__), "main_window.ui")
        ui_file = QFile(path)
        ui_file.open(QFile.ReadOnly)
        self.window = loader.load(ui_file, self)
        ui_file.close()
        self.week = 1
        self.sel_block = (-1, -1)
        self.group = 'WCY18IJ5S1'
        self.blocks = list()
        self.loading_widget = None

        self.main_widget = self.window.findChild(QWidget, 'college_schedule')
        self.table_widget = self.window.findChild(QTableWidget, 'table_widget')
        self.next_week = self.window.findChild(QPushButton, 'next_week')
        self.previous_week = self.window.findChild(QPushButton, 'previous_week')
        self.save_note_button = self.window.findChild(QPushButton, 'save_note')
        self.group_box = self.window.findChild(QComboBox, 'group_box')
        self.download_button = self.window.findChild(QPushButton, 'download_data')
        self.note = self.window.findChild(QTextEdit, 'note')

        self.next_week.clicked.connect(lambda: self.get_week_click(self.week + 1))
        self.previous_week.clicked.connect(lambda: self.get_week_click(self.week - 1))
        self.save_note_button.clicked.connect(self.save_note_click)
        self.download_button.clicked.connect(lambda: self.load_data(True))
        self.table_widget.cellClicked.connect(self.block_click)
        self.table_widget.cellDoubleClicked.connect(self.block_double_click)
        self.group_box.currentTextChanged.connect(self.group_change)
        self.loading_signal.connect(self.loading_slot)
        self.set_blocks_signal.connect(self.set_blocks_slot)

        self.scraper = Scraper()
        t = threading.Thread(target=lambda: self.scraper.start(self, self.group))
        t.start()
Пример #26
0
    def __init__(self):
        QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
        app = QApplication(sys.argv)
        app.setStyle(QStyleFactory.create('Fusion'))

        ui_file_name = '%s.ui' % Path(__file__).stem
        ui_file = QFile(ui_file_name)
        if not ui_file.open(QIODevice.ReadOnly):
            print('Cannot open %s: %s' % (ui_file_name, ui_file.errorString()))
            sys.exit(-1)

        loader = QUiLoader()
        self.window = loader.load(ui_file)
        ui_file.close()
        if not self.window:
            print(loader.errorString())
            sys.exit(-1)

        self.connect()
        self.setting()

        self.window.show()

        sys.exit(app.exec())
Пример #27
0
SONG_FIELDS = set(
    ('inputFrameRate', 'backgroundColor', 'videoHeight', 'videoWidth',
     'uploadYouTube', 'coverArt', 'videoDescription', 'videoTags',
     'videoTitle', 'videoVisibility', 'fileOutputDir', 'fileOutputName',
     'playlistName', 'commandString'))
ALBUM_FIELDS = set(
    ('albumPlaylist', 'fileOutputDirAlbum', 'fileOutputNameAlbum',
     'uploadYouTube', 'videoDescriptionAlbum', 'videoTagsAlbum',
     'videoTitleAlbum', 'videoVisibilityAlbum'))

QDir.temp().mkdir(APPLICATION)
QRC_TO_FILE_PATH = {
    ":/image/default.jpg":
    QDir.temp().absoluteFilePath("./{}/qrc_default.jpg".format(APPLICATION))
}

for qrc, file in QRC_TO_FILE_PATH.items():
    # unpack resources so ffmpeg can use them
    QFile.copy(qrc, file)


# clear temp dir on exit
def clean_up_images():
    temp_dir = QDir("{}/{}".format(QDir().tempPath(), APPLICATION))
    temp_dir.setNameFilters(["*.cover"])
    for file in temp_dir.entryList():
        temp_dir.remove(file)


atexit.register(clean_up_images)
Пример #28
0
import sys
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QFile, QIODevice

app = QApplication(sys.argv)
ui_file_name = "file_manager_design_1.ui"
ui_file = QFile(ui_file_name)
loader = QUiLoader()
window = loader.load(ui_file)


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        print(window)

        window.btn_load.clicked.connect(self.check())
        window.btn_file.clicked.connect(self.check())

        window.box_filters.setItemText(0, 'box_filters works')

    def setup(self):
        pass

    def check(self):
        print('btn_load works')
        print('btn_file works')

Пример #29
0
        mp4_command = '''"%s" -y -loop 1 -framerate 2 -i "%s" -i "%s" -c:v libx264 -preset medium -tune stillimage -crf 18 -codec:a aac -strict -2 -b:a 384k -r:a 48000 -shortest -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" %s "%s"''' % (
            'ffmpeg', os.path.join(cwd, spectrogram_filename),
            os.path.join(cwd, master_filename), mp4_metadata,
            os.path.join(cwd, mp4_filename))
        mp4_command = mp4_command.replace('\\', '/')
        print('mp4_command:            ' + mp4_command)
        os.system(mp4_command)
        os.system('rm *wavuntagged.wav')
        os.system('{} {}'.format(soundfile_editor, master_filename))
        print("")
    except:
        traceback.print_exc()


application = QApplication(sys.argv)
ui_file = QFile(piece_ui_filepath)
if not ui_file.open(QIODevice.ReadOnly):
    print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
    sys.exit(-1)
ui_loader = QUiLoader()
main_window = ui_loader.load(ui_file)
print("main_window: {} {}".format(main_window, type(main_window)))
ui_file.close()
if not main_window:
    print(loader.errorString())
    sys.exit(-1)
'''
Each instance of this class encapsulates one Csound control channel along with  
the Qt Widget that manages it. The methods of this class should have cases for 
handling different types of Csound channels (strings or numbers), and 
different types of Qt Widgets.
Пример #30
0
from PySide6.QtWebSockets import QWebSocketServer

from dialog import Dialog
from core import Core
from websocketclientwrapper import WebSocketClientWrapper


if __name__ == '__main__':
    app = QApplication(sys.argv)
    if not QSslSocket.supportsSsl():
        print('The example requires SSL support.')
        sys.exit(-1)
    cur_dir = os.path.dirname(os.path.abspath(__file__))
    jsFileInfo = QFileInfo(cur_dir + "/qwebchannel.js")
    if not jsFileInfo.exists():
        QFile.copy(":/qtwebchannel/qwebchannel.js",
                   jsFileInfo.absoluteFilePath())

    # setup the QWebSocketServer
    server = QWebSocketServer("QWebChannel Standalone Example Server",
                              QWebSocketServer.NonSecureMode)
    if not server.listen(QHostAddress.LocalHost, 12345):
        print("Failed to open web socket server.")
        sys.exit(-1)

    # wrap WebSocket clients in QWebChannelAbstractTransport objects
    clientWrapper = WebSocketClientWrapper(server)

    # setup the channel
    channel = QWebChannel()
    clientWrapper.clientConnected.connect(channel.connectTo)