completely redone menu now using widgets instead of QML
This commit is contained in:
84
JiboTools/JiboTools/.gitignore
vendored
Normal file
84
JiboTools/JiboTools/.gitignore
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
**/.qmlls.ini
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
*.qbs.user*
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
# Directories with generated files
|
||||
.moc/
|
||||
.obj/
|
||||
.pch/
|
||||
.rcc/
|
||||
.uic/
|
||||
/build*/
|
||||
/.qtcreator/
|
||||
643
JiboTools/JiboTools/form.ui
Normal file
643
JiboTools/JiboTools/form.ui
Normal file
@@ -0,0 +1,643 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>782</width>
|
||||
<height>649</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Jibo Tools</string>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="rootLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabJibo">
|
||||
<attribute name="title">
|
||||
<string>Jibo</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="jiboPageLayout">
|
||||
<property name="spacing">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="configFrame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>420</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="configLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="configTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Config</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="configScroll">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="configScrollContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>380</width>
|
||||
<height>478</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="configScrollLayout">
|
||||
<property name="spacing">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupPreview">
|
||||
<property name="title">
|
||||
<string>Preview</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formPreview">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelOverride">
|
||||
<property name="text">
|
||||
<string>Override</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="overrideCheck">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelPreviewConnected">
|
||||
<property name="text">
|
||||
<string>Connected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="previewConnectedCheck">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupHomeAssistant">
|
||||
<property name="title">
|
||||
<string>Home Assistant</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formHomeAssistant">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelHaEnable">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="haEnableCheck">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelHaServerIp">
|
||||
<property name="text">
|
||||
<string>Server IP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="haServerIpField">
|
||||
<property name="placeholderText">
|
||||
<string>Home Assistant host</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupAiProvider">
|
||||
<property name="title">
|
||||
<string>AI Provider</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formAiProvider">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelAiEnable">
|
||||
<property name="text">
|
||||
<string>Enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="aiEnableCheck">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelAiProvider">
|
||||
<property name="text">
|
||||
<string>Provider</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="aiProviderCombo"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelAiEndpoint">
|
||||
<property name="text">
|
||||
<string>API endpoint</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="aiEndpointField">
|
||||
<property name="placeholderText">
|
||||
<string>http://...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="labelAiKey">
|
||||
<property name="text">
|
||||
<string>API key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="aiKeyField">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::EchoMode::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="labelTokens">
|
||||
<property name="text">
|
||||
<string>Tokens used</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="tokensUsedLabel">
|
||||
<property name="text">
|
||||
<string>-1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="configBottomSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="jiboCardFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="jiboCardLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="IpConfig">
|
||||
<item alignment="Qt::AlignmentFlag::AlignRight">
|
||||
<widget class="QPushButton" name="TryToConnect">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="JiboIpField">
|
||||
<property name="inputMask">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>e.g 192.168.1.54</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="jiboHeaderLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="jiboTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connect Your Jibo</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="jiboTopSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="jiboImage">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>260</width>
|
||||
<height>260</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>260</width>
|
||||
<height>260</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="jiboBottomSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="RobotSettings">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Robot Settings</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>Reboot</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Reboot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabUpdate">
|
||||
<attribute name="title">
|
||||
<string>Update</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="updatePageLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="updateFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="updateFrameLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>18</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateInfoText">
|
||||
<property name="text">
|
||||
<string>Installer and updater remain available via CLI. Use the buttons below to launch their GUIs.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="updateSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="updateButtonsLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="installButton">
|
||||
<property name="text">
|
||||
<string>Install</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="checkUpdatesButton">
|
||||
<property name="text">
|
||||
<string>Check for updates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabSkills">
|
||||
<attribute name="title">
|
||||
<string>Skills</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="skillsLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="skillsComingSoon">
|
||||
<property name="text">
|
||||
<string>Coming soon.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabSsh">
|
||||
<attribute name="title">
|
||||
<string>SSH</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="sshLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="sshComingSoon">
|
||||
<property name="text">
|
||||
<string>Coming soon.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabFtp">
|
||||
<attribute name="title">
|
||||
<string>FTP</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="ftpLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="ftpComingSoon">
|
||||
<property name="text">
|
||||
<string>Coming soon.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabStatus">
|
||||
<attribute name="title">
|
||||
<string>Status</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="statusLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="statusRow">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="statusDot">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="statusText">
|
||||
<property name="text">
|
||||
<string>No Jibo IP configured</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabRobotOs">
|
||||
<attribute name="title">
|
||||
<string>Robot OS</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="robotOsLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="robotOsComingSoon">
|
||||
<property name="text">
|
||||
<string>Coming soon.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
BIN
JiboTools/JiboTools/gui/Assets/Jibo/Archive.zip
Normal file
BIN
JiboTools/JiboTools/gui/Assets/Jibo/Archive.zip
Normal file
Binary file not shown.
BIN
JiboTools/JiboTools/gui/Assets/Jibo/JiboFaceForward.png
Normal file
BIN
JiboTools/JiboTools/gui/Assets/Jibo/JiboFaceForward.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
BIN
JiboTools/JiboTools/gui/Assets/Jibo/NoJiboConnected.png
Normal file
BIN
JiboTools/JiboTools/gui/Assets/Jibo/NoJiboConnected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
JiboTools/JiboTools/gui/assets/jibo.kra
Normal file
BIN
JiboTools/JiboTools/gui/assets/jibo.kra
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 696 B After Width: | Height: | Size: 696 B |
17
JiboTools/JiboTools/gui/installer_gui.py
Normal file
17
JiboTools/JiboTools/gui/installer_gui.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from .tool_runner_window import ToolRunnerWindow
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QApplication(sys.argv)
|
||||
win = ToolRunnerWindow(title="Installer", script="jibo_automod.py")
|
||||
win.show()
|
||||
return int(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
318
JiboTools/JiboTools/gui/main_panel.py
Normal file
318
JiboTools/JiboTools/gui/main_panel.py
Normal file
@@ -0,0 +1,318 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
QTabWidget,
|
||||
)
|
||||
|
||||
from .process_runner import resolve_python, resolve_python_invocation
|
||||
from .ui_loader import load_ui, require_child
|
||||
|
||||
|
||||
def _set_dot(label: QLabel, color: str) -> None:
|
||||
label.setStyleSheet(
|
||||
"QLabel {"
|
||||
f"background-color: {color};"
|
||||
"border-radius: 5px;"
|
||||
"}"
|
||||
)
|
||||
|
||||
|
||||
class MainWindowController:
|
||||
def __init__(self) -> None:
|
||||
project_root = Path(__file__).resolve().parents[1]
|
||||
ui_path = project_root / "form.ui"
|
||||
self.window = load_ui(ui_path)
|
||||
if not hasattr(self.window, "setWindowTitle"):
|
||||
raise RuntimeError("form.ui must have a QMainWindow root")
|
||||
|
||||
self._ssh_client: Optional[object] = None
|
||||
self._identity: Optional[dict] = None
|
||||
self._connecting = False
|
||||
|
||||
# Tabs + connection pill
|
||||
self.tab_widget = require_child(self.window, "tabWidget", QTabWidget)
|
||||
self.connection_pill, self.conn_dot, self.conn_text = self._create_connection_pill()
|
||||
self.tab_widget.setCornerWidget(self.connection_pill, Qt.TopRightCorner)
|
||||
|
||||
# Jibo/config
|
||||
self.jibo_ip = require_child(self.window, "JiboIpField", QLineEdit)
|
||||
self.connect_button = require_child(self.window, "TryToConnect", QPushButton)
|
||||
self.jibo_title = require_child(self.window, "jiboTitle", QLabel)
|
||||
self.override_check = require_child(self.window, "overrideCheck", QCheckBox)
|
||||
self.preview_connected_check = require_child(self.window, "previewConnectedCheck", QCheckBox)
|
||||
|
||||
self.ha_enable = require_child(self.window, "haEnableCheck", QCheckBox)
|
||||
self.ha_server_ip = require_child(self.window, "haServerIpField", QLineEdit)
|
||||
|
||||
self.ai_enable = require_child(self.window, "aiEnableCheck", QCheckBox)
|
||||
self.ai_provider = require_child(self.window, "aiProviderCombo", QComboBox)
|
||||
self.ai_endpoint = require_child(self.window, "aiEndpointField", QLineEdit)
|
||||
self.ai_key = require_child(self.window, "aiKeyField", QLineEdit)
|
||||
self.tokens_used = require_child(self.window, "tokensUsedLabel", QLabel)
|
||||
|
||||
# Jibo card controls
|
||||
self.robot_settings_button = require_child(self.window, "RobotSettings", QPushButton)
|
||||
self.robot_action_combo = require_child(self.window, "comboBox", QComboBox)
|
||||
self.jibo_image = require_child(self.window, "jiboImage", QLabel)
|
||||
|
||||
# Update page
|
||||
self.install_button = require_child(self.window, "installButton", QPushButton)
|
||||
self.check_updates_button = require_child(self.window, "checkUpdatesButton", QPushButton)
|
||||
|
||||
# Status page
|
||||
self.status_dot = require_child(self.window, "statusDot", QLabel)
|
||||
self.status_text = require_child(self.window, "statusText", QLabel)
|
||||
|
||||
self._configure_ui()
|
||||
self._wire_signals()
|
||||
self._sync_enabled()
|
||||
self._sync_all()
|
||||
|
||||
@property
|
||||
def host(self) -> str:
|
||||
return self.jibo_ip.text().strip()
|
||||
|
||||
@property
|
||||
def session_connected(self) -> bool:
|
||||
return self._ssh_client is not None
|
||||
|
||||
def effective_connected(self) -> bool:
|
||||
"""Effective connection state for visuals.
|
||||
|
||||
The Preview override is kept for UI testing, but the real connect/
|
||||
disconnect state comes from an active SSH session.
|
||||
"""
|
||||
|
||||
if self.override_check.isChecked():
|
||||
return self.preview_connected_check.isChecked()
|
||||
return self.session_connected
|
||||
|
||||
def _configure_ui(self) -> None:
|
||||
# Simple styling, roughly matching the previous QML look.
|
||||
self.connection_pill.setStyleSheet(
|
||||
"QFrame#connectionPill {"
|
||||
"background-color: #f6f6f6;"
|
||||
"border: 1px solid #e4e4e4;"
|
||||
"border-radius: 14px;"
|
||||
"}"
|
||||
)
|
||||
|
||||
# Provider choices
|
||||
self.ai_provider.clear()
|
||||
self.ai_provider.addItems(["Self-hosted", "OpenAI", "Other"])
|
||||
|
||||
# Robot controls start disabled until connected.
|
||||
self.robot_settings_button.setEnabled(False)
|
||||
self.robot_action_combo.setEnabled(False)
|
||||
|
||||
# Defaults
|
||||
self.tokens_used.setText("-1")
|
||||
self.connect_button.setText("Connect")
|
||||
self.jibo_title.setText("Connect Your Jibo")
|
||||
|
||||
def _wire_signals(self) -> None:
|
||||
self.connect_button.clicked.connect(self._toggle_connection)
|
||||
|
||||
self.override_check.toggled.connect(self._sync_enabled)
|
||||
self.preview_connected_check.toggled.connect(self._sync_all)
|
||||
self.override_check.toggled.connect(self._sync_all)
|
||||
|
||||
self.ha_enable.toggled.connect(self._sync_enabled)
|
||||
self.ai_enable.toggled.connect(self._sync_enabled)
|
||||
|
||||
self.install_button.clicked.connect(self._launch_installer)
|
||||
self.check_updates_button.clicked.connect(self._launch_updater)
|
||||
|
||||
def _sync_enabled(self) -> None:
|
||||
self.preview_connected_check.setEnabled(self.override_check.isChecked())
|
||||
self.ha_server_ip.setEnabled(self.ha_enable.isChecked())
|
||||
|
||||
ai_enabled = self.ai_enable.isChecked()
|
||||
self.ai_provider.setEnabled(ai_enabled)
|
||||
self.ai_endpoint.setEnabled(ai_enabled)
|
||||
self.ai_key.setEnabled(ai_enabled)
|
||||
|
||||
# Connection button enabled unless a connect attempt is in progress.
|
||||
self.connect_button.setEnabled(not self._connecting)
|
||||
|
||||
def _sync_all(self) -> None:
|
||||
host = self.host
|
||||
connected = self.session_connected
|
||||
visual_connected = self.effective_connected()
|
||||
|
||||
if connected:
|
||||
title = (self._identity or {}).get("name") or "Connected"
|
||||
else:
|
||||
title = "Connect Your Jibo"
|
||||
self.jibo_title.setText(title)
|
||||
|
||||
self.robot_settings_button.setEnabled(connected)
|
||||
self.robot_action_combo.setEnabled(connected)
|
||||
self.connect_button.setText("Disconnect" if connected else "Connect")
|
||||
|
||||
dot_color = "#2ecc71" if connected else ("#e67e22" if host else "#bdc3c7")
|
||||
_set_dot(self.conn_dot, dot_color)
|
||||
_set_dot(self.status_dot, dot_color)
|
||||
|
||||
if connected:
|
||||
self.conn_text.setText("Connected")
|
||||
else:
|
||||
self.conn_text.setText("Disconnected" if host else "No IP")
|
||||
|
||||
if connected:
|
||||
self.status_text.setText(f"Connected via SSH to {host}" if host else "Connected via SSH")
|
||||
elif host:
|
||||
self.status_text.setText(f"Disconnected ({host})")
|
||||
else:
|
||||
self.status_text.setText("No Jibo IP configured")
|
||||
|
||||
# Image swap
|
||||
assets = Path(__file__).resolve().parent / "Assets" / "Jibo"
|
||||
img_path = assets / ("JiboFaceForward.png" if visual_connected else "NoJiboConnected.png")
|
||||
pm = QPixmap(str(img_path))
|
||||
if not pm.isNull():
|
||||
pm = pm.scaled(
|
||||
self.jibo_image.size(),
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
self.jibo_image.setPixmap(pm)
|
||||
|
||||
def _launch_installer(self) -> None:
|
||||
program, prefix = resolve_python_invocation()
|
||||
subprocess.Popen(
|
||||
[program, *prefix, "-m", "gui.installer_gui"],
|
||||
cwd=str(Path(__file__).resolve().parents[1]),
|
||||
)
|
||||
|
||||
def _launch_updater(self) -> None:
|
||||
program, prefix = resolve_python_invocation()
|
||||
subprocess.Popen(
|
||||
[program, *prefix, "-m", "gui.updater_gui"],
|
||||
cwd=str(Path(__file__).resolve().parents[1]),
|
||||
)
|
||||
|
||||
def _disconnect(self) -> None:
|
||||
try:
|
||||
if self._ssh_client is not None:
|
||||
self._ssh_client.close()
|
||||
finally:
|
||||
self._ssh_client = None
|
||||
self._identity = None
|
||||
self._sync_all()
|
||||
|
||||
def _toggle_connection(self) -> None:
|
||||
if self.session_connected:
|
||||
self._disconnect()
|
||||
return
|
||||
|
||||
host = self.host
|
||||
if not host:
|
||||
self.status_text.setText("Enter a Jibo IP address")
|
||||
return
|
||||
|
||||
self._connecting = True
|
||||
self._sync_enabled()
|
||||
self.status_text.setText(f"Connecting to {host}...")
|
||||
|
||||
try:
|
||||
import paramiko # type: ignore
|
||||
except Exception:
|
||||
self._connecting = False
|
||||
self._sync_enabled()
|
||||
self.status_text.setText("Paramiko not installed; install requirements")
|
||||
return
|
||||
|
||||
try:
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(
|
||||
hostname=host,
|
||||
username="root",
|
||||
password="jibo",
|
||||
look_for_keys=False,
|
||||
allow_agent=False,
|
||||
timeout=10,
|
||||
banner_timeout=10,
|
||||
auth_timeout=10,
|
||||
)
|
||||
|
||||
sftp = client.open_sftp()
|
||||
try:
|
||||
with sftp.open("/var/jibo/identity.json", "r") as f:
|
||||
raw = f.read()
|
||||
finally:
|
||||
sftp.close()
|
||||
|
||||
if isinstance(raw, bytes):
|
||||
raw_text = raw.decode("utf-8", errors="replace")
|
||||
else:
|
||||
raw_text = str(raw)
|
||||
|
||||
identity = json.loads(raw_text)
|
||||
|
||||
# Success: store session.
|
||||
self._ssh_client = client
|
||||
self._identity = identity if isinstance(identity, dict) else None
|
||||
self.status_text.setText(f"Connected via SSH to {host}")
|
||||
except Exception as e:
|
||||
try:
|
||||
client.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.status_text.setText(f"Connect failed: {e}")
|
||||
finally:
|
||||
self._connecting = False
|
||||
self._sync_enabled()
|
||||
self._sync_all()
|
||||
|
||||
def _create_connection_pill(self) -> tuple[QFrame, QLabel, QLabel]:
|
||||
pill = QFrame()
|
||||
pill.setObjectName("connectionPill")
|
||||
|
||||
layout = QHBoxLayout(pill)
|
||||
layout.setContentsMargins(10, 4, 10, 4)
|
||||
layout.setSpacing(6)
|
||||
|
||||
dot = QLabel()
|
||||
dot.setObjectName("connDot")
|
||||
dot.setFixedSize(10, 10)
|
||||
|
||||
text = QLabel("No IP")
|
||||
text.setObjectName("connText")
|
||||
|
||||
layout.addWidget(dot)
|
||||
layout.addWidget(text)
|
||||
|
||||
# Keep it tight on the tab bar.
|
||||
pill.setSizePolicy(pill.sizePolicy().horizontalPolicy(), pill.sizePolicy().verticalPolicy())
|
||||
pill.setMinimumHeight(28)
|
||||
return pill, dot, text
|
||||
|
||||
|
||||
def main() -> int:
|
||||
_ = resolve_python # keep import stable (used elsewhere)
|
||||
app = QApplication(sys.argv)
|
||||
ctrl = MainWindowController()
|
||||
ctrl.window.show()
|
||||
return int(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -12,7 +12,24 @@ from typing import Optional
|
||||
from PySide6.QtCore import QObject, QProcess, QTimer, Signal, Slot, Property
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
def _find_repo_root(start: Path) -> Path:
|
||||
"""Find the outer JiboAutoMod repo root.
|
||||
|
||||
The Qt Creator project lives under JiboTools/JiboTools, while the CLI tools
|
||||
(jibo_updater.py, jibo_automod.py) usually live in the outer repo root.
|
||||
"""
|
||||
|
||||
cur = start.resolve()
|
||||
for _ in range(6):
|
||||
if (cur / "jibo_updater.py").exists() and (cur / "jibo_automod.py").exists():
|
||||
return cur
|
||||
if cur.parent == cur:
|
||||
break
|
||||
cur = cur.parent
|
||||
return start.resolve().parents[1]
|
||||
|
||||
|
||||
REPO_ROOT = _find_repo_root(Path(__file__).resolve())
|
||||
|
||||
|
||||
def resolve_python_invocation() -> tuple[str, list[str]]:
|
||||
@@ -28,13 +45,20 @@ def resolve_python_invocation() -> tuple[str, list[str]]:
|
||||
if venv_py.exists():
|
||||
return (str(venv_py), [])
|
||||
|
||||
# Prefer the current interpreter when running inside a venv (e.g. Qt Creator).
|
||||
try:
|
||||
if sys.executable and Path(sys.executable).exists():
|
||||
return (sys.executable, [])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if os.name == "nt" and shutil.which("py"):
|
||||
return ("py", ["-3"])
|
||||
|
||||
if shutil.which("python3"):
|
||||
return ("python3", [])
|
||||
|
||||
return (sys.executable or "python", [])
|
||||
return ("python", [])
|
||||
|
||||
|
||||
def resolve_python() -> str:
|
||||
148
JiboTools/JiboTools/gui/tool_runner.ui
Normal file
148
JiboTools/JiboTools/gui/tool_runner.ui
Normal file
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ToolRunnerWindow</class>
|
||||
<widget class="QMainWindow" name="ToolRunnerWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>900</width>
|
||||
<height>560</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Tool</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="rootLayout">
|
||||
<property name="leftMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="headerLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="text">
|
||||
<string>Tool</string>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="headerSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="startStopButton">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openTerminalButton">
|
||||
<property name="text">
|
||||
<string>Open in terminal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="argsLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="hostField">
|
||||
<property name="placeholderText">
|
||||
<string>Jibo IP (required for updater)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="extraArgsField">
|
||||
<property name="placeholderText">
|
||||
<string>Extra arguments (optional)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="footerLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="statusLabel">
|
||||
<property name="text">
|
||||
<string>Idle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="footerSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearLogButton">
|
||||
<property name="text">
|
||||
<string>Clear log</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
123
JiboTools/JiboTools/gui/tool_runner_window.py
Normal file
123
JiboTools/JiboTools/gui/tool_runner_window.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import QObject, Slot
|
||||
from PySide6.QtGui import QCloseEvent, QTextCursor
|
||||
from PySide6.QtWidgets import QMainWindow, QApplication, QLabel, QLineEdit, QPushButton, QPlainTextEdit
|
||||
|
||||
from .process_runner import ProcessRunner, resolve_python_invocation
|
||||
from .terminal_helper import TerminalHelper
|
||||
from .ui_loader import load_ui, require_child
|
||||
|
||||
|
||||
class ToolRunnerWindow(QObject):
|
||||
def __init__(self, *, title: str, script: str) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._script = script
|
||||
self._is_updater = "jibo_updater.py" in script
|
||||
|
||||
ui_path = Path(__file__).resolve().parent / "tool_runner.ui"
|
||||
self.window = load_ui(ui_path)
|
||||
if not isinstance(self.window, QMainWindow):
|
||||
raise RuntimeError("tool_runner.ui must have a QMainWindow as root")
|
||||
|
||||
self.window.setWindowTitle(title)
|
||||
|
||||
self.runner = ProcessRunner()
|
||||
self.terminal = TerminalHelper()
|
||||
|
||||
self._title_label = require_child(self.window, "titleLabel", QLabel)
|
||||
self._start_stop = require_child(self.window, "startStopButton", QPushButton)
|
||||
self._open_terminal = require_child(self.window, "openTerminalButton", QPushButton)
|
||||
self._host_field = require_child(self.window, "hostField", QLineEdit)
|
||||
self._extra_args = require_child(self.window, "extraArgsField", QLineEdit)
|
||||
self._log = require_child(self.window, "logEdit", QPlainTextEdit)
|
||||
self._status = require_child(self.window, "statusLabel", QLabel)
|
||||
self._clear = require_child(self.window, "clearLogButton", QPushButton)
|
||||
|
||||
self._title_label.setText(title)
|
||||
|
||||
self._host_field.setVisible(self._is_updater)
|
||||
|
||||
self._start_stop.clicked.connect(self._toggle)
|
||||
self._open_terminal.clicked.connect(self._open_in_terminal)
|
||||
self._clear.clicked.connect(lambda: self._log.setPlainText(""))
|
||||
|
||||
self.runner.outputAppended.connect(self._append_output)
|
||||
self.runner.runningChanged.connect(self._sync_buttons)
|
||||
self.runner.exitCodeChanged.connect(self._sync_status)
|
||||
|
||||
self._sync_buttons()
|
||||
self._sync_status()
|
||||
|
||||
# Ensure the process is stopped when the window closes.
|
||||
self.window.closeEvent = self._on_close # type: ignore[assignment]
|
||||
|
||||
def show(self) -> None:
|
||||
self.window.show()
|
||||
|
||||
def _build_args(self) -> list[str]:
|
||||
args: list[str] = [self._script]
|
||||
|
||||
if self._is_updater:
|
||||
host = self._host_field.text().strip()
|
||||
if host:
|
||||
args += ["--ip", host]
|
||||
|
||||
extra = self._extra_args.text().strip()
|
||||
if extra:
|
||||
args += shlex.split(extra)
|
||||
|
||||
return args
|
||||
|
||||
@Slot()
|
||||
def _toggle(self) -> None:
|
||||
if self.runner.running:
|
||||
self.runner.stop()
|
||||
else:
|
||||
program, prefix = resolve_python_invocation()
|
||||
self.runner.start(program, [*prefix, *self._build_args()])
|
||||
|
||||
@Slot()
|
||||
def _open_in_terminal(self) -> None:
|
||||
program, prefix = resolve_python_invocation()
|
||||
self.terminal.openTerminal(program, [*prefix, *self._build_args()])
|
||||
|
||||
@Slot(str)
|
||||
def _append_output(self, chunk: str) -> None:
|
||||
# Keep it simple: append and scroll to end.
|
||||
self._log.moveCursor(QTextCursor.End)
|
||||
self._log.insertPlainText(chunk)
|
||||
self._log.moveCursor(QTextCursor.End)
|
||||
|
||||
def _sync_buttons(self) -> None:
|
||||
running = self.runner.running
|
||||
self._start_stop.setText("Stop" if running else "Start")
|
||||
self._open_terminal.setEnabled(not running)
|
||||
|
||||
def _sync_status(self) -> None:
|
||||
if self.runner.running:
|
||||
self._status.setText("Running...")
|
||||
return
|
||||
code = self.runner.exitCode
|
||||
if code >= 0:
|
||||
self._status.setText(f"Exit: {code}")
|
||||
else:
|
||||
self._status.setText("Idle")
|
||||
|
||||
def _on_close(self, event: QCloseEvent) -> None:
|
||||
try:
|
||||
self.runner.stop()
|
||||
except Exception:
|
||||
pass
|
||||
event.accept()
|
||||
|
||||
|
||||
def run_tool_window(*, title: str, script: str) -> int:
|
||||
app = QApplication.instance() or QApplication([])
|
||||
win = ToolRunnerWindow(title=title, script=script)
|
||||
win.show()
|
||||
return int(app.exec())
|
||||
34
JiboTools/JiboTools/gui/ui_loader.py
Normal file
34
JiboTools/JiboTools/gui/ui_loader.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TypeVar, cast
|
||||
|
||||
from PySide6.QtCore import QFile
|
||||
from PySide6.QtUiTools import QUiLoader
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def load_ui(ui_path: Path) -> object:
|
||||
loader = QUiLoader()
|
||||
file = QFile(str(ui_path))
|
||||
if not file.open(QFile.ReadOnly):
|
||||
raise RuntimeError(f"Failed to open UI file: {ui_path}")
|
||||
try:
|
||||
widget = loader.load(file)
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
if widget is None:
|
||||
raise RuntimeError(f"Failed to load UI: {ui_path}")
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
def require_child(parent: object, name: str, typ: type[T]) -> T:
|
||||
# Qt objects implement findChild; keep typing light.
|
||||
child = parent.findChild(typ, name) # type: ignore[attr-defined]
|
||||
if child is None:
|
||||
raise RuntimeError(f"UI is missing required widget '{name}' ({typ.__name__})")
|
||||
return cast(T, child)
|
||||
124
JiboTools/JiboTools/gui/ui_tool_runner.py
Normal file
124
JiboTools/JiboTools/gui/ui_tool_runner.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'tool_runner.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.10.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit,
|
||||
QMainWindow, QPlainTextEdit, QPushButton, QSizePolicy,
|
||||
QSpacerItem, QStatusBar, QVBoxLayout, QWidget)
|
||||
|
||||
class Ui_ToolRunnerWindow(object):
|
||||
def setupUi(self, ToolRunnerWindow):
|
||||
if not ToolRunnerWindow.objectName():
|
||||
ToolRunnerWindow.setObjectName(u"ToolRunnerWindow")
|
||||
ToolRunnerWindow.resize(900, 560)
|
||||
self.centralwidget = QWidget(ToolRunnerWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.rootLayout = QVBoxLayout(self.centralwidget)
|
||||
self.rootLayout.setSpacing(12)
|
||||
self.rootLayout.setObjectName(u"rootLayout")
|
||||
self.rootLayout.setContentsMargins(16, 16, 16, 16)
|
||||
self.headerLayout = QHBoxLayout()
|
||||
self.headerLayout.setSpacing(10)
|
||||
self.headerLayout.setObjectName(u"headerLayout")
|
||||
self.titleLabel = QLabel(self.centralwidget)
|
||||
self.titleLabel.setObjectName(u"titleLabel")
|
||||
font = QFont()
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
self.titleLabel.setFont(font)
|
||||
|
||||
self.headerLayout.addWidget(self.titleLabel)
|
||||
|
||||
self.headerSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||||
|
||||
self.headerLayout.addItem(self.headerSpacer)
|
||||
|
||||
self.startStopButton = QPushButton(self.centralwidget)
|
||||
self.startStopButton.setObjectName(u"startStopButton")
|
||||
|
||||
self.headerLayout.addWidget(self.startStopButton)
|
||||
|
||||
self.openTerminalButton = QPushButton(self.centralwidget)
|
||||
self.openTerminalButton.setObjectName(u"openTerminalButton")
|
||||
|
||||
self.headerLayout.addWidget(self.openTerminalButton)
|
||||
|
||||
|
||||
self.rootLayout.addLayout(self.headerLayout)
|
||||
|
||||
self.argsLayout = QHBoxLayout()
|
||||
self.argsLayout.setSpacing(10)
|
||||
self.argsLayout.setObjectName(u"argsLayout")
|
||||
self.hostField = QLineEdit(self.centralwidget)
|
||||
self.hostField.setObjectName(u"hostField")
|
||||
|
||||
self.argsLayout.addWidget(self.hostField)
|
||||
|
||||
self.extraArgsField = QLineEdit(self.centralwidget)
|
||||
self.extraArgsField.setObjectName(u"extraArgsField")
|
||||
|
||||
self.argsLayout.addWidget(self.extraArgsField)
|
||||
|
||||
|
||||
self.rootLayout.addLayout(self.argsLayout)
|
||||
|
||||
self.logEdit = QPlainTextEdit(self.centralwidget)
|
||||
self.logEdit.setObjectName(u"logEdit")
|
||||
self.logEdit.setReadOnly(True)
|
||||
|
||||
self.rootLayout.addWidget(self.logEdit)
|
||||
|
||||
self.footerLayout = QHBoxLayout()
|
||||
self.footerLayout.setSpacing(10)
|
||||
self.footerLayout.setObjectName(u"footerLayout")
|
||||
self.statusLabel = QLabel(self.centralwidget)
|
||||
self.statusLabel.setObjectName(u"statusLabel")
|
||||
|
||||
self.footerLayout.addWidget(self.statusLabel)
|
||||
|
||||
self.footerSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||||
|
||||
self.footerLayout.addItem(self.footerSpacer)
|
||||
|
||||
self.clearLogButton = QPushButton(self.centralwidget)
|
||||
self.clearLogButton.setObjectName(u"clearLogButton")
|
||||
|
||||
self.footerLayout.addWidget(self.clearLogButton)
|
||||
|
||||
|
||||
self.rootLayout.addLayout(self.footerLayout)
|
||||
|
||||
ToolRunnerWindow.setCentralWidget(self.centralwidget)
|
||||
self.statusbar = QStatusBar(ToolRunnerWindow)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
ToolRunnerWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(ToolRunnerWindow)
|
||||
|
||||
QMetaObject.connectSlotsByName(ToolRunnerWindow)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, ToolRunnerWindow):
|
||||
ToolRunnerWindow.setWindowTitle(QCoreApplication.translate("ToolRunnerWindow", u"Tool", None))
|
||||
self.titleLabel.setText(QCoreApplication.translate("ToolRunnerWindow", u"Tool", None))
|
||||
self.startStopButton.setText(QCoreApplication.translate("ToolRunnerWindow", u"Start", None))
|
||||
self.openTerminalButton.setText(QCoreApplication.translate("ToolRunnerWindow", u"Open in terminal", None))
|
||||
self.hostField.setPlaceholderText(QCoreApplication.translate("ToolRunnerWindow", u"Jibo IP (required for updater)", None))
|
||||
self.extraArgsField.setPlaceholderText(QCoreApplication.translate("ToolRunnerWindow", u"Extra arguments (optional)", None))
|
||||
self.statusLabel.setText(QCoreApplication.translate("ToolRunnerWindow", u"Idle", None))
|
||||
self.clearLogButton.setText(QCoreApplication.translate("ToolRunnerWindow", u"Clear log", None))
|
||||
# retranslateUi
|
||||
|
||||
17
JiboTools/JiboTools/gui/updater_gui.py
Normal file
17
JiboTools/JiboTools/gui/updater_gui.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from .tool_runner_window import ToolRunnerWindow
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QApplication(sys.argv)
|
||||
win = ToolRunnerWindow(title="Updater", script="jibo_updater.py")
|
||||
win.show()
|
||||
return int(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
18
JiboTools/JiboTools/main_panel.py
Normal file
18
JiboTools/JiboTools/main_panel.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Qt Creator entrypoint.
|
||||
|
||||
This project is intended to run cleanly from Qt Creator. The UI is implemented
|
||||
with Qt Widgets loaded from `.ui` files at runtime (see gui/main_panel.py and
|
||||
form.ui).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def main() -> int:
|
||||
from gui.main_panel import main as widgets_main
|
||||
|
||||
return int(widgets_main())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
19
JiboTools/JiboTools/pyproject.toml
Normal file
19
JiboTools/JiboTools/pyproject.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[project]
|
||||
name = "JiboTools"
|
||||
|
||||
[tool.pyside6-project]
|
||||
files = [
|
||||
"main_panel.py",
|
||||
"form.ui",
|
||||
"gui/__init__.py",
|
||||
"gui/main_panel.py",
|
||||
"gui/installer_gui.py",
|
||||
"gui/updater_gui.py",
|
||||
"gui/process_runner.py",
|
||||
"gui/terminal_helper.py",
|
||||
"gui/tool_runner.ui",
|
||||
"gui/tool_runner_window.py",
|
||||
"gui/ui_loader.py",
|
||||
"gui/Assets/Jibo/JiboFaceForward.png",
|
||||
"gui/Assets/Jibo/NoJiboConnected.png",
|
||||
]
|
||||
2
JiboTools/JiboTools/requirements.txt
Normal file
2
JiboTools/JiboTools/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
PySide6
|
||||
paramiko
|
||||
444
JiboTools/JiboTools/ui_form.py
Normal file
444
JiboTools/JiboTools/ui_form.py
Normal file
@@ -0,0 +1,444 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'form.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.10.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFormLayout,
|
||||
QFrame, QGroupBox, QHBoxLayout, QLabel,
|
||||
QLineEdit, QMainWindow, QPushButton, QScrollArea,
|
||||
QSizePolicy, QSpacerItem, QStatusBar, QTabWidget,
|
||||
QVBoxLayout, QWidget)
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
if not MainWindow.objectName():
|
||||
MainWindow.setObjectName(u"MainWindow")
|
||||
MainWindow.resize(782, 649)
|
||||
MainWindow.setTabShape(QTabWidget.TabShape.Rounded)
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.rootLayout = QVBoxLayout(self.centralwidget)
|
||||
self.rootLayout.setSpacing(12)
|
||||
self.rootLayout.setObjectName(u"rootLayout")
|
||||
self.rootLayout.setContentsMargins(14, 14, 14, 14)
|
||||
self.tabWidget = QTabWidget(self.centralwidget)
|
||||
self.tabWidget.setObjectName(u"tabWidget")
|
||||
self.tabWidget.setDocumentMode(True)
|
||||
self.tabWidget.setTabsClosable(False)
|
||||
self.tabWidget.setMovable(False)
|
||||
self.tabJibo = QWidget()
|
||||
self.tabJibo.setObjectName(u"tabJibo")
|
||||
self.jiboPageLayout = QHBoxLayout(self.tabJibo)
|
||||
self.jiboPageLayout.setSpacing(14)
|
||||
self.jiboPageLayout.setObjectName(u"jiboPageLayout")
|
||||
self.configFrame = QFrame(self.tabJibo)
|
||||
self.configFrame.setObjectName(u"configFrame")
|
||||
self.configFrame.setMinimumSize(QSize(420, 0))
|
||||
self.configFrame.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.configFrame.setFrameShadow(QFrame.Shadow.Raised)
|
||||
self.configLayout = QVBoxLayout(self.configFrame)
|
||||
self.configLayout.setSpacing(10)
|
||||
self.configLayout.setObjectName(u"configLayout")
|
||||
self.configLayout.setContentsMargins(18, 18, 18, 18)
|
||||
self.configTitle = QLabel(self.configFrame)
|
||||
self.configTitle.setObjectName(u"configTitle")
|
||||
font = QFont()
|
||||
font.setPointSize(12)
|
||||
font.setBold(True)
|
||||
self.configTitle.setFont(font)
|
||||
|
||||
self.configLayout.addWidget(self.configTitle)
|
||||
|
||||
self.configScroll = QScrollArea(self.configFrame)
|
||||
self.configScroll.setObjectName(u"configScroll")
|
||||
self.configScroll.setWidgetResizable(True)
|
||||
self.configScrollContents = QWidget()
|
||||
self.configScrollContents.setObjectName(u"configScrollContents")
|
||||
self.configScrollContents.setGeometry(QRect(0, 0, 380, 478))
|
||||
self.configScrollLayout = QVBoxLayout(self.configScrollContents)
|
||||
self.configScrollLayout.setSpacing(14)
|
||||
self.configScrollLayout.setObjectName(u"configScrollLayout")
|
||||
self.groupPreview = QGroupBox(self.configScrollContents)
|
||||
self.groupPreview.setObjectName(u"groupPreview")
|
||||
self.formPreview = QFormLayout(self.groupPreview)
|
||||
self.formPreview.setObjectName(u"formPreview")
|
||||
self.labelOverride = QLabel(self.groupPreview)
|
||||
self.labelOverride.setObjectName(u"labelOverride")
|
||||
|
||||
self.formPreview.setWidget(0, QFormLayout.ItemRole.LabelRole, self.labelOverride)
|
||||
|
||||
self.overrideCheck = QCheckBox(self.groupPreview)
|
||||
self.overrideCheck.setObjectName(u"overrideCheck")
|
||||
self.overrideCheck.setChecked(True)
|
||||
|
||||
self.formPreview.setWidget(0, QFormLayout.ItemRole.FieldRole, self.overrideCheck)
|
||||
|
||||
self.labelPreviewConnected = QLabel(self.groupPreview)
|
||||
self.labelPreviewConnected.setObjectName(u"labelPreviewConnected")
|
||||
|
||||
self.formPreview.setWidget(1, QFormLayout.ItemRole.LabelRole, self.labelPreviewConnected)
|
||||
|
||||
self.previewConnectedCheck = QCheckBox(self.groupPreview)
|
||||
self.previewConnectedCheck.setObjectName(u"previewConnectedCheck")
|
||||
|
||||
self.formPreview.setWidget(1, QFormLayout.ItemRole.FieldRole, self.previewConnectedCheck)
|
||||
|
||||
|
||||
self.configScrollLayout.addWidget(self.groupPreview)
|
||||
|
||||
self.groupHomeAssistant = QGroupBox(self.configScrollContents)
|
||||
self.groupHomeAssistant.setObjectName(u"groupHomeAssistant")
|
||||
self.formHomeAssistant = QFormLayout(self.groupHomeAssistant)
|
||||
self.formHomeAssistant.setObjectName(u"formHomeAssistant")
|
||||
self.labelHaEnable = QLabel(self.groupHomeAssistant)
|
||||
self.labelHaEnable.setObjectName(u"labelHaEnable")
|
||||
|
||||
self.formHomeAssistant.setWidget(0, QFormLayout.ItemRole.LabelRole, self.labelHaEnable)
|
||||
|
||||
self.haEnableCheck = QCheckBox(self.groupHomeAssistant)
|
||||
self.haEnableCheck.setObjectName(u"haEnableCheck")
|
||||
|
||||
self.formHomeAssistant.setWidget(0, QFormLayout.ItemRole.FieldRole, self.haEnableCheck)
|
||||
|
||||
self.labelHaServerIp = QLabel(self.groupHomeAssistant)
|
||||
self.labelHaServerIp.setObjectName(u"labelHaServerIp")
|
||||
|
||||
self.formHomeAssistant.setWidget(1, QFormLayout.ItemRole.LabelRole, self.labelHaServerIp)
|
||||
|
||||
self.haServerIpField = QLineEdit(self.groupHomeAssistant)
|
||||
self.haServerIpField.setObjectName(u"haServerIpField")
|
||||
|
||||
self.formHomeAssistant.setWidget(1, QFormLayout.ItemRole.FieldRole, self.haServerIpField)
|
||||
|
||||
|
||||
self.configScrollLayout.addWidget(self.groupHomeAssistant)
|
||||
|
||||
self.groupAiProvider = QGroupBox(self.configScrollContents)
|
||||
self.groupAiProvider.setObjectName(u"groupAiProvider")
|
||||
self.formAiProvider = QFormLayout(self.groupAiProvider)
|
||||
self.formAiProvider.setObjectName(u"formAiProvider")
|
||||
self.labelAiEnable = QLabel(self.groupAiProvider)
|
||||
self.labelAiEnable.setObjectName(u"labelAiEnable")
|
||||
|
||||
self.formAiProvider.setWidget(0, QFormLayout.ItemRole.LabelRole, self.labelAiEnable)
|
||||
|
||||
self.aiEnableCheck = QCheckBox(self.groupAiProvider)
|
||||
self.aiEnableCheck.setObjectName(u"aiEnableCheck")
|
||||
|
||||
self.formAiProvider.setWidget(0, QFormLayout.ItemRole.FieldRole, self.aiEnableCheck)
|
||||
|
||||
self.labelAiProvider = QLabel(self.groupAiProvider)
|
||||
self.labelAiProvider.setObjectName(u"labelAiProvider")
|
||||
|
||||
self.formAiProvider.setWidget(1, QFormLayout.ItemRole.LabelRole, self.labelAiProvider)
|
||||
|
||||
self.aiProviderCombo = QComboBox(self.groupAiProvider)
|
||||
self.aiProviderCombo.setObjectName(u"aiProviderCombo")
|
||||
|
||||
self.formAiProvider.setWidget(1, QFormLayout.ItemRole.FieldRole, self.aiProviderCombo)
|
||||
|
||||
self.labelAiEndpoint = QLabel(self.groupAiProvider)
|
||||
self.labelAiEndpoint.setObjectName(u"labelAiEndpoint")
|
||||
|
||||
self.formAiProvider.setWidget(2, QFormLayout.ItemRole.LabelRole, self.labelAiEndpoint)
|
||||
|
||||
self.aiEndpointField = QLineEdit(self.groupAiProvider)
|
||||
self.aiEndpointField.setObjectName(u"aiEndpointField")
|
||||
|
||||
self.formAiProvider.setWidget(2, QFormLayout.ItemRole.FieldRole, self.aiEndpointField)
|
||||
|
||||
self.labelAiKey = QLabel(self.groupAiProvider)
|
||||
self.labelAiKey.setObjectName(u"labelAiKey")
|
||||
|
||||
self.formAiProvider.setWidget(3, QFormLayout.ItemRole.LabelRole, self.labelAiKey)
|
||||
|
||||
self.aiKeyField = QLineEdit(self.groupAiProvider)
|
||||
self.aiKeyField.setObjectName(u"aiKeyField")
|
||||
self.aiKeyField.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
|
||||
self.formAiProvider.setWidget(3, QFormLayout.ItemRole.FieldRole, self.aiKeyField)
|
||||
|
||||
self.labelTokens = QLabel(self.groupAiProvider)
|
||||
self.labelTokens.setObjectName(u"labelTokens")
|
||||
|
||||
self.formAiProvider.setWidget(4, QFormLayout.ItemRole.LabelRole, self.labelTokens)
|
||||
|
||||
self.tokensUsedLabel = QLabel(self.groupAiProvider)
|
||||
self.tokensUsedLabel.setObjectName(u"tokensUsedLabel")
|
||||
|
||||
self.formAiProvider.setWidget(4, QFormLayout.ItemRole.FieldRole, self.tokensUsedLabel)
|
||||
|
||||
|
||||
self.configScrollLayout.addWidget(self.groupAiProvider)
|
||||
|
||||
self.configBottomSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||
|
||||
self.configScrollLayout.addItem(self.configBottomSpacer)
|
||||
|
||||
self.configScroll.setWidget(self.configScrollContents)
|
||||
|
||||
self.configLayout.addWidget(self.configScroll)
|
||||
|
||||
|
||||
self.jiboPageLayout.addWidget(self.configFrame)
|
||||
|
||||
self.jiboCardFrame = QFrame(self.tabJibo)
|
||||
self.jiboCardFrame.setObjectName(u"jiboCardFrame")
|
||||
self.jiboCardFrame.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.jiboCardFrame.setFrameShadow(QFrame.Shadow.Raised)
|
||||
self.jiboCardLayout = QVBoxLayout(self.jiboCardFrame)
|
||||
self.jiboCardLayout.setSpacing(12)
|
||||
self.jiboCardLayout.setObjectName(u"jiboCardLayout")
|
||||
self.jiboCardLayout.setContentsMargins(18, 18, 18, 18)
|
||||
self.IpConfig = QHBoxLayout()
|
||||
self.IpConfig.setObjectName(u"IpConfig")
|
||||
self.TryToConnect = QPushButton(self.jiboCardFrame)
|
||||
self.TryToConnect.setObjectName(u"TryToConnect")
|
||||
self.TryToConnect.setCheckable(False)
|
||||
|
||||
self.IpConfig.addWidget(self.TryToConnect, 0, Qt.AlignmentFlag.AlignRight)
|
||||
|
||||
self.JiboIpField = QLineEdit(self.jiboCardFrame)
|
||||
self.JiboIpField.setObjectName(u"JiboIpField")
|
||||
|
||||
self.IpConfig.addWidget(self.JiboIpField)
|
||||
|
||||
|
||||
self.jiboCardLayout.addLayout(self.IpConfig)
|
||||
|
||||
self.jiboHeaderLayout = QHBoxLayout()
|
||||
self.jiboHeaderLayout.setObjectName(u"jiboHeaderLayout")
|
||||
self.jiboTitle = QLabel(self.jiboCardFrame)
|
||||
self.jiboTitle.setObjectName(u"jiboTitle")
|
||||
font1 = QFont()
|
||||
font1.setPointSize(12)
|
||||
font1.setBold(True)
|
||||
font1.setUnderline(False)
|
||||
self.jiboTitle.setFont(font1)
|
||||
self.jiboTitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.jiboHeaderLayout.addWidget(self.jiboTitle)
|
||||
|
||||
|
||||
self.jiboCardLayout.addLayout(self.jiboHeaderLayout)
|
||||
|
||||
self.jiboTopSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||
|
||||
self.jiboCardLayout.addItem(self.jiboTopSpacer)
|
||||
|
||||
self.jiboImage = QLabel(self.jiboCardFrame)
|
||||
self.jiboImage.setObjectName(u"jiboImage")
|
||||
self.jiboImage.setMinimumSize(QSize(260, 260))
|
||||
self.jiboImage.setMaximumSize(QSize(260, 260))
|
||||
self.jiboImage.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.jiboCardLayout.addWidget(self.jiboImage)
|
||||
|
||||
self.jiboBottomSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
|
||||
|
||||
self.jiboCardLayout.addItem(self.jiboBottomSpacer)
|
||||
|
||||
self.RobotSettings = QPushButton(self.jiboCardFrame)
|
||||
self.RobotSettings.setObjectName(u"RobotSettings")
|
||||
self.RobotSettings.setEnabled(False)
|
||||
self.RobotSettings.setFlat(False)
|
||||
|
||||
self.jiboCardLayout.addWidget(self.RobotSettings)
|
||||
|
||||
self.comboBox = QComboBox(self.jiboCardFrame)
|
||||
self.comboBox.setObjectName(u"comboBox")
|
||||
self.comboBox.setEnabled(False)
|
||||
self.comboBox.setEditable(True)
|
||||
|
||||
self.jiboCardLayout.addWidget(self.comboBox)
|
||||
|
||||
|
||||
self.jiboPageLayout.addWidget(self.jiboCardFrame)
|
||||
|
||||
self.tabWidget.addTab(self.tabJibo, "")
|
||||
self.tabUpdate = QWidget()
|
||||
self.tabUpdate.setObjectName(u"tabUpdate")
|
||||
self.updatePageLayout = QVBoxLayout(self.tabUpdate)
|
||||
self.updatePageLayout.setSpacing(12)
|
||||
self.updatePageLayout.setObjectName(u"updatePageLayout")
|
||||
self.updateFrame = QFrame(self.tabUpdate)
|
||||
self.updateFrame.setObjectName(u"updateFrame")
|
||||
self.updateFrame.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.updateFrame.setFrameShadow(QFrame.Shadow.Raised)
|
||||
self.updateFrameLayout = QVBoxLayout(self.updateFrame)
|
||||
self.updateFrameLayout.setSpacing(12)
|
||||
self.updateFrameLayout.setObjectName(u"updateFrameLayout")
|
||||
self.updateFrameLayout.setContentsMargins(18, 18, 18, 18)
|
||||
self.updateInfoText = QLabel(self.updateFrame)
|
||||
self.updateInfoText.setObjectName(u"updateInfoText")
|
||||
self.updateInfoText.setWordWrap(True)
|
||||
|
||||
self.updateFrameLayout.addWidget(self.updateInfoText)
|
||||
|
||||
self.updateSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||
|
||||
self.updateFrameLayout.addItem(self.updateSpacer)
|
||||
|
||||
self.updateButtonsLayout = QHBoxLayout()
|
||||
self.updateButtonsLayout.setSpacing(12)
|
||||
self.updateButtonsLayout.setObjectName(u"updateButtonsLayout")
|
||||
self.installButton = QPushButton(self.updateFrame)
|
||||
self.installButton.setObjectName(u"installButton")
|
||||
|
||||
self.updateButtonsLayout.addWidget(self.installButton)
|
||||
|
||||
self.checkUpdatesButton = QPushButton(self.updateFrame)
|
||||
self.checkUpdatesButton.setObjectName(u"checkUpdatesButton")
|
||||
|
||||
self.updateButtonsLayout.addWidget(self.checkUpdatesButton)
|
||||
|
||||
|
||||
self.updateFrameLayout.addLayout(self.updateButtonsLayout)
|
||||
|
||||
|
||||
self.updatePageLayout.addWidget(self.updateFrame)
|
||||
|
||||
self.tabWidget.addTab(self.tabUpdate, "")
|
||||
self.tabSkills = QWidget()
|
||||
self.tabSkills.setObjectName(u"tabSkills")
|
||||
self.skillsLayout = QVBoxLayout(self.tabSkills)
|
||||
self.skillsLayout.setObjectName(u"skillsLayout")
|
||||
self.skillsComingSoon = QLabel(self.tabSkills)
|
||||
self.skillsComingSoon.setObjectName(u"skillsComingSoon")
|
||||
|
||||
self.skillsLayout.addWidget(self.skillsComingSoon)
|
||||
|
||||
self.tabWidget.addTab(self.tabSkills, "")
|
||||
self.tabSsh = QWidget()
|
||||
self.tabSsh.setObjectName(u"tabSsh")
|
||||
self.sshLayout = QVBoxLayout(self.tabSsh)
|
||||
self.sshLayout.setObjectName(u"sshLayout")
|
||||
self.sshComingSoon = QLabel(self.tabSsh)
|
||||
self.sshComingSoon.setObjectName(u"sshComingSoon")
|
||||
|
||||
self.sshLayout.addWidget(self.sshComingSoon)
|
||||
|
||||
self.tabWidget.addTab(self.tabSsh, "")
|
||||
self.tabFtp = QWidget()
|
||||
self.tabFtp.setObjectName(u"tabFtp")
|
||||
self.ftpLayout = QVBoxLayout(self.tabFtp)
|
||||
self.ftpLayout.setObjectName(u"ftpLayout")
|
||||
self.ftpComingSoon = QLabel(self.tabFtp)
|
||||
self.ftpComingSoon.setObjectName(u"ftpComingSoon")
|
||||
|
||||
self.ftpLayout.addWidget(self.ftpComingSoon)
|
||||
|
||||
self.tabWidget.addTab(self.tabFtp, "")
|
||||
self.tabStatus = QWidget()
|
||||
self.tabStatus.setObjectName(u"tabStatus")
|
||||
self.statusLayout = QVBoxLayout(self.tabStatus)
|
||||
self.statusLayout.setSpacing(10)
|
||||
self.statusLayout.setObjectName(u"statusLayout")
|
||||
self.statusRow = QHBoxLayout()
|
||||
self.statusRow.setSpacing(10)
|
||||
self.statusRow.setObjectName(u"statusRow")
|
||||
self.statusDot = QLabel(self.tabStatus)
|
||||
self.statusDot.setObjectName(u"statusDot")
|
||||
self.statusDot.setMinimumSize(QSize(10, 10))
|
||||
self.statusDot.setMaximumSize(QSize(10, 10))
|
||||
|
||||
self.statusRow.addWidget(self.statusDot)
|
||||
|
||||
self.statusText = QLabel(self.tabStatus)
|
||||
self.statusText.setObjectName(u"statusText")
|
||||
self.statusText.setWordWrap(True)
|
||||
|
||||
self.statusRow.addWidget(self.statusText)
|
||||
|
||||
|
||||
self.statusLayout.addLayout(self.statusRow)
|
||||
|
||||
self.tabWidget.addTab(self.tabStatus, "")
|
||||
self.tabRobotOs = QWidget()
|
||||
self.tabRobotOs.setObjectName(u"tabRobotOs")
|
||||
self.robotOsLayout = QVBoxLayout(self.tabRobotOs)
|
||||
self.robotOsLayout.setObjectName(u"robotOsLayout")
|
||||
self.robotOsComingSoon = QLabel(self.tabRobotOs)
|
||||
self.robotOsComingSoon.setObjectName(u"robotOsComingSoon")
|
||||
|
||||
self.robotOsLayout.addWidget(self.robotOsComingSoon)
|
||||
|
||||
self.tabWidget.addTab(self.tabRobotOs, "")
|
||||
|
||||
self.rootLayout.addWidget(self.tabWidget)
|
||||
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.statusbar = QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
self.statusbar.setSizeGripEnabled(False)
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
|
||||
|
||||
QMetaObject.connectSlotsByName(MainWindow)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Jibo Tools", None))
|
||||
self.configTitle.setText(QCoreApplication.translate("MainWindow", u"Config", None))
|
||||
self.groupPreview.setTitle(QCoreApplication.translate("MainWindow", u"Preview", None))
|
||||
self.labelOverride.setText(QCoreApplication.translate("MainWindow", u"Override", None))
|
||||
self.overrideCheck.setText("")
|
||||
self.labelPreviewConnected.setText(QCoreApplication.translate("MainWindow", u"Connected", None))
|
||||
self.previewConnectedCheck.setText("")
|
||||
self.groupHomeAssistant.setTitle(QCoreApplication.translate("MainWindow", u"Home Assistant", None))
|
||||
self.labelHaEnable.setText(QCoreApplication.translate("MainWindow", u"Enabled", None))
|
||||
self.haEnableCheck.setText("")
|
||||
self.labelHaServerIp.setText(QCoreApplication.translate("MainWindow", u"Server IP", None))
|
||||
self.haServerIpField.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Home Assistant host", None))
|
||||
self.groupAiProvider.setTitle(QCoreApplication.translate("MainWindow", u"AI Provider", None))
|
||||
self.labelAiEnable.setText(QCoreApplication.translate("MainWindow", u"Enabled", None))
|
||||
self.aiEnableCheck.setText("")
|
||||
self.labelAiProvider.setText(QCoreApplication.translate("MainWindow", u"Provider", None))
|
||||
self.labelAiEndpoint.setText(QCoreApplication.translate("MainWindow", u"API endpoint", None))
|
||||
self.aiEndpointField.setPlaceholderText(QCoreApplication.translate("MainWindow", u"http://...", None))
|
||||
self.labelAiKey.setText(QCoreApplication.translate("MainWindow", u"API key", None))
|
||||
self.labelTokens.setText(QCoreApplication.translate("MainWindow", u"Tokens used", None))
|
||||
self.tokensUsedLabel.setText(QCoreApplication.translate("MainWindow", u"-1", None))
|
||||
self.TryToConnect.setText(QCoreApplication.translate("MainWindow", u"Connect", None))
|
||||
self.JiboIpField.setInputMask("")
|
||||
self.JiboIpField.setText("")
|
||||
self.JiboIpField.setPlaceholderText(QCoreApplication.translate("MainWindow", u"e.g 192.168.1.54", None))
|
||||
self.jiboTitle.setText(QCoreApplication.translate("MainWindow", u"Connect Your Jibo", None))
|
||||
self.jiboImage.setText("")
|
||||
self.RobotSettings.setText(QCoreApplication.translate("MainWindow", u"Robot Settings", None))
|
||||
self.comboBox.setCurrentText(QCoreApplication.translate("MainWindow", u"Reboot", None))
|
||||
self.comboBox.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Reboot", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabJibo), QCoreApplication.translate("MainWindow", u"Jibo", None))
|
||||
self.updateInfoText.setText(QCoreApplication.translate("MainWindow", u"Installer and updater remain available via CLI. Use the buttons below to launch their GUIs.", None))
|
||||
self.installButton.setText(QCoreApplication.translate("MainWindow", u"Install", None))
|
||||
self.checkUpdatesButton.setText(QCoreApplication.translate("MainWindow", u"Check for updates", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabUpdate), QCoreApplication.translate("MainWindow", u"Update", None))
|
||||
self.skillsComingSoon.setText(QCoreApplication.translate("MainWindow", u"Coming soon.", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabSkills), QCoreApplication.translate("MainWindow", u"Skills", None))
|
||||
self.sshComingSoon.setText(QCoreApplication.translate("MainWindow", u"Coming soon.", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabSsh), QCoreApplication.translate("MainWindow", u"SSH", None))
|
||||
self.ftpComingSoon.setText(QCoreApplication.translate("MainWindow", u"Coming soon.", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabFtp), QCoreApplication.translate("MainWindow", u"FTP", None))
|
||||
self.statusDot.setText("")
|
||||
self.statusText.setText(QCoreApplication.translate("MainWindow", u"No Jibo IP configured", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabStatus), QCoreApplication.translate("MainWindow", u"Status", None))
|
||||
self.robotOsComingSoon.setText(QCoreApplication.translate("MainWindow", u"Coming soon.", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabRobotOs), QCoreApplication.translate("MainWindow", u"Robot OS", None))
|
||||
# retranslateUi
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,37 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import QUrl
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
|
||||
from .process_runner import ProcessRunner, resolve_python
|
||||
from .terminal_helper import TerminalHelper
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QGuiApplication(sys.argv)
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
|
||||
runner = ProcessRunner()
|
||||
terminal = TerminalHelper()
|
||||
engine.rootContext().setContextProperty("runner", runner)
|
||||
engine.rootContext().setContextProperty("terminal", terminal)
|
||||
engine.rootContext().setContextProperty("pyExec", resolve_python())
|
||||
engine.rootContext().setContextProperty("toolScript", "jibo_automod.py")
|
||||
engine.rootContext().setContextProperty("toolTitle", "Installer")
|
||||
|
||||
qml_path = Path(__file__).resolve().parent / "qml" / "ToolRunner.qml"
|
||||
engine.load(QUrl.fromLocalFile(str(qml_path)))
|
||||
|
||||
if not engine.rootObjects():
|
||||
return 1
|
||||
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,59 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import QUrl, QObject, Slot
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
|
||||
from .process_runner import ConnectionMonitor, resolve_python, resolve_python_invocation
|
||||
|
||||
|
||||
class Launcher(QObject):
|
||||
def __init__(self, python_program: str, python_prefix: list[str]) -> None:
|
||||
super().__init__()
|
||||
self._python_program = python_program
|
||||
self._python_prefix = list(python_prefix)
|
||||
|
||||
@Slot()
|
||||
def launchInstaller(self) -> None:
|
||||
# Start installer GUI in a separate process.
|
||||
import subprocess
|
||||
subprocess.Popen(
|
||||
[self._python_program, *self._python_prefix, "-m", "gui.installer_gui"],
|
||||
cwd=str(Path(__file__).resolve().parents[1]),
|
||||
)
|
||||
|
||||
@Slot()
|
||||
def launchUpdater(self) -> None:
|
||||
import subprocess
|
||||
subprocess.Popen(
|
||||
[self._python_program, *self._python_prefix, "-m", "gui.updater_gui"],
|
||||
cwd=str(Path(__file__).resolve().parents[1]),
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QGuiApplication(sys.argv)
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
|
||||
conn = ConnectionMonitor()
|
||||
py_program, py_prefix = resolve_python_invocation()
|
||||
py_exec = resolve_python()
|
||||
engine.rootContext().setContextProperty("conn", conn)
|
||||
engine.rootContext().setContextProperty("pyExec", py_exec)
|
||||
engine.rootContext().setContextProperty("launcher", Launcher(py_program, py_prefix))
|
||||
|
||||
qml_path = Path(__file__).resolve().parent / "qml" / "MainPanel.qml"
|
||||
engine.load(QUrl.fromLocalFile(str(qml_path)))
|
||||
|
||||
if not engine.rootObjects():
|
||||
return 1
|
||||
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,168 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
ApplicationWindow {
|
||||
id: win
|
||||
width: 880
|
||||
height: 520
|
||||
visible: true
|
||||
title: "Jibo Tools"
|
||||
|
||||
property string host: hostField.text.trim()
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 14
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 12
|
||||
|
||||
Text {
|
||||
text: "Connection"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 10
|
||||
height: 10
|
||||
radius: 5
|
||||
color: conn.connected ? "#2ecc71" : (host.length > 0 ? "#e67e22" : "#bdc3c7")
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: conn.connected ? "SSH reachable" : (host.length > 0 ? "Not reachable" : "No IP")
|
||||
color: "#555"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
TextField {
|
||||
id: hostField
|
||||
placeholderText: "Jibo IP (e.g. 192.168.1.50)"
|
||||
Layout.preferredWidth: 280
|
||||
onTextChanged: conn.host = text
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 16
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 320
|
||||
Layout.fillHeight: true
|
||||
radius: 14
|
||||
color: "#f6f6f6"
|
||||
border.color: "#e4e4e4"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: "Your Jibo"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Rectangle {
|
||||
id: jiboCard
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
width: 240
|
||||
height: 240
|
||||
radius: 18
|
||||
color: "#ffffff"
|
||||
border.color: "#e4e4e4"
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 200
|
||||
height: 200
|
||||
source: "../assets/jibo.svg"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
// Best-effort: open Chrome remote devices page.
|
||||
Qt.openUrlExternally("chrome://inspect/#devices")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Text {
|
||||
text: "Click Jibo to open chrome://inspect"
|
||||
color: "#666"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: 14
|
||||
color: "#f6f6f6"
|
||||
border.color: "#e4e4e4"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 12
|
||||
|
||||
Text {
|
||||
text: "Actions"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Installer and updater remain available via CLI.\nUse the buttons below to launch their GUIs."
|
||||
color: "#555"
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 12
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Install"
|
||||
enabled: true
|
||||
onClicked: {
|
||||
launcher.launchInstaller()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Check for updates"
|
||||
enabled: true
|
||||
onClicked: {
|
||||
launcher.launchUpdater()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
ApplicationWindow {
|
||||
id: win
|
||||
width: 900
|
||||
height: 560
|
||||
visible: true
|
||||
title: (typeof toolTitle === "string" ? toolTitle : "Tool")
|
||||
|
||||
property string script: (typeof toolScript === "string" ? toolScript : "")
|
||||
property bool isUpdater: script.indexOf("jibo_updater.py") >= 0
|
||||
|
||||
function buildArgs() {
|
||||
var args = []
|
||||
args.push(script)
|
||||
|
||||
if (isUpdater) {
|
||||
var h = hostField.text.trim()
|
||||
if (h.length > 0) {
|
||||
args.push("--ip")
|
||||
args.push(h)
|
||||
}
|
||||
}
|
||||
|
||||
var extra = extraArgs.text.trim()
|
||||
if (extra.length > 0) {
|
||||
// naive split (keeps GUI minimal)
|
||||
var parts = extra.split(/\s+/)
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
if (parts[i].length > 0) args.push(parts[i])
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 12
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: title
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Button {
|
||||
text: runner.running ? "Stop" : "Start"
|
||||
onClicked: {
|
||||
if (runner.running) {
|
||||
runner.stop()
|
||||
} else {
|
||||
runner.start(pyExec, buildArgs())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Open in terminal"
|
||||
enabled: !runner.running
|
||||
onClicked: {
|
||||
terminal.openTerminal(pyExec, buildArgs())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
TextField {
|
||||
id: hostField
|
||||
visible: isUpdater
|
||||
placeholderText: "Jibo IP (required for updater)"
|
||||
Layout.preferredWidth: 260
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: extraArgs
|
||||
placeholderText: "Extra arguments (optional)"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: 12
|
||||
color: "#0f0f0f"
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
clip: true
|
||||
|
||||
TextArea {
|
||||
id: log
|
||||
readOnly: true
|
||||
wrapMode: TextArea.Wrap
|
||||
color: "#e8e8e8"
|
||||
font.family: "monospace"
|
||||
background: null
|
||||
text: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: runner.running ? "Running..." : (runner.exitCode >= 0 ? ("Exit: " + runner.exitCode) : "Idle")
|
||||
color: "#666"
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Button {
|
||||
text: "Clear log"
|
||||
onClicked: log.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: runner
|
||||
function onOutputAppended(chunk) {
|
||||
log.text += chunk
|
||||
log.cursorPosition = log.length
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import QUrl
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtQml import QQmlApplicationEngine
|
||||
|
||||
from .process_runner import ProcessRunner, resolve_python
|
||||
from .terminal_helper import TerminalHelper
|
||||
|
||||
|
||||
def main() -> int:
|
||||
app = QGuiApplication(sys.argv)
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
|
||||
runner = ProcessRunner()
|
||||
terminal = TerminalHelper()
|
||||
engine.rootContext().setContextProperty("runner", runner)
|
||||
engine.rootContext().setContextProperty("terminal", terminal)
|
||||
engine.rootContext().setContextProperty("pyExec", resolve_python())
|
||||
engine.rootContext().setContextProperty("toolScript", "jibo_updater.py")
|
||||
engine.rootContext().setContextProperty("toolTitle", "Updater")
|
||||
|
||||
qml_path = Path(__file__).resolve().parent / "qml" / "ToolRunner.qml"
|
||||
engine.load(QUrl.fromLocalFile(str(qml_path)))
|
||||
|
||||
if not engine.rootObjects():
|
||||
return 1
|
||||
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user