def get_animation_details(self): """ Build a dictionary of all animation settings and properties from XML """ if not self.selected: return {} elif self.selected and self.selected.row() == -1: return {} # Get all selected rows items ItemRow = self.blender_model.model.itemFromIndex(self.selected).row() animation_title = self.blender_model.model.item(ItemRow, 1).text() xml_path = self.blender_model.model.item(ItemRow, 2).text() service = self.blender_model.model.item(ItemRow, 3).text() # load xml effect file xmldoc = xml.parse(xml_path) # Get list of params animation = { "title": animation_title, "path": xml_path, "service": service, "params": [] } # Loop through params for param in xmldoc.getElementsByTagName("param"): # Set up item dict, "default" key is required param_item = {"default": ""} # Get details of param for att in ["title", "description", "name", "type"]: if param.attributes[att]: param_item[att] = param.attributes[att].value for tag in ["min", "max", "step", "digits", "default"]: for p in param.getElementsByTagName(tag): if p.childNodes: param_item[tag] = p.firstChild.data try: # Build values dict from list of (name, num) tuples param_item["values"] = dict([ (p.attributes["name"].value, p.attributes["num"].value) for p in param.getElementsByTagName("value") if ("name" in p.attributes and "num" in p.attributes) ]) except (TypeError, AttributeError) as ex: log.warn("XML parser: %s", ex) pass # Append param object to list animation["params"].append(param_item) # Free up XML document memory xmldoc.unlink() # Return animation dictionary return animation
def get_icon(theme_name): """Get either the current theme icon or fallback to default theme (for custom icons). Returns None if none found or empty name.""" if theme_name: has_icon = QIcon.hasThemeIcon(theme_name) if not has_icon: log.warn('Icon theme {} not found. Will use backup icon.'.format(theme_name)) fallback_icon, fallback_path = get_default_icon(theme_name) # log.info('Fallback icon path for {} is {}'.format(theme_name, fallback_path)) if has_icon or fallback_icon: return QIcon.fromTheme(theme_name, fallback_icon) return None
def set(self, key, value): """ Store setting, but adding isn't allowed. All possible settings must be in default settings file. """ key = key.lower() # Load user setting's values (for easy merging) user_values = {} for item in self._data: if "setting" in item and "value" in item: user_values[item["setting"].lower()] = item # Save setting if key in user_values: user_values[key]["value"] = value else: log.warn("{} key '{}' not valid. The following are valid: {}".format(self.data_type, key, list(self._data.keys())))
def set(self, key, value): """ Store setting, but adding isn't allowed. All possible settings must be in default settings file. """ key = key.lower() # Index settings values (for easy updates) user_values = { item["setting"].lower(): item for item in self._data if all(["setting" in item, "value" in item]) } # Save setting if key in user_values: user_values[key].update({"value": value}) else: log.warn( "{} key '{}' not valid. The following are valid: {}".format( self.data_type, key, list(self._data.keys()), ))
def get(self, key): """ Get copied value of a given key in data store """ # Verify key is valid type if not isinstance(key, list): log.warning("get() key must be a list. key: {}".format(key)) return None if not key: log.warning("Cannot get empty key.") return None # Get reference to internal data structure obj = self._data # Iterate through key list finding sub-objects either by name or by an object match criteria such as {"id":"ADB34"}. for key_index in range(len(key)): key_part = key[key_index] # Key_part must be a string or dictionary if not isinstance(key_part, dict) and not isinstance(key_part, str): log.error("Unexpected key part type: {}".format(type(key_part).__name__)) return None # If key_part is a dictionary and obj is a list or dict, each key is tested as a property of the items in the current object # in the project data structure, and the first match is returned. if isinstance(key_part, dict) and isinstance(obj, list): # Overall status of finding a matching sub-object found = False # Loop through each item in object to find match for item_index in range(len(obj)): item = obj[item_index] # True until something disqualifies this as a match match = True # Check each key in key_part dictionary and if not found to be equal as a property in item, move on to next item in list for subkey in key_part.keys(): # Get each key in dictionary (i.e. "id", "layer", etc...) subkey = subkey.lower() # If object is missing the key or the values differ, then it doesn't match. if not (subkey in item and item[subkey] == key_part[subkey]): match = False break # If matched, set key_part to index of list or dict and stop loop if match: found = True obj = item break # No match found, return None if not found: return None # If key_part is a string, homogenize to lower case for comparisons if isinstance(key_part, str): key_part = key_part.lower() # Check current obj type (should be dictionary) if not isinstance(obj, dict): log.warn( "Invalid project data structure. Trying to use a key on a non-dictionary object. Key part: {} (\"{}\").\nKey: {}".format( (key_index), key_part, key)) return None # If next part of path isn't in current dictionary, return failure if not key_part in obj: log.warn("Key not found in project. Mismatch on key part {} (\"{}\").\nKey: {}".format((key_index), key_part, key)) return None # Get the matching item obj = obj[key_part] # After processing each key, we've found object, return copy of it return copy.deepcopy(obj)
def _set(self, key, values=None, add=False, partial_update=False, remove=False): """ Store setting, but adding isn't allowed. All possible settings must be in default settings file. """ log.info( "_set key: {} values: {} add: {} partial: {} remove: {}".format(key, values, add, partial_update, remove)) parent, my_key = None, "" # Verify key is valid type if not isinstance(key, list): log.warning("_set() key must be a list. key: {}".format(key)) return None if not key: log.warning("Cannot set empty key.") return None # Get reference to internal data structure obj = self._data # Iterate through key list finding sub-objects either by name or by an object match criteria such as {"id":"ADB34"}. for key_index in range(len(key)): key_part = key[key_index] # Key_part must be a string or dictionary if not isinstance(key_part, dict) and not isinstance(key_part, str): log.error("Unexpected key part type: {}".format(type(key_part).__name__)) return None # If key_part is a dictionary and obj is a list or dict, each key is tested as a property of the items in the current object # in the project data structure, and the first match is returned. if isinstance(key_part, dict) and isinstance(obj, list): # Overall status of finding a matching sub-object found = False # Loop through each item in object to find match for item_index in range(len(obj)): item = obj[item_index] # True until something disqualifies this as a match match = True # Check each key in key_part dictionary and if not found to be equal as a property in item, move on to next item in list for subkey in key_part.keys(): # Get each key in dictionary (i.e. "id", "layer", etc...) subkey = subkey.lower() # If object is missing the key or the values differ, then it doesn't match. if not (subkey in item and item[subkey] == key_part[subkey]): match = False break # If matched, set key_part to index of list or dict and stop loop if match: found = True obj = item my_key = item_index break # No match found, return None if not found: return None # If key_part is a string, homogenize to lower case for comparisons if isinstance(key_part, str): key_part = key_part.lower() # Check current obj type (should be dictionary) if not isinstance(obj, dict): return None # If next part of path isn't in current dictionary, return failure if not key_part in obj: log.warn("Key not found in project. Mismatch on key part {} (\"{}\").\nKey: {}".format((key_index), key_part, key)) return None # Get sub-object based on part key as new object, continue to next part obj = obj[key_part] my_key = key_part # Set parent to the last set obj (if not final iteration) if key_index < (len(key) - 1) or key_index == 0: parent = obj # After processing each key, we've found object and parent, return former value/s on update ret = copy.deepcopy(obj) # Apply the correct action to the found item if remove: del parent[my_key] else: # Add or Full Update # For adds to list perform an insert to index or the end if not specified if add and isinstance(parent, list): # log.info("adding to list") parent.append(values) # Otherwise, set the given index elif isinstance(values, dict): # Update existing dictionary value obj.update(values) else: # Update root string self._data[my_key] = values # Return the previous value to the matching item (used for history tracking) return ret
def get(self, key): """ Get copied value of a given key in data store """ # Verify key is valid type if not isinstance(key, list): log.warning("get() key must be a list. key: {}".format(key)) return None if not key: log.warning("Cannot get empty key.") return None # Get reference to internal data structure obj = self._data # Iterate through key list finding sub-objects either by name or by an object match criteria such as {"id":"ADB34"}. for key_index in range(len(key)): key_part = key[key_index] # Key_part must be a string or dictionary if not isinstance(key_part, dict) and not isinstance( key_part, str): log.error("Unexpected key part type: {}".format( type(key_part).__name__)) return None # If key_part is a dictionary and obj is a list or dict, each key is tested as a property of the items in the current object # in the project data structure, and the first match is returned. if isinstance(key_part, dict) and isinstance(obj, list): # Overall status of finding a matching sub-object found = False # Loop through each item in object to find match for item_index in range(len(obj)): item = obj[item_index] # True until something disqualifies this as a match match = True # Check each key in key_part dictionary and if not found to be equal as a property in item, move on to next item in list for subkey in key_part.keys(): # Get each key in dictionary (i.e. "id", "layer", etc...) subkey = subkey.lower() # If object is missing the key or the values differ, then it doesn't match. if not (subkey in item and item[subkey] == key_part[subkey]): match = False break # If matched, set key_part to index of list or dict and stop loop if match: found = True obj = item break # No match found, return None if not found: return None # If key_part is a string, homogenize to lower case for comparisons if isinstance(key_part, str): key_part = key_part.lower() # Check current obj type (should be dictionary) if not isinstance(obj, dict): log.warn( "Invalid project data structure. Trying to use a key on a non-dictionary object. Key part: {} (\"{}\").\nKey: {}" .format((key_index), key_part, key)) return None # If next part of path isn't in current dictionary, return failure if not key_part in obj: log.warn( "Key not found in project. Mismatch on key part {} (\"{}\").\nKey: {}" .format((key_index), key_part, key)) return None # Get the matching item obj = obj[key_part] # After processing each key, we've found object, return copy of it return copy.deepcopy(obj)
def _set(self, key, values=None, add=False, partial_update=False, remove=False): """ Store setting, but adding isn't allowed. All possible settings must be in default settings file. """ log.info( "_set key: {} values: {} add: {} partial: {} remove: {}".format( key, values, add, partial_update, remove)) parent, my_key = None, "" # Verify key is valid type if not isinstance(key, list): log.warning("_set() key must be a list. key: {}".format(key)) return None if not key: log.warning("Cannot set empty key.") return None # Get reference to internal data structure obj = self._data # Iterate through key list finding sub-objects either by name or by an object match criteria such as {"id":"ADB34"}. for key_index in range(len(key)): key_part = key[key_index] # Key_part must be a string or dictionary if not isinstance(key_part, dict) and not isinstance( key_part, str): log.error("Unexpected key part type: {}".format( type(key_part).__name__)) return None # If key_part is a dictionary and obj is a list or dict, each key is tested as a property of the items in the current object # in the project data structure, and the first match is returned. if isinstance(key_part, dict) and isinstance(obj, list): # Overall status of finding a matching sub-object found = False # Loop through each item in object to find match for item_index in range(len(obj)): item = obj[item_index] # True until something disqualifies this as a match match = True # Check each key in key_part dictionary and if not found to be equal as a property in item, move on to next item in list for subkey in key_part.keys(): # Get each key in dictionary (i.e. "id", "layer", etc...) subkey = subkey.lower() # If object is missing the key or the values differ, then it doesn't match. if not (subkey in item and item[subkey] == key_part[subkey]): match = False break # If matched, set key_part to index of list or dict and stop loop if match: found = True obj = item my_key = item_index break # No match found, return None if not found: return None # If key_part is a string, homogenize to lower case for comparisons if isinstance(key_part, str): key_part = key_part.lower() # Check current obj type (should be dictionary) if not isinstance(obj, dict): return None # If next part of path isn't in current dictionary, return failure if not key_part in obj: log.warn( "Key not found in project. Mismatch on key part {} (\"{}\").\nKey: {}" .format((key_index), key_part, key)) return None # Get sub-object based on part key as new object, continue to next part obj = obj[key_part] my_key = key_part # Set parent to the last set obj (if not final iteration) if key_index < (len(key) - 1) or key_index == 0: parent = obj # After processing each key, we've found object and parent, return former value/s on update ret = copy.deepcopy(obj) # Apply the correct action to the found item if remove: del parent[my_key] else: # Add or Full Update # For adds to list perform an insert to index or the end if not specified if add and isinstance(parent, list): # log.info("adding to list") parent.append(values) # Otherwise, set the given index elif isinstance(values, dict): # Update existing dictionary value obj.update(values) else: # Update root string self._data[my_key] = values # Return the previous value to the matching item (used for history tracking) return ret
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": clip_updated = True try: c.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: c.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: c.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: c.data[property_key] = str(new_value) except Exception as ex: log.warn( 'Invalid String value passed to property: %s' % ex) elif property_type == "reader": # Transition clip_updated = True 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 Exception as ex: log.warn( 'Invalid Reader value passed to property: %s (%s)' % (value, ex)) # 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 __init__(self): # Create dialog class QDialog.__init__(self) # Load UI from designer ui_util.load_ui(self, self.ui_path) # Init Ui ui_util.init_ui(self) # get translations _ = get_app()._tr # Connections to objects imported from .ui file tab = { "openshot-qt": self.tab_openshot_qt, "libopenshot": self.tab_libopenshot, "libopenshot-audio": self.tab_libopenshot_audio, } vbox = { "openshot-qt": self.vbox_openshot_qt, "libopenshot": self.vbox_libopenshot, "libopenshot-audio": self.vbox_libopenshot_audio, } filter = { "openshot-qt": self.txtChangeLogFilter_openshot_qt, "libopenshot": self.txtChangeLogFilter_libopenshot, "libopenshot-audio": self.txtChangeLogFilter_libopenshot_audio, } # Update github link button github_text = _("OpenShot on GitHub") github_html = ''' <html><head/><body> <p align="center"> <a href="https://github.com/OpenShot/"> <span style=" text-decoration: underline; color:#55aaff;">%s</span> </a> </p> </body></html> ''' % (github_text) self.lblGitHubLink.setText(github_html) # Read changelog file for each project for project in ['openshot-qt', 'libopenshot', 'libopenshot-audio']: new_changelog_path = os.path.join(info.PATH, 'resources', '{}.log'.format(project)) old_changelog_path = os.path.join(info.PATH, 'settings', '{}.log'.format(project)) if os.path.exists(new_changelog_path): log.debug("Reading changelog file: {}".format(new_changelog_path)) changelog_list = parse_new_changelog(new_changelog_path) elif os.path.isfile(old_changelog_path): log.debug("Reading legacy changelog file: {}".format(old_changelog_path)) changelog_list = parse_changelog(old_changelog_path) else: changelog_list = None # Hopefully we found ONE of the two if changelog_list is None: log.warn("Could not load changelog for {}".format(project)) # Hide the tab for this changelog tabindex = self.tabChangelog.indexOf(tab[project]) if tabindex >= 0: self.tabChangelog.removeTab(tabindex) continue # Populate listview widget with changelog data cl_treeview = ChangelogTreeView( commits=changelog_list, commit_url="https://github.com/OpenShot/{}/commit/%s/".format(project)) vbox[project].addWidget(cl_treeview) filter[project].textChanged.connect( partial(self.Filter_Triggered, filter[project], cl_treeview))
def __init__(self): # Create dialog class QDialog.__init__(self) # Load UI from designer & init ui_util.load_ui(self, self.ui_path) ui_util.init_ui(self) # get translations self.app = get_app() _ = self.app._tr # Hide chnagelog button by default self.btnchangelog.setVisible(False) projects = ['openshot-qt', 'libopenshot', 'libopenshot-audio'] # Old paths paths = [os.path.join(info.PATH, 'settings', '{}.log'.format(p)) for p in projects] # New paths paths.extend([os.path.join(info.PATH, 'resources', '{}.log'.format(p)) for p in projects]) if any([os.path.exists(path) for path in paths]): self.btnchangelog.setVisible(True) else: log.warn("No changelog files found, disabling button") create_text = _('Create & Edit Amazing Videos and Movies') description_text = _( "OpenShot Video Editor 2.x is the next generation of the award-winning <br/>" "OpenShot video editing platform.") learnmore_text = _('Learn more') copyright_text = _('Copyright © %(begin_year)s-%(current_year)s') % { 'begin_year': '2008', 'current_year': str(datetime.datetime.today().year) } about_html = ''' <html><head/><body><hr/> <p align="center"> <span style=" font-size:10pt; font-weight:600;">%s</span> </p> <p align="center"> <span style=" font-size:10pt;">%s </span> <a href="https://www.openshot.org/%s?r=about-us"> <span style=" font-size:10pt; text-decoration: none; color:#55aaff;">%s</span> </a> <span style=" font-size:10pt;">.</span> </p> </body></html> ''' % ( create_text, description_text, info.website_language(), learnmore_text) company_html = ''' <html><head/> <body style="font-size:11pt; font-weight:400; font-style:normal;"> <hr /> <p align="center" style="margin:12px 12px 0 0; -qt-block-indent:0; text-indent:0;"> <span style="font-size:10pt; font-weight:600;">%s </span> <a href="http://www.openshotstudios.com?r=about-us"> <span style="font-size:10pt; font-weight:600; text-decoration: none; color:#55aaff;"> OpenShot Studios, LLC<br /></span></a> </p> </body></html> ''' % (copyright_text) # Set description and company labels self.lblAboutDescription.setText(about_html) self.lblAboutCompany.setText(company_html) # set events handlers self.btncredit.clicked.connect(self.load_credit) self.btnlicense.clicked.connect(self.load_license) self.btnchangelog.clicked.connect(self.load_changelog) # Look for frozen version info frozen_version_label = "" version_path = os.path.join(info.PATH, "settings", "version.json") if os.path.exists(version_path): with open(version_path, "r", encoding="UTF-8") as f: version_info = json.loads(f.read()) if version_info: frozen_version_label = "<br/><br/><b>%s</b><br/>Build Date: %s" % \ (version_info.get('build_name'), version_info.get('date')) # Init some variables openshot_qt_version = _("Version: %s") % info.VERSION libopenshot_version = "libopenshot: %s" % openshot.OPENSHOT_VERSION_FULL self.txtversion.setText( "<b>%s</b><br/>%s%s" % (openshot_qt_version, libopenshot_version, frozen_version_label)) self.txtversion.setAlignment(Qt.AlignCenter) # Track metrics track_metric_screen("about-screen")