Using QtPropertyBrowser as advanced configuration editor - user-interface

Has anyone used QtPropertyBrowser as an advanced configuration editor? All I see a GUI examples, to edit properties of GUI elements. But how would I start if I wanted to edit something more abstract, like application configuration.
Here's an example:
I'm creating an application which has a multipage configuration dialog. Some settings are about the printer, some are about a database, some are generic application settings, some are about template filenames and/or templates. But before designing all those detailed config pages in the editor, I'd like to have an "advanced" or "expert" tab in the dialog which lists all imaginable configuration options. Later in the design process I would collect more and more options, decide which are the "easy" options to put into some more user-friendly configuration pages. And googling around I came accross QtPropertyBrowser which seems to be the right tool. But I'm not sure how to start? I'm pretty sure that, instead of GUI objects, I need some sort of abstract configuration objects (one or more). But I don't know where or how to start with that. Currently all my ideas look way to complicated.
Any suggestions or pointers to hints?

You might want to take a look at the runtime type information available through QMetaObject class. Your data objects should be QObject's descendants and have a QOBJECT macro declared. Also you would need a simple routine which would iterate through data objects properties and create and set up corresponding editor's properties. Meta object is also providing an interface for resetting values and methods invocation. More information on the Qt property system is here: The Property System. Below is a small example on how you could do this:
Property browser and managers declaration and initialization:
QtTreePropertyBrowser *_browser;
QtIntPropertyManager *_intManager;
QtDoublePropertyManager *_doubleManager;
QtStringPropertyManager *_stringManager;
_intManager = new QtIntPropertyManager();
_doubleManager = new QtDoublePropertyManager();
_stringManager = new QtStringPropertyManager();
_browser = new QtTreePropertyBrowser(ui->centralWidget);
load properties names and values:
void loadProperties(QObject *object)
{
_browser->clear();
if (object)
{
const QMetaObject *meta = object->metaObject();
qDebug() << "class : " << meta->className();
for (int i=0; i<meta->propertyCount(); i++)
{
QMetaProperty metaProperty = meta->property(i);
QVariant value = metaProperty.read(object);
QtProperty *property = NULL;
qDebug() << "property : " << metaProperty.name() << " : " << value.toInt();
if (metaProperty.type() == QVariant::Int)
{
property = _intManager->addProperty(metaProperty.name());
_intManager->setValue(property, value.toInt());
}
else if (metaProperty.type() == QVariant::Double)
{
property = _doubleManager->addProperty(metaProperty.name());
_doubleManager->setValue(property, value.toDouble());
}
else if (metaProperty.type() == QVariant::String)
{
property = _stringManager->addProperty(metaProperty.name());
_stringManager->setValue(property, value.toString());
}
if (property)
_browser->addProperty(property);
}
}
}

In the samples folder of QtPropertyBrowser comes a demo (object_controller) that I think is exactly what you want. Although the example shows the properties of a class QWidget ObjectController receives as referenced in function setObject (QObject * object); a QObject. Just keep in mind that all you want to inspect must inherit QObject, directly or indirectly, that is the meta-object containing information about the class. You read about the properties in Qt.

Related

Updating display of directory/folder path in MFC File-Open dialog [duplicate]

I'm trying to make a 'Save As' dialog with an event that would change the default path based on the type of file we choose from the filters combo box. The problem is, all the examples I've seen execute the code on result IDOK or IDCANCEL while I'd need the code to be executed while the dialog is still opened.
Also, is there any way to differentiate between what filter has been chosen if the filters have the same type? The GetFileExt() method just returns the extension but I have no way of telling if it was the first .my filter or the template .my filter.
I've seen something like LPOFNHOOKPROC but there was no example of how would I even use it and I'm not sure whether it would even solve my problem or not.
void CMyClass::OnFileOpen()
{
CString pathNam;
CString fileName;
TCHAR szFilters[]= _T("MyType Files (*.my)|*.my|Template MyType (*.my)|*.my||");
CFileDialog fileDlg(TRUE, _T("my"), _T("*.my"),
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);
if(fileDlg.DoModal() == IDOK)
{
pathName = fileDlg.GetPathName();
fileName = fileDlg.GetFileTitle();
}
}
EDIT:
I am now able to get the specific filter that's been chosen by getting the OFN and checking the nFilterIndex value. So the remaining problem is whether I can update the path based on the chosen file format?
EDIT2:
I've found the OnChangeType method and overloaded it in the subclass and it indeed executes the method and the code within, but when I try to update the file path I get an access violation:
void TFileDialogExt::OnTypeChange()
{
LPWSTR buff = L"C:\\TEST\\template.my";
if(m_pOFN->nFilterIndex == 2)
m_ofn.lpstrFile = buff;
}
Basically you have to subclass CFileDialog and handle its CFileDialog::OnTypeChange method.
But, as suggested by Microsoft: you'd better use a new Common Item Dialog instead.
I did some research about this and found some useful questions:
Programmatically pre-select using IFileDialog in C++
How to use SHCreateItemFromParsingName with names from the shell namespace?
Also, have a look at: SHCreateItemFromParsingName.
Here is a sample OnTypeChange handler:
void CMyFileDialog::OnTypeChange()
{
{
IFileOpenDialog* pfod = NULL;
HRESULT hr = (static_cast<IFileDialog*>(m_pIFileDialog))->QueryInterface(IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
IShellItem* psiInitialDir;
CString strFolder = L"d:\\";
hr = SHCreateItemFromParsingName(strFolder.GetString(), NULL, IID_PPV_ARGS(&psiInitialDir));
if(SUCCEEDED(hr))
{
pfod->SetFolder(psiInitialDir);
}
}
}
CFileDialog::OnTypeChange();
}
My code uses a hard coded path for testing purposes, but you should now be able to complete your code:
Determine which path you want to use based on the currently selected filter index.
Use similar logic as here to navigate to that folder.

