wxpython收费吗_使用wxPython开发一个简单GUI应用
python用的时间不长,一般用来做字符串处理、简单测试的一些小程序。最近工作中需要做一个简单的GUI应用,使用麦克录音并存成wave文件。然后就想拿wxPython练练手。
一、概述
GUI开发采用wxPython,界面编辑工具采用wxGlade,声音采集和播放采用PyAudio,小数据库采用sqlite3,最后使用py2exe打包发布。基本的应用开发流程都包括了。
二、wxGlade
界面编辑工具也是找了几个,比如wxFormBuilder,甚至是收费的DialogBlocks,wxFormBuilder 很漂亮,不过bug好像较多,经常自动退出。wxGlade有经典的Linux GUI界面风格,分立式窗体,了解了基本的原理后用起来很方便。主要是其中的sizer,add slot、insert slot增加空位,然后添加控件。也可以添加自定义的控件,只需要设置自定义控件的Class属性。然后在MainFrame的Extra code for this widget增加from YourModule import YourCLass。因为界面比较简单,我没有采用XRC资源导入的模式,而是直接生成MainFrame的代码。由于界面设计可能会变,在应用中新建一 个MainFrameEx类继承MainFrame,将事件处理放在继承类中完成。这样每次使用wxGlade编辑界面后可以直接覆盖生成的代码。
三、PyAudio
PyAudio是从PortAudio移植的,现在还是alpha版。不过使用起来还真是方便,看看网站上提供的example就可以了。没有什么大问题。需要注意多线程的问题,PyAudio对象尽量复用。注意线程中刷新wxWidget需要使用wx.CallAfter方法。
# -*- coding: UTF-8 -*-
import pyaudio
import wave
import threading
import wx
import datetime
import traceback
import os
import logging
logger = logging.getLogger("root")
class BMRecord(threading.Thread):
CHUNK = 1024
FORMAT = pyaudio.paInt16 # 至少为16位
DEVICE = 1
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
def __init__(self, window, audio, device, prefix):
threading.Thread.__init__(self)
self.window = window
self.audio = audio
self.prefix = prefix # wave文件命名的前缀
self.DEVICE = device
self.CHANNELS = 2 # 双通道采集
self.RECORD_SECONDS = int(window.params["length"])
self.RATE = int(window.params["rate"])
self.FORMAT = pyaudio.paInt16
def record(self):
self.filename = ""
self.filetime = ""
try :
stream = self.audio.open(format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
input_device_index=self.DEVICE,
frames_per_buffer=self.CHUNK)
frames = []
for i in range(0, int(self.RATE * self.RECORD_SECONDS / self.CHUNK)):
data = stream.read(self.CHUNK)
frames.append(data)
i = i
stream.stop_stream()
stream.close()
now = datetime.datetime.now()
strnow = now.strftime('%Y%m%d%H%M%S')
self.filetime = now.strftime('%Y-%m-%d %H:%M:%S')
savepath = self.checkPath(strnow)
# 双声道存为两个单声道文件
frames1 = []
frames2 = []
wavedata = b''.join(frames)
for i in range(len(wavedata) / 4):
frames1.append(wavedata[i * 4 : i * 4 + 2])
frames2.append(wavedata[i * 4 + 2 : i * 4 + 4])
# 双声道存储
fullpath = savepath + "/" + strnow + self.prefix + "X.wav"
wf = wave.open(fullpath, 'wb')
wf.setnchannels(2)
wf.setsampwidth(self.audio.get_sample_size(self.FORMAT))
wf.setframerate(self.RATE)
wf.writeframes(wavedata)
wf.close()
# 两个单声道存储
filenames = [strnow + self.prefix + "Z.wav", strnow + self.prefix + "Y.wav"]
fullpath = savepath + "/" + filenames[0]
wf = wave.open(fullpath, 'wb')
wf.setnchannels(1)
wf.setsampwidth(self.audio.get_sample_size(self.FORMAT))
wf.setframerate(self.RATE)
wf.writeframes(b''.join(frames1))
wf.close()
fullpath = savepath + "/" + filenames[1]
wf = wave.open(fullpath, 'wb')
wf.setnchannels(1)
wf.setsampwidth(self.audio.get_sample_size(self.FORMAT))
wf.setframerate(self.RATE)
wf.writeframes(b''.join(frames2))
wf.close()
self.message = "录制成功"
self.filenames = filenames
logger.info(filenames[0] + "," + filenames[1] + ", recorded")
return 0
except Exception:
self.message = traceback.format_exc()
logger.error(traceback.format_exc())
return -1
def checkPath(self, pathname):
curpath = os.path.abspath(os.curdir)
strdate = pathname[0:8]
fullpath = curpath + "/data/" + strdate
if not os.path.exists(fullpath) :
os.makedirs(fullpath)
return fullpath
def run(self):
ret = self.record()
wx.CallAfter(self.window.recordResult, ret, self.filenames, self.filetime, self.message)
四、sqlite
sqlite模块是Python内置的用起来很方便:
import sqlite3
import datetime
class BMDatabase():
def loadData(self, whichDay):
conn = sqlite3.connect("data/bmon.db")
cur = conn.cursor()
start = datetime.datetime.strptime(whichDay, "%Y-%m-%d")
end = start + datetime.timedelta(days=1)
res = cur.execute("select * from CheckRecord where rec_time between ? and ? order by rec_time", (start, end))
return res.fetchall()
def getFile(self, waveFile):
conn = sqlite3.connect("data/bmon.db")
cur = conn.cursor()
res = cur.execute("select * from CheckRecord where rec_file=?", (waveFile,))
row = res.fetchone()
return row
def save(self, filename, filetime, result):
conn = sqlite3.connect("data/bmon.db")
cur = conn.cursor()
record = [(filename, filetime, result)]
cur.executemany('INSERT INTO CheckRecord (rec_file,rec_time,result) VALUES (?,?,?)', record)
conn.commit()
五、自定义wxWidget控件
自绘控件主要是处理EVT_PAINT事件: self.Bind(wx.EVT_PAINT, self.on_paint)
import wx
class WavePane(wx.StaticText):
waveData = None
spectrum = None
def __init__(self, parent, nid=wx.ID_ANY, caption=""):
wx.StaticText.__init__(self, parent)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_PAINT, self.on_paint)
def on_size(self, event):
self.Refresh()
event.Skip()
def on_paint(self, event):
w, h = self.GetClientSize()
dc = wx.AutoBufferedPaintDC(self)
brush = wx.Brush(wx.Color(0, 0, 0x80), wx.SOLID)
dc.SetBrush(brush)
dc.Clear()
dc.SetPen(wx.Pen(wx.BLACK, 1))
dc.SetTextForeground(wx.Color(0, 0xFF, 0))
font = dc.GetFont()
font.SetPointSize(8)
dc.SetFont(font)
dc.DrawRectangle(0, 0, w - 1, h - 1)
if self.waveData <> None:
dc.BeginDrawing()
dc.SetPen(wx.Pen(wx.Color(0, 0xFF, 0), 1))
au = self.waveData
step = int(au.nframes / w)
height = au.height # 或者65536 / 2.0
i = 0
j = 0
while i < au.nframes:
if au.frames[2 * i + 1] >= 0x80: # 负数
value = au.frames[2 * i] + au.frames[2 * i + 1] * 256 - 65536
else:
value = au.frames[2 * i] + au.frames[2 * i + 1] * 256
dc.DrawLine(j, int(h / 2.0), j, int(h / 2.0 * (1 - value * 1.0 / height)))
i += step
j += 1
if 2 * i + 1 >= au.nframes * 2:
break;
dc.DrawText(str(au.maxValue), 1, 1)
dc.DrawText(str(au.minValue), 1, h - 16)
dc.EndDrawing()
elif self.spectrum <> None :
dc.BeginDrawing()
dc.SetPen(wx.Pen(wx.Color(0, 0xFF, 0), 1))
brush = wx.Brush(wx.Color(0, 0xFF, 0), wx.SOLID)
dc.SetBrush(brush)
dc.SetTextForeground(wx.RED)
barWidth = int(w / 72)
i = 0
j = barWidth
while i < 36:
y = int((1 - self.spectrum[i] * 1.0 / self.maxSpectrum) * h)
dc.DrawRectangle(j, y , barWidth, h - y - 1)
if y < h - 2:
dc.FloodFill(j + 1, y + 1, wx.Color(0, 0xFF, 0), wx.FLOOD_BORDER)
# Bar的编号
if i == 0 or(i + 1) % 5 == 0:
if i < 9:
dc.DrawText(str(i + 1), j + 4, h - 13)
else:
dc.DrawText(str(i + 1), j , h - 13)
i += 1
j += barWidth * 2
dc.SetTextForeground(wx.Color(0, 0xFF, 0))
dc.DrawText(str(self.maxSpectrum), 1, 1)
dc.EndDrawing()
def setWaveData(self, waveData):
self.waveData = waveData
self.Refresh()
def setSpectrum(self, spectrum):
self.spectrum = spectrum
if spectrum <> None:
maxValue = 0
for i in range(0, 36):
if spectrum[i] > maxValue :
maxValue = spectrum[i]
self.maxSpectrum = maxValue
self.Refresh()
六、日志的使用
使用循环日志
import logging.handlers
logger = logging.getLogger("root")
handler = logging.handlers.RotatingFileHandler(os.path.join(os.getcwd(), 'bmon.log'),
maxBytes=5 * 1024 * 1024, backupCount=5)
formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
七、py2exe
最后一步就是打包成可执行文件。setup.py:
from distutils.core import setup
import py2exe
# setup(console=["hello.py"])
py2exe_options = dict({
"includes":['sip', 'encodings', 'encodings.ascii', 'encodings.utf_8', 'encodings.cp866'],
"dll_excludes":["MSVCP90.dll"]})
setup(version="1.0",
description="Bearing Monitor",
name="bmon",
zipfile=None,
dist_dir="bmon",
windows=["bmon.py"],
options={'py2exe': py2exe_options},
icon_resources=[(1, "check_all.ico")],
data_files=[("", ["check_all.ico"])]
)
然后命令行下执行:python setup.py py2exe,就可以生成dist发布目录
八、小结
这个简单应用涉及的主要模块就这么几个,组合成了一个简单的GUI应用。Python开发还真是很简单,前提是得熟悉各种模块。
wxpython收费吗_使用wxPython开发一个简单GUI应用相关推荐
- wxpython问卷调查界面_自己做的一个简单的问卷调查系统
疫情期间,先来没事做,就简单的用ssm 写了一个问卷调查系统.用于我们学校得青协调查用. 这就是我做得一个首页的页面,首页做的比较简单嘻嘻. 因为刚接触ssm 框架,我首先的思路是先把整体的ssm框架 ...
- java计算机毕业设计vue开发一个简单音乐播放器MyBatis+系统+LW文档+源码+调试部署
java计算机毕业设计vue开发一个简单音乐播放器MyBatis+系统+LW文档+源码+调试部署 java计算机毕业设计vue开发一个简单音乐播放器MyBatis+系统+LW文档+源码+调试部署 本源 ...
- [译]使用 Rust 开发一个简单的 Web 应用,第 4 部分 —— CLI 选项解析
原文地址:A Simple Web App in Rust, Part 4 -- CLI Option Parsing 原文作者:Joel's Journal 译文出自:掘金翻译计划 本文永久链接:g ...
- java计算机毕业设计vue开发一个简单音乐播放器源码+mysql数据库+系统+lw文档+部署
java计算机毕业设计vue开发一个简单音乐播放器源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计vue开发一个简单音乐播放器源码+mysql数据库+系统+lw文档+部署 本源码技 ...
- 利用WCF的callback机制开发一个简单的多人游戏模型
本文介绍了如何利用WCF和callback机制开发一个简单的多人在线游戏模型. 运行过程如下: 当game service 启动之后,若干个客户端便会自动连接到服务器.当某个客户端点击join gam ...
- php开发mvc教程,php开发一个简单的MVC
本文通过实例为大家介绍用php开发一个简单mvc的方法,起到势砖引玉的作用,本文比较适合刚接触mvc的朋友. MVC其实就是三个Model,Contraller,View单词的简称. Model,主要 ...
- Nginx开发一个简单的HTTP过滤模块
本文将学些开发一个简单的HTTP过滤模块,它能够对Content-Type为text/plain的包体前加上前缀字符串prefix. <一> 过滤模块的调用顺序 过滤模块可以叠加,也就是说 ...
- python可视化界面编程 pycharm_pycharm开发一个简单界面和通用mvc模板(操作方法图解)...
文章首先使用pycharm的 PyQt5 Designer 做一个简单的界面,然后引入所谓的"mvc框架". 一.设计登录界面 下面开始第一个话题,使用pycharm的 PyQt5 ...
- 开发一个简单的WebPart
开发一个简单的WebPart,首先我们需要对Visual Studio .NET 2003进行相应功能的扩展,我们可以在微软的网站下载到一个扩展功能包,名字叫:WebPartTemplatesforV ...
最新文章
- 打造生物智能和人工智能“双螺旋”,智源研究院发布“人工智能的认知神经基础”重大研究方向...
- 借力5G,云VR将推动VR产业迎“第二春”
- Maven下载、安装和配置(转)
- GitHub 上值得关注的 iOS 开源项目
- 机械制图及计算机绘图技能实训,机械制图测绘与CAD技能实训(二)
- LeetCode 1654. 到家的最少跳跃次数(BFS)
- oracle函数lad,01-查询Oracle中所有用户信息
- 使用代理,调用json-server的服务接口
- Matlab之字符串的查找(findstr)与替换(strrep)
- Python解答蓝桥杯省赛真题之从入门到真题
- SAP HANA XS 专栏
- 蘑菇战争 2 for Mac游戏介绍
- MACBOOK 连接不上wifi的解决办法
- 机器学习作业-交通流量预测综述
- 编写python 函数,实现冒泡排序算法。
- oracle查询遇到关键字
- 蓝桥杯真题:数字三角形
- AD转换中参考电压的作用
- oracle手动挂载crs盘,Oracle rac手动修改crs资源
- Ubuntu 10.04下安装TL-WN721N(RT5370)无线网卡驱动
热门文章
- 城市系统应用其一-表征城市交通模式
- Excel 2010 SQL应用096 聚合函数之标准偏差及标准差
- 3D全景模型展示可视化技术演示
- 网络和计算机加密解密感叹号,网络连接显示感叹号但是能上网怎么办 网络连接显示感叹号原因【图文】...
- mysql报错:1264-Out of range value for column ‘字段‘ at row 1
- 空气质量天气质量数据来源整理
- 小甲鱼python入门笔记(一)(全)
- 【网络编程】TCP 网络应用程序开发
- OSEK直接网络管理软件开发
- java 高效列转行,java 列转行