I implemented it by subclassing QwtScaleDraw and modifying drawLabel and drawBackbone. Works quite well. The inspiration came from qtiplot, which uses the same approach. There is some gap between the non-major ticks and the grid, but I guess thats just not avoidable due to Scale and Plot being different widgets.

Here is the relevant code:
Qt Code:
  1. void ScaleDraw::drawTick(QPainter *painter, double value, int len) const {
  2. if (m_tickDirection == outwards)
  3. return QwtScaleDraw::drawTick(painter, value, len);
  4.  
  5. if ( len <= 0 )
  6. return;
  7.  
  8. int pw2 = qMin((int)painter->pen().width(), len) / 2;
  9.  
  10. QwtScaleMap scaleMap = map();
  11. const QwtMetricsMap metricsMap = QwtPainter::metricsMap();
  12. QPoint pos = this->pos();
  13.  
  14. if ( !metricsMap.isIdentity() )
  15. {
  16. /*
  17.   The perfect position of the ticks is important.
  18.   To avoid rounding errors we have to use
  19.   device coordinates.
  20.   */
  21. QwtPainter::resetMetricsMap();
  22.  
  23. pos = metricsMap.layoutToDevice(pos);
  24.  
  25. if ( orientation() == Qt::Vertical )
  26. {
  27. scaleMap.setPaintInterval(
  28. metricsMap.layoutToDeviceY((int)scaleMap.p1()),
  29. metricsMap.layoutToDeviceY((int)scaleMap.p2())
  30. );
  31. len = metricsMap.layoutToDeviceX(len);
  32. }
  33. else
  34. {
  35. scaleMap.setPaintInterval(
  36. metricsMap.layoutToDeviceX((int)scaleMap.p1()),
  37. metricsMap.layoutToDeviceX((int)scaleMap.p2())
  38. );
  39. len = metricsMap.layoutToDeviceY(len);
  40. }
  41. }
  42.  
  43. const int tval = scaleMap.transform(value);
  44.  
  45. switch(alignment())
  46. {
  47. case LeftScale:
  48. {
  49. QwtPainter::drawLine(painter, pos.x() - pw2 - majTickLength(), tval,
  50. pos.x() + len - majTickLength(), tval);
  51.  
  52. break;
  53. }
  54.  
  55. case RightScale:
  56. {
  57. QwtPainter::drawLine(painter, pos.x() + pw2 + majTickLength(), tval,
  58. pos.x() - len + majTickLength(), tval);
  59. break;
  60. }
  61.  
  62. case BottomScale:
  63. {
  64. QwtPainter::drawLine(painter, tval, pos.y() + pw2 + majTickLength(),
  65. tval, pos.y() - len + majTickLength());
  66. break;
  67. }
  68.  
  69. case TopScale:
  70. {
  71. QwtPainter::drawLine(painter, tval, pos.y() - pw2 - majTickLength(),
  72. tval, pos.y() - majTickLength() + len);
  73. break;
  74. }
  75. }
  76. QwtPainter::setMetricsMap(metricsMap); // restore metrics map
  77. }
  78.  
  79. /*!
  80.   Draws the baseline of the scale
  81.   \param painter Painter
  82.  
  83.   \sa drawTick(), drawLabel()
  84. */
  85. void ScaleDraw::drawBackbone(QPainter *painter) const
  86. {
  87. if (m_tickDirection == outwards)
  88. return QwtScaleDraw::drawBackbone(painter);
  89.  
  90. const int bw2 = painter->pen().width() / 2;
  91.  
  92. const QPoint &pos = this->pos();
  93. const int len = length() - 1;
  94.  
  95. switch(alignment())
  96. {
  97. case LeftScale: {
  98. double leftShift = majTickLength();
  99. QwtPainter::drawLine(painter, pos.x() - bw2 - leftShift,
  100. pos.y(), pos.x() - bw2 - leftShift, pos.y() + len );
  101. break;
  102. }
  103. case RightScale: {
  104. double rightShift = majTickLength();
  105. QwtPainter::drawLine(painter, pos.x() + bw2 + rightShift,
  106. pos.y(), pos.x() + bw2 + rightShift, pos.y() + len);
  107. break;
  108. }
  109. case TopScale: {
  110. double upShift = majTickLength();
  111. QwtPainter::drawLine(painter, pos.x(), pos.y() - bw2 - upShift,
  112. pos.x() + len, pos.y() - bw2 - upShift);
  113. break;
  114. }
  115. case BottomScale: {
  116. double downShift = majTickLength();
  117. QwtPainter::drawLine(painter, pos.x(), pos.y() + bw2 + downShift,
  118. pos.x() + len, pos.y() + bw2 + downShift);
  119. break;
  120. }
  121. }
  122. }
To copy to clipboard, switch view to plain text mode 

This is still based on Qwt 5.x, I'll do the upgrade soon.

So thanks for you help!