PyQt5制作十进制二进制转换器

此文章旨在记录自己学习内容,内容十分基础,还有许多不完善的多体谅 :)

我的继承开发环境是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的事件循环

拙劣的文章到此就结束了,感谢阅读!!

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值