<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ATSU</title>
    <description>The latest articles on DEV Community by ATSU (@atsu).</description>
    <link>https://dev.to/atsu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F933716%2F4d00f3b0-a094-4e39-b036-fcde27a6d9a4.jpg</url>
      <title>DEV Community: ATSU</title>
      <link>https://dev.to/atsu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atsu"/>
    <language>en</language>
    <item>
      <title>Setting Up a Raspberry Pi for Remote Access Without a Monitor or Keyboard</title>
      <dc:creator>ATSU</dc:creator>
      <pubDate>Fri, 08 Mar 2024 07:07:40 +0000</pubDate>
      <link>https://dev.to/atsu/2024-edition-setting-up-a-raspberry-pi-for-remote-access-without-a-monitor-or-keyboard-wip-48ef</link>
      <guid>https://dev.to/atsu/2024-edition-setting-up-a-raspberry-pi-for-remote-access-without-a-monitor-or-keyboard-wip-48ef</guid>
      <description>&lt;p&gt;Install the following software for this tutorial&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.realvnc.com/en/connect/download/viewer/" rel="noopener noreferrer"&gt;RealVNC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, start the raspberrypi Imager&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fct9abw8l0nka9ba5zsn2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fct9abw8l0nka9ba5zsn2.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When opened, the following configuration screen will be launched.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin67kyf4g11tsgxtnx01.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fin67kyf4g11tsgxtnx01.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time,  I'll install Raspberry Pi OS (64-bit) Desktop on a 32GB microSD card, using the Desktop version for graphical display via VNC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a5ermamb0f3siartb1m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a5ermamb0f3siartb1m.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpkbzr904fz8kmx0l4wi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgpkbzr904fz8kmx0l4wi.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write with the following settings&lt;/p&gt;

&lt;p&gt;Press 「Next」&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgcohg9jmwqxhv2x36sn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgcohg9jmwqxhv2x36sn.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When asked 「Would you like to apply OS customisation settings?」, press 「EDIT SETTINGS」.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqlujyoug1deds6tcse5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqlujyoug1deds6tcse5.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the 「GENERAL」 category, set up Wi-Fi and password in the 「OS Customization」 screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsa5m6bvnirdtaxur7t8i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsa5m6bvnirdtaxur7t8i.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select 「SERVICE」 and enter your Wi-Fi and password. If you wish to configure using a public key, select 「SERVICE」 next to 「GENERAL」 and choose 「Allow public-key authentication only. Set authorized_keys for 'pi':'」&lt;/p&gt;

&lt;p&gt;And click「SAVE」&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1qzxmr040s40k9qs25x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1qzxmr040s40k9qs25x.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8fna5q1w1g98615jix8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8fna5q1w1g98615jix8.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When asked 「Would you like to apply OS customisation settings?」, press「YES」.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpcxv5co7135i8nim2x9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpcxv5co7135i8nim2x9.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please wait a moment while the Raspberry Pi OS is being written to the microSD card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r8thbtxfbc371f47v46.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r8thbtxfbc371f47v46.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The display will show 「verifying...」 and after a short while, the process will be completed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5zk24z9quhwlr9avj7i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5zk24z9quhwlr9avj7i.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click 「CONTINUE」 and then follow the on-screen instructions to remove the microSD card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1e7ryd73iw1we6m49ss.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1e7ryd73iw1we6m49ss.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, insert the microSD card into the Raspberry Pi and power it on.&lt;/p&gt;

&lt;p&gt;Enter the following command in the terminal to connect to the Raspberry Pi via SSH. If a password has been set, enter the password to log in.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ssh pi@raspberrypi.local


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ping pi@raspberrypi.local


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check IPAdress&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ssh pi@10.207.171.xxx（IP Address）


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If configured as follows, the 'hostname,' 'Username,' and 'Password' will be as follows.&lt;/p&gt;

&lt;p&gt;「hostname」: 「raspberrypi」&lt;br&gt;
「Username」：「pi」&lt;br&gt;
「Password」：「raspberry」&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxpuytgch6gjmjx3pzxy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxpuytgch6gjmjx3pzxy.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the first login, you will be asked a question like the following, to which you should respond with 'yes'.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ssh pi@(IPAdress)
The authenticity of host '10.207.171.xxx (10.207.171.xxx)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxx.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Yes


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once logged in, you will see a prompt like 「pi@raspberrypi:~ $」, indicating that you have successfully SSH connected to the Raspberry Pi.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Linux raspberrypi 6.1.0-rpi7-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: xxx. xxx. xxx
pi@raspberrypi:~ $


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Having established a connection via SSH in the CLI, I would now like to connect graphically using VNC. To do this, it's necessary to enable VNC connections by running 「sudo raspi-config」.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo raspi-config


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The screen will change to a blue background, switching to the interface for configuring Raspberry Pi settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fosokks4mcvepwosgciep.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fosokks4mcvepwosgciep.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select 「3 Interface Options」, Click「Enter」&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdw792guyejw8mkdht0vj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdw792guyejw8mkdht0vj.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select「I2 VNC」&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs24r36to30rkpl0rg9zc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs24r36to30rkpl0rg9zc.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;「When asked 'Would you like the VNC server to be enabled?」 select 「Yes」.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcihzus5450ab2965878c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcihzus5450ab2965878c.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will momentarily switch back to the original black screen. Afterwards, 「The VNC Server is enabled」 will be displayed, so select 「OK」.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65ki33jvo5tee1zsinzw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65ki33jvo5tee1zsinzw.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbj3boh7pc4xborlerqa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbj3boh7pc4xborlerqa.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that VNC is enabled, next launch Real VNC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pllv45o0316eqawlcwk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pllv45o0316eqawlcwk.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtlashpjrmadbsph4n01.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtlashpjrmadbsph4n01.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the IP address in the input field at the top&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxckw8za1j2bu8by8yqj2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxckw8za1j2bu8by8yqj2.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, enter the Username and Password you set earlier&lt;/p&gt;

&lt;p&gt;If the IP address is entered correctly, the following screen will be displayed for the first time, so click 「Continue」.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk3g4v3v3p5nvxfxhlqa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk3g4v3v3p5nvxfxhlqa.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, enter the Username and Password you set earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsqsvmxjag1gqrjnj1jk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsqsvmxjag1gqrjnj1jk.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing so will display the Raspberry Pi's home screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6925dtmntdvz27ds1kz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6925dtmntdvz27ds1kz.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>ssh</category>
      <category>vnc</category>
      <category>beginners</category>
    </item>
    <item>
      <title>ref in Vue</title>
      <dc:creator>ATSU</dc:creator>
      <pubDate>Thu, 15 Feb 2024 12:13:27 +0000</pubDate>
      <link>https://dev.to/atsu/web-development-with-vite-vue-and-flask-update-43k2</link>
      <guid>https://dev.to/atsu/web-development-with-vite-vue-and-flask-update-43k2</guid>
      <description>&lt;p&gt;I have updated the content of this article &lt;a href="https://dev.to/atsu/web-development-with-vite-vue-and-flask-40ep"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Added new functionality to &lt;code&gt;src/components/HelloWorld.vue&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ref is a function that makes a piece of data reactive. This means when the data changes, Vue automatically updates the part of your website where that data is displayed. Think of ref like magic glue that connects your data to your website's appearance. Whenever the data changes, the website's look updates by itself without any extra work from you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;
import { ref } from "vue";

defineProps({
  msg: String,
});

const count = ref(0);
const isProcessing = ref(false);

const incrementCount = () =&amp;gt; {
  isProcessing.value = true;
  fetch(`/increment/${count.value}`)
    .then((response) =&amp;gt; {
      if (response.ok) {
        return response.text();
      } else {
        console.error(`${response.status} ${response.statusText}`);
        isProcessing.value = false;
      }
    })
    .then((val) =&amp;gt; {
      count.value = parseInt(val);
      isProcessing.value = false;
    })
    .catch((error) =&amp;gt; {
      console.error(`Error: ${error.message}`);
      isProcessing.value = false;
    });
};
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;h1&amp;gt;{{ msg }}&amp;lt;/h1&amp;gt;

  &amp;lt;div class="card"&amp;gt;
    &amp;lt;button type="button" :disabled="isProcessing" @click="incrementCount"&amp;gt;
      count is {{ count }}
    &amp;lt;/button&amp;gt;
    &amp;lt;p&amp;gt;
      Edit
      &amp;lt;code&amp;gt;components/HelloWorld.vue&amp;lt;/code&amp;gt; to test HMR
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;p&amp;gt;
    Check out
    &amp;lt;a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
      &amp;gt;create-vue&amp;lt;/a
    &amp;gt;, the official Vue + Vite starter
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;
    Install
    &amp;lt;a href="https://github.com/johnsoncodehk/volar" target="_blank"&amp;gt;Volar&amp;lt;/a&amp;gt;
    in your IDE for a better DX
  &amp;lt;/p&amp;gt;
  &amp;lt;p class="read-the-docs"&amp;gt;Click on the Vite and Vue logos to learn more&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;style scoped&amp;gt;
.read-the-docs {
  color: #888;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;const count = ref(0);&lt;br&gt;
const isProcessing = ref(false);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;const count = ref(0);&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;This line creates a reactive piece of data named count and starts it off at 0. Imagine count as a scoreboard on a game. Whenever count changes, the scoreboard on your website updates to show the new score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;const isProcessing = ref(false);&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;This creates another reactive piece of data called isProcessing and sets it to false initially. You can think of isProcessing as a traffic light that controls when you can press the button. When &lt;code&gt;isProcessing&lt;/code&gt; is true (the light is red), you cannot press the button. When it's false (the light turns green), you can press it again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;:disabled="isProcessing":&lt;/strong&gt; &lt;br&gt;
This binds the disabled attribute of the button to the &lt;code&gt;isProcessing&lt;/code&gt; reactive reference. When &lt;code&gt;isProcessing&lt;/code&gt; is true, the button becomes disabled, preventing users from clicking it again until the current process completes. This is a common pattern to prevent duplicate requests or actions while an asynchronous operation is in progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;isProcessing.value = true;&lt;/strong&gt;&lt;br&gt;
This disables the button by setting &lt;code&gt;isProcessing&lt;/code&gt; to true.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>flask</category>
      <category>vite</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Creating Kerf-Checker tool with Vite, Vue, and Flask</title>
      <dc:creator>ATSU</dc:creator>
      <pubDate>Tue, 12 Dec 2023 04:58:31 +0000</pubDate>
      <link>https://dev.to/atsu/creating-kerf-checker-tool-with-vite-vue-and-flask-1nkm</link>
      <guid>https://dev.to/atsu/creating-kerf-checker-tool-with-vite-vue-and-flask-1nkm</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlvmp2z2lf6vgnsohb63.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlvmp2z2lf6vgnsohb63.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is the &lt;a href="https://github.com/ATSU3/kerf-tool-test-fit-guides-generator" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://youtu.be/22ndr35xqFg?si=JdmioLhSOblEsgKQ" rel="noopener noreferrer"&gt;App test video&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the article titled &lt;a href="https://dev.to/atsu/web-development-with-vite-vue-and-flask-40ep"&gt;[Web Development with Vite, Vue, and Flask]&lt;/a&gt; I detailed the process of creating a web application template with a Vue and a Python backend. The application, Kerf Tool Test Fit Guides Generator, is designed to assist in determining the precise kerf needed for materials that will be joined together through laser cutting or CNC machining, particularly useful in furniture design and fabrication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt; &lt;br&gt;
It's a tool to determine the right kerf adjustments for materials before cutting, aiming to achieve a snug fit without the need for adhesives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functionality&lt;/strong&gt; &lt;br&gt;
Users enter various parameters like start width, pitch, gap, number of kerfs, and blade diameter. The application then generates a downloadable SVG file that visually represents the kerfs to be tested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Interface&lt;/strong&gt; &lt;br&gt;
Developed in Vue, the application provides a simple form where users input the necessary parameters and receive immediate visual feedback, including error messages if the input values are not within acceptable ranges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python Backend Processing&lt;/strong&gt;&lt;br&gt;
The Flask backend handles the SVG generation logic. It receives parameters from the frontend, validates them (e.g., ensuring the pitch is not too small), and then either returns an SVG file or an error message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laser Cutting Specifics&lt;/strong&gt;&lt;br&gt;
For those using laser cutters, the blade diameter input can be set to zero, as the kerf adjustment takes into account the laser's precision, which does not require accounting for a physical blade's width.&lt;/p&gt;

&lt;p&gt;You can run the application with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/ATSU3/kerf-tool-test-fit-guides-generator.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd kerf-tool-test-fit-guides-generator
yarn
yarn build
flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is Kerf Check tool?
&lt;/h2&gt;

&lt;p&gt;The Kerf Check tool is designed to ensure parts cut by laser or CNC fit together perfectly. It's essential for making press-fit kits where the right fit is key to holding everything together. For example, with 3 mm boards, matching slots can be too loose. This tool helps find the best fit by letting users test different gaps with an SVG file, for a tight, secure assembly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yrgt4ysn7psj6vdvmqo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6yrgt4ysn7psj6vdvmqo.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2req4eh3zrmibrg3b3ay.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2req4eh3zrmibrg3b3ay.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are using a laser cutting machine, enter 0 for blade-diameter: since you do not need to add a T-bone filet (since you do not need to consider the bit diameter).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8763o5y0zuhpi8umz6q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8763o5y0zuhpi8umz6q.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've found that 2.65mm is just right for 3mm cardboard, so it will hold firm when we finally create a Press-fit -construction like this one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanjnv2s75x8xucgtswf2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanjnv2s75x8xucgtswf2.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu12jhyw58wwha5fyi27o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu12jhyw58wwha5fyi27o.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When machining with CNC, it is not possible to make right angles, so giving them grooves that are larger than the radius of the drill, and we make escape routes for the drill by adding t-bone filet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcty2cl7udeem991can5s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcty2cl7udeem991can5s.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbhy5mwn489umtg5v2a7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbhy5mwn489umtg5v2a7.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ia5qwvzcdhu9dei9gw9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ia5qwvzcdhu9dei9gw9.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found 15.3 to be the appropriate value when connecting a 15 mm lumber-core&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Here is the &lt;a href="https://github.com/ATSU3/kerf-tool-test-fit-guides-generator" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app uses Vue.js for the user interface and relies on Python for SVG calculations. This way, as the app gets more complex, we can make the most of Vue for the design and Python for the heavy math.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;App.vue&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This code generate custom SVG images based on user-provided parameters. It features a user interface where users can input values for the starting width, pitch, gap, number of kerfs, and blade diameter. &lt;/p&gt;

&lt;p&gt;Clicking the "Create SVG" button, the app validates the pitch value and, if it meets the required minimum, sends a request to the server to generate the SVG. If the pitch is too small, an error message is displayed immediately, enhancing the user experience by preventing invalid requests. Successful SVG generation results in an image preview on the page, along with a prompt to click the image for downloading.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;
import { ref, reactive, watch } from 'vue';

const appState = reactive({
  clicked: false,
  success: false,
  downloadURL: '#',
  displayDownloadText: false,
  errorMessage: '',
  parameters: {
    stw: 14,
    inc: 0.05,
    gap: 20,
    num: 4,
    tbone: 12.7
  }
});

watch(() =&amp;gt; appState.parameters.inc, (newValue) =&amp;gt; {
  if (newValue &amp;gt;= 0.01) {
    appState.errorMessage = '';
  }
});

const createSVG = async () =&amp;gt; {
  appState.clicked = true;
  appState.displayDownloadText = false;

  if (appState.parameters.inc &amp;lt; 0.01) {
    appState.errorMessage = "(pitch) is too small. (pitch) can only be set to 1/100 mm increments. (刻み幅)は1/100mm単位までしか設定できません。";
    appState.success = false;
    return;
  }

  try {
    const response = await fetch(`/kerf_check`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json; charset=utf-8",
      },
      body: JSON.stringify(appState.parameters),
    });

    if (!response.ok) {
      const errorData = await response.json();
      appState.errorMessage = errorData.error || response.statusText;
      appState.success = false;
      return;
    }

    const data = await response.text();
    const blob = new Blob([data], { type: "image/svg+xml" });
    appState.downloadURL = window.URL.createObjectURL(blob);
    appState.success = true;
    appState.displayDownloadText = true;
  } catch (error) {
    appState.errorMessage = "An unexpected error occurred.";
    appState.success = false;
  }
};
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div id="app-1" class="kerf-check-container"&amp;gt;
    &amp;lt;h1&amp;gt;Kerf Tool Test Fit Guides Generator&amp;lt;/h1&amp;gt;

    &amp;lt;div class="input-container"&amp;gt;
      &amp;lt;label&amp;gt;基準幅(start-width): &amp;lt;input v-model.number="appState.parameters.stw" placeholder="14"&amp;gt; mm&amp;lt;/label&amp;gt;
      &amp;lt;label&amp;gt;刻み幅(pitch): &amp;lt;input v-model.number="appState.parameters.inc" placeholder="0.05"&amp;gt; mm&amp;lt;/label&amp;gt;
      &amp;lt;label&amp;gt;隙間(gap): &amp;lt;input v-model.number="appState.parameters.gap" placeholder="20"&amp;gt; mm&amp;lt;/label&amp;gt;
      &amp;lt;label&amp;gt;個数(number-of-kerfs): &amp;lt;input v-model.number="appState.parameters.num" placeholder="4"&amp;gt; 個&amp;lt;/label&amp;gt;
      &amp;lt;label&amp;gt;刃径(blade-diameter): &amp;lt;input v-model.number="appState.parameters.tbone" placeholder="12.7"&amp;gt; mm&amp;lt;/label&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;button @click="createSVG" class="create-svg-button"&amp;gt;Create SVG&amp;lt;/button&amp;gt;

    &amp;lt;div v-if="appState.displayDownloadText" class="download-instruction"&amp;gt;Click the image to download&amp;lt;/div&amp;gt;

    &amp;lt;div v-if="appState.success" class="image-container"&amp;gt;
      &amp;lt;a :href="appState.downloadURL" download="kerf_check.svg"&amp;gt;
        &amp;lt;img :src="appState.downloadURL" alt="Generated SVG" class="generated-image"&amp;gt;
      &amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div v-if="appState.errorMessage" class="error-message"&amp;gt;
      {{ appState.errorMessage }}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;style scoped&amp;gt;
