per Ki aus Basic konvertiert
1. (alles markieren) Analyse: Stille-Finder
2. Bearbeiten: Audio mit Textmarke trennen
3. Datei: Exportieren -> Mehrere exportieren
NodeMCU-32S first project
Sketch hochladen
via brute force backtracking
""" funktionale Programmierung: - seiteneffektfrei, von keinem Zustand abhängig
https://www.youtube.com/watch?v=YZIUPOGx_ts&t=510s
Vorteile funktionaler vs. imperativer Programmierung
stateless, einfacher zu verstehen und zu testen, egal welche Reihenfolge der Methoden,
kann leichter parallelisert werden
(Rekursion)
---- Funktion höherer Ordnung
- Funktion die eine andere Funktion als Argument bekommt
und/oder
- Funktion die eine Funktion als Rückgabe hat
- Bsp. map (funktionale Äquivalent der for-Schleife), filter (wie map nur lambda ist Prädikat), reduce
---- Closure
- annonyme Funktion + Snapshot des Geltungsbereiches in sich diese Funktion befunden hat
Bsp.: def mal(x: int) -> Callable[[int], int]; multipliziere_liste(xs: list, k: int) -> list
---- Lambda
- annonyme Funktionen
---- Currying (nach Haskall Curry benannt: Programmiersprache Haskall nach ihm benannt)
- Funktion mit Anzahl von Argumenten bei der ein Argument festgehalten
und eine Funktion mit einer Stelligkeit weniger zurückgegeben wird
---- Filter (Liste + Funktion ist ein Prädikat)
- statt map eine weitere Funktion höherer Ordnung: filter
(lambda muss jetzt aber eiun Prädikat sein, also eine Funktion die Wahr oder Falsch zurückgibt)
---- Reduce oder "fold left" (Liste + zweistellige Funktion + Startwert)
- nimmt eine Liste + zweistellige Funktion + Startwert und reduziert damit die Liste auf ein Element
---- list comprehensions (Listen-Abstraktion)
Spezialität in Python
statt: list(map(lambda x: x * k, xs)) jetzt: [x * k for x in xs]
oder
statt: list(map(lambda x: x * k, xs)) jetzt: [x * k for x in xs]
"""
from typing import Callable
# Funktion höherer Ordnung, weil sie eine Funktion zurückgibt
def mal(x: int) -> Callable[[int], int]:
#Subfunktion Closure und Reduce
def mal_x(y : int) -> int:
return x * y
return mal_x
def multipliziere_liste(xs: list, k: int) -> list:
mal_k = mal(k)
# map gibt nur Iterator zurück, deshalb list
return list(map(mal_k, xs))
def multipliziere_liste_lambda(xs: list, k: int) -> list:
# map gibt nur Iterator zurück, deshalb list
return list(map(lambda x: x * k, xs))
# multipliziere_liste_lambda mit Nutzung von Python-Listen-Abstraktion
def multipliziere_liste_lambda_lc(xs: list, k: int) -> list:
return [x * k for x in xs]
def filter_liste(xs: list, k: int) -> list:
# map gibt nur Iterator zurück, deshalb list
return list(filter(lambda x: x > k, xs))
# filter_liste mit Nutzung von Python-Listen-Abstraktion
def filter_liste_lc(xs: list, k: int) -> list:
return [x for x in xs if x > k]
from functools import reduce
def summer(xs: list) -> int:
return reduce(lambda x1, x2 : x1 + x2, xs, 0)
mal_drei = mal(3)
print("mal_drei = Closure mal(3) : {0}".format(mal_drei))
print("mal_drei(5) = {0}".format(mal_drei(5)))
factor = 4
liste = [1,4,3,7]
res = multipliziere_liste(liste,4)
print("mit {0} multipliziere_liste {1} = {2}".format(factor, liste, res))
aLambda = lambda x: x+1
print("a lambda funktion: {0}".format(aLambda))
res = multipliziere_liste_lambda(liste,4)
print("mit {0} multipliziere_liste_lambda {1} = {2}".format(factor, liste, res))
res = filter_liste(liste,3)
print("filter_liste {0} mit Prädikat Elemente > 3 = {1}".format(liste, res))
print("Summe (reduce) {0}; lambda: x1+x2; start: 0 = {1}".format(liste,summer(liste)))
res = multipliziere_liste_lambda_lc(liste,4)
print("mit {0} multipliziere_liste_lambda_lc {1} = {2}".format(factor, liste, res))
res = filter_liste_lc(liste,3)
print("filter_liste_lc {0} mit Prädikat Elemente > 3 = {1}".format(liste, res))
# funktionale Audrücke mit einer Zeile
def anzahl_gerade(xs: list):
return [x for x in xs if x % 2 == 0]
print("anzahl_gearde {0} = {1}".format(liste, anzahl_gerade(liste)))
# curry
def foo(x: int, y: int, z: int):
return x +y +z
def foo(x: int) -> Callable[[int], int]:
def addiere_x(y : int) -> int:
return x + y
return addiere_x
print(foo(foo(5)(3))(2))
#bar = curry(foo, 5)
# bar = curry(foo,5)
# bar(3,2) -> 10 (5+3+2)
# mein_filter
# mein_filter(lambda x: x>3, [1,2,3,4,5] ) -> [4,5]
def gt(xs: list) -> list:
return [x for x in xs if x > 3]
print("gt 3: {0} = {1}".format(liste,gt(liste)))
// Kotlin Lambdas
fun f ( a:String, b:String ) : String { return a + "_fun_" + b + "!" }
var f = { a:String, b:String -> a + "_lambda_" + b + "!" }
// Properties
val stringToInt: (String) -> Int = String::toInt
val intToString: (Int) -> String = Int::toString
val stringPlus: (String, String) -> String = String::plus
fun main() {
println( f("Hello","world") ) // output: Hello_fun_world!
println( f.invoke("Hello","world") ) // invoke the lambda explicit; output: Hello_lambda_world!
println( (stringToInt("3") +4) ) // output: 7
println( (intToString(3) +4) ) // output: 34
println(stringPlus("Hello, ", "world!")) // output: Hello, world!
}
filename = "lorem.txt"
# filename = "ctArtikel.txt"
# filename = "ringelnatz.txt"
f = open(filename, "r")
# generate demotext: https://www.loremipsum.de/
# redundant words (stopwords): https://github.com/solariz/german_stopwords
wordscloud = {}
words = []
for line in f.readlines():
fwords = line.strip().split(" ")
for word in fwords:
word = word.replace('(','').replace(')','')
if len(word) > 1:
words.append(word)
if word in wordscloud:
wordscloud[word] = wordscloud[word] +1
else:
wordscloud[word] = 1
f.close()
print("words count: " + str(len(words)))
# all words with their frequences sorted descendant
sort_orders = sorted(wordscloud.items(), key=lambda x: x[1], reverse=True)
print(sort_orders)
top10 = {}
# words of the heigest ten frequences ()
for item in sort_orders:
if len(top10) > 9:
break
frequence = item[1]
word = item[0]
top10[frequence] = word
print("most important (frequence representant) words: " + str(top10) )
import random
l = list(top10.keys())
random.shuffle(l)
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'PyQt5 word cloud (' + filename+ ')'
self.width = 600
self.height = 600
self.initUI()
def getSpannedText(self, word, size):
return "<span style='font-size:" + str(size) + "pt; color:white;'>" + word + " </span>"
def average(self,lst):
return sum(lst) / len(lst)
def initUI(self):
self.setWindowTitle(self.title)
self.setMinimumSize(self.width, self.height)
widget = QWidget()
layout= QHBoxLayout()
widget.setLayout(layout)
s = ""
maxSize = 100
scale = maxSize / self.average(l)
print(scale)
for index in l:
word = top10[index]
size = index * scale
s = s + self.getSpannedText(word, size)
print("size: " + str(size))
label = QLabel(word,self)
label.setWordWrap(True)
label.setText(s)
layout.addWidget(label)
self.setCentralWidget(widget)
self.show()
if __name__=='__main__':
app=QApplication(sys.argv)
ex=App()
sys.exit(app.exec_())
import math, sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# https://wiki.python.org/moin/PyQt5/Threading%2C_Signals_and_Slots
class Window(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.thread = Worker()
self.startButton = QPushButton(self.tr("&Load image"))
self.stopButton = QPushButton(self.tr("&Stop image loading"))
self.stopButton.setEnabled(False)
self.viewer = QLabel()
self.viewer.setFixedSize(1200, 1200)
self.thread.finished.connect(self.updateUi)
#self.thread.terminated.connect(self.updateUi)
self.thread.output['QRect', 'QImage'].connect(self.addImage)
self.startButton.clicked.connect(self.makePicture)
self.stopButton.clicked.connect(self.stopLoadingPicture)
layout = QGridLayout()
layout.addWidget(self.startButton, 0, 0)
layout.addWidget(self.stopButton, 0, 1)
layout.addWidget(self.viewer, 1, 0, 1, 3)
self.setLayout(layout)
self.setWindowTitle(self.tr("Loading image by thread"))
def makePicture(self):
self.startButton.setEnabled(False)
self.stopButton.setEnabled(True)
pixmap = QPixmap(self.viewer.size())
pixmap.fill(Qt.black)
self.viewer.setPixmap(pixmap)
self.thread.render("img/man.jpg")
def stopLoadingPicture(self):
self.stopButton.setEnabled(False)
self.thread.__del__()
def addImage(self, rect, image):
pixmap = self.viewer.pixmap()
painter = QPainter()
painter.begin(pixmap)
painter.drawImage(rect, image)
painter.end()
self.viewer.update(rect)
def updateUi(self):
self.startButton.setEnabled(True)
class Worker(QThread):
output = pyqtSignal(QRect, QImage)
def __init__(self, parent = None):
QThread.__init__(self, parent)
self.exiting = False
def __del__(self):
self.exiting = True
self.wait()
def render(self, name):
self.exiting = False
self.name = name
self.start()
def run(self):
# Note: This is never called directly. It is called by Qt once the
# thread environment has been set up.
img = QPixmap(self.name).toImage()
width = img.width()
height = img.height()
image = QImage(width, height,
QImage.Format_ARGB32)
image.fill(qRgba(0, 0, 0, 0))
y = 0
while y < height:
x = 0
while x < width:
if self.exiting:
return
painter = QPainter()
painter.begin(image)
c = img.pixel(x,y)
color = QColor(c)
painter.setPen(color)
painter.drawPoint(x,y)
painter.end()
x = x+1
if y % 30 == 0:
self.output.emit( QRect(0, 0, width, height), image)
y = y +1
self.output.emit( QRect(0, 0, width, height), image)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Im Qt Designer erstelltes UI
Create UI with QT Designer (https://build-system.fman.io/qt-designer-download)
Generate Python code from exported ui: pyuic5 -x -o MainWindow.py main.ui
adjust the slot methods: inc dec
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
# use QT Designer design the ui and save as main.ui
# pyuic5 -x -o MainWindow.py main.ui
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(10, 10, 113, 32))
self.pushButton.setObjectName("pushButton")
self.incButton = QtWidgets.QPushButton(self.centralwidget)
self.incButton.setGeometry(QtCore.QRect(130, 90, 113, 32))
self.incButton.setObjectName("incButton")
self.decButton = QtWidgets.QPushButton(self.centralwidget)
self.decButton.setGeometry(QtCore.QRect(130, 280, 113, 32))
self.decButton.setObjectName("decButton")
self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
self.lcdNumber.setGeometry(QtCore.QRect(70, 120, 251, 151))
self.lcdNumber.setObjectName("lcdNumber")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.pushButton.clicked.connect(MainWindow.close)
self.incButton.clicked.connect(self.inc)
self.decButton.clicked.connect(self.dec)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def inc(self):
self.lcdNumber.display(self.lcdNumber.value() + 1)
def dec(self):
self.lcdNumber.display(self.lcdNumber.value() - 1)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Close"))
self.incButton.setText(_translate("MainWindow", "+1"))
self.decButton.setText(_translate("MainWindow", "-1"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Alternative: import the ui (https://nitratine.net/blog/post/how-to-import-a-pyqt5-ui-file-in-a-python-gui/)
from PyQt5 import QtWidgets, uic
import sys
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('main.ui', self)
self.show()
self.lcdNumber = self.findChild(QtWidgets.QLCDNumber, 'lcdNumber')
self.incButton = self.findChild(QtWidgets.QPushButton, 'incButton')
self.decButton = self.findChild(QtWidgets.QPushButton, 'decButton')
self.incButton.clicked.connect(self.inc)
self.decButton.clicked.connect(self.dec)
def inc(self):
self.lcdNumber.display(self.lcdNumber.value() + 1)
def dec(self):
self.lcdNumber.display(self.lcdNumber.value() - 1)
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
The official pyQt: https://pyside.github.io/docs/pyside/
sudo apt update
sudo apt install openjdk-8-jdk
wget https://services.gradle.org/distributions/gradle-5.6-bin.zip -P /tmp
sudo unzip -d /opt/gradle /tmp/gradle-5.6*.zip
sudo nano /etc/profile.d/gradle.sh
export GRADLE_HOME=/opt/gradle/gradle-5.6
export PATH=${GRADLE_HOME}/bin:${PATH}
sudo chmod +x /etc/profile.d/gradle.sh
source /etc/profile.d/gradle.sh
gradle -v
<!DOCTYPE html>
<html>
<head>
<!-- CSS -->
<style>
#div1 {
height: 500px;
width: 500px;
margin-left: 50px;
-webkit-perspective: 1000px; // Safari 4-8
perspective: 1000px;
}
#div2 {
padding: 60px;
-webkit-transform-style: preserve-3d; // Safari 3-8
-webkit-transform: rotateX(-50deg); // Safari 3-8
transform-style: preserve-3d;
transform: rotateX(-50deg);
}
table,
td,
caption {
border: 1px solid black;
border-collapse: collapse;
padding: 0.4em;
text-align: center;
}
td:nth-of-type(6),
td:nth-of-type(7) {
background: #ebf5d7;
color: #666;
}
caption,
tr:first-of-type {
background: #666;
color: #c4ced3;
font-weight: bold;
}
td.heute {
color: red;
font-weight: bold;
background: #ffebe6;
}
</style>
</head>
<body>
<a href="#"onclick="window.print();">Drucken</a>
<canvas id="CanvasText" width="500" height="280">
<!-- fallback contentwould go here -->
</canvas>
<table style="width:100%">
<tr>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="0"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="1"> </table>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="2"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="3"> </table>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="4"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="5"> </table>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="6"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="7"> </table>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="8"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="9"> </table>
</div>
</div>
</td>
</tr>
<tr>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="10"> </table>
</div>
</div>
</td>
<td>
<div id="div1">
<h1><div id="div2">
<table name="calendar" year="2020" month="11"> </table>
</div>
</div>
</td>
</tr>
</table>
</body>
<script>
// variant 1: draws on canvas
var canvas = document.getElementById('CanvasText');
var context = canvas.getContext('2d');
context.font = 'bold 20px Arial, sans-serif';
context.fillText('August 2019', canvas.width / 2 - 60, 30);
context.textBaseline = 'bottom';
function drawTextAlongArc(context, str, centerX, centerY, radius,
angle) {
var len = str.length,
s;
context.save();
context.translate(centerX, centerY);
context.rotate(-1 * angle / 2);
context.rotate(-1 * (angle / len) / 2);
for (var n = 0; n < len; n++) {
context.rotate(angle / len);
context.save();
context.translate(0, -1 * radius);
s = str[n];
context.fillText(s, 0, 0);
context.restore();
}
context.restore();
}
var canvas = document.getElementById('CanvasText'),
context = canvas.getContext('2d'),
centerX = canvas.width / 2,
centerY = canvas.height - 30,
angle = Math.PI * 0.3,
radius = 200;
context.font = '20pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'blue';
context.strokeStyle = 'blue';
context.lineWidth = 4;
drawTextAlongArc(context, 'M D M D F S S', centerX,
centerY +20, radius, angle);
context.font = '16pt Calibri';
drawTextAlongArc(context, ' 1 2 3 4', centerX, centerY +30,
radius-10, angle);
context.font = '14pt Calibri';
drawTextAlongArc(context, '5 6 7 8 9 10 11', centerX, centerY +45,
radius-20, angle);
context.font = '13pt Calibri';
drawTextAlongArc(context, '12 13 14 15 16 17 18', centerX, centerY
+51, radius-40, angle);
context.font = '11pt Calibri';
drawTextAlongArc(context, '19 20 21 22 23 24 25', centerX, centerY
+67, radius-54, angle);
context.font = '10pt Calibri';
drawTextAlongArc(context, '26 27 28 29 30 31', centerX, centerY +80,
radius-70, angle);
// variant 2: generates a table for a calendar sheet (only works on Chrome browser)
var tabelle = document.getElementsByName('calendar');
var i;
for (i = 0; i < tabelle.length; i++) {
var d = new Date();
d.setDate(1)
d.setMonth(tabelle[i].getAttribute("month"))
d.setFullYear(tabelle[i].getAttribute("year"))
var dm = d.getMonth() + 1;
var dj = d.getYear() + 1900;
Kalender(tabelle[i],dm, dj);
}
function Kalender(Where, Monat, Jahr) {
Monatsname = new Array("Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember");
Tag = new Array("Mo", "Di", "Mi", "Do", "Fr", "Sa", "So");
// aktuelles Datum für die spätere Hervorhebung ermitteln
var jetzt = new Date();
var DieserMonat = jetzt.getMonth() + 1;
var DiesesJahr = jetzt.getYear() + 1900;
var DieserTag = jetzt.getDate();
// ermittle Wochentag des ersten Tags im Monat halte diese Information in Start fest
var Zeit = new Date(Jahr, Monat - 1, 1);
var Start = Zeit.getDay();
if (Start > 0) {
Start--;
} else {
Start = 6;
}
// die meisten Monate haben 31 Tage...
var Stop = 31;
// ...April (4), Juni (6), September (9) und November (11) haben nur 30 Tage...
if (Monat == 4 || Monat == 6 || Monat == 9 || Monat == 11) --Stop;
// ...und der Februar nur 28 Tage...
if (Monat == 2) {
Stop = Stop - 3;
// ...außer in Schaltjahren
if (Jahr % 4 == 0) Stop++;
if (Jahr % 100 == 0) Stop--;
if (Jahr % 400 == 0) Stop++;
}
var tabelle = Where
// schreibe Tabellenüberschrift
var Monatskopf = Monatsname[Monat - 1] + " " + Jahr;
var caption = tabelle.createCaption();
caption.innerHTML = Monatskopf;
// schreibe Tabellenkopf
var row = tabelle.insertRow(0);
for (var i = 0; i <= 6; i++) {
var cell = row.insertCell(i);
cell.innerHTML = Tag[i];
}
// ermittle Tag und schreibe Zeile
var Tageszahl = 1;
for (var i = 0; i <= 4; i++) {
var row = tabelle.insertRow(1 + i);
for (var j = 0; j <= 6; j++) {
// Zellen vor dem Start-Tag in der ersten Zeile und Zeilen nach dem Stop-Tag werden leer aufgefüllt
if (((i == 0) && (j <= 5) && (j < Start)) || (Tageszahl > Stop)) {
var cell = row.insertCell(j);
cell.innerHTML = ' ';
} else {
// normale Zellen werden mit der Tageszahl befüllt und mit der Klasse Kalendertag markiert
var cell = row.insertCell(j);
cell.innerHTML = Tageszahl;
cell.className = 'kalendertag'
// und der aktuelle Tag (heute) wird noch einmal speziell mit der Klasse "heute" markiert
if ((Jahr == DiesesJahr) && (Monat == DieserMonat) && (Tageszahl ==
DieserTag)) {
cell.className = cell.className + ' heute';
}
Tageszahl++;
}
}
}
}
</script>
</html>
In Windows Powershell (als Adminitsrator):
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Subsystem (Ubuntu 18.04) Installieren:
https://www.microsoft.com/de-de/p/ubuntu-1804-lts/9n9tngvndl3q?rtc=1&activetab=pivot:overviewtab
Java (openjdk 8) installieren:
$ sudo apt update
$ sudo apt install openjdk-8-jre
($ sudo apt install openjdk-8-jdk)
AndroidStudio 3.5 installieren:
$ sudo add-apt-repository ppa.maarten-fonville/android-studio
$ sudo apt-get install android-studio
$ opt/android-studio/bin$ ./studio.sh
Startup Error: Unable to detect graphics environment
$ sudo apt install xinit
VcXsrv Windows X Server (https://sourceforge.net/projects/vcxsrv/files/latest/download)
XLaunch make settings: display 0 (save config)
$ sudo apt-get install x11-apps
$ sudo apt install vlc
$ sudo apt install blockout2
$ sudo apt install firefox
$ export DISPLAY=:0
opt/android-studio/bin$ ./studio.sh
TODO Problem: KVM zu installieren (Grundlage für Android Emulator) oder USB Verbindung herzustellen zu Android Device (per TCP mgl., dann aber weitere Instanz von Android Studio unter Windows notwendig, um Devices per adb zu sehen in Linux Umgebung)
Ktor Samples: