Saturday, 3 June 2017

Advanced custom Qt Container Widgets and Qt Designer

Qt has a nice UI editor called Designer, and you can create custom widgets that go in Designer's toolkit. But the only example I've ever found is this one in the Qt docs, which doesn't explain how to create container widgets.

The problem is to create a widget that contains some decoration or controls, but also has a sub-window where people can put their own widgets.

For example, I wanted an "accordion" control that had a checkbox at the top to open and close it, then to be able to put any other control inside this.

The way it will be structured is a QAccordion, with a VBoxLayout, will contain a QCheckbox and a QWidget called the content widget. This content widget will have its own VBoxLayout, where the controls will go.

You will create two classes: one called (say) QAccordion, which implements the widget, and one called QAccordionInterface, which tells Designer that it's available.

<#include <qwidget>
#include "GeneratedFiles/ui_QAccordion.h"
#include "Export.h"

class SIMUL_QT_WIDGETS_EXPORT QAccordion : public QWidget
 Q_PROPERTY(QString title READ title WRITE setTitle DESIGNABLE true)
 Q_PROPERTY(bool open READ isOpen WRITE setOpen DESIGNABLE true)
 QAccordion(QWidget *parent = 0);
 void setTitle(QString f);
 QString title() const;
 void setOpen(bool o);
 bool isOpen() const;
public slots:
 void on_accordionCheckBox_toggled();
 void setSearchText(const QString &);
 void childEvent ( QChildEvent * event ) override;
 void paintEvent(QPaintEvent *) override;
 Ui::Accordion ui;
 bool setup_complete;
 QWidget *contentsWidget;
 void hookupContentsWidget();

The subclass Ui::Accordion shows that I created the basic class in Designer itself. This is optional, but the QAccordion.ui file is just:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <widget class="QWidget" name="Accordion">
  <property name="geometry">
  <property name="sizePolicy">
   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
  <property name="windowTitle">
  <layout class="QVBoxLayout" name="verticalLayout">
    <widget class="QCheckBox" name="accordionCheckBox">
     <property name="text">
     <property name="checked">

By putting the layout and checkbox in the ui file, they will be created in code, in Ui::Accordion.

But: if we were to create the whole thing, including the contents widget in here, after we built the class the contents widget would NOT be accessible in Designer, and neither would its layout be recognized. So instead we put these in a function called domXml in QAccordionInterface.

 QString QAccordionInterface::domXml() const
     return "<ui language=\"c++\">\n"
            " <widget class=\"QAccordion\" name=\"accordion\">\n"
            "  <property name=\"geometry\">\n"
            "   <rect>\n"
            "    <x>0</x>\n"
            "    <y>0</y>\n"
            "    <width>100</width>\n"
            "    <height>24</height>\n"
            "   </rect>\n"
            "  </property>\n"
            "  <property name=\"toolTip\" >\n"
            "   <string></string>\n"
            "  </property>\n"
            "  <property name=\"whatsThis\" >\n"
            "   <string>.</string>\n"
            "  </property>\n"
      "  <widget class=\"QWidget\" name=\"contentsWidget\" native=\"true\" >\n"
      "   <layout class=\"QVBoxLayout\" name=\"accContentsVLayout\">\n"
      "    <property name=\"spacing\">\n"
      "     <number>2</number>\n"
      "    </property>\n"
      "    <property name=\"leftMargin\">\n"
      "     <number>2</number>\n"
      "    </property>\n"
      "    <property name=\"topMargin\">\n"
      "     <number>2</number>\n"
      "    </property>\n"
      "    <property name=\"rightMargin\">\n"
      "     <number>2</number>\n"
      "    </property>\n"
      "    <property name=\"bottomMargin\">\n"
      "     <number>2</number>\n"
      "    </property>\n"
       "    </layout>\n"
      "  </widget>\n"
            " </widget>\n"

By specifying the contents widget and its layout here, Designer will know to dynamically create them when you add a QAccordion, so they'll appear in the editor. You can then drag any control into the contents widget, and it will be correctly positioned. Be careful that you drag it to the contents widget and not the QAccordion itself or a subcontrol. Designer doesn't properly obey its "isContainer" function, so it sees any custom control as a container, not just the ones you indicate.

So now in designer, we can add QAccordions. Without styling they just look like checkboxes with a space below where you can drag controls:

After applying some styling, the final result looks like this:

The accordion elements - Cloud Window, Precipitation etc are inside a searchable property panel, implemented on the same principles.

And here are the files for the final class:

Saturday, 20 May 2017

