コード例 #1
0
    def __init__(self, main: AbstractMainWindow) -> None:
        super().__init__(main, 'Benchmark')

        self.setup_ui()

        self.running = False
        self.unsequenced = False
        self.buffer: Deque[Future] = deque()
        self.run_start_time = 0.0
        self.start_frame  = Frame(0)
        self.  end_frame  = Frame(0)
        self.total_frames = FrameInterval(0)
        self.frames_left  = FrameInterval(0)

        self.sequenced_timer = Qt.QTimer()
        self.sequenced_timer.setTimerType(Qt.Qt.PreciseTimer)
        self.sequenced_timer.setInterval(0)

        self.update_info_timer = Qt.QTimer()
        self.update_info_timer.setTimerType(Qt.Qt.PreciseTimer)
        self.update_info_timer.setInterval(
            self.main.BENCHMARK_REFRESH_INTERVAL)

        self. start_frame_control.valueChanged.connect(lambda value: self.update_controls(start=      value))
        self.  start_time_control.valueChanged.connect(lambda value: self.update_controls(start=Frame(value)))
        self.   end_frame_control.valueChanged.connect(lambda value: self.update_controls(  end=      value))
        self.    end_time_control.valueChanged.connect(lambda value: self.update_controls(  end=Frame(value)))
        self.total_frames_control.valueChanged.connect(lambda value: self.update_controls(total=              value))
        self.  total_time_control.valueChanged.connect(lambda value: self.update_controls(total=FrameInterval(value)))
        self.   prefetch_checkbox.stateChanged.connect(self.on_prefetch_changed)
        self.    run_abort_button.     clicked.connect(self.on_run_abort_pressed)
        self.     sequenced_timer.     timeout.connect(self._request_next_frame_sequenced)
        self.   update_info_timer.     timeout.connect(self.update_info)

        set_qobject_names(self)
コード例 #2
0
    def __init__(self, main: AbstractMainWindow) -> None:
        from concurrent.futures import Future

        super().__init__(main, 'Playback')
        self.setup_ui()

        self.play_buffer: Deque[Future] = deque()
        self.play_timer = Qt.QTimer()
        self.play_timer.setTimerType(Qt.Qt.PreciseTimer)
        self.play_timer.timeout.connect(self._show_next_frame)

        self.fps_history: Deque[int] = deque(
            [],
            int(self.main.FPS_AVERAGING_WINDOW_SIZE) + 1)
        self.current_fps = 0.0
        self.fps_timer = Qt.QTimer()
        self.fps_timer.setTimerType(Qt.Qt.PreciseTimer)
        self.fps_timer.timeout.connect(
            lambda: self.fps_spinbox.setValue(self.current_fps))

        self.play_start_time: Optional[int] = None
        self.play_start_frame = Frame(0)
        self.play_end_time = 0
        self.play_end_frame = Frame(0)

        self.play_pause_button.clicked.connect(self.on_play_pause_clicked)
        self.seek_to_prev_button.clicked.connect(self.seek_to_prev)
        self.seek_to_next_button.clicked.connect(self.seek_to_next)
        self.seek_n_frames_b_button.clicked.connect(self.seek_n_frames_b)
        self.seek_n_frames_f_button.clicked.connect(self.seek_n_frames_f)
        self.seek_to_start_button.clicked.connect(self.seek_to_start)
        self.seek_to_end_button.clicked.connect(self.seek_to_end)
        self.seek_frame_control.valueChanged.connect(
            self.on_seek_frame_changed)
        self.seek_time_control.valueChanged.connect(self.on_seek_time_changed)
        self.fps_spinbox.valueChanged.connect(self.on_fps_changed)
        self.fps_reset_button.clicked.connect(self.reset_fps)
        self.fps_unlimited_checkbox.stateChanged.connect(
            self.on_fps_unlimited_changed)

        add_shortcut(Qt.Qt.Key_Space, self.play_pause_button.click)
        add_shortcut(Qt.Qt.Key_Left, self.seek_to_prev_button.click)
        add_shortcut(Qt.Qt.Key_Right, self.seek_to_next_button.click)
        add_shortcut(Qt.Qt.SHIFT + Qt.Qt.Key_Left,
                     self.seek_n_frames_b_button.click)
        add_shortcut(Qt.Qt.SHIFT + Qt.Qt.Key_Right,
                     self.seek_n_frames_f_button.click)
        add_shortcut(Qt.Qt.Key_Home, self.seek_to_start_button.click)
        add_shortcut(Qt.Qt.Key_End, self.seek_to_end_button.click)

        set_qobject_names(self)
