Electron Adventures: 에피소드 96: Pywebview 터미널 앱

이제 우리는 Pywebview에서 몇 가지 Hello World를 수행했으므로 좀 더 복잡한 것, 즉 터미널 앱을 빌드해 보겠습니다.

앞에서 언급했듯이 Pywebview에는 프런트엔드에 어떤 종류의 디버깅 도구도 없기 때문에 거기에 심각한 코드를 작성하려고 시도하는 것은 끔찍한 생각입니다. 다행스럽게도 우리는 이미 작동하는 터미널 앱을 가지고 있으며 Pywebview로 이식하기만 하면 됩니다.

터미널.html



이 문서는 이전에 여러 번 가지고 있던 것과 거의 동일합니다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="./terminal.css" />
  </head>
  <body>
    <h1>Very amazing terminal app</h1>
    <div id="terminal">
      <div id="history">
      </div>

      <div class="input-line">
        <span class="prompt">$</span>
        <form>
          <input type="text" autofocus />
        </form>
      </div>
    </div>
    <script src="./terminal.js"></script>
  </body>
</html>


터미널.css



스타일링도 마찬가지입니다.

body {
  background-color: #444;
  color: #fff;
}

h1 {
  font-family: monospace;
}

#terminal {
  font-family: monospace;
}

.input-line {
  display: flex;
}

.input-line > * {
  flex: 1;
}

.input-line > .prompt {
  flex: 0;
  padding-right: 0.5rem;
}

.output {
  padding-bottom: 0.5rem;
}

.input {
  color: #ffa;
}

.output {
  color: #afa;
  white-space: pre;
}

form {
  display: flex;
}

input {
  flex: 1;
  font-family: monospace;
  background-color: #444;
  color: #fff;
  border: none;
}


터미널.js



한 가지만 새롭습니다.

let form = document.querySelector("form")
let input = document.querySelector("input")
let terminalHistory = document.querySelector("#history")

function createInputLine(command) {
  let inputLine = document.createElement("div")
  inputLine.className = "input-line"

  let promptSpan = document.createElement("span")
  promptSpan.className = "prompt"
  promptSpan.append("$")
  let inputSpan = document.createElement("span")
  inputSpan.className = "input"
  inputSpan.append(command)

  inputLine.append(promptSpan)
  inputLine.append(inputSpan)

  return inputLine
}

function createTerminalHistoryEntry(command, commandOutput) {
  let inputLine = createInputLine(command)
  let output = document.createElement("div")
  output.className = "output"
  output.append(commandOutput)
  terminalHistory.append(inputLine)
  terminalHistory.append(output)
}

form.addEventListener("submit", async (e) => {
  e.preventDefault()
  let command = input.value
  let output = await window.pywebview.api.execute(command)
  createTerminalHistoryEntry(command, output)
  input.value = ""
  input.scrollIntoView()
})


그 물건은 let output = await window.pywebview.api.execute(command) . execute(command) 함수는 Python 백엔드에서 노출되어야 합니다.

단말기



마지막으로 Python 코드:

#!/usr/bin/env python3

import webview
import subprocess

class App:
  def execute(self, command):
    result = subprocess.run(command, capture_output=True, shell=True, encoding="utf-8")
    return result.stdout + result.stderr

app = App()

window = webview.create_window(
  "Terminal App",
  "terminal.html",
  js_api=App()
)
webview.start()


우리는 단지 하나의 메소드를 공개합니다. string (with encoding="utf-8" )로 변환해야 한다는 것을 기억해야 합니다. pywebview는 bytes를 보낼 수 없기 때문입니다. 기술적으로는 요즘 유효한 JavaScript 유형( Uint8Array )이지만

결과



결과는 다음과 같습니다.



아 잠깐만, 중간에 이 쓰레기는 무엇입니까? 결과적으로 우리의 엉뚱한 OS 전용 웹보기는 "--"를 아무도 요청하지 않은 긴 대시로 자동 전환하기로 결정했습니다. Chrome이나 Safari는 그렇게 하지 않으며, 내가 본 다른 프로그램도 아닙니다. Pywebview가 사용하는 형편없는 프런트엔드입니다.

나는 이미 Pywebview의 다른 모든 문제를 언급했지만 이것은 OS와 함께 번들로 제공되는 모든 것을 사용하는 것이 얼마나 끔찍한 생각인지 다시 보여줍니다. 사람들은 종종 번들 브라우저로 인해 Electron 앱이 커지는 것에 대해 우쭐대지만, 그 몇 MB는 이러한 모든 문제를 한 번에 피할 수 있습니다.

평소와 같이 all the code for the episode is here .

좋은 웹페이지 즐겨찾기