I want users to be able to create my ScriptableObject assets outside of play mode so they can bind them to their objects in the scene. Since I don't want Zenject creating the ScriptableObjects, a Factory solution is not what I'm looking for. Therefore, I must somehow get the instance of these script objects to the Zenject installer and use 'QueueForInject'. Here are the two approaches I've found:
A) Manually adding these script objects to the Installer via the Inspector Window. Example:
public class GameInstaller : MonoInstaller<GameInstaller>
{
// Visible in the inspector window so user can add Script Objects to List
public List<ScriptableObject> myScriptObjectsToInject;
public override void InstallBindings()
{
foreach (ScriptableObject scriptObject in myScriptObjectsToInject)
{
Container.QueueForInject(scriptObject);
}
}
}
B) Use Unity's AssetDatabase and Resources APIs to find all script object instances then iterate through the list to QueueForInject. Example
// search for all ScriptableObject that reside in the 'Resources' folder
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject");
foreach (string guid in guids)
{
// retrieve the path string to the asset on disk relative to the Unity project folder
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
// retrieve the type of the asset (i.e. the name of the class, which is whatever derives from ScriptableObject)
System.Type myType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
//Find the relative path of the asset.
string resourcesDirectoryName = $"/Resources/";
int indexOfLastResourceDirectory = assetPath.LastIndexOf(resourcesDirectoryName) + resourcesDirectoryName.Length;
int indexOfExtensionPeriod = assetPath.LastIndexOf(".");
string assetPathRelative = assetPath.Substring(indexOfLastResourceDirectory, indexOfExtensionPeriod - indexOfLastResourceDirectory);
//Grab the instance of the ScriptableObject.
ScriptableObject scriptObject = Resources.Load(assetPathRelative, myType) as ScriptableObject;
if (scriptObject == null)
{
Debug.LogWarning(
"ScriptableObject asset found, but it is not in a 'Resources' folder. Current folder = " +
assetPath);
continue;
}
else
{
Container.QueueForInject(scriptObject);
}
}
With option A) the end user must remember to manually place the script object in the list for every new script object that they create. I'd rather find an automated way so the user doesn't have to know/remember this extra manual process.
With option B) I get an automated solution which, is great. But the end user must remember to store each ScriptableObject asset file in a directory called "Resources" because it's the only way the Resources.Load API will find the instance. I can warn the user if not found, which is nice. But they still are forced to comply to remove the warnings.
I can live with option B if I must, but I'd really like to take it a step further and make it completely invisible to the end user. Has anyone come up with a craftier automated solution for ScriptObjects that exist outside of Play Mode?
I try to modify the default value of a property in Paraview using a custom Plugin.
When I add a Dicom file in my pipeline, the default representation is set to "Outline" in the property panel, but I want it to be "Volume".
The goal is to interact with existing properties in Paraview
I git clone the paraview repository, and I used CMake to get the .sln file and compile it with Visual Studio. I do the same with example plugin provided by Paraview itself (like the toolbar or property widget to understand how it works), everything works for now.
But when I try to set the data representation to "volume", nothing work (no results, it still uniform.)
What i've tried (from my plugin):
pqApplicationCore* applicationCore = pqApplicationCore::instance();
pqObjectBuilder* objectBuilder = applicationCore->getObjectBuilder();
pqServerManagerModel* serverManagerModel = applicationCore->getServerManagerModel();
if (serverManagerModel->getNumberOfItems<pqServer*>() == 1)
{
// Getting the first (and only) server
pqServer* server = serverManagerModel->getItemAtIndex<pqServer*>(0);
//Creating a reader for dicom files
pqPipelineSource* pipelineSource =
objectBuilder->createReader("sources", "DICOMReader", { file }, server);
// Getting the first view
pqView* v = serverManagerModel->getItemAtIndex<pqView*>(0);
// Setting the data representation to Volume, at least, i try to set it.
pqDataRepresentation* data = objectBuilder->createDataRepresentation(
pipelineSource->getOutputPorts().at(0), v, "UniformGridRepresentation");
// SOLUTION
vtkSMPVRepresentationProxy::SetScalarColoring(data->getProxy(), "DICOMImage", vtkDataObject::POINT);
pqSMAdaptor::setEnumerationProperty(data->getProxy()->GetProperty("Representation"), "Volume");
// wrong
data->setProperty("VolumeRendering", "volume");
data->setVisible(true);
}
CMakeList.txt
set(interfaces)
set(sources
MyToolBar.cxx
MyToolBar.h
MyToolBarActions.cxx
MyToolBarActions.h)
paraview_plugin_add_action_group(…….)
paraview_plugin_add_toolbar(…..)
paraview_add_plugin(pluginName
VERSION "1.0"
UI_INTERFACES ${interfaces}
SOURCES ${sources})
target_link_libraries(cmakePluginName PRIVATE ParaView::ServerManagerRendering)
I expected the "Representation" field to be on "Volume" but still in "Outline"
I also tried to change the "UniformGridRepresentation" to something else, with no results, except weird things and crashes.
Any ideas?
The setProperty you used concern Qt property (this class inherits from QObject) and not ParaView Proxy property.
you should replace this line with the following:
edit: add the SetScalarColoring part
vtkSMPVRepresentationProxy::SetScalarColoring(data->getProxy(), <ArrayName>, vtkDataObject::POINT);
pqSMAdaptor::setEnumerationProperty(data->getProxy()->GetProperty("Representation"), "Volume");
<ArrayName> is the data you want to use for coloration. If not specified, a unique Solid Color is used but it is not available for volume rendering.
vtkDataObject::POINT can also be vtkDataObject::CELL if <ArrayName> is associated to the cells and not to the points.
It is possible to automatically generate Sitecore templates just coding models? I'm using Sitecore 8.0 and I saw Glass Mapper Code First approach but I cant find more information about that.
Not sure why there isn't much info about it, but you can definitely model/code first!. I do it alot using the attribute configuration approach like so:
[SitecoreType(true, "{generated guid}")]
public class ExampleModel
{
[SitecoreField("{generated guid}", SitecoreFieldType.SingleLineText)]
public virtual string Title { get; set; }
}
Now how this works. The SitecoreType 'true' value for the first parameter indicates it may be used for codefirst. There is a GlassCodeFirstDataprovider which has an Initialize method, executed in Sitecore's Initialize pipeline. This method will collect all configurations marked for codefirst and create it in the sql dataprovider. The sections and fields are stored in memory. It also takes inheritance into account (base templates).
I think you first need to uncomment some code in the GlassMapperScCustom class you get when you install the project via Nuget. The PostLoad method contains the few lines that execute the Initialize method of each CodeFirstDataprovider.
var dbs = global::Sitecore.Configuration.Factory.GetDatabases();
foreach (var db in dbs)
{
var provider = db.GetDataProviders().FirstOrDefault(x => x is GlassDataProvider) as GlassDataProvider;
if (provider != null)
{
using (new SecurityDisabler())
{
provider.Initialise(db);
}
}
}
Furthermore I would advise to use code first on development only. You can create packages or serialize the templates as usual and deploy them to other environment so you dont need the dataprovider (and potential risks) there.
You can. But it's not going to be Glass related.
Code first is exactly what Sitecore.PathFinder is looking to achieve. There's not a lot of info publicly available on this yet however.
Get started here: https://github.com/JakobChristensen/Sitecore.Pathfinder
I have some components in my Sitecore install that can be added to one of any multiple placeholders on a page. The data source location of the renderings of these components can change based on what placeholder they are added to the site. I started creating a processor like
<getRenderingDatasource>
<processor patch:after="*[#type='custom']" type="custom" />
</getRenderingDatasource>
The class is like
public class GetDynamicDataSourceLocations : GetDatasourceLocation
{
public void Process(GetRenderingDatasourceArgs args)
{
...
}
}
I can't get the placeholder that I'm trying to attach the rendering to. Is there any way I can get the placeholder or atleast the parent where the component is being added?
Thanks
It's a very good idea you have here, but the GetRenderingDatasourceArgs can't provide you with the data you need if you're configuring the allowed datasource locations on the placeholder.
I've searched through querystring & form variables and context items as well, but there is no reference to the placeholder available in the getRenderingDatasource pipeline.
I did come up with something that could be the solution, although it's a bit hacky.
Create a processor for the getPlaceholderRenderings. The GetPlaceholderRenderingsArgs will provide you with a placeholder key.
Store the key in a session variable (I don't know another way of transferring data between pipelines at this point)
Retrieve the key from the session in your getRenderingDatasource processor.
This is the code I used to test it:
// Add to the getRenderingDatasource pipeline.
public class GetPlaceholderKey
{
public void Process(GetPlaceholderRenderingsArgs args)
{
System.Web.HttpContext.Current.Session["Placeholder"] = args.PlaceholderKey;
}
}
// Add to the getRenderingDatasource pipeline.
public class GetAllowedDatasources
{
public void Process(GetRenderingDatasourceArgs args)
{
Debug.WriteLine(System.Web.HttpContext.Current.Session["Placeholder"]);
}
}
This works when you add a rendering to a placeholder, but I have not tested other scenarios.
I can imagine it would not work when you set the datasource of a rendering that is already placed in the placeholder.
I would like to know the cleanest way of registering a file extension with my C++ application so that when a data file associated with my program is double clicked, the application is opened and the filename is passed as a parameter to the application.
Currently, I do this through my wix installer, but there are some instances where the application will not be installed on ths user's computer, so I also need the option of creating the registry key through the application.
Additionally, will this also mean that if the application is removed, unused entries in the registry will be left lying around?
Your basic overview of the process is found in this MSDN article. The key parts are at the bottom of the list:
Register the ProgID
A ProgID (essentially, the file type registry key) is what contains your important file type properties, such as icon, description, and context menu items including applications used when the file is double clicked. Many extensions may have the same file type. That mapping is done in the next step:
Register the file name extension for the file type
Here, you set a registry value for your extension, setting that extension's file type to the ProgID you created in the previous step.
The minimum amount of work required to get a file to open with your application is setting/creating two registry keys. In this example .reg file, I create a file type (blergcorp.blergapp.v1) and associate a file extension (.blerg) with it.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command]
#="c:\path\to\app.exe \"%1\""
[HKEY_CURRENT_USER\Software\Classes\.blerg]
#="blergcorp.blergapp.v1"
Now, you probably want to accomplish this programmatically. To be absolutely kosher, you could check for the existence of these keys, and change your program behavior accordingly, especially if you're assuming control of some common file extension. However, the goal can be accomplished by setting those two keys using the SetValue function.
I'm not positive of the exact C++ syntax, but in C# the syntax looks something like this:
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\blergcorp.blergapp.v1\shell\open\command", null, #"c:\path\to\app.exe \"%1\"");
Registry.SetValue(#"HKEY_CURRENT_USER\Software\Classes\.blerg", null, "blergcorp.blergapp.v1");
Of course you could manually open each sub key, manually create the ProgID and extension subkey, and then set the key value, but a nice thing about the SetValue function is that if the keys or values don't exist, they will automatically be created. Very handy.
Now, a quick word about which hive to use. Many file association examples online, including ones on MSDN, show these keys being set in HKEY_CLASSES_ROOT. I don't recommend doing this. That hive is a merged, virtual view of HKEY_LOCAL_MACHINE\Software\Classes (the system defaults) and HKEY_CURRENT_USER\Software\Classes (the per-user settings), and writes to any subkey in the hive are redirected to the same key in HKEY_LOCAL_MACHINE\Software\Classes. Now, there's no direct problem doing this, but you may run into this issue: If you write to HKCR (redirected to HKLM), and the user has specified the same keys with different values in HKCU, the HKCU values will take precedence. Therefore, your writes will succeed but you won't see any change, because HKEY_CURRENT_USER settings take precedence over HKEY_LOCAL_MACHINE settings.
Therefore, you should take this into consideration when designing your application. Now, on the flip side, you can write to only HKEY_CURRENT_USER, as my examples here show. However, that file association setting will only be loaded for the current user, and if your application has been installed for all users, your application won't launch when that other user opens the file in Windows.
That should be a decent primer for what you want to do. For further reading I suggest
Best Practices for File Association
File Types and File Association, especially
How File Associations Work
And see also my similar answer to a similar question:
Associating file extensions with a program
This is a two step process:
1. Define a program that would take care of extension: (unless you want to use existing one)
1.1 create a key in "HKCU\\Software\\Classes\\" for example
"Software\\Classes\\YourProgramName.file.ext"
1.2 create subkey "Software\\Classes\\YourProgramName.file.ext\\DefaultIcon"
1.2.1 set default value ("") to your application full path to get
icon from resources
1.3 create a subkey "Software\\Classes\\YourProgramName.file.ext\\Shell\\OperationName\\Command"
OperationName = for example Open, Print or Other
1.3.1 set default value ("") to your application full path +optional runtime params (filename)
2.Associate file extension with program.
2.1 create a key HKCU\\Software\\Classes\\.ext - here goes your extension
2.2 set default value to the program definition key
("YourProgramName.file.ext")
Below is part of the program written in c# which associate file extension. It is not c++ but i think it is simple enought to explain itself and AFAIK it is verv simmilar if not identical to the code in c++
1.
RegistryKey keyPFCTExt0 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc", true);
if (keyPFCTExt0 == null)
{
keyPFCTExt0 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc");
keyPFCTExt0.CreateSubKey("DefaultIcon");
RegistryKey keyPFCTExt0ext = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\DefaultIcon", true);
keyPFCTExt0ext.SetValue("", Application.ExecutablePath +",0");
keyPFCTExt0ext.Close();
keyPFCTExt0.CreateSubKey("Shell\\PFCT_Decrypt\\Command");
}
keyPFCTExt0.SetValue("", "PFCT.file.enc");
keyPFCTExt0.Close();
2.
RegistryKey keyPFCTExt1 = Registry.CurrentUser.OpenSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command", true);
if (keyPFCTExt1 == null)
keyPFCTExt1 = Registry.CurrentUser.CreateSubKey("Software\\Classes\\PFCT.file.enc\\Shell\\PFCT_Decrypt\\Command");
keyPFCTExt1.SetValue("", Application.ExecutablePath + " !d %1"); //!d %1 are optional params, here !d string and full file path
keyPFCTExt1.Close();
I don't know why people keep saying that HKEY_CURRENT_USER\Software\Classes\<.ext>'s Default value (which will redirect you into another (software-created) class.
It does work, but it will be overridden by
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\UserChoice
And I believe Microsoft recommends the second practice- because it's what the built-in "open with" is doing. The value of Progid" key is equal to default value of HKEY_CURRENT_USER\Software\Classes\<.ext> in this case.
I found the following while trying to manipulate associations using C#:
hkcu\software\microsoft\windows\currentVersion\explorer\fileexts.reg\userchoice -> for user specific settings. The values in the openWithProgIds
key point to the keys in the hkcr.
hkcr\xfile\shell\open\muiVerb value or hkcr\xfile\shell\open\command\default value -> affects open handler. This is the value that contains the path to a program.
hkcr\ .x -> affects context menu (new x) among other things related to the menus.
I don't know the C++ code, but given these info you must be able to manipulate the registry using the registry API.