6.3 Android 打包_使用Buildozer打包成apk

文章介绍了如何在Linux环境下使用Buildozer工具,配合Python-for-Android,将开发的Python程序打包成可以在Android设备上运行的APK文件。这个过程包括安装Buildozer及其依赖,创建和编辑buildozer.spec配置文件,以及指定应用信息和依赖包。最后,通过buildozer命令生成调试版本的apk。
该文章已生成可运行项目,

        在 Linux 环境下通过 Buildozer、 Python-for-Android 和 Kivy Launcher 等方面绍如写好的程序打包为在 Android 环境下可执行的文件。

6.3.1 使用 Buildozer 软件打包为 apk【我用的方式】

        使用 Buildozer 可以自动构建整个打包的过程,该软件会自动下载并设置 python-for-android的必备组件,包括 Android SDK 和 NDK,然后构建一个可自动推送到设备的 apk,这也是官方首推新使用的一种方式。
        安装 命令:

git clone https://github.com/kivy/buildozer.git
cd buildozer
sudo python setup.py install

pip3 install --user --upgrade buildozer

        完成后还需要按安装Buildozer的依赖环境。如:Ubuntu18(64bit)可以执行一下命令

sudo apt update
sudo apt install -y git zip unzip openjdk-8-jdk python3-pip autoconf libtool pkg-configzlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev
pip3 install --user --upgrade cython virtualenv # 如果在虚拟环境中把 --user删除
# 把下面这一行添加到 ~/.bashrc文件最后一行
export PATH=$PATH:~/.local/bin/

        安装成功后,通过cd命令进入到需要打包项目的同级目录中,执行以下命令生成buildozer.spec配置文件:

buildozer init

参考以下参数编辑 buildozer.spec 配置文件:

【由于使用文字翻译的照片,可能有符号是中文输入法的情况,仅供参考】

[app]
# 以下为通用配置
# 应用名称
title = 51斩百词
# 包名
package.name = myapp

#域名
package.domain = com.cocpy

# main.py文件所在的位置,由于该配置文件是在项目内生成的(即同级目录),故设置为“.”
source.dir = .

# 需要打包进来的文件的扩展名
source,include_exts = py,kv,atlas,db,sql,ttf,png,jpg,docx,xml,mp3

# 匹配列表
# source,include_patterns = files/*,ima
本文章已经生成可运行项目
import sys import numpy as np import pyaudio from scipy.signal import get_window from scipy.interpolate import interp1d from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QComboBox, QLabel, QPushButton, QLineEdit, QMessageBox, QGroupBox, QRadioButton) from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QIntValidator, QDoubleValidator from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure # 从您提供的数据中提取的精确计权曲线 FREQUENCIES = [1, 1.25, 1.6, 2, 2.5, 3.15, 4, 5, 6.3, 8, 10, 12.5, 16, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, 25000, 31500, 40000, 50000, 63000, 80000, 100000] A_WEIGHTING = [-148.58, -140.83, -132.28, -124.55, -116.85, -108.89, -100.72, -93.14, -85.4, -77.55, -70.43, -63.58, -56.42, -50.39, -44.82, -39.53, -34.54, -30.27, -26.22, -22.4, -19.14, -16.19, -13.24, -10.85, -8.67, -6.64, -4.77, -3.25, -1.91, -0.79, 0, 0.58, 0.99, 1.2, 1.27, 1.2, 0.96, 0.55, -0.12, -1.15, -2.49, -4.25, -6.71, -9.35, -12.33, -15.7, -19.41, -23.02, -26.85, -30.88, -34.68] B_WEIGHTING = [-96.41, -90.6, -84.19, -78.41, -72.64, -66.69, -60.59, -54.95, -49.21, -43.43, -38.24, -33.32, -28.28, -24.16, -20.48, -17.13, -14.1, -11.63, -9.36, -7.31, -5.65, -4.23, -2.94, -2.04, -1.36, -0.85, -0.5, -0.28, -0.13, -0.04, 0, 0.01, -0.02, -0.09, -0.21, -0.4, -0.73, -1.18, -1.89, -2.94, -4.3, -6.07, -8.53, -11.17, -14.16, -17.53, -21.24, -24.85, -28.68, -32.71, -36.51] C_WEIGHTING = [-52.51, -48.65, -44.38, -40.53, -36.7, -32.76, -28.73, -25.03, -21.3, -17.59, -14.33, -11.34, -8.43, -6.22, -4.44, -3.03, -1.98, -1.3, -0.82, -0.5, -0.3, -0.17, -0.08, -0.03, 0, 0.02, 0.03, 0.03, 0.03, 0.02, 0, -0.03, -0.09, -0.17, -0.3, -0.5, -0.83, -1.29, -1.99, -3.05, -4.41, -6.18, -8.63, -11.28, -14.26, -17.64, -21.35, -24.95, -28.79, -32.82, -36.62] # 组合数据 STANDARD_WEIGHTING = { "A": list(zip(FREQUENCIES, A_WEIGHTING)), "B": list(zip(FREQUENCIES, B_WEIGHTING)), "C": list(zip(FREQUENCIES, C_WEIGHTING)) } class RealTimeFFT(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("实时声级计(精确计权曲线)") self.setGeometry(100, 100, 1400, 900) self.setMinimumWidth(800) # 设置合理的最小宽度,避免界面元素被压缩过度 self.setMinimumHeight(600) # 设置合理的最小高度 self.audio = pyaudio.PyAudio() self.stream = None self.is_running = False self.weighting_type = "A" # 默认 A 计权 # 创建更灵活的画布 self.fig = Figure(figsize=(10, 7), dpi=100, tight_layout=True) self.ax_spectrum = self.fig.add_subplot(2, 1, 1) # 主频谱 self.ax_standard = self.fig.add_subplot(2, 1, 2) # 标准曲线对比 self.canvas = FigureCanvas(self.fig) # 控件:选择麦克风 self.mic_label = QLabel("选择麦克风:") self.mic_combo = QComboBox() self.populate_mic_devices() # 控件:选择窗函数 self.window_label = QLabel("选择窗函数:") self.window_combo = QComboBox() self.window_combo.addItems(["blackman", "hamming", "hann", "bartlett", "boxcar"]) # 控件:增益补偿 self.gain_label = QLabel("增益补偿 (dB):") self.gain_combo = QComboBox() self.gain_combo.addItems(["150", "160", "170", "180", "190", "200"]) self.gain_combo.setCurrentText("150") # 控件:刷新速度(输入框) self.refresh_label = QLabel("刷新速度 (ms):") self.refresh_edit = QLineEdit("50") self.refresh_edit.setValidator(QIntValidator(10, 1000)) # 新增坐标轴范围输入控件 self.x_start_label = QLabel("X轴起始频点:") self.x_start_edit = QLineEdit("10") self.x_start_edit.setValidator(QDoubleValidator(0.1, 100000, 2)) self.x_end_label = QLabel("X轴截止频点:") self.x_end_edit = QLineEdit("20000") self.x_end_edit.setValidator(QDoubleValidator(0.1, 100000, 2)) self.y_start_label = QLabel("Y轴起始值:") self.y_start_edit = QLineEdit("-50") self.y_start_edit.setValidator(QDoubleValidator(-100, 100, 2)) self.y_end_label = QLabel("Y轴截止值:") self.y_end_edit = QLineEdit("100") self.y_end_edit.setValidator(QDoubleValidator(-100, 100, 2)) # 控件:计权选择(A、B、C) self.weighting_group = QGroupBox("频率计权(精确标准)") self.weighting_layout = QHBoxLayout() self.a_weighting_radio = QRadioButton("A计权") self.b_weighting_radio = QRadioButton("B计权") self.c_weighting_radio = QRadioButton("C计权") self.a_weighting_radio.setChecked(True) self.weighting_layout.addWidget(self.a_weighting_radio) self.weighting_layout.addWidget(self.b_weighting_radio) self.weighting_layout.addWidget(self.c_weighting_radio) self.weighting_group.setLayout(self.weighting_layout) self.a_weighting_radio.clicked.connect(lambda: self.set_weighting("A")) self.b_weighting_radio.clicked.connect(lambda: self.set_weighting("B")) self.c_weighting_radio.clicked.connect(lambda: self.set_weighting("C")) # 控件:开始/停止按钮 self.start_btn = QPushButton("开始") self.start_btn.clicked.connect(self.toggle_stream) self.stop_btn = QPushButton("停止") self.stop_btn.clicked.connect(self.stop_stream) self.stop_btn.setEnabled(False) # 统计信息 self.stats_label = QLabel("当前声级: -- dB 峰值: -- dB") # 布局设置:新增坐标轴输入框布局 input_layout = QHBoxLayout() input_layout.addWidget(self.x_start_label) input_layout.addWidget(self.x_start_edit) input_layout.addWidget(self.x_end_label) input_layout.addWidget(self.x_end_edit) input_layout.addWidget(self.y_start_label) input_layout.addWidget(self.y_start_edit) input_layout.addWidget(self.y_end_label) input_layout.addWidget(self.y_end_edit) # 优化控制布局,添加伸缩项使控件分布更均匀 control_layout = QHBoxLayout() control_layout.addWidget(self.mic_label) control_layout.addWidget(self.mic_combo) control_layout.addWidget(self.window_label) control_layout.addWidget(self.window_combo) control_layout.addWidget(self.gain_label) control_layout.addWidget(self.gain_combo) control_layout.addWidget(self.refresh_label) control_layout.addWidget(self.refresh_edit) control_layout.addLayout(input_layout) control_layout.addWidget(self.weighting_group) control_layout.addWidget(self.start_btn) control_layout.addWidget(self.stop_btn) control_layout.addStretch() # 添加伸缩项 stats_layout = QHBoxLayout() stats_layout.addWidget(self.stats_label) # 主布局设置 main_layout = QVBoxLayout() main_layout.addLayout(control_layout) main_layout.addLayout(stats_layout) main_layout.addWidget(self.canvas) # 设置布局伸缩性和边距 main_layout.setContentsMargins(5, 5, 5, 5) # 设置适当边距 main_layout.setSpacing(5) main_layout.setStretch(2, 1) # 让画布区域可伸缩 central_widget = QWidget() central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) # FFT 参数初始化 self.CHUNK = 1024 * 2 self.FORMAT = pyaudio.paInt16 self.CHANNELS = 1 self.RATE = 44100 self.freq_axis = np.fft.rfftfreq(self.CHUNK, 1.0 / self.RATE) # 初始化定时器 self.timer = QTimer(self) self.timer.timeout.connect(self.update_plot) # 预绘制标准曲线 self.plot_standard_weighting() def populate_mic_devices(self): """获取麦克风设备列表""" info = self.audio.get_host_api_info_by_index(0) num_devices = info.get(&#39;deviceCount&#39;) for i in range(num_devices): device = self.audio.get_device_info_by_host_api_device_index(0, i) if device[&#39;maxInputChannels&#39;] > 0: self.mic_combo.addItem(device[&#39;name&#39;], i) def set_weighting(self, weighting): """设置计权类型并更新对比曲线""" self.weighting_type = weighting self.plot_standard_weighting() def start_stream(self): """启动音频流""" device_index = self.mic_combo.currentData() try: refresh_ms = int(self.refresh_edit.text()) if not (10 <= refresh_ms <= 1000): raise ValueError("刷新速度需在10-1000ms之间") self.stream = self.audio.open( input_device_index=device_index, format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK ) self.is_running = True self.timer.start(refresh_ms) self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) print(f"已启动音频流,设备: {self.mic_combo.currentText()}") except Exception as e: QMessageBox.critical(self, "错误", f"无法启动音频流: {str(e)}") def stop_stream(self): """停止音频流""" self.is_running = False self.timer.stop() if self.stream: self.stream.stop_stream() self.stream.close() self.stream = None self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) self.stats_label.setText("当前声级: -- dB 峰值: -- dB") def toggle_stream(self): if not self.is_running: self.start_stream() def interpolate_weighting(self, freq): """根据标准数据插值计算计权修正值""" std_data = STANDARD_WEIGHTING[self.weighting_type] freqs = np.array([f for f, _ in std_data]) weights = np.array([w for _, w in std_data]) return np.interp(freq, freqs, weights) def plot_standard_weighting(self): """绘制标准计权曲线""" self.ax_standard.clear() std_data = STANDARD_WEIGHTING[self.weighting_type] freqs = [f for f, _ in std_data] weights = [w for _, w in std_data] self.ax_standard.plot(freqs, weights, color=&#39;red&#39;, label=f"标准 {self.weighting_type}计权") self.ax_standard.axvline(x=1000, color=&#39;gray&#39;, linestyle=&#39;--&#39;, label="1kHz 参考线") self.ax_standard.axhline(y=0, color=&#39;gray&#39;, linestyle=&#39;--&#39;) self.ax_standard.set_xscale(&#39;log&#39;) self.ax_standard.set_xlabel("频率 (Hz)") self.ax_standard.set_ylabel("计权修正值 (dB)") self.ax_standard.set_xlim(10, 20000) self.ax_standard.grid(True, linestyle=&#39;--&#39;, alpha=0.7) self.ax_standard.legend() self.fig.tight_layout() # 确保布局紧凑 self.canvas.draw() def update_plot(self): """更新频谱和统计信息,根据输入框值更新坐标轴范围""" if not self.is_running or not self.stream: return try: # 读取音频数据 data = self.stream.read(self.CHUNK) data_np = np.frombuffer(data, dtype=np.int16) / 32768.0 # 应用窗函数 window_name = self.window_combo.currentText() window = get_window(window_name, self.CHUNK) windowed_data = data_np * window # 计算FFT fft_data = np.fft.rfft(windowed_data) magnitude = np.abs(fft_data) * 2 / np.sum(window) magnitude[0] /= 2 # 直流分量修正 # 增益补偿 gain = float(self.gain_combo.currentText()) magnitude *= 10 ** (gain / 20) # 计算计权修正 weighting = np.array([self.interpolate_weighting(f) for f in self.freq_axis]) db_data = 20 * np.log10(magnitude + 1e-10) + weighting # 获取坐标轴范围输入值 x_start = float(self.x_start_edit.text()) x_end = float(self.x_end_edit.text()) y_start = float(self.y_start_edit.text()) y_end = float(self.y_end_edit.text()) # 统计信息(在设置的X轴范围内) valid_indices = (self.freq_axis >= x_start) & (self.freq_axis <= x_end) valid_db = db_data[valid_indices] avg_db = np.mean(valid_db) if valid_db.size > 0 else 0 max_db = np.max(valid_db) if valid_db.size > 0 else 0 self.stats_label.setText(f"当前声级: {avg_db:.1f} dB 峰值: {max_db:.1f} dB") # 绘制频谱 self.ax_spectrum.clear() self.ax_spectrum.plot(self.freq_axis, db_data, color=&#39;blue&#39;, label="实时频谱") self.ax_spectrum.set_title(f"实时FFT频谱({self.weighting_type}计权)") self.ax_spectrum.set_xlabel("频率 (Hz)") self.ax_spectrum.set_ylabel("声压级 (dB)") self.ax_spectrum.set_xscale(&#39;log&#39;) self.ax_spectrum.set_xlim(x_start, x_end) self.ax_spectrum.set_ylim(y_start, y_end) self.ax_spectrum.grid(True, linestyle=&#39;--&#39;, alpha=0.7) self.ax_spectrum.legend() # 刷新画布 self.fig.tight_layout() # 确保布局紧凑 self.canvas.draw() except Exception as e: print(f"更新失败: {e}") self.stop_stream() def closeEvent(self, event): """关闭时释放资源""" self.stop_stream() self.audio.terminate() event.accept() if __name__ == &#39;__main__&#39;: app = QApplication(sys.argv) font = app.font() font.setFamily("SimHei") # 设置中文字体支持 app.setFont(font) window = RealTimeFFT() window.show() sys.exit(app.exec_())这个代码在pycharm上正常运行,希望同步生手机apk安装包给手机安装app后使用,教我一步步打包,我是超级新手,所有的步骤都要很详细,比如输入命令时要告诉我是在powershell,还是pycharm的终端输入类似这样的动作要很详细
最新发布
09-03
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静候光阴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值