[education/kstars] kstars/ekos/capture: Fix issue with selecting video as a frame type: Disable preview and loop. Set remote directorty to a sane value so that remotely INDI can successfully write the video file.

Jasem Mutlaq <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit e8f6cebcca71605dfaf0b359eb0078012a2fe919 by Jasem Mutlaq.
Committed on 30/07/2026 at 18:55.
Pushed by mutlaqja into branch 'master'.

Fix issue with selecting video as a frame type: Disable preview and loop. Set remote directorty to a sane value so that remotely INDI can successfully write the video file.

M  +5    -2    kstars/ekos/capture/camera_actions.cpp
M  +33   -20   kstars/ekos/capture/camera_config.cpp
M  +7    -4    kstars/ekos/capture/capture.cpp

https://invent.kde.org/education/kstars/-/commit/e8f6cebcca71605dfaf0b359eb0078012a2fe919

diff --git a/kstars/ekos/capture/camera_actions.cpp b/kstars/ekos/capture/camera_actions.cpp
index 8b114bce0c..bc61f52e2c 100644
--- a/kstars/ekos/capture/camera_actions.cpp
+++ b/kstars/ekos/capture/camera_actions.cpp
@@ -293,8 +293,11 @@ void Camera::updateStartButtons(bool start, bool pause)
 
 void Camera::setBusy(bool enable)
 {
-    previewB->setEnabled(!enable);
-    loopB->setEnabled(!enable);
+    // Preview and loop capture stay disabled for the Video frame type even once busy
+    // (e.g. slewing) clears, since they are not meaningful for video recording.
+    const bool isVideo = captureTypeS->currentText() == CAPTURE_TYPE_VIDEO;
+    previewB->setEnabled(!enable && !isVideo);
+    loopB->setEnabled(!enable && !isVideo);
     opticalTrainCombo->setEnabled(!enable);
     trainB->setEnabled(!enable);
 
diff --git a/kstars/ekos/capture/camera_config.cpp b/kstars/ekos/capture/camera_config.cpp
index 32ebbd8619..f0fdb30875 100644
--- a/kstars/ekos/capture/camera_config.cpp
+++ b/kstars/ekos/capture/camera_config.cpp
@@ -936,6 +936,30 @@ void Camera::updateCaptureFormats()
     else
         captureEncodingS->setCurrentText(currentEncoding);
     captureEncodingS->blockSignals(false);
+
+    // Apply the frame-type-dependent UI state here too, not just from checkFrameType(): this
+    // function repopulates captureTypeS with signals blocked, so restoring a persisted
+    // non-Light selection (e.g. reconnecting to a camera with Video previously selected)
+    // would otherwise never trigger checkFrameType() and leave the Save mode / calibration /
+    // loop / preview buttons out of sync until the user manually reselects the frame type.
+    const int index = captureTypeS->currentIndex();
+    calibrationB->setEnabled(index != FRAME_LIGHT);
+    generateDarkFlatsB->setEnabled(index != FRAME_LIGHT);
+    exposureOptions->setCurrentIndex(isVideo ? 1 : 0);
+    exposureOptionsLabel->setToolTip(isVideo ? i18n("Duration of the video sequence") : i18n("Number of images to capture"));
+    exposureOptionsLabel->setText(isVideo ? i18n("Duration:") : i18n("Count:"));
+    exposureLabel->setToolTip(isVideo ? i18n("Exposure time in seconds of a single video frame") :
+                              i18n("Exposure time in seconds for individual images"));
+
+    // Loop and preview capture are only meaningful for still images, not video recording
+    loopB->setEnabled(!isVideo);
+    previewB->setEnabled(!isVideo);
+
+    // enforce the upload mode for videos
+    if (isVideo)
+        selectUploadMode(ISD::Camera::UPLOAD_REMOTE);
+    else
+        checkUploadMode(fileUploadModeS->currentIndex());
 }
 
 void Camera::updateHFRCheckAlgo()
