Taming QDial



  • Hi,

    Do you have the following requirements ?

    1 Preventing QDial jump-to-position on click ?
    2 Preventing QDial jump-to-maximum on End key, and jump-to-minimum on Home key ?
    3 Smooth control of an on-screen visual, or off-screen motor ?

    Then read on :-)

    I've seen each of the above requested on the forum. Example code provided below.

    The basic idea, is to constrain the size of user-requested position change.
    Specifically, to constrain the change to an accelerating limit, up to a coast maximum.
    As such, the constaint acts to prevent 1 and 2, and provide for 3:

    The effect of this relatively simple mod, is that now if you drag, starting
    from a position away from the nub, it smoothly accelerates to meet you; whether
    moving away from or toward the nub. Keys likewise smoothly accelerate up to the
    configured singleStep rate for arrow keys, pageStep rate for Page Up/Down,
    and coast maximum for Home/End, in place of the former behaviour.

    The accelerating limit is reset at mouse/touch move end. QDial or its inheritee
    QAbstractSlider, omit a signal for key-initiated move end. So end is the simple expedient
    of a timeout, period 0.5s to accommodate the default key repeat threshold.

    If you want to see a working demo, proceed as follows. Should take 10 mins tops,
    to get up and running:

    As mentioned in other posts, we're using Qt Script only, not fullblown Qt. This due to
    restriction placed by our vendor tool, that launches our .js. Which then in turn,
    via vendor-specific command, launches our .ui.

    Qt Script, or the vendor environment at least, limits code access to properties, signals,
    public slots, and the vendor-specific commands. Public functions are inaccessible.

    I would attach my example code, TameDial.js/us. But the forum errors, saying I lack
    the privileges as yet. Instead, I have copy-pasted the .js/ui file content below.
    They are not too long. Just copy-paste back into an empty TameDial.js/ui file.
    Now with better wrapping support :-)

    You will need our vendor tool, to open the .js, which launches the .ui. The demo version of the
    tool can be freely downloaded from https://www.emtas.de/en/download/canopen-deviceexplorer-demo/.
    It times out and exits after 1 hour of use, although can be restarted. Within the tool, to
    launch the .js, select PlugIns > CAN/CANopen Scripting, then Load, and nav to the .js

    The QDial has default properties, except maximum increased from 99 to 999, for resolution
    and smoothness. And singleStep increased from 1 to 5, to expose arrow key acceleration.

    Naturally the same approach and code could be used for the other QAbstractSliders,
    namely QScrollBar and QSlider. Feel free to tame those :-)

    Best regards,

    David King

    TameDial.js
    -----------
    
    util.loadUIFile( "TameDial.ui", "TameDial" );
    // Vendor-specific UI file load command
    
    var Dial = TameDial.findChild( "dial" );  // Dial handle
    
    Dial.valueChanged.connect( Change );  // Value change
    Dial.sliderReleased.connect( End );   // Move end
    
    var Wrap = Dial.wrapping;  // Wrapping
    
    if ( Wrap )  // Wrapping enabled
    {
       var Min = Dial.minimum;  // Dial minimum
       var Max = Dial.maximum;  // Dial maximum
    
       var Range = Max - Min;  // Dial range
       var Half = Range / 2;   // Half range
    };
    
    var User = true;  // User-requested change
    var Control = 0;  // Controlled value
    var Limit = 0;    // Speed limit
    var Coast = 20;   // Speed coast (tune if desired)
    var Time = 0;     // End timeout
    
    util.every( 0, "Cyclic()" );  // Cyclic task
    
    
    function Change( )  // Value change
    {
       if ( User )  // Prevent reentrancy
       {
          Time = Date.now( ) + 500;
          // End timeout (500ms after last movement)
    
          var Value = Dial.value;  // User-requested value
    
          if ( Limit > 1 )
          // Ignore first two requests, including mouse press,
          // and mouse release within timeout, so no click move
          {
             var Delta = Value - Control;  // Value delta
    
             if ( Wrap )  // Wrapping enabled
             {
                if ( Delta > Half  &&  Value != Max ) Delta -= Range;
                else if ( Delta < -Half  &&  Value != Min ) Delta += Range;
                // Delta wrap
             }
    
             if ( Delta > Limit ) Delta = Limit;
             else if ( Delta < -Limit ) Delta = -Limit;
             // If delta exceeds limit, constrain
    
             Control += Delta;  // Controlled value change
    
             if ( Wrap )  // Wrapping enabled
             {
                if ( Control > Max ) Control -= Range;
                else if ( Control < Min ) Control += Range;
                // Controlled value wrap
             }
          }
    
          if ( Control != Value ) User = false, Dial.value = Control, User = true;
          // If difference, update dial
    
          if ( Limit != Coast ) Limit++;
          // If not coast speed, increment limit
    
          TameDial.windowTitle = "TameDial   Value " + Value +
             "  Control " + Control + "  Limit " + Limit  // Metrics
       }
    }
    
    function End( )  // Move end
    {
       Limit = 0;  // Speed limit reset
    
       TameDial.windowTitle = "TameDial"  // Indicate end
    }
    
    function Cyclic( )  // Cyclic task
    {
       if ( Time  &&  Date.now( ) > Time ) Time = 0, End( );
       // If end timeout, remove, end move
       // Short-circuit evaluate efficiently bails expression if no timeout
    }
    
    
    TameDial.ui
    -----------
    
    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>TameDial</class>
     <widget class="QWidget" name="TameDial">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>400</width>
        <height>200</height>
       </rect>
      </property>
      <property name="minimumSize">
       <size>
        <width>400</width>
        <height>200</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
        <width>400</width>
        <height>200</height>
       </size>
      </property>
      <property name="windowTitle">
       <string>TameDial</string>
      </property>
      <property name="sizeGripEnabled" stdset="0">
       <bool>false</bool>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout">
       <property name="spacing">
        <number>16</number>
       </property>
       <property name="leftMargin">
        <number>16</number>
       </property>
       <property name="topMargin">
        <number>16</number>
       </property>
       <property name="rightMargin">
        <number>16</number>
       </property>
       <property name="bottomMargin">
        <number>16</number>
       </property>
       <item>
        <widget class="QDial" name="dial">
         <property name="maximum">
          <number>999</number>
         </property>
         <property name="singleStep">
          <number>5</number>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

  • Qt Champions 2016

    Hi
    Cool.
    I think you are the first i have seen here using Qt via QtScript + js :)
    I didn't know you much you can actually do !


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.