termux图形界面,采用termux-x11,waylnad驱动

以前在手机上显示temux桌面,采用vnc, 与xdsl大法。而xdsl大法非常方便。无论是外接键盘还是鼠标什么,就与电脑一样的操作,但是xserver-xdsl采用的图形驱动是xserver的,现在有大神写了termux-x11,采用的是wayland驱动。于是网上找到别人写的教程,我也试试。

一 安装一些东东, 假设termux安装完成。x11的那几个东东也安装完成了。去github上的termux-x11下载termux-x11.deb或有个apk文件到手机上,安装之。apk文件安装不用说了,如果要安装termux-x11.deb可能会出错。
1
2
3
4
5
6
7
8
$ pkg update & upgrade
$ pkg install x11-repo -y # 这一步可不用,因为在安装termux时己经装了。
$ pkg install termux-x11 xwayland -y
$ dpkg -i termux-x11.deb # 如果出错,是因为有些关联没有安装则
$ apt install -f
$ pkg update & upgrade
# 然后再次执行 dpkg -i termux-x11.deb
$ pkg install xfce4
二 在termux-x11下启动桌面
  1. 刚才安装的apt,会在安卓下生产Termux:X11的app图标,执行之。或

    1
    2
    3
    $ termux-x11  #会出现一个termux-X11的窗口   
    $ export DISPLAY=:0
    $ dbus-launch --exit-with-session startxfce4

    会在termux-x11的窗口出现xfce4的桌面。
    termux-x11下显示本地桌面

外接蓝牙键盘与鼠标可用,但是国为手机太小,又老花眼了,手指鼠标没有试

手机投屏电脑的软件之一,Scrcpy

2023年10月在debian12下重新用了一次,此时版本为2.1.1

  1. 到gihub上下载最新版安装如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ sudo apt install ffmpeg libsdl2-2.0-0 adb wget \
    gcc git pkg-config meson ninja-build libsdl2-dev \
    libavcodec-dev libavdevice-dev libavformat-dev libavutil-dev \
    libswresample-dev libusb-1.0-0 libusb-1.0-0-dev
    $ git clone https://github.com/Genymobile/scrcpy
    $ cd scrcpy
    $ ./install_release.sh
    # 安装成功,如果以后有新的版本,安装命令如下
    $ cd scrcpy
    $ git pull
    $ ./install_release.sh
  2. 打开手机“允许USB调试”,用用USB线与电脑链接,手机会弹出是否充许电脑对手机进行调试,要在手机上点击允许。
  3. 有线连接
    1
    2
    3
    4
    5
    $ adb devices
    List of devices attached
    XZ9CRS8ED unauthorized
    # 表示连上手机了
    $ scrcpy # 就可以用了
  4. 无线连接
    1
    2
    3
    4
    5
    6
    7
    # 退出上一步的有线,并执行
    $ adb shell ip a # 查看手机的IP地址,如果为192.168.8.666
    $ adb tcpio 5555
    # 提示“restarting in TCP mode port: 5555”
    # 可以拔掉USB线了
    $ adb connect 192.168.8.666:5555
    $ scrcpy
  5. 快捷键,利于控制,MOD键,默认为左边的Alt键,可通过如下命令更改
    1
    2
    3
    4
    5
    # 要更改使用右 Ctrl 键,则执行
    scrcpy --shortcut-mod=rctrl
    使用左 Ctrl 键 + 左 Alt 键,或 Super 键
    scrcpy --shortcut-mod=lctrl+lalt,lsupe
    # 可选的按键有 lCtrl、rCtrl、lAlt、rAlt、lSuper`和 rSuper
    MOD+f : 把scrcpy这个窗口在电脑上全屏
    MOD+左箭头 : 向左旋转scrcpy窗口,注意手机的屏幕并没有旋转
    MOD+右箭头 : 向右旋转scrcpy窗口,注意手机的屏幕并没有旋转
    MOD+h 或 鼠标中键 :反回手机主屏幕
    MOD+b 或 鼠标右键双击两次 : 返回
    MOD+s : 切换手机中的App
    MOD+m : 手机熄屏时,可以解锁屏幕
    双击鼠标右键两次 : 手机熄屏时,可以解锁屏幕
    MOD+o :关闭手机屏幕,但在电脑上还是显示。这里人为开手机屏幕也得了。得用下个快捷键打开。这一点非常好,手机省电了
    MOD+Shift+o : 打开手机屏幕。
    MOD+r : 旋转手机屏幕,按一次横屏,再按一次坚屏
    如果把MOD换成 Ctrl键,测Ctrl+c Ctrl+x Ctrl+v也可以用了
    Ctrl+Shift+V 把手机上的剪贴本放到电脑上

以下为2022年连接时的记录

两年前Scrcpy还不成熟时,试用过一下,不好用。就没有用了,现在的版本好了很多,也稳定了好多。趁电脑安装ubuntu20.04最新版时,又试用了一下。相当不错,链接两个小时,没有一次掉线。又怕忘记如何链接的,就做个笔记。
一 安装
1
2
$ sudo snap install scrcpy # 用apt install scrcpy也可以,但是采用snap安装,可以安装比较新的版本
$ sudo apt install android-tools-adb
二 手机与电脑先物理连接

打开手机“允许USB调试”,用用USB线与电脑链接

三 配置
1
$ lsusb  # 查看链接到USB接品的设备

USB device
可以看到,我的手机的ID号是:18d1:4ee2

1
2
3
4
5
6
7
8
$ mkdir ~/.android
$ eho 0x18d1 > ~/.android/adb_usb.ini
$ cd /etc/udev/rules.d
$ vim android.rules
# 写入
SUBSYTEM=="usb", SYSFS{idVendor}=="04e8", MODE="0666"
# 保存退出
$ sudo chmod 777 android.rules
四 启动服务
1
2
3
$ sudo service udev restart
$ adb start-server
$ adb devices

终端会显示
说明己经连接上了手机。则可以启动scrcpy了。

1
$ scrcpy

可以链接了。

五 无线链接

首先 手机与电脑要保持在一个局域网内,先用USB线连接手机

1
$ adb tcpip 5555 #设端口为5555

把线拔掉

1
2
3
4
$ adb connect 192.168.8.218:5555 # 192.168.8.218是手机的IP地址
$ scrcpy
$ scrcpy --turn-screen-off # 连接时,可关掉手机屏幕
$ scrcpy --bit-rate 2M --max-size 800 # 降低无线链接的速率(2M),与屏幕分辨率(800),以保持无线链接不卡线

手机投屏电脑的软件之一,Scrcpy

两年前Scrcpy还不成熟时,试用过一下,不好用。就没有用了,现在的版本好了很多,也稳定了好多。趁电脑安装ubuntu20.04最新版时,又试用了一下。相当不错,链接两个小时,没有一次掉线。又怕忘记如何链接的,就做个笔记。
一 安装
1
2
$ sudo snap install scrcpy # 用apt install scrcpy也可以,但是采用snap安装,可以安装比较新的版本
$ sudo apt install android-tools-adb
二 手机与电脑先物理连接

打开手机“允许USB调试”,用用USB线与电脑链接

三 配置
1
$ lsusb  # 查看链接到USB接品的设备

USB device
可以看到,我的手机的ID号是:18d1:4ee2

1
2
3
4
5
6
7
8
$ mkdir ~/.android
$ eho 0x18d1 > ~/.android/adb_usb.ini
$ cd /etc/udev/rules.d
$ vim android.rules
# 写入
SUBSYTEM=="usb", SYSFS{idVendor}=="04e8", MODE="0666"
# 保存退出
$ sudo chmod 777 android.rules
四 启动服务
1
2
3
$ sudo service udev restart
$ adb start-server
$ adb devices

终端会显示
说明己经连接上了手机。则可以启动scrcpy了。

1
$ scrcpy

可以链接了。

五 无线链接

首先 手机与电脑要保持在一个局域网内,先用USB线连接手机

1
$ adb tcpip 5555 #设端口为5555

把线拔掉

1
2
3
4
$ adb connect 192.168.8.218:5555 # 192.168.8.218是手机的IP地址
$ scrcpy
$ scrcpy --turn-screen-off # 连接时,可关掉手机屏幕
$ scrcpy --bit-rate 2M --max-size 800 # 降低无线链接的速率(2M),与屏幕分辨率(800),以保持无线链接不卡线

修改Anlinux的shell,以安装ubuntu的新版本

多年前采用anlinux的app,来在termux上安装linux,后面有大神写出了proot-distro的东东来安装linux非常方便,还能通过修改/etc/proot-distro/目录下的文件,选择各种版本的linux,于是果断放弃了anlinux转用proot-distoo。然而proot-distro的作者却自己封装了linux的rootfs文件,没有辣么方便了,又从proot-distro更新到2.9.3后,竟然连我以前装的linux版本都启动不了了。于是想起了以前的anlinux的app,拿它与proot-distro的shell比较过,觉得它更简单,于是修改之,自己安装Canonical官方发布的arm版 ubuntu21.04,例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/data/data/com.termux/files/usr/bin/bash
folder=ubuntu-hirsute
if [ -d "$folder" ]; then
first=1
echo "skipping downloading"
fi
# 下载Canonical官方发布的 ubutnu21.04 的 rootfs
wget "https://partner-images.canonical.com/core/hirsute/current/ubuntu-hirsute-core-cloudimg-arm64-root.tar.gz"
tarball="ubuntu-hirsute-core-cloudimg-arm64-root.tar.gz"
cur=`pwd`
mkdir -p "$folder"
cd "$folder"
echo "Decompressing Rootfs, please be patient."
proot --link2symlink tar -zxvf ${cur}/${tarball}||:
cd "$cur"

mkdir -p ubuntu-binds
bin=start-hirsute.sh
echo "writing launch script"
cat > $bin <<- EOM
#!/bin/bash
cd \$(dirname \$0)
## unset LD_PRELOAD in case termux-exec is installed
unset LD_PRELOAD
command="proot"
command+=" --link2symlink"
command+=" -0"
command+=" -r $folder"
if [ -n "\$(ls -A ubuntu-binds)" ]; then
for f in ubuntu-binds/* ;do
. \$f
done
fi
command+=" -b /dev"
command+=" -b /proc"
command+=" -b ubuntu-hirsute/root:/dev/shm"
## uncomment the following line to have access to the home directory of termux
#command+=" -b /data/data/com.termux/files/home:/root"
## uncomment the following line to mount /sdcard directly to /
command+=" -b /sdcard"
command+=" -b /storage"
command+=" -w /root"
command+=" /usr/bin/env -i"
command+=" HOME=/root"
command+=" PATH=/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/games:/usr/local/games"
command+=" TERM=\$TERM"
command+=" LANG=C.UTF-8"
command+=" /bin/bash --login"
com="\$@"
if [ -z "\$1" ];then
exec \$command
else
\$command -c "\$com"
fi
EOM

echo "fixing shebang of $bin"
termux-fix-shebang $bin
echo "making $bin executable"
chmod +x $bin
echo "removing image for some space"
rm $tarball
echo "You can now launch Ubuntu with the ./${bin} script"
安装完成后,执行 ./start-hirsute.sh 进入ubuntu21.04,发现不能apt update,后来发现是没有dns解析,于是
1
2
3
4
5
6
cd /etc
vim resolv.conf
添加
nameserver 114.114.114.114
nameserver 8.8.8.8
保存后退出
进入linux后,出现以下错误
1
2
3
4
goups: cannot find name for group ID 3003
groups: cannot find name for group ID 9997
goups: cannot find name for group ID 20xxx
groups: cannot find name for group ID 50xxx

这是由于termux安装ubuntu后没有给原本Android的系统用户组赋予名称导致的。由于每次在不同设备上安装termux.apk分配的u0_id (whoamis查到是u0_axxx)中的id值是不同的,这个需要每次改一下
解决方法:
1.用startubuntu进入系统su
2.执行以下命令

1
2
3
4
$ addgroup --system --gid 3003 inet
$ addgroup --system --gid 9997 sdcard_rw
$ addgroup --system --gid 50xxx all_axxx
$ reset

streamlit学习笔记五-----暂称全局变量吧

在编写一个streamlit代码时,发现有一个变量会在几个函数中使用,可是可这个变量如果定义在外的话,每个函数调用改变它后,虽的函数再调用,这个变量并不是上次改过的值,值还是初始化时赋的值。比如 代码一
1
2
3
4
5
6
7
8
9
10
11
12
import streamlit as st

st.title('代码一例子')
count = 0

increment = st.button('增加')
if increment:
count += 1

st.write('Count = ', count)

# 这个代码执行后,不管你咋按那个按钮,count都是1。
肿么才能避免呢? 采用 st.session_state,streamlit把它叫做 ”会话状态“
1
2
3
4
5
6
7
8
9
10
import streamlit as st

# 检查 'key' 是否存在于 session_state 中
# 如果不存在,则初始化之
if 'key' not in st.session_state:
st.session_state['key'] = 'value'

# 另一种语法
if 'key' not in st.session_state:
st.session_state.key = 'value'

一. 采用 “会话状态”,那么代码一可以改为代码二

1
2
3
4
5
6
7
8
9
10
11
12
import streamlit as st

st.title('代码一改为代码二')
# 检查 'count' 是否在 session_state中
if 'count' not in st.session_state:
st.session_state.count = 0

increment = st.button('增加')
if increment:
st.session_state.count += 1

st.write('Count = ', st.session_state.count)

二. 使用 “会话状态” 与 “回调函数” 来修改代码一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import streamlit as st

st.title('采用回调来更改代码一')
# 检查 'count' 是否在 session_state中
if 'count' not in st.session_state:
st.session_state.count = 0

# 定义函数,一会儿就要回调它
def increment_counter():
st.session_state.count += 1

# 在 st.button里回调函数 increment_counter()
st.button('Increment', on_click=increment_counter)

st.write('Count = ', st.session_state.count)

三. 以上的回调函数是木有参数的,那么如果有参数呢?

1. 使用 args 小部件来传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import streamlit as st

st.title('回调函数有参数')
if 'count' not in st.session_state:
st.session_state.count = 0

increment_value = st.number_input('输入一个数值', value=0, step=1)

# 函数有参数
def increment_counter(increment_value):
st.session_state.count += increment_value
# 采用 args 把参数导入
increment = st.button('Increment', on_click=increment_counter,
args=(increment_value, ))

st.write('Count = ', st.session_state.count)
2. 使用 kwargs 传递命名参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import streamlit as st

st.title('回调参数用 kwargs 传递参数')
if 'count' not in st.session_state:
st.session_state.count = 0

def increment_counter(increment_value=0):
st.session_state.count += increment_value

def decrement_counter(decrement_value=0):
st.session_state.count -= decrement_value

st.button('Increment', on_click=increment_counter,
kwargs=dict(increment_value=5))

st.button('Decrement', on_click=decrement_counter,
kwargs=dict(decrement_value=1))

st.write('Count = ', st.session_state.count)

四. 会话状态(全局变量)与 小部件中的值关联,采用 “key” 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import streamlit as st

if "celsius" not in st.session_state:
# set the initial default value of the slider widget
st.session_state.celsius = 50.0

st.slider(
"Temperature in Celsius",
min_value=-100.0,
max_value=100.0,
key="celsius" # 采用 key 把slider小部件中的值与全局变量celsius关联
)

# This will get the value of the slider widget
st.write(st.session_state.celsius)

flask全局变量三,视图间的参数传递

在”flask全局变量一“,时,我讲了采用session[“变量名”],来定义全局变量,实现在视图间传递变量。现在用用另一名方法,用上下文的方法,采用g变量,来定义全局变量。比方法一要简单
有几个上下文装饰器
装饰器名 方式
@app.before_request 函数会在请求处理之前被调用
@app.after_request 函数会在请求处理完成后才被调用
@app.teardown_request 函数会在请求处理完成后才被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from flask import Flask
from flask import g, request

app=Flask(__name__)

@app.before_request
def span_num_init():
g.start_num = 0 # 定义g变量
g.span_mum = 5 # 定义g变量

@app.route("/fun1")
def fun1():
g.start_num=9
g.span_num=16

@app.route("/fun2")
def fun2():
print("start_num=",g.start_num) # 此处start_num又变为0
print("span_num=",g.span_num]) # 此处start_num又变为5

flask全局变量二,自定义上下文参数

如果我要在模板中一个多选菜单的项目是个变量。那么要每个视图函数读一次变量,再发送到模版中去。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_option_item():
item = ["项目1""项目2""项目3""项目4""项目5""项目6""项目7",]
return item
#视图1
@app.route(/index1)
def index1():
item=get_option_item()
return render_template("index1.html", item=itme)
....
#视图n
@app.route(/index_n)
def index_n():
item=get_option_item()
return render_template("indexn.html", item=itme)

在模板中

1
2
3
4
5
<select>
{% for select_i in item %}
<option> select_i </option>
{% endfor %}
</select>
每个视图要读一次很烦,那么采用上下文变量的方式就很好的解决了这个问题。修改一下python代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# current_app对象是一个定义在应用上下文中的代理
from flask import current_app
app=Flask(__name__)

# 函数用@app.context_processor装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。
@app.context_processor
def get_option_item():
item = ["项目1""项目2""项目3""项目4""项目5""项目6""项目7",]
return dict(item=item) #注意是字典
# 视图函数不用再调用了
#视图n
@app.route(/index_n)
def index_n():
# item=get_option_item()
return render_template("indexn.html")
模板不用改变,直接就可用了。

streamlit学习笔记五-----多个小部件分组

默认情况下,每次用户与您的streamlit应用(表单)交互时,Streamlit 都会重新运行您的脚本。然而,有时在实际重新运行脚本之前等待一组相关的小部件(表单项)被填充完后,再执行更好,这里st.form()诞生了。

格式

st.form(key: str, clear_on_submit: bool = False)
key: 字符串,表示这个表单的标识
clear_on_submit:布而值,如果为 True,则在用户按下提交按钮后,表单内的所有小部件都将重置为其默认值。

一 with用法
1
2
3
4
5
6
7
8
9
10
11
12
with st.form("my_form"):
st.write("Inside the form")
slider_val = st.slider("Form slider")
checkbox_val = st.checkbox("Form checkbox")

# 每一个form必须有一个 submit 按钮.
submitted = st.form_submit_button("Submit")
if submitted:
st.write("slider", slider_val, "checkbox", checkbox_val)

st.write("己通退了表单j")

二 指名用法
1
2
3
4
5
6
7
8

form23 = st.form("my_form")
form23.slider("Inside the form")
st.slider("在此表单外面了")

# 增加一个submit按钮到此表单:
form23.form_submit_button("Submit")

flask 的form学习之三,一个小form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MockCreate(Form):
user_email = StringField("email address",[validators.Email()])
api = StringField("api",[Required()])
submit = SubmitField("Submit")
code = IntegerField("code example: 200",[Required()])
alias = StringField("alias for api")
data = TextAreaField("json format",[Required()])


@app.route("/mockservice",methods=['GET','POST'])
def MockController():
form = MockCreate()
code = form['code']
api = form['api']
return render_template("testf.html",api=api,data=code)