home *** CD-ROM | disk | FTP | other *** search
- #
- # This file is part of OpenVIP (http://openvip.sourceforge.net)
- #
- # Copyright (C) 2002-2003
- # Michal Dvorak, Jiri Sedlar, Antonin Slavik, Vaclav Slavik, Jozef Smizansky
- #
- # This program is licensed under GNU General Public License version 2;
- # see file COPYING in the top level directory for details.
- #
- # $Id: MainFrame.py,v 1.66 2003/06/18 16:55:08 vaclavslavik Exp $
- #
- # The main application window
- #
- from wxPython.wx import *
- from wxPython.html import *
- from wxPython.htmlhelp import *
-
- import TimelineWidget
- from ObjectPanel import ObjectPanel
- from PreviewFrame import PreviewFrame
- from OutputSettingsDialog import OutputSettingsDialog
- from AboutDialog import AboutDialog
- import globals, logging, model, notify, openvip, render, worker
- import copy
- from conv2 import getBestAudioFormat, getBestVideoFormat
-
- def create(parent):
- return mainFrame(parent)
-
- [wxID_MAINFRAME, wxID_MAINFRAMESPLITTER, wxID_MAINFRAMETOOLBAR,
- ] = map(lambda _init_ctrls: wxNewId(), range(3))
-
- [wxID_MAINFRAMETOOLBARIMPORT, wxID_TOOLBARUNDO, wxID_TOOLBARREDO] = map(lambda init_toolbar: wxNewId(), range(3))
-
- [wxID_FILEMENUNEWTMLN, wxID_MAINFRAMEFILEMENUEXIT, wxID_FILEMENULOADTMLN, wxID_FILEMENUSAVETMLN, wxID_FILEMENUSAVETMLNAS, wxID_FILEMENURENDER, wxID_EDITMENUUNDO, wxID_EDITMENUREDO, wxID_EDITMENUIMPORT, wxID_VIEWMENUFAST, wxID_VIEWMENUONE, wxID_VIEWMENUTHUMB, wxID_VIEWMENUPREV, wxID_EDITMENUREMOVE, wxID_EDITMENUINSTRANS] = map(lambda _init_coll_filemenu_Items: wxNewId(), range(15))
-
- wxID_CUTMODEL = wxNewId()
- wxID_ZOOMBOX = wxNewId()
- wxID_ZOOMPLUS = wxNewId()
- wxID_ZOOMMINUS = wxNewId()
- wxID_ZOOMSLIDER = wxNewId()
- wxID_PREVFRAME = wxNewId()
- wxID_SHOW_HELP = wxNewId()
- wxID_SHOW_ABOUT = wxNewId()
- wxID_ZOOMMIN = wxNewId()
- wxID_ZOOMMAX = wxNewId()
- wxID_ZOOMIN = wxNewId()
- wxID_ZOOMOUT = wxNewId()
-
- ZOOM_LEFT = 0
- ZOOM_RIGHT = 1000
- ZOOM_STEP = 50
-
- class mainFrame(wxFrame):
- """This is the main application window. It displays menu, toolbar,
- timeline widget and object panel. It responds to events from
- menu and toolbar."""
- def init_menuBar(self, parent):
- parent.Append(self.filemenu, title='&File')
- parent.Append(self.editmenu, title='&Edit')
- parent.Append(self.viewmenu, title='&View')
-
- help = wxMenu()
- help.Append(wxID_SHOW_HELP, '&Contents')
- help.AppendSeparator()
- help.Append(wxID_SHOW_ABOUT, '&About ...')
- parent.Append(help, title='&Help')
- EVT_MENU(self, wxID_SHOW_HELP, self.OnHelp)
- EVT_MENU(self, wxID_SHOW_ABOUT, self.OnAbout)
-
- def init_filemenu_items(self, parent):
- parent.Append(wxID_FILEMENUNEWTMLN,"&New timeline\tCtrl-N")
- parent.Append(wxID_FILEMENULOADTMLN,"&Open timeline ...\tCtrl-O")
- self.savetimeline_item=wxMenuItem(parent, wxID_FILEMENUSAVETMLN,"&Save timeline\tCtrl-S")
- parent.AppendItem(self.savetimeline_item)
- self.savetimelineas_item=wxMenuItem(parent, wxID_FILEMENUSAVETMLNAS,"Save timeline &as ...")
- parent.AppendItem(self.savetimelineas_item)
- self.render_item=wxMenuItem(parent, wxID_FILEMENURENDER,"&Render ...")
- parent.AppendItem(self.render_item)
- parent.AppendSeparator()
- parent.Append(helpString='', id=wxID_MAINFRAMEFILEMENUEXIT,
- item='E&xit', kind=wxITEM_NORMAL)
- EVT_MENU(self, wxID_FILEMENUNEWTMLN, self.OnNewTimeline)
- EVT_MENU(self, wxID_FILEMENULOADTMLN, self.OnLoadTimeline)
- EVT_MENU(self, wxID_FILEMENUSAVETMLN, self.OnSaveTimeline)
- EVT_MENU(self, wxID_FILEMENUSAVETMLNAS, self.OnSaveTimelineAs)
- EVT_MENU(self, wxID_FILEMENURENDER, self.OnRender)
- EVT_MENU(self, wxID_MAINFRAMEFILEMENUEXIT, self.OnExit)
-
- def init_editmenu_items(self, parent):
- self.undo_item=wxMenuItem(parent, wxID_EDITMENUUNDO,"&Undo\tCtrl-Z")
- parent.AppendItem(self.undo_item)
- self.redo_item=wxMenuItem(parent, wxID_EDITMENUREDO,"&Redo\tCtrl-Y")
- parent.AppendItem(self.redo_item)
- self.remove_item=wxMenuItem(parent, wxID_EDITMENUREMOVE,"R&emove selected objects\tCtrl-X")
- parent.AppendItem(self.remove_item)
- parent.AppendSeparator()
- self.importclip_item=wxMenuItem(parent, wxID_EDITMENUIMPORT,"&Insert clip ...")
- parent.AppendItem(self.importclip_item)
- self.inserttrans_item=wxMenuItem(parent, wxID_EDITMENUINSTRANS,"Insert &transition ...")
- parent.AppendItem(self.inserttrans_item)
- EVT_MENU(self, wxID_EDITMENUUNDO, self.OnUndo)
- EVT_MENU(self, wxID_EDITMENUREDO, self.OnRedo)
- EVT_MENU(self, wxID_EDITMENUIMPORT, self.OnImport)
- EVT_MENU(self, wxID_EDITMENUREMOVE, self.OnRemove)
- EVT_MENU(self, wxID_EDITMENUINSTRANS, self.OnInsertTrans)
-
- def init_viewmenu_items(self, parent):
- self.onethumb_item=wxMenuItem(parent, wxID_VIEWMENUONE,"&Info mode\tCtrl-1", kind = wxITEM_RADIO)
- parent.AppendItem(self.onethumb_item)
- self.allthumb_item=wxMenuItem(parent, wxID_VIEWMENUTHUMB,"Film&strip mode\tCtrl-2", kind = wxITEM_RADIO)
- parent.AppendItem(self.allthumb_item)
- self.fasttmln_item=wxMenuItem(parent, wxID_VIEWMENUFAST,"&Fast mode\tCtrl-3", kind = wxITEM_RADIO)
- parent.AppendItem(self.fasttmln_item)
- parent.AppendSeparator()
- parent.Append(wxID_ZOOMMIN, 'Minimal Zoom')
- parent.Append(wxID_ZOOMOUT, 'Zoom &Out')
- parent.Append(wxID_ZOOMIN, 'Zoom &In')
- parent.Append(wxID_ZOOMMAX, 'Maximal Zoom')
- parent.AppendSeparator()
- self.prevwindow_item = wxMenuItem(parent, wxID_VIEWMENUPREV, "&Preview window\tCtrl-P", kind = wxITEM_CHECK)
- parent.AppendItem(self.prevwindow_item)
- EVT_MENU(self, wxID_VIEWMENUFAST, self.OnViewFast)
- EVT_MENU(self, wxID_VIEWMENUONE, self.OnViewOne)
- EVT_MENU(self, wxID_VIEWMENUTHUMB, self.OnViewThumb)
- EVT_MENU(self, wxID_VIEWMENUPREV, self.OnPreviewFrame)
- mode = wxConfigBase_Get().ReadInt('/TimelineWidget/thumb_mode',
- TimelineWidget.MODE_ONE_THUMBNAIL)
- if mode == TimelineWidget.MODE_FAST:
- self.fasttmln_item.Check(True)
- elif mode == TimelineWidget.MODE_ONE_THUMBNAIL:
- self.onethumb_item.Check(True)
- else:
- self.allthumb_item.Check(True)
-
- def init_toolbar(self, parent):
- parent.AddTool(bitmap=wxBitmap('bitmaps/open.png', wxBITMAP_TYPE_PNG),
- id=wxID_MAINFRAMETOOLBARIMPORT, isToggle=False, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Import clip')
- parent.AddTool(bitmap=wxBitmap('bitmaps/undo.png', wxBITMAP_TYPE_PNG),
- id=wxID_TOOLBARUNDO, isToggle=False, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Undo')
- parent.AddTool(bitmap=wxBitmap('bitmaps/redo.png', wxBITMAP_TYPE_PNG),
- id=wxID_TOOLBARREDO, isToggle=False, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Redo')
-
- parent.AddSeparator()
-
- parent.AddTool(bitmap=wxBitmap('bitmaps/cut.png', wxBITMAP_TYPE_PNG),
- id=wxID_CUTMODEL, isToggle=True, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Cut object')
-
- parent.AddSeparator()
-
- parent.AddTool(bitmap=wxBitmap('bitmaps/minus.png', wxBITMAP_TYPE_PNG),
- id=wxID_ZOOMMINUS, isToggle=False, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Zoom Out')
-
- self.ZoomSlider = wxSlider(parent, wxID_ZOOMSLIDER,
- value=(ZOOM_RIGHT - ZOOM_LEFT) / 2, minValue=ZOOM_LEFT, maxValue=ZOOM_RIGHT,
- size=wxSize(80,-1),
- style=wxSL_HORIZONTAL|wxDOUBLE_BORDER)
- parent.AddControl(self.ZoomSlider)
- self.ZoomSlider.lastvalue = self.ZoomSlider.GetValue()
-
- #arbitrary values, will be recalculated anyway
- self.ZoomSlider.minzoom = 10
- self.ZoomSlider.maxzoom = 10 + ZOOM_RIGHT - ZOOM_LEFT
- self.ZoomSlider.lastThumbHeight = -1
- self.ZoomSlider.lastTimelineWidth = -1
- self.ZoomSlider.initialized = False
-
- parent.AddTool(bitmap=wxBitmap('bitmaps/plus.png', wxBITMAP_TYPE_PNG),
- id=wxID_ZOOMPLUS, isToggle=False, longHelpString='',
- pushedBitmap=wxNullBitmap, shortHelpString='Zoom In')
-
- parent.SetToolSeparation(20)
-
- EVT_TOOL(self, wxID_ZOOMPLUS, self.OnZoom)
- EVT_TOOL(self, wxID_ZOOMMINUS, self.OnZoom)
- EVT_TOOL(self, wxID_ZOOMIN, self.OnZoom)
- EVT_TOOL(self, wxID_ZOOMOUT, self.OnZoom)
- EVT_TOOL(self, wxID_ZOOMMIN, self.OnZoom)
- EVT_TOOL(self, wxID_ZOOMMAX, self.OnZoom)
- EVT_SCROLL(self.ZoomSlider, self.OnZoom)
- EVT_TOOL(self, wxID_TOOLBARUNDO, self.OnUndo)
- EVT_TOOL(self, wxID_TOOLBARREDO, self.OnRedo)
- EVT_TOOL(self, wxID_MAINFRAMETOOLBARIMPORT, self.OnImport)
- EVT_TOOL(self, wxID_CUTMODEL, self.OnCut)
-
- parent.Realize()
-
- def _init_utils(self):
- self.menuBar = wxMenuBar()
-
- self.filemenu = wxMenu(title='')
- self.init_filemenu_items(self.filemenu)
- self.editmenu = wxMenu(title='')
- self.init_editmenu_items(self.editmenu)
- self.viewmenu = wxMenu(title='')
- self.init_viewmenu_items(self.viewmenu)
-
- self.init_menuBar(self.menuBar)
-
- def _init_ctrls(self, prnt):
- # generated method, don't edit
- cfg = wxConfigBase_Get()
- size = wxSize(cfg.ReadInt('/MainFrame/width', 800),
- cfg.ReadInt('/MainFrame/height', 600))
- wxFrame.__init__(self, id=wxID_MAINFRAME, name='mainFrame', parent=prnt,
- size=size, style=wxDEFAULT_FRAME_STYLE, title='OpenVIP Editor')
- self._init_utils()
- self.SetMenuBar(self.menuBar)
-
- self.splitter = wxSplitterWindow(id=wxID_MAINFRAMESPLITTER,
- name='splitter', parent=self, point=wxPoint(0, 0),
- size=wxSize(739, 485), style=wxSP_3D)
- self.splitter.SetMinimumPaneSize(50)
-
- self.toolbar = wxToolBar(id=wxID_MAINFRAMETOOLBAR, name='toolbar',
- parent=self, style=wxTB_HORIZONTAL | wxNO_BORDER )
- self.init_toolbar(self.toolbar)
-
- def __init__(self, parent):
- self.help = None
- self._init_ctrls(parent)
- self.SetToolBar(self.toolbar)
- self.objectPanel = ObjectPanel(self.splitter, -1, wxDefaultPosition,
- wxDefaultSize, wxTAB_TRAVERSAL,
- "objectPanel")
- self.previewframe = PreviewFrame(self, wxID_PREVFRAME, "")
- EVT_CLOSE(self.previewframe, self.OnClosePrevFrame)
- self.timeline = TimelineWidget.TimelineWidget(self.splitter, -1,
- wxDefaultSize, objectPanel=self.objectPanel, previewframe=self.previewframe)
- self.splitter.SplitVertically(self.timeline, self.objectPanel,
- self.splitter.GetSize()[0]*3/4)
- self.current_filename = None
- self.history = []
- self.outdialog = OutputSettingsDialog(self, -1, "")
- self.outdialog.viewer.SetValue(wxConfigBase_Get().Read('/MainFrame/viewer', ''))
- self.EnableItems(False)
- self.importclip_item.Enable(True)
- self.toolbar.EnableTool(wxID_MAINFRAMETOOLBARIMPORT,True)
- EVT_CLOSE(self, self.OnClose)
- render.EVT_RENDERING_DONE(self, self.OnRenderFinished)
-
- def EnableItems(self, enable):
- """Toggles on or off all widgets that cannot be used without
- a timeline."""
- self.savetimeline_item.Enable(enable)
- self.savetimelineas_item.Enable(enable)
- self.render_item.Enable(enable)
- self.undo_item.Enable(enable)
- self.redo_item.Enable(enable)
- self.importclip_item.Enable(enable)
- self.remove_item.Enable(enable)
- self.inserttrans_item.Enable(enable)
- self.onethumb_item.Enable(enable)
- self.allthumb_item.Enable(enable)
- self.fasttmln_item.Enable(enable)
- self.prevwindow_item.Enable(enable)
- self.viewmenu.Enable(wxID_ZOOMIN,enable);
- self.viewmenu.Enable(wxID_ZOOMOUT,enable);
- self.viewmenu.Enable(wxID_ZOOMMIN,enable);
- self.viewmenu.Enable(wxID_ZOOMMAX,enable);
- self.toolbar.EnableTool(wxID_MAINFRAMETOOLBARIMPORT,enable)
- self.toolbar.EnableTool(wxID_TOOLBARUNDO,enable)
- self.toolbar.EnableTool(wxID_TOOLBARREDO,enable)
- self.toolbar.EnableTool(wxID_CUTMODEL,enable)
- self.toolbar.EnableTool(wxID_ZOOMMINUS,enable)
- self.toolbar.EnableTool(wxID_ZOOMPLUS,enable)
- self.ZoomSlider.Enable(enable)
-
- def OnClose(self, event):
- """Saves main window size and external viewer path on exit."""
- self.previewframe.WaitUntilNotUsed()
- cfg = wxConfigBase_Get()
- size = self.GetSize()
- cfg.WriteInt('/MainFrame/width', size.x)
- cfg.WriteInt('/MainFrame/height', size.y)
- cfg.Write('/MainFrame/viewer', self.outdialog.viewer.GetValue())
- event.Skip()
-
- def RecalcZoomValue(self):
- """When minimal or maximal setting of zoom change, this function tries to set the
- slider to such a position that the absolute zoom value doesn't change."""
- self.ZoomSlider.lastTimelineWidth = self.timeline.GetClientWidth()
- if self.timeline.model.length() == 0:
- #timeline is empty, set default zoom next time a user adds something
- self.ZoomSlider.initialized = False
- newminz = 10
- self.ZoomSlider.SetValue(ZOOM_LEFT);
- else:
- newminz = float(self.timeline.GetClientWidth()) / self.timeline.model.length()
- newmaxz = newminz
- for o in self.timeline.model.objects:
- if (o.track)[0] == 'V' and o.track != "VFx":
- aspect = -1
- fps = -1
- try:
- fi = globals.get_file_info(o.src_spec["filename"])
- aspect = (fi.video_streams[0]).aspect
- fps = (fi.video_streams[0]).fps
- except openvip.Error:
- wxLogWarning("Could not get information about file '%s'." % o.src_spec)
- tmp = aspect * fps * self.timeline.GetThumbnailHeight()
- if tmp > newmaxz:
- newmaxz = tmp
-
- if newmaxz == newminz:
- #there are probably no video tracks - just put something into the new maximum zoom setting
- newmaxz = newminz * 10
-
- if not self.ZoomSlider.initialized:
- #the timeline was empty before the current function call, so we will set default zoom
- self.ZoomSlider.minzoom = newminz
- self.ZoomSlider.maxzoom = newmaxz
- self.ZoomSlider.SetValue(ZOOM_LEFT);
- self.CheckZoomButtons()
- return
-
- maxz = self.ZoomSlider.maxzoom
- minz = self.ZoomSlider.minzoom
- v = self.ZoomSlider.GetValue()
- lastv = self.ZoomSlider.lastvalue
- self.ZoomSlider.SetValue( ((v - ZOOM_LEFT) * (maxz - minz) + (minz - newminz) * (ZOOM_RIGHT - ZOOM_LEFT)) / (newmaxz - newminz) + ZOOM_LEFT )
- self.ZoomSlider.lastvalue = ((lastv - ZOOM_LEFT) * (maxz - minz) + (minz - newminz) * (ZOOM_RIGHT - ZOOM_LEFT)) / (newmaxz - newminz) + ZOOM_LEFT
- self.ZoomSlider.minzoom = newminz
- self.ZoomSlider.maxzoom = newmaxz
- if newminz < minz and self.ZoomSlider.GetValue() == ZOOM_LEFT:
- self.ZoomSlider.SetValue(ZOOM_LEFT + 1)
- self.ZoomSlider.lastvalue = ZOOM_LEFT + 1
- if newmaxz > maxz and self.ZoomSlider.GetValue() == ZOOM_RIGHT:
- self.ZoomSlider.SetValue(ZOOM_RIGHT - 1)
- self.ZoomSlider.lastvalue = ZOOM_RIGHT - 1
- self.CheckZoomButtons()
-
-
- def UpdateZoom(self):
- """This function actually changes the zoom of timeline."""
- if self.timeline.GetClientWidth() != self.ZoomSlider.lastTimelineWidth:
- self.RecalcZoomValue()
- if self.timeline.model == None: return
- if self.timeline.model.length() != 0:
- self.ZoomSlider.initialized = True
- th = self.timeline.GetThumbnailHeight()
- if self.ZoomSlider.lastThumbHeight < 0:
- self.ZoomSlider.lastThumbHeight = th
- elif th != self.ZoomSlider.lastThumbHeight:
- self.ZoomSlider.maxzoom = float(self.ZoomSlider.maxzoom) * th / self.ZoomSlider.lastThumbHeight
- self.ZoomSlider.lastThumbHeight = th
- self.timeline.SetZoom( float(self.ZoomSlider.GetValue() - ZOOM_LEFT) / (ZOOM_RIGHT - ZOOM_LEFT) * (self.ZoomSlider.maxzoom - self.ZoomSlider.minzoom) + self.ZoomSlider.minzoom )
- self.CheckZoomButtons()
-
- def OnZoom(self, event):
- """This function only does basic event handling."""
- if self.timeline.GetClientWidth() != self.ZoomSlider.lastTimelineWidth:
- self.RecalcZoomValue()
- max = self.ZoomSlider.GetMax()
- min = self.ZoomSlider.GetMin()
- tmp = self.ZoomSlider.GetValue()
- if (event.GetId() == wxID_ZOOMPLUS or event.GetId() == wxID_ZOOMIN):
- tmp = self.ZoomSlider.GetValue() + ZOOM_STEP
- if tmp > max : tmp = max
- elif (event.GetId() == wxID_ZOOMMINUS or event.GetId() == wxID_ZOOMOUT):
- tmp = self.ZoomSlider.GetValue() - ZOOM_STEP
- if tmp < min : tmp = min
- elif (event.GetId() == wxID_ZOOMMIN):
- tmp = ZOOM_LEFT
- elif (event.GetId() == wxID_ZOOMMAX):
- tmp = ZOOM_RIGHT
- if (tmp != self.ZoomSlider.lastvalue):
- self.ZoomSlider.SetValue( tmp )
- self.ZoomSlider.lastvalue = tmp
- self.UpdateZoom()
-
- def CheckZoomButtons(self):
- """If zoom is maximal/minimal, disable the corresponding button."""
- if self.ZoomSlider.GetValue() == self.ZoomSlider.GetMin():
- self.toolbar.EnableTool(wxID_ZOOMMINUS,False)
- else:
- self.toolbar.EnableTool(wxID_ZOOMMINUS,True)
- if self.ZoomSlider.GetValue() == self.ZoomSlider.GetMax():
- self.toolbar.EnableTool(wxID_ZOOMPLUS,False)
- else:
- self.toolbar.EnableTool(wxID_ZOOMPLUS,True)
- self.ZoomSlider.Refresh()
-
- def OnCut(self, event):
- if self.timeline.model == None: return
- if event.IsChecked():
- self.timeline.Cut()
- else:
- self.timeline.CancelCut()
-
- def OnNewTimeline(self, event):
- """Creates a new timeline and clears the history of changes."""
- self.timeline.SetModel(model.Timeline())
- self.history_index = 0
- self.history = [copy.deepcopy(self.timeline.model)]
- notify.listen(self.timeline.model, self.OnChangeTimeline)
- self.EnableItems(True)
- self.RecalcZoomValue()
- self.UpdateZoom()
-
- def OnLoadTimeline(self, event):
- """Displays fileselector for loading timeline from a file,
- loads it and sets a default zoom."""
- ret = wxFileSelector('Load timeline', wildcard='OpenVIP timeline (*.timeline)|*.timeline',
- flags=wxOPEN|wxFILE_MUST_EXIST)
- if ret == '': return
- self.OpenFile(ret)
- self.RecalcZoomValue()
- self.UpdateZoom()
-
- def OnSaveTimeline(self, event):
- """Saves timeline to a file."""
- if self.current_filename!=None:
- self.timeline.model.save(self.current_filename)
- else:
- self.OnSaveTimelineAs(event)
-
- def OnSaveTimelineAs(self, event):
- """Saves timeline to a file, always asks for a new name."""
- ret = wxFileSelector('Save timeline', wildcard='OpenVIP timeline (*.timeline)|*.timeline',
- flags=wxSAVE|wxOVERWRITE_PROMPT)
- if ret == '': return
- self.timeline.model.save(ret)
- self.current_filename = ret
-
- def OnUndo(self, event):
- """Reverts the last change. The history_index always points to
- a copy of the current model in the history list."""
- if self.history_index>0:
- self.history_index-=1
- notify.unlisten(self.timeline.model, self.OnChangeTimeline)
- self.timeline.SetModel(copy.deepcopy(self.history[self.history_index]))
- notify.listen(self.timeline.model, self.OnChangeTimeline)
-
- def OnRedo(self, event):
- """Step forward in history list."""
- if self.history_index<len(self.history)-1:
- self.history_index+=1
- notify.unlisten(self.timeline.model, self.OnChangeTimeline)
- self.timeline.SetModel(copy.deepcopy(self.history[self.history_index]))
- notify.listen(self.timeline.model, self.OnChangeTimeline)
-
- def OnChangeTimeline(self):
- """Timeline changed - update the history and preview frame."""
- self.history=self.history[:self.history_index+1]
- self.history.append(copy.deepcopy(self.timeline.model))
- if len(self.history)>50:
- self.history.pop(0)
- self.history_index=len(self.history)-1
- self.RecalcZoomValue()
- if not self.ZoomSlider.initialized:
- self.UpdateZoom()
- self.previewframe.SetModel(self.timeline.model)
-
- def OnExit(self, event):
- self.previewframe.WaitUntilNotUsed()
- self.Close()
-
- def OnRenderFinished(self, event):
- """Execute external viewer after rendering finished."""
- if self.use_viewer:
- wxExecute(self.outdialog.viewer.GetValue()+" "+self.outdialog.output_filename.GetValue(),wxEXEC_ASYNC)
- self.use_viewer = False;
-
- def OnRender(self, event):
- """Shows the render dialog and renders the output file."""
- timesel=self.timeline.GetTimeSelection()
- self.outdialog.render_radio.EnableItem(1, timesel!=None)
- vf = getBestVideoFormat(self.timeline.model,self.timeline.model.length(),self.timeline.model.beginning())
- if vf!=None:
- self.outdialog.video_width.SetValue(vf.width)
- self.outdialog.video_height.SetValue(vf.height)
- self.outdialog.video_fps.SetValue(vf.fps)
- af = getBestAudioFormat(self.timeline.model,self.timeline.model.length(),self.timeline.model.beginning())
- if af!=None:
- self.outdialog.audio_samplerate.SetValue(str(af.sample_rate))
- self.outdialog.channels_choice.SetSelection(af.channels-1)
- if (self.outdialog.ShowModal() == wxID_OK):
- if self.outdialog.HasVideo():
- vf = model.VideoFormat(self.outdialog.video_width.GetValue(),self.outdialog.video_height.GetValue(),self.outdialog.video_fps.GetValue())
- else:
- vf = None
- if self.outdialog.HasAudio():
- if self.outdialog.channels_choice.GetStringSelection()=='mono':
- channels=1
- else:
- channels=2
- af = model.AudioFormat(int(self.outdialog.audio_samplerate.GetValue()),channels)
- else:
- af = None
- out_params=self.outdialog.GetParams()
- if af!=None or vf!=None:
- if self.outdialog.render_radio.GetSelection()==1:
- time_from=timesel[0]
- time_to=timesel[1]
- if time_from<self.timeline.model.beginning():
- time_from=self.timeline.model.beginning()
- if time_to>self.timeline.model.beginning()+self.timeline.model.length():
- time_from=self.timeline.model.beginning()+self.timeline.model.length()
- else:
- time_from=time_to=None
- net=self.timeline.model.tonetworkxml(self.outdialog.output_filename.GetValue(),vf,af,out_params=out_params,time_from=time_from,time_to=time_to)
- self.use_viewer=self.outdialog.use_viewer.IsChecked()
- render.renderFromString(net, self, cancellable=True,
- background=False)
-
- def OpenFile(self, filename):
- """Loads timeline from file."""
- try:
- m = model.load(filename)
- except model.Error, err:
- wxLogError(err.msg)
- wxLogError("File '%s' is not valid OpenVIP file." % filename)
- return False
- self.timeline.SetModel(m)
- self.current_filename = filename
- self.history_index = 0
- self.history = [copy.deepcopy(self.timeline.model)]
- notify.listen(self.timeline.model, self.OnChangeTimeline)
- self.EnableItems(True)
- return True
-
- def OnViewFast(self, event):
- """Enables the Fast timeline mode."""
- self.timeline.SetMode(TimelineWidget.MODE_FAST)
-
- def OnViewOne(self, event):
- """Enables the Info timeline mode."""
- self.timeline.SetMode(TimelineWidget.MODE_ONE_THUMBNAIL)
-
- def OnViewThumb(self, event):
- """Enables the Filmstrip timeline mode."""
- self.timeline.SetMode(TimelineWidget.MODE_THUMBNAILS)
-
- def OnPreviewFrame(self, event):
- """Shows or hides the preview frame (from menu)."""
- if self.previewframe.IsShown()==False:
- self.previewframe.Show()
- self.previewframe.SetModel(self.timeline.model)
- else:
- self.previewframe.Show(False)
-
- def OnClosePrevFrame(self, event):
- """Hides the preview frame (close button event)."""
- self.previewframe.Show(False)
- self.prevwindow_item.Check(False)
- self.previewframe.WaitUntilNotUsed()
-
- def OnImport(self, event):
- """Imports clip from a file, i.e. creates objects on timeline which
- correspond to audio and video streams (if any)."""
- filename = wxFileSelector('Import clip',
- wildcard='All supported filetypes|*.avi;*.avs;*.mpg;*.m2v;*.wav;*.mp3;*.ogg;*.png;*.jpg|AVI Clip (*.avi)|*.avi;*.avs|MPEG Clip (*.mpg, *.m2v)|*.mpg;*.m2v|Audio files|*.wav;*.mp3;*.ogg|Images (*.png, *.jpg)|*.png;*.jpg|All files|*',
- flags=wxOPEN)
-
- if filename == "": return
- try:
- fi = globals.get_file_info(filename)
- except openvip.Error:
- wxLogError("File '%s' is not valid multimedia file." % filename)
- return
-
- gr = model.Group()
- gr.id = model.getUniqueID(self.timeline.model, "group")
- countv = 0
- for v in fi.video_streams:
- countv += 1
- obj = model.Object()
- obj.time_from = 0
- obj.time_to = v.length / v.fps
- obj.src_from = obj.src_to = 0
- obj.track = "V" #temporary - remember type of the object
- obj.src_spec["filename"] = fi.filename
- obj.src_channel = "video" + str(countv - 1)
- gr.objects.append(obj)
-
- counta = 0
- for a in fi.audio_streams:
- counta += 1
- obj = model.Object()
- obj.time_from = 0
- obj.time_to = 1.0 * a.length / a.sample_rate
- obj.src_from = obj.src_to = 0
- obj.track = "A" #temporary - remember type of the object
- obj.src_spec["filename"] = fi.filename
- obj.src_channel = "audio" + str(counta - 1)
- gr.objects.append(obj)
-
- if countv + counta == 0:
- wxLogError("No video or audio streams found in file '%s'" % filename)
- return
-
- ic = ImportClass(self.timeline, gr, self)
- self.timeline.AddObject(ic)
-
- def OnInsertTrans(self, event):
- """Inserts transition using the callback mechanism."""
- ic = InsertTransitionCallback(self.timeline, self)
- self.timeline.AddObject(ic)
-
- def OnRemove(self, event):
- """Removes selected objects from timeline."""
- for o in self.timeline.selection:
- if isinstance(o, model.Object):
- self.timeline.model.objects.remove(o)
- elif isinstance(o, model.Transition):
- self.timeline.model.transitions.remove(o)
- for g in self.timeline.model.groups:
- if g.objects.count(o)>0:
- g.objects.remove(o)
- if len(g.objects)==0:
- self.timeline.model.groups.remove(g)
- notify.notify(self.timeline.model)
-
- def OnHelp(self, event):
- """Displays the help window."""
- if self.help == None:
- self.help = wxHtmlHelpController()
- self.help.AddBook('doc/htmlhelp.hhp')
- self.help.DisplayContents()
-
- def OnAbout(self, event):
- """Displays the About dialog box."""
- self.about = AboutDialog(self, -1, "")
- self.about.ShowModal()
-
- class InsertTransitionCallback:
- """Callback class for inserting new transitions
- (see TimelineWidget.py)."""
- def __init__(self, tl, mainframe):
- self.timeline = tl
- self.mainframe = mainframe
- lib = globals.core
- self.video_transitions = lib.enum_plugins(openvip.PLUGIN_VIDEO_TRANSITION)
- self.audio_transitions = lib.enum_plugins(openvip.PLUGIN_AUDIO_TRANSITION)
-
- def veto(self,time,track):
- # Is it an audio or video transition track?
- if track == 'VFx':
- track1='VA'
- track2='VB'
- elif track == 'AFx':
- track1='AA'
- track2='AB'
- else:
- return True
-
- # Find the two objects for transition
- object_a=object_b=None
- for o in self.timeline.model.objects:
- if o.track==track1 and o.time_from<time<o.time_to:
- object_a=o
- elif o.track==track2 and o.time_from<time<o.time_to:
- object_b=o
- if object_a==None or object_b==None:
- return True
-
- # Find the interval that the transition should occupy
- if object_a.time_from<=object_b.time_from<time<object_a.time_to<=object_b.time_to:
- self.time_from = object_b.time_from
- self.time_to = object_a.time_to
- self.direction = 'AB'
- elif object_b.time_from<=object_a.time_from<time<object_b.time_to<=object_a.time_to:
- self.time_from = object_a.time_from
- self.time_to = object_b.time_to
- self.direction = 'BA'
- elif object_b.time_from<=object_a.time_from<time<object_a.time_to<=object_b.time_to:
- self.time_from = object_a.time_from
- self.time_to = object_a.time_to
- self.direction = 'AB'
- elif object_a.time_from<=object_b.time_from<time<object_b.time_to<=object_a.time_to:
- self.time_from = object_b.time_from
- self.time_to = object_b.time_to
- self.direction = 'AB'
- else:
- return True
-
- # Check whether the interval is free
- for o in self.timeline.model.transitions:
- if o.track == track:
- if self.time_from<o.time_from<self.time_to: return True
- if self.time_from<o.time_to<self.time_to: return True
- if o.time_from<=self.time_from<=self.time_to<=o.time_to: return True
- return False
-
- def add(self,time,track):
- if track=='VFx':
- msg='Available video transitions:'
- list=self.video_transitions
- else:
- msg='Available audio transitions:'
- list=self.audio_transitions
- t_dialog = wxSingleChoiceDialog(self.mainframe, msg, "Select transition ...", [p.description for p in list])
- if (t_dialog.ShowModal() == wxID_OK) and (t_dialog.GetSelection()>=0):
- t = model.Transition()
- t.id = model.getUniqueID(self.timeline.model, "trans")
- t.name = list[t_dialog.GetSelection()].name
- t.track = track
- t.time_from=self.time_from
- t.time_to=self.time_to
- t.direction=self.direction
- self.timeline.model.transitions.append(t)
- notify.notify(self.timeline.model)
-
- class ImportClass:
- """Callback class for inserting new objects
- (see TimelineWidget.py)."""
- def __init__(self, tl, gr, main):
- self.timeline = tl
- self.gr = gr
- self.main = main
- def isFree(self, time_from, time_to, track, checkboth):
- # is the given interval on the given track unoccupied?
- # if checkboth is true, isFree checks both audio and video tracks of given index
- if checkboth:
- # track_opp will contain name of corresponding track of opposite type
- track_opp = track
- if track[0] == "V": track_opp = "A" + track[1]
- else: track_opp = "V" + track[1]
- for o in self.timeline.model.objects:
- if (o.track == track) or (checkboth and (o.track == track_opp)):
- if o.time_from >= time_from and o.time_from <= time_to: return False
- if o.time_to >= time_from and o.time_to <= time_to: return False
- if o.time_to < time_from and o.time_to > time_to: return False
- return True
-
- def freeTrack(self, time_from, time_to, firsttrack):
- # returns first track with given interval unoccupied (starting at firsttrack)
- base = firsttrack[0]
- idx = firsttrack[1]
- while not self.isFree(time_from, time_to, base + idx, False):
- idx = nextIdx(idx)
- if idx == '': return ''
- return base + idx
-
- def add(self,time,track):
- idxV = track[1] # in case of multiple added objects we iterate through available tracks
- idxA = track[1] # if there are more objects of the same type (audio/video), we put each
- # of them to next available track
- for o in self.gr.objects:
- if o.track == 'V':
- tr = self.freeTrack(time, time + o.time_to, o.track + idxV)
- if tr == '':
- LogError("Not enough empty video tracks!")
- else:
- idxV = tr[1]
- idxV = nextIdx(idxV)
- else:
- tr = self.freeTrack(time, time + o.time_to, o.track + idxA)
- if tr == '':
- LogError("Not enough empty audio tracks!")
- else:
- idxA = tr[1]
- idxA = nextIdx(idxA)
- if tr != '':
- o.track = tr
- o.time_from = time
- o.time_to += time
- o.id = model.getUniqueID(self.timeline.model, "object")
- # print("pridan objekt na stopu " + tr + ", " + str(o.time_from) + " - " + str(o.time_to) + ", id = " + str(o.id) + "\n")
- self.timeline.model.objects.append(o)
- else: self.gr.objects.remove(o)
- self.timeline.model.groups.append(self.gr)
- notify.notify(self.timeline.model)
- self.timeline.CreateThumbnails()
-
- def veto(self,time,track):
- # Decide if the object can be placed somewhere here
- if track[0] != self.gr.objects[0].track: return True
- if track == 'VFx' or track == 'AFx': return True
- checkboth = audiovideo(self.gr)
- return not self.isFree(time, time + self.gr.objects[0].time_to, track, checkboth)
-
- def nextIdx(idx):
- """Returns next track index (or "" if idx == 4)."""
- if idx == 'B' : idx = '0'
- else: idx = chr(ord(idx) + 1)
- if (idx == '5'): #maximum trackumber is 4
- return ''
- else: return idx
-
- def audiovideo(group):
- """Checks whether both audio and video tracks are present."""
- audio = video = False
- for o in group.objects:
- if o.track[0] == "V": video = True
- if o.track[0] == "A": audio = True
- return audio and video
-