此文章旨在记录自己学习内容,内容十分基础,还有许多不完善的多体谅 :)
我的集成开发环境是Pycharm Community 2021, PyQt5的版本是5.15.9,推荐使用pip命令在线下载,因为它会自动根据你的Python版本来选择合适的PyQt5版本。此处就不过多介绍如何下载和配置PyQt5。
该程序为是本人的作业,要求不能用Python的内置函数来转换进制,且必须用到栈来实现,所以代码冗长请见谅。
转换器基本功能:
(1)二进制数转换十进制数(整数,小数);
(2)十进制转换二进制数(整数,小数);
1 界面设计
1.1 设置外部工具
首先,找到你电脑上的Python文件夹,在Python/Scripts目录下找到designer.exe文件,并复制该文件的路径。(没有该文件可能是没有下载PyQt5及其相应的组件)
之后,打开Pycharm,点击左上角的文件里的设置
在工具那一栏里选择外部工具,点击右侧方框上的加号新增一个工具,名称可以随意命名,此处我将其命名为QtDesigner。在程序那一栏里粘贴之前复制的文件路径,在工作目录那一写上$ProjectFileDir$,之后点击确认即可,记得勾选该工具并且点击右下角的应用。
之后测试是否配置完成,随便选取一个Python文件右键在下部找到外部工具QtDesigenr,点击之后会进入Designer的界面,此时证明该外部工具配置成功。
1.2 界面设计
新建一个Widget,之后拖动左侧工具栏里的工具,放到右侧进行界面设计,此处我就不过多讲解,放上我的原图和用过的工具。其中接收输入和展示输出的都是LineEdit。按钮是PushButton,Label用于显示文字。
更改控键的文本大小和背景颜色可以右键该控件,选择样式表,输入以下内容:
上面的数字用于更改字体大小,下面的可以上面的"添加颜色"来选择色调,之后点击右下角应用即可更改。请注意上面一句末尾为分号;来分隔句子,否则无法更改。
2 代码设计
QtDesigner的信号与槽的连接太过局限,所以仅仅用于UI界面的设计。功能函数设计部分在Pycharm上进行编写。
首先,新建一个文件夹来放刚刚制作好的ui文件,之后再该文件里新建一个python文件,在此命名为main.py。
输入以下代码,正常运行能够开启刚刚设计的ui界面,此时点击是没用的,所以之后我们开始连接信号和槽。
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5 import uic
import sys
class MyWindow(QWidget):
# 以下为类的初始化方法,当创建MyWindow的实例时,它会被自动调用
def __init__(self):
super().__init__() # 确保了QWidget的所有初始化代码都被执行
self.init_ui() # 用于初始化或设置窗口的用户界面
def init_ui(self):
self.ui = uic.loadUi("./CBconvert.ui") # 该处填写ui文件
self.ui.setWindowTitle("进制转换器") # 设置窗口标题
self.ui.setFixedSize(690, 460) # 设置窗口大小
if __name__ == '__main__':
app = QApplication(sys.argv) # 允许Qt应用程序处理命令行参数(配置文件)
w = MyWindow() # 实例化上述编写的MyWindow类
w.ui.show() # 展示窗口,即我用Qt设置的ui文件
app.exec() # 进入Qt的事件循环
再创建一个栈类来用于后续的进制转换,此处用列表模拟栈的后进先出的性质。
# 引入一个顺序栈,之后用于数制转换
class sqStack:
def __init__(self):
self.items = []
def push(self, item): # 入栈
self.items.append(item)
def pop(self): # 出栈,首先需要判断是否为空,以防抛出"栈下溢"的异常
if not self.is_empty():
return self.items.pop()
return None
def is_empty(self): # 判断是否为空,为空返回False
return len(self.items) == 0
def length(self): # 计算其栈中元素个数,即栈的长度
return len(self.items)
之后,在MyWindow类里,添加十进制转二进制方法dec_convert_bin()和二进制转十进制方法bin_convert_dec()
# 二进制转十进制
def bin_convert_dec(self, bin_str):
# 分离整数部分和小数部分,使用 str.partition() 方法分割成三个部分:整数部分、分隔符(.)和小数部分
# 其中分隔符不关心,则用占位符_来忽略
integer_part, _, fractional_part = bin_str.partition('.')
# 如果整数部分为空字符串(即没有整数部分),则将其设置为 "0"
if integer_part == "":
integer_part = "0"
# 转换整数部分
d = 0
base = 1
for digit in reversed(integer_part): # 这部分代码遍历整数部分的每一位(从最低位开始),并计算其对应的十进制值
if digit == '1': # base 初始化为 1,并在每次迭代中乘以 2,以便计算当前位的值
d += base # 如果当前位是 '1',则将其对应的值加到d上
base *= 2 # d 就是整数部分的十进制表示
decimal_integer = d
# 转换小数部分
decimal_fractional = 0
power = -1
for digit in fractional_part: # 这部分代码遍历小数部分的每一位,并计算其对应的十进制值
decimal_fractional += int(digit) * (2 ** power) # 对于小数部分,每一位的值是其代表的 2 的幂次方的值
power -= 1 # power 初始化为 -1,并在每次迭代中减去 1
# 合并整数部分和小数部分
decimal = decimal_integer + decimal_fractional
return decimal
# 十进制转二进制
def dec_convert_bin(self, decimal_num, precision):
# 获取小数部分与整数部分
integer_part = int(decimal_num)
fraction_part = decimal_num - integer_part
# 处理整数部分,不断取余压入栈中,然后不断更新整数部分
integer_stack = sqStack()
while integer_part > 0:
integer_stack.push(integer_part % 2) # 取余并将结果入栈
integer_part //= 2
# 处理小数部分,每次循环小数部分乘以2,取整数部分为二进制数字,并将其添加到字符串,并除去整数部分
fraction_binary = '' # 存储二进制表达式
while fraction_part != 0 and len(fraction_binary) < precision: # 循环继续,直到小数部分变为0或达到指定的精度 precision
fraction_part *= 2
fraction_digit = int(fraction_part)
fraction_binary += str(fraction_digit)
fraction_part -= fraction_digit
# 组合小数部分和整数部分,循环从栈中弹出元素
if integer_stack.is_empty():
binary_num = "0"
else:
binary_num = ''.join(str(integer_stack.pop()) for _ in range(integer_stack.length()))
if fraction_binary:
binary_num += '.' + fraction_binary
return binary_num
以下设计一个函数用于判断十进制还是二进制,并且执行相应的转换,并且为确保程序报错闪退,进行程序健全性设计。以下用正则表达式来排除错误的输入。
def judgeDB(self):
"""实现判断输入的数是十进制还是二进制,并且相互转换的逻辑"""
bin_str = self.user_binary.text()
dec_str = self.user_decimal.text()
if bin_str == dec_str == "": # 防止无输入导致报错
self.convert_result.setText("请输入数据~")
elif bin_str.count(".") > 1 or dec_str.count(".") > 1:
self.convert_result.setText("请正确输入数据~")
elif bin_str == "":
# 正则表达式来排除非数字和非小数点的输入
n = re.search("[^0-9.]", dec_str)
if n is None:
decimal_num = float(dec_str)
precision = self.precision_change()
binary_num = self.dec_convert_bin(decimal_num, precision)
self.convert_result.setText(binary_num)
else:
self.convert_result.setText("请输入十进制小数或整数哦~")
self.user_binary.clear()
self.user_decimal.clear()
elif dec_str == "":
n = re.search("[^0-1.]", bin_str)
if n is None:
binary = bin_str
binary = self.bin_convert_dec(binary)
self.convert_result.setText(str(binary))
else:
self.convert_result.setText("请输入二进制小数或整数哦~")
self.user_binary.clear()
self.user_decimal.clear()
# 防止同时输入
else:
self.convert_result.setText("请不要同时输入~")
self.user_decimal.clear()
self.user_binary.clear()
以下为清除按钮的功能编写,清除所有文本框内容,二进制小数精度框除外
# 清空用户输入的数字
def clear_number(self):
self.user_decimal.clear()
self.user_binary.clear()
self.convert_result.clear()
以下为十进制转二进制时展示小数的精度显示
# 确认更改精度
def precision_change(self):
precision = self.user_precision.text() # 接收用户输入的精度
# 这意味着如果用户没有指定精度,则默认使用精度 10
if precision == "":
self.user_precision.setText("10") # 在精度处显示10
precision = 10
return precision # 精度赋值为10并返回
else:
# 用try except来捕获异常,防止非整数的输入,输入错误时提醒用户
try:
int(precision)
if not 1 <= int(precision) <= 20:
self.convert_result.setText("请输入1-20间的整数~")
else:
self.convert_result.clear()
return int(precision)
except ValueError:
self.convert_result.setText("请输入1-20间的整数~")
以下为界面的初始化设置
def init_ui(self):
self.ui = uic.loadUi("./CBconvert.ui")
self.ui.setWindowTitle("进制转换器") # 设置窗口标题
self.ui.setFixedSize(690, 460) # 设置窗口大小
# 设置文本框的输入长度
self.ui.lineEdit_3.setMaxLength(15)
self.ui.lineEdit_2.setMaxLength(15)
self.ui.lineEdit_4.setMaxLength(2)
self.ui.lineEdit.setReadOnly(True) # 设置结果显示栏为只读状态
# 将UI中控件的引用保存到类的成员变量中,以便代码编写
self.user_decimal = self.ui.lineEdit_2 # 获取用户输入的十进制
self.user_binary = self.ui.lineEdit_3 # 获取用户输入的二进制
self.convert_result = self.ui.lineEdit # 获取转换结果
self.user_precision = self.ui.lineEdit_4 # 获取精度
convert_btn = self.ui.pushButton_2 # 获取转换按钮信息
empty_btn = self.ui.pushButton # 获取清空按钮信息
precision_btn = self.ui.pushButton_3 # 确认进制转换
# 连接信号与槽,点击相应的按钮可以触发相应的功能
# 当用户点击这些按钮时,将分别调用clear_number、judgeDB和precision_change方法
empty_btn.clicked.connect(self.clear_number)
convert_btn.clicked.connect(self.judgeDB)
precision_btn.clicked.connect(self.precision_change)
3 结果展示
以下为部分结果展示
4 源码
import sys
import re
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5 import uic
# 引入一个顺序栈,之后用于数制转换
class sqStack:
def __init__(self):
self.items = []
def push(self, item): # 入栈
self.items.append(item)
def pop(self): # 出栈,首先需要判断是否为空,以防抛出"栈下溢"的异常
if not self.is_empty():
return self.items.pop()
return None
def is_empty(self): # 判断是否为空,为空返回False
return len(self.items) == 0
def length(self): # 计算其栈中元素个数,即栈的长度
return len(self.items)
# 创建一个窗口类,该类继承自QWidget
class MyWindow(QWidget):
# 以下为类的初始化方法,当创建MyWindow的实例时,它会被自动调用
def __init__(self):
super().__init__() # 调用了QWidget的__init__方法,确保了QWidget的所有初始化代码都被执行
self.init_ui() # 用于初始化或设置窗口的用户界面
# 二进制转十进制
def bin_convert_dec(self, bin_str):
# 分离整数部分和小数部分,使用 str.partition() 方法分割成三个部分:整数部分、分隔符(.)和小数部分
# 其中分隔符不关心,则用占位符_来忽略
integer_part, _, fractional_part = bin_str.partition('.')
# 如果整数部分为空字符串(即没有整数部分),则将其设置为 "0"
if integer_part == "":
integer_part = "0"
# 转换整数部分
d = 0
base = 1
for digit in reversed(integer_part): # 这部分代码遍历整数部分的每一位(从最低位开始),并计算其对应的十进制值
if digit == '1': # base 初始化为 1,并在每次迭代中乘以 2,以便计算当前位的值
d += base # 如果当前位是 '1',则将其对应的值加到d上
base *= 2 # d 就是整数部分的十进制表示
decimal_integer = d
# 转换小数部分
decimal_fractional = 0
power = -1
for digit in fractional_part: # 这部分代码遍历小数部分的每一位,并计算其对应的十进制值
decimal_fractional += int(digit) * (2 ** power) # 对于小数部分,每一位的值是其代表的 2 的幂次方的值
power -= 1 # power 初始化为 -1,并在每次迭代中减去 1
# 合并整数部分和小数部分
decimal = decimal_integer + decimal_fractional
return decimal
# 十进制转二进制
def dec_convert_bin(self, decimal_num, precision):
# 获取小数部分与整数部分
integer_part = int(decimal_num)
fraction_part = decimal_num - integer_part
# 处理整数部分,不断取余压入栈中,然后不断更新整数部分
integer_stack = sqStack()
while integer_part > 0:
integer_stack.push(integer_part % 2) # 取余并将结果入栈
integer_part //= 2
# 处理小数部分,每次循环小数部分乘以2,取整数部分为二进制数字,并将其添加到字符串,并除去整数部分
fraction_binary = '' # 存储二进制表达式
while fraction_part != 0 and len(fraction_binary) < precision: # 循环继续,直到小数部分变为0或达到指定的精度 precision
fraction_part *= 2
fraction_digit = int(fraction_part)
fraction_binary += str(fraction_digit)
fraction_part -= fraction_digit
# 组合小数部分和整数部分,循环从栈中弹出元素
if integer_stack.is_empty():
binary_num = "0"
else:
binary_num = ''.join(str(integer_stack.pop()) for _ in range(integer_stack.length()))
if fraction_binary:
binary_num += '.' + fraction_binary
return binary_num
def judgeDB(self):
"""实现判断输入的数是十进制还是二进制,并且相互转换的逻辑"""
bin_str = self.user_binary.text()
dec_str = self.user_decimal.text()
if bin_str == dec_str == "": # 防止无输入导致报错
self.convert_result.setText("请输入数据~")
elif bin_str.count(".") > 1 or dec_str.count(".") > 1:
self.convert_result.setText("请正确输入数据~")
elif bin_str == "":
# 正则表达式来排除非数字和非小数点的输入
n = re.search("[^0-9.]", dec_str)
if n is None:
decimal_num = float(dec_str)
precision = self.precision_change()
binary_num = self.dec_convert_bin(decimal_num, precision)
self.convert_result.setText(binary_num)
else:
self.convert_result.setText("请输入十进制小数或整数哦~")
self.user_binary.clear()
self.user_decimal.clear()
elif dec_str == "":
n = re.search("[^0-1.]", bin_str)
if n is None:
binary = bin_str
binary = self.bin_convert_dec(binary)
self.convert_result.setText(str(binary))
else:
self.convert_result.setText("请输入二进制小数或整数哦~")
self.user_binary.clear()
self.user_decimal.clear()
# 防止同时输入
else:
self.convert_result.setText("请不要同时输入~")
self.user_decimal.clear()
self.user_binary.clear()
# 清空用户输入的数字
def clear_number(self):
self.user_decimal.clear()
self.user_binary.clear()
self.convert_result.clear()
# 确认更改精度
def precision_change(self):
precision = self.user_precision.text() # 接收用户输入的精度
# 这意味着如果用户没有指定精度,则默认使用精度 10
if precision == "":
self.user_precision.setText("10") # 在精度处显示10
precision = 10
return precision # 精度赋值为10并返回
else:
# 用try except来捕获异常,防止非整数的输入,输入错误时提醒用户
try:
int(precision)
if not 1 <= int(precision) <= 20:
self.convert_result.setText("请输入1-20间的整数~")
else:
self.convert_result.clear()
return int(precision)
except ValueError:
self.convert_result.setText("请输入1-20间的整数~")
def init_ui(self):
self.ui = uic.loadUi("./CBconvert.ui")
self.ui.setWindowTitle("进制转换器") # 设置窗口标题
self.ui.setFixedSize(690, 460) # 设置窗口大小
# 设置文本框的输入长度
self.ui.lineEdit_3.setMaxLength(15)
self.ui.lineEdit_2.setMaxLength(15)
self.ui.lineEdit_4.setMaxLength(2)
self.ui.lineEdit.setReadOnly(True) # 设置结果显示栏为只读状态
# 将UI中控件的引用保存到类的成员变量中,以便代码编写
self.user_decimal = self.ui.lineEdit_2 # 获取用户输入的十进制
self.user_binary = self.ui.lineEdit_3 # 获取用户输入的二进制
self.convert_result = self.ui.lineEdit # 获取转换结果
self.user_precision = self.ui.lineEdit_4 # 获取精度
convert_btn = self.ui.pushButton_2 # 获取转换按钮信息
empty_btn = self.ui.pushButton # 获取清空按钮信息
precision_btn = self.ui.pushButton_3 # 确认进制转换
# 连接信号与槽,点击相应的按钮可以触发相应的功能
# 当用户点击这些按钮时,将分别调用clear_number、judgeDB和precision_change方法
empty_btn.clicked.connect(self.clear_number)
convert_btn.clicked.connect(self.judgeDB)
precision_btn.clicked.connect(self.precision_change)
# 此段代码确保窗口持续运行
if __name__ == '__main__':
app = QApplication(sys.argv) # 允许Qt应用程序处理命令行参数(配置文件)
w = MyWindow() # 实例化上述编写的MyWindow类
w.ui.show() # 展示窗口,即我用Qt设置的ui文件
app.exec() # 进入Qt的事件循环
拙劣的文章到此就结束了,感谢阅读!!