File size: 3,212 Bytes
2680bd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
from textwrap import wrap
import matplotlib
from matplotlib.patches import Rectangle
import numpy as np 
from matplotlib import pyplot as plt
from matplotlib.axes import Axes
from matplotlib.figure import Figure
import inspect 
from typing import Dict, List, Tuple
import textwrap
import random

class TimelineVisualizer(object):
    LEFT_MARGIN = -50
    def __init__(
        self, 
        ax:Axes, 
        data:Dict[str, List[Tuple[int, int, str]]],
        total_frames:int,
        title:str|None=None
    ):
        '''
        data:
        set_name: [(start_frame, end_frame, text)]
        '''
        self.ax = ax 
        self.data = data 
        self.title = title
        self.total_frames = total_frames

    def get_text_wrapping_width(self, fontsize, x_left, x_right):
        fig:Figure = self.ax.get_figure()
        dpi = fig.get_dpi()

        pixel_coords = self.ax.transData.transform([(x_left, 0), (x_right, 0)])
        available_width_pixels = pixel_coords[1, 0] - pixel_coords[0, 0]

        avg_char_width_pixels = fontsize * 0.5 * (dpi / 72.0) 

        if avg_char_width_pixels <= 0: 
            return 1 

        wrap_width = int(available_width_pixels / avg_char_width_pixels)
        return wrap_width
    @staticmethod
    def get_random_light_color():
        min_val = 180
    
        # 在阈值和255之间生成随机整数
        r = random.randint(min_val, 255)
        g = random.randint(min_val, 255)
        b = random.randint(min_val, 255)
        
        # Matplotlib 的颜色元组格式要求值为 0-1 的浮点数,
        # 因此需要将 0-255 的整数进行归一化处理(除以255)
        return (r / 255.0, g / 255.0, b / 255.0)
        
    def initialize_ax(self):
        self.ax.set_xlim(self.LEFT_MARGIN, self.total_frames)
        self.ax.set_ylim(-0.8 * len(self.data), 1)
        self.ax.set_xticks(range(0, self.total_frames + 1, 50))
        self.ax.set_yticks([])
        self.ax.set_xlabel("Frames", fontsize=10)
        self.ax.spines[['left', 'right', 'top']].set_visible(False)

        self.vline = self.ax.axvline(0, color='k', lw=2)

        for set_index, (set_name, set_segments) in enumerate(self.data.items()):
            wrapped_set_name = textwrap.fill(set_name, width=13)
            self.ax.text(self.LEFT_MARGIN + 1, -0.8 * set_index + 0.4, wrapped_set_name, ha='left', va='center', weight='bold', fontsize=9)
            for start_frame, end_frame, text in set_segments:
                face_color = TimelineVisualizer.get_random_light_color()
                self.ax.add_patch(Rectangle((start_frame, -0.8 * set_index), end_frame - start_frame, 0.8, ec='k', lw=0.5, facecolor=face_color))
                auto_width = self.get_text_wrapping_width(9, start_frame, end_frame)
                wrapped_text = textwrap.fill(text, width=auto_width)
                self.ax.text((start_frame + end_frame) / 2, -0.8 * set_index + 0.4, wrapped_text, ha='center', va='center', fontsize=9)

    def update(self, frame):
        self.vline.set_xdata((frame, frame))


if __name__ == "__main__":
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    print(inspect.getfile(type(ax)))
    print(isinstance(ax, Axes))