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:
Declan Freeman-Gleason
2020-07-31 13:50:50 -07:00
committed by GitHub
parent 0b98dc3c9f
commit 19b57235fe
34 changed files with 1099 additions and 566 deletions

View File

@@ -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 {