.kerf-check-container {
  text-align: center;
  max-width: 600px;
  margin: auto;
}

.input-container {
  display: flex;
  flex-direction: column;
  margin-bottom: 20px;
}

.input-container label {
  margin-bottom: 10px;
}

.create-svg-button {
  margin-bottom: 20px;
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
}

.download-instruction {
  margin-bottom: 10px;
  font-style: italic;
}

.image-container {
  margin-bottom: 20px;
}

.generated-image {
  cursor: pointer;
  max-width: 100%;
  border: 1px solid #ccc;
}

.error-message {
  color: red;
}
&amp;lt;/style&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/kerf_check&lt;/code&gt; route, which accepts POST requests and processes JSON data sent by the client. This route expects to receive specific parameters: &lt;code&gt;stw&lt;/code&gt; (start width), &lt;code&gt;inc&lt;/code&gt; (increment, which is the pitch), &lt;code&gt;gap&lt;/code&gt;, &lt;code&gt;num&lt;/code&gt; (number of kerfs), and &lt;code&gt;tbone&lt;/code&gt; (blade diameter).&lt;/p&gt;

&lt;p&gt;If the absolute value of inc is less than 0.01, the server responds with a 500 Internal Server Error &lt;br&gt;
Otherwise, it calls the &lt;code&gt;generate_svg&lt;/code&gt; function with the received parameters to generate an SVG image representation of kerfs.&lt;br&gt;
The &lt;code&gt;generate_svg&lt;/code&gt; function creates an SVG image as a string with the specified dimensions and kerf patterns. It calculates the width and height of the SVG canvas based on the parameters and uses a series of SVG path commands to draw the kerfs. The function also adds text elements to the SVG to label each kerf with its width. The SVG string is then returned to the client with a content type of &lt;code&gt;image/svg+xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the request is successful and no error is encountered in the &lt;code&gt;generate_svg&lt;/code&gt; function, the server responds with the generated SVG data and a 200 OK status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
from flask import Flask, render_template, request

args = dict()
try:
    with open("args.json") as f:
        args = json.load(f)
except FileNotFoundError:
    pass

app = Flask(__name__, **args)


@app.route("/")
def hello():
    return render_template("index.html")


@app.route("/kerf_check", methods=["POST"])
def kerf_check():
    json = request.json
    stw = json["stw"]
    inc = json["inc"]
    gap = json["gap"]
    num = json["num"]
    tbone = json["tbone"]

    if abs(inc) &amp;lt; 0.01:
        return (
            "(pitch) is too small. (pitch) can only be set to 1/100 mm increments. (刻み幅)は1/100mm単位までしか設定できません。",
            500,
        )

    txt = generate_svg(stw, inc, gap, num, tbone)

    return txt, 200, {"Content-Type": "image/svg+xml"}


def generate_svg(stw, inc, gap, num, tbone):
    # GAP = 16
    MARGIN = 10
    PARTS_HEIGHT = 50
    KERF_HEIGHT = 20

    if inc &amp;lt; 0:
        inc = -inc
        stw = stw - inc * (num - 1)

    cw = (stw + inc * num + gap) * num + gap + MARGIN * 2
    ch = PARTS_HEIGHT + MARGIN * 2
    orign_x = MARGIN
    orign_y = MARGIN

    st1 = f'&amp;lt;svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{cw}mm" height="{ch}mm" viewBox="0 0 {cw} {ch}"&amp;gt;'
    ed1 = "&amp;lt;/svg&amp;gt;"

    temp = f'&amp;lt;path stroke="red" stroke-width="0.1" fill="none" d="M{orign_x},{orign_y}'
    txt = ""

    x = orign_x
    y = orign_y
    kerf_width = stw

    y += PARTS_HEIGHT
    temp += f" V{y}"
    x += gap
    temp += f" H{x}"

    for i in range(num):
        kerf_width = stw + inc * i
        kerf_width = round(kerf_width, 2)
        if len(str(kerf_width)) == 1:
            adjst = len(str(kerf_width)) - 1
        else:
            adjst = len(str(kerf_width)) - 2

        txt += f'&amp;lt;text x="{x - adjst}" y="25" font-size="3" fill="black"&amp;gt;{kerf_width}&amp;lt;/text&amp;gt;'

        y -= KERF_HEIGHT
        temp += f" V{y}"
        t_bone = f" A {tbone / 2} {tbone / 2} 0 0 1 {x} {y - tbone}"
        temp += t_bone
        x += kerf_width
        temp += f" H{x}"
        t_bone = f" A {tbone / 2} {tbone / 2} 0 0 1 {x} {y}"
        temp += t_bone
        y += KERF_HEIGHT
        temp += f" V{y}"
        x += gap
        temp += f" H{x}"

    y = y - PARTS_HEIGHT
    temp += f" V{y}"
    x = orign_x
    temp += f" H{x}"
    temp += '"/&amp;gt;'

    result = f"{st1}\n{temp}\n{txt}\n{ed1}"

    return result

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>vue</category>
      <category>flask</category>
      <category>vite</category>
      <category>python</category>
    </item>
    <item>
      <title>Web Development with Vite, Vue, and Flask</title>
      <dc:creator>ATSU</dc:creator>
      <pubDate>Thu, 07 Dec 2023 00:18:22 +0000</pubDate>
      <link>https://dev.to/atsu/web-development-with-vite-vue-and-flask-40ep</link>
      <guid>https://dev.to/atsu/web-development-with-vite-vue-and-flask-40ep</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll explore how to use Vue.js and Flask together to create an application. We'll cover the process of creating a front-end template using the development tool Vite and subsequently integrating a Flask back-end. This process is quite engaging, and I believe you'll find it intriguing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;br&gt;
