def setUpClass(TestQueryClass): """ Init unit test data """ # Create Qt application TestQueryClass.app = OpenShotApp(sys.argv, mode="unittest") TestQueryClass.clip_ids = [] TestQueryClass.file_ids = [] TestQueryClass.transition_ids = [] # Import additional classes that need the app defined first from classes.query import Clip, File, Transition # Insert some clips into the project data for num in range(5): # Create clip c = openshot.Clip(os.path.join(info.IMAGES_PATH, "AboutLogo.png")) # Parse JSON clip_data = json.loads(c.Json()) # Insert into project data query_clip = Clip() query_clip.data = clip_data query_clip.save() # Keep track of the ids TestQueryClass.clip_ids.append(query_clip.id) # Insert some files into the project data for num in range(5): # Create file r = openshot.DummyReader(openshot.Fraction(24, 1), 640, 480, 44100, 2, 30.0) # Parse JSON file_data = json.loads(r.Json()) # Insert into project data query_file = File() query_file.data = file_data query_file.data["path"] = os.path.join(info.IMAGES_PATH, "AboutLogo.png") query_file.data["media_type"] = "image" query_file.save() # Keep track of the ids TestQueryClass.file_ids.append(query_file.id) # Insert some transitions into the project data for num in range(5): # Create mask object transition_object = openshot.Mask() transitions_data = json.loads(transition_object.Json()) # Insert into project data query_transition = Transition() query_transition.data = transitions_data query_transition.save() # Keep track of the ids TestQueryClass.transition_ids.append(query_transition.id)
def getMenu(self): # Build menu for selection button menu = QMenu(self) # Get translation object _ = get_app()._tr # Look up item for more info if self.item_type == "clip": self.item_name = Clip.get(id=self.item_id).title() elif self.item_type == "transition": self.item_name = Transition.get(id=self.item_id).title() elif self.item_type == "effect": self.item_name = Effect.get(id=self.item_id).title() # Add selected clips for item_id in get_app().window.selected_clips: clip = Clip.get(id=item_id) item_name = clip.title() item_icon = QIcon(QPixmap(clip.data.get('image'))) action = menu.addAction(item_name) action.setIcon(item_icon) action.setData({'item_id':item_id, 'item_type':'clip'}) action.triggered.connect(self.Action_Triggered) # Add effects for these clips (if any) for effect in clip.data.get('effects'): item_name = Effect.get(id=effect.get('id')).title() item_icon = QIcon(QPixmap(os.path.join(info.PATH, "effects", "icons", "%s.png" % effect.get('class_name').lower()))) action = menu.addAction(' > %s' % _(item_name)) action.setIcon(item_icon) action.setData({'item_id': effect.get('id'), 'item_type': 'effect'}) action.triggered.connect(self.Action_Triggered) # Add selected transitions for item_id in get_app().window.selected_transitions: trans = Transition.get(id=item_id) item_name = _(trans.title()) item_icon = QIcon(QPixmap(trans.data.get('reader',{}).get('path'))) action = menu.addAction(_(item_name)) action.setIcon(item_icon) action.setData({'item_id': item_id, 'item_type': 'transition'}) action.triggered.connect(self.Action_Triggered) # Add selected effects for item_id in get_app().window.selected_effects: effect = Effect.get(id=item_id) item_name = _(effect.title()) item_icon = QIcon(QPixmap(os.path.join(info.PATH, "effects", "icons", "%s.png" % effect.data.get('class_name').lower()))) action = menu.addAction(_(item_name)) action.setIcon(item_icon) action.setData({'item_id': item_id, 'item_type': 'effect'}) action.triggered.connect(self.Action_Triggered) # Return the menu object return menu
def update_item_timeout(self): # Get the next item id, and type self.item_id = self.next_item_id self.item_type = self.next_item_type self.item_name = None self.item_icon = None # Stop timer self.update_timer.stop() # Get translation object _ = get_app()._tr # Look up item for more info if self.item_type == "clip": clip = Clip.get(id=self.item_id) if clip: self.item_name = clip.title() self.item_icon = QIcon(QPixmap(clip.data.get('image'))) elif self.item_type == "transition": trans = Transition.get(id=self.item_id) if trans: self.item_name = _(trans.title()) self.item_icon = QIcon( QPixmap(trans.data.get('reader', {}).get('path'))) elif self.item_type == "effect": effect = Effect.get(id=self.item_id) if effect: self.item_name = _(effect.title()) self.item_icon = QIcon( QPixmap( os.path.join( info.PATH, "effects", "icons", "%s.png" % effect.data.get('class_name').lower()))) # Truncate long text if self.item_name and len(self.item_name) > 25: self.item_name = "%s..." % self.item_name[:22] # Set label if self.item_id: self.lblSelection.setText("<strong>%s</strong>" % _("Selection:")) self.btnSelectionName.setText(self.item_name) self.btnSelectionName.setVisible(True) if self.item_icon: self.btnSelectionName.setIcon(self.item_icon) else: self.lblSelection.setText("<strong>%s</strong>" % _("No Selection")) self.btnSelectionName.setVisible(False) # Set the menu on the button self.btnSelectionName.setMenu(self.getMenu())
def test_intersect(self): """ Test special filter argument 'intersect' """ trans = Transition.get(id=self.transition_ids[0]) self.assertTrue(trans) pos = trans.data.get("position", -1.0) duration = trans.data.get("duration", -1.0) self.assertTrue(pos >= 0.0) self.assertTrue(duration >= 0.0) time = pos + (duration / 2) def get_times(item): pos = item.data.get("position", -1.0) end = pos + item.data.get("duration", -1.0) return (pos, end) t_intersect = Transition.filter(intersect=time) t_ids = [t.id for t in t_intersect] t_all = Transition.filter() t_rest = [x for x in t_all if x.id not in t_ids] c_intersect = Clip.filter(intersect=time) c_ids = [c.id for c in c_intersect] c_all = Clip.filter() c_rest = [x for x in c_all if x.id not in c_ids] for item in t_intersect + c_intersect: item_id = item.id pos, end = get_times(item) self.assertTrue(pos <= time) self.assertTrue(time <= end) for item in t_rest + c_rest: item_id = item.id pos, end = get_times(item) if pos < time: self.assertTrue(end <= time) if end > time: self.assertTrue(pos >= time)
def actionRemoveTransition_trigger(self, event): log.info('actionRemoveTransition_trigger') # Loop through selected clips for tran_id in self.selected_transitions: # Find matching file transitions = Transition.filter(id=tran_id) for t in transitions: # Clear selected clips self.removeSelection(tran_id, "transition") # Remove transition t.delete()
def update_item_timeout(self): # Get the next item id, and type self.item_id = self.next_item_id self.item_type = self.next_item_type self.item_name = None self.item_icon = None # Stop timer self.update_timer.stop() # Get translation object _ = get_app()._tr # Look up item for more info if self.item_type == "clip": clip = Clip.get(id=self.item_id) self.item_name = clip.title() self.item_icon = QIcon(QPixmap(clip.data.get('image'))) elif self.item_type == "transition": trans = Transition.get(id=self.item_id) self.item_name = _(trans.title()) self.item_icon = QIcon(QPixmap(trans.data.get('reader', {}).get('path'))) elif self.item_type == "effect": effect = Effect.get(id=self.item_id) self.item_name = _(effect.title()) self.item_icon = QIcon(QPixmap(os.path.join(info.PATH, "effects", "icons", "%s.png" % effect.data.get('class_name').lower()))) # Truncate long text if self.item_name and len(self.item_name) > 25: self.item_name = "%s..." % self.item_name[:22] # Set label if self.item_id: self.lblSelection.setText("<strong>%s</strong>" % _("Selection:")) self.btnSelectionName.setText(self.item_name) self.btnSelectionName.setVisible(True) if self.item_icon: self.btnSelectionName.setIcon(self.item_icon) else: self.lblSelection.setText("<strong>%s</strong>" % _("No Selection")) self.btnSelectionName.setVisible(False) # Set the menu on the button self.btnSelectionName.setMenu(self.getMenu())
def remove_keyframe(self, item): """Remove an existing keyframe (if any)""" # Determine what was changed property = self.model.item(item.row(), 0).data() property_name = property[1]["name"] property_type = property[1]["type"] closest_point_x = property[1]["closest_point_x"] property_type = property[1]["type"] property_key = property[0] clip_id, item_type = item.data() # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info("remove keyframe: %s" % c.data) # Determine type of keyframe (normal or color) keyframe_list = [] if property_type == "color": keyframe_list = [ c.data[property_key]["red"], c.data[property_key]["blue"], c.data[property_key]["green"] ] else: keyframe_list = [c.data[property_key]] # Loop through each keyframe (red, blue, and green) for keyframe in keyframe_list: # Keyframe # Loop through points, find a matching points on this frame closest_point = None point_to_delete = None for point in keyframe["Points"]: if point["co"]["X"] == self.frame_number: # Found point, Update value clip_updated = True point_to_delete = point break if point["co"]["X"] == closest_point_x: closest_point = point # If no point found, use closest point x if not point_to_delete: point_to_delete = closest_point # Delete point (if needed) if point_to_delete: clip_updated = True log.info("Found point to delete at X=%s" % point_to_delete["co"]["X"]) keyframe["Points"].remove(point_to_delete) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data[property_key]} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.refreshFrameSignal.emit() # Clear selection self.parent.clearSelection()
def accept(self): """ Ok button clicked """ log.info('accept') # Get settings from form start_position = self.txtStartTime.value() track_num = self.cmbTrack.currentData() fade_value = self.cmbFade.currentData() fade_length = self.txtFadeLength.value() transition_path = self.cmbTransition.currentData() transition_length = self.txtTransitionLength.value() image_length = self.txtImageLength.value() zoom_value = self.cmbZoom.currentData() # Init position position = start_position random_transition = False if transition_path == "random": random_transition = True # Get frames per second fps = get_app().project.get(["fps"]) fps_float = float(fps["num"]) / float(fps["den"]) # Loop through each file (in the current order) for file in self.treeFiles.timeline_model.files: # Create a clip clip = Clip() clip.data = {} if (file.data["media_type"] == "video" or file.data["media_type"] == "image"): # Determine thumb path thumb_path = os.path.join(info.THUMBNAIL_PATH, "%s.png" % file.data["id"]) else: # Audio file thumb_path = os.path.join(info.PATH, "images", "AudioThumbnail.png") # Get file name path, filename = os.path.split(file.data["path"]) # Convert path to the correct relative path (based on this folder) file_path = file.absolute_path() # Create clip object for this file c = openshot.Clip(file_path) # Append missing attributes to Clip JSON new_clip = json.loads(c.Json()) new_clip["position"] = position new_clip["layer"] = track_num new_clip["file_id"] = file.id new_clip["title"] = filename new_clip["image"] = thumb_path # Skip any clips that are missing a 'reader' attribute # TODO: Determine why this even happens, as it shouldn't be possible if not new_clip.get("reader"): continue # Skip to next file # Overwrite frame rate (incase the user changed it in the File Properties) file_properties_fps = float(file.data["fps"]["num"]) / float( file.data["fps"]["den"]) file_fps = float(new_clip["reader"]["fps"]["num"]) / float( new_clip["reader"]["fps"]["den"]) fps_diff = file_fps / file_properties_fps new_clip["reader"]["fps"]["num"] = file.data["fps"]["num"] new_clip["reader"]["fps"]["den"] = file.data["fps"]["den"] # Scale duration / length / and end properties new_clip["reader"]["duration"] *= fps_diff new_clip["end"] *= fps_diff new_clip["duration"] *= fps_diff # Check for optional start and end attributes start_time = 0 end_time = new_clip["reader"]["duration"] if 'start' in file.data.keys(): start_time = file.data['start'] new_clip["start"] = start_time if 'end' in file.data.keys(): end_time = file.data['end'] new_clip["end"] = end_time # Adjust clip duration, start, and end new_clip["duration"] = new_clip["reader"]["duration"] if file.data["media_type"] == "image": end_time = image_length new_clip["end"] = end_time # Adjust Fade of Clips (if no transition is chosen) if not transition_path: if fade_value != None: # Overlap this clip with the previous one (if any) position = max(start_position, new_clip["position"] - fade_length) new_clip["position"] = position if fade_value == 'Fade In' or fade_value == 'Fade In & Out': start = openshot.Point( round(start_time * fps_float) + 1, 0.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( min( round((start_time + fade_length) * fps_float) + 1, round(end_time * fps_float) + 1), 1.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip['alpha']["Points"].append(start_object) new_clip['alpha']["Points"].append(end_object) if fade_value == 'Fade Out' or fade_value == 'Fade In & Out': start = openshot.Point( max( round((end_time * fps_float) + 1) - (round(fade_length * fps_float) + 1), round(start_time * fps_float) + 1), 1.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round(end_time * fps_float) + 1, 0.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip['alpha']["Points"].append(start_object) new_clip['alpha']["Points"].append(end_object) # Adjust zoom amount if zoom_value != None: # Location animation if zoom_value == "Random": animate_start_x = uniform(-0.5, 0.5) animate_end_x = uniform(-0.15, 0.15) animate_start_y = uniform(-0.5, 0.5) animate_end_y = uniform(-0.15, 0.15) # Scale animation start_scale = uniform(0.5, 1.5) end_scale = uniform(0.85, 1.15) elif zoom_value == "Zoom In": animate_start_x = 0.0 animate_end_x = 0.0 animate_start_y = 0.0 animate_end_y = 0.0 # Scale animation start_scale = 1.0 end_scale = 1.25 elif zoom_value == "Zoom Out": animate_start_x = 0.0 animate_end_x = 0.0 animate_start_y = 0.0 animate_end_y = 0.0 # Scale animation start_scale = 1.25 end_scale = 1.0 # Add keyframes start = openshot.Point( round(start_time * fps_float) + 1, start_scale, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round(end_time * fps_float) + 1, end_scale, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["gravity"] = openshot.GRAVITY_CENTER new_clip["scale_x"]["Points"].append(start_object) new_clip["scale_x"]["Points"].append(end_object) new_clip["scale_y"]["Points"].append(start_object) new_clip["scale_y"]["Points"].append(end_object) # Add keyframes start_x = openshot.Point( round(start_time * fps_float) + 1, animate_start_x, openshot.BEZIER) start_x_object = json.loads(start_x.Json()) end_x = openshot.Point( round(end_time * fps_float) + 1, animate_end_x, openshot.BEZIER) end_x_object = json.loads(end_x.Json()) start_y = openshot.Point( round(start_time * fps_float) + 1, animate_start_y, openshot.BEZIER) start_y_object = json.loads(start_y.Json()) end_y = openshot.Point( round(end_time * fps_float) + 1, animate_end_y, openshot.BEZIER) end_y_object = json.loads(end_y.Json()) new_clip["gravity"] = openshot.GRAVITY_CENTER new_clip["location_x"]["Points"].append(start_x_object) new_clip["location_x"]["Points"].append(end_x_object) new_clip["location_y"]["Points"].append(start_y_object) new_clip["location_y"]["Points"].append(end_y_object) if transition_path: # Add transition for this clip (if any) # Open up QtImageReader for transition Image if random_transition: random_index = randint(0, len(self.transitions)) transition_path = self.transitions[random_index] # Get reader for transition transition_reader = openshot.QtImageReader(transition_path) brightness = openshot.Keyframe() brightness.AddPoint(1, 1.0, openshot.BEZIER) brightness.AddPoint( round( min(transition_length, end_time - start_time) * fps_float) + 1, -1.0, openshot.BEZIER) contrast = openshot.Keyframe(3.0) # Create transition dictionary transitions_data = { "layer": track_num, "title": "Transition", "type": "Mask", "start": 0, "end": min(transition_length, end_time - start_time), "brightness": json.loads(brightness.Json()), "contrast": json.loads(contrast.Json()), "reader": json.loads(transition_reader.Json()), "replace_image": False } # Overlap this clip with the previous one (if any) position = max(start_position, position - transition_length) transitions_data["position"] = position new_clip["position"] = position # Create transition tran = Transition() tran.data = transitions_data tran.save() # Save Clip clip.data = new_clip clip.save() # Increment position by length of clip position += (end_time - start_time) # Accept dialog super(AddToTimeline, self).accept()
def read_legacy_project_file(self, file_path): """Attempt to read a legacy version 1.x openshot project file""" import sys, pickle from classes.query import File, Track, Clip, Transition from classes.app import get_app import openshot try: import json except ImportError: import simplejson as json # Get translation method _ = get_app()._tr # Append version info v = openshot.GetVersion() project_data = {} project_data["version"] = { "openshot-qt": info.VERSION, "libopenshot": v.ToString() } # Get FPS from project from classes.app import get_app fps = get_app().project.get(["fps"]) fps_float = float(fps["num"]) / float(fps["den"]) # Import legacy openshot classes (from version 1.X) from classes.legacy.openshot import classes as legacy_classes from classes.legacy.openshot.classes import project as legacy_project from classes.legacy.openshot.classes import sequences as legacy_sequences from classes.legacy.openshot.classes import track as legacy_track from classes.legacy.openshot.classes import clip as legacy_clip from classes.legacy.openshot.classes import keyframe as legacy_keyframe from classes.legacy.openshot.classes import files as legacy_files from classes.legacy.openshot.classes import transition as legacy_transition from classes.legacy.openshot.classes import effect as legacy_effect from classes.legacy.openshot.classes import marker as legacy_marker sys.modules['openshot.classes'] = legacy_classes sys.modules['classes.project'] = legacy_project sys.modules['classes.sequences'] = legacy_sequences sys.modules['classes.track'] = legacy_track sys.modules['classes.clip'] = legacy_clip sys.modules['classes.keyframe'] = legacy_keyframe sys.modules['classes.files'] = legacy_files sys.modules['classes.transition'] = legacy_transition sys.modules['classes.effect'] = legacy_effect sys.modules['classes.marker'] = legacy_marker # Keep track of files that failed to load failed_files = [] with open(file_path.encode('UTF-8'), 'rb') as f: try: # Unpickle legacy openshot project file v1_data = pickle.load(f, fix_imports=True, encoding="UTF-8") file_lookup = {} # Loop through files for item in v1_data.project_folder.items: # Is this item a File (i.e. ignore folders) if isinstance(item, legacy_files.OpenShotFile): # Create file try: clip = openshot.Clip(item.name) reader = clip.Reader() file_data = json.loads(reader.Json()) # Determine media type if file_data["has_video"] and not self.is_image( file_data): file_data["media_type"] = "video" elif file_data["has_video"] and self.is_image( file_data): file_data["media_type"] = "image" elif file_data["has_audio"] and not file_data[ "has_video"]: file_data["media_type"] = "audio" # Save new file to the project data file = File() file.data = file_data file.save() # Keep track of new ids and old ids file_lookup[item.unique_id] = file except: # Handle exception quietly msg = ( "%s is not a valid video, audio, or image file." % item.name) log.error(msg) failed_files.append(item.name) # Delete all tracks track_list = copy.deepcopy(Track.filter()) for track in track_list: track.delete() # Create new tracks track_counter = 0 for legacy_t in reversed(v1_data.sequences[0].tracks): t = Track() t.data = { "number": track_counter, "y": 0, "label": legacy_t.name } t.save() track_counter += 1 # Loop through clips track_counter = 0 for sequence in v1_data.sequences: for track in reversed(sequence.tracks): for clip in track.clips: # Get associated file for this clip if clip.file_object.unique_id in file_lookup.keys( ): file = file_lookup[clip.file_object.unique_id] else: # Skip missing file log.info( "Skipping importing missing file: %s" % clip.file_object.unique_id) continue # Create clip if (file.data["media_type"] == "video" or file.data["media_type"] == "image"): # Determine thumb path thumb_path = os.path.join( info.THUMBNAIL_PATH, "%s.png" % file.data["id"]) else: # Audio file thumb_path = os.path.join( info.PATH, "images", "AudioThumbnail.png") # Get file name path, filename = os.path.split(file.data["path"]) # Convert path to the correct relative path (based on this folder) file_path = file.absolute_path() # Create clip object for this file c = openshot.Clip(file_path) # Append missing attributes to Clip JSON new_clip = json.loads(c.Json()) new_clip["file_id"] = file.id new_clip["title"] = filename new_clip["image"] = thumb_path # Check for optional start and end attributes new_clip["start"] = clip.start_time new_clip["end"] = clip.end_time new_clip["position"] = clip.position_on_track new_clip["layer"] = track_counter # Clear alpha (if needed) if clip.video_fade_in or clip.video_fade_out: new_clip["alpha"]["Points"] = [] # Video Fade IN if clip.video_fade_in: # Add keyframes start = openshot.Point( round(clip.start_time * fps_float) + 1, 0.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round((clip.start_time + clip.video_fade_in_amount) * fps_float) + 1, 1.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["alpha"]["Points"].append( start_object) new_clip["alpha"]["Points"].append(end_object) # Video Fade OUT if clip.video_fade_out: # Add keyframes start = openshot.Point( round((clip.end_time - clip.video_fade_out_amount) * fps_float) + 1, 1.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round(clip.end_time * fps_float) + 1, 0.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["alpha"]["Points"].append( start_object) new_clip["alpha"]["Points"].append(end_object) # Clear Audio (if needed) if clip.audio_fade_in or clip.audio_fade_out: new_clip["volume"]["Points"] = [] else: p = openshot.Point(1, clip.volume / 100.0, openshot.BEZIER) p_object = json.loads(p.Json()) new_clip["volume"] = {"Points": [p_object]} # Audio Fade IN if clip.audio_fade_in: # Add keyframes start = openshot.Point( round(clip.start_time * fps_float) + 1, 0.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round((clip.start_time + clip.video_fade_in_amount) * fps_float) + 1, clip.volume / 100.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["volume"]["Points"].append( start_object) new_clip["volume"]["Points"].append(end_object) # Audio Fade OUT if clip.audio_fade_out: # Add keyframes start = openshot.Point( round((clip.end_time - clip.video_fade_out_amount) * fps_float) + 1, clip.volume / 100.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point( round(clip.end_time * fps_float) + 1, 0.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["volume"]["Points"].append( start_object) new_clip["volume"]["Points"].append(end_object) # Save clip clip_object = Clip() clip_object.data = new_clip clip_object.save() # Loop through transitions for trans in track.transitions: # Fix default transition if not trans.resource or not os.path.exists( trans.resource): trans.resource = os.path.join( info.PATH, "transitions", "common", "fade.svg") # Open up QtImageReader for transition Image transition_reader = openshot.QtImageReader( trans.resource) trans_begin_value = 1.0 trans_end_value = -1.0 if trans.reverse: trans_begin_value = -1.0 trans_end_value = 1.0 brightness = openshot.Keyframe() brightness.AddPoint(1, trans_begin_value, openshot.BEZIER) brightness.AddPoint( round(trans.length * fps_float) + 1, trans_end_value, openshot.BEZIER) contrast = openshot.Keyframe(trans.softness * 10.0) # Create transition dictionary transitions_data = { "id": get_app().project.generate_id(), "layer": track_counter, "title": "Transition", "type": "Mask", "position": trans.position_on_track, "start": 0, "end": trans.length, "brightness": json.loads(brightness.Json()), "contrast": json.loads(contrast.Json()), "reader": json.loads(transition_reader.Json()), "replace_image": False } # Save transition t = Transition() t.data = transitions_data t.save() # Increment track counter track_counter += 1 except Exception as ex: # Error parsing legacy contents msg = _("Failed to load project file %(path)s: %(error)s" % { "path": file_path, "error": ex }) log.error(msg) raise Exception(msg) # Show warning if some files failed to load if failed_files: # Throw exception raise Exception( _("Failed to load the following files:\n%s" % ", ".join(failed_files))) # Return mostly empty project_data dict (with just the current version #) log.info("Successfully loaded legacy project file: %s" % file_path) return project_data
def mouseMoveEvent(self, event): # Get data model and selection model = self.clip_properties_model.model # Do not change selected row during mouse move if self.lock_selection and self.prev_row: row = self.prev_row else: row = self.indexAt(event.pos()).row() self.prev_row = row self.lock_selection = True if row is None: return if model.item(row, 0): self.selected_label = model.item(row, 0) self.selected_item = model.item(row, 1) # Is the user dragging on the value column if self.selected_label and self.selected_item: # Get the position of the cursor and % value value_column_x = self.columnViewportPosition(1) cursor_value = event.x() - value_column_x cursor_value_percent = cursor_value / self.columnWidth(1) try: cur_property = self.selected_label.data() except Exception: # If item is deleted during this drag... an exception can occur # Just ignore, since this is harmless return property_key = cur_property[0] property_name = cur_property[1]["name"] property_type = cur_property[1]["type"] property_max = cur_property[1]["max"] property_min = cur_property[1]["min"] readonly = cur_property[1]["readonly"] item_id, item_type = self.selected_item.data() # Bail if readonly if readonly: return # Get the original data of this item (prior to any updates, for the undo/redo system) if not self.original_data: # Ignore undo/redo history temporarily (to avoid a huge pile of undo/redo history) get_app().updates.ignore_history = True # Find this clip c = None if item_type == "clip": # Get clip object c = Clip.get(id=item_id) elif item_type == "transition": # Get transition object c = Transition.get(id=item_id) elif item_type == "effect": # Get effect object c = Effect.get(id=item_id) if c: if property_key in c.data: # Grab the original data for this item/property self.original_data = c.data # For numeric values, apply percentage within parameter's allowable range if property_type in ["float", "int"] and property_name != "Track": if self.previous_x == -1: # Start tracking movement (init diff_length and previous_x) self.diff_length = 10 self.previous_x = event.x() # Calculate # of pixels dragged drag_diff = self.previous_x - event.x() # update previous x self.previous_x = event.x() # Ignore small initial movements if abs(drag_diff) < self.diff_length: # Lower threshold to 0 incrementally, to guarantee it'll eventually be exceeded self.diff_length = max(0, self.diff_length - 1) return # Compute size of property's possible values range min_max_range = float(property_max) - float(property_min) if min_max_range < 1000.0: # Small range - use cursor to calculate new value as percentage of total range self.new_value = property_min + (min_max_range * cursor_value_percent) else: # range is unreasonably long (such as position, start, end, etc.... which can be huge #'s) # Get the current value and apply fixed adjustments in response to motion self.new_value = QLocale().system().toDouble( self.selected_item.text())[0] if drag_diff > 0: # Move to the left by a small amount self.new_value -= 0.50 elif drag_diff < 0: # Move to the right by a small amount self.new_value += 0.50 # Clamp value between min and max (just incase user drags too big) self.new_value = max(property_min, self.new_value) self.new_value = min(property_max, self.new_value) # Update value of this property self.clip_properties_model.value_updated( self.selected_item, -1, self.new_value) # Repaint self.viewport().update()
def mouseMoveEvent(self, event): # Get data model and selection model = self.clip_properties_model.model row = self.indexAt(event.pos()).row() column = self.indexAt(event.pos()).column() if model.item(row, 0): self.selected_label = model.item(row, 0) self.selected_item = model.item(row, 1) # Is the user dragging on the value column if self.selected_label and self.selected_item: frame_number = self.clip_properties_model.frame_number # Get the position of the cursor and % value value_column_x = self.columnViewportPosition(1) value_column_y = value_column_x + self.columnWidth(1) cursor_value = event.x() - value_column_x cursor_value_percent = cursor_value / self.columnWidth(1) try: property = self.selected_label.data() except Exception as ex: # If item is deleted during this drag... an exception can occur # Just ignore, since this is harmless return property_key = property[0] property_name = property[1]["name"] property_type = property[1]["type"] property_max = property[1]["max"] property_min = property[1]["min"] property_value = property[1]["value"] readonly = property[1]["readonly"] item_id, item_type = self.selected_item.data() # Bail if readonly if readonly: return # Get the original data of this item (prior to any updates, for the undo/redo system) if not self.original_data: # Ignore undo/redo history temporarily (to avoid a huge pile of undo/redo history) get_app().updates.ignore_history = True # Find this clip c = None if item_type == "clip": # Get clip object c = Clip.get(id=item_id) elif item_type == "transition": # Get transition object c = Transition.get(id=item_id) elif item_type == "effect": # Get effect object c = Effect.get(id=item_id) if c: if property_key in c.data: # Grab the original data for this item/property self.original_data = c.data # Calculate percentage value if property_type in ["float", "int"]: min_max_range = float(property_max) - float(property_min) # Determine if range is unreasonably long (such as position, start, end, etc.... which can be huge #'s) if min_max_range > 1000.0: # Get the current value self.new_value = QLocale().system().toDouble( self.selected_item.text())[0] # Huge range - increment / decrement slowly if self.previous_x == -1: # init previous_x for the first time self.previous_x = event.x() # calculate # of pixels dragged drag_diff = self.previous_x - event.x() if drag_diff > 0: # Move to the left by a small amount self.new_value -= 0.50 elif drag_diff < 0: # Move to the right by a small amount self.new_value += 0.50 # update previous x self.previous_x = event.x() else: # Small range - use cursor % to calculate new value self.new_value = property_min + (min_max_range * cursor_value_percent) # Clamp value between min and max (just incase user drags too big) self.new_value = max(property_min, self.new_value) self.new_value = min(property_max, self.new_value) # Update value of this property self.clip_properties_model.value_updated( self.selected_item, -1, self.new_value) # Repaint self.viewport().update()
def accept(self): """ Ok button clicked """ log.info('accept') # Get settings from form start_position = self.txtStartTime.value() track_num = self.cmbTrack.currentData() fade_value = self.cmbFade.currentData() fade_length = self.txtFadeLength.value() transition_path = self.cmbTransition.currentData() transition_length = self.txtTransitionLength.value() image_length = self.txtImageLength.value() zoom_value = self.cmbZoom.currentData() # Init position position = start_position random_transition = False if transition_path == "random": random_transition = True # Get frames per second fps = get_app().project.get(["fps"]) fps_float = float(fps["num"]) / float(fps["den"]) # Loop through each file (in the current order) for file in self.treeFiles.timeline_model.files: # Create a clip clip = Clip() clip.data = {} if (file.data["media_type"] == "video" or file.data["media_type"] == "image"): # Determine thumb path thumb_path = os.path.join(info.THUMBNAIL_PATH, "%s.png" % file.data["id"]) else: # Audio file thumb_path = os.path.join(info.PATH, "images", "AudioThumbnail.png") # Get file name path, filename = os.path.split(file.data["path"]) # Convert path to the correct relative path (based on this folder) file_path = file.absolute_path() # Create clip object for this file c = openshot.Clip(file_path) # Append missing attributes to Clip JSON new_clip = json.loads(c.Json()) new_clip["position"] = position new_clip["layer"] = track_num new_clip["file_id"] = file.id new_clip["title"] = filename new_clip["image"] = thumb_path # Overwrite frame rate (incase the user changed it in the File Properties) file_properties_fps = float(file.data["fps"]["num"]) / float(file.data["fps"]["den"]) file_fps = float(new_clip["reader"]["fps"]["num"]) / float(new_clip["reader"]["fps"]["den"]) fps_diff = file_fps / file_properties_fps new_clip["reader"]["fps"]["num"] = file.data["fps"]["num"] new_clip["reader"]["fps"]["den"] = file.data["fps"]["den"] # Scale duration / length / and end properties new_clip["reader"]["duration"] *= fps_diff new_clip["end"] *= fps_diff new_clip["duration"] *= fps_diff # Check for optional start and end attributes start_time = 0 end_time = new_clip["reader"]["duration"] if 'start' in file.data.keys(): start_time = file.data['start'] new_clip["start"] = start_time if 'end' in file.data.keys(): end_time = file.data['end'] new_clip["end"] = end_time # Adjust clip duration, start, and end new_clip["duration"] = new_clip["reader"]["duration"] if file.data["media_type"] == "image": end_time = image_length new_clip["end"] = end_time # Adjust Fade of Clips (if no transition is chosen) if not transition_path: if fade_value != None: # Overlap this clip with the previous one (if any) position = max(start_position, new_clip["position"] - fade_length) new_clip["position"] = position if fade_value == 'Fade In' or fade_value == 'Fade In & Out': start = openshot.Point((start_time * fps_float) + 1, 0.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point(min((start_time + fade_length) * fps_float, end_time * fps_float), 1.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip['alpha']["Points"].append(start_object) new_clip['alpha']["Points"].append(end_object) if fade_value == 'Fade Out' or fade_value == 'Fade In & Out': start = openshot.Point(max((end_time * fps_float) - (fade_length * fps_float), start_time * fps_float), 1.0, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point(end_time * fps_float, 0.0, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip['alpha']["Points"].append(start_object) new_clip['alpha']["Points"].append(end_object) # Adjust zoom amount if zoom_value != None: # Location animation if zoom_value == "Random": animate_start_x = uniform(-0.5, 0.5) animate_end_x = uniform(-0.15, 0.15) animate_start_y = uniform(-0.5, 0.5) animate_end_y = uniform(-0.15, 0.15) # Scale animation start_scale = uniform(0.5, 1.5) end_scale = uniform(0.85, 1.15) elif zoom_value == "Zoom In": animate_start_x = 0.0 animate_end_x = 0.0 animate_start_y = 0.0 animate_end_y = 0.0 # Scale animation start_scale = 1.0 end_scale = 1.25 elif zoom_value == "Zoom Out": animate_start_x = 0.0 animate_end_x = 0.0 animate_start_y = 0.0 animate_end_y = 0.0 # Scale animation start_scale = 1.25 end_scale = 1.0 # Add keyframes start = openshot.Point((start_time * fps_float) + 1, start_scale, openshot.BEZIER) start_object = json.loads(start.Json()) end = openshot.Point(end_time * fps_float, end_scale, openshot.BEZIER) end_object = json.loads(end.Json()) new_clip["gravity"] = openshot.GRAVITY_CENTER new_clip["scale_x"]["Points"].append(start_object) new_clip["scale_x"]["Points"].append(end_object) new_clip["scale_y"]["Points"].append(start_object) new_clip["scale_y"]["Points"].append(end_object) # Add keyframes start_x = openshot.Point((start_time * fps_float) + 1, animate_start_x, openshot.BEZIER) start_x_object = json.loads(start_x.Json()) end_x = openshot.Point(end_time * fps_float, animate_end_x, openshot.BEZIER) end_x_object = json.loads(end_x.Json()) start_y = openshot.Point((start_time * fps_float) + 1, animate_start_y, openshot.BEZIER) start_y_object = json.loads(start_y.Json()) end_y = openshot.Point(end_time * fps_float, animate_end_y, openshot.BEZIER) end_y_object = json.loads(end_y.Json()) new_clip["gravity"] = openshot.GRAVITY_CENTER new_clip["location_x"]["Points"].append(start_x_object) new_clip["location_x"]["Points"].append(end_x_object) new_clip["location_y"]["Points"].append(start_y_object) new_clip["location_y"]["Points"].append(end_y_object) if transition_path: # Add transition for this clip (if any) # Open up QtImageReader for transition Image if random_transition: random_index = randint(0, len(self.transitions)) transition_path = self.transitions[random_index] # Get reader for transition transition_reader = openshot.QtImageReader(transition_path) brightness = openshot.Keyframe() brightness.AddPoint(1, 1.0, openshot.BEZIER) brightness.AddPoint(min(transition_length, end_time - start_time) * fps_float, -1.0, openshot.BEZIER) contrast = openshot.Keyframe(3.0) # Create transition dictionary transitions_data = { "layer": track_num, "title": "Transition", "type": "Mask", "start": 0, "end": min(transition_length, end_time - start_time), "brightness": json.loads(brightness.Json()), "contrast": json.loads(contrast.Json()), "reader": json.loads(transition_reader.Json()), "replace_image": False } # Overlap this clip with the previous one (if any) position = max(start_position, position - transition_length) transitions_data["position"] = position new_clip["position"] = position # Create transition tran = Transition() tran.data = transitions_data tran.save() # Save Clip clip.data = new_clip clip.save() # Increment position by length of clip position += (end_time - start_time) # Accept dialog super(AddToTimeline, self).accept()
def value_updated(self, item, interpolation=-1, value=None, interpolation_details=[]): """ Table cell change event - also handles context menu to update interpolation value """ if self.ignore_update_signal: return # Get translation method _ = get_app()._tr # Determine what was changed property = self.model.item(item.row(), 0).data() property_name = property[1]["name"] closest_point_x = property[1]["closest_point_x"] previous_point_x = property[1]["previous_point_x"] property_type = property[1]["type"] property_key = property[0] clip_id, item_type = item.data() # Get value (if any) if item.text(): # Set and format value based on property type if value != None: # Override value new_value = value elif property_type == "string": # Use string value new_value = item.text() elif property_type == "bool": # Use boolean value if item.text() == _("False"): new_value = False else: new_value = True elif property_type == "int": # Use int value new_value = QLocale().system().toInt(item.text())[0] else: # Use decimal value new_value = QLocale().system().toFloat(item.text())[0] else: new_value = None log.info("%s for %s changed to %s at frame %s with interpolation: %s at closest x: %s" % (property_key, clip_id, new_value, self.frame_number, interpolation, closest_point_x)) # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info("value updated: %s" % c.data) # Check the type of property (some are keyframe, and some are not) if property_type != "reader" and type(c.data[property_key]) == dict: # Keyframe # Loop through points, find a matching points on this frame found_point = False point_to_delete = None for point in c.data[property_key]["Points"]: log.info("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"]["X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update or delete point if new_value != None: point["co"]["Y"] = float(new_value) log.info("updating point: co.X = %s to value: %s" % (point["co"]["X"], float(new_value))) else: point_to_delete = point break elif interpolation > -1 and point["co"]["X"] == previous_point_x: # Only update interpolation type (and the LEFT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_right"] = point.get("handle_right") or {"Y": 0.0, "X": 0.0} point["handle_right"]["X"] = interpolation_details[0] point["handle_right"]["Y"] = interpolation_details[1] log.info("updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) elif interpolation > -1 and point["co"]["X"] == closest_point_x: # Only update interpolation type (and the RIGHT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_left"] = point.get("handle_left") or {"Y": 0.0, "X": 0.0} point["handle_left"]["X"] = interpolation_details[2] point["handle_left"]["Y"] = interpolation_details[3] log.info("updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) # Delete point (if needed) if point_to_delete: clip_updated = True log.info("Found point to delete at X=%s" % point_to_delete["co"]["X"]) c.data[property_key]["Points"].remove(point_to_delete) # Create new point (if needed) elif not found_point and new_value != None: clip_updated = True log.info("Created new point at X=%s" % self.frame_number) c.data[property_key]["Points"].append({'co': {'X': self.frame_number, 'Y': new_value}, 'interpolation': 1}) if not clip_updated: # If no keyframe was found, set a basic property if property_type == "int": # Integer clip_updated = True c.data[property_key] = int(new_value) elif property_type == "float": # Float clip_updated = True c.data[property_key] = new_value elif property_type == "bool": # Boolean clip_updated = True c.data[property_key] = bool(new_value) elif property_type == "string": # String clip_updated = True c.data[property_key] = str(new_value) elif property_type == "reader": # Reader clip_updated = True # Transition try: clip_object = openshot.Clip(value) clip_object.Open() c.data[property_key] = json.loads(clip_object.Reader().Json()) clip_object.Close() clip_object = None except: log.info('Failed to load %s into Clip object for reader property' % value) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data.get(property_key)} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.refreshFrameSignal.emit() # Clear selection self.parent.clearSelection()
def mouseMoveEvent(self, event): # Get data model and selection model = self.clip_properties_model.model row = self.indexAt(event.pos()).row() column = self.indexAt(event.pos()).column() if model.item(row, 0): self.selected_label = model.item(row, 0) self.selected_item = model.item(row, 1) # Is the user dragging on the value column if self.selected_label and self.selected_item: frame_number = self.clip_properties_model.frame_number # Get the position of the cursor and % value value_column_x = self.columnViewportPosition(1) value_column_y = value_column_x + self.columnWidth(1) cursor_value = event.x() - value_column_x cursor_value_percent = cursor_value / self.columnWidth(1) property = self.selected_label.data() property_key = property[0] property_name = property[1]["name"] property_type = property[1]["type"] property_max = property[1]["max"] property_min = property[1]["min"] property_value = property[1]["value"] readonly = property[1]["readonly"] item_id, item_type = self.selected_item.data() # Bail if readonly if readonly: return # Get the original data of this item (prior to any updates, for the undo/redo system) if not self.original_data: # Ignore undo/redo history temporarily (to avoid a huge pile of undo/redo history) get_app().updates.ignore_history = True # Find this clip c = None if item_type == "clip": # Get clip object c = Clip.get(id=item_id) elif item_type == "transition": # Get transition object c = Transition.get(id=item_id) elif item_type == "effect": # Get effect object c = Effect.get(id=item_id) if c: if property_key in c.data: # Grab the original data for this item/property self.original_data = c.data # Calculate percentage value if property_type in ["float", "int"]: min_max_range = float(property_max) - float(property_min) # Determine if range is unreasonably long (such as position, start, end, etc.... which can be huge #'s) if min_max_range > 1000.0: # Get the current value self.new_value = QLocale().system().toDouble(self.selected_item.text())[0] # Huge range - increment / decrement slowly if self.previous_x == -1: # init previous_x for the first time self.previous_x = event.x() # calculate # of pixels dragged drag_diff = self.previous_x - event.x() if drag_diff > 0: # Move to the left by a small amount self.new_value -= 0.50 elif drag_diff < 0: # Move to the right by a small amount self.new_value += 0.50 # update previous x self.previous_x = event.x() else: # Small range - use cursor % to calculate new value self.new_value = property_min + (min_max_range * cursor_value_percent) # Clamp value between min and max (just incase user drags too big) self.new_value = max(property_min, self.new_value) self.new_value = min(property_max, self.new_value) # Update value of this property self.clip_properties_model.value_updated(self.selected_item, -1, self.new_value) # Repaint self.viewport().update()
def setUpClass(cls): """ Init unit test data """ # Create Qt application cls.app = QGuiApplication.instance() cls.clip_ids = [] cls.file_ids = [] cls.transition_ids = [] clips = [] # Insert some clips into the project data for num in range(5): # Create clip c = openshot.Clip(os.path.join(info.IMAGES_PATH, "AboutLogo.png")) c.Position(num * 10.0) c.End(5.0) # Parse JSON clip_data = json.loads(c.Json()) # Insert into project data query_clip = Clip() query_clip.data = clip_data query_clip.save() # Keep track of the ids cls.clip_ids.append(query_clip.id) clips.append(query_clip) # Insert some files into the project data for num in range(5): # Create file r = openshot.DummyReader(openshot.Fraction(24, 1), 640, 480, 44100, 2, 30.0) # Parse JSON file_data = json.loads(r.Json()) # Insert into project data query_file = File() query_file.data = file_data query_file.data["path"] = os.path.join(info.IMAGES_PATH, "AboutLogo.png") query_file.data["media_type"] = "image" query_file.save() # Keep track of the ids cls.file_ids.append(query_file.id) # Insert some transitions into the project data for c in clips: # Create mask object t = openshot.Mask() # Place over the last second of current clip pos = c.data.get("position", 0.0) start = c.data.get("start", 0.0) end = c.data.get("end", 0.0) t.Position((pos - start + end) - 1.0) t.End(1.0) # Insert into project data transitions_data = json.loads(t.Json()) query_transition = Transition() query_transition.data = transitions_data query_transition.save() # Keep track of the ids cls.transition_ids.append(query_transition.id) # Don't keep the full query objects around del clips
def value_updated(self, item, interpolation=-1, value=None): """ Table cell change event - also handles context menu to update interpolation value """ if self.ignore_update_signal: return # Get translation method _ = get_app()._tr # Determine what was changed property = self.model.item(item.row(), 0).data() property_name = property[1]["name"] closest_point_x = property[1]["closest_point_x"] property_type = property[1]["type"] property_key = property[0] clip_id, item_type = item.data() # Get value (if any) if item.text(): # Set and format value based on property type if value != None: # Override value new_value = value elif property_type == "string": # Use string value new_value = item.text() elif property_type == "bool": # Use boolean value if item.text() == _("False"): new_value = False else: new_value = True else: # Use numeric value new_value = QLocale().system().toFloat(item.text())[0] else: new_value = None log.info("%s for %s changed to %s at frame %s with interpolation: %s at closest x: %s" % (property_key, clip_id, new_value, self.frame_number, interpolation, closest_point_x)) # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info(c.data) # Check the type of property (some are keyframe, and some are not) if type(c.data[property_key]) == dict: # Keyframe # Loop through points, find a matching points on this frame found_point = False point_to_delete = None for point in c.data[property_key]["Points"]: log.info("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"]["X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update or delete point if new_value != None: point["co"]["Y"] = float(new_value) log.info("updating point: co.X = %s to value: %s" % (point["co"]["X"], float(new_value))) else: point_to_delete = point break elif interpolation > -1 and point["co"]["X"] == closest_point_x: # Only update interpolation type found_point = True clip_updated = True point["interpolation"] = interpolation log.info("updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) break # Delete point (if needed) if point_to_delete: clip_updated = True log.info("Found point to delete at X=%s" % point_to_delete["co"]["X"]) c.data[property_key]["Points"].remove(point_to_delete) # Create new point (if needed) elif not found_point and new_value != None: clip_updated = True log.info("Created new point at X=%s" % self.frame_number) c.data[property_key]["Points"].append({'co': {'X': self.frame_number, 'Y': new_value}, 'interpolation': 1}) elif property_type == "int": # Integer clip_updated = True c.data[property_key] = int(new_value) elif property_type == "float": # Float clip_updated = True c.data[property_key] = new_value elif property_type == "bool": # Boolean clip_updated = True c.data[property_key] = bool(new_value) elif property_type == "string": # String clip_updated = True c.data[property_key] = str(new_value) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data[property_key]} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.preview_thread.refreshFrame() # Clear selection self.parent.clearSelection()
def color_update(self, item, new_color, interpolation=-1): """Insert/Update a color keyframe for the selected row""" # Determine what was changed property = self.model.item(item.row(), 0).data() property_type = property[1]["type"] closest_point_x = property[1]["closest_point_x"] property_key = property[0] clip_id, item_type = item.data() if property_type == "color": # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info(c.data) # Loop through each keyframe (red, blue, and green) for color, new_value in [("red", new_color.red()), ("blue", new_color.blue()), ("green", new_color.green())]: # Keyframe # Loop through points, find a matching points on this frame found_point = False for point in c.data[property_key][color]["Points"]: log.info("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"]["X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update point point["co"]["Y"] = new_value log.info("updating point: co.X = %s to value: %s" % (point["co"]["X"], float(new_value))) break elif interpolation > -1 and point["co"]["X"] == closest_point_x: # Only update interpolation type found_point = True clip_updated = True point["interpolation"] = interpolation log.info("updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) break # Create new point (if needed) if not found_point: clip_updated = True log.info("Created new point at X=%s" % self.frame_number) c.data[property_key][color]["Points"].append({'co': {'X': self.frame_number, 'Y': new_value}, 'interpolation': 1}) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data[property_key]} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.preview_thread.refreshFrame() # Clear selection self.parent.clearSelection()
def remove_keyframe(self, item): """Remove an existing keyframe (if any)""" # Determine what was changed property = self.model.item(item.row(), 0).data() property_name = property[1]["name"] property_type = property[1]["type"] closest_point_x = property[1]["closest_point_x"] property_type = property[1]["type"] property_key = property[0] clip_id, item_type = item.data() # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info(c.data) # Determine type of keyframe (normal or color) keyframe_list = [] if property_type == "color": keyframe_list = [c.data[property_key]["red"], c.data[property_key]["blue"], c.data[property_key]["green"]] else: keyframe_list = [c.data[property_key]] # Loop through each keyframe (red, blue, and green) for keyframe in keyframe_list: # Keyframe # Loop through points, find a matching points on this frame closest_point = None point_to_delete = None for point in keyframe["Points"]: if point["co"]["X"] == self.frame_number: # Found point, Update value clip_updated = True point_to_delete = point break if point["co"]["X"] == closest_point_x: closest_point = point # If no point found, use closest point x if not point_to_delete: point_to_delete = closest_point # Delete point (if needed) if point_to_delete: clip_updated = True log.info("Found point to delete at X=%s" % point_to_delete["co"]["X"]) keyframe["Points"].remove(point_to_delete) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data[property_key]} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.preview_thread.refreshFrame() # Clear selection self.parent.clearSelection()
def changed(self, action): # Clear previous rects self.clip_rects.clear() self.clip_rects_selected.clear() self.marker_rects.clear() # Get layer lookup layers = {} for count, layer in enumerate(reversed(sorted(Track.filter()))): layers[layer.data.get('number')] = count # Wait for timeline object and valid scrollbar positions if hasattr(get_app().window, "timeline") and self.scrollbar_position[2] != 0.0: # Get max width of timeline project_duration = get_app().project.get("duration") pixels_per_second = self.width() / project_duration # Determine scale factor vertical_factor = self.height() / len(layers.keys()) for clip in Clip.filter(): # Calculate clip geometry (and cache it) clip_x = (clip.data.get('position', 0.0) * pixels_per_second) clip_y = layers.get(clip.data.get('layer', 0), 0) * vertical_factor clip_width = ( (clip.data.get('end', 0.0) - clip.data.get('start', 0.0)) * pixels_per_second) clip_rect = QRectF(clip_x, clip_y, clip_width, 1.0 * vertical_factor) if clip.id in get_app().window.selected_clips: # selected clip self.clip_rects_selected.append(clip_rect) else: # un-selected clip self.clip_rects.append(clip_rect) for clip in Transition.filter(): # Calculate clip geometry (and cache it) clip_x = (clip.data.get('position', 0.0) * pixels_per_second) clip_y = layers.get(clip.data.get('layer', 0), 0) * vertical_factor clip_width = ( (clip.data.get('end', 0.0) - clip.data.get('start', 0.0)) * pixels_per_second) clip_rect = QRectF(clip_x, clip_y, clip_width, 1.0 * vertical_factor) if clip.id in get_app().window.selected_transitions: # selected clip self.clip_rects_selected.append(clip_rect) else: # un-selected clip self.clip_rects.append(clip_rect) for marker in Marker.filter(): # Calculate clip geometry (and cache it) marker_x = (marker.data.get('position', 0.0) * pixels_per_second) marker_rect = QRectF(marker_x, 0, 0.5, len(layers) * vertical_factor) self.marker_rects.append(marker_rect) # Force re-paint self.update()
def color_update(self, item, new_color, interpolation=-1, interpolation_details=[]): """Insert/Update a color keyframe for the selected row""" # Determine what was changed property = self.model.item(item.row(), 0).data() property_type = property[1]["type"] closest_point_x = property[1]["closest_point_x"] previous_point_x = property[1]["previous_point_x"] property_key = property[0] clip_id, item_type = item.data() if property_type == "color": # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info("color update: %s" % c.data) # Loop through each keyframe (red, blue, and green) for color, new_value in [("red", new_color.red()), ("blue", new_color.blue()), ("green", new_color.green())]: # Keyframe # Loop through points, find a matching points on this frame found_point = False for point in c.data[property_key][color]["Points"]: log.info("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"][ "X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update point point["co"]["Y"] = new_value log.info( "updating point: co.X = %s to value: %s" % (point["co"]["X"], float(new_value))) break elif interpolation > -1 and point["co"][ "X"] == previous_point_x: # Only update interpolation type (and the LEFT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_right"] = point.get( "handle_right") or { "Y": 0.0, "X": 0.0 } point["handle_right"][ "X"] = interpolation_details[0] point["handle_right"][ "Y"] = interpolation_details[1] log.info( "updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) elif interpolation > -1 and point["co"][ "X"] == closest_point_x: # Only update interpolation type (and the RIGHT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_left"] = point.get( "handle_left") or { "Y": 0.0, "X": 0.0 } point["handle_left"][ "X"] = interpolation_details[2] point["handle_left"][ "Y"] = interpolation_details[3] log.info( "updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) # Create new point (if needed) if not found_point: clip_updated = True log.info("Created new point at X=%s" % self.frame_number) c.data[property_key][color]["Points"].append({ 'co': { 'X': self.frame_number, 'Y': new_value }, 'interpolation': 1 }) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data[property_key]} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.refreshFrameSignal.emit() # Clear selection self.parent.clearSelection()
def value_updated(self, item, interpolation=-1, value=None, interpolation_details=[]): """ Table cell change event - also handles context menu to update interpolation value """ if self.ignore_update_signal: return # Get translation method _ = get_app()._tr # Determine what was changed property = self.model.item(item.row(), 0).data() property_name = property[1]["name"] closest_point_x = property[1]["closest_point_x"] previous_point_x = property[1]["previous_point_x"] property_type = property[1]["type"] property_key = property[0] clip_id, item_type = item.data() # Get value (if any) if item.text(): # Set and format value based on property type if value != None: # Override value new_value = value elif property_type == "string": # Use string value new_value = item.text() elif property_type == "bool": # Use boolean value if item.text() == _("False"): new_value = False else: new_value = True elif property_type == "int": # Use int value new_value = QLocale().system().toInt(item.text())[0] else: # Use decimal value new_value = QLocale().system().toFloat(item.text())[0] else: new_value = None log.info( "%s for %s changed to %s at frame %s with interpolation: %s at closest x: %s" % (property_key, clip_id, new_value, self.frame_number, interpolation, closest_point_x)) # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Update clip attribute if property_key in c.data: log.info("value updated: %s" % c.data) # Check the type of property (some are keyframe, and some are not) if type(c.data[property_key]) == dict: # Keyframe # Loop through points, find a matching points on this frame found_point = False point_to_delete = None for point in c.data[property_key]["Points"]: log.info("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"][ "X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update or delete point if new_value != None: point["co"]["Y"] = float(new_value) log.info( "updating point: co.X = %s to value: %s" % (point["co"]["X"], float(new_value))) else: point_to_delete = point break elif interpolation > -1 and point["co"][ "X"] == previous_point_x: # Only update interpolation type (and the LEFT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_right"] = point.get( "handle_right") or { "Y": 0.0, "X": 0.0 } point["handle_right"][ "X"] = interpolation_details[0] point["handle_right"][ "Y"] = interpolation_details[1] log.info( "updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) elif interpolation > -1 and point["co"][ "X"] == closest_point_x: # Only update interpolation type (and the RIGHT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_left"] = point.get( "handle_left") or { "Y": 0.0, "X": 0.0 } point["handle_left"][ "X"] = interpolation_details[2] point["handle_left"][ "Y"] = interpolation_details[3] log.info( "updating interpolation mode point: co.X = %s to %s" % (point["co"]["X"], interpolation)) log.info("use interpolation preset: %s" % str(interpolation_details)) # Delete point (if needed) if point_to_delete: clip_updated = True log.info("Found point to delete at X=%s" % point_to_delete["co"]["X"]) c.data[property_key]["Points"].remove(point_to_delete) # Create new point (if needed) elif not found_point and new_value != None: clip_updated = True log.info("Created new point at X=%s" % self.frame_number) c.data[property_key]["Points"].append({ 'co': { 'X': self.frame_number, 'Y': new_value }, 'interpolation': 1 }) elif property_type == "int": # Integer clip_updated = True c.data[property_key] = int(new_value) elif property_type == "float": # Float clip_updated = True c.data[property_key] = new_value elif property_type == "bool": # Boolean clip_updated = True c.data[property_key] = bool(new_value) elif property_type == "string": # String clip_updated = True c.data[property_key] = str(new_value) # Reduce # of clip properties we are saving (performance boost) c.data = {property_key: c.data.get(property_key)} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.refreshFrameSignal.emit() # Clear selection self.parent.clearSelection()
def value_updated(self, item, interpolation=-1, value=None, interpolation_details=[]): """ Table cell change event - also handles context menu to update interpolation value """ if self.ignore_update_signal: return # Get translation method _ = get_app()._tr # Determine what was changed property = self.model.item(item.row(), 0).data() closest_point_x = property[1]["closest_point_x"] previous_point_x = property[1]["previous_point_x"] property_type = property[1]["type"] property_key = property[0] object_id = property[1]["object_id"] clip_id, item_type = item.data() # Get value (if any) if item.text(): # Set and format value based on property type if value is not None: # Override value new_value = value elif property_type == "string": # Use string value new_value = item.text() elif property_type == "bool": # Use boolean value if item.text() == _("False"): new_value = False else: new_value = True elif property_type == "int": # Use int value new_value = QLocale().system().toInt(item.text())[0] else: # Use decimal value new_value = QLocale().system().toFloat(item.text())[0] else: new_value = None log.info( "%s for %s changed to %s at frame %s with interpolation: %s at closest x: %s" % (property_key, clip_id, new_value, self.frame_number, interpolation, closest_point_x)) # Find this clip c = None clip_updated = False if item_type == "clip": # Get clip object c = Clip.get(id=clip_id) elif item_type == "transition": # Get transition object c = Transition.get(id=clip_id) elif item_type == "effect": # Get effect object c = Effect.get(id=clip_id) if c: # Create reference clip_data = c.data if object_id: clip_data = c.data.get('objects').get(object_id) # Update clip attribute if property_key in clip_data: log_id = "{}/{}".format(clip_id, object_id) if object_id else clip_id log.debug("%s: update property %s. %s", log_id, property_key, clip_data.get(property_key)) # Check the type of property (some are keyframe, and some are not) if property_type != "reader" and type( clip_data[property_key]) == dict: # Keyframe # Loop through points, find a matching points on this frame found_point = False point_to_delete = None for point in clip_data[property_key].get('Points', []): log.debug("looping points: co.X = %s" % point["co"]["X"]) if interpolation == -1 and point["co"][ "X"] == self.frame_number: # Found point, Update value found_point = True clip_updated = True # Update or delete point if new_value is not None: point["co"]["Y"] = float(new_value) log.debug( "updating point: co.X = %d to value: %.3f", point["co"]["X"], float(new_value)) else: point_to_delete = point break elif interpolation > -1 and point["co"][ "X"] == previous_point_x: # Only update interpolation type (and the LEFT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_right"] = point.get( "handle_right") or { "Y": 0.0, "X": 0.0 } point["handle_right"][ "X"] = interpolation_details[0] point["handle_right"][ "Y"] = interpolation_details[1] log.debug( "updating interpolation mode point: co.X = %d to %d", point["co"]["X"], interpolation) log.debug("use interpolation preset: %s", str(interpolation_details)) elif interpolation > -1 and point["co"][ "X"] == closest_point_x: # Only update interpolation type (and the RIGHT side of the curve) found_point = True clip_updated = True point["interpolation"] = interpolation if interpolation == 0: point["handle_left"] = point.get( "handle_left") or { "Y": 0.0, "X": 0.0 } point["handle_left"][ "X"] = interpolation_details[2] point["handle_left"][ "Y"] = interpolation_details[3] log.debug( "updating interpolation mode point: co.X = %d to %d", point["co"]["X"], interpolation) log.debug("use interpolation preset: %s", str(interpolation_details)) # Delete point (if needed) if point_to_delete: clip_updated = True log.debug("Found point to delete at X=%s" % point_to_delete["co"]["X"]) clip_data[property_key]["Points"].remove( point_to_delete) # Create new point (if needed) elif not found_point and new_value is not None: clip_updated = True log.debug("Created new point at X=%d", self.frame_number) clip_data[property_key].setdefault( 'Points', []).append({ 'co': { 'X': self.frame_number, 'Y': new_value }, 'interpolation': 1 }) if not clip_updated: # If no keyframe was found, set a basic property if property_type == "int": clip_updated = True try: clip_data[property_key] = int(new_value) except Exception as ex: log.warn( 'Invalid Integer value passed to property: %s' % ex) elif property_type == "float": clip_updated = True try: clip_data[property_key] = float(new_value) except Exception as ex: log.warn('Invalid Float value passed to property: %s' % ex) elif property_type == "bool": clip_updated = True try: clip_data[property_key] = bool(new_value) except Exception as ex: log.warn( 'Invalid Boolean value passed to property: %s' % ex) elif property_type == "string": clip_updated = True try: clip_data[property_key] = str(new_value) except Exception as ex: log.warn( 'Invalid String value passed to property: %s' % ex) elif property_type in ["font", "caption"]: clip_updated = True try: clip_data[property_key] = str(new_value) except Exception as ex: log.warn( 'Invalid Font/Caption value passed to property: %s' % ex) elif property_type == "reader": # Transition clip_updated = True try: clip_object = openshot.Clip(value) clip_object.Open() clip_data[property_key] = json.loads( clip_object.Reader().Json()) clip_object.Close() clip_object = None except Exception as ex: log.warn( 'Invalid Reader value passed to property: %s (%s)' % (value, ex)) # Reduce # of clip properties we are saving (performance boost) clip_data = {property_key: clip_data.get(property_key)} if object_id: clip_data = {'objects': {object_id: clip_data}} # Save changes if clip_updated: # Save c.save() # Update the preview get_app().window.refreshFrameSignal.emit() log.info("Item %s: changed %s to %s at frame %s (x: %s)" % (clip_id, property_key, new_value, self.frame_number, closest_point_x)) # Clear selection self.parent.clearSelection()