ExecutionPage.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import Qt.labs.folderlistmodel 2.15
  5. import "../components"
  6. Page {
  7. id: page
  8. property var backend: null
  9. property var stackView: null
  10. property string patternName: ""
  11. property string patternPreview: "" // Backend provides this via executionStarted signal
  12. // Debug backend connection
  13. onBackendChanged: {
  14. console.log("ExecutionPage: backend changed to", backend)
  15. if (backend) {
  16. console.log("ExecutionPage: backend.serialConnected =", backend.serialConnected)
  17. console.log("ExecutionPage: backend.isConnected =", backend.isConnected)
  18. }
  19. }
  20. Component.onCompleted: {
  21. console.log("ExecutionPage: Component completed, backend =", backend)
  22. if (backend) {
  23. console.log("ExecutionPage: initial serialConnected =", backend.serialConnected)
  24. }
  25. }
  26. // Direct connection to backend signals
  27. Connections {
  28. target: backend
  29. function onSerialConnectionChanged(connected) {
  30. console.log("ExecutionPage: received serialConnectionChanged signal:", connected)
  31. }
  32. function onConnectionChanged() {
  33. console.log("ExecutionPage: received connectionChanged signal")
  34. if (backend) {
  35. console.log("ExecutionPage: after connectionChanged, serialConnected =", backend.serialConnected)
  36. }
  37. }
  38. function onExecutionStarted(fileName, preview) {
  39. console.log("🎯 ExecutionPage: executionStarted signal received!")
  40. console.log("🎯 Pattern:", fileName)
  41. console.log("🎯 Preview path:", preview)
  42. // Update preview directly from backend signal
  43. patternName = fileName
  44. patternPreview = preview
  45. }
  46. }
  47. Rectangle {
  48. anchors.fill: parent
  49. color: "#f5f5f5"
  50. }
  51. ColumnLayout {
  52. anchors.fill: parent
  53. spacing: 0
  54. // Header (consistent with other pages)
  55. Rectangle {
  56. Layout.fillWidth: true
  57. Layout.preferredHeight: 50
  58. color: "white"
  59. // Bottom border
  60. Rectangle {
  61. anchors.bottom: parent.bottom
  62. width: parent.width
  63. height: 1
  64. color: "#e5e7eb"
  65. }
  66. RowLayout {
  67. anchors.fill: parent
  68. anchors.leftMargin: 15
  69. anchors.rightMargin: 10
  70. ConnectionStatus {
  71. backend: page.backend
  72. Layout.rightMargin: 8
  73. }
  74. Label {
  75. text: "Pattern Execution"
  76. font.pixelSize: 18
  77. font.bold: true
  78. color: "#333"
  79. }
  80. Item {
  81. Layout.fillWidth: true
  82. }
  83. }
  84. }
  85. // Content - Side by side layout
  86. Item {
  87. Layout.fillWidth: true
  88. Layout.fillHeight: true
  89. Row {
  90. anchors.fill: parent
  91. spacing: 0
  92. // Left side - Pattern Preview (60% of width)
  93. Rectangle {
  94. width: parent.width * 0.6
  95. height: parent.height
  96. color: "#ffffff"
  97. Image {
  98. anchors.fill: parent
  99. anchors.margins: 10
  100. source: {
  101. var finalSource = ""
  102. // Trust the backend's preview path - it already has recursive search
  103. if (patternPreview) {
  104. // Backend returns absolute path, just add file:// prefix
  105. finalSource = "file://" + patternPreview
  106. console.log("🖼️ Using backend patternPreview:", finalSource)
  107. } else {
  108. console.log("🖼️ No preview from backend")
  109. }
  110. return finalSource
  111. }
  112. fillMode: Image.PreserveAspectFit
  113. onStatusChanged: {
  114. console.log("📷 Image status:", status, "for source:", source)
  115. if (status === Image.Error) {
  116. console.log("❌ Image failed to load:", source)
  117. } else if (status === Image.Ready) {
  118. console.log("✅ Image loaded successfully:", source)
  119. } else if (status === Image.Loading) {
  120. console.log("🔄 Image loading:", source)
  121. }
  122. }
  123. onSourceChanged: {
  124. console.log("🔄 Image source changed to:", source)
  125. }
  126. Rectangle {
  127. anchors.fill: parent
  128. color: "#f0f0f0"
  129. visible: parent.status === Image.Error || parent.source == ""
  130. Column {
  131. anchors.centerIn: parent
  132. spacing: 10
  133. Text {
  134. text: "⚙"
  135. font.pixelSize: 48
  136. color: "#ccc"
  137. anchors.horizontalCenter: parent.horizontalCenter
  138. }
  139. Text {
  140. text: "Pattern Preview"
  141. color: "#999"
  142. font.pixelSize: 14
  143. anchors.horizontalCenter: parent.horizontalCenter
  144. }
  145. }
  146. }
  147. }
  148. }
  149. // Divider
  150. Rectangle {
  151. width: 1
  152. height: parent.height
  153. color: "#e5e7eb"
  154. }
  155. // Right side - Controls (40% of width)
  156. Rectangle {
  157. width: parent.width * 0.4 - 1
  158. height: parent.height
  159. color: "white"
  160. ScrollView {
  161. anchors.fill: parent
  162. anchors.margins: 10
  163. clip: true
  164. contentWidth: availableWidth
  165. Column {
  166. width: parent.width
  167. spacing: 8
  168. // Pattern Name
  169. Rectangle {
  170. width: parent.width
  171. height: 50
  172. radius: 8
  173. color: "#f8f9fa"
  174. border.color: "#e5e7eb"
  175. border.width: 1
  176. Column {
  177. anchors.centerIn: parent
  178. spacing: 4
  179. Label {
  180. text: "Current Pattern"
  181. font.pixelSize: 10
  182. color: "#666"
  183. anchors.horizontalCenter: parent.horizontalCenter
  184. }
  185. Label {
  186. text: {
  187. // Use WebSocket current pattern first, then fallback to passed parameter
  188. var displayName = ""
  189. if (backend && backend.currentFile) displayName = backend.currentFile
  190. else if (patternName) displayName = patternName
  191. else return "No pattern running"
  192. // Clean up the name for display
  193. var parts = displayName.split('/')
  194. displayName = parts[parts.length - 1]
  195. displayName = displayName.replace('.thr', '')
  196. return displayName
  197. }
  198. font.pixelSize: 12
  199. font.bold: true
  200. color: "#333"
  201. anchors.horizontalCenter: parent.horizontalCenter
  202. width: parent.parent.width - 20
  203. elide: Text.ElideMiddle
  204. horizontalAlignment: Text.AlignHCenter
  205. }
  206. }
  207. }
  208. // Progress
  209. Rectangle {
  210. width: parent.width
  211. height: 70
  212. radius: 8
  213. color: "#f8f9fa"
  214. border.color: "#e5e7eb"
  215. border.width: 1
  216. Column {
  217. anchors.fill: parent
  218. anchors.margins: 10
  219. spacing: 8
  220. Label {
  221. text: "Progress"
  222. font.pixelSize: 12
  223. font.bold: true
  224. color: "#333"
  225. }
  226. ProgressBar {
  227. width: parent.width
  228. height: 8
  229. value: backend ? backend.progress / 100 : 0
  230. }
  231. Label {
  232. text: backend ? Math.round(backend.progress) + "%" : "0%"
  233. anchors.horizontalCenter: parent.horizontalCenter
  234. font.pixelSize: 14
  235. font.bold: true
  236. color: "#333"
  237. }
  238. }
  239. }
  240. // Control Buttons
  241. Rectangle {
  242. width: parent.width
  243. height: 90
  244. radius: 8
  245. color: "#f8f9fa"
  246. border.color: "#e5e7eb"
  247. border.width: 1
  248. Column {
  249. anchors.fill: parent
  250. anchors.margins: 10
  251. spacing: 10
  252. Label {
  253. text: "Controls"
  254. font.pixelSize: 12
  255. font.bold: true
  256. color: "#333"
  257. }
  258. // Control buttons row
  259. Row {
  260. width: parent.width
  261. height: 35
  262. spacing: 8
  263. // Pause/Resume button
  264. Rectangle {
  265. width: (parent.width - 16) / 3 // Divide width evenly with spacing
  266. height: parent.height
  267. radius: 6
  268. color: pauseMouseArea.pressed ? "#1e40af" : (backend && backend.currentFile !== "" ? "#2563eb" : "#9ca3af")
  269. Text {
  270. anchors.centerIn: parent
  271. text: (backend && backend.isRunning) ? "||" : "▶"
  272. color: "white"
  273. font.pixelSize: 14
  274. font.bold: true
  275. }
  276. MouseArea {
  277. id: pauseMouseArea
  278. anchors.fill: parent
  279. enabled: backend && backend.currentFile !== ""
  280. onClicked: {
  281. if (backend) {
  282. if (backend.isRunning) {
  283. backend.pauseExecution()
  284. } else {
  285. backend.resumeExecution()
  286. }
  287. }
  288. }
  289. }
  290. }
  291. // Stop button
  292. Rectangle {
  293. height: parent.height
  294. radius: 6
  295. color: stopMouseArea.pressed ? "#b91c1c" : (backend && backend.currentFile !== "" ? "#dc2626" : "#9ca3af")
  296. Text {
  297. anchors.centerIn: parent
  298. text: "■"
  299. color: "white"
  300. font.pixelSize: 14
  301. font.bold: true
  302. }
  303. MouseArea {
  304. id: stopMouseArea
  305. anchors.fill: parent
  306. enabled: backend
  307. onClicked: {
  308. if (backend) {
  309. backend.stopExecution()
  310. }
  311. }
  312. }
  313. }
  314. // Skip button
  315. Rectangle {
  316. width: (parent.width - 16) / 3
  317. height: parent.height
  318. radius: 6
  319. color: skipMouseArea.pressed ? "#525252" : (backend && backend.currentFile !== "" ? "#6b7280" : "#9ca3af")
  320. Text {
  321. anchors.centerIn: parent
  322. text: "▶▶"
  323. color: "white"
  324. font.pixelSize: 14
  325. font.bold: true
  326. }
  327. MouseArea {
  328. id: skipMouseArea
  329. anchors.fill: parent
  330. enabled: backend && backend.currentFile !== ""
  331. onClicked: {
  332. if (backend) {
  333. backend.skipPattern()
  334. }
  335. }
  336. }
  337. }
  338. }
  339. }
  340. }
  341. // Speed Control Section
  342. Rectangle {
  343. width: parent.width
  344. height: 120
  345. radius: 8
  346. color: "#f8f9fa"
  347. border.color: "#e5e7eb"
  348. border.width: 1
  349. Column {
  350. anchors.fill: parent
  351. anchors.margins: 10
  352. spacing: 10
  353. Label {
  354. text: "Speed"
  355. font.pixelSize: 12
  356. font.bold: true
  357. color: "#333"
  358. }
  359. // Touch-friendly button row for speed options
  360. Row {
  361. id: speedControlRow
  362. width: parent.width
  363. spacing: 8
  364. property string currentSelection: backend ? backend.getCurrentSpeedOption() : "200"
  365. // Speed buttons
  366. Repeater {
  367. model: ["100", "150", "200", "300", "500"]
  368. Rectangle {
  369. width: (speedControlRow.width - 32) / 5 // Distribute evenly with spacing
  370. height: 50
  371. color: speedControlRow.currentSelection === modelData ? "#2196F3" : "#f0f0f0"
  372. border.color: speedControlRow.currentSelection === modelData ? "#1976D2" : "#ccc"
  373. border.width: 2
  374. radius: 8
  375. Label {
  376. anchors.centerIn: parent
  377. text: modelData
  378. font.pixelSize: 12
  379. font.bold: true
  380. color: speedControlRow.currentSelection === modelData ? "white" : "#333"
  381. }
  382. MouseArea {
  383. anchors.fill: parent
  384. onClicked: {
  385. if (backend) {
  386. backend.setSpeedByOption(modelData)
  387. speedControlRow.currentSelection = modelData
  388. }
  389. }
  390. }
  391. }
  392. }
  393. // Update selection when backend changes
  394. Connections {
  395. target: backend
  396. function onSpeedChanged(speed) {
  397. if (backend) {
  398. speedControlRow.currentSelection = backend.getCurrentSpeedOption()
  399. }
  400. }
  401. }
  402. }
  403. }
  404. }
  405. }
  406. }
  407. }
  408. }
  409. }
  410. }
  411. }