2015年12月4日 星期五

QTest::toString(const T&), class template, explicit template specialization

若有定義好==運算子,QCOMPARE巨集可比較自定義類別。

舉例來說:
class Employee {
public:
  Employee() { id_ = count_++; }
  unsigned id() const { return id_; }
private:
  static unsigned count_;
  unsigned id_;
}

unsigned Employee::count_ = 0;

定義==運算子
bool operator ==(const Employee& lhs, const Employee& rhs)
{
  return lhs.id() == rhs.id();
}


於Test Case
Employee employee1;
Employee employee2;
QCOMPARE(empolyee1, employee2);


如此,執行Unit Test會跑出Compare values are not the same。但不會像比較double等內定型別一樣,列出ActualExpected


若要希望QCOMPARE自定類別也可列出ActualExpected,需explicit template specialization QTest::toString(const T&):

namespace QTest {
template <>
char* toString(const Employee& employee)
{
  QByteArray ba = "Employee, id = ";
  ba += QByteArray::number(employee.id());
  return qstrdup(ba.data());
}
}

如此,會跑出
Actual   (employee1): Employee, id = 0
Expected (employee2): Employee, id = 1



假使希望對class template做相同的事:

template <unsigned _rowCount>
class Vector {
public:
  double& operator ()(unsigned row) { return data_[row-1]; }
  double operator ()(unsigned row) const {return data_[row-1]; }
private:
  double data_[_rowCount];
}