Combining Vue.js for the front-end and Flask for the back-end in web application development offers several advantages, especially when leveraging their respective strengths.&lt;/p&gt;

&lt;p&gt;Python excels in handling backend operations, data manipulation, and complex mathematical calculations. Its vast ecosystem of libraries allows for easy integration with databases, data processing tools, and machine learning frameworks.&lt;/p&gt;

&lt;p&gt;Vue.js, a progressive JavaScript framework, is designed to build modern, interactive user interfaces. JavaScript, being the language of the web, is well-suited for client-side development. Vue.js simplifies the creation of dynamic, responsive, and visually appealing UIs. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a Vue front-end app template with Vite&lt;/li&gt;
&lt;li&gt;Integrating with Flask&lt;/li&gt;
&lt;li&gt;Creating a Docker image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Creating Vue app template&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to your development directory (e.g., &lt;code&gt;Projects&lt;/code&gt;) and execute the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn create vite&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;During the setup, you'll be prompted to answer several questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project name: vite-project (can be any name, for example: 'vite-project')
Select a framework: Vue
Select a variant: JavaScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A directory named &lt;code&gt;vite-project&lt;/code&gt; will be created. Navigate into it and run the &lt;code&gt;yarn&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd vite-project
yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you can already run the template application with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you visit &lt;a href="http://localhost:5173"&gt;http://localhost:5173&lt;/a&gt; in a web browser, you'll see the application's interface. There's a button in the center of the screen labeled "count is 0." Clicking this button will increment the count.&lt;/p&gt;

&lt;p&gt;Next, build the app.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The source will be compiled, and the artifacts will be placed in the &lt;code&gt;dist&lt;/code&gt; distribution directory. To perform a final check of the application, execute:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn preview&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Accessing &lt;a href="http://localhost:4173"&gt;http://localhost:4173&lt;/a&gt; in a browser, you should see the application interface. The app's behavior will be identical to what was seen with 'yarn dev', but with the compiled JavaScript running the show.&lt;/p&gt;

