入门级Python应用程序教程(含完整代码)

入门级Python应用程序教程(含完整代码)


2024年1月12日发(作者:)

入门级Python应用程序---伺服电机控制

v信@宝德,百度@baode_w

前言

本文只适合像作者这样的入门级小白,大虾级别的请忽略。

由于作者水平有限,不当之处,欢迎批评指正。

本文所述应用程序只考虑功能实现,不考虑程序优化。

一、应用环境

系统环境:Window 10;

Python版本:3.7;

IDE:PyCharm;

界面设计:PYQT5;

电机:光毓机电RMD-S系列电机;

控制端口:RS-485,使用USB转485模块。

二、准备环境

软件安装及环境变量设置方法网上较多,请自行查阅,这里不再赘述。

双击打开PyCharm,新建一个New Project,注意添加项目的环境依赖venv。配置下Python3.7的依赖环境库venv library root,External Libraries—>Python

3.7(venv) —>venv library root—>,将值改成“true”,如下图。

为PyCharm添加外部工具,FileSettingsToolsExternal Tools “+”。

以PYQT5为例,单击“+”,弹出新建外部工具对话框,如下图所示。

对话框中Name是在IDE中的显示名字,可以自定义填写。Tool Settings需填写两项,Program项单击浏览Python3.7根目录“Python37Libsite-packagespyqt5_”(当然,你得先安装有PYQT5,>_<),Working directory项填入“$ProjectFileDir$”,其他选项默认即可,单击“OK”保存。

其他外部工具如PYUIC、PYRCC等,添加过程类似。

三、编写程序

在项目中新建两个py文件,其中一个py文件()关联QT界面的信号(Signal)和槽(Slot),另外一个py文件()用来编写主程序,如此便于分离QT界面和逻辑程序,避免修改界面时对主程序存在干扰,当修改QT界面时,QT只会更新,不会更新主程序。

主程序中需要包含PTContrl完成调用,以及主程序需要加载一些要用到的模块,如sys、serial、Qtimer等,如下图所示。

主程序功能包括,串口端口检测、波特率设置、打开端口,串口实时发送两个电机的角度值、速度值、转动方向,定时从串口接收两个电机编码器数据,并解码显示角度值。

主程序完整代码如下:

import sys

import serial

import binascii

import _ports

#from PyQt5 import QtCore, QtGui, QtWidgets

from import QTimer

from ets import QApplication,QMainWindow,QFileDialog

from ets import QMessageBox

from time import sleep

import PTContrl

#import PDial

#from ActiveMain import RulerProgress

#主程序

class MainControl(QMainWindow,_SFmotorcontrol):

def __init__(self):

QMainWindow.__init__(self)

_SFmotorcontrol.__init__(self)

i(self)

= ()

_num_sended = 0

1 = (_()-18000)/100

2 = (_() - 9000) / 100

1_H = 1#电机1速度高8位

1_L = 244 # 电机1速度,0x1F4,5dps

2_H = 1#电机2速度5dps高8位

2_L = 244 # 电机2速度5dps,低8位

1_zero = 0#水平电机零位0

2_zero = 0#垂直电机零位0

lect_m("115200")

lect_m("19200")

lect_m("57600")

lect_m("9600")

1_postvalue = 0

2_postvalue = 0

_dir = 0

_dir1 = 0

s1 = 1

s2 = 2

_left = 0

_right = 1

_up = 0

_down = 1

_flag = 0

_flag = 0

_stop = 0

_auto_act =0

1 = QTimer(self)

1_timeout = 250#timer1溢出时间250ms

#for addr in range(1,33,1):

# m(str(addr))

# m(str(addr))

_check()

t(_Connect)#按钮点击事件链接到自定义对象

#_t(1_change)#拨盘值改变事件链接到自定义对象

#_t(1_change)

#_t(1_change)

#_check_ible(False)

_check_t(_check)

#_t(_send)

#_t(_send)

#self.H_speed_t(_change)

#self.V_speed_t(_change)

_t(_send1)

_t(_send2)

_t(_send1)

_t(_send2)

_t(_stop)

_t(_stop)

_t(_stop)

_t(_stop)

_t(_send)

t(_send)

select_t(_check)

_ue(18000)

#_bled(True)

_bled(False)

_bled(False)

_bled(False)

_bled(False)

_bled(False)

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;")

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

