FanItem.qml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * Copyright (C) 2015 Malte Veerman <malte.veerman@gmail.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. *
  18. */
  19. import QtQuick 2.6
  20. import QtQuick.Controls 2.1
  21. import QtQuick.Layouts 1.2
  22. import org.kde.kirigami 2.3 as Kirigami
  23. import Fancontrol.Qml 1.0 as Fancontrol
  24. import "math.js" as MoreMath
  25. import "units.js" as Units
  26. import "colors.js" as Colors
  27. Item {
  28. id: root
  29. property QtObject fan
  30. property int margin: Kirigami.Units.smallSpacing
  31. property bool showControls: true
  32. property bool editable: true
  33. readonly property QtObject systemdCom: Fancontrol.Base.hasSystemdCommunicator ? Fancontrol.Base.systemdCom : null
  34. readonly property QtObject tempModel: Fancontrol.Base.tempModel
  35. readonly property real minTemp: Fancontrol.Base.minTemp
  36. readonly property real maxTemp: Fancontrol.Base.maxTemp
  37. readonly property string unit: Fancontrol.Base.unit
  38. readonly property real convertedMinTemp: Units.fromCelsius(minTemp, unit)
  39. readonly property real convertedMaxTemp: Units.fromCelsius(maxTemp, unit)
  40. onConvertedMinTempChanged: {
  41. meshCanvas.requestPaint();
  42. curveCanvas.requestPaint();
  43. }
  44. onConvertedMaxTempChanged: {
  45. meshCanvas.requestPaint();
  46. curveCanvas.requestPaint();
  47. }
  48. onFanChanged: curveCanvas.requestPaint()
  49. Item {
  50. id: graph
  51. property int fontSize: MoreMath.bound(8, height / 20 + 1, 16)
  52. property int verticalScalaCount: height > Kirigami.Units.gridUnit * 30 ? 11 : 6
  53. property var horIntervals: MoreMath.intervals(root.convertedMinTemp, root.convertedMaxTemp, 10)
  54. anchors {
  55. left: parent.left
  56. right: parent.right
  57. top: parent.top
  58. bottom: root.showControls ? settingsArea.top : parent.bottom
  59. bottomMargin: root.margin
  60. }
  61. visible: graphBackground.height > 0 && graphBackground.width > 0
  62. Item {
  63. id: verticalScala
  64. anchors {
  65. top: graphBackground.top
  66. bottom: graphBackground.bottom
  67. left: parent.left
  68. }
  69. width: MoreMath.maxWidth(children) + graph.fontSize / 3
  70. Repeater {
  71. id: verticalRepeater
  72. model: graph.verticalScalaCount
  73. Text {
  74. x: verticalScala.width - implicitWidth - graph.fontSize / 3
  75. y: graphBackground.height - graphBackground.height / (graph.verticalScalaCount - 1) * index - graph.fontSize * 2 / 3
  76. horizontalAlignment: Text.AlignRight
  77. color: Kirigami.Theme.textColor
  78. text: Number(index * (100 / (graph.verticalScalaCount - 1))).toLocaleString(Qt.locale(), 'f', 0) + Qt.locale().percent
  79. font.pixelSize: graph.fontSize
  80. }
  81. }
  82. }
  83. Item {
  84. id: horizontalScala
  85. anchors {
  86. right: graphBackground.right
  87. bottom: parent.bottom
  88. left: graphBackground.left
  89. }
  90. height: graph.fontSize * 2
  91. Repeater {
  92. model: graph.horIntervals.length;
  93. Text {
  94. x: graphBackground.scaleX(Units.toCelsius(graph.horIntervals[index], unit)) - width/2
  95. y: horizontalScala.height / 2 - implicitHeight / 2
  96. color: Kirigami.Theme.textColor
  97. text: Number(graph.horIntervals[index]).toLocaleString() + i18n(unit)
  98. font.pixelSize: graph.fontSize
  99. }
  100. }
  101. }
  102. Rectangle {
  103. id: graphBackground
  104. Kirigami.Theme.colorSet: Kirigami.Theme.View
  105. color: Kirigami.Theme.backgroundColor
  106. border.color: Kirigami.Theme.textColor
  107. border.width: 2
  108. anchors {
  109. top: parent.top
  110. left: verticalScala.right
  111. bottom: horizontalScala.top
  112. right: parent.right
  113. topMargin: parent.fontSize
  114. rightMargin: parent.fontSize * 2
  115. }
  116. function scaleX(temp) {
  117. return (temp - minTemp) * width / (maxTemp - minTemp);
  118. }
  119. function scaleY(pwm) {
  120. return height - pwm * height / 255;
  121. }
  122. function scaleTemp(x) {
  123. return x / width * (maxTemp - minTemp) + minTemp;
  124. }
  125. function scalePwm(y) {
  126. return 255 - y / height * 255;
  127. }
  128. Canvas {
  129. id: curveCanvas
  130. anchors.fill: parent
  131. anchors.margins: parent.border.width
  132. renderStrategy: Canvas.Threaded
  133. renderTarget: Canvas.FramebufferObject
  134. onPaint: {
  135. var c = curveCanvas.getContext("2d");
  136. c.clearRect(0, 0, width, height);
  137. if (!fan || !fan.hasTemp) {
  138. return;
  139. }
  140. var gradient = c.createLinearGradient(0, 0, width, 0);
  141. gradient.addColorStop(0, "rgb(0, 0, 255)");
  142. gradient.addColorStop(1, "rgb(255, 0, 0)");
  143. c.fillStyle = gradient;
  144. c.lineWidth = graph.fontSize / 3;
  145. c.strokeStyle = gradient;
  146. c.lineJoin = "round";
  147. c.beginPath();
  148. if (fan.minPwm == 0) {
  149. c.moveTo(stopPoint.centerX, height);
  150. } else {
  151. c.moveTo(0, stopPoint.centerY);
  152. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  153. }
  154. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  155. c.lineTo(maxPoint.centerX, maxPoint.centerY);
  156. c.lineTo(width, maxPoint.centerY);
  157. c.stroke();
  158. c.lineTo(width, height);
  159. if (fan.minPwm == 0) {
  160. c.lineTo(stopPoint.centerX, height);
  161. } else {
  162. c.lineTo(0, height);
  163. }
  164. c.fill();
  165. //blend graphBackground
  166. gradient = c.createLinearGradient(0, 0, 0, height);
  167. gradient.addColorStop(0, Colors.setAlpha(graphBackground.color, 0.5));
  168. gradient.addColorStop(1, Colors.setAlpha(graphBackground.color, 0.9));
  169. c.fillStyle = gradient;
  170. c.fill();
  171. }
  172. Connections {
  173. target: fan
  174. onTempChanged: curveCanvas.requestPaint()
  175. onMinPwmChanged: curveCanvas.markDirty(Qt.rect(0, 0, stopPoint.x, stopPoint.y))
  176. }
  177. }
  178. Canvas {
  179. id: meshCanvas
  180. anchors.fill: parent
  181. anchors.margins: parent.border.width
  182. renderStrategy: Canvas.Threaded
  183. renderTarget: Canvas.FramebufferObject
  184. onPaint: {
  185. var c = meshCanvas.getContext("2d");
  186. c.clearRect(0, 0, width, height);
  187. //draw mesh
  188. c.beginPath();
  189. c.strokeStyle = Colors.setAlpha(Kirigami.Theme.textColor, 0.3);
  190. //horizontal lines
  191. for (var i=0; i<=100; i+=100/(graph.verticalScalaCount-1)) {
  192. var y = graphBackground.scaleY(i*2.55);
  193. if (i != 0 && i != 100) {
  194. for (var j=0; j<=width; j+=15) {
  195. c.moveTo(j, y);
  196. c.lineTo(Math.min(j+5, width), y);
  197. }
  198. }
  199. }
  200. //vertical lines
  201. if (graph.horIntervals.length > 1) {
  202. for (var i=1; i<graph.horIntervals.length; i++) {
  203. var x = graphBackground.scaleX(Units.toCelsius(graph.horIntervals[i], unit));
  204. for (var j=0; j<=height; j+=20) {
  205. c.moveTo(x, j);
  206. c.lineTo(x, Math.min(j+5, height));
  207. }
  208. }
  209. }
  210. c.stroke();
  211. }
  212. }
  213. StatusPoint {
  214. id: currentPwm
  215. size: graph.fontSize
  216. visible: graphBackground.contains(center) && !!fan && fan.hasTemp
  217. fan: root.fan
  218. }
  219. PwmPoint {
  220. id: stopPoint
  221. color: !!fan ? fan.hasTemp ? "blue" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(0, 0, 1, 0.5)) : "transparent"
  222. size: graph.fontSize
  223. visible: !!fan ? fan.hasTemp : false
  224. draggable: root.editable
  225. maximumX: Math.min(graphBackground.scaleX(graphBackground.scaleTemp(maxPoint.x)-1), maxPoint.x-1)
  226. minimumY: Math.max(graphBackground.scaleY(graphBackground.scalePwm(maxPoint.y)-1), maxPoint.y+1)
  227. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.minTemp, root.maxTemp)) - width/2 : -width/2
  228. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.minStop) - height/2 : -height/2
  229. onDragFinished: {
  230. fan.minStop = Math.round(graphBackground.scalePwm(centerY));
  231. fan.minTemp = Math.round(graphBackground.scaleTemp(centerX));
  232. if (fan.minPwm !== 0) fan.minPwm = fan.minStop;
  233. }
  234. onPositionChanged: {
  235. var left = fan.minPwm === 0 ? x : 0;
  236. var width = maxPoint.x - left;
  237. var height = y - maxPoint.y;
  238. curveCanvas.markDirty(Qt.rect(left, maxPoint.y, width, height));
  239. }
  240. }
  241. PwmPoint {
  242. id: maxPoint
  243. color: !!fan ? fan.hasTemp ? "red" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(1, 0, 0, 0.5)) : "transparent"
  244. size: graph.fontSize
  245. visible: !!fan ? fan.hasTemp : false
  246. draggable: root.editable
  247. minimumX: Math.max(graphBackground.scaleX(graphBackground.scaleTemp(stopPoint.x)+1), stopPoint.x+1)
  248. maximumY: Math.min(graphBackground.scaleY(graphBackground.scalePwm(stopPoint.y)+1), stopPoint.y-1)
  249. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.maxTemp, root.maxTemp)) - width/2 : graphBackground.width - width/2
  250. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.maxPwm) - height/2 : -height/2
  251. onDragFinished: {
  252. fan.maxPwm = Math.round(graphBackground.scalePwm(centerY));
  253. fan.maxTemp = Math.round(graphBackground.scaleTemp(centerX));
  254. }
  255. onPositionChanged: {
  256. var width = x - stopPoint.x;
  257. var height = stopPoint.y - y;
  258. curveCanvas.markDirty(Qt.rect(stopPoint.x, y, width, height));
  259. }
  260. }
  261. }
  262. }
  263. FanControls {
  264. id: settingsArea
  265. fan: root.fan
  266. padding: root.margin
  267. anchors {
  268. left: parent.left
  269. leftMargin: padding
  270. right: parent.right
  271. rightMargin: padding
  272. bottom: parent.bottom
  273. bottomMargin: padding
  274. }
  275. visible: root.showControls && root.height >= height + 2*margin
  276. }
  277. }