コード例 #3
0
    def import_matroska_xml_chapters(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports chapters as scenes.
        Preserve end time and text if they're present.
        '''
        from xml.etree import ElementTree

        timestamp_pattern = re.compile(r'(\d{2}):(\d{2}):(\d{2}(?:\.\d{3})?)')

        try:
            root = ElementTree.parse(str(path)).getroot()
        except ElementTree.ParseError as exc:
            logging.warning(
                f'Scening import: error occured'
                ' while parsing \'{path.name}\':')
            logging.warning(exc.msg)
            return
        for chapter in root.iter('ChapterAtom'):
            start_element = chapter.find('ChapterTimeStart')
            if start_element is None or start_element.text is None:
                continue
            match = timestamp_pattern.match(start_element.text)
            if match is None:
                continue
            start =  Frame(Time(
                hours   =   int(match[1]),
                minutes =   int(match[2]),
                seconds = float(match[3])
            ))

            end = None
            end_element = chapter.find('ChapterTimeEnd')
            if end_element is not None and end_element.text is not None:
                match = timestamp_pattern.match(end_element.text)
                if match is not None:
                    end = Frame(Time(
                        hours   =   int(match[1]),
                        minutes =   int(match[2]),
                        seconds = float(match[3])
                    ))

            label = ''
            label_element = chapter.find('ChapterDisplay/ChapterString')
            if label_element is not None and label_element.text is not None:
                label = label_element.text

            try:
                scening_list.add(start, end, label)
            except ValueError:
                out_of_range_count += 1
コード例 #4
0
    def import_matroska_timestamps_v1(self, path: Path,
                                      scening_list: SceningList,
                                      out_of_range_count: int) -> None:
        '''
        Imports gaps between timestamps as scenes.
        '''
        pattern = re.compile(r'(\d+),(\d+),(\d+(?:\.\d+)?)')

        for match in pattern.finditer(path.read_text()):
            try:
                scening_list.add(Frame(int(match[1])), Frame(int(match[2])),
                                 '{:.3f} fps'.format(float(match[3])))
            except ValueError:
                out_of_range_count += 1
コード例 #5
0
    def import_ass(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports lines as scenes.
        Text is ignored.
        '''
        import pysubs2

        subs = pysubs2.load(str(path))
        for line in subs:
            t_start = Time(milliseconds=line.start)
            t_end   = Time(milliseconds=line.end)
            try:
                scening_list.add(Frame(t_start), Frame(t_end))
            except ValueError:
                out_of_range_count += 1
コード例 #6
0
    def import_vsedit(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports bookmarks as single-frame scenes
        '''

        for bookmark in path.read_text().split(', '):
            scening_list.add(Frame(int(bookmark)))
コード例 #7
0
    def switch_frame(self,
                     frame: Optional[Frame] = None,
                     time: Optional[Time] = None,
                     *,
                     render_frame: bool = True) -> None:
        if frame is not None:
            time = Time(frame)
        elif time is not None:
            frame = Frame(time)
        else:
            logging.debug('switch_frame(): both frame and time is None')
            return
        if frame > self.current_output.end_frame:
            return

        self.current_output.last_showed_frame = frame

        self.timeline.set_position(frame)
        self.toolbars.main.on_current_frame_changed(frame, time)
        for toolbar in self.toolbars:
            toolbar.on_current_frame_changed(frame, time)

        if render_frame:
            self.current_output.graphics_scene_item.setImage(
                self.render_frame(frame))
コード例 #8
0
    def switch_frame(self,
                     pos: Union[Frame, Time],
                     *,
                     render_frame: bool = True) -> None:
        if isinstance(pos, Frame):
            frame = pos
            time = Time(frame)
        elif isinstance(pos, Time):
            frame = Frame(pos)
            time = pos
        else:
            logging.debug('switch_frame(): position is neither Frame nor Time')
            return
        if frame > self.current_output.end_frame:
            return

        self.current_output.last_showed_frame = frame

        self.timeline.set_position(frame)
        self.toolbars.main.on_current_frame_changed(frame, time)
        for toolbar in self.toolbars:
            toolbar.on_current_frame_changed(frame, time)

        if render_frame:
            self.current_output.graphics_scene_item.setImage(
                self.render_frame(frame))
コード例 #9
0
    def import_lwi(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports Key=1 frames as single-frame scenes.
        Ignores everything besides Index=0 video stream.
        '''
        from copy import deepcopy

        AV_CODEC_ID_FIRST_AUDIO = 0x10000
        STREAM_INDEX = 0
        IS_KEY = 1

        pattern = re.compile(r'Index={}.*?Codec=(\d+).*?\n.*?Key=(\d)'.format(
            STREAM_INDEX
        ))

        frame = Frame(0)
        for match in pattern.finditer(path.read_text(),
                                      re.RegexFlag.MULTILINE):
            if int(match[1]) >= AV_CODEC_ID_FIRST_AUDIO:
                frame += FrameInterval(1)
                continue

            if not int(match[2]) == IS_KEY:
                frame += FrameInterval(1)
                continue

            try:
                scening_list.add(deepcopy(frame))
            except ValueError:
                out_of_range_count += 1

            frame += FrameInterval(1)
コード例 #10
0
    def import_celltimes(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports cell times as single-frame scenes
        '''

        for line in path.read_text().splitlines():
            scening_list.add(Frame(int(line)))
コード例 #11
0
    def import_matroska_timestamps_v2(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports intervals of constant FPS as scenes.
        Uses FPS for scene label.
        '''
        timestamps: List[Time] = []
        for line in path.read_text().splitlines():
            try:
                timestamps.append(Time(milliseconds=float(line)))
            except ValueError:
                continue

        if len(timestamps) < 2:
            logging.warning(
                'Scening import: timestamps file contains less than'
                ' 2 timestamps, so there\'s nothing to import.')
            return

        deltas = [
            timestamps[i] - timestamps[i - 1]
            for i in range(1, len(timestamps))
        ]
        scene_delta = deltas[0]
        scene_start = Frame(0)
        scene_end: Optional[Frame] = None
        for i in range(1, len(deltas)):
            if abs(round(float(deltas[i] - scene_delta), 6)) <= 0.000_001:
                continue
            # TODO: investigate, why offset by -1 is necessary here
            scene_end = Frame(i - 1)
            try:
                scening_list.add(
                    scene_start, scene_end,
                    '{:.3f} fps'.format(1 / float(scene_delta)))
            except ValueError:
                out_of_range_count += 1
            scene_start = Frame(i)
            scene_end = None
            scene_delta = deltas[i]

        if scene_end is None:
            try:
                scening_list.add(
                    scene_start, Frame(len(timestamps) - 1),
                    '{:.3f} fps'.format(1 / float(scene_delta)))
            except ValueError:
                out_of_range_count += 1
コード例 #12
0
    def import_tfm(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports TFM's 'OVR HELP INFORMATION'.
        Single combed frames are put into single-frame scenes.
        Frame groups are put into regular scenes.
        Combed probability is used for label.
        '''
        class TFMFrame(Frame):
            mic: Optional[int]

        tfm_frame_pattern = re.compile(r'(\d+)\s\((\d+)\)')
        tfm_group_pattern = re.compile(r'(\d+),(\d+)\s\((\d+(?:\.\d+)%)\)')

        log = path.read_text()

        start_pos = log.find('OVR HELP INFORMATION')
        if start_pos == -1:
            logging.warning(
                'Scening import: TFM log doesn\'t contain'
                '"OVR Help Information" section.')
            return
        log = log[start_pos:]

        tfm_frames: Set[TFMFrame] = set()
        for match in tfm_frame_pattern.finditer(log):
            tfm_frame = TFMFrame(int(match[1]))
            tfm_frame.mic = int(match[2])
            tfm_frames.add(tfm_frame)

        for match in tfm_group_pattern.finditer(log):
            try:
                scene = scening_list.add(
                    Frame(int(match[1])),
                    Frame(int(match[2])),
                    '{} combed'.format(match[3]))
            except ValueError:
                out_of_range_count += 1
                continue

            tfm_frames -= set(range(int(scene.start), int(scene.end) + 1))

        for tfm_frame in tfm_frames:
            try:
                scening_list.add(tfm_frame, label=str(tfm_frame.mic))
            except ValueError:
                out_of_range_count += 1
コード例 #13
0
    def import_cue(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports tracks as scenes.
        Uses TITLE for scene label.
        '''
        from cueparser import CueSheet

        def offset_to_time(offset: str) -> Optional[Time]:
            pattern = re.compile(r'(\d{1,2}):(\d{1,2}):(\d{1,2})')
            match = pattern.match(offset)
            if match is None:
                return None
            return Time(
                minutes      = int(match[1]),
                seconds      = int(match[2]),
                milliseconds = int(match[3]) / 75 * 1000)

        cue_sheet = CueSheet()
        cue_sheet.setOutputFormat('')
        cue_sheet.setData(path.read_text())
        cue_sheet.parse()

        for track in cue_sheet.tracks:
            if track.offset is None:
                continue
            offset = offset_to_time(track.offset)
            if offset is None:
                logging.warning(
                    f'Scening import: INDEX timestamp \'{track.offset}\''
                    ' format isn\'t suported.')
                continue
            start = Frame(offset)

            end = None
            if track.duration is not None:
                end = Frame(offset + TimeInterval(track.duration))

            label = ''
            if track.title is not None:
                label = track.title

            try:
                scening_list.add(start, end, label)
            except ValueError:
                out_of_range_count += 1
コード例 #14
0
 def on_start_frame_changed(self, value: Union[Frame, int]) -> None:
     frame = Frame(value)
     index = self.tableview.selectionModel().selectedRows()[0]
     if not index.isValid():
         return
     index = index.siblingAtColumn(SceningList.START_FRAME_COLUMN)
     if not index.isValid():
         return
     self.scening_list.setData(index, frame, Qt.Qt.UserRole)
コード例 #15
0
 def import_dgi(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
     '''
     Imports IDR frames as single-frame scenes.
     '''
     pattern = re.compile(r'IDR\s\d+\n(\d+):FRM', re.RegexFlag.MULTILINE)
     for match in pattern.findall(path.read_text()):
         try:
             scening_list.add(Frame(match))
         except ValueError:
             out_of_range_count += 1
コード例 #16
0
 def import_qp(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
     '''
     Imports I- and K-frames as single-frame scenes.
     '''
     pattern = re.compile(r'(\d+)\sI|K')
     for match in pattern.findall(path.read_text()):
         try:
             scening_list.add(Frame(int(match)))
         except ValueError:
             out_of_range_count += 1
コード例 #17
0
 def import_xvid(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
     '''
     Imports I-frames as single-frame scenes.
     '''
     for i, line in enumerate(path.read_text().splitlines()):
         if not line.startswith('i'):
             continue
         try:
             scening_list.add(Frame(i - 3))
         except ValueError:
             out_of_range_count += 1
コード例 #18
0
    def __init__(self, main_window: AbstractMainWindow) -> None:
        from vspreview.models import ZoomLevels

        super().__init__(main_window)
        self.setup_ui()

        self.outputs = Outputs()

        self.outputs_combobox.setModel(self.outputs)
        self.frame_spinbox.setMinimum(0)
        self.time_spinbox.setMinimumTime(Qt.QTime())
        self.zoom_levels = ZoomLevels(
            [0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 4.0, 8.0])
        self.zoom_combobox.setModel(self.zoom_levels)
        self.zoom_combobox.setCurrentIndex(3)

        self.save_file_types = {'Single Image (*.png)': self.save_as_png}

        self.outputs_combobox.currentIndexChanged.connect(
            self.main.switch_output)
        self.frame_spinbox.valueChanged.connect(
            lambda f: self.main.on_current_frame_changed(Frame(f)))
        self.time_spinbox.timeChanged.connect(
            lambda qtime: self.main.on_current_frame_changed(
                t=qtime_to_timedelta(qtime)))  # type: ignore
        self.copy_frame_button.clicked.connect(
            self.on_copy_frame_button_clicked)
        self.copy_timestamp_button.clicked.connect(
            self.on_copy_timestamp_button_clicked)
        self.zoom_combobox.currentTextChanged.connect(self.on_zoom_changed)
        self.save_as_button.clicked.connect(self.on_save_as_clicked)
        self.switch_timeline_mode.clicked.connect(
            self.on_switch_timeline_mode_clicked)

        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_1,
                     lambda: self.main.switch_output(0))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_2,
                     lambda: self.main.switch_output(1))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_3,
                     lambda: self.main.switch_output(2))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_4,
                     lambda: self.main.switch_output(3))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_5,
                     lambda: self.main.switch_output(4))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_6,
                     lambda: self.main.switch_output(5))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_7,
                     lambda: self.main.switch_output(6))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_8,
                     lambda: self.main.switch_output(7))
        add_shortcut(Qt.Qt.CTRL + Qt.Qt.Key_9,
                     lambda: self.main.switch_output(8))