_leSheet("border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

_leSheet("border-color:rgb(0,0,255);gridline-color:rgb(0,0,255);color:

rgb(0,170,0);border-left-color: rgb(255, 0, 0);")

leSheet(

"background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

_check_leSheet("border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

#leSheet("border:2px groove gray;border-radius:10px;")

#_leSheet("handle{background:blue;}")

#border:0px solid transparent;

#border-radius:2px;} ")

#while True:

#在下面编写自己的子程序

#端口打开或关闭

def on_Connect(self):

if ():

()

()

t("连 接")

leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font: 12pt;")

_bled(False)

_bled(False)

_bled(False)

_bled(False)

_bled(False)

if _() == "停止":

_send()

else:

= select_tText()

te = int(lect_tText())

ze = 8

ts = 1

= "N"

le = True

t = 2

try:

()

if _open:

t("断 开")

leSheet(

"background-color:rgb(200,0,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font: 12pt;")

(1_timeout)#启动定时器

_bled(True)

_bled(True)

_bled(True)

_bled(True)

_bled(True)

#_()

except:

al(self, "Port Error", "此串口不能被打开!")

return None

#it_ = _()

#t(str(_()/100))# =

#_()

#print(it)

#水平转电机停止

def roll_stop(self):

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

_leSheet("border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

order_sum = 62 + 166 + 8 + s1

order_sum = int(hex(order_sum)[-2:], 16)

_s = [62, 166, s1, 8, order_sum, 0, 60, 140, 0, 0, 0, 0, 0, 200]

# 发送位置359度,速度0dps,顺时针,停止命令

_send()

#1_t(str(_()/100)+"度")

#2_t(str(_() / 100) + "度")

#俯仰电机停止

def pitch_stop(self):

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

_leSheet("border:2px groove gray;border-radius:10px;padding:2px

4px;font:12pt;")

order_sum = 62 + 166 + 8 + s2

order_sum = int(hex(order_sum)[-2:], 16)

_s = [62, 166, s2, 8, order_sum, 0, 40, 35, 0, 0, 0, 0, 0, 75] #

发送位置90度,速度0dps,顺时针

_send()

#it_t(str(self.H_speed_()/100)+"dps")

#it_t(str(self.V_speed_() / 100)+"dps")

#串口端口检测

def port_check(self):

# 检测所有存在的串口,将信息存储在字典中

_Dict = {}

port_list = list(_ts())

select_()

for port in port_list:

_Dict["%s" % port[0]] = "%s" % port[1]

select_m(port[0])

if len(_Dict) == 0:

_t(" 无可用串口")

#t(" 检测无可用串口")

# 发送数据

def data_send(self):

if ():

if _s != "":

# 非空字符串

_t("发送:" + str(_s))#print(send_list)

input = bytes(_s)

#else:

# ascii发送

#input_s = (input_s + 'rn').encode('utf-8')

num = (input)#发送数据

_num_sended += num

#_t("发送:" + hex(input_s))

else:

al(self, "Port Error", "串口未打开!")

pass

def auto_send(self):

if _flag == 0:

_flag = 1

_t("停止")

_leSheet("background-color:rgb(200,0,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

else:

_flag = 0

_auto_act = 0

_t("自动")

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

_stop = 1

pass

def ask_send(self):

if ():

ask_list1 = [0x3e,0x90,0x01,0x00,0xcf]

ask_list2 = [0x3e,0x90,0x02,0x00,0xd0]

if _flag:

(ask_list1)

_flag = 0

else:

(ask_list2)

_flag = 1

#else:

sleep(0.02)#线程挂起0.05秒

rec_data = ""

#rec_hex = ""

n = ing()

if n :

rec_data = str(binascii.b2a_hex((n)))[2:-1]

#for rec_list in rec_hex:

# rec_data = rec_data + str(rec_list) #= rec_data + str(ne())

#print(rec_data)

if len(rec_data) > 15:

#print(rec_data)

#print(len(rec_data))

_t("接收:" + rec_data + ",长度:" + str(len(rec_data)))

# print(send_list)

#rec_data = rec_data[0:8]

#head = rec_data[:2]

#order = rec_data[3:4]

addr = int((rec_data[5:6]),16)

engle_data1 = rec_data[10:12]

engle_data2 = rec_data[12:14]

engle_data = int((engle_data2 + engle_data1),16)

#engle_data = engle_data[::-1]#倒置

#engle_sum = engle_data[1] * 0x100 + engle_data[2]

#engle_sum = int(str(engle_sum),16)

if addr == s1:#电机1返回数据

engle_data = (engle_data - 1_zero) * 36000 // 4096#精度360/4096,*100

1_postvalue = engle_data

1_t(str('%.1f'%(engle_data/100)))#保留1位小数点

engle_data = engle_data + 18000

if engle_data > 36000:

engle_data = engle_data - 36000

_ue(engle_data)

if addr == s2:#电机2返回数据

engle_data = (engle_data - 2_zero) * 36000 // 4096 # 精度360/4096,*100

2_postvalue = engle_data

if engle_data > 10000:

2_t("-" + str('%.1f' % ((36000-engle_data )/

100))) # 保留1位小数点

else:

2_t(str('%.1f' % (engle_data / 100))) # 保留1位小数点

engle_data = engle_data + 9000

#2_t(str('%.1f'%(engle_data/100)))#保留1位小数点

_ue(engle_data)

if _flag:

if _auto_act ==1:#自动翻转

if 1_postvalue > 35500:

_send1()#send 0deg

_auto_act = 1

if 1_postvalue < 400:

_send2()#send 359 deg

_auto_act = 1

else:#初次自动运行

if 1_postvalue > 18000 or 1_postvalue == 18000 :

_send2()#send 359 deg

_auto_act = 1

if 1_postvalue < 18000:

_send1()#send 0deg

_send()

_auto_act = 1

#自动停止

if _stop == 1:

_stop = 0

_stop()#send auto stop

#print(rec_data)

#al(self, "Port Error", "串口未打开!")

# pass

#手动与自动之间的切换?手动点击时停止自动

#水平电机顺时针转动

def roll_send1(self):

if _() and _flag ==1:

_send()

if _flag ==0:

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

order_sum = 62 + 166 + 8 + s1

order_sum = int(hex(order_sum)[-2:], 16)

data_sum = int(hex(1 + 1_H + 1_L)[-2:] ,16)

_s =

[62,166,s1,8,order_sum,1,0,0,0,1_L,1_H,0,0,data_sum]#发送位置0度,速度2dps,顺时针

#input_s = bytes(input_s)

#(input_s)

_send()

#水平电机逆时针转动

def roll_send2(self):

if _() and _flag ==1:

_send()

if _flag == 0:

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

order_sum = 62 + 166 + 8 + s1

order_sum = int(hex(order_sum)[-2:], 16)

data_sum = int(hex(60+140 + 1_H + 1_L)[-2:], 16)

_s =

[62,166,s1,8,order_sum,0,60,140,0,1_L,1_H,0,0,data_sum]#发送位置359度,速度2dps,逆时针

#input_s = bytes(input_s)

#(input_s)

_send()

#俯仰电机顺时针转动

def pitch_send1(self):

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

order_sum = 62 + 166 + 8 + s2

order_sum = int(hex(order_sum)[-2:], 16)

data_sum = int(hex(40 + 35 + 2_H + 2_L)[-2:], 16)

_s =

[62,166,s2,8,order_sum,0,40,35,0,2_L,2_H,0,0,data_sum]#发送位置90度,速度2dps,顺时针

#input_s = bytes(input_s)

#(input_s)

_send()

#俯仰电机逆时针转动

def pitch_send2(self):

_leSheet("background-color:rgb(0,200,0);border:2px groove

gray;border-radius:10px;padding:2px 4px;font:12pt;")

order_sum = 62 + 166 + 8 + s2

order_sum = int(hex(order_sum)[-2:], 16)

data_sum = int(hex(1+120 + 105 + 2_H + 2_L)[-2:], 16)

_s =

[62,166,s2,8,order_sum,1,120,105,0,2_L,2_H,0,0,data_sum]#发送位置270度,速度2dps,逆时针

#input_s = bytes(input_s)

#(input_s)

_send()

#程序启动关联

if __name__ == "__main__":

app=QApplication() # 创建一个QApplication,也就是你要开发的软件app

#MainWindow = indow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件

ui = MainControl() # ui是Ui_SFmotorcontrol类的实例化对象

#i(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow

() # 执行QMainWindow的show()方法,显示这个QMainWindow

(_()) # 使用exit()或者点击关闭按钮退出QApplication

四、设计QT界面

在PyCharm软件中启动外部工具PYQT5,Tools—>External Tools—>QT5,如下图所示。

启动PYQT5的设计界面,进入创建对话框,如下图,创建一个新的Main

Window。

修改Main Window的对象名称ObjectName为“SFmotorcontrol”(也可以自己定义),但要记住,在关联py文件里面要用到。QT设计界面左侧有许多控件,将需要的控件直接往建好的空白Main Window里拖,简单排布一下,统一修改好记的对象名称,具体设计方法自行参阅QT使用方法,设计好的界面如下图所示。

编辑好后保存成ui文件(),并添加到PyCharm项目中,使用PyCharm的外部工具PYUIC将做好的ui文件转换成py文件(就是前文所说的界面关联py文件),得到,完整的代码如下。

QT界面关联py文件完整代码:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file ''

#

# Created by: PyQt5 UI code generator 5.11.3

#

# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_SFmotorcontrol(object):

def setupUi(self, SFmotorcontrol):

ectName("SFmotorcontrol")

(640, 580)

imumSize((640, 580))

imumSize((640, 580))

font = ()

ily("宋体")

ntSize(12)

t(font)

icon = ()

map(p(""), , )

dowIcon(icon)

lwidget = t(SFmotorcontrol)

ectName("centralwidget")

onnect = Box(lwidget)

metry((10, 410, 621, 131))

t(False)

ckable(False)

ectName("SetupConnect")

t = utton(onnect)

metry((500, 50, 111, 41))

ectName("Connect")

select_box = Box(onnect)

select_metry((70, 60, 69, 22))

select_table(True)

select_rentText("")

select_ectName("Serialselect_box")

lect_box = Box(onnect)

lect_metry((350, 60, 71, 22))

lect_table(True)

lect_ectName("BaudSelect_box")

= (onnect)

metry((20, 60, 54, 21))

ectName("label")

_3 = (onnect)

_metry((280, 55, 71, 31))

_ectName("label_3")

_check_button = utton(onnect)

_check_metry((150, 50, 81, 41))

_check_ectName("port_check_button")

_send = (onnect)

_metry((20, 100, 571, 21))

_t("")

_ectName("label_send")

_engle1 = (lwidget)

_bled(False)

_metry((50, 36, 181, 181))

_sor(r(ngHandCursor))

_leSheet("border-color: rgb(0, 0, 255);n"

"gridline-color: rgb(0, 0, 255);n"

"color: rgb(0, 170, 0);")

_imum(36000)

_eStep(100)

_perty("value", 18000)

_derPosition(18000)

_cking(True)

_pping(True)

_chTarget(10.0)

_chesVisible(True)

_ectName("dial_engle1")

_engle2 = (lwidget)

_bled(False)

_metry((380, 36, 181, 181))

_sor(r(ngHandCursor))

_imum(0)

_imum(36000)

_gleStep(1)

_eStep(100)

_perty("value", 9000)

_derPosition(9000)

_cking(True)

_entation(al)

_ertedAppearance(False)

_ertedControls(True)

_pping(True)

_chTarget(10.0)

_chesVisible(True)

_ectName("dial_engle2")

_4 = (lwidget)

_metry((40, 320, 91, 31))

font = ()

ntSize(16)

d(True)

ght(75)

_t(font)

_ectName("label_4")

_5 = (lwidget)

_metry((480, 320, 91, 31))

font = ()

ntSize(16)

d(True)

ght(75)

_t(font)

_ectName("label_5")

_9 = (lwidget)

_metry((129, 225, 54, 12))

_ectName("label_9")

_10 = (lwidget)

_metry((463, 15, 54, 12))

_ectName("label_10")

_11 = (lwidget)

_metry((235, 120, 54, 12))

_ectName("label_11")

_12 = (lwidget)

_metry((360, 120, 54, 12))

_tFormat(xt)

_ectName("label_12")

_13 = (lwidget)

_metry((136, 14, 54, 12))

_ectName("label_13")

_14 = (lwidget)

_metry((454, 222, 54, 12))

_ectName("label_14")

_15 = (lwidget)

_metry((20, 120, 54, 12))

_ectName("label_15")

_label = (lwidget)

_metry((20, 511, 131, 21))

_t("")

_ectName("statu_label")

1_label = (lwidget)

1_metry((105, 110, 71, 31))

font = ()

ily("Times New Roman")

ntSize(16)

d(True)

ght(75)

1_t(font)

1_gnment(enter)

1_ectName("engle1_label")

_Button1 = utton(lwidget)

_metry((280, 270, 50, 50))

font = ()

ntSize(12)

_t(font)

_ectName("auto_Button1")

2_label = (lwidget)

2_metry((436, 113, 71, 31))

font = ()

ily("Times New Roman")

ntSize(16)

d(True)

ght(75)

2_t(font)

2_gnment(enter)

2_ectName("engle2_label")

= t(lwidget)

metry((474, 30, 120, 191))

lTip("")

tusTip("")

oFillBackground(True)

ectName("widget")

_Button = utton(lwidget)

_metry((280, 210, 50, 50))

_ectName("up_Button")

_Button = utton(lwidget)

_metry((340, 270, 50, 50))

_ectName("right_Button")

_Button = utton(lwidget)

_metry((220, 270, 50, 50))

_ectName("left_Button")

_Button = utton(lwidget)

_metry((280, 330, 50, 50))

_ectName("down_Button")

_()

__()

__()

__()

__()

__()

__()

__()

__()

__()

__()

__()

1__()

__()

_()

__()

2__()

__()

__()

__()

__()

tralWidget(lwidget)

r = ar(SFmotorcontrol)

metry((0, 0, 640, 23))

ectName("menubar")

uBar(r)

bar = sBar(SFmotorcontrol)

ectName("statusbar")

tusBar(bar)

slateUi(SFmotorcontrol)

tSlotsByName(SFmotorcontrol)

def retranslateUi(self, SFmotorcontrol):

_translate = ate

dowTitle(_translate("SFmotorcontrol", "云台控制软件"))

le(_translate("SFmotorcontrol", "连接设置"))

t(_translate("SFmotorcontrol", "连 接"))

lect_rentText(_translate("SFmotorcontrol", "115200"))

t(_translate("SFmotorcontrol", "串口:"))

_t(_translate("SFmotorcontrol", "波特率:"))

_check_t(_translate("SFmotorcontrol", "串口检测"))

_lTip(_translate("SFmotorcontrol",

"

使用鼠标拖动

"))

_t(_translate("SFmotorcontrol", "水平旋转"))

_t(_translate("SFmotorcontrol", "垂直俯仰"))

_t(_translate("SFmotorcontrol", "180"))

_t(_translate("SFmotorcontrol", "90"))

_t(_translate("SFmotorcontrol", "90"))

_t(_translate("SFmotorcontrol", "0"))

_t(_translate("SFmotorcontrol", "0"))

_t(_translate("SFmotorcontrol", "-90"))

_t(_translate("SFmotorcontrol", "270"))

1_t(_translate("SFmotorcontrol", "0.0"))

_t(_translate("SFmotorcontrol", "自动"))

2_t(_translate("SFmotorcontrol", "0.0"))

_t(_translate("SFmotorcontrol", "上"))

_t(_translate("SFmotorcontrol", "右"))

_t(_translate("SFmotorcontrol", "左"))

_t(_translate("SFmotorcontrol", "下"))

在PyCharm中运行主程序,点击“RUN”—>RUN‘PTMainPRO’,无错则显示设计好的界面,如下图所示,按钮在程序中做了些美化,形状和颜色和前文所述的有所不同。

五、应用程序打包

程序编好后,最后一步当然是打包成可执行的exe文件啦,如此才能摆脱程序对IDE环境的依赖,能够在其他电脑上运行。在这里,我们使用pyinstaller来进行程序打包,当然还有其他方法来完成这件事情,感兴趣的可以自行尝试一哈。

首先,需要安装pyinstaller,懒人可在cmd中pip安装,输入“pip install

pyinstaller”回车,然后等待即可。

在执行pyinstaller之前,需要确认所有关于应用程序所需的资源(包含图片以及依赖的.py文件都放进dist下的项目目录中),否则会打包不成功。

为确保打包成功,项目路径中最好不要出现中文(大虾指导的,具体为什么,额,不清楚,包括前文所述的环境搭建时也有这个坑,爬过的才知道)。

管理员运行cmd,cd到项目路径下,执行“pyinstaller ”回车,执行结果如下图。

如此表明打包成功,来到我们的项目目录下,会发现在dist文件夹下多了一个文件夹PTMainPRO,这就是我们的应用程序所有需要的文件,当然包括可执行文件,如下图所示。

当然,也可以打包成只有一个exe文件,有兴趣的朋友可以深入研究下

pyinstaller的执行参数。

至此,一个简单的基于Python+QT的PC应用程序就完成了,感谢大家的耐心阅读,若有不当之处,请批评指正。


发布者:admin,转转请注明出处:http://www.yc00.com/news/1705059538a1389676.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信