Sfx: a generic effect compiler for shaders

So Microsoft, Nvidia and the rest used to support effect files: a text source file that contained multiple shaders, but also "techniques" and "passes", where each pass can have state specified: blending, rasterization etc.

Some time ago, for reasons I guess of supply and demand, fx fell out of fashion. Microsoft still provides the D3D11 version of its Effects library as open source here, and this reads binary output that the fxc tool can create. But Fxc is being replaced by by this, which doesn't support effects. Nvidia's Tristan Lorach proposed a new framework, nvFX (pdf), but I don't think it's under active development.

D3D12 has no effect support. So we need to add it.

My approach at Simul is called Sfx. The idea is to take an initial file that's compatible with Microsoft's HLSL effect format, with the code written in HLSL. Sfx will extract the passes and compile all the relevant shaders by building smaller individual shader source files and calling an external compiler. A small json file will specify which compiler to use, and various other parameters to allow translation from HLSL to whatever language the compiler expects.

For example, HLSL.json looks like:

  "compiler": "C:/Program Files (x86)/Windows Kits/10/bin/x64/fxc.exe",
  "defaultOptions": "/T {shader_model} /nologo",
  "sourceExtension": "hlsl",
  "outputExtension": "cso",
 "outputOption": "/Fo",
 "entryPointOption":  "/E{name}",
  "multiplePixelOutputFormats": false

So Sfx should be compiler-independent. It outputs two things: a text file with the .sfxo extension, and a number of platform-specific shader binaries. The sfxo looks like this:

texture fontTexture 2d read_only 0 single
SamplerState clampSamplerState 9,LINEAR,CLAMP,CLAMP,CLAMP,
SamplerState cmcNearestSamplerState 13,POINT,CLAMP,MIRROR,CLAMP,
RasterizerState RenderNoCull (false,CULL_NONE,0,0,false,FILL_SOLID,true,false,false,0)
RasterizerState wireframeRasterizer (true,CULL_NONE,0,0,false,FILL_WIREFRAME,false,false,false,0)
BlendState AlphaBlendRGB false,(true),1,1,4,5,0,0,(7)
DepthStencilState DisableDepth false,0,4
 technique backg
  pass p0
   rasterizer: RenderNoCull
   depthstencil: DisableDepth 0
   blend: AlphaBlendRGB (0,0,0,0) 4294967295
   vertex: font_FontVertexShader_vv.cso,(),(),()
   pixel: font_FontPixelShader.cso,(),(),()

This stands to improve over time. In this case, we've taken an sfx file containing this definition:

VertexShader vs = CompileShader(vs_4_0, FontVertexShader());
technique text
    pass p0
  SetRasterizerState( RenderNoCull );
  SetDepthStencilState( DisableDepth, 0 );
  SetBlendState(AddBlendRGB,vec4( 0.0, 0.0, 0.0, 0.0), 0xFFFFFFFF );

so we've compiled FontPixelShader() and FontVertexShader() into the cso files: this example is for HLSL. As we extend Sfx to other languages, the json definition in particular will become more complex - possibly using regexes to specify how HLSL is translated into other C-style shader languages.

Thursday, 18 May 2017

HDR output in Unreal Engine