==運算子:
template <unsigned _rowCount>
bool operator ==(const Vector<_rowcount>& lhs, const Vector<_rowcount>& rhs>
{
  for (unsigned row = 1; row <= _rowCount; ++row) {
    if (lhs(row) != rhs(row)) return false;
  }
  return true;
}

class template之toString之寫法較特殊:
namespace QTest {
template <typename T, unsigned _rowCount>
char* toString(const Vector<_rowcount>& v)
{
  QByteArray ba = "Vector(";
  for (unsigned row = 1; row <= _rowCount; ++row) {
    ba += QByteArray::number(v(row));
    ba += (row != _rowCount) ? ", " : ")";
  }
}
}
儘管T未使用,仍不可只寫<>,否則無法正確explicit template specialization toString(const T&)ActualExpected也不會正確顯示。

先前EmployeetoString也可改寫為<typename T>,脫褲子放屁就是了。


於Test Case
Vector<3> vector1;
vector1(1) = vector1(2) = vector1(3) = 1;
Vector<3> vector2;
vector2(1) = vector2(2) = vector2(3) = 2;
QCOMPARE(vector1, vector2);


如此,會跑出
Actual   (vector1): Vector(1, 1, 1)
Expected (vector2): Vector(2, 2, 2)

2015年9月27日 星期日

更新Xcode 7.0後 Qt 5.5.0程式無法編譯

作業系統: OSX 10.10.5
Qt版本: 5.5.0
Qt Creator版本: 3.3.2


更新Xcode 7.0後,編譯出現下列錯誤:

:-1: warning: no such sysroot directory: '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk'

結果是MacOSX10.10.sdk已消失,變成MacOSX10.11.sdk


有兩種解決方法:

1.
在.pro檔加上 QMAKE_MAC_SDK = macosx10.11

2.
在/usr/local/Cellar/qt5/5.5.0/mkspecs/qdevice.pri 加上
!host_build:QMAKE_MAC_SDK = macosx10.11


參考:
stackoverflow


2015年5月1日 星期五

Qt實作emit數字signal之PushButton

之前寫的某個程式,有個0~200的slider。
因為需要經常性將slider值設回100,加了一個text為100的push button,於click後作動。

原本在MainWindow寫好接收push button clicked signal的slot,於其中呼叫slider的setValue(int)設值。

後來越看越覺得這樣多一層函式很醜,因為setValue(int)本來就是slot。
為何不直接繼承出一種有clicked(int) signal的push button?

實作如下:

class MyPushButton : public QPushButton{
  Q_OBJECT
public:
  explicit MyPushButton(QWidget* parent = 0);

signals:
  void clicked(int value);
  void clicked(double value);

protected:
  void mouseReleaseEvent(QMouseEvent* e);

};

繼承QPushButton,增加clicked(int)clicked(double)兩個signals,並override mouseReleaseEvent以於正確時機emit。


NumberButton::NumberButton(QWidget* parent)
  : QPushButton(parent)

{ }

建構式,不搞怪乖乖把parent傳到QPushButton建構式

void MyPushbutton::mouseReleaseEvent(QMouseEvent* e)
{
  if(e->button() == Qt::LeftButton && hitButton(e->pos())){
    emit clicked(text().toInt());
    emit clicked(text().toDouble());
  }

  QPushButton::mouseReleaseEvent(e);

}

放開的button為滑鼠左鍵(表示先前一定有按下),且游標仍在Button內,則發出signal。
最後將QMouseEvent傳回QPushButton::mouseReleaseEvent,做正常處理。


2015年4月25日 星期六

#ifdef、#elif、defined

 最近因為想把用Open GL的程式放到OS X跑,把Preprocessor #include的部分改為

#ifdef _WIN32
...
#elif __APPLE__
#include
...
#endif

後來感覺#elif部分怪怪,查了一下,發現#ifdef其實是#if defined的方便寫法,而原本的#elif部分並不是檢查有無define,而是做條件判斷。

隨便寫個C++程式測試:

...
#ifdef NOT_DEFINED
...
#elif ALSO_NOT_DEFINED
  std::cout << "print for test" << std::endl;
...

可編譯,不輸出


...
#define DEFINED

...
#elif DEFINE
  std::cout << "print for test" << std::endl;
...

無法編譯,"error: expected value in expression"


...
#define DEFINED_AS_ZERO 0

...
#elif DEFINED_AS_ZERO
  std::cout << "print for test" << std::endl;
...

可編譯,不輸出


...
#define DEFINED_AS_ONE 1

...
#elif DEFINED_AS_ONE
  std::cout << "print for test" << std::endl;
...

可編譯,有輸出


改用#elif defined,皆可編譯,除ALSO_NOT_DEFINED外皆有輸出


白目想說cout __APPLE__看看,輸出1,無怪乎原本用#elif也能順利include。但#elif define(__APPLE__)感覺較良好,還是改吧。

2015年4月19日 星期日

於MBPR安裝Qwt

安裝

1. 自SourgeForge下載qwt-6.1.2.tar.bz2

brew有Qwt, 但測試安裝好像是綁Qt4

2. tar jxvf qwt-6.1.2.tar.bz2解壓縮檔案

3. (於qwt資料夾)qmake

4. make

5. make install

在Window裝差不多, 就抓的壓縮檔不同和用的compiler, compile指令不同

使用

1. 在要用Qwt的專案.pro檔加上

include (/usr/local/qwt-6.1.2/features/qwt.prf)

程式到這邊qmake, 編譯都沒問題, 但執行時會出現

dyld: dyld: Library not loaded: qwt.framework/Versions/6/qwt
Reference from: (你的檔案路徑)
Reason: image not found
程式未預期終止。

google到解決方法

2. 點Qt Creator左邊"專案", "建置並執行"分頁上方Clang 64bit下建置右邊的"執行", 進"執行設定", 在下面"Run Environment", 點"詳情", 點"新增", 加入變數"DYLD_FRAMEWORK_PATH", 值"/usr/local/qwt-6.1.2/lib"

值和解決方法不同的原因是我qwt lib沒裝在/usr/local/lib, 看大陸人部落格才想起來

3. 執行ok, 收工

這只是透過Qt Creator執行ok就是了, 之後大概要用前面解決方法 b

2015年4月17日 星期五

於MBPR安裝Qt5與Qt Creator - 使用Homebrew

主要參考此網頁, 安裝成功之作業系統為OS X 10.10.3

1. 安裝Homebrew

  • 開啟terminal (以Spotlight搜尋terminal.app)
  • 官網之script(ruby -e "...)貼至terminal執行
  • 按指示完成安裝, 會順便灌好xcode, xcode command line tools, git ...

2. (於terminal) brew update

取得最新版的Homebrew, 我不確定剛下載好是不是需要update.., 但就update吧

3. brew install qt5

經過漫長的下載, 編譯後將Qt安裝在/usr/local/opt/qt5

4. brew linkapps

通過此步驟才能在應用程式找到Assistant, Designer, Linguist.. 雖然說有了Creator我從沒開過Assistant, Designer, 但Spotlight不知為何連不到Designer, Assistant, 還是link一下好

以下說明Creator安裝方法

5. brew install caskroom/cask/qt-creator

正常好像會先brew install caskroom/cask/brew-cask把cask裝好, 但上述步驟應可直接裝好cask, 再裝Qt Creator

再來要於Qt Creator設定使用的Qt版本(qmake位置)及編譯器

6. 開啟Qt Creator(用Spotlight), 選螢幕上方的"工具"-"外部"-"Configure", 開啟喜好設定視窗

7. 點左邊的"建置並執行", 點上方"Qt Versions"分頁, 點右方"新增"

準備連結qmake

8. 從上面的combo box(下拉式表單)選Macintosh HD

qmake在/usr/local/opt/qt5/bin , 但因usr是隱藏資料夾, 無法點選

9. 按鍵盤之 "Command" + "Shift" + "."

跑出usr等一堆隱藏資料夾

10. 打開/usr/local/opt/qt5/bin/qmake

有了qmake產生makefile檔, 再來就是設定使用的Compiler

11. 點上方"Kits"分頁, 點右方"新增", 下方"Compiler"combo box選"Clang (x86 64bit in /usr/bin)", "Qt版本"選"Qt 5.4.1 (5.4.1)"

至此大功告成, 趕快新增Qt Widgets Application測試. 體驗在Windows和Linux垂涎已久的Macintosh Style


可能有時候還是想在terminal執行qmake, 以下說明PATH變數設定方法

1. vim ~/.bash-profile

不會用vim就用別的文字編輯器吧

2. 加上 export PATH="/usr/local/bin:$PATH" , 儲存檔案

3. 重新開啟terminal, echo $PATH確定有/usr/local/opt/qt5/bin

現在已可在terminal run qmake, 可qmake --version看qt版本

2015年4月15日 星期三

以GTAV操作手法於FlightGear飛Cessna 172P - XBOX360搖桿XML檔設定

GTAV PS4控制器按鍵對應

  • 左操作桿-左右: 副翼(Aileron),操作桿向右,飛機右滾轉
  • 左操作桿-上下: 升降舵(Elevator),操作桿向下、升降舵向下
  • L1: 方向舵(Rudder)左擺
  • R2: 方向舵右擺
  • L2: 熄火/減速
  • R2: 發動/加速
  • 右操作桿-左右: heading(or yaw) 視向(view direction),操作桿向右→往右看
  • 右操作桿-上下: pitch 視向,操作桿向下→往下看
  • 左操作桿-按下: 收/放 起落架
  • 右操作桿-按下: 按下-視向轉正後方;放開-視向轉回正前方
  • 觸控版: 改變視角
  • OPTIONS按鈕: 暫停

FlightGear控制器操作設定方法

FlightGear內的搖桿設定GUI功能有限,為達目標需編輯xml檔。

指定連結到特定xml檔

首先,編輯/FlightGear/data/joysticks.xml如下(參考網頁)
<PropertyList>
  <js n="0" include="Input/Joysticks/custom.xml"/>
</PropertyList>
如此,可強制第一支控制器js n="0"(基本上也只會插一個)以/FlightGear/data/Input/Joysticks/custom.xml對應操作按鍵。

編輯joystick xml檔

基本編輯方法建議參考此教學與/data/Docs/readme Command章節。以下僅就如何於各軸(axis)、按鈕(button)實現GTAV操作對應進行說明。

左操作桿-左右

<axis n="0">
  <binding>
property-scale command將property aileron之value設為axis值
    <command type="string">property-scale</command>
    <property type="string">/controls/flight/aileron</property>
  </binding>
</axis>

左操作桿-上下

<axis n="1">
  <binding>
    <command type="string">property-scale</command>
    <property type="string">/controls/flight/elevator</property>
因左操作桿向下為正,而elevator向上為正,故設factor為-1以反轉
    <factor type="double">-1</factor>
  </binding>
</axis>

L1-R1

XBOX 360搖桿之L1-R1不是類比,不能直接對應成L1-R1按多少→Rudder擺多少。
因此,設定成按下R1→Rudder持續向右擺,放開→回歸原點;按下L1→Rudder持續向左擺,方開→回歸原點。
L1
<button n="4">
  <binding>
property-interpolate command以設定之rate變動property rudder至value
    <command type="string">property-interpolate</command>
    <property type="string">/controls/flight/rudder</property>
Rudder左擺極值為-1
    <value type="double">-1</value>
設定rate,即每秒變動量
    <rate type="double">0.5</rate>
  </binding>
mod-up表放開按鈕
  <mod-up>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
放開L1 Rudder回中心,而中心值為0
      <value type="double">0</value>
      <rate type="double">0.5</rate>
    </binding>
  </mod-up>
</button>
R1
<button n="5">
  <binding>
    <command type="string">property-interpolate</command>
    <property type="string">/controls/flight/rudder</property>
Rudder右擺極值為1
    <value type="double">1</value>
    <rate type="double">0.5</rate>
  </binding>
  <mod-up>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">0</value>
      <rate type="double">0.5</rate>
    </binding>
  </mod-up>
</button>

L2-R2

這個最複雜,因為R2同時要包含油門控制+引擎發動,L2要同時包含煞車+引擎熄火,使用nasal script處理
<axis n="2">
  <binding>
    <command type="string">nasal</command>
    <script type="string">
於script中取得axis值
      var axisValue = cmdarg().getNode("setting").getValue(); 

若引擎未啟動、且R2按到接近底(至底axis值為-1),將starter switch設為1,啟動引擎
      if(getprop("/engines/engine/running") != 1
    and -0.9 > axisValue){
  setprop("/controls/switches/starter", 1);

若magnetos未設為3,設為3
  if(getprop("/controls/engines/engine/magnetos") != 3){ <!-- magnetos setting -->
    setprop("/controls/engines/engine/magnetos", 3);
  }
      }
若引擎已啟動,將starter switch設回0
      else{
 setprop("/controls/switches/starter", 0); 
      }

      <!-- axisValue 0~-1, R2, throttle -->
若axis值小於0,即按R2(0~-1),設定油門(0~1)為axis反轉值
      if(0 >= axisValue){
  setprop("/controls/engines/engine/throttle", -axisValue);
      } 

      <!-- axisValue 0~1, L2, brake -->
若axis值大於0,即按L2(0~1),設定左右煞車(0~1)為axis值
      if(axisValue >= 0){ # L2 is positive (0 ~ 1)
 setprop("/controls/gear/brake-left", axisValue);
 setprop("/controls/gear/brake-right", axisValue);
      } 

若axis值大於0.8,即L2按接近底,將magnetos設為0,引擎熄火
      if(axisValue > 0.8 and getprop("/controls/engines/engine/magnetos") != 0){
 setprop("/controls/engines/engine/magnetos", 0)
      }
    </script>
  </binding>
</axis>

右操作桿-左右

<axis n="4">
axis值約小於-0.9,即右操作桿左擺至接近極限時為low。
  <low> <!-- left --> <!-- axis value < -0.9 -->
動作需重複,即右操作桿左擺時持續減小pitch數值
    <repeatable type="string">true</repeatable>
    <binding>
property-adjust command用以將property數值加上step值
      <command type="string">property-adjust</command>
      <property type="string">/sim/current-view/goal-heading-offset-deg</property>
      <step type="double">-1</step>
    </binding>
  </low>
axis值約大於0.9,即右操作桿右擺至接近極限時為high。
  <high> <!-- right --> <!-- axis value > 0.9 -->
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">property-adjust</command>
      <property type="string">/sim/current-view/goal-heading-offset-deg</property>
      <step type="double">1</step>
    </binding>
  </high>
</axis>

右操作桿-上下

<axis n="3">
axis上擺至接近極限為low
  <low>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">property-adjust</command>
      <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
      <step type="double">-1</step>
    </binding>
  </low>
axis上擺至接近極限為high
  <high>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">property-adjust</command>
      <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
      <step type="double">1</step>
    </binding>
  </high>
</axis>

左操作桿-按下

Cessna 172起落架不能收,跳過

右操作桿-按下

按下看正後方,放開跳回正前方。
<button n="9">
設heading為180度 (正後方)
  <binding>
    <command type="string">property-assign</command>
    <property type="string">/sim/current-view/goal-heading-offset-deg</property>
    <value type="double">180</value>
  </binding>
設pitch為0度
  <binding>
    <command type="string">property-assign</command>
    <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
    <value type="double">0</value>
  </binding>
放開
    <mod-up>
設heading為0度 (正前方)
      <binding>
 <command type="string">property-assign</command>
 <property type="string">/sim/current-view/goal-heading-offset-deg</property>
 <value type="double">0</value>
      </binding>
設pitch為0度
      <binding>
 <command type="string">property-assign</command>
 <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
 <value type="double">0</value>
      </binding>
    </mod-up>
</button>

觸控板

用BACK按鈕代替,利用property-cycle command使property於設定之value循環
<button n="6">
  <binding>
    <command type="string">property-cycle</command>
    <property type="string">/sim/current-view/view-number</property>
cockpit view
    <value>0</value>
model view
    <value>7</value>
chase without yaw view
    <value>5</value>
  </binding>
</button>

OPTIONS按鈕

用START按扭代替,直接pause command就好,easy。
<button n="7"> # START button
  <desc type="string">pause</desc>
  <repeatable type="string">false</repeatable>
  <binding>
    <command type="string">pause</command>
  </binding>
</button>

dead-band

前面說明省略dead-band部分。FlightGear之dead-band 結果以下程式碼說明 (可看joystick文件?)
README.Joystick.html,User Guid to FGInput - Joystick And Keyboard Binding For FlightGear
實際dead-band效果如下函式 (1 >= input >= -1, 1 > deadBand > 0 )

double doDeadBand(double input, double deadBand)
{
  if(input >= deadBand){
    return (input - deadBand) / (1 - deadBand);
  }

  if(input > -deadBand){
    return 0.0;
  }

  return -(abs(input) - deadBand) / (1 - deadBand);
}

完整檔案

<?xml version="1.0"?>

<!-- 
  add 

  <PropertyList>
    <js n="0" include="Input/Joysticks/GTA V like joystick setting for XBOX 360 Controller.xml"/>
  </PropertyList> 

  in FlightGear/data/joysticks.xml

  put this file in FlightGear/data/Input/Joysticks
-->

<PropertyList>
  <name type="string">GTA V like joystick setting for XBOX 360 Controller</name>

  <axis n="0">
    <name type="string">left stick left-right (right positive)</name>
    <desc type="string">adjust aileron</desc>
    <dead-band type="double">0.2</dead-band> <!-- dead-band tag must precede binding tag -->
    <!-- if (input < abs(dead-band)) output = 0 -->
    <!-- if (input > dead-band) output = (output - dead-band) / (1 - dead-band) -->
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/aileron</property>
    </binding>
  </axis>

  <axis n="1">
    <name type="string">left stick up-down (down positive)</name>
    <desc type="string">adjust elevator</desc> 
    <dead-band type="double">0.2</dead-band>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/elevator</property>
      <factor type="double">-1</factor> <!-- down positive, thus inverse -->
    </binding>
  </axis>

  <axis n="2">
    <name type="string">R2-L2 (L2 positive)</name>
    <desc type="string">start stop engine, adjust throttle, brake</desc>
    <dead-band type="double">0.1</dead-band>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        var axisValue = cmdarg().getNode("setting").getValue(); 
        <!-- the value of /input/joysticks/js/axis[2]/binding/setting -->

        <!-- engine start stop control -->
        if(getprop("/engines/engine/running") != 1 <!-- engine not running -->
             and -0.9 > axisValue){ <!-- R2 pushed near end -->
           setprop("/controls/switches/starter", 1);

           if(getprop("/controls/engines/engine/magnetos") != 3){ <!-- magnetos setting -->
             setprop("/controls/engines/engine/magnetos", 3);
           }
        }
        else{ <!-- engine running -->
          setprop("/controls/switches/starter", 0); 
        }

        <!-- axisValue 0~-1, R2, throttle -->
        if(0 >= axisValue){
           setprop("/controls/engines/engine/throttle", -axisValue);
        } 

        <!-- axisValue 0~1, L2, brake -->
        if(axisValue >= 0){ # L2 is positive (0 ~ 1)
          setprop("/controls/gear/brake-left", axisValue);
          setprop("/controls/gear/brake-right", axisValue);
        } 

        <!-- close engine -->
        if(axisValue > 0.8 and getprop("/controls/engines/engine/magnetos") != 0){
          setprop("/controls/engines/engine/magnetos", 0)
        }
      </script>
    </binding>
  </axis>

  <axis n="3">
    <name type="string">right stick up-down (down positive)</name>
    <desc type="string">adjust view direction pitch</desc>
    <low> <!-- up --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- down --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
<!-- add 

  <PropertyList>
    <js n="0" include="Input/Joysticks/GTA V like joystick setting for XBOX 360 Controller.xml"/>
  </PropertyList> 

  in FlightGear/data/joysticks.xml

  put this file in FlightGear/data/Input/Joysticks
-->

<?xml version="1.0"?>

<PropertyList>
  <name type="string">GTA V like joystick setting for XBOX 360 Controller</name>

  <axis n="0">
    <name type="string">left stick left-right (right positive)</name>
    <desc type="string">adjust aileron</desc>
    <dead-band type="double">0.2</dead-band> <!-- dead-band tag must precede binding tag -->
    <!-- if (input < abs(dead-band)) output = 0 -->
    <!-- if (input > dead-band) output = (output - dead-band) / (1 - dead-band) -->
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/aileron</property>
    </binding>
  </axis>

  <axis n="1">
    <name type="string">left stick up-down (down positive)</name>
    <desc type="string">adjust elevator</desc> 
    <dead-band type="double">0.2</dead-band>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/elevator</property>
      <factor type="double">-1</factor> <!-- down positive, thus inverse -->
    </binding>
  </axis>

  <axis n="2">
    <name type="string">R2-L2 (L2 positive)</name>
    <desc type="string">start stop engine, adjust throttle, brake</desc>
    <dead-band type="double">0.1</dead-band>
    <binding>
<!-- add 

  <PropertyList>
    <js n="0" include="Input/Joysticks/GTA V like joystick setting for XBOX 360 Controller.xml"/>
  </PropertyList> 

  in FlightGear/data/joysticks.xml

  put this file in FlightGear/data/Input/Joysticks
-->

<?xml version="1.0"?>

<PropertyList>
  <name type="string">GTA V like joystick setting for XBOX 360 Controller</name>

  <axis n="0">
    <name type="string">left stick left-right (right positive)</name>
    <desc type="string">adjust aileron</desc>
    <dead-band type="double">0.2</dead-band> <!-- dead-band tag must precede binding tag -->
    <!-- if (input < abs(dead-band)) output = 0 -->
    <!-- if (input > dead-band) output = (output - dead-band) / (1 - dead-band) -->
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/aileron</property>
    </binding>
  </axis>

  <axis n="1">
    <name type="string">left stick up-down (down positive)</name>
    <desc type="string">adjust elevator</desc> 
    <dead-band type="double">0.2</dead-band>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/elevator</property>
      <factor type="double">-1</factor> <!-- down positive, thus inverse -->
    </binding>
  </axis>

  <axis n="2">
    <name type="string">R2-L2 (L2 positive)</name>
    <desc type="string">start stop engine, adjust throttle, brake</desc>
    <dead-band type="double">0.1</dead-band>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        var axisValue = cmdarg().getNode("setting").getValue(); 
        <!-- the value of /input/joysticks/js/axis[2]/binding/setting -->

        <!-- engine start stop control -->
        if(getprop("/engines/engine/running") != 1 <!-- engine not running -->
             and -0.9 > axisValue){ <!-- R2 pushed near end -->
           setprop("/controls/switches/starter", 1);

           if(getprop("/controls/engines/engine/magnetos") != 3){ <!-- magnetos setting -->
             setprop("/controls/engines/engine/magnetos", 3);
           }
        }
        else{ <!-- engine running -->
          setprop("/controls/switches/starter", 0); 
        }

        <!-- axisValue 0~-1, R2, throttle -->
        if(0 >= axisValue){
           setprop("/controls/engines/engine/throttle", -axisValue);
        } 

        <!-- axisValue 0~1, L2, brake -->
        if(axisValue >= 0){ # L2 is positive (0 ~ 1)
          setprop("/controls/gear/brake-left", axisValue);
          setprop("/controls/gear/brake-right", axisValue);
        } 

        <!-- close engine -->
        if(axisValue > 0.8 and getprop("/controls/engines/engine/magnetos") != 0){
          setprop("/controls/engines/engine/magnetos", 0)
        }
      </script>
    </binding>
  </axis>

  <axis n="3">
    <name type="string">right stick up-down (down positive)</name>
    <desc type="string">adjust view direction pitch</desc>
    <low> <!-- up --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- down --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <axis n="4">
    <name type="string">right stick left-right (right positive)</name>
    <desc type="string">adjust view direction heading (yaw)</desc>
    <low> <!-- left --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- right --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <button n="0">
    <name type="string">A button</name>
    <desc type="string">decrease elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(-1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="1">
    <name type="string">B button</name>
    <desc type="string">increase aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="2">
    <name type="string">X button</name>
    <desc type="string">decrease aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(-1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)  
      </script>
    </binding>
  </button>

  <button n="3">
    <name type="string">Y button</name>
    <desc type="string">increase elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button> 
  
  <button n="4">
    <name type="string">L1 button</name>
    <desc type="string">Rudder Right (Turn Left)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">-1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release L1 button --> 
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="5">
    <name type="string">R2 button</name>
    <desc type="string">Rudder Left (Turn Right)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release R2 button -->
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="6">
    <name type="string">BACK button</name>
    <desc type="string">change view</desc>
    <binding>
      <command type="string">property-cycle</command>
      <property type="string">/sim/current-view/view-number</property>
      <value>0</value> <!-- cockpit view -->
      <value>7</value> <!-- model view -->
      <value>5</value> <!-- chase without yaw view -->
    </binding>
  </button>

  <button n="7">
    <name type="string">START button</name>
    <desc type="string">pause</desc>
    <binding>
      <command type="string">pause</command>
    </binding>
  </button>

  <button n="9">
    <name type="string">right stick button</name>
    <desc type = "string">look back, look forward when released</desc>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        setprop("/sim/current-view/goal-heading-offset-deg", 180);
        setprop("/sim/current-view/goal-pitch-offset-deg", 0);
      </script>
    </binding>
    <mod-up>
      <binding>
        <command type="string">nasal</command>
        <script type="string">
          setprop("/sim/current-view/goal-heading-offset-deg", 0);
          setprop("/sim/current-view/goal-pitch-offset-deg", 0);
        </script>
      </binding>
    </mod-up>
  </button>
</PropertyList>
      <command type="string">nasal</command>
      <script type="string">
        var axisValue = cmdarg().getNode("setting").getValue(); 
        <!-- the value of /input/joysticks/js/axis[2]/binding/setting -->

        <!-- engine start stop control -->
        if(getprop("/engines/engine/running") != 1 <!-- engine not running -->
             and -0.9 > axisValue){ <!-- R2 pushed near end -->
           setprop("/controls/switches/starter", 1);

           if(getprop("/controls/engines/engine/magnetos") != 3){ <!-- magnetos setting -->
             setprop("/controls/engines/engine/magnetos", 3);
           }
        }
        else{ <!-- engine running -->
          setprop("/controls/switches/starter", 0); 
        }

        <!-- axisValue 0~-1, R2, throttle -->
        if(0 >= axisValue){
           setprop("/controls/engines/engine/throttle", -axisValue);
        } 

        <!-- axisValue 0~1, L2, brake -->
        if(axisValue >= 0){ # L2 is positive (0 ~ 1)
          setprop("/controls/gear/brake-left", axisValue);
          setprop("/controls/gear/brake-right", axisValue);
        } 

        <!-- close engine -->
        if(axisValue > 0.8 and getprop("/controls/engines/engine/magnetos") != 0){
          setprop("/controls/engines/engine/magnetos", 0)
        }
      </script>
    </binding>
  </axis>

  <axis n="3">
    <name type="string">right stick up-down (down positive)</name>
    <desc type="string">adjust view direction pitch</desc>
    <low> <!-- up --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- down --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <axis n="4">
    <name type="string">right stick left-right (right positive)</name>
    <desc type="string">adjust view direction heading (yaw)</desc>
    <low> <!-- left --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- right --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>
<!-- add 

  <PropertyList>
    <js n="0" include="Input/Joysticks/GTA V like joystick setting for XBOX 360 Controller.xml"/>
  </PropertyList> 

  in FlightGear/data/joysticks.xml

  put this file in FlightGear/data/Input/Joysticks
-->

<?xml version="1.0"?>

<PropertyList>
  <name type="string">GTA V like joystick setting for XBOX 360 Controller</name>

  <axis n="0">
    <name type="string">left stick left-right (right positive)</name>
    <desc type="string">adjust aileron</desc>
    <dead-band type="double">0.2</dead-band> <!-- dead-band tag must precede binding tag -->
    <!-- if (input < abs(dead-band)) output = 0 -->
    <!-- if (input > dead-band) output = (output - dead-band) / (1 - dead-band) -->
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/aileron</property>
    </binding>
  </axis>

  <axis n="1">
    <name type="string">left stick up-down (down positive)</name>
    <desc type="string">adjust elevator</desc> 
    <dead-band type="double">0.2</dead-band>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/elevator</property>
      <factor type="double">-1</factor> <!-- down positive, thus inverse -->
    </binding>
  </axis>

  <axis n="2">
    <name type="string">R2-L2 (L2 positive)</name>
    <desc type="string">start stop engine, adjust throttle, brake</desc>
    <dead-band type="double">0.1</dead-band>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        var axisValue = cmdarg().getNode("setting").getValue(); 
        <!-- the value of /input/joysticks/js/axis[2]/binding/setting -->

        <!-- engine start stop control -->
        if(getprop("/engines/engine/running") != 1 <!-- engine not running -->
             and -0.9 > axisValue){ <!-- R2 pushed near end -->
           setprop("/controls/switches/starter", 1);

           if(getprop("/controls/engines/engine/magnetos") != 3){ <!-- magnetos setting -->
             setprop("/controls/engines/engine/magnetos", 3);
           }
        }
        else{ <!-- engine running -->
          setprop("/controls/switches/starter", 0); 
        }

        <!-- axisValue 0~-1, R2, throttle -->
        if(0 >= axisValue){
           setprop("/controls/engines/engine/throttle", -axisValue);
        } 

        <!-- axisValue 0~1, L2, brake -->
        if(axisValue >= 0){ # L2 is positive (0 ~ 1)
          setprop("/controls/gear/brake-left", axisValue);
          setprop("/controls/gear/brake-right", axisValue);
        } 

        <!-- close engine -->
        if(axisValue > 0.8 and getprop("/controls/engines/engine/magnetos") != 0){
          setprop("/controls/engines/engine/magnetos", 0)
        }
      </script>
    </binding>
  </axis>

  <axis n="3">
    <name type="string">right stick up-down (down positive)</name>
    <desc type="string">adjust view direction pitch</desc>
    <low> <!-- up --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- down --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <axis n="4">
    <name type="string">right stick left-right (right positive)</name>
    <desc type="string">adjust view direction heading (yaw)</desc>
    <low> <!-- left --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- right --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <button n="0">
    <name type="string">A button</name>
    <desc type="string">decrease elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(-1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="1">
    <name type="string">B button</name>
    <desc type="string">increase aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="2">
    <name type="string">X button</name>
    <desc type="string">decrease aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(-1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)  
      </script>
    </binding>
  </button>

  <button n="3">
    <name type="string">Y button</name>
    <desc type="string">increase elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button> 
  
  <button n="4">
    <name type="string">L1 button</name>
    <desc type="string">Rudder Right (Turn Left)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">-1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release L1 button --> 
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="5">
    <name type="string">R2 button</name>
    <desc type="string">Rudder Left (Turn Right)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release R2 button -->
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="6">
    <name type="string">BACK button</name>
    <desc type="string">change view</desc>
    <binding>
      <command type="string">property-cycle</command>
      <property type="string">/sim/current-view/view-number</property>
      <value>0</value> <!-- cockpit view -->
      <value>7</value> <!-- model view -->
      <value>5</value> <!-- chase without yaw view -->
    </binding>
  </button>

  <button n="7">
    <name type="string">START button</name>
    <desc type="string">pause</desc>
    <binding>
      <command type="string">pause</command>
    </binding>
  </button>

  <button n="9">
    <name type="string">right stick button</name>
    <desc type = "string">look back, look forward when released</desc>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        setprop("/sim/current-view/goal-heading-offset-deg", 180);
        setprop("/sim/current-view/goal-pitch-offset-deg", 0);
      </script>
    </binding>
    <mod-up>
      <binding>
        <command type="string">nasal</command>
        <script type="string">
          setprop("/sim/current-view/goal-heading-offset-deg", 0);
          setprop("/sim/current-view/goal-pitch-offset-deg", 0);
        </script>
      </binding>
    </mod-up>
  </button>
</PropertyList>

  <button n="0">
    <name type="string">A button</name>
    <desc type="string">decrease elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(-1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="1">
    <name type="string">B button</name>
    <desc type="string">increase aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)
      </script>
    </binding>
  </button>

  <button n="2">
    <name type="string">X button</name>
    <desc type="string">decrease aileron trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.aileronTrim(-1);
        gui.popupTip("aileron trim: " ~ getprop("/controls/flight/aileron-trim"), 1)  
      </script>
    </binding>
  </button>

  <button n="3">
    <name type="string">Y button</name>
    <desc type="string">increase elevator trim </desc>
    <repeatable type="string">true</repeatable>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        controls.elevatorTrim(1);
        gui.popupTip("elevator trim: " ~ getprop("/controls/flight/elevator-trim"), 1)
      </script>
    </binding>
  </button> 
  
  <button n="4">
    <name type="string">L1 button</name>
    <desc type="string">Rudder Right (Turn Left)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">-1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release L1 button --> 
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="5">
    <name type="string">R2 button</name>
    <desc type="string">Rudder Left (Turn Right)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release R2 button -->
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="6">
    <name type="string">BACK button</name>
    <desc type="string">change view</desc>
    <binding>
      <command type="string">property-cycle</command>
      <property type="string">/sim/current-view/view-number</property>
      <value>0</value> <!-- cockpit view -->
      <value>7</value> <!-- model view -->
      <value>5</value> <!-- chase without yaw view -->
    </binding>
  </button>

  <button n="7">
    <name type="string">START button</name>
    <desc type="string">pause</desc>
    <binding>
      <command type="string">pause</command>
    </binding>
  </button>

  <button n="9">
    <name type="string">right stick button</name>
    <desc type = "string">look back, look forward when released</desc>
    <binding>
      <command type="string">nasal</command>
      <script type="string">
        setprop("/sim/current-view/goal-heading-offset-deg", 180);
        setprop("/sim/current-view/goal-pitch-offset-deg", 0);
      </script>
    </binding>
    <mod-up>
      <binding>
        <command type="string">nasal</command>
        <script type="string">
          setprop("/sim/current-view/goal-heading-offset-deg", 0);
          setprop("/sim/current-view/goal-pitch-offset-deg", 0);
        </script>
      </binding>
    </mod-up>
  </button>
</PropertyList>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <axis n="4">
    <name type="string">right stick left-right (right positive)</name>
    <desc type="string">adjust view direction heading (yaw)</desc>
    <low> <!-- left --> <!-- axis value < -0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">-1</step>
      </binding>
    </low>
    <high> <!-- right --> <!-- axis value > 0.9 -->
      <repeatable type="string">true</repeatable>
      <binding>
        <command type="string">property-adjust</command>
        <property type="string">/sim/current-view/goal-heading-offset-deg</property>
        <step type="double">1</step>
      </binding>
    </high>
  </axis>

  <button n="4">
    <name type="string">L1 button</name>
    <desc type="string">Rudder Right (Turn Left)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">-1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release L1 button --> 
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="5">
    <name type="string">R2 button</name>
    <desc type="string">Rudder Left (Turn Right)</desc>
    <binding>
      <command type="string">property-interpolate</command>
      <property type="string">/controls/flight/rudder</property>
      <value type="double">1</value>
      <rate type="double">0.5</rate>
    </binding>
    <mod-up> <!-- release R2 button -->
      <binding>
        <command type="string">property-interpolate</command>
        <property type="string">/controls/flight/rudder</property>
        <value type="double">0</value>
        <rate type="double">0.5</rate>
      </binding>
    </mod-up>
  </button>

  <button n="6">
    <name type="string">BACK button</name>
    <desc type="string">change view</desc>
    <binding>
      <command type="string">property-cycle</command>
      <property type="string">/sim/current-view/view-number</property>
      <value>0</value> <!-- cockpit view -->
      <value>7</value> <!-- model view -->
      <value>5</value> <!-- chase without yaw view -->
    </binding>
  </button>

  <button n="7">
    <name type="string">START button</name>
    <desc type="string">pause</desc>
    <binding>
      <command type="string">pause</command>
    </binding>
  </button>

  <button n="9">
    <name type="string">right stick button</name>
    <desc type = "string">look back, look forward when released</desc>
    <binding>
      <command type="string">property-assign</command>
      <property type="string">/sim/current-view/goal-heading-offset-deg</property>
      <value type="double">180</value>
    </binding>
    <binding>
      <command type="string">property-assign</command>
      <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
      <value type="double">0</value>
    </binding>
    <mod-up>
      <binding>
 <command type="string">property-assign</command>
 <property type="string">/sim/current-view/goal-heading-offset-deg</property>
 <value type="double">0</value>
      </binding>
      <binding>
 <command type="string">property-assign</command>
 <property type="string">/sim/current-view/goal-pitch-offset-deg</property>
 <value type="double">0</value>
      </binding>
    </mod-up>
  </button>
</PropertyList>

Qwt安裝 - Windows

操作環境

  • 作業系統: Windows 7
  • 現有Qt版本: 5.2.0 MSVC2012 OpenGL 64bit (使用官方"qt-windows-opensource-5.2.0-msvc2012_opengl-x86_64-offline.exe"Package安裝)
  • 安裝之Qwt版本: Qwt-6.1.2

簡易安裝流程

  1. source forge下載qwt壓縮檔(Windows請下載.zip)
  2. 解壓縮檔案
  3. 設定好PATH變數,方可於命令視窗使用qmake
  4. (命令視窗) qmake qwt.pro
  5. (VS2012 x64 Native Tools Command Prompt) nmake
  6. (VS2012 x64 Native Tools Command Prompt) nmake install

安裝完畢,收工,easy解。

說明

其實Qwt的安裝說明講的很清楚,但自己當初也是不會弄。

Qwt doesn't distribute binary packages

When no binary packages are available (f.e. on Windows) Qwt needs to be compiled and installed on the target system.

簡單講,沒有可以double click後就完成安裝的安裝檔。必須要自己compile source code,install。以下照前述簡易安裝流程說明各步驟。

Path設定

直接在命令視窗列(可以在資料夾內按住shift,點擊右鍵,點選此處開啟命令視窗)打qmake會出現

'qmake' 不是內部或外部命令,可執行的程式或批次檔。

這代表Path變數未設定。Windows 7之qmake Path設定方法如下:

  1. 點選"開始",在"電腦"圖示點擊右鍵,點選"內容",跳出"控制台-系統安全性-系統"視窗
  2. 點選左上方之"進階系統設定",跳出"系統內容"視窗,當下分頁為"進階"
  3. 點選下方的"環境變數",跳出"環境變數"視窗
  4. 於下方"系統變數"框,選"Path"變數反白之,點選下方"編輯",跳出"編輯系統變數"視窗
  5. 編輯"變數值",在尾端加入Qt bin資料夾路徑 (我自己的情況,加入";C:\Qt\Qt5.2.0\5.2.0\msvc2012_64_opengl\bin")
  6. 在"編輯系統變數"視窗點選"確定"
  7. 在"環境變數"視窗點選"確定"
  8. 在"系統內容"視窗點選"確定"

qmake

qmake簡單講就是從.pro檔產生Makefile檔。有了Makefile檔才可進行後續compile, linking等動作。

完成qmake之Path設定後,於解壓縮之qwt-6.1.2資料夾開啟命令視窗,輸入

qmake qwt.pro

命令視窗應無產生任何提示,但資料夾內會生成Makefile檔,已可compile

nmake

nmake是MSVC的compile指令(說明連結),一般的命令視窗輸入nmake會跑出

'nmake'不是內部或外部命令、可執行的程式或批次檔。

因此,要使用Visual Studio的Command Prompt。因為我安裝的Qt版本為64bit,因此compile Qwt也要使用64bit的"VS2012 x64 Native Tools Command Promt",開啟方式為

開始→所有程式→Microsoft Visual Studio 2012→Visual Studio Tools→VS2012 x64 Native Tools Command Promt

開啟Command Prompt後,於方才qmake的工作路徑,輸入

nmake

順利的話就開始漫長的compile時程,請耐心等候。(也沒多久,大概1x min)

nmake install

一樣在"VS2012 x64 Native Tools Command Promt",輸入

nmake install

nmake install應該只是把compile好的檔案複製到路徑(C:/qwt-6.1.2/),複製完畢就大功告成。

補充說明

個人覺得直接用command line比開Qt Creator方便多了,故此分享。

錯誤補充說明

我一開始用到"適用於VS2012的開發人員命令提示字元"32bit的,產生error

Qt5OpenGL.lib(Qt5OpenGL.dll): fatal error LNK1112: 模組電腦類型'x64'與目標電腦類型'X86'衝突
NMAKE : fatal error U1077: 'echo' : 傳回碼'0x458'
...(省略)

google後才知道要用"VS2012 x64 Native Tools Command"

此外,若Qwt路徑有中文,似乎也無法順利compile。

2015年3月13日 星期五

Git學習心得、使用方法整理

推薦學習資源

  1. 官方網站:總得到官網下載安裝後才能練習、使用。
  2. try git:互動網頁做中學,快速熟悉基本指令。
  3. 連猴子都能懂的Git入門指南:目前看過最好上手的中文資源。
  4. Introduction to Git with Scott Chacon of GitHub

一定要實際操作,學習速度才會快。下的指令有問題通常Git Bash會提示該怎麼做。

同義名詞

同義(或近似)名詞弄清楚比較不會在看不同資源的時候混淆。

working tree = working directory = workspace
index = staging area
repository = .git repository = .git directory

不commit的檔案狀態暫存法(非stash)

可能是個人習慣不好,偶而會coding到一半不保存資料怕危險,commit又嫌不夠具代表性。一個解決方法是先commit,事後再git commit --amend。但因檔案能在working tree、index與last commit有三種不同版本,其實也可以先add,保存一版,在working tree持續工作,待整理到一個段落再add、commit。

假如很遺憾地將程式改爆炸了,取回檔案index版本(已add之版本)之方法為

$ git checkout -- myFile

假如更悲慘地在add前就爆炸了,取回檔案last commit版本之方法為

$ git checkout HEAD myFile

真的掛光光就git reset HEAD --hard吧,阿彌陀佛。

已push,但想reset回某次commit→revert

問題已發生,就先不檢討為什麼不在commit前做好測試了。reset掉已push出去的commit會造成下次push的困擾(簡單講就是我不會處理),這種情況下git revert可派上用場。

回到上上次commit

$ git revert HEAD

此時working tree與git reset HEAD^ --hard相同

git revert會自動commit,預設下迫使你進入可愛的Vim編輯環境。若想手動commit,可以git revert HEAD -n。如此working tree與index已變動,待自行commit。

若很不幸地需要回到上n+1次commit狀態

$ git revert HEAD~n..HEAD -n

如此會消除上n+1次commit後所做變動。若不-n,每revert一次commit就會建立一個commit。

以usb做為遠端repo,於兩台PC工作

大多網路資料都在網路上建立remote repository,但如果只是個人要在兩台PC切換工作,其實也可用usb當remote repository,以下簡單介紹之。

首先在PC1插入usb,假設usb路徑為/F,工作directory為/D/myFolder

$ cd /F
$ git clone /D/myFolder --bare

這樣就clone好bare的git repository到抽取式硬碟了。

移至PC2,假設usb路徑仍為/F,在想拷貝檔案資料夾的位置右鍵開啟Git Bash

$ git clone /F/myFolder.git
$ cd myFolder
$ git remote rename origin usb

進行一些修改,完成commit,將資料push回usb

$ git push usb master

回到原電腦,插入usb,更新資料

$ git remote add usb /F/myFolder.git
$ git pull usb

git pull usb也可拆解為git fetch usbgit merge remotes/usb/master,個人比較習慣後者。

編寫程式、修改、commit後再push資料

$ git push usb master

以上,loop pullpush