How to dynamically change a parameter of module from another one in OMNeT++5.5?

I have written a line of codes in omnetpp.ini file like:
S.node[0..4].forwarding = false
What I want to do is change the parameter "forwarding" dynamically (dynamically switching between true and false) in UDPBasicApp module.
void UDPBasicApp::processForwardSwitch(){
if (isSelfish) {
//std::cout << host->par("forwarding").str() << std::endl;
if (rand()%10<5)
host->par("forwarding").setBoolValue(false);
else
host->par("forwarding").setBoolValue(true);
}
scheduleAt(simTime()+forwardSwitchInterval, forwardSwitchTimer);
}
I can confirm the value of the parameter does changes during simulation, but it seems no effect to the modules related to this parameter, e.g. routing modules.
Anyone help?
Thanks in advance!
Usually parameters are read only once, during initialization of a module.

How can I reuse code between Javascript macros and minimize work done within the macros?

I currently have two macros that are part of a (very limited-audience) plugin I'm developing, that both look basically like:
(function(){
exports.name = "name";
exports.params = [
{name: "value"}
];
function get(tiddler) {
// return some contents of some tiddler fields according to some rule
}
function parse(data) {
// convert string to some kind of useful object
}
function logic(x, y) {
// determine whether the two objects correspond in some way
};
function format(data, title) {
// produce WikiText for a link with some additional decoration
};
exports.run = function(value) {
value = parse(value);
var result = [];
this.wiki.each(function(tiddler, title) {
var data = get(tiddler);
if (data !== undefined && logic(value, parse(data))) {
result.push(format(data, title));
}
});
return result.join(" | ");
};
})();
So they're already fairly neatly factored when considered individually; the problem is that only the core logic is really different between the two macros. How can I share the functions get, logic and format between the macros? I tried just putting them in a separate tiddler, but that doesn't work; when the macros run, TW raises an error claiming that the functions are "not defined". Wrapping each function as its own javascript macro in a separate tiddler, e.g.
(function(){
exports.name = "get";
exports.params = [
{name: "tiddler"}
];
exports.run = function(tiddler) {
// return some contents of some tiddler fields according to some rule
}
})();
also didn't help.
I'd also like to set this up to be more modular/flexible, by turning the main get/parse/logic/format process into a custom filter, then letting a normal filter expression take care of the iteration and using e.g. the widget or <> macro to display the items. How exactly do I set this up? The documentation tells me
If the provided filter operators are not enough, a developer can add
new filters by adding a module with the filteroperator type
but I can't find any documentation of the API for this, nor any examples.
How can I share the functions get, logic and format between the macros?
You can use the Common/JS standard require('<tiddler title>') syntax to access the exports of another tiddler. The target tiddler should be set up as a JS module (ie, the type field set to application/javascript and the module-type field set to library). You can see an example here:
https://github.com/Jermolene/TiddlyWiki5/blob/master/core/modules/widgets/count.js#L15
I'd also like to set this up to be more modular/flexible, by turning the main get/parse/logic/format process into a custom filter, then letting a normal filter expression take care of the iteration and using e.g. the widget or <> macro to display the items. How exactly do I set this up?
The API for writing filter operators isn't currently documented, but there are many examples to look at:
https://github.com/Jermolene/TiddlyWiki5/tree/master/core/modules/filters