&lt;p&gt;The typical front-end creation process involves modifying the source code, checking its functionality with &lt;code&gt;yarn dev&lt;/code&gt;, and if there are no issues, executing &lt;code&gt;yarn build&lt;/code&gt;. The final operation is verified with &lt;code&gt;yarn preview&lt;/code&gt;, and this cycle continues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working with Flask&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initially, let's use Flask simply to display the application's interface. Flask defines where to place different types of resources, necessitating file placement according to these rules:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;static_folder&lt;/td&gt;
&lt;td&gt;JS, CSS, Location of static resources such as images&lt;/td&gt;
&lt;td&gt;static&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;template_folder&lt;/td&gt;
&lt;td&gt;Location of HTML files to be rendered by Flask&lt;/td&gt;
&lt;td&gt;templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;static_url_path&lt;/td&gt;
&lt;td&gt;Path name for accessing the static folder&lt;/td&gt;
&lt;td&gt;/static&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Comparing the actual artifacts in the &lt;code&gt;dist&lt;/code&gt; folder layout with the default values, some differences are apparent. Specifically, JS and CSS files are placed in the &lt;code&gt;dist/assets&lt;/code&gt; folder, but we aim to align the layout with Flask's requirements by renaming &lt;code&gt;assets&lt;/code&gt; to &lt;code&gt;static&lt;/code&gt; in the configuration.&lt;/p&gt;

