aboutsummaryrefslogtreecommitdiff
path: root/src/component.py
blob: 306072c5bd6d70b86b2d935acb2e770dd6a771a5 (plain)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
'''
    Base classes for components to import.
'''
from PyQt5 import uic, QtCore, QtWidgets
import os


class Component(QtCore.QObject):
    '''
        A class for components to inherit. Read comments for documentation
        on making a valid component. All subclasses must implement this signal:
            modified = QtCore.pyqtSignal(int, bool)
    '''

    def __init__(self, moduleIndex, compPos, core):
        super().__init__()
        self.currentPreset = None
        self.canceled = False
        self.moduleIndex = moduleIndex
        self.compPos = compPos
        self.core = core

    def __str__(self):
        return self.__doc__

    def version(self):
        # change this number to identify new versions of a component
        return 1

    def cancel(self):
        # please stop any lengthy process in response to this variable
        self.canceled = True

    def reset(self):
        self.canceled = False

    def update(self):
        self.modified.emit(self.compPos, self.savePreset())
        # read your widget values, then call super().update()

    def loadPreset(self, presetDict, presetName):
        '''
            Subclasses take (presetDict, presetName=None) as args.
            Must use super().loadPreset(presetDict, presetName) first,
            then update self.page widgets using the preset dict.
        '''
        self.currentPreset = presetName \
            if presetName is not None else presetDict['preset']

    def preFrameRender(self, **kwargs):
        '''
            Triggered only before a video is exported (video_thread.py)
                self.worker = the video thread worker
                self.completeAudioArray = a list of audio samples
                self.sampleSize = number of audio samples per video frame
                self.progressBarUpdate = signal to set progress bar number
                self.progressBarSetText = signal to set progress bar text
            Use the latter two signals to update the MainWindow if needed
            for a long initialization procedure (i.e., for a visualizer)

            Return a list of properties to signify if your component is
            non-animated ('static') or returns sound ('audio').
        '''
        for var, value in kwargs.items():
            exec('self.%s = value' % var)

    def command(self, arg):
        '''
            Configure a component using argument from the commandline.
            Use super().command(arg) at the end of a subclass's method,
            if no arguments are found in that method first
        '''
        if arg.startswith('preset='):
            _, preset = arg.split('=', 1)
            path = os.path.join(self.core.getPresetDir(self), preset)
            if not os.path.exists(path):
                print('Couldn\'t locate preset "%s"' % preset)
                quit(1)
            else:
                print('Opening "%s" preset on layer %s' % (
                    preset, self.compPos)
                )
                self.core.openPreset(path, self.compPos, preset)
        else:
            print(
                self.__doc__, 'Usage:\n'
                'Open a preset for this component:\n'
                '    "preset=Preset Name"')
            self.commandHelp()
            quit(0)

    def commandHelp(self):
        '''Print help text for this Component's commandline arguments'''

    def pickColor(self):
        '''
            Use color picker to get color input from the user,
            and return this as an RGB string and QPushButton stylesheet.
            In a subclass apply stylesheet to any color selection widgets
        '''
        dialog = QtWidgets.QColorDialog()
        dialog.setOption(QtWidgets.QColorDialog.ShowAlphaChannel, True)
        color = dialog.getColor()
        if color.isValid():
            RGBstring = '%s,%s,%s' % (
                str(color.red()), str(color.green()), str(color.blue()))
            btnStyle = "QPushButton{background-color: %s; outline: none;}" \
                % color.name()
            return RGBstring, btnStyle
        else:
            return None, None

    def RGBFromString(self, string):
        '''Turns an RGB string like "255, 255, 255" into a tuple'''
        try:
            tup = tuple([int(i) for i in string.split(',')])
            if len(tup) != 3:
                raise ValueError
            for i in tup:
                if i > 255 or i < 0:
                    raise ValueError
            return tup
        except:
            return (255, 255, 255)

    def loadUi(self, filename):
        return uic.loadUi(os.path.join(self.core.componentsPath, filename))

    '''
    ### Reference methods for creating a new component
    ### (Inherit from this class and define these)

    def widget(self, parent):
        self.parent = parent
        page = uic.loadUi(os.path.join(
            os.path.dirname(os.path.realpath(__file__)), 'example.ui'))
        # --- connect widget signals here ---
        self.page = page
        return page

    def update(self):
        self.parent.drawPreview()
        super().update()

    def previewRender(self, previewWorker):
        width = int(previewWorker.core.settings.value('outputWidth'))
        height = int(previewWorker.core.settings.value('outputHeight'))
        from frame import BlankFrame
        image = BlankFrame(width, height)
        return image

    def frameRender(self, layerNo, frameNo):
        audioArrayIndex = frameNo * self.sampleSize
        width = int(self.worker.core.settings.value('outputWidth'))
        height = int(self.worker.core.settings.value('outputHeight'))
        from frame import BlankFrame
        image = BlankFrame(width, height)
        return image

    def audio(self):
        \'''
            Return audio to mix into master as a string (path to audio file),
            or an object that returns raw audio data [future feature].
        \'''

    @classmethod
    def names(cls):
        \'''
            Alternative names for renaming a component between project files.
        \'''
        return []
    '''


class BadComponentInit(Exception):
    def __init__(self, arg, name):
        string = '''################################
Mandatory argument "%s" not specified
  in %s instance initialization
###################################'''
        print(string % (arg, name))
        quit()