Annoying "feature" of the BivariateManager

If the BivariateManager plugin is part of a ParaView project and the user loads a state file, he will often have to choose the “Use filenames from state” option, if e.g. he wants to open a state file from somebody else that comes together with other files in a package.

With the version that I am currently using (13.1), this will result in four warning messages about PNG files that could not be found in that location, but were loaded from some plugin location instead. This has no negative consequences, but it is highly confusing for the user because he has of course no idea what “Bremm.png” etc. is actually good for!?

Already in the source code of pqBivariateManager.cxx the solution of that problem is pointed out:

// Nice to have : textures names can be retrieved from the ressources directly.

Right - that’s it! Only that it is slightly more important than just “nice to have”, and I do not want my customers to always have to deal with these useless messages, but still I do not want to drop the option to use bivariate representations for whatever data they may have!

So right now I am trying to realize this job in my own version of the ParaView code. The point where I am struggling is at the end of pqBivariateManager.cxx: Here the programmer did a whole lot of actions that should not be required if the files would really be inside a resource file, like:

  • find the path where the files are stored
  • read the files
  • generate some “proxy” for them

Now in order to drop all this stuff, I would need to find out where these proxies with the PNG files are actually being used - and here I am failing! Just “grepping” through the source code, I do not find one single location where the bitmaps are actually being used!

Could it be that I can simply drop all this bitmap stuff entirely without losing any important functionality!?? This would of course be the easiest solution! Otherwise I can of course load them with the common “Qt style” method like “:/pqBivariateRepresentations/Recourses/Bremm.png” etc. - but I would have to know where such code is indeed required!

Actually my guess is that generating these proxies has also the adverse effect of writing them into all state files, which finally results in the above annoying warning messages…

Hi @cobo !

First, thanks for using the bivariate plugin!

Your analysis is perfectly correct! This nice to have have far reaching impact that were no identified. Storing this files into Qt resources file would definitely fix the issue.

It should not be that hard to do, but its not trivial either. Please open an issue on https://gitlab.kitware.com/paraview/paraview/-/issues

Ok, right now I would prefer to code a solution and only then make it some “issue with solution proposal”! The point is that

  • I want it now and in “my current” ParaView version, not at some time in the future when possibly somebody else finds time
  • Of course I could contribute code through the gitlab interface, but honestly I realized that the related “bureaucracy” is above my skills level in the git world…

This said, this is the actual state of my art relating such a self-coded solution. The construction site that I opened is in pqBivariateManager.cxx.

Loading the four PNG is no big magic, and once this is done, I can address them as follows:

namespace
{
  // Nice to have : textures names can be retrieved from the ressources directly.
  //const std::string TEXTURE_FILES[4]{ "Bremm.png", "Schumann.png", "Steiger.png", "Teulingfig2.png" };
  const std::string TEXTURE_FILES[4]
  {":/pqBivariateRepresentations/Resources/Bremm.png",
   ":/pqBivariateRepresentations/Resources/Schumann.png",
   ":/pqBivariateRepresentations/Resources/Steiger.png",
   ":/pqBivariateRepresentations/Resources/Teulingfig2.png"
  };
}

But at the end of the code I am struggling in the function

void pqBivariateManager::generateTextureProxies(pqServer* server)

My understanding is that instead of passing the full path and filenames, I would have to ensure that in vtkNetworkImageSource:UpdateImage() the mode “ReadFromMemory” is chosen, instead of “ReadFromFile”. And that of course for this purpose I need to provide the image data somehow as a vtkImageData object. And this has to happen in the above generate… function - right?

Ok, if now the variable textureFile contains one of the four images as a std::string in “Qt resource format”, I was generating such vtkImageData thing with the following code (which however I could not test yet - except that I could see that at least “something” was being read from the resources…):

if(textureFile[0] == ':') // or something more sophisticated...

  QPixmap pix(textureFile.c_str());
  QImage img = pix.toImage();

  vtkNew<vtkQImageToImageSource> qimageToImageSource;
  qimageToImageSource->SetQImage(&img);
  qimageToImageSource->Update();

  vtkNew<vtkImageData> image;
  image->DeepCopy(qimageToImageSource->GetOutput());

  // TEST: see if we read "something" from the resources - and see if at least it is
  //         different for the four bitmaps (result: yes, it is!)
  std::cout << "this is a Qt resource" << std::endl;
  std::cout << "<" << textureFile << ">" << std::endl;
  std::cout << "this is a vtkImageData pointer " << image.Get() << std::endl;
  std::cout << "- ref count " << image->GetReferenceCount() << std::endl;
  image->PrintSelf(std::cout, vtkIndent(2));
  unsigned char const* buf = static_cast<unsigned char const*>(image->GetScalarPointer());
  std::cout << "buf < " << std::setw(2) << std::setfill('0') << std::hex << (int)buf[0]
            << " " << std::setw(2) << std::setfill('0') << std::hex << (int)buf[1]
            << " " << std::setw(2) << std::setfill('0') << std::hex << (int)buf[2] << " >" << std::endl;

  ......
}

Now what I see is how the “trick” is done for passing a filename - at the very end of the function. And I understand that I need to generate a “Trivial Producer” with my vtkImageData information, probably in a somewhat similar way.

And this is exactly the place where I am struggling! Because this jungle of proxies, sources, server manager stuff etc. is not really easy to detangle…

This is the code for passing the file path and name:

      auto texturePath = texturesFolder + textureFile;

      // Create texture proxies. We do not use pqObjectBuilder because we need to specify
      // a custom proxy name (which is the name of the texture file, minus the extension)
      vtkSMSessionProxyManager* spxm = server->proxyManager();
      vtkSMProxy* textureProxy = spxm->NewProxy("textures", "ImageTexture");
      auto textureName = vtksys::SystemTools::GetFilenameWithoutExtension(textureFile);
      spxm->RegisterProxy("textures", textureName.c_str(), textureProxy);
      vtkSMPropertyHelper(textureProxy->GetProperty("FileName")).Set(texturePath.c_str());
      textureProxy->UpdateVTKObjects();
      textureProxy->Delete();