Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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())))
Ejemplo n.º 4
0
    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())))
Ejemplo n.º 5
0
    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()),
                ))
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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()
Ejemplo n.º 11
0
    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))
Ejemplo n.º 12
0
    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 &amp; 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 &copy; %(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")