Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Dialogs to have a MainWindow independent config #418

Merged
merged 46 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
bc63b60
Example running a dialog before the main window
chapulina May 27, 2022
1e9390f
parse show quick setup menu by default option
Jun 14, 2022
20dbff3
merege chapulina/3/start_dialog
Jun 14, 2022
b891da0
add load window config, load config without loading plugins
Jun 16, 2022
1638867
rename quick setup to quick start
Jun 19, 2022
f0acce5
code check
Jun 20, 2022
0da8144
convert createMainWindow to public
Jun 22, 2022
a28a25e
Example running a dialog before the main window
chapulina May 27, 2022
0dc0221
convert createMainWindow to public
Jun 22, 2022
38b96e2
add quick start test in windowConfig
Jun 22, 2022
85c9b51
fix quick start test case
Jun 22, 2022
54b6062
fixing cmdLine test error on linux
Jun 22, 2022
75c9feb
remove fixme from quick start example
Jun 22, 2022
bbfa2e0
comment out cmdline list remove dir test
Jun 23, 2022
055617f
add load window config test
Jun 23, 2022
b498ad9
show quick start menu test added
Jun 23, 2022
c4b8bda
fix quick start test
Jun 23, 2022
29879fc
allow dialogs to read and write configs
Jun 27, 2022
4e3c705
codecheck
Jun 27, 2022
3b2c8ec
code check
Jun 28, 2022
bb08ad3
uncomment cmdline test
Jun 28, 2022
f6a5973
remove old write quick start config imp
Jun 28, 2022
80a8e54
remove old write quick start config test
Jun 28, 2022
2d6bbff
fix old quick start test
Jun 28, 2022
d9520a1
Merge branch 'ign-gui3' of https://github.com/ignitionrobotics/ign-gu…
Jun 28, 2022
b9070a1
remove old write quick start functions from application
Jun 28, 2022
a7b1125
add dialog test
Jun 28, 2022
48e5c22
code check
Jun 28, 2022
4bc9384
fix test
Jun 28, 2022
5031ea9
fix test another compile issue
Jun 28, 2022
a0220a0
remove un used function
Jun 28, 2022
a8df04e
Merge branch 'ign-gui3' into quick_setup_menu_config
chapulina Jun 30, 2022
02a06bc
rename fucntions for reading writting configs
Jun 30, 2022
7a42006
Merge branch 'quick_setup_menu_config' of github.com:gazebosim/gz-gui…
Jun 30, 2022
5e80ba5
fix test
Jun 30, 2022
1dc1f26
fix test
Jun 30, 2022
7787453
use template argument for attribute value update
Jul 1, 2022
e8a1e3d
code check
Jul 1, 2022
f61237f
clean up
Jul 6, 2022
cc72807
Merge branch 'ign-gui3' of github.com:gazebosim/gz-gui into quick_set…
Jul 6, 2022
0d81497
rename ReadAttributeValue to ReadConfigAttribute and clean up
Jul 8, 2022
095a2b0
test config exists
Jul 8, 2022
703d896
Merge branch 'ign-gui3' of github.com:gazebosim/gz-gui into quick_set…
Jul 8, 2022
30dabb5
test config file creation and deletion
Jul 8, 2022
632e43e
fix comments
Jul 8, 2022
94e5bbc
dialog test fix
Jul 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/ignition/gui/Application.hh
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ namespace ignition
/// and plugins. This function doesn't instantiate the plugins, it just
/// keeps them in memory and they can be applied later by either
/// instantiating a window or several dialogs.
/// and plugins.
/// \param[in] _path Full path to configuration file.
/// \return True if successful
/// \sa InitializeMainWindow
Expand Down Expand Up @@ -156,6 +157,7 @@ namespace ignition
/// \return True if successful
public: bool RemovePlugin(const std::string &_pluginName);


/// \brief Get a plugin by its unique name.
/// \param[in] _pluginName Plugn instance's unique name. This is the
/// plugin card's object name.
Expand Down
23 changes: 23 additions & 0 deletions include/ignition/gui/Dialog.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define IGNITION_GUI_DIALOG_HH_

#include <memory>
#include <string>

#include "ignition/gui/qt.h"
#include "ignition/gui/Export.hh"
Expand Down Expand Up @@ -55,6 +56,28 @@ namespace ignition
/// \return Pointer to the item
public: QQuickItem *RootItem() const;

/// \brief Store dialog default config
/// \param[in] _config XML config as string
public: void SetDefaultConfig(const std::string &_config);

/// \brief Write dialog config
/// \param[in] _path config path
/// \param[in] _attribute XMLElement attribute name
/// \param[in] _value XMLElement attribute value
/// \return true if written to config file
public: bool UpdateConfigAttribute(
const std::string &_path, const std::string &_attribute,
const bool _value) const;

/// \brief Gets a config attribute value, if not found in config
/// write the default in the config and get it.
/// creates config file if it doesn't exist.
/// \param[in] _path config path
/// \param[in] _attribute attribute name
/// \return attribute value as string
public: std::string ReadConfigAttribute(const std::string &_path,
const std::string &_attribute) const;

/// \internal
/// \brief Private data pointer
private: std::unique_ptr<DialogPrivate> dataPtr;
Expand Down
1 change: 1 addition & 0 deletions include/ignition/gui/MainWindow.hh
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ namespace ignition

/// \brief Concatenation of all plugin configurations.
std::string plugins{""};

};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Application_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ TEST(ApplicationTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(LoadDefaultConfig))
}

//////////////////////////////////////////////////
TEST(ApplicationTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(InitializeMainWindow))
TEST(ApplicationTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(CreateMainWindow))
{
ignition::common::Console::SetVerbosity(4);

Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set (sources
set (gtest_sources
Application_TEST
Conversions_TEST
Dialog_TEST
DragDropModel_TEST
Helpers_TEST
GuiEvents_TEST
Expand Down
143 changes: 143 additions & 0 deletions src/Dialog.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*
*/

#include <tinyxml2.h>

#include <ignition/common/Console.hh>
#include "ignition/gui/Application.hh"
#include "ignition/gui/Dialog.hh"
Expand All @@ -25,6 +27,9 @@ namespace ignition
{
class DialogPrivate
{
/// \brief default dialog config
public: std::string config{""};

/// \brief Pointer to quick window
public: QQuickWindow *quickWindow{nullptr};
};
Expand Down Expand Up @@ -75,3 +80,141 @@ QQuickItem *Dialog::RootItem() const
return dialogItem;
}

/////////////////////////////////////////////////
bool Dialog::UpdateConfigAttribute(const std::string &_path,
const std::string &_attribute, const bool _value) const
{
if (_path.empty())
{
ignerr << "Missing config file" << std::endl;
return false;
}

// Use tinyxml to read config
tinyxml2::XMLDocument doc;
auto success = !doc.LoadFile(_path.c_str());
if (!success)
{
ignerr << "Failed to load file [" << _path << "]: XMLError"
<< std::endl;
return false;
}

// Update attribute value for the correct dialog
for (auto dialogElem = doc.FirstChildElement("dialog");
dialogElem != nullptr;
dialogElem = dialogElem->NextSiblingElement("dialog"))
{
if(dialogElem->Attribute("name") == this->objectName().toStdString())
{
dialogElem->SetAttribute(_attribute.c_str(), _value);
}
}

// Write config file
tinyxml2::XMLPrinter printer;
doc.Print(&printer);

std::string config = printer.CStr();
std::ofstream out(_path.c_str(), std::ios::out);
if (!out)
{
ignerr << "Unable to open file: " << _path
<< ".\nCheck file permissions.\n";
}
else
out << config;

return true;
}

/////////////////////////////////////////////////
void Dialog::SetDefaultConfig(const std::string &_config)
{
this->dataPtr->config = _config;
}

/////////////////////////////////////////////////
std::string Dialog::ReadConfigAttribute(const std::string &_path,
const std::string &_attribute) const
{
tinyxml2::XMLDocument doc;
std::string value {""};
std::string config = "<?xml version=\"1.0\"?>\n\n";
tinyxml2::XMLPrinter defaultPrinter;
bool configExists{true};
std::string dialogName = this->objectName().toStdString();

auto Value = [&_attribute, &doc, &dialogName]()
{
// Process each dialog
// If multiple attributes share the same name, return the first one
for (auto dialogElem = doc.FirstChildElement("dialog");
dialogElem != nullptr;
dialogElem = dialogElem->NextSiblingElement("dialog"))
{
if (dialogElem->Attribute("name") == dialogName)
{
if (dialogElem->Attribute(_attribute.c_str()))
return dialogElem->Attribute(_attribute.c_str());
}
}
return "";
};

// Check if the passed in config file exists.
// (If the default config path doesn't exist yet, it's expected behavior.
// It will be created the first time now.)
if (!common::exists(_path))
{
configExists = false;
doc.Parse(this->dataPtr->config.c_str());
value = Value();
}
else
{
auto success = !doc.LoadFile(_path.c_str());
if (!success)
chapulina marked this conversation as resolved.
Show resolved Hide resolved
{
ignerr << "Failed to load file [" << _path << "]: XMLError"
<< std::endl;
return "";
}
value = Value();

// config exists but attribute not there read from default config
if (value.empty())
{
tinyxml2::XMLDocument missingDoc;
missingDoc.Parse(this->dataPtr->config.c_str());
value = Value();
missingDoc.Print(&defaultPrinter);
}
}

// Write config file
tinyxml2::XMLPrinter printer;
doc.Print(&printer);

// Don't write the xml version decleration if file exists
if (configExists)
{
config = "";
}

igndbg << "Setting dialog " << this->objectName().toStdString()
<< " default config." << std::endl;
config += printer.CStr();
config += defaultPrinter.CStr();
std::ofstream out(_path.c_str(), std::ios::out);
if (!out)
{
ignerr << "Unable to open file: " << _path
<< ".\nCheck file permissions.\n";
return "";
}
else
out << config;

return value;
}
137 changes: 137 additions & 0 deletions src/Dialog_TEST.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <gtest/gtest.h>

#include <ignition/common/Console.hh>
#include <ignition/utilities/ExtraTestMacros.hh>

#include "test_config.h" // NOLINT(build/include)
#include "ignition/gui/Application.hh"
#include "ignition/gui/Dialog.hh"

std::string kTestConfigFile = "/tmp/ign-gui-test.config"; // NOLINT(*)
int g_argc = 1;
char* g_argv[] =
{
reinterpret_cast<char*>(const_cast<char*>("./Dialog_TEST")),
};

using namespace ignition;
using namespace gui;
using namespace std::chrono_literals;

/////////////////////////////////////////////////
TEST(DialogTest, IGN_UTILS_TEST_DISABLED_ON_WIN32(UpdateDialogConfig))
{
ignition::common::Console::SetVerbosity(4);
Application app(g_argc, g_argv, ignition::gui::WindowType::kDialog);

// Change default config path
App()->SetDefaultConfigPath(kTestConfigFile);

auto dialog = new Dialog;
ASSERT_NE(nullptr, dialog);

// Read attribute value when the default the config is not set
{
EXPECT_FALSE(common::exists(kTestConfigFile));
std::string allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");
EXPECT_EQ(allow, "");

// Config file is created when a read is attempted
EXPECT_TRUE(common::exists(kTestConfigFile));

// Delete file
std::remove(kTestConfigFile.c_str());
}

// Read a non existing attribute
{
EXPECT_FALSE(common::exists(kTestConfigFile));
dialog->setObjectName("quick_menu");
dialog->SetDefaultConfig(std::string(
"<dialog name=\"quick_menu\" show=\"true\"/>"));
std::string allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");
EXPECT_EQ(allow, "");

// Config file is created when a read is attempted
EXPECT_TRUE(common::exists(kTestConfigFile));

// Delete file
std::remove(kTestConfigFile.c_str());
}

// Read an existing attribute
{
EXPECT_FALSE(common::exists(kTestConfigFile));
std::string show = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"show");
EXPECT_EQ(show, "true");

// Config file is created when a read is attempted
EXPECT_TRUE(common::exists(kTestConfigFile));

// Delete file
std::remove(kTestConfigFile.c_str());
}

// Update a non existing attribute
{
EXPECT_FALSE(common::exists(kTestConfigFile));

// Call a read to create config file
std::string allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");

// Empty string for a non existing attribute
EXPECT_EQ(allow, "");
dialog->UpdateConfigAttribute(app.DefaultConfigPath(), "allow", true);
allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");
EXPECT_EQ(allow, "true");

// Config file is created when a read is attempted
EXPECT_TRUE(common::exists(kTestConfigFile));

// Delete file
std::remove(kTestConfigFile.c_str());
}

// Update a existing attribute
{
EXPECT_FALSE(common::exists(kTestConfigFile));

// Call a read to create config file
std::string allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");
dialog->UpdateConfigAttribute(app.DefaultConfigPath(), "allow", false);
allow = dialog->ReadConfigAttribute(app.DefaultConfigPath(),
"allow");
EXPECT_EQ(allow, "false");

// Config file is created when a read is attempted
EXPECT_TRUE(common::exists(kTestConfigFile));

// Delete file
std::remove(kTestConfigFile.c_str());
}

delete dialog;
}