이재빈

Add:0421

1 +[*.{js,jsx,ts,tsx,vue}]
2 +indent_style = space
3 +indent_size = 2
4 +trim_trailing_whitespace = true
5 +insert_final_newline = true
1 +.DS_Store
2 +node_modules
3 +/dist
4 +
5 +# local env files
6 +.env.local
7 +.env.*.local
8 +
9 +# Log files
10 +npm-debug.log*
11 +yarn-debug.log*
12 +yarn-error.log*
13 +
14 +# Editor directories and files
15 +.idea
16 +.vscode
17 +*.suo
18 +*.ntvs*
19 +*.njsproj
20 +*.sln
21 +*.sw?
22 +
23 +#Electron-builder output
24 +/dist_electron
...\ No newline at end of file ...\ No newline at end of file
1 +# image-labeller
2 +Crop된 명패 이미지 분류 작업을 도와주는 툴.
3 +
4 +### Build
5 +```
6 +yarn electron:build
7 +```
8 +dist_electron/win-unpacked 폴더에서 image-labeller.exe를 실행.
9 +
10 +### 사용 설명
11 +1. 좌측 사이드바에서 Settings 클릭.
12 +2. 결과물 CSV 파일의 이름을 지정. (저장 경로는 데이터셋이 위치한 Workspace 폴더)
13 +3. 데이터셋의 시작 파일 이름을 지정. (비어있으면 첫 파일부터 읽음)
14 +4. 데이터셋이 위치한 폴더를 선택.
15 +5. 좌측 사이드바에서 Main 클릭.
16 +6. 이미지 분류 작업 시작.
17 +
18 +* 단축키
19 + * 방향키 (좌/우) : 이전/다음 이미지 로딩
20 + * 숫자키 (0~9) : 호수 입력.
21 + * backspace / delete : 입력한 마지막 자리부터 제거.
22 + * s : CSV 파일로 저장.
23 +
24 +* 참고 사항
25 + * 실제로 라벨링은 숫자 6자리로 변환됨. (hasNum, digitLen, digit1, digit2, digit3, digit4)
26 + * 아무것도 입력하지 않고 다음 이미지 로딩 시 [0, 0, 10, 10, 10, 10]으로 자동 라벨링됨.
27 +![img1](./src/img1.png)
1 +module.exports = {
2 + presets: [
3 + '@vue/cli-plugin-babel/preset'
4 + ]
5 +}
1 +{
2 + "name": "image-labeller",
3 + "version": "0.1.0",
4 + "private": true,
5 + "scripts": {
6 + "serve": "vue-cli-service serve",
7 + "build": "vue-cli-service build",
8 + "lint": "vue-cli-service lint",
9 + "electron:build": "vue-cli-service electron:build",
10 + "electron:serve": "vue-cli-service electron:serve",
11 + "postinstall": "electron-builder install-app-deps",
12 + "postuninstall": "electron-builder install-app-deps"
13 + },
14 + "main": "background.js",
15 + "dependencies": {
16 + "core-js": "^3.6.4",
17 + "csv-writer": "^1.6.0",
18 + "vue": "^2.6.11",
19 + "vue-router": "^3.1.6",
20 + "vuetify": "^2.2.11",
21 + "vuex": "^3.1.3"
22 + },
23 + "devDependencies": {
24 + "@vue/cli-plugin-babel": "~4.3.0",
25 + "@vue/cli-plugin-eslint": "~4.3.0",
26 + "@vue/cli-plugin-router": "~4.3.0",
27 + "@vue/cli-plugin-vuex": "~4.3.0",
28 + "@vue/cli-service": "~4.3.0",
29 + "@vue/eslint-config-standard": "^5.1.2",
30 + "babel-eslint": "^10.1.0",
31 + "electron": "^6.0.0",
32 + "eslint": "^6.7.2",
33 + "eslint-plugin-import": "^2.20.2",
34 + "eslint-plugin-node": "^11.1.0",
35 + "eslint-plugin-promise": "^4.2.1",
36 + "eslint-plugin-standard": "^4.0.0",
37 + "eslint-plugin-vue": "^6.2.2",
38 + "node-sass": "^4.12.0",
39 + "sass": "^1.19.0",
40 + "sass-loader": "^8.0.2",
41 + "vue-cli-plugin-electron-builder": "~1.4.6",
42 + "vue-cli-plugin-vuetify": "~2.0.5",
43 + "vue-template-compiler": "^2.6.11",
44 + "vuetify-loader": "^1.3.0"
45 + },
46 + "eslintConfig": {
47 + "root": true,
48 + "env": {
49 + "node": true
50 + },
51 + "extends": [
52 + "plugin:vue/essential",
53 + "@vue/standard"
54 + ],
55 + "parserOptions": {
56 + "parser": "babel-eslint"
57 + },
58 + "rules": {}
59 + },
60 + "browserslist": [
61 + "> 1%",
62 + "last 2 versions",
63 + "not dead"
64 + ]
65 +}
No preview for this file type
1 +<!DOCTYPE html>
2 +<html lang="en">
3 + <head>
4 + <meta charset="utf-8">
5 + <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 + <meta name="viewport" content="width=device-width,initial-scale=1.0">
7 + <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8 + <title><%= htmlWebpackPlugin.options.title %></title>
9 + <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
11 + </head>
12 + <body>
13 + <noscript>
14 + <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
15 + </noscript>
16 + <div id="app"></div>
17 + <!-- built files will be auto injected -->
18 + </body>
19 +</html>
1 +<template>
2 + <v-app>
3 +
4 + <Sidebar/>
5 +
6 + <v-content>
7 + <router-view></router-view>
8 + </v-content>
9 + </v-app>
10 +</template>
11 +
12 +<script>
13 +import Sidebar from './components/Sidebar'
14 +
15 +export default {
16 + name: 'App',
17 + components: {
18 + Sidebar
19 + },
20 + data: () => ({
21 + //
22 + })
23 +}
24 +</script>
1 +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
1 +/* Contents */
2 +.top-tap-bar {
3 + padding: 5px 10px;
4 + margin-bottom: 10px;
5 +}
6 +
7 +.content-wrapper {
8 + margin: 10px;
9 +}
1 +'use strict'
2 +
3 +import { app, protocol, BrowserWindow } from 'electron'
4 +import {
5 + createProtocol
6 + /* installVueDevtools */
7 +} from 'vue-cli-plugin-electron-builder/lib'
8 +const isDevelopment = process.env.NODE_ENV !== 'production'
9 +
10 +// Keep a global reference of the window object, if you don't, the window will
11 +// be closed automatically when the JavaScript object is garbage collected.
12 +let win
13 +
14 +// Scheme must be registered before the app is ready
15 +protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
16 +
17 +function createWindow () {
18 + // Create the browser window.
19 + win = new BrowserWindow({
20 + width: 1000,
21 + height: 600,
22 + titleBarStyle: 'customButtonsOnHover',
23 + webPreferences: {
24 + nodeIntegration: true,
25 + webSecurity: false
26 + }
27 + })
28 + win.removeMenu() // Remove top toolbar
29 +
30 + if (process.env.WEBPACK_DEV_SERVER_URL) {
31 + // Load the url of the dev server if in development mode
32 + win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
33 + if (!process.env.IS_TEST) win.webContents.openDevTools()
34 + } else {
35 + createProtocol('app')
36 + // Load the index.html when not in development
37 + win.loadURL('app://./index.html')
38 + }
39 +
40 + win.on('closed', () => {
41 + win = null
42 + })
43 +}
44 +
45 +// Quit when all windows are closed.
46 +app.on('window-all-closed', () => {
47 + // On macOS it is common for applications and their menu bar
48 + // to stay active until the user quits explicitly with Cmd + Q
49 + if (process.platform !== 'darwin') {
50 + app.quit()
51 + }
52 +})
53 +
54 +app.on('activate', () => {
55 + // On macOS it's common to re-create a window in the app when the
56 + // dock icon is clicked and there are no other windows open.
57 + if (win === null) {
58 + createWindow()
59 + }
60 +})
61 +
62 +// This method will be called when Electron has finished
63 +// initialization and is ready to create browser windows.
64 +// Some APIs can only be used after this event occurs.
65 +app.on('ready', async () => {
66 + if (isDevelopment && !process.env.IS_TEST) {
67 + // Install Vue Devtools
68 + // Devtools extensions are broken in Electron 6.0.0 and greater
69 + // See https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/378 for more info
70 + // Electron will not launch with Devtools extensions installed on Windows 10 with dark mode
71 + // If you are not using Windows 10 dark mode, you may uncomment these lines
72 + // In addition, if the linked issue is closed, you can upgrade electron and uncomment these lines
73 + // try {
74 + // await installVueDevtools()
75 + // } catch (e) {
76 + // console.error('Vue Devtools failed to install:', e.toString())
77 + // }
78 +
79 + }
80 + createWindow()
81 +})
82 +
83 +// Exit cleanly on request from parent process in development mode.
84 +if (isDevelopment) {
85 + if (process.platform === 'win32') {
86 + process.on('message', data => {
87 + if (data === 'graceful-exit') {
88 + app.quit()
89 + }
90 + })
91 + } else {
92 + process.on('SIGTERM', () => {
93 + app.quit()
94 + })
95 + }
96 +}
97 +
98 +/************************************/
99 +/* Custom */
100 +/************************************/
101 +var fs = require('fs')
102 +const ipc = require('electron').ipcMain
103 +
104 +// global variables
105 +global.workspacePath = ''
106 +global.resultFilename = ''
107 +global.startFilename = ''
108 +global.workspaceLoaded = false
109 +global.setResultFilename = function (val) {
110 + global.resultFilename = val
111 +}
112 +global.setStartFilename = function (val) {
113 + global.startFilename = val
114 +}
115 +
116 +// Variables
117 +var wsControl = {
118 + filenames: [],
119 + fileLength: 0,
120 + current: {
121 + filename: '',
122 + filepath: '',
123 + idx: -1,
124 + digits: []
125 + }
126 +}
127 +var resultDigits = []
128 +
129 +// functions
130 +function loadWorkspace () {
131 + var files = fs.readdirSync(global.workspacePath)
132 + files.sort((a, b) => { return Number(a.substr(0, a.length - 3)) - Number(b.substr(0, b.length - 3)) })
133 + console.log(files)
134 +
135 + var nowStart = (global.startFilename === '')
136 + for (var i = 0; i < files.length; i++) {
137 + var _file = files[i]
138 + if (!nowStart) {
139 + if (_file === global.startFilename) nowStart = true
140 + else continue
141 + }
142 +
143 + var _suffix = _file.substr(_file.length - 4, _file.length)
144 + if (_suffix === '.png' || _suffix === '.PNG') {
145 + wsControl.filenames.push(_file)
146 + wsControl.fileLength = wsControl.filenames.length
147 + }
148 + }
149 +
150 + if (wsControl.fileLength === 0) {
151 + return false
152 + }
153 + return true
154 +}
155 +
156 +function setCurrentIdx (idx) {
157 + if (idx >= wsControl.fileLength || idx < 0) {
158 + return false
159 + }
160 + wsControl.current.idx = idx
161 + wsControl.current.filename = wsControl.filenames[idx]
162 + wsControl.current.filepath = global.workspacePath + '\\' + wsControl.filenames[idx]
163 + return true
164 +}
165 +
166 +function setCurrDigits (digits) {
167 + wsControl.current.digits = digits
168 + if (wsControl.current.idx < resultDigits.length) {
169 + resultDigits[wsControl.current.idx] = digits
170 + } else if (wsControl.current.idx >= resultDigits.length) {
171 + resultDigits.push(digits)
172 + }
173 +}
174 +
175 +function loadCurrDigits (idx) {
176 + if (idx < 0 || idx >= resultDigits.length) wsControl.current.digits = [{ id: 0, value: '' }, { id: 1, value: '' }, { id: 2, value: '' }, { id: 3, value: '' }]
177 + else wsControl.current.digits = resultDigits[idx]
178 +}
179 +
180 +function _digitsToLabels (digits) {
181 + var result = []
182 + var len = 0
183 + if (digits[0].value === '') return [0, 0, 10, 10, 10, 10]
184 + else {
185 + result.push(1) // hasNum
186 + result.push(0) // digitLen
187 + for (var i = 0; i < 4; i++) {
188 + if (digits[i].value !== '') {
189 + result.push(digits[i].value)
190 + len++
191 + } else {
192 + result.push(10)
193 + }
194 + }
195 + result[1] = len
196 + }
197 + return result
198 +}
199 +
200 +// Linked with openWorkspace() in "@/views/Settings"
201 +// listen to an open-file-dialog command and sending back selected information
202 +const dialog = require('electron').dialog
203 +
204 +ipc.on('open-file-dialog', function (event) {
205 + dialog.showOpenDialog({
206 + properties: ['openDirectory']
207 + }, function (files) {
208 + if (files) {
209 + global.workspacePath = files[0]
210 + global.workspaceLoaded = loadWorkspace()
211 + event.sender.send('selected-file', files[0])
212 + event.sender.send('workspace-load-event', global.workspaceLoaded)
213 + }
214 + })
215 +})
216 +
217 +// Image control
218 +ipc.on('set-current', function (event, data) {
219 + var ok = false
220 +
221 + if (data.key === 'prev') {
222 + setCurrDigits(data.digits)
223 + ok = setCurrentIdx(wsControl.current.idx - 1)
224 + loadCurrDigits(wsControl.current.idx)
225 + } else if (data.key === 'next') {
226 + setCurrDigits(data.digits)
227 + ok = setCurrentIdx(wsControl.current.idx + 1)
228 + loadCurrDigits(wsControl.current.idx)
229 + }
230 +
231 + if (ok) {
232 + event.sender.send('current-image-changed', wsControl.current)
233 + }
234 +})
235 +
236 +// Export as CSV
237 +const createCsvWriter = require('csv-writer').createObjectCsvWriter
238 +ipc.on('save-to-csv', function (event) {
239 + const csvWriter = createCsvWriter({
240 + path: global.workspacePath + '\\' + global.resultFilename,
241 + header: [
242 + { id: 'filename', title: 'FILENAME' },
243 + { id: 'label1', title: 'hasNum' },
244 + { id: 'label2', title: 'digitLen' },
245 + { id: 'label3', title: 'DIGIT1' },
246 + { id: 'label4', title: 'DIGIT2' },
247 + { id: 'label5', title: 'DIGIT3' },
248 + { id: 'label6', title: 'DIGIT4' }
249 + ]
250 + })
251 +
252 + var records = []
253 + for (var i = 0; i < resultDigits.length; i++) {
254 + var record = { filename: '', label1: 0, label2: 0, label3: 0, label4: 0, label5: 0, label6: 0 }
255 + var labels = _digitsToLabels(resultDigits[i])
256 + console.log(labels)
257 + record.filename = wsControl.filenames[i]
258 + record.label1 = labels[0]
259 + record.label2 = labels[1]
260 + record.label3 = labels[2]
261 + record.label4 = labels[3]
262 + record.label5 = labels[4]
263 + record.label6 = labels[5]
264 + records.push(record)
265 + }
266 +
267 + csvWriter.writeRecords(records)
268 + .then(() => {
269 + event.returnValue = 'Saved successfully.'
270 + })
271 + .catch(function (err) {
272 + event.returnValue = String(err)
273 + })
274 +})
1 +<template>
2 + <div class="sidebar">
3 + <!-- Side bar -->
4 + <v-navigation-drawer
5 + app
6 + dark
7 + width="220"
8 + permanent
9 + >
10 +
11 + <!-- Header -->
12 + <v-list-item>
13 + <v-list-item-content>
14 + <v-list-item-title class="title nonselectable-text">
15 + {{ title }}
16 + </v-list-item-title>
17 + <v-list-item-subtitle class="nonselectable-text">
18 + {{ subtitle }}
19 + </v-list-item-subtitle>
20 + </v-list-item-content>
21 + </v-list-item>
22 +
23 + <v-divider></v-divider>
24 +
25 + <!-- Sidebar Items -->
26 + <v-list dense nav>
27 + <v-list-item
28 + v-for="(item, idx) in items"
29 + :to="item.route"
30 + :key="idx"
31 + class="sidebar-link"
32 + link
33 + >
34 + <v-list-item-icon>
35 + <v-icon>{{ item.icon }}</v-icon>
36 + </v-list-item-icon>
37 +
38 + <v-list-item-content>
39 + <v-list-item-title>{{ item.title }}</v-list-item-title>
40 + </v-list-item-content>
41 + </v-list-item>
42 + </v-list>
43 +
44 + <!-- Version -->
45 + <div class="version-wrapper">
46 + <div class="body-2">{{ version.main }}</div>
47 + <div class="caption">{{ version.build }}</div>
48 + </div>
49 +
50 + </v-navigation-drawer>
51 + </div>
52 +</template>
53 +
54 +<script>
55 +export default {
56 + name: 'Sidebar',
57 + data: () => ({
58 + title: 'Image Labeller',
59 + subtitle: 'for KHU Capstone Design 1',
60 + items: [
61 + { title: 'Main', icon: 'mdi-view-dashboard', route: '/main' },
62 + { title: 'Settings', icon: 'mdi-cog-outline', route: '/settings' }
63 + ],
64 + version: {
65 + main: 'v0.7.3-alpha',
66 + build: 'build-2004080227'
67 + }
68 + })
69 +}
70 +</script>
71 +
72 +<style lang="scss" scoped>
73 +.sidebar {
74 + .nonselectable-text {
75 + user-select: none;
76 + }
77 + .sidebar-link {
78 + -webkit-app-region: no-drag;
79 + }
80 + .version-wrapper {
81 + position: absolute;
82 + bottom: 10px;
83 + right: 10px;
84 + color: #aaa;
85 + text-align: right;
86 + }
87 +}
88 +</style>
1 +import Vue from 'vue'
2 +import App from './App.vue'
3 +import router from './router'
4 +import store from './store'
5 +import vuetify from './plugins/vuetify'
6 +
7 +Vue.config.productionTip = false
8 +
9 +new Vue({
10 + router,
11 + store,
12 + vuetify,
13 + render: h => h(App)
14 +}).$mount('#app')
1 +import Vue from 'vue'
2 +import Vuetify from 'vuetify/lib'
3 +
4 +Vue.use(Vuetify)
5 +
6 +export default new Vuetify({
7 +})
1 +import Vue from 'vue'
2 +import VueRouter from 'vue-router'
3 +
4 +import Main from '../views/Main.vue'
5 +import Settings from '../views/Settings.vue'
6 +
7 +Vue.use(VueRouter)
8 +
9 +const routes = [
10 + {
11 + path: '/',
12 + redirect: '/settings'
13 + },
14 + {
15 + path: '/main',
16 + name: 'Main',
17 + component: Main
18 + },
19 + {
20 + path: '/settings',
21 + name: 'Settings',
22 + component: Settings
23 + }
24 +]
25 +
26 +const router = new VueRouter({
27 + mode: 'history',
28 + base: process.env.BASE_URL,
29 + routes,
30 + linkActiveClass: 'nav-item active'
31 +})
32 +
33 +export default router
1 +import Vue from 'vue'
2 +import Vuex from 'vuex'
3 +
4 +Vue.use(Vuex)
5 +
6 +export default new Vuex.Store({
7 + state: {
8 + },
9 + mutations: {
10 + },
11 + actions: {
12 + },
13 + modules: {
14 + }
15 +})
1 +<template>
2 + <div class="content">
3 + <div class="content-wrapper" :key="$route.fullPath">
4 +
5 + <div v-if="!workspaceLoaded" style="color: red">Workspace not loaded!</div>
6 +
7 + <div v-else :key="image.now">
8 + <!-- Image Panel -->
9 + <div class="image-panel">
10 + <div class="image-wrapper" :key="image.filename">
11 + <span class="body-2">{{ image.filename }}</span>
12 + <v-img id="target-image" :src="image.filepath"></v-img>
13 + </div>
14 + </div>
15 +
16 + <v-divider></v-divider>
17 +
18 + <!-- Input Panel -->
19 + <div class="input-panel">
20 + <v-container>
21 + <v-row justify="space-around">
22 + <v-col
23 + v-for="digit in digits"
24 + :key="digit.id"
25 + :id="`input-digit-`+digit.id"
26 + cols="12"
27 + md="3"
28 + >
29 + <v-sheet
30 + class="pa-12"
31 + color="grey lighten-3"
32 + >
33 + <v-sheet
34 + :elevation="6"
35 + class="mx-auto digit-wrapper"
36 + height="80"
37 + width="60"
38 + >
39 + <span class="display-3">{{ digit.value }}</span>
40 + </v-sheet>
41 + </v-sheet>
42 + </v-col>
43 + </v-row>
44 + </v-container>
45 + </div>
46 +
47 + <!-- Control Panel -->
48 + <v-bottom-navigation class="control-panel">
49 + <v-btn value="prev" @click="prevImage">
50 + <span>Prev</span>
51 + <v-icon>mdi-skip-previous-outline</v-icon>
52 + </v-btn>
53 +
54 + <v-btn class="non-clickable">
55 + <span>{{ image.filename }}</span>
56 + </v-btn>
57 +
58 + <v-btn value="next" @click="nextImage">
59 + <span>Next</span>
60 + <v-icon>mdi-skip-next-outline</v-icon>
61 + </v-btn>
62 + </v-bottom-navigation>
63 +
64 + <v-snackbar
65 + v-model="snackbar.isOpened"
66 + :timeout="3000"
67 + >
68 + {{ snackbar.text }}
69 + <v-btn
70 + color="blue"
71 + text
72 + @click="snackbar.isOpened = false"
73 + >
74 + Close
75 + </v-btn>
76 + </v-snackbar>
77 +
78 + </div>
79 + </div>
80 + </div>
81 +</template>
82 +
83 +<script>
84 +export default {
85 + name: 'Main',
86 + data: () => ({
87 + workspaceLoaded: false,
88 + currDigitID: 0,
89 + digits: [
90 + { id: 0, value: '' },
91 + { id: 1, value: '' },
92 + { id: 2, value: '' },
93 + { id: 3, value: '' }
94 + ],
95 + image: {
96 + filepath: '',
97 + filename: '',
98 + now: null
99 + },
100 + snackbar: {
101 + isOpened: false,
102 + text: ''
103 + }
104 + }),
105 + mounted () {
106 + // Check if workspace loaded
107 + this.checkWorkspaceLoaded()
108 +
109 + // Detect current image data
110 + var vm = this
111 + const ipc = require('electron').ipcRenderer
112 + ipc.on('current-image-changed', function (event, curr) {
113 + console.log(curr)
114 + vm.image.now = curr.idx
115 + vm.image.filepath = curr.filepath
116 + vm.image.filename = curr.filename
117 + // vm.currDigitID = 0
118 + // vm.digits = curr.digits
119 + vm.loadDigits(curr.digits)
120 + })
121 +
122 + // Capture keydown events
123 + this._keyCapture()
124 + },
125 + methods: {
126 + checkWorkspaceLoaded () {
127 + const remote = require('electron').remote
128 + this.workspaceLoaded = remote.getGlobal('workspaceLoaded')
129 + },
130 + saveToCSV () {
131 + const ipc = require('electron').ipcRenderer
132 + // ipc.send('save-to-csv')
133 + var result = ipc.sendSync('save-to-csv')
134 + console.log(result)
135 +
136 + this.snackbar.text = result
137 + this.snackbar.isOpened = true
138 + },
139 + prevImage () {
140 + this._setCurrentImage('prev')
141 + },
142 + nextImage () {
143 + this._setCurrentImage('next')
144 + },
145 + inputDigit (keyCode) {
146 + if (this.currDigitID > 3) return false
147 + else {
148 + this.digits[this.currDigitID].value = keyCode - 48
149 + this.currDigitID++
150 + return true
151 + }
152 + },
153 + removeDigit () {
154 + if (this.currDigitID <= 0) return false
155 + else {
156 + this.digits[this.currDigitID - 1].value = ''
157 + this.currDigitID--
158 + return true
159 + }
160 + },
161 + loadDigits (newDigits) {
162 + var len = 0
163 + for (var i = 0; i < 4; i++) {
164 + if (newDigits[i].value !== '') len++
165 + else break
166 + }
167 + this.currDigitID = len
168 + this.digits = newDigits
169 + console.log(len)
170 + },
171 + _setCurrentImage (key) {
172 + const ipc = require('electron').ipcRenderer
173 + ipc.send('set-current', { key: key, digits: this.digits })
174 + },
175 + _clearDigits () {
176 + for (var i = 0; i < 4; i++) this.digits[i].value = ''
177 + this.currDigitID = 0
178 + },
179 + _keyCapture () {
180 + var vm = this
181 + window.addEventListener('keyup', function (e) {
182 + if (e.keyCode === 37) { // Left arrow
183 + vm.prevImage()
184 + } else if (e.keyCode === 39) { // Right arrow
185 + vm.nextImage()
186 + } else if (e.keyCode === 83) { // 's' Save
187 + vm.saveToCSV()
188 + } else if (e.keyCode >= 48 && e.keyCode <= 57) { // Numbers
189 + vm.inputDigit(e.keyCode)
190 + } else if (e.keyCode === 8 || e.keyCode === 46) { // Remove
191 + vm.removeDigit()
192 + }
193 + })
194 + }
195 + }
196 +}
197 +</script>
198 +
199 +<style lang="scss" scoped>
200 +@import "@/assets/scss/common.scss";
201 +
202 +.image-panel {
203 + .image-wrapper {
204 + width: 240px;
205 + margin: 10px auto;
206 +
207 + #target-image {
208 + width: 240px;
209 + height: 240px;
210 + background-color: #000000;
211 + }
212 + }
213 +}
214 +
215 +.input-panel {
216 + .digit-wrapper {
217 + text-align: center;
218 + vertical-align: middle;
219 + display: table-cell;
220 + }
221 +}
222 +
223 +.control-panel {
224 + position: absolute;
225 + bottom: 0;
226 + padding-top: 5px;
227 +
228 + .non-clickable {
229 + cursor: default;
230 + }
231 +}
232 +</style>
1 +<template>
2 + <div class="content">
3 + <div class="content-wrapper" :key="$route.fullPath">
4 +
5 + <!-- Output Control -->
6 + <v-expansion-panels
7 + class="control-panel"
8 + v-model="panels[0]"
9 + >
10 + <v-expansion-panel>
11 + <v-expansion-panel-header>Output File Control</v-expansion-panel-header>
12 + <v-expansion-panel-content>
13 + <v-text-field
14 + v-model="output"
15 + label="Output Filename"
16 + prepend-icon="mdi-file-delimited-outline"
17 + >
18 + {{ output }}
19 + </v-text-field>
20 + <div class="control-button-wrapper">
21 + <v-btn class="control-button"
22 + small
23 + color="primary"
24 + @click="saveOutputFilename"
25 + >
26 + Save Filename
27 + </v-btn>
28 + </div>
29 + </v-expansion-panel-content>
30 + </v-expansion-panel>
31 + </v-expansion-panels>
32 +
33 + <v-divider></v-divider>
34 +
35 + <!-- Start Filename Control -->
36 + <v-expansion-panels
37 + class="start-filename-panel"
38 + v-model="panels[1]"
39 + >
40 + <v-expansion-panel>
41 + <v-expansion-panel-header>Start Filename Control</v-expansion-panel-header>
42 + <v-expansion-panel-content>
43 + <v-text-field
44 + v-model="startFilename"
45 + label="Start Filename"
46 + prepend-icon="mdi-file-delimited-outline"
47 + >
48 + {{ startFilename }}
49 + </v-text-field>
50 + <div class="control-button-wrapper">
51 + <v-btn class="control-button"
52 + small
53 + color="primary"
54 + @click="saveStartFilename"
55 + >
56 + Save Filename
57 + </v-btn>
58 + </div>
59 + </v-expansion-panel-content>
60 + </v-expansion-panel>
61 + </v-expansion-panels>
62 +
63 + <v-divider></v-divider>
64 +
65 + <!-- Workspace Control -->
66 + <v-expansion-panels
67 + class="control-panel"
68 + v-model="panels[2]"
69 + >
70 + <v-expansion-panel>
71 + <v-expansion-panel-header>Workspace Control</v-expansion-panel-header>
72 + <v-expansion-panel-content>
73 + <span v-if='workspace !== null' class="font-italic font-weight-light">{{ workspace }}</span>
74 + <span v-else class="font-italic font-weight-light">Please open your workspace</span>
75 + <div class="control-button-wrapper">
76 + <v-btn
77 + class="control-button"
78 + id="btn-workspace"
79 + small
80 + color="primary"
81 + @click="openWorkspace"
82 + >
83 + Open Workspace
84 + </v-btn>
85 + </div>
86 + </v-expansion-panel-content>
87 + </v-expansion-panel>
88 + </v-expansion-panels>
89 +
90 + <v-snackbar
91 + v-model="snackbar.isOpened"
92 + :timeout="4000"
93 + >
94 + {{ snackbar.text }}
95 + <v-btn
96 + color="blue"
97 + text
98 + @click="snackbar.isOpened = false"
99 + >
100 + Close
101 + </v-btn>
102 + </v-snackbar>
103 +
104 + </div>
105 + </div>
106 +</template>
107 +
108 +<script>
109 +export default {
110 + name: 'settings',
111 + data: () => ({
112 + panels: [0, 0, 0],
113 + workspace: null,
114 + output: 'result.csv',
115 + startFilename: '0.png',
116 + snackbar: {
117 + isOpened: false,
118 + text: ''
119 + }
120 + }),
121 + mounted () {
122 + this.getSetting()
123 + },
124 + methods: {
125 + getSetting () {
126 + const remote = require('electron').remote
127 +
128 + var _workspace = remote.getGlobal('workspacePath')
129 + if (_workspace !== '') this.workspace = _workspace
130 + else this.workspace = null
131 +
132 + var _result = remote.getGlobal('resultFilename')
133 + if (_result !== '') this.output = _result
134 + else {
135 + this.output = 'result.csv'
136 + remote.getGlobal('setResultFilename')(this.output)
137 + }
138 + },
139 + openWorkspace () {
140 + var vm = this
141 +
142 + const ipc = require('electron').ipcRenderer
143 +
144 + ipc.send('open-file-dialog')
145 + ipc.on('selected-file', function (event, path) {
146 + vm.workspace = `${path}`
147 + })
148 + ipc.on('workspace-load-event', function (event, ok) {
149 + if (ok === true) {
150 + vm.snackbar.text = 'Workspace loaded successfully!'
151 + vm.snackbar.isOpened = true
152 + } else {
153 + vm.snackbar.text = 'Workspace loaded failed!'
154 + vm.snackbar.isOpened = true
155 + }
156 + })
157 + },
158 + saveOutputFilename () {
159 + const remote = require('electron').remote
160 + remote.getGlobal('setResultFilename')(this.output)
161 + },
162 + saveStartFilename () {
163 + const remote = require('electron').remote
164 + remote.getGlobal('setStartFilename')(this.startFilename)
165 + }
166 + }
167 +}
168 +</script>
169 +
170 +<style lang="scss" scoped>
171 +@import "@/assets/scss/common.scss";
172 +
173 +.control-panel {
174 + margin: 10px 0;
175 +}
176 +
177 +.control-button-wrapper {
178 + float: right;
179 + .control-button {
180 + margin-right: 10px;
181 + }
182 +}
183 +</style>
1 +module.exports = {
2 + transpileDependencies: [
3 + 'vuetify'
4 + ]
5 +}
This diff could not be displayed because it is too large.