@@ -959,22 +983,11 @@ void Camera::clearAutoFocusHFR()
 
 void Camera::checkFrameType(int index)
 {
+    // All frame-type-dependent UI state is applied inside updateCaptureFormats() itself, since
+    // that function is also called when reconnecting to a camera (with signals blocked while
+    // captureTypeS is repopulated) and must stay in sync there too, not just on this signal.
+    Q_UNUSED(index);
     updateCaptureFormats();
-
-    calibrationB->setEnabled(index != FRAME_LIGHT);
-    generateDarkFlatsB->setEnabled(index != FRAME_LIGHT);
-    const bool isVideo = captureTypeS->currentText() == CAPTURE_TYPE_VIDEO;
-    exposureOptions->setCurrentIndex(isVideo ? 1 : 0);
-    exposureOptionsLabel->setToolTip(isVideo ? i18n("Duration of the video sequence") : i18n("Number of images to capture"));
-    exposureOptionsLabel->setText(isVideo ? i18n("Duration:") : i18n("Count:"));
-    exposureLabel->setToolTip(isVideo ? i18n("Exposure time in seconds of a single video frame") :
-                              i18n("Exposure time in seconds for individual images"));
-
-    // enforce the upload mode for videos
-    if (isVideo)
-        selectUploadMode(ISD::Camera::UPLOAD_REMOTE);
-    else
-        checkUploadMode(fileUploadModeS->currentIndex());
 }
 
 void Camera::updateVideoDurationUnit()
@@ -1022,16 +1035,16 @@ void Camera::checkUploadMode(int index)
 
     // When uploading remotely (or both locally and remotely), make sure the remote
     // directory has a sensible default matching the current frame type. Only touch it
-    // if it is empty, or if it still holds an auto-generated %h-based path from a
+    // if it is empty, or if it still holds an auto-generated _HOME_-based path from a
     // previous frame type change, so a directory the user typed in manually is never
-    // overwritten. %h is expanded by INDI to the remote system's home directory since
-    // it cannot be known in advance.
+    // overwritten. _HOME_ is expanded by INDI to the remote system's home directory
+    // since it cannot be known in advance.
     if (index != ISD::Camera::UPLOAD_CLIENT)
     {
         const QString suffix = isVideo ? QLatin1String("Videos") : QLatin1String("Pictures");
         const QString remoteDir = fileRemoteDirT->text();
-        if (remoteDir.isEmpty() || remoteDir.startsWith(QLatin1String("%h/")))
-            fileRemoteDirT->setText(QLatin1String("%h/") + suffix);
+        if (remoteDir.isEmpty() || remoteDir.startsWith(QLatin1String("_HOME_/")))
+            fileRemoteDirT->setText(QLatin1String("_HOME_/") + suffix);
     }
 
     generatePreviewFilename();
diff --git a/kstars/ekos/capture/capture.cpp b/kstars/ekos/capture/capture.cpp
index 21c8b6cfcc..7b28ac5f61 100644
--- a/kstars/ekos/capture/capture.cpp
+++ b/kstars/ekos/capture/capture.cpp
@@ -506,13 +506,13 @@ void Capture::checkCloseCameraTab(int tabIndex)
 const QSharedPointer<Camera> Capture::mainCamera() const
 {
     if (cameras().size() > 0)
-        return moduleState()->cameras()[0];
+    return moduleState()->cameras()[0];
     else
     {
         QSharedPointer<CaptureModuleState> cms;
         cms.reset(new CaptureModuleState());
-        return QSharedPointer<Camera>(new Camera(0));
-    }
+            return QSharedPointer<Camera>(new Camera(0));
+        }
 }
 
 int Capture::findCameraPosition(QString train, bool addIfNecessary)
@@ -575,7 +575,10 @@ void Capture::setMountStatus(ISD::Mount::Status newState)
         default:
             if (mainCameraState()->isBusy() == false)
             {
-                mainCamera()->previewB->setEnabled(true);
+                // Preview capture is not meaningful for the Video frame type
+                const bool isVideo = mainCamera()->captureTypeS->currentText() == CAPTURE_TYPE_VIDEO;
+                mainCamera()->previewB->setEnabled(!isVideo);
+                mainCamera()->loopB->setEnabled(!isVideo);
                 if (mainCameraDevices()->getActiveCamera())
                     mainCamera()->liveVideoB->setEnabled(mainCameraDevices()->getActiveCamera()->hasVideoStream());
                 mainCamera()->startB->setEnabled(true);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.