mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-06 03:31:41 +00:00
UI bug fixes and feature refinements (#59)
* Rework settings page; touch up contour, output, and 3D tabs; font sizing No stream placeholder; driver mode refined; cameras page Make settings snackbar work Lint fix Fix settings page padding Actually hide settings fields if unsupported * Make toggle buttons less confusing; fix driver toggle; form validation * Make eyedropper work and make input/select styling more consistent * Fix color picker and tabbing bugs * Set up camera and settings pages to talk to the backend * Add auto reconnect * Add lots of tooltips and improve related thematic consistency * Only show output stream while color picking * Unbreak robot offset * Increase tooltip delay and refactor tooltip label into a component * Remove toggle button switching behavior * Fix PnP tab and add a flag to disable FOV configuration * Move FPS indicator * Make GPU acceleration status use one value in the store * Only allow IPv4 static IPs and remove accidentally committed index
This commit is contained in:
committed by
GitHub
parent
0b98dc3c9f
commit
19b57235fe
@@ -11,42 +11,58 @@
|
||||
>
|
||||
<v-col
|
||||
cols="12"
|
||||
:class="['pb-3 ', $store.getters.isDriverMode ? '' : 'pr-lg-3']"
|
||||
:lg="$store.getters.isDriverMode ? 12 : 8"
|
||||
:class="['pb-3 ', 'pr-lg-3']"
|
||||
lg="8"
|
||||
align-self="stretch"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
height="100%"
|
||||
style="display: flex; flex-direction: column"
|
||||
dark
|
||||
>
|
||||
<v-card-title
|
||||
class="pb-0 mb-0 pl-4 pt-1"
|
||||
style="height: 10%;"
|
||||
style="height: 15%; min-height: 50px;"
|
||||
>
|
||||
Cameras
|
||||
<div>
|
||||
Cameras <span
|
||||
class="pl-2 caption grey--text text--lighten-2"
|
||||
style="line-height: 220%; display: inline-block; vertical-align: bottom;"
|
||||
>{{ parseFloat(fps).toFixed(2) }} FPS</span>
|
||||
</div>
|
||||
<v-switch
|
||||
v-model="driverMode"
|
||||
label="Driver Mode"
|
||||
style="margin-left: auto;"
|
||||
color="accent"
|
||||
/>
|
||||
</v-card-title>
|
||||
<v-row
|
||||
align="center"
|
||||
style="height: 90%;"
|
||||
>
|
||||
<v-col
|
||||
v-for="idx in (selectedOutputs instanceof Array ? selectedOutputs : [selectedOutputs])"
|
||||
:key="idx"
|
||||
cols="12"
|
||||
:md="selectedOutputs.length === 1 ? 12 : Math.floor(12 / selectedOutputs.length)"
|
||||
class="pb-0 pt-0"
|
||||
style="height: 100%;"
|
||||
>
|
||||
<div style="position: relative; width: 100%; height: 100%;">
|
||||
<cvImage
|
||||
:id="idx === 0 ? 'normal-stream' : ''"
|
||||
:address="$store.getters.streamAddress[idx]"
|
||||
:disconnected="!$store.state.backendConnected"
|
||||
scale="100"
|
||||
max-height="300px"
|
||||
max-height-md="320px"
|
||||
max-height-xl="450px"
|
||||
:max-height="$store.getters.isDriverMode ? '40vh' : '300px'"
|
||||
:max-height-md="$store.getters.isDriverMode ? '50vh' : '320px'"
|
||||
:max-height-xl="$store.getters.isDriverMode ? '60vh' : '450px'"
|
||||
:alt="'Stream' + idx"
|
||||
:color-picking="$store.state.colorPicking && idx == 0"
|
||||
@click="onImageClick"
|
||||
/>
|
||||
<span style="position: absolute; top: 2%; left: 2%; font-size: 28px; -webkit-text-stroke: 1px black;">{{ parseFloat(fps).toFixed(2) }}</span>
|
||||
<!-- <span class="fps-indicator">{{ parseFloat(fps).toFixed(2) }}</span>-->
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -55,7 +71,7 @@
|
||||
<v-col
|
||||
cols="12"
|
||||
class="pb-3"
|
||||
:lg="$store.getters.isDriverMode ? 12 : 4"
|
||||
lg="4"
|
||||
align-self="stretch"
|
||||
>
|
||||
<v-card
|
||||
@@ -64,7 +80,7 @@
|
||||
<camera-and-pipeline-select />
|
||||
</v-card>
|
||||
<v-card
|
||||
v-if="!$store.getters.isDriverMode"
|
||||
:disabled="$store.getters.isDriverMode || $store.state.colorPicking"
|
||||
class="mt-3"
|
||||
color="primary"
|
||||
>
|
||||
@@ -82,14 +98,18 @@
|
||||
dark
|
||||
class="fill"
|
||||
>
|
||||
<v-btn color="secondary">
|
||||
<v-icon>mdi-cube-outline</v-icon>
|
||||
<span>3D</span>
|
||||
</v-btn>
|
||||
<v-btn color="secondary">
|
||||
<v-btn
|
||||
color="secondary"
|
||||
>
|
||||
<v-icon>mdi-crop-square</v-icon>
|
||||
<span>2D</span>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="secondary"
|
||||
>
|
||||
<v-icon>mdi-cube-outline</v-icon>
|
||||
<span>3D</span>
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
</v-col>
|
||||
<v-col lg="12">
|
||||
@@ -146,7 +166,7 @@
|
||||
slider-color="accent"
|
||||
>
|
||||
<v-tab
|
||||
v-for="(tab, i) in tabs.filter(it => it.name !== '3D' || is3D)"
|
||||
v-for="(tab, i) in tabs.filter(it => it.name !== '3D' || $store.getters.currentPipelineSettings.is3D)"
|
||||
:key="i"
|
||||
>
|
||||
{{ tab.name }}
|
||||
@@ -154,12 +174,10 @@
|
||||
</v-tabs>
|
||||
<div class="pl-4 pr-4 pt-2">
|
||||
<keep-alive>
|
||||
<!-- vision component -->
|
||||
<component
|
||||
:is="(tabs[selectedTabs[idx]] || tabs[0]).component"
|
||||
ref="component"
|
||||
:ref="(tabs[selectedTabs[idx]] || tabs[0]).name"
|
||||
v-model="$store.getters.pipeline"
|
||||
:is3d="is3D"
|
||||
@update="$emit('save')"
|
||||
/>
|
||||
</keep-alive>
|
||||
@@ -211,12 +229,20 @@
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTabs: [0, 0, 0, 0],
|
||||
selectedTabsData: [0, 0, 0, 0],
|
||||
snackbar: false,
|
||||
is3D: false,
|
||||
counterData: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedTabs: {
|
||||
get() {
|
||||
return this.$store.getters.isDriverMode ? [0] : this.selectedTabsData;
|
||||
},
|
||||
set(value) {
|
||||
this.selectedTabsData = value;
|
||||
}
|
||||
},
|
||||
tabGroups: {
|
||||
get() {
|
||||
let tabs = {
|
||||
@@ -248,10 +274,10 @@
|
||||
|
||||
// 2D array of tab names and component names; each sub-array is a separate tab group
|
||||
let ret = [];
|
||||
if (this.$vuetify.breakpoint.smAndDown || !this.$store.state.compactMode || this.$store.getters.isDriverMode) {
|
||||
if (this.$vuetify.breakpoint.smAndDown || this.$store.getters.isDriverMode || (this.$vuetify.breakpoint.mdAndDown && !this.$store.state.compactMode)) {
|
||||
// One big tab group with all the tabs
|
||||
ret[0] = Object.values(tabs);
|
||||
} else if (this.$vuetify.breakpoint.mdAndDown) {
|
||||
} else if (this.$vuetify.breakpoint.mdAndDown || !this.$store.state.compactMode) {
|
||||
// Two tab groups, one with "input, threshold, contours, output" and the other with "target info, 3D"
|
||||
ret[0] = [tabs.input, tabs.threshold, tabs.contours, tabs.output];
|
||||
ret[1] = [tabs.targets, tabs.pnp];
|
||||
@@ -273,10 +299,20 @@
|
||||
},
|
||||
processingMode: {
|
||||
get() {
|
||||
return this.is3D ? 0 : 1;
|
||||
return this.$store.getters.currentPipelineSettings.is3D ? 1 : 0;
|
||||
},
|
||||
set(value) {
|
||||
this.is3D = value === 0;
|
||||
this.$store.getters.currentPipelineSettings.is3D = value === 1;
|
||||
this.handlePipelineUpdate("is3D", value === 1);
|
||||
}
|
||||
},
|
||||
driverMode: {
|
||||
get() {
|
||||
return this.$store.getters.isDriverMode;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.getters.currentCameraSettings.currentPipelineIndex = value ? -1 : 0;
|
||||
this.handleInputWithIndex('currentPipeline', value ? -1 : 0);
|
||||
}
|
||||
},
|
||||
selectedOutputs: {
|
||||
@@ -284,7 +320,9 @@
|
||||
get() {
|
||||
// We switch the selector to single-select only on sm-and-down size devices, so we have to return a Number instead of an Array in that state
|
||||
let ret;
|
||||
if (!this.$store.getters.isDriverMode) {
|
||||
if (this.$store.state.colorPicking) {
|
||||
ret = [0]; // We want the input stream only while color picking
|
||||
} else if (!this.$store.getters.isDriverMode) {
|
||||
ret = this.$store.state.selectedOutputs || [0];
|
||||
} else {
|
||||
ret = [1]; // We want the output stream in driver mode
|
||||
@@ -324,9 +362,12 @@
|
||||
},
|
||||
methods: {
|
||||
onImageClick(event) {
|
||||
if (this.selectedTab === 1) {
|
||||
this.$refs.component.onClick(event);
|
||||
}
|
||||
// Only run on the input stream
|
||||
if (event.target.alt !== "Stream0") return;
|
||||
// Get a reference to the threshold tab (if it is shown) and call its "onClick" method
|
||||
let ref = this.$refs["Threshold"];
|
||||
if (ref && ref[0])
|
||||
ref[0].onClick(event)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -343,12 +384,12 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.colsClass {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.videoClass {
|
||||
text-align: center;
|
||||
.fps-indicator {
|
||||
position: absolute;
|
||||
top: 2%;
|
||||
left: 2%;
|
||||
font-size: 1.75rem;
|
||||
text-shadow: 1px 1px 5px rgba(1, 1, 1, 0.65);
|
||||
}
|
||||
|
||||
th {
|
||||
|
||||
Reference in New Issue
Block a user