コード例 #19
0
 def import_ogm_chapters(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
     '''
     Imports chapters as signle-frame scenes.
     Uses NAME for scene label.
     '''
     pattern = re.compile(
         r'(CHAPTER\d+)=(\d{2}):(\d{2}):(\d{2}(?:\.\d{3})?)\n\1NAME=(.*)',
         re.RegexFlag.MULTILINE)
     for match in pattern.finditer(path.read_text()):
         time = Time(
             hours   =   int(match[2]),
             minutes =   int(match[3]),
             seconds = float(match[4]))
         try:
             scening_list.add(Frame(time), label=match[5])
         except ValueError:
             out_of_range_count += 1
コード例 #20
0
    def import_ses(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None:
        '''
        Imports bookmarks as single-frame scenes
        '''
        import pickle

        with path.open('rb') as f:
            try:
                session = pickle.load(f)
            except pickle.UnpicklingError:
                logging.warning('Scening import: failed to load .ses file.')
                return
        if 'bookmarks' not in session:
            return

        for bookmark in session['bookmarks']:
            scening_list.add(Frame(bookmark[0]))
コード例 #21
0
    def __init__(self, parent: Qt.QWidget) -> None:
        from vspreview.utils import main_window

        super().__init__(parent)
        self.app = Qt.QApplication.instance()
        self.main = main_window()

        self._mode = self.Mode.TIME

        self.rect_f = Qt.QRectF()

        self.end_t = Time(seconds=1)
        self.end_f = Frame(1)

        self.notch_interval_target_x = round(75 * self.main.display_scale)
        self.notch_height = round(6 * self.main.display_scale)
        self.font_height = round(10 * self.main.display_scale)
        self.notch_label_interval = round(-1 * self.main.display_scale)
        self.notch_scroll_interval = round(2 * self.main.display_scale)
        self.scroll_height = round(10 * self.main.display_scale)

        self.setMinimumSize(self.notch_interval_target_x,
                            round(33 * self.main.display_scale))

        font = self.font()
        font.setPixelSize(self.font_height)
        self.setFont(font)

        self.cursor_x = 0
        # used as a fallback when self.rectF.width() is 0,
        # so cursorX is incorrect
        self.cursor_ftx: Optional[Union[Frame, Time, int]] = None
        # False means that only cursor position'll be recalculated
        self.need_full_repaint = True

        self.toolbars_notches: Dict[AbstractToolbar, Notches] = {}

        self.setAttribute(Qt.Qt.WA_OpaquePaintEvent)
        self.setMouseTracking(True)
コード例 #22
0
 def seek_to_start(self, checked: Optional[bool] = None) -> None:
     self.stop()
     self.main.current_frame = Frame(0)
コード例 #23
0
 def to_frame(self, t: timedelta) -> Frame:
     return Frame(
         round(t.total_seconds() *
               (self.current_output.fps_num / self.current_output.fps_den)))
コード例 #24
0
 def on_seek_frame_changed(self, frame: Union[Frame, int]) -> None:
     frame = Frame(frame)
     qt_silent_call(self.seek_time_spinbox.setTime,
                    timedelta_to_qtime(self.main.to_timedelta(frame)))
コード例 #25
0
    def drawWidget(self, painter: Qt.QPainter) -> None:
        from copy import deepcopy

        from vspreview.utils import strfdelta

        # calculations

        if self.need_full_repaint:
            labels_notches = Notches()
            label_notch_bottom = (self.rect_f.top() + self.font_height +
                                  self.notch_label_interval +
                                  self.notch_height + 5)
            label_notch_top = label_notch_bottom - self.notch_height
            label_notch_x = self.rect_f.left()

            if self.mode == self.Mode.TIME:
                notch_interval_t = self.calculate_notch_interval_t(
                    self.notch_interval_target_x)
                label_format = self.generate_label_format(
                    notch_interval_t, self.end_t)
                label_notch_t = Time()

                while (label_notch_x < self.rect_f.right()
                       and label_notch_t <= self.end_t):
                    line = Qt.QLineF(label_notch_x, label_notch_bottom,
                                     label_notch_x, label_notch_top)
                    labels_notches.add(
                        Notch(deepcopy(label_notch_t), line=line))
                    label_notch_t += notch_interval_t
                    label_notch_x = self.t_to_x(label_notch_t)

            elif self.mode == self.Mode.FRAME:
                notch_interval_f = self.calculate_notch_interval_f(
                    self.notch_interval_target_x)
                label_notch_f = Frame(0)

                while (label_notch_x < self.rect_f.right()
                       and label_notch_f <= self.end_f):
                    line = Qt.QLineF(label_notch_x, label_notch_bottom,
                                     label_notch_x, label_notch_top)
                    labels_notches.add(
                        Notch(deepcopy(label_notch_f), line=line))
                    label_notch_f += notch_interval_f
                    label_notch_x = self.f_to_x(label_notch_f)

            self.scroll_rect = Qt.QRectF(
                self.rect_f.left(),
                label_notch_bottom + self.notch_scroll_interval,
                self.rect_f.width(), self.scroll_height)

            for toolbar, notches in self.toolbars_notches.items():
                if not toolbar.is_notches_visible():
                    continue

                for notch in notches:
                    if isinstance(notch.data, Frame):
                        x = self.f_to_x(notch.data)
                    elif isinstance(notch.data, Time):
                        x = self.t_to_x(notch.data)
                    y = self.scroll_rect.top()
                    notch.line = Qt.QLineF(x, y, x,
                                           y + self.scroll_rect.height() - 1)

        cursor_line = Qt.QLineF(
            self.cursor_x, self.scroll_rect.top(), self.cursor_x,
            self.scroll_rect.top() + self.scroll_rect.height() - 1)

        # drawing

        if self.need_full_repaint:
            painter.fillRect(self.rect_f,
                             self.palette().color(Qt.QPalette.Window))

            painter.setPen(
                Qt.QPen(self.palette().color(Qt.QPalette.WindowText)))
            painter.setRenderHint(Qt.QPainter.Antialiasing, False)
            painter.drawLines([notch.line for notch in labels_notches])

            painter.setRenderHint(Qt.QPainter.Antialiasing)
            for i, notch in enumerate(labels_notches):
                line = notch.line
                anchor_rect = Qt.QRectF(line.x2(),
                                        line.y2() - self.notch_label_interval,
                                        0, 0)

                if self.mode == self.Mode.TIME:
                    time = cast(Time, notch.data)
                    label = strfdelta(time, label_format)
                if self.mode == self.Mode.FRAME:
                    label = str(notch.data)

                if i == 0:
                    rect = painter.boundingRect(
                        anchor_rect, Qt.Qt.AlignBottom + Qt.Qt.AlignLeft,
                        label)
                    if self.mode == self.Mode.TIME:
                        rect.moveLeft(-2.5)
                elif i == (len(labels_notches) - 1):
                    rect = painter.boundingRect(
                        anchor_rect, Qt.Qt.AlignBottom + Qt.Qt.AlignHCenter,
                        label)
                    if rect.right() > self.rect_f.right():
                        rect = painter.boundingRect(
                            anchor_rect, Qt.Qt.AlignBottom + Qt.Qt.AlignRight,
                            label)
                else:
                    rect = painter.boundingRect(
                        anchor_rect, Qt.Qt.AlignBottom + Qt.Qt.AlignHCenter,
                        label)
                painter.drawText(rect, label)

        painter.setRenderHint(Qt.QPainter.Antialiasing, False)
        painter.fillRect(self.scroll_rect, Qt.Qt.gray)

        for toolbar, notches in self.toolbars_notches.items():
            if not toolbar.is_notches_visible():
                continue

            for notch in notches:
                painter.setPen(notch.color)
                painter.drawLine(notch.line)

        painter.setPen(Qt.Qt.black)
        painter.drawLine(cursor_line)

        self.need_full_repaint = False
コード例 #26
0
    def setData(self,
                index: Qt.QModelIndex,
                value: Any,
                role: int = Qt.Qt.EditRole) -> bool:
        from copy import deepcopy

        if not index.isValid():
            return False
        if role not in (Qt.Qt.EditRole, Qt.Qt.UserRole):
            return False

        row = index.row()
        column = index.column()
        scene = deepcopy(self.items[row])

        if column == self.START_FRAME_COLUMN:
            if not isinstance(value, Frame):
                raise TypeError
            if scene.start != scene.end:
                if value > scene.end:
                    return False
                scene.start = value
            else:
                scene.start = value
                scene.end = value
            proper_update = True
        elif column == self.END_FRAME_COLUMN:
            if not isinstance(value, Frame):
                raise TypeError
            if scene.start != scene.end:
                if value < scene.start:
                    return False
                scene.end = value
            else:
                scene.start = value
                scene.end = value
            proper_update = True
        elif column == self.START_TIME_COLUMN:
            if not isinstance(value, Time):
                raise TypeError
            frame = Frame(value)
            if scene.start != scene.end:
                if frame > scene.end:
                    return False
                scene.start = frame
            else:
                scene.start = frame
                scene.end = frame
            proper_update = True
        elif column == self.END_TIME_COLUMN:
            if not isinstance(value, Time):
                raise TypeError
            frame = Frame(value)
            if scene.start != scene.end:
                if frame < scene.start:
                    return False
                scene.end = frame
            else:
                scene.start = frame
                scene.end = frame
            proper_update = True
        elif column == self.LABEL_COLUMN:
            if not isinstance(value, str):
                raise TypeError
            scene.label = value
            proper_update = False

        if proper_update is True:
            i = bisect_right(self.items, scene)
            if i > row:
                i -= 1
            if i != row:
                self.beginMoveRows(self.createIndex(row, 0), row, row,
                                   self.createIndex(i, 0), i)
                del self.items[row]
                self.items.insert(i, scene)
                self.endMoveRows()
            else:
                self.items[index.row()] = scene
                self.dataChanged.emit(index, index)
        else:
            self.items[index.row()] = scene
            self.dataChanged.emit(index, index)
        return True