mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-03 03:01:40 +00:00
UI Rebrand (#1)
* Rebrand UI and increase responsiveness * Fix typo in PipelineViews and SettingsViews directory name * Inset FPS indicator in stream preview * Rebrand favicon
This commit is contained in:
committed by
GitHub
parent
3e8f3736af
commit
895edb988a
343
photon-client/src/views/SettingsViews/Cameras.vue
Normal file
343
photon-client/src/views/SettingsViews/Cameras.vue
Normal file
@@ -0,0 +1,343 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<CVselect
|
||||
v-model="currentCameraIndex"
|
||||
name="Camera"
|
||||
:list="$store.getters.cameraList"
|
||||
@input="handleInput('currentCamera',currentCameraIndex)"
|
||||
/>
|
||||
<CVnumberinput
|
||||
v-model="cameraSettings.fov"
|
||||
name="Diagonal FOV"
|
||||
/>
|
||||
<br>
|
||||
<CVnumberinput
|
||||
v-model="cameraSettings.tilt"
|
||||
name="Camera pitch"
|
||||
:step="0.01"
|
||||
/>
|
||||
<br>
|
||||
<v-btn
|
||||
style="margin-top:10px"
|
||||
small
|
||||
color="#ffd843"
|
||||
@click="sendCameraSettings"
|
||||
>
|
||||
Save Camera Settings
|
||||
</v-btn>
|
||||
</div>
|
||||
<div style="margin-top: 15px">
|
||||
<span>3D Calibration</span>
|
||||
<v-divider
|
||||
color="white"
|
||||
style="margin-bottom: 10px"
|
||||
/>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<CVselect
|
||||
v-model="resolutionIndex"
|
||||
name="Resolution"
|
||||
:list="stringResolutionList"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<CVnumberinput
|
||||
v-model="squareSize"
|
||||
name="Square Size (in)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-btn
|
||||
small
|
||||
:color="calibrationModeButton.color"
|
||||
:disabled="checkResolution"
|
||||
@click="sendCalibrationMode"
|
||||
>
|
||||
{{ calibrationModeButton.text }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
small
|
||||
:color="cancellationModeButton.color"
|
||||
:disabled="checkCancellation"
|
||||
@click="sendCalibrationFinish"
|
||||
>
|
||||
{{ cancellationModeButton.text }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-btn
|
||||
color="whitesmoke"
|
||||
small
|
||||
@click="downloadBoard"
|
||||
>
|
||||
Download Checkerboard
|
||||
</v-btn>
|
||||
<a
|
||||
ref="calibrationFile"
|
||||
style="color: black; text-decoration: none; display: none"
|
||||
:href="require('../../assets/chessboard.png')"
|
||||
download="Calibration Board.png"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="isCalibrating">
|
||||
<v-col>
|
||||
<span>Snapshot Amount: {{ snapshotAmount }}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<div v-if="isCalibrating">
|
||||
<v-checkbox
|
||||
v-model="isAdvanced"
|
||||
label="Advanced Menu"
|
||||
dark
|
||||
/>
|
||||
<div v-if="isAdvanced">
|
||||
<CVslider
|
||||
v-model="$store.getters.pipeline.exposure"
|
||||
name="Exposure"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@input="e=> handleInput('exposure', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-model="$store.getters.pipeline.brightness"
|
||||
name="Brightness"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@input="e=> handleInput('brightness', e)"
|
||||
/>
|
||||
<CVslider
|
||||
v-if="$store.getters.pipeline.gain !== -1"
|
||||
v-model="$store.getters.pipeline.gain"
|
||||
name="Gain"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@input="e=> handleInput('gain', e)"
|
||||
/>
|
||||
<CVselect
|
||||
v-model="$store.getters.pipeline.videoModeIndex"
|
||||
name="FPS"
|
||||
:list="stringFpsList"
|
||||
@input="changeFps"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-snackbar
|
||||
v-model="snack"
|
||||
top
|
||||
:color="snackbar.color"
|
||||
>
|
||||
<span>{{ snackbar.text }}</span>
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVselect from '../../components/common/cv-select'
|
||||
import CVnumberinput from '../../components/common/cv-number-input'
|
||||
import CVslider from '../../components/common/cv-slider'
|
||||
|
||||
export default {
|
||||
name: 'CameraSettings',
|
||||
components: {
|
||||
CVselect,
|
||||
CVnumberinput,
|
||||
CVslider
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isCalibrating: false,
|
||||
resolutionIndex: undefined,
|
||||
calibrationModeButton: {
|
||||
text: "Start Calibration",
|
||||
color: "green"
|
||||
},
|
||||
cancellationModeButton: {
|
||||
text: "Cancel Calibration",
|
||||
color: "red"
|
||||
},
|
||||
snackbar: {
|
||||
color: "success",
|
||||
text: ""
|
||||
},
|
||||
squareSize: 1.0,
|
||||
snapshotAmount: 0,
|
||||
hasEnough: false,
|
||||
snack: false,
|
||||
isAdvanced: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
checkResolution() {
|
||||
return this.resolutionIndex === undefined;
|
||||
},
|
||||
checkCancellation() {
|
||||
if (this.isCalibrating) {
|
||||
return false
|
||||
} else if (this.checkResolution) {
|
||||
return true;
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
currentCameraIndex: {
|
||||
get() {
|
||||
return this.$store.state.currentCameraIndex;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('currentCameraIndex', value);
|
||||
}
|
||||
},
|
||||
filteredResolutionList: {
|
||||
get() {
|
||||
let tmp_list = [];
|
||||
for (let i in this.$store.state.resolutionList) {
|
||||
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
|
||||
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
|
||||
if (!tmp_list.some(e => e.width === res.width && e.height === res.height)) {
|
||||
res['actualIndex'] = parseInt(i);
|
||||
tmp_list.push(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp_list;
|
||||
}
|
||||
},
|
||||
filteredFpsList() {
|
||||
let selectedRes = this.$store.state.resolutionList[this.resolutionIndex];
|
||||
let tmpList = [];
|
||||
for (let i in this.$store.state.resolutionList) {
|
||||
if (this.$store.state.resolutionList.hasOwnProperty(i)) {
|
||||
let res = JSON.parse(JSON.stringify(this.$store.state.resolutionList[i]));
|
||||
if (!tmpList.some(e => e['fps'] === res['fps'])) {
|
||||
if (res.width === selectedRes.width && res.height === selectedRes.height) {
|
||||
res['actualIndex'] = parseInt(i);
|
||||
tmpList.push(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmpList;
|
||||
},
|
||||
stringFpsList() {
|
||||
let tmp = [];
|
||||
for (let i of this.filteredFpsList) {
|
||||
tmp.push(i['fps']);
|
||||
}
|
||||
return tmp;
|
||||
},
|
||||
stringResolutionList: {
|
||||
get() {
|
||||
let tmp = [];
|
||||
for (let i of this.filteredResolutionList) {
|
||||
tmp.push(`${i['width']} X ${i['height']}`)
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
},
|
||||
cameraSettings: {
|
||||
get() {
|
||||
return this.$store.getters.cameraSettings;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('cameraSettings', value);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
downloadBoard() {
|
||||
this.axios.get("http://" + this.$address + require('../../assets/chessboard.png'), {responseType: 'blob'}).then((response) => {
|
||||
require('downloadjs')(response.data, "Calibration Board", "image/png")
|
||||
})
|
||||
},
|
||||
changeFps() {
|
||||
this.handleInput('videoModeIndex', this.filteredFpsList[this.$store.getters.pipeline['videoModeIndex']]['actualIndex']);
|
||||
},
|
||||
sendCameraSettings() {
|
||||
const self = this;
|
||||
this.axios.post("http://" + this.$address + "/api/settings/camera", this.cameraSettings).then(
|
||||
function (response) {
|
||||
if (response.status === 200) {
|
||||
self.$store.state.saveBar = true;
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
sendCalibrationMode() {
|
||||
const self = this;
|
||||
let data = {};
|
||||
let connection_string = "/api/settings/";
|
||||
if (self.isCalibrating === true) {
|
||||
connection_string += "snapshot"
|
||||
} else {
|
||||
connection_string += "startCalibration";
|
||||
data['resolution'] = this.filteredResolutionList[this.resolutionIndex].actualIndex;
|
||||
data['squareSize'] = this.squareSize;
|
||||
self.hasEnough = false;
|
||||
}
|
||||
this.axios.post("http://" + this.$address + connection_string, data).then(
|
||||
function (response) {
|
||||
if (response.status === 200) {
|
||||
if (self.isCalibrating) {
|
||||
self.snapshotAmount = response.data['snapshotCount'];
|
||||
self.hasEnough = response.data['hasEnough'];
|
||||
if (self.hasEnough === true) {
|
||||
self.cancellationModeButton.text = "Finish Calibration";
|
||||
self.cancellationModeButton.color = "green";
|
||||
}
|
||||
} else {
|
||||
self.calibrationModeButton.text = "Take Snapshot";
|
||||
self.isCalibrating = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
sendCalibrationFinish() {
|
||||
const self = this;
|
||||
let connection_string = "/api/settings/endCalibration";
|
||||
let data = {};
|
||||
data['squareSize'] = this.squareSize;
|
||||
self.axios.post("http://" + this.$address + connection_string, data).then((response) => {
|
||||
if (response.status === 200) {
|
||||
self.snackbar = {
|
||||
color: "success",
|
||||
text: "calibration successful. \n" +
|
||||
"accuracy: " + response.data['accuracy'].toFixed(5)
|
||||
};
|
||||
self.snack = true;
|
||||
}
|
||||
self.isCalibrating = false;
|
||||
self.hasEnough = false;
|
||||
self.snapshotAmount = 0;
|
||||
self.calibrationModeButton.text = "Start Calibration";
|
||||
self.cancellationModeButton.text = "Cancel Calibration";
|
||||
self.cancellationModeButton.color = "red";
|
||||
}
|
||||
).catch(() => {
|
||||
self.snackbar = {
|
||||
color: "error",
|
||||
text: "calibration failed"
|
||||
};
|
||||
self.snack = true;
|
||||
self.isCalibrating = false;
|
||||
self.hasEnough = false;
|
||||
self.snapshotAmount = 0;
|
||||
self.calibrationModeButton.text = "Start Calibration";
|
||||
self.cancellationModeButton.text = "Cancel Calibration";
|
||||
self.cancellationModeButton.color = "red";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
188
photon-client/src/views/SettingsViews/General.vue
Normal file
188
photon-client/src/views/SettingsViews/General.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-top: 15px">
|
||||
<span>General Settings:</span>
|
||||
<v-divider color="white" />
|
||||
</div>
|
||||
<CVnumberinput
|
||||
v-model="settings.teamNumber"
|
||||
name="Team Number"
|
||||
/>
|
||||
<CVradio
|
||||
v-model="settings.connectionType"
|
||||
:list="['DHCP','Static']"
|
||||
/>
|
||||
<v-divider color="white" />
|
||||
<CVinput
|
||||
v-model="settings.ip"
|
||||
name="IP"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
<CVinput
|
||||
v-model="settings.netmask"
|
||||
name="NetMask"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
<CVinput
|
||||
v-model="settings.gateway"
|
||||
name="Gateway"
|
||||
:disabled="isDisabled"
|
||||
/>
|
||||
<v-divider color="white" />
|
||||
<CVinput
|
||||
v-model="settings.hostname"
|
||||
name="Hostname"
|
||||
/>
|
||||
<v-btn
|
||||
style="margin-top:10px"
|
||||
small
|
||||
color="#ffd843"
|
||||
@click="sendGeneralSettings"
|
||||
>
|
||||
Save General Settings
|
||||
</v-btn>
|
||||
<div style="margin-top: 20px">
|
||||
<span>Install or Update:</span>
|
||||
<v-divider color="white" />
|
||||
</div>
|
||||
<div v-if="!isLoading">
|
||||
<v-row
|
||||
dense
|
||||
align="center"
|
||||
>
|
||||
<v-col :cols="3">
|
||||
<span>Choose a newer version: </span>
|
||||
</v-col>
|
||||
<v-col :cols="6">
|
||||
<v-file-input
|
||||
v-model="file"
|
||||
accept=".jar"
|
||||
dark
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-btn
|
||||
small
|
||||
@click="installOrUpdate"
|
||||
>
|
||||
{{ fileUploadText }}
|
||||
</v-btn>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
style="text-align: center; margin-top: 20px"
|
||||
>
|
||||
<v-progress-circular
|
||||
color="white"
|
||||
:indeterminate="true"
|
||||
size="32"
|
||||
width="4"
|
||||
/>
|
||||
<br>
|
||||
<span>Please wait this may take a while</span>
|
||||
</div>
|
||||
<v-snackbar
|
||||
v-model="snack"
|
||||
top
|
||||
:color="snackbar.color"
|
||||
>
|
||||
<span>{{ snackbar.text }}</span>
|
||||
</v-snackbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CVnumberinput from '../../components/common/cv-number-input'
|
||||
import CVradio from '../../components/common/cv-radio'
|
||||
import CVinput from '../../components/common/cv-input'
|
||||
|
||||
export default {
|
||||
name: 'General',
|
||||
components: {
|
||||
CVnumberinput,
|
||||
CVradio,
|
||||
CVinput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
file: undefined,
|
||||
snackbar: {
|
||||
color: "success",
|
||||
text: ""
|
||||
},
|
||||
snack: false,
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fileUploadText() {
|
||||
if (this.file !== undefined) {
|
||||
return "Update and run at startup"
|
||||
} else {
|
||||
return "Run current version at startup"
|
||||
}
|
||||
},
|
||||
isDisabled() {
|
||||
return this.settings.connectionType === 0;
|
||||
},
|
||||
settings: {
|
||||
get() {
|
||||
return this.$store.state.settings;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sendGeneralSettings() {
|
||||
const self = this;
|
||||
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings).then(
|
||||
function (response) {
|
||||
if (response.status === 200) {
|
||||
self.snackbar = {
|
||||
color: "success",
|
||||
text: "Save successful, Please restart for changes to take action"
|
||||
};
|
||||
self.snack = true;
|
||||
}
|
||||
},
|
||||
function (error) {
|
||||
self.snackbar = {
|
||||
color: "error",
|
||||
text: error.response.data
|
||||
};
|
||||
self.snack = true;
|
||||
}
|
||||
)
|
||||
},
|
||||
installOrUpdate() {
|
||||
let formData = new FormData();
|
||||
formData.append('file', this.file);
|
||||
if (this.file !== undefined) {
|
||||
this.isLoading = true;
|
||||
}
|
||||
this.axios.post("http://" + this.$address + "/api/install", formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}).then(() => {
|
||||
this.snackbar = {
|
||||
color: "success",
|
||||
text: "Installation successful"
|
||||
};
|
||||
this.isLoading = false;
|
||||
this.snack = true;
|
||||
}).catch(error => {
|
||||
this.snackbar = {
|
||||
color: "error",
|
||||
text: error.response.data
|
||||
};
|
||||
this.isLoading = false;
|
||||
this.snack = true;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="" scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user