To get Unreal Engine to output from consoles in HDR format (i.e. to HDR TV's), there are a few settings. according to this post, there are variables that can go in the .ini files. But in my experience, setting EnableHDROutput to 1 in Engine.ini, causes UE to crash on initialization. As of May 2017, the solution seems to be to either enter the hdr settings every time via the console, or use the Blueprint function EnableHDRDisplayOutput:

This seems to also cover the r.HDR.Display.OutputDevice and r.HDR.Display,ColorGamut settings, so one call will do it.

Sunday, 16 April 2017

cldoc - a promising documentation generator

cldoc needs Python 2, and won't yet work with Python 3. You have to make sure it's at least Python 2.7.9 so that you get pip.exe, which will be needed to install various extras. After installing Python 2, run:

python -m ensurepip --upgrade

to get pip, then:

pip install pyparsing

You might have to do this from C:\Python27\Scripts. If you don't want to replace your Python 3 setup in Path and PYTHONPATH, just calling "pip install", even from the Python 2 directory, will run the pip for Python 3.

You may need the latest Clang: older versions don't all have full support for C++14 features on Windows.

Create a Sublime Text build system, called cldoc.sublime-build, and fill it with:
 "cmd": ["C:\\Python27\\python.exe","C:\\PATH TO\\cldoc-dev","generate","--","--output","C:/PATH TO/docout","C:/PATH TO/*.h"]
 ,"env": {"PYTHONPATH":"C:\\Python27"}
,"file_regex": "^(.*)\\:([0-9]*)\\:([0-9]*)\\:"

Testing the docs in Windows

To test the documentation website on Windows, you must follow the instructions. Watch out for the SSL errors. Once you have jekyll on your Windows machine, you can go to the site directory and call e.g.:
jekyll build --incremental --destination G:/
That destination part is because the links to css files etc start with a slash. So the only way you can test this locally is if the site is at the root of some directory. Create a small partition on your local drive, and put the site there. .htaccess for XAMPP:

Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !-f
RewriteRule ^([a-zA-Z0-9_-]+)$ $1.html 

Thursday, 17 November 2016

Flex/Bison error lines compatible with Visual Studio

Flex and Bison can be built from source, to be found at To get errors and warnings that you can double-click in Visual Studio, you can modify the code as follows:

In the file bison\src\location.c, find the function unsigned location_print (location loc, FILE *out) - and replace it with:

location_print (location loc, FILE *out)
  unsigned res = 0;
  int end_col = 0 != loc.end.column ? loc.end.column - 1 : 0;
  res += fprintf (out, "%s",
                  quotearg_n_style (3, escape_quoting_style, loc.start.file));
  if(0 <= loc.start.line||loc.start.file != loc.end.file||0 <= loc.end.line)
      res += fprintf (out, "(");
  if (0 <= loc.start.line)
      res += fprintf (out, "%d", loc.start.line);
      if (0 <= loc.start.column)
        res += fprintf (out, ",%d", loc.start.column);
  if (loc.start.file != loc.end.file)
  // Ignore: Visual Studio can't cope with this case.
  if(0 <= loc.start.line||loc.start.file != loc.end.file||0 <= loc.end.line)
      res += fprintf (out, ")");
  return res;
That fixes it for Bison. For Flex, in flex/src/parse.c, change the function line_pinpoint( str, line ) to
void line_pinpoint( str, line )
const char *str;
int line;
 fprintf( stderr, "%s(%d): %s\n", infilename, line, str );

My branch incorporating these changes is here.

Tuesday, 8 November 2016

Using Sublime Text with FASTBuild

Sublime text can be used to run FASTBuild, with this build script. Save this as "FASTBuild.sublime-build", in your user AppData\Roaming\Sublime Text 3\Packages\User directory (or Mac/Linux equivalent location).
 "cmd": ["C:/Simul/master/Simul/External/FASTBuild/FBuild.exe","-config","$file"]
 ,"file_regex": "(.*)\\((.*),(.*)\\): FASTBuild (Error .*)$"
 ,"selector": "source.bff"
By using the selector "source.bff", it should match up automatically with the syntax (below), but I've not quite figured this part out yet. Here's a preliminary Sublime Text syntax for FASTBuild. Call it "FASTBuild.sublime-syntax" and save it in the same directory.

%YAML 1.2
name: FASTBuild
file_extensions: [bff]
scope: source.bff

    - include: scope:source.c#comments

    - match: "[^\"]"
      scope: string
    - match: \"
      pop: true
      scope: string
    - match: \"
      push: instring
      scope: string

    - match: "[^']"
      scope: string
    - match: "'"
      pop: true
      scope: string
    - match: "'"
      push: inquotes
      scope: string

    - match: "\\.(\\w*)"
      scope: keyword
    - match: "^\\s*(#\\s*\\binclude)\\b"
        1: keyword.control.include.c++
    - match: "^\\s*(#)\\s*\\b(import)\\b"
      scope: keyword.control.c

    - include: scope:source.c#incomplete-inc
    - include: preprocessor-macro-define
    - include: scope:source.c#pragma-mark
    - include: preprocessor-includes
    - include: preprocessor-import
    - include: comments
    - include: preprocessor
    - include: strings
    - include: quotes
    - include: variables
    - include: global
    - match: \b(if|else|for|while)\b
      scope: keyword.control.c

Tuesday, 9 August 2016

Qt oddness within Unity

I've noticed some very strange behaviour when launching a Qt-based UI from Unity recently. Possibly from updating Qt, but the UI would fail to respond to mouse clicks or other signals, until I dragged or resized the window, at which point, all the inputs I had made would then replay very quickly and get up to date.
The only solution I've found is to create a QTimer outside of the main window context (the QApplication is its owner), and set it to call QCoreApplication::processEvents every 100ms or so.

QTimer *timer=new QTimer(pApp);