2 #Copyright 2009 - 2010 Paul Hänsch
4 #This file is part of Viper.
6 #Viper is free software: you can redistribute it and/or modify
7 #it under the terms of the GNU General Public License as published by
8 #the Free Software Foundation, either version 3 of the License, or
9 #(at your option) any later version.
11 #Viper is distributed in the hope that it will be useful,
12 #but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 #GNU General Public License for more details.
16 #You should have received a copy of the GNU General Public License
17 #along with Viper. If not, see <http://www.gnu.org/licenses/>
23 class VChunk(LabelFrame, Chunk):
25 Visual video chunk widget
27 This widget represents a video chunk and provides controls for
31 schedule (string) - commands to the owning object, values can be
32 mv left : move this chunk left
33 mv right: move this chunk right
34 copy : double this chunk
35 apply : realize new video properties
36 delete : remove this chunk
38 subgets - dictionary of widgets forming this
40 played (Tk.Boolean) - indicates status of the play checkbox (use ...play.get())
41 marked (Tk.Boolean) - indicates status of the mark checkbox (use ...mark.get())
44 def __init__(self, master = None, file = '', start = None, frames = None, play = True):
48 chunk - a Chunk object to represent
49 master - the holding widget
54 self.played = BooleanVar(value = True)
55 self.marked = BooleanVar(value = True)
57 tooltips = Pmw.Balloon()
59 Chunk.__init__(self, file, start, frames)
60 LabelFrame.__init__(self, master = master, text =
61 file.split('/')[-1] + ':' + str(start) + ':' + str(frames)
63 self.subgets['video'] = Frame(self, height=90, width=120, bg = '#000000')
64 self.subgets['video'].grid(column=0, row=0, rowspan=4, sticky=W)
66 self.subgets['sb_m_start'] = Spinbox(self, from_ = 0, increment = 1, width = 3,
67 to = int(float(self.videoprops['ID_LENGTH']) / 60))
68 self.subgets['sb_m_start'].delete(0, END)
69 self.subgets['sb_m_start'].insert(0, int(self.start / 60))
70 self.subgets['sb_m_start'].grid(column=2, row=1, rowspan=1)
72 self.subgets['sb_s_start'] = Spinbox(self, from_ = 0, increment = 1, width = 2, to = 60)
73 self.subgets['sb_s_start'].delete(0, END)
74 self.subgets['sb_s_start'].insert(0, int(self.start) % 60)
75 self.subgets['sb_s_start'].grid(column=3, row=1, rowspan=1)
77 self.subgets['sb_f_start'] = Spinbox(self, from_ = 0, increment = 1, width = 2,
78 to = int(float(self.videoprops['ID_VIDEO_FPS'])))
79 self.subgets['sb_f_start'].delete(0, END)
80 self.subgets['sb_f_start'].insert(0, int(self.start *
81 float(self.videoprops['ID_VIDEO_FPS'])
82 ) % int(float(self.videoprops['ID_VIDEO_FPS'])))
83 self.subgets['sb_f_start'].grid(column=4, row=1, rowspan=1)
85 tooltips.bind(self.subgets['sb_m_start'],
86 'Define the staring minute of the video chunk in the source video file.')
87 tooltips.bind(self.subgets['sb_s_start'],
88 'Define the starting second of the video chunk in the source video file.')
89 tooltips.bind(self.subgets['sb_f_start'],
90 'Define the starting frame of the video chunk in the source video file.')
92 frames = int(self.start * float(self.videoprops['ID_VIDEO_FPS'])) + self.frames
93 self.subgets['sb_m_stop'] = Spinbox(self, from_ = 0, increment = 1, width = 3,
94 to = int(float(self.videoprops['ID_LENGTH']) / 60))
95 self.subgets['sb_m_stop'].delete(0, END)
96 self.subgets['sb_m_stop'].insert(0, int(frames / float(self.videoprops['ID_VIDEO_FPS'])
98 self.subgets['sb_m_stop'].grid(column=2, row=2, rowspan=1)
100 self.subgets['sb_s_stop'] = Spinbox(self, from_ = 0, increment = 1, width = 2, to = 60)
101 self.subgets['sb_s_stop'].delete(0, END)
102 self.subgets['sb_s_stop'].insert(0, int(frames / float(self.videoprops['ID_VIDEO_FPS'])
104 self.subgets['sb_s_stop'].grid(column=3, row=2, rowspan=1)
106 self.subgets['sb_f_stop'] = Spinbox(self, from_ = 0, increment = 1, width = 2,
107 to = int(float(self.videoprops['ID_VIDEO_FPS'])))
108 self.subgets['sb_f_stop'].delete(0, END)
109 self.subgets['sb_f_stop'].insert(0, frames % int(float(self.videoprops['ID_VIDEO_FPS'])))
110 self.subgets['sb_f_stop'].grid(column=4, row=2, rowspan=1)
112 tooltips.bind(self.subgets['sb_m_stop'],
113 'Define the stop minute of the video chunk in the source video file.')
114 tooltips.bind(self.subgets['sb_s_stop'],
115 'Define the stop second of the video chunk in the source video file.')
116 tooltips.bind(self.subgets['sb_f_stop'],
117 'Define the stop frame of the video chunk in the source video file.')
119 self.subgets['b_apply'] = Button(self, text = 'Set', command=self.chprops)
120 self.subgets['b_apply'].grid(column=5, row=1, rowspan = 2, sticky=W)
121 tooltips.bind(self.subgets['b_apply'],
122 'Apply changes of start and end time.')
124 self.subgets['b_mvbw'] = Button(self, text = '<-', command=self.mvbw)
125 self.subgets['b_mvbw'].grid(column = 1, row = 3, rowspan = 1, columnspan = 2, sticky=E)
126 tooltips.bind(self.subgets['b_mvbw'],
127 'Swap this chunk with its left neighbour (move it left).')
129 self.subgets['b_copy'] = Button(self, text = 'Copy', command=self.copy)
130 self.subgets['b_copy'].grid(column = 3, columnspan = 2, row = 3, rowspan = 1)
131 tooltips.bind(self.subgets['b_copy'],
132 'Create another chunk with the same properties as this one.')
134 self.subgets['b_mvfw'] = Button(self, text = '->', command=self.mvfw)
135 self.subgets['b_mvfw'].grid(column = 5, row = 3, rowspan = 1, sticky=W)
136 tooltips.bind(self.subgets['b_mvfw'],
137 'Swap this chunk with its right neighbour (move it right).')
139 self.subgets['c_play'] = Checkbutton(self, text='Play', offvalue=False,
140 onvalue=True, variable = self.played)
141 # self.subgets['c_play'].select()
142 self.subgets['c_play'].grid(column=1, columnspan = 2, row=0)
143 tooltips.bind(self.subgets['c_play'],
144 'Mark this chunk for playback in main window.')
146 self.subgets['c_mark'] = Checkbutton(self, text='Mark', offvalue=False,
147 onvalue=True, variable = self.marked)
148 # self.subgets['c_mark'].select()
149 self.subgets['c_mark'].grid(column=3, columnspan = 2, row=0)
150 tooltips.bind(self.subgets['c_mark'],
151 'Mark this chunk for application of choosen filters and effects.')
153 self.subgets['b_del'] = Button(self, text='Del', command = self.delete)
154 self.subgets['b_del'].grid(column=5, row=0, sticky=NE)
155 tooltips.bind(self.subgets['b_del'],
156 'Remove this chunk from the project.')
159 def load_filters(self, dbcur, num):
161 Load filter list from database
163 db - sqlite database cursor
164 num - database primary key id of this chunk
167 dbcur.execute('SELECT iorder FROM filters ' +
168 'WHERE chunk = ' + str(num) + ' ORDER BY iorder')
169 for filter in dbcur.fetchall():
170 dbcur.execute('SELECT line FROM filter_lines WHERE filter = ' +
171 str(filter[0]) + ' ORDER BY iorder')
173 for item in dbcur.fetchall():
175 self.add_filter(line)
179 Scheduler for internal use
181 self.schedule = 'delete'
185 Scheduler for internal use
187 self.schedule = 'mv left'
191 Scheduler for internal use
193 self.schedule = 'mv right'
197 Scheduler for internal use
199 self.schedule = 'copy'
201 def store(self, dbcur):
203 Store chunk to database
205 The tables "chunks", "filters" and "filter_lines"
206 must be prepared in database.
208 dbcur - sqlite database cursor
210 if self.played.get(): played = '1'
212 if self.marked.get(): marked = '1'
214 dbcur.execute('INSERT INTO chunks (filename, start, frames, played, marked) ' +
215 'VALUES ("' + self.videofile + '", ' + str(self.start) + ', ' +
216 str(self.frames) + ', ' + played + ', ' + marked + ');')
218 dbcur.execute('SELECT last_insert_rowid();')
219 chunk_id = str(dbcur.fetchone()[0])
221 for filter in self.filters:
222 dbcur.execute('INSERT INTO filters (chunk) VALUES (' + chunk_id + ');')
223 dbcur.execute('SELECT last_insert_rowid();')
224 filter_id = str(dbcur.fetchone()[0])
226 dbcur.execute('INSERT INTO filter_lines (line, filter) ' +
227 'VALUES ("' + line + '", ' + filter_id + ')')
231 Changes 'start' and 'frames' property of chunk.
233 This method is called when the Apply (Set) button is pressed.
234 The methos also schedules the apply event
237 start = (int(self.subgets['sb_m_start'].get()) * 60 +
238 int(self.subgets['sb_s_start'].get()) +
239 int(self.subgets['sb_f_start'].get()) / float(self.videoprops['ID_VIDEO_FPS'])
241 frames = int((int(self.subgets['sb_m_stop'].get()) * 60 +
242 int(self.subgets['sb_s_stop'].get())) *
243 float(self.videoprops['ID_VIDEO_FPS']) +
244 int(self.subgets['sb_f_stop'].get()) -
245 (start * float(self.videoprops['ID_VIDEO_FPS']))
248 self.option(start=start, frames=frames)
249 if self.animate: self.play()
250 self.config(text = self.videofile.split('/')[-1] + ':' + str(start) + ':' + str(frames))
252 print 'could not parse props'
253 self.schedule = 'apply'
256 Flic.play(self, self.subgets['video'].winfo_id(), width=120, height=90)