signature and functionality of selection callback of treeview in gtkmm

I have a treeview and want to get notified if the selection changes. What is the signature for the callback?
I found a code snippet like:
Gtk::TreeView *treeview = Gtk::manage(new Gtk::TreeView);
Glib::RefPtr< Gtk::TreeSelection > sel = treeview->get_selection();
sel->set_mode( Gtk::SELECTION_MULTIPLE );
sel->set_select_function(sigc::ptr_fun(&SelFun));
But I can't find anything about the SelFun!
How is the signature
How to find out which rows and columns are selected inside this function?
How to access data from the model with that object
Yes, I have actually no idea how the TreeView/Model/Path/Selection interacts. Every link to an example is highly welcome!
You seem to want multiple selection. I had the same problem too. Once you have enabled mutliple selection, getting the selected rows is a little more difficult. The method of acquiring them varies slightly.
I'll provide the most general method. First, you need to overload the signal_changed() signal after you have enabled multiple selection. Then, assign the TreeView's TreeSelection to a RefPtr for easy access.
Glib::RefPtr<Gtk::TreeSelection> TreeView_TreeSelection;
TreeView_TreeSelection = your_TreeView.get_selection();
Next, connect the TreeSelection to the signal_changed() signal.
TreeView_TreeSelection -> signal_changed().connect(sigc::mem_fun(your_TreeView,
&your_Class::on_selection_changed));
Now, make sure to make a void function header in "your_Class" named on_selction_changed() or whatever you want. Just make sure to change the name in the connection above to whatever your class' name is.
The final step is to make the function. Here is a simple example of getting a vector of all of the TreePaths of the rows selected, then converting those TreePaths into a vector of TreeModel::Row pointers.
void your_Class::on_selection_changed()
{
if((TreeView_TreeSelection -> count_selected_rows()) == 0)
{
return;
}
vector<Gtk::TreeModel::Path> selected_rows = TreeView_TreeSelection -> get_selected_rows();
vector<Gtk::TreeModel::Row*> selected_TreeRows;
vector<Gtk::TreeModel::Path>::iterator TreePath_iterator = selected_rows.begin();
Gtk::TreeRow *row;
while(TreePath_iterator != selected_rows.end()
{
selected_row_it = p_TreeModel -> get_iter(TreePath_iterator);
row = (*selected_row_it);
selected_TreeRows.push_back(row);
TreePath_iterator++;
}
}
Do you know how to iterate through a TreeModel using the STL-like contain API called children() of a TreeModel? It's most useful for iterating over all of the rows of a TreeModel or getting the size (AKA row count) of a TreeModel. Its use depends on whether you're using a ListStore, TreeStore or a custom TreeModel.

How can I listen for the deletion of a ProjectItem via DTE?

I've got a designer that relies on the existence of other solution items. If one of those items is deleted the designer crashes and you have to edit as XML to fix. Not exactly user friendly.
I do, however, have the DTE object representing the instance of Visual Studio, as well as the ProjectItems I am dependent on.
Is it possible to, somewhere in the depths of the DTE, register a listener for the deletion of that ProjectItem? And, if so, How would I do it?
It looks like the culprit here is garbage collection. I found the following two event sets behaved identically.
Events2 events2 = dte.Events as Events2;
if (events2 != null)
{
this.projectItemsEvents = events2.ProjectItemsEvents;
this.projectItemsEvents.ItemAdded += this.ProjectItemsEvents_ItemAdded;
this.projectItemsEvents.ItemRemoved += this.ProjectItemsEvents_ItemRemoved;
this.projectItemsEvents.ItemRenamed += this.ProjectItemsEvents_ItemRenamed;
}
this.csharpProjectItemsEvents =
dte.Events.GetObject("CSharpProjectItemsEvents") as ProjectItemsEvents;
if (this.csharpProjectItemsEvents != null)
{
this.csharpProjectItemsEvents.ItemAdded += this.CSharpProjectItemsEvents_ItemAdded;
this.csharpProjectItemsEvents.ItemRemoved += this.CSharpProjectItemsEvents_ItemRemoved;
this.csharpProjectItemsEvents.ItemRenamed += this.CSharpProjectItemsEvents_ItemRenamed;
}
The key to both was making sure to keep a reference to the events object in the subscriber. Once I added the reference, they behaved like I expected.
private ProjectItemsEvents projectItemsEvents;
private ProjectItemsEvents csharpProjectItemsEvents;
Check out this FAQ article which explains how to register for ProjectItems events (including ItemDeleted).

Resources