Skip to content
Snippets Groups Projects
Commit 5c0da84d authored by Fabian Paus's avatar Fabian Paus
Browse files

RemoteGui: Removing tabs

parent 446afdc5
No related branches found
No related tags found
No related merge requests found
Showing
with 291 additions and 31 deletions
<?xml version="1.0" encoding="utf-8"?>
<scenario name="RemoteGuiTest" creation="2018-12-05.13:56:25" globalConfigName="./config/global.cfg" package="ArmarXGui">
<application name="RemoteGuiProviderApp" instance="" package="ArmarXGui" enabled="true"/>
<application name="RemoteGuiExampleApp" instance="" package="ArmarXGui" enabled="true"/>
<application name="RemoteGuiExampleApp" instance="Widgets" package="ArmarXGui" enabled="true"/>
<application name="RemoteGuiExampleApp" instance="Events" package="ArmarXGui" enabled="true"/>
</scenario>
......@@ -119,7 +119,7 @@ ArmarX.RemoteGuiExample.Mode = Events
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.RemoteGuiExample.ObjectName = ""
ArmarX.RemoteGuiExample.ObjectName = RemoteGuiExample_Events
# ArmarX.RemoteGuiExample.RemoteGuiName: Name of the remote GUI provider
......
# ==================================================================
# RemoteGuiExampleApp properties
# ==================================================================
# ArmarX.AdditionalPackages: List of additional ArmarX packages which should be in the list of default packages. If you have custom packages, which should be found by the gui or other apps, specify them here. Comma separated List.
# Attributes:
# - Default: Default value not mapped.
# - Case sensitivity: no
# - Required: no
# ArmarX.AdditionalPackages = Default value not mapped.
# ArmarX.ApplicationName: Application name
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.ApplicationName = ""
# ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_USER_CONFIG_DIR is set, the cache path will be made relative to ARMARX_USER_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${HOME}/.armarx)
# Attributes:
# - Default: mongo/.cache
# - Case sensitivity: no
# - Required: no
# ArmarX.CachePath = mongo/.cache
# ArmarX.Config: Comma-separated list of configuration files
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.Config = ""
# ArmarX.DataPath: Semicolon-separated search list for data files
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.DataPath = ""
# ArmarX.DefaultPackages: List of ArmarX packages which are accessible by default. Comma separated List. If you want to add your own packages and use all default ArmarX packages, use the property 'AdditionalPackages'.
# Attributes:
# - Default: Default value not mapped.
# - Case sensitivity: no
# - Required: no
# ArmarX.DefaultPackages = Default value not mapped.
# ArmarX.DependenciesConfig: Path to the (usually generated) config file containing all data paths of all dependent projects. This property usually does not need to be edited.
# Attributes:
# - Default: ./config/dependencies.cfg
# - Case sensitivity: no
# - Required: no
# ArmarX.DependenciesConfig = ./config/dependencies.cfg
# ArmarX.DisableLogging: Turn logging off in whole application
# Attributes:
# - Default: 0
# - Case sensitivity: no
# - Required: no
# ArmarX.DisableLogging = 0
# ArmarX.EnableProfiling: Enable profiling of CPU load produced by this application
# Attributes:
# - Default: 0
# - Case sensitivity: no
# - Required: no
# ArmarX.EnableProfiling = 0
# ArmarX.LoadLibraries: Libraries to load at start up of the application. Must be enabled by the Application with enableLibLoading(). Format: PackageName:LibraryName;... or /absolute/path/to/library;...
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.LoadLibraries = ""
# ArmarX.RedirectStdout: Redirect std::cout and std::cerr to ArmarXLog
# Attributes:
# - Default: 1
# - Case sensitivity: no
# - Required: no
# ArmarX.RedirectStdout = 1
# ArmarX.RemoteGuiExample.EnableProfiling: enable profiler which is used for logging performance events
# Attributes:
# - Default: 0
# - Case sensitivity: no
# - Required: no
# ArmarX.RemoteGuiExample.EnableProfiling = 0
# ArmarX.RemoteGuiExample.MinimumLoggingLevel: Local logging level only for this component
# Attributes:
# - Default: Undefined
# - Case sensitivity: no
# - Required: no
# ArmarX.RemoteGuiExample.MinimumLoggingLevel = Undefined
# ArmarX.RemoteGuiExample.Mode: Select which example to execute
# Attributes:
# - Default: Widgets
# - Case sensitivity: no
# - Required: no
ArmarX.RemoteGuiExample.Mode = Widgets
# ArmarX.RemoteGuiExample.ObjectName: Name of IceGrid well-known object
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
ArmarX.RemoteGuiExample.ObjectName = RemoteGuiExample_Widgets
# ArmarX.RemoteGuiExample.RemoteGuiName: Name of the remote GUI provider
# Attributes:
# - Default: RemoteGuiProvider
# - Case sensitivity: no
# - Required: no
# ArmarX.RemoteGuiExample.RemoteGuiName = RemoteGuiProvider
# ArmarX.RemoteHandlesDeletionTimeout: The timeout (in ms) before a remote handle deletes the managed object after the use count reached 0. This time can be used by a client to increment the count again (may be required when transmitting remote handles)
# Attributes:
# - Default: 3000
# - Case sensitivity: no
# - Required: no
# ArmarX.RemoteHandlesDeletionTimeout = 3000
# ArmarX.StartDebuggerOnCrash: If this application crashes (segmentation fault) qtcreator will attach to this process and start the debugger.
# Attributes:
# - Default: 0
# - Case sensitivity: no
# - Required: no
# ArmarX.StartDebuggerOnCrash = 0
# ArmarX.ThreadPoolSize: Size of the ArmarX ThreadPool that is always running.
# Attributes:
# - Default: 1
# - Case sensitivity: no
# - Required: no
# ArmarX.ThreadPoolSize = 1
# ArmarX.TopicSuffix: Suffix appended to all topic names for outgoing topics. This is mainly used to direct all topics to another name for TopicReplaying purposes.
# Attributes:
# - Default: ""
# - Case sensitivity: no
# - Required: no
# ArmarX.TopicSuffix = ""
# ArmarX.UseTimeServer: Enable using a global Timeserver (e.g. from ArmarXSimulator)
# Attributes:
# - Default: 0
# - Case sensitivity: no
# - Required: no
# ArmarX.UseTimeServer = 0
# ArmarX.Verbosity: Global logging level for whole application
# Attributes:
# - Default: Info
# - Case sensitivity: no
# - Required: no
# ArmarX.Verbosity = Info
......@@ -31,7 +31,7 @@ using namespace armarx;
RemoteGuiExample::RemoteGuiExample()
: runningTask(this, &RemoteGuiExample::run)
: runningTask(new RunningTask<RemoteGuiExample>(this, &RemoteGuiExample::run))
{
}
......@@ -61,19 +61,26 @@ void RemoteGuiExample::onConnectComponent()
break;
}
runningTask.start();
runningTask->start();
}
void RemoteGuiExample::onDisconnectComponent()
{
runningTask.stop();
ARMARX_INFO << "Stopping task";
if (!runningTask->isStopped())
{
runningTask->stop();
}
ARMARX_INFO << "Removing tab: " << tabName;
remoteGui->removeTab(tabName);
}
void RemoteGuiExample::onExitComponent()
{
onDisconnectComponent();
}
armarx::PropertyDefinitionsPtr RemoteGuiExample::createPropertyDefinitions()
......@@ -86,7 +93,7 @@ void RemoteGuiExample::run()
{
int cycleDurationMs = 20;
CycleUtil c(cycleDurationMs);
while (!runningTask.isStopped())
while (!runningTask->isStopped())
{
RemoteGui::ValueMap values = remoteGui->getValues(tabName);
......@@ -272,13 +279,13 @@ void RemoteGuiExample::updateTab_Events(const RemoteGui::ValueMap& values)
// Detect button clicks and update the counter accordingly
int currentValue = getValue<int>(values, "IntSpin");
bool upClicked = buttonClicked(values, oldValues, "UpButton");
bool downClicked = buttonClicked(values, oldValues, "DownButton");
if (upClicked)
{
++currentValue;
}
bool downClicked = buttonClicked(values, oldValues, "DownButton");
if (downClicked)
{
--currentValue;
......
......@@ -124,6 +124,6 @@ namespace armarx
std::string tabName;
RemoteGui::ValueMap oldValues;
RunningTask<RemoteGuiExample> runningTask;
RunningTask<RemoteGuiExample>::pointer_type runningTask;
};
}
......@@ -114,7 +114,19 @@ void RemoteGuiProvider::createTab(const std::string& tabId, const RemoteGui::Wid
fillRecursively(tabWidgetStates[tabId], tabStates[tabId], rootWidget);
}
topic->reportTabCreated(tabId, rootWidget);
topic->reportTabChanged(tabId);
}
void RemoteGuiProvider::removeTab(const std::string& tabId, const Ice::Current&)
{
ARMARX_INFO << "Removing remote tab: " << tabId;
{
std::unique_lock<std::mutex> lock(tabMutex);
tabs.erase(tabId);
tabWidgetStates.erase(tabId);
tabStates.erase(tabId);
}
topic->reportTabsRemoved();
}
RemoteGui::WidgetMap RemoteGuiProvider::getTabs(const Ice::Current&)
......
......@@ -102,6 +102,7 @@ namespace armarx
std::string getTopicName(const Ice::Current&) override;
void createTab(const std::string& tabId, const RemoteGui::WidgetPtr& rootWidget, const Ice::Current&) override;
void removeTab(const std::string& tabId, const Ice::Current&) override;
RemoteGui::WidgetMap getTabs(const Ice::Current&) override;
RemoteGui::TabWidgetStateMap getTabStates(const Ice::Current&) override;
......
......@@ -100,6 +100,7 @@ RemoteGuiWidgetController::RemoteGuiWidgetController()
connect(widget.sendRemoteValuesButton, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
connect(this, SIGNAL(tabChanged(QString)), this, SLOT(onTabChanged(QString)), Qt::QueuedConnection);
connect(this, SIGNAL(tabsChanged()), this, SLOT(onTabsChanged()), Qt::QueuedConnection);
connect(this, SIGNAL(widgetChanged(QString)), this, SLOT(onWidgetChanged(QString)), Qt::QueuedConnection);
connect(this, SIGNAL(stateChanged(QString)), this, SLOT(onStateChanged(QString)), Qt::QueuedConnection);
}
......@@ -156,26 +157,37 @@ void RemoteGuiWidgetController::onConnectComponent()
}
// Load initial values into GUI
for (auto& tabWidget : tabs)
{
QString id = QString::fromStdString(tabWidget.first);
emit tabChanged(id);
}
emit tabsChanged();
// Subscribe to updates
usingTopic(remoteGuiProvider->getTopicName());
}
void RemoteGuiWidgetController::reportTabCreated(const std::string& tab, const RemoteGui::WidgetPtr& rootWidget, const Ice::Current&)
void RemoteGuiWidgetController::reportTabChanged(const std::string& tab, const Ice::Current&)
{
{
std::unique_lock<std::mutex> lock(stateMutex);
tabs[tab] = rootWidget;
tabs = remoteGuiProvider->getTabs();
tabWidgetStates = remoteGuiProvider->getTabStates();
tabStates = remoteGuiProvider->getValuesForAllTabs();
}
QString qTabId = QString::fromStdString(tab);
emit tabChanged(qTabId);
}
void RemoteGuiWidgetController::reportTabsRemoved(const Ice::Current&)
{
// Setup data structures
{
std::unique_lock<std::mutex> lock(stateMutex);
tabs = remoteGuiProvider->getTabs();
tabWidgetStates = remoteGuiProvider->getTabStates();
tabStates = remoteGuiProvider->getValuesForAllTabs();
}
emit tabsChanged();
}
void RemoteGuiWidgetController::reportWidgetChanged(const std::string& tab, const RemoteGui::WidgetStateMap& widgetState, const Ice::Current&)
{
{
......@@ -200,14 +212,38 @@ void RemoteGuiWidgetController::onTabChanged(QString qid)
{
// The root widget of a tab was changed, so we have to create everything from scratch
std::string id = qid.toStdString();
std::unique_lock<std::mutex> lock(stateMutex);
createTab(id);
updateWidgets(id);
removeObsoleteTabs();
}
void RemoteGuiWidgetController::onTabsChanged()
{
std::unique_lock<std::mutex> lock(stateMutex);
// Add or update existing tabs
for (auto& tabWidget : tabs)
{
QString qid = QString::fromStdString(tabWidget.first);
std::string id = qid.toStdString();
createTab(id);
updateWidgets(id);
}
removeObsoleteTabs();
}
void RemoteGuiWidgetController::onWidgetChanged(QString qid)
{
// The display options of some widgets changed (e.g. enabled, hidden)
std::string id = qid.toStdString();
std::unique_lock<std::mutex> lock(stateMutex);
updateWidgets(id);
}
......@@ -261,8 +297,6 @@ void RemoteGuiWidgetController::onSendButtonClicked()
void RemoteGuiWidgetController::createTab(std::string const& tabId)
{
std::unique_lock<std::mutex> lock(stateMutex);
RemoteGui::WidgetPtr widgetP = tabs.at(tabId);
ARMARX_CHECK_EXPRESSION(widgetP);
......@@ -298,11 +332,32 @@ void RemoteGuiWidgetController::createTab(std::string const& tabId)
layout->addWidget(newTab);
}
void RemoteGuiWidgetController::removeObsoleteTabs()
{
for (int i = 0; i < widget.remoteTabWidget->count();)
{
QString qTabId = widget.remoteTabWidget->tabText(i);
std::string tabId = qTabId.toStdString();
bool found = tabs.count(tabId) > 0;
if (found)
{
++i;
}
else
{
ARMARX_INFO << "Removing tab: " << tabId;
widget.remoteTabWidget->widget(i)->deleteLater();
widget.remoteTabWidget->removeTab(i);
guiWidgets.erase(tabId);
}
}
}
void RemoteGuiWidgetController::updateWidgets(std::string const& tabId)
{
// External widget state update received
std::unique_lock<std::mutex> lock(stateMutex);
RemoteGui::WidgetStateMap const& states = tabWidgetStates.at(tabId);
std::map<std::string, QWidget*> const& widgets = guiWidgets.at(tabId);
......@@ -377,8 +432,6 @@ QWidget* RemoteGuiWidgetController::createWidgetFromDescription(const std::strin
{
guiDescriptions[tabId][desc->name] = desc;
values[desc->name] = initialValue;
ARMARX_INFO << "Created widget in tab '" << tabId << "' with name '" << desc->name << "'";
}
else
{
......
......@@ -103,13 +103,15 @@ namespace armarx
void onConnectComponent() override;
// RemoteGuiListenerInterface interface
void reportTabCreated(const std::string& tab, const RemoteGui::WidgetPtr& rootWidget, const Ice::Current&) override;
void reportTabChanged(const std::string& tab, const Ice::Current&) override;
void reportTabsRemoved(const Ice::Current&) override;
void reportWidgetChanged(const std::string& tab, const RemoteGui::WidgetStateMap& widgetState, const Ice::Current&) override;
void reportStateChanged(const std::string& tab, const RemoteGui::ValueMap& newState, const Ice::Current&) override;
public slots:
/* QT slot declarations */
void onTabChanged(QString id);
void onTabsChanged();
void onWidgetChanged(QString id);
void onStateChanged(QString id);
......@@ -120,11 +122,13 @@ namespace armarx
signals:
/* QT signal declarations */
void tabChanged(QString id);
void tabsChanged();
void widgetChanged(QString id);
void stateChanged(QString id);
private:
void createTab(std::string const& tabId);
void removeObsoleteTabs();
void updateWidgets(std::string const& tabId);
void updateState(std::string const& tabId);
......@@ -157,10 +161,5 @@ namespace armarx
std::map<std::string, std::map<std::string, QWidget*>> guiWidgets;
std::atomic<bool> internalUpdate {false};
};
}
......@@ -168,6 +168,8 @@ module armarx
void createTab(string tab, RemoteGui::Widget rootWidget);
void removeTab(string tab);
RemoteGui::WidgetMap getTabs();
RemoteGui::TabWidgetStateMap getTabStates();
......@@ -185,7 +187,9 @@ module armarx
interface RemoteGuiListenerInterface
{
void reportTabCreated(string tab, RemoteGui::Widget rootWidget);
void reportTabChanged(string tab);
void reportTabsRemoved();
void reportStateChanged(string tab, RemoteGui::ValueMap newValues);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment