https://flask.palletsprojects.com/en/2.2.x/installation/
Create an environment
C:\flask_dev>py -3 -m venv venv
Activate the environment
C:\flask_dev>venv\Scripts\activate
Install Flask
venv C:\flask_dev>pip install Flask
#C:\flask_dev\flasktoexe\app.py
from flask import Flask,render_template
import webview
app = Flask(__name__, static_folder='./static', template_folder='./templates')
@app.route('/')
def home():
return render_template("index.html",name='cairocoders Home page')
@app.route('/page2')
def page2():
return render_template("page2.html",name='cairocoders page 2')
webview.create_window('Flask to exe', app)
if __name__ == '__main__':
#app.run(debug=True)
webview.start()
install pywebview pywebview is a lightweight cross-platform wrapper around a webview component that allows to display HTML content in its own native GUI window.
https://pypi.org/project/pywebview/
C:\flask_dev>pip install pywebview
templates/index.html
//templates/index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Calculator</title>
<link href="{{ url_for('static',filename='style.css') }}" rel="stylesheet">
<script src="{{ url_for('static',filename='script.js') }}" defer></script>
</head>
<body>
<div class="calculator-grid">
<div class="output">
<div data-previous-operand class="previous-operand"></div>
<div data-current-operand class="current-operand"></div>
</div>
<button data-all-clear class="span-two">AC</button>
<button data-delete class="operation">DEL</button>
<button data-operation class="operation">÷</button>
<button data-number>1</button>
<button data-number>2</button>
<button data-number>3</button>
<button data-operation class="operation">*</button>
<button data-number>4</button>
<button data-number>5</button>
<button data-number>6</button>
<button data-operation class="operation">+</button>
<button data-number>7</button>
<button data-number>8</button>
<button data-number>9</button>
<button data-operation class="operation">-</button>
<button data-number>.</button>
<button data-number>0</button>
<button data-equals class="span-two">=</button>
</div>
<a href="/page2">Page 2</a>
</body>
</html>
templates/page2.html
//templates/page2.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Page 2</title>
</head>
<body>
<h1>Page2</h1>
<a href="/">Home</a>
</body>
</html>
static/style.html
//static/style.html
*, *::before, *::after {
box-sizing: border-box;
font-family: Gotham Rounded, sans-serif;
font-weight: normal;
}
body {
margin: 0;
padding: 0;
background: linear-gradient(to right, #0187d0, #a5c2c8);
}
.calculator-grid {
display: grid;
justify-content: center;
align-content: center;
min-height: 100vh;
grid-template-columns: repeat(4, 100px);
grid-template-rows: minmax(120px, auto) repeat(5, 100px);
}
.calculator-grid > button {
cursor: pointer;
font-size: 2rem;
border: 1px, solid #FFFFFF;
outline: none;
}
.calculator-grid > button:hover {
background-color: #a5a5a5;
}
.span-two {
grid-column: span 2;
color: #adf802;
background-color: #fd9502;
}
.operation {
color: #ffffff;
background-color: #fd9502;
}
.output{
grid-column: 1 / -1;
background-color: rgba(0, 0, 0, 0.75);
display: flex;
align-items: flex-end;
justify-content: space-around;
flex-direction: column;
padding: 10px;
word-wrap: break-word;
word-break: break-all;
}
.output .previous-operand{
color: rgba(255,255, 255, 0.75);
font-size: 1.5rem;
}
.output .current-operand{
color: white;
font-size: 2.5rem;
}
static/script.js
//static/script.js
class Calculator {
constructor(previousOperandTextElement, currentOperandTextElement) {
this.previousOperandTextElement = previousOperandTextElement
this.currentOperandTextElement = currentOperandTextElement
this.clear()
}
clear() {
this.currentOperand = ''
this.previousOperand = ''
this.operation = undefined
}
delete() {
this.currentOperand = this.currentOperand.toString().slice(0, -1)
}
appendNumber(number) {
if (number === '.' && this.currentOperand.includes('.')) return
this.currentOperand = this.currentOperand.toString() + number.toString()
}
chooseOperation(operation) {
if (this.currentOperand === '') return
if (this.previousOperand !== '') {
this.compute()
}
this.operation = operation
this.previousOperand = this.currentOperand
this.currentOperand = ''
}
compute() {
let computation
const prev = parseFloat(this.previousOperand)
const current = parseFloat(this.currentOperand)
if (isNaN(prev) || isNaN(current)) return
switch (this.operation) {
case '+':
computation = prev + current
break
case '-':
computation = prev - current
break
case '*':
computation = prev * current
break
case '÷':
computation = prev / current
break
default:
return
}
this.currentOperand = computation
this.operation = undefined
this.previousOperand = ''
}
getDisplayNumber(number) {
const stringNumber = number.toString()
const integerDigits = parseFloat(stringNumber.split('.')[0])
const decimalDigits = stringNumber.split('.')[1]
let integerDisplay
if (isNaN(integerDigits)) {
integerDisplay = ''
} else {
integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
}
if (decimalDigits != null) {
return `${integerDisplay}.${decimalDigits}`
} else {
return integerDisplay
}
}
updateDisplay() {
this.currentOperandTextElement.innerText =
this.getDisplayNumber(this.currentOperand)
if (this.operation != null) {
this.previousOperandTextElement.innerText =
`${this.getDisplayNumber(this.previousOperand)} ${this.operation}`
} else {
this.previousOperandTextElement.innerText = ''
}
}
}
const numberButtons = document.querySelectorAll('[data-number]')
const operationButtons = document.querySelectorAll('[data-operation]')
const equalsButton = document.querySelector('[data-equals]')
const deleteButton = document.querySelector('[data-delete]')
const allClearButton = document.querySelector('[data-all-clear]')
const previousOperandTextElement = document.querySelector('[data-previous-operand]')
const currentOperandTextElement = document.querySelector('[data-current-operand]')
const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement)
numberButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.appendNumber(button.innerText)
calculator.updateDisplay()
})
})
operationButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.chooseOperation(button.innerText)
calculator.updateDisplay()
})
})
equalsButton.addEventListener('click', button => {
calculator.compute()
calculator.updateDisplay()
})
allClearButton.addEventListener('click', button => {
calculator.clear()
calculator.updateDisplay()
})
deleteButton.addEventListener('click', button => {
calculator.delete()
calculator.updateDisplay()
})
document.addEventListener('keydown', function (event) {
let patternForNumbers = /[0-9]/g;
let patternForOperators = /[+\-*\/]/g
if (event.key.match(patternForNumbers)) {
event.preventDefault();
calculator.appendNumber(event.key)
calculator.updateDisplay()
}
if (event.key === '.') {
event.preventDefault();
calculator.appendNumber(event.key)
calculator.updateDisplay()
}
if (event.key.match(patternForOperators)) {
event.preventDefault();
calculator.chooseOperation(event.key)
calculator.updateDisplay()
}
if (event.key === 'Enter' || event.key === '=') {
event.preventDefault();
calculator.compute()
calculator.updateDisplay()
}
if (event.key === "Backspace") {
event.preventDefault();
calculator.delete()
calculator.updateDisplay()
}
if (event.key == 'Delete') {
event.preventDefault();
calculator.clear()
calculator.updateDisplay()
}
});
run (venv) C:\flask_dev\flasktoexe>flask run convert to exe file
install Auto PY to EXE
Converts .py to .exe using a simple graphical interface.
https://pypi.org/project/auto-py-to-exe/
C:\>pip install auto-py-to-exe
execute terminal:
C:\>auto-py-to-exe