&lt;p&gt;To do this, open the &lt;code&gt;vite.config.js&lt;/code&gt; file and add the following setting to &lt;code&gt;build.assetsDir&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],

  build: {
    assetsDir: "static",
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;yarn build&lt;/code&gt;, confirm that the output has changed to &lt;code&gt;dist/static&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, Flask takes the stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, render_template

app = Flask(__name__, static_folder="dist/static", template_folder="dist", static_url_path="/static")


@app.route("/", defaults={"path": ""})
@app.route("/&amp;lt;path:path&amp;gt;")
def index(path):
    return render_template("index.html")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Due to the layout generated by Vite differing from Flask's default, parameters like &lt;code&gt;static_folder&lt;/code&gt; are explicitly specified with actual values.&lt;/p&gt;

&lt;p&gt;Run the Flask server with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flask run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Accessing &lt;a href="http://localhost:5000"&gt;http://localhost:5000&lt;/a&gt; in a browser should display the application interface. The Vite logo might not appear, but the count-up functionality should work fine.&lt;/p&gt;

&lt;p&gt;The logo's absence is due to the image file's location. If you check the &lt;code&gt;dist&lt;/code&gt; folder, &lt;code&gt;vite.svg&lt;/code&gt; is found directly under &lt;code&gt;dist&lt;/code&gt;, but as per the table, it needs to be in the &lt;code&gt;static_folder&lt;/code&gt; (=&lt;code&gt;dist/static&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To address this, move &lt;code&gt;vite.svg&lt;/code&gt; from the &lt;code&gt;public&lt;/code&gt; directory to &lt;code&gt;static&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd public
mkdir static
mv vite.svg static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason for not manually handling files under &lt;code&gt;dist&lt;/code&gt; is to ensure they are automatically placed during &lt;code&gt;yarn build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Files in the &lt;code&gt;public&lt;/code&gt; directory are copied to the &lt;code&gt;dist&lt;/code&gt; directory, maintaining the tree structure during the build.&lt;/p&gt;

&lt;p&gt;Additionally, the source referencing &lt;code&gt;vite.svg&lt;/code&gt; must be updated. Relevant locations include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="icon" type="image/svg+xml" href="/static/vite.svg" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img src="/static/vite.svg" class="logo" alt="Vite logo" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;yarn build&lt;/code&gt;, &lt;code&gt;vite.svg&lt;/code&gt; should be in &lt;code&gt;dist/static&lt;/code&gt;. Re-run &lt;code&gt;flask run&lt;/code&gt; and visit &lt;a href="http://localhost:5000"&gt;http://localhost:5000&lt;/a&gt;. This time, the logo should display correctly!&lt;/p&gt;

&lt;p&gt;To further utilize Flask, let's have it handle the incrementing process. Add to &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.route("/increment/&amp;lt;int:count&amp;gt;")
def increment(count):
    return f"{count + 1}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the Vue side to request Flask to increment when the button is pressed. The relevant code is in &lt;br&gt;
&lt;code&gt;src/components/HelloWorld.vue&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="card"&amp;gt;
  &amp;lt;button type="button" @click="count++"&amp;gt;count is {{ count }}&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;
    Edit
    &amp;lt;code&amp;gt;components/HelloWorld.vue&amp;lt;/code&amp;gt; to test HMR
  &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;@click=...&lt;/code&gt; with a function that requests Flask. Implement 'incrementCount' as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const count = ref(0);

const incrementCount = () =&amp;gt; {
  fetch(`/increment/${count.value}`)
    .then((response) =&amp;gt; {
      if (response.ok) {
        return response.text();
      } else {
        throw new Error(`${response.status} ${response.statusText}`);
      }
    })
    .then((val) =&amp;gt; {
      count.value = parseInt(val);
    })
    .catch((error) =&amp;gt; {
      console.error(`Error: ${error.message}`);
    });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substitute the button press action with this function name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="card"&amp;gt;
  &amp;lt;button type="button" @click="incrementCount"&amp;gt;count is {{ count }}&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;
    Edit
    &amp;lt;code&amp;gt;components/HelloWorld.vue&amp;lt;/code&amp;gt; to test HMR
  &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify operation with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn build
flask run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating a Docker Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having completed most of our objectives, let's also try creating a Docker image.&lt;/p&gt;

&lt;p&gt;Use VSCode convenient feature to create a Dockerfile:&lt;/p&gt;

&lt;p&gt;Open the folder vite-project in VScode.&lt;br&gt;
(I assume you already have it open...)&lt;/p&gt;

&lt;p&gt;Access the command palette (Ctrl+Shift+P) and search for 'Add Docker Files'.&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;Docker: Add Docker Files to Workspace...&lt;/code&gt;. (If it doesn't appear, you might need to install the Docker extension).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Select Application Platform&lt;/code&gt;.&lt;br&gt;
→ &lt;code&gt;Python Flask&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Choose the app's entry point(e.g. manage.py, app.py)&lt;/code&gt;&lt;br&gt;
→ Choose &lt;code&gt;app.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;What port(s) does your app listen on? Enter a comma-separated list, or empty for no exposed port.&lt;/code&gt;&lt;br&gt;
→ Enter a comma-separated list, or empty for no exposed port. Select the displayed number as it is. It is probably 5002, but you can change it to any number you like.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Include optional Docker Compose files&lt;/code&gt;.&lt;br&gt;
→ &lt;code&gt;Do you want to create docker-compose.yml as well?&lt;/code&gt; Select &lt;code&gt;Yes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile Editing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make the following changes to the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Originally, we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
COPY . /app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the &lt;code&gt;COPY&lt;/code&gt; source to &lt;code&gt;./dist&lt;/code&gt; since we want the &lt;code&gt;dist&lt;/code&gt; directory in the container. Also, move &lt;code&gt;index.html&lt;/code&gt; from the &lt;code&gt;dist&lt;/code&gt; directory to the &lt;code&gt;templates&lt;/code&gt; directory to adhere to the standard Flask structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
COPY ./dist/ .
RUN mkdir -p templates &amp;amp;&amp;amp; mv index.html templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Modifying and Placing app.py&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the Docker version, the Flask layout has been reverted to default, so the optional parameters specified in &lt;code&gt;app.py&lt;/code&gt; should be removed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app = Flask(__name__, static_folder="dist/static", template_folder="dist", static_url_path="/static")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Docker, this can be simplified to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app = Flask(__name__)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy &lt;code&gt;app.py&lt;/code&gt; to the &lt;code&gt;public&lt;/code&gt; directory and run &lt;code&gt;yarn build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp app.py public
yarn build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build the Docker image again with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the container with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to &lt;a href="http://localhost:5002"&gt;http://localhost:5002&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Did it work?&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="http://localhost:5002"&gt;http://localhost:5002&lt;/a&gt; and check if the app works correctly!&lt;/p&gt;

&lt;p&gt;Finally, to avoid managing separate versions of &lt;code&gt;app.py&lt;/code&gt; for Docker and non-Docker environments, consider using a configuration file to set parameters like &lt;code&gt;static_folder&lt;/code&gt; based on the environment. This approach is simple yet effective.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;app.py&lt;/code&gt; could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
from flask import Flask, render_template

args = dict()
try:
    with open("args.json") as f:
        args = json.load(f)
except FileNotFoundError:
    pass

app = Flask(__name__, **args)


@app.route("/", defaults={"path": ""})
@app.route("/&amp;lt;path:path&amp;gt;")
def index(path):
    return render_template("index.html")


@app.route("/increment/&amp;lt;int:count&amp;gt;")
def increment(count):
    return f"{count + 1}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an &lt;code&gt;args.json&lt;/code&gt; file with the following contents:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;args.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "static_folder": "dist/static",
  "template_folder": "dist",
  "static_url_path": "/static"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, parameters can be environment-specific, and &lt;code&gt;app.py&lt;/code&gt; can be centrally managed. If &lt;code&gt;args.json&lt;/code&gt; is missing, Flask will catch the FileNotFoundError and default values will be used.&lt;/p&gt;

</description>
      <category>flask</category>
      <category>vue</category>
      <category>vite</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
