Repos can be refreshed through Project Manager individually or all at once

Signed-off-by: nggieber <nggieber@amazon.com>
monroegm-disable-blank-issue-2
nggieber 4 years ago
parent 74d74050f2
commit b4a85a2ed6

@ -108,7 +108,7 @@ namespace O3DE::ProjectManager
// Draw refresh button // Draw refresh button
painter->drawPixmap( painter->drawPixmap(
repoUpdatedDateRect.left() + repoUpdatedDateRect.width() + s_refreshIconSpacing, repoUpdatedDateRect.left() + s_updatedMaxWidth + s_refreshIconSpacing,
contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better
m_refreshIcon); m_refreshIcon);
@ -150,6 +150,11 @@ namespace O3DE::ProjectManager
emit RemoveRepo(modelIndex); emit RemoveRepo(modelIndex);
return true; return true;
} }
else if (keyEvent->key() == Qt::Key_R)
{
emit RefreshRepo(modelIndex);
return true;
}
} }
if (event->type() == QEvent::MouseButtonPress) if (event->type() == QEvent::MouseButtonPress)
@ -160,6 +165,7 @@ namespace O3DE::ProjectManager
CalcRects(option, fullRect, itemRect, contentRect); CalcRects(option, fullRect, itemRect, contentRect);
const QRect buttonRect = CalcButtonRect(contentRect); const QRect buttonRect = CalcButtonRect(contentRect);
const QRect deleteButtonRect = CalcDeleteButtonRect(contentRect); const QRect deleteButtonRect = CalcDeleteButtonRect(contentRect);
const QRect refreshButtonRect = CalcRefreshButtonRect(contentRect, buttonRect);
if (buttonRect.contains(mouseEvent->pos())) if (buttonRect.contains(mouseEvent->pos()))
{ {
@ -172,6 +178,11 @@ namespace O3DE::ProjectManager
emit RemoveRepo(modelIndex); emit RemoveRepo(modelIndex);
return true; return true;
} }
else if (refreshButtonRect.contains(mouseEvent->pos()))
{
emit RefreshRepo(modelIndex);
return true;
}
} }
return QStyledItemDelegate::editorEvent(event, model, option, modelIndex); return QStyledItemDelegate::editorEvent(event, model, option, modelIndex);
@ -231,6 +242,13 @@ namespace O3DE::ProjectManager
return QRect(topLeft, QSize(s_iconSize, s_iconSize)); return QRect(topLeft, QSize(s_iconSize, s_iconSize));
} }
QRect GemRepoItemDelegate::CalcRefreshButtonRect(const QRect& contentRect, const QRect& buttonRect) const
{
const int topLeftX = buttonRect.left() + s_buttonWidth + s_buttonSpacing + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 2 + s_refreshIconSpacing;
const QPoint topLeft = QPoint(topLeftX, contentRect.center().y() - s_refreshIconSize / 3);
return QRect(topLeft, QSize(s_refreshIconSize, s_refreshIconSize));
}
void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const
{ {
painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon); painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon);

@ -68,12 +68,14 @@ namespace O3DE::ProjectManager
signals: signals:
void RemoveRepo(const QModelIndex& modelIndex); void RemoveRepo(const QModelIndex& modelIndex);
void RefreshRepo(const QModelIndex& modelIndex);
protected: protected:
void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const;
QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const;
QRect CalcButtonRect(const QRect& contentRect) const; QRect CalcButtonRect(const QRect& contentRect) const;
QRect CalcDeleteButtonRect(const QRect& contentRect) const; QRect CalcDeleteButtonRect(const QRect& contentRect) const;
QRect CalcRefreshButtonRect(const QRect& contentRect, const QRect& buttonRect) const;
void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
void DrawEditButtons(QPainter* painter, const QRect& contentRect) const; void DrawEditButtons(QPainter* painter, const QRect& contentRect) const;

@ -24,6 +24,7 @@ namespace O3DE::ProjectManager
GemRepoItemDelegate* itemDelegate = new GemRepoItemDelegate(model, this); GemRepoItemDelegate* itemDelegate = new GemRepoItemDelegate(model, this);
connect(itemDelegate, &GemRepoItemDelegate::RemoveRepo, this, &GemRepoListView::RemoveRepo); connect(itemDelegate, &GemRepoItemDelegate::RemoveRepo, this, &GemRepoListView::RemoveRepo);
connect(itemDelegate, &GemRepoItemDelegate::RefreshRepo, this, &GemRepoListView::RefreshRepo);
setItemDelegate(itemDelegate); setItemDelegate(itemDelegate);
} }
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -28,5 +28,6 @@ namespace O3DE::ProjectManager
signals: signals:
void RemoveRepo(const QModelIndex& modelIndex); void RemoveRepo(const QModelIndex& modelIndex);
void RefreshRepo(const QModelIndex& modelIndex);
}; };
} // namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager

@ -126,6 +126,36 @@ namespace O3DE::ProjectManager
} }
} }
void GemRepoScreen::HandleRefreshAllButton()
{
bool refreshResult = PythonBindingsInterface::Get()->RefreshAllGemRepos();
Reinit();
if (!refreshResult)
{
QMessageBox::critical(
this, tr("Operation failed"), QString("Some repos failed to refresh."));
}
}
void GemRepoScreen::HandleRefreshRepoButton(const QModelIndex& modelIndex)
{
const QString repoUri = m_gemRepoModel->GetRepoUri(modelIndex);
AZ::Outcome<void, AZStd::string> refreshResult = PythonBindingsInterface::Get()->RefreshGemRepo(repoUri);
if (refreshResult.IsSuccess())
{
Reinit();
}
else
{
QMessageBox::critical(
this, tr("Operation failed"),
QString("Failed to refresh gem repo %1<br>Error:<br>%2")
.arg(m_gemRepoModel->GetName(modelIndex), refreshResult.GetError().c_str()));
}
}
void GemRepoScreen::FillModel() void GemRepoScreen::FillModel()
{ {
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos(); AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos();
@ -237,6 +267,8 @@ namespace O3DE::ProjectManager
m_AllUpdateButton->setObjectName("gemRepoHeaderRefreshButton"); m_AllUpdateButton->setObjectName("gemRepoHeaderRefreshButton");
topMiddleHLayout->addWidget(m_AllUpdateButton); topMiddleHLayout->addWidget(m_AllUpdateButton);
connect(m_AllUpdateButton, &QPushButton::clicked, this, &GemRepoScreen::HandleRefreshAllButton);
topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
QPushButton* addRepoButton = new QPushButton(tr("Add Repository"), this); QPushButton* addRepoButton = new QPushButton(tr("Add Repository"), this);
@ -280,6 +312,7 @@ namespace O3DE::ProjectManager
middleVLayout->addWidget(m_gemRepoListView); middleVLayout->addWidget(m_gemRepoListView);
connect(m_gemRepoListView, &GemRepoListView::RemoveRepo, this, &GemRepoScreen::HandleRemoveRepoButton); connect(m_gemRepoListView, &GemRepoListView::RemoveRepo, this, &GemRepoScreen::HandleRemoveRepoButton);
connect(m_gemRepoListView, &GemRepoListView::RefreshRepo, this, &GemRepoScreen::HandleRefreshRepoButton);
hLayout->addLayout(middleVLayout); hLayout->addLayout(middleVLayout);

@ -40,6 +40,8 @@ namespace O3DE::ProjectManager
public slots: public slots:
void HandleAddRepoButton(); void HandleAddRepoButton();
void HandleRemoveRepoButton(const QModelIndex& modelIndex); void HandleRemoveRepoButton(const QModelIndex& modelIndex);
void HandleRefreshAllButton();
void HandleRefreshRepoButton(const QModelIndex& modelIndex);
private: private:
void FillModel(); void FillModel();

@ -301,6 +301,7 @@ namespace O3DE::ProjectManager
m_enableGemProject = pybind11::module::import("o3de.enable_gem"); m_enableGemProject = pybind11::module::import("o3de.enable_gem");
m_disableGemProject = pybind11::module::import("o3de.disable_gem"); m_disableGemProject = pybind11::module::import("o3de.disable_gem");
m_editProjectProperties = pybind11::module::import("o3de.project_properties"); m_editProjectProperties = pybind11::module::import("o3de.project_properties");
m_repo = pybind11::module::import("o3de.repo");
m_pathlib = pybind11::module::import("pathlib"); m_pathlib = pybind11::module::import("pathlib");
// make sure the engine is registered // make sure the engine is registered
@ -939,6 +940,46 @@ namespace O3DE::ProjectManager
} }
} }
AZ::Outcome<void, AZStd::string> PythonBindings::RefreshGemRepo(const QString& repoUri)
{
bool refreshResult = false;
AZ::Outcome<void, AZStd::string> result = ExecuteWithLockErrorHandling(
[&]
{
auto pyUri = QString_To_Py_String(repoUri);
auto pythonRefreshResult = m_repo.attr("refresh_repo")(pyUri);
// Returns an exit code so boolify it then invert result
refreshResult = !pythonRefreshResult.cast<bool>();
});
if (!result.IsSuccess())
{
return result;
}
else if (!refreshResult)
{
return AZ::Failure<AZStd::string>("Failed to refresh repo.");
}
return AZ::Success();
}
bool PythonBindings::RefreshAllGemRepos()
{
bool refreshResult = false;
bool result = ExecuteWithLock(
[&]
{
auto pythonRefreshResult = m_repo.attr("refresh_repos")();
// Returns an exit code so boolify it then invert result
refreshResult = !pythonRefreshResult.cast<bool>();
});
return result && refreshResult;
}
bool PythonBindings::AddGemRepo(const QString& repoUri) bool PythonBindings::AddGemRepo(const QString& repoUri)
{ {
bool registrationResult = false; bool registrationResult = false;

@ -58,6 +58,8 @@ namespace O3DE::ProjectManager
AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) override; AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) override;
// Gem Repos // Gem Repos
AZ::Outcome<void, AZStd::string> RefreshGemRepo(const QString& repoUri) override;
bool RefreshAllGemRepos() override;
bool AddGemRepo(const QString& repoUri) override; bool AddGemRepo(const QString& repoUri) override;
bool RemoveGemRepo(const QString& repoUri) override; bool RemoveGemRepo(const QString& repoUri) override;
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override; AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override;
@ -87,6 +89,7 @@ namespace O3DE::ProjectManager
pybind11::handle m_enableGemProject; pybind11::handle m_enableGemProject;
pybind11::handle m_disableGemProject; pybind11::handle m_disableGemProject;
pybind11::handle m_editProjectProperties; pybind11::handle m_editProjectProperties;
pybind11::handle m_repo;
pybind11::handle m_pathlib; pybind11::handle m_pathlib;
}; };
} }

@ -168,6 +168,19 @@ namespace O3DE::ProjectManager
// Gem Repos // Gem Repos
/**
* Refresh gem repo in the current engine.
* @param repoUri the absolute filesystem path or url to the gem repo.
* @return An outcome with the success flag as well as an error message in case of a failure.
*/
virtual AZ::Outcome<void, AZStd::string> RefreshGemRepo(const QString& repoUri) = 0;
/**
* Refresh all gem repos in the current engine.
* @return true on success, false on failure.
*/
virtual bool RefreshAllGemRepos() = 0;
/** /**
* Registers this gem repo with the current engine. * Registers this gem repo with the current engine.
* @param repoUri the absolute filesystem path or url to the gem repo. * @param repoUri the absolute filesystem path or url to the gem repo.

@ -13,7 +13,7 @@ import shutil
import urllib.parse import urllib.parse
import urllib.request import urllib.request
import hashlib import hashlib
from datetime import datetime
from o3de import manifest, utils, validation from o3de import manifest, utils, validation
logger = logging.getLogger() logger = logging.getLogger()
@ -27,6 +27,7 @@ def process_add_o3de_repo(file_name: str or pathlib.Path,
return 1 return 1
cache_folder = manifest.get_o3de_cache_folder() cache_folder = manifest.get_o3de_cache_folder()
repo_data = {}
with file_name.open('r') as f: with file_name.open('r') as f:
try: try:
repo_data = json.load(f) repo_data = json.load(f)
@ -34,6 +35,17 @@ def process_add_o3de_repo(file_name: str or pathlib.Path,
logger.error(f'{file_name} failed to load: {str(e)}') logger.error(f'{file_name} failed to load: {str(e)}')
return 1 return 1
with file_name.open('w') as f:
try:
time_now = datetime.now()
# Convert to lower case because AM/PM is capitalized
time_str = time_now.strftime('%d/%m/%Y %I:%M%p').lower()
repo_data.update({'last_updated': time_str})
f.write(json.dumps(repo_data, indent=4) + '\n')
except Exception as e:
logger.error(f'{file_name} failed to save: {str(e)}')
return 1
# A repo may not contain all types of object. # A repo may not contain all types of object.
manifest_download_list = [] manifest_download_list = []
try: try:
@ -130,6 +142,29 @@ def get_gem_json_paths_from_cached_repo(repo_uri: str) -> list:
return gem_list return gem_list
def refresh_repo(repo_uri: str,
cache_folder: str = None,
repo_set: set = None) -> int:
if not cache_folder:
cache_folder = manifest.get_o3de_cache_folder()
if not repo_set:
repo_set = set()
parsed_uri = urllib.parse.urlparse(f'{repo_uri}/repo.json')
repo_sha256 = hashlib.sha256(parsed_uri.geturl().encode())
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
download_file_result = utils.download_file(parsed_uri, cache_file)
if download_file_result != 0:
return download_file_result
if not validation.valid_o3de_repo_json(cache_file):
logger.error(f'Repo json {repo_uri} is not valid.')
cache_file.unlink()
return 1
return process_add_o3de_repo(cache_file, repo_set)
def refresh_repos() -> int: def refresh_repos() -> int:
json_data = manifest.load_o3de_manifest() json_data = manifest.load_o3de_manifest()
@ -147,20 +182,7 @@ def refresh_repos() -> int:
if repo_uri not in repo_set: if repo_uri not in repo_set:
repo_set.add(repo_uri) repo_set.add(repo_uri)
parsed_uri = urllib.parse.urlparse(f'{repo_uri}/repo.json') last_failure = refresh_repo(repo_uri, cache_folder, repo_set)
repo_sha256 = hashlib.sha256(parsed_uri.geturl().encode())
cache_file = cache_folder / str(repo_sha256.hexdigest() + '.json')
if not cache_file.is_file():
download_file_result = utils.download_file(parsed_uri, cache_file)
if download_file_result != 0:
return download_file_result
if not validation.valid_o3de_repo_json(cache_file):
logger.error(f'Repo json {repo_uri} is not valid.')
cache_file.unlink()
return 1
last_failure = process_add_o3de_repo(cache_file, repo_set)
if last_failure: if last_failure:
result = last_failure result = last_failure

Loading…
Cancel
Save