PyQt5+fitz实现图片与PDF互相转换

前言

为了方便实用的实现PDF和图片之间的相互转换,采用PyQt5作为作为界面设计,使用fitz来进行数据处理。其中PyQt5使用pip install PyQt5 进行安装。fitz使用pip install PyMuPDF (历史遗留问题导致与包名不同)进行安装。

主界面

程序主界面

主界面里的左侧为两个按钮,右边是操作区,通过点击按钮实现功能切换。
主界面代码如下:

class PdfTools(QWidget):
    def __init__(self):
        super().__init__()
        self.setup_UI()
    def setup_UI(self):
        #窗口名称及大小
        hbox = QHBoxLayout()
        self.setWindowTitle('PDF图片互转程序--by klxy')
        self.setWindowIcon(QIcon(':/klxy.ico'))
        self.setGeometry(300,300,500,600)
        
        #建立splitter
        splitterleft = QSplitter(Qt.Vertical)#按钮所在布局
        #建立堆叠布局
        stack = QStackedLayout()#创建堆叠布局
        #实例化子窗口
        self.itp = ITPPage()
        self.pti = PTIPage()
        
        #功能切换按键
        self.btn_itp = QPushButton("图\n片\n合\n并\n为\nP\nD\nF")
        self.btn_pti = QPushButton("P\nD\nF\n转\n图\n片")
        self.btn_itp.setToolTip("多张图片合并为一个PDF文件")
        self.btn_pti.setToolTip("一个PDF文件拆分成多张图片")
        self.btn_itp.setStyleSheet("QPushButton{font-family:'仿宋';font-size:14px;color:rgb(0,0,0,255);}")
        self.btn_pti.setStyleSheet("QPushButton{font-family:'仿宋';font-size:14px;color:rgb(0,0,0,255);}")
        #将按键添加到布局
        splitterleft.addWidget(self.btn_itp)
        splitterleft.addWidget(self.btn_pti)
        #splitterleft.setSizes([100,100,100])#用于确定每个控件宽度,不设置时,默认平均分配,等比放大缩小
        #将窗口添加到布局
        stack.addWidget(self.itp)
        stack.addWidget(self.pti)
        #事件绑定
        self.btn_itp.clicked.connect(lambda:(stack.setCurrentWidget(self.itp)))
        self.btn_pti.clicked.connect(lambda:(stack.setCurrentWidget(self.pti)))
        #设置整体布局
        hbox.addWidget(splitterleft)
        hbox.addLayout(stack)
        self.setLayout(hbox)
if __name__ == "__main__":
    app = QApplication(sys.argv)
    tools = PdfTools()
    tools.show()
    sys.exit(app.exec_())

主界面的布局是QHBoxLayout(),横向的布局,左侧是一个QSplitter(采用垂直布局)用来放置按钮,无论窗口怎么调节,按钮始终充满这个区域,右侧是一个堆叠布局(用于放置功能窗口),使用self.button.clicked.connect(lambda:(stack.setCurrentWidget(Qwidget)))来进行窗口的切换。

图片合并为PDF

首先是界面

图片合并为PDF
代码如下:

class UI_ITP_page(QWidget):
    def setup_UI(self):
        self.setStyleSheet("background-color:Azure")
        self.setGeometry(300,300,200,200)
        self.setAcceptDrops(True)#设置拖拽允许
        self.setWindowTitle('ITP')
        box = QBoxLayout(QBoxLayout.TopToBottom)

        self.filecount = QLabel('已导入图片数量:无')#显示窗口图片张数
        self.filecount.setStyleSheet("background-color:LightPink")

        self.piclist = QListWidget()#listwidget窗口
        self.piclist.setGeometry(50,50,100,100)

        self.compose = QPushButton("合成PDF")
        self.compose.setStyleSheet("background-color:cyan;font-family:'仿宋';")

        self.clear = QPushButton("清空列表")
        
        self.look = QPushButton("预览PDF")
		#将控件按垂直布局排列
        box.addWidget(self.filecount)
        box.addWidget(self.piclist)
        box.addWidget(self.compose)
        box.addWidget(self.look)
        box.addWidget(self.clear)
        self.setLayout(box)

界面非常的简洁,一个用于显示图片张数的label,一个合成按钮,一个清空按钮,一个预览按钮,还有一个空白区域。

如何添加图片

图片导入

self.setAcceptDrops(True)#设置控件接受拖拽
self.piclist.setGeometry(50,50,100,100)#设置窗口大小(这里随意设置)
self.piclist.setSpacing(10)#设置每个项的间隔
self.piclist.setIconSize(QSize(100,100))#图标默认值
def dragEnterEvent(self,e):
        if e.mimeData().hasUrls():
            e.accept()
        else:
            e.ignore()
def dropEvent(self, e):
        global links
        if e.mimeData().hasUrls():
            e.accept()
            urls = e.mimeData().urls()
            links = []
            for u in urls:
                links.append(str(u.toLocalFile()))
                #print(str(u.toLocalFile()))
        else:
            e.ignore()
        self.additems()
def additems(self):
        for pic in links:
            if pic[-4:] in ['.jpg','.png','.gif','.bmp','.JPG','.PNG','.GIF','.BMP']:
                pix = QPixmap()#...
                pix.load(pic)#...
                item = QListWidgetItem(QIcon(pix.scaled(150,pix.height()/pix.width()*150,Qt.KeepAspectRatio, Qt.SmoothTransformation)),pic)#
                self.piclist.addItem(item)
        self.filecount.setText('已导入图片数量:'+str(self.piclist.count())+"张")
        self.compose.setEnabled(True)

这里开启了控件的拖入功能,然后重写了拖入的事件,在拖入的时候,通过dropEvent()处理拖入的对象,获取图片的地址添加到links,然后用pic[-4:] (文件地址的格式部分)筛选出图片,将图片加载为QPixmap对象,这里的QListWidgetItem(QIcon(pix.scaled(150,pix.height()/pix.width()*150,Qt.KeepAspectRatio, Qt.SmoothTransformation)),pic)很重要,一般来说可以直接使用QIcon(picpath)来加载图片,但是在加载比较大的图片(几Mb)的时候,图片的拖动会很卡顿,由于每次拖动变换图片顺序,都会重绘缩略图,所以这里对导入时的缩略图进行限制,可以保证流畅地拖动。

如何拖动item

拖动图片

self.piclist.setMovement(QListView.Free)#表示数据项可以随意移动
self.piclist.setDragDropMode(QAbstractItemView.InternalMove)#这一项保证拖动的时候不会进行复制

拖拽功能就这两个需要设置,如果只设置第一个,则每次拖动都会复制一个item

常量含义
NoDragDrop0在wiew内不支持拖和放
DragOnly1在view内支持拖动
DropOnly2在view内接受放下
DragDrop3在view内支持拖动和放下
InternalMove4在view内接受它自己范围内的移动操作《不是拷贝)

右键菜单

图片右键菜单

self.piclist.setContextMenuPolicy(3)#设置右键菜单启用
self.piclist.customContextMenuRequested[QtCore.QPoint].connect(self.myMenu)
def myMenu(self,point):
	menu = QMenu()#建立一个菜单对象
if self.piclist.itemAt(point) is None:
    pass#在空白处点击右键无效
else:
    menu.addAction(QAction(u'顺时针转90°',self))
    menu.addAction(QAction(u'逆时针转90°',self))
    menu.addAction(QAction(u'删除',self))
    menu.triggered[QAction].connect(partial(self.changePic,point))
    menu.exec_(QCursor.pos())
    def picRotate(self,picitem,angle):
	    image = QImage()
	    picpath = picitem.text()
	    image.load(picpath)
	    trans = QTransform()
	    trans.rotate(angle)
	    image = image.transformed(trans)
	    image.save(picpath)#将旋转后的图片替换原来的图片
	    picitem.setIcon(QIcon(picpath))#刷新图标(这里可以对缩略图像素进行一下限制)
def changePic(self,p,e):    
    item = self.piclist.itemAt(p)
    if e.text() == '顺时针转90°':
        self.picRotate(item,90)
    if e.text() == '逆时针转90°':
        self.picRotate(item,-90)
    if e.text() == '删除':
       self.piclist.takeItem(self.piclist.indexFromItem(item).row())
       self.filecount.setText('已导入图片数量:'+str(self.piclist.count())+"张")

setContextMenuPolicy()启用右键菜单,用customContextMenuRequested对菜单进行绑定。

合成PDF

现在我们已经对图片排好序,调好角度,接下来就是合成PDF了。
合成PDF这个动图有点花了,没办法,5M限制。

def pictopdf(self,pics):
        doc = fitz.open()#创建一个PDF文件
        for i,img in enumerate(pics):
            page = doc.new_page()
            
            imgdoc = fitz.open(img)
            pdfbytes = imgdoc.convertToPDF()
            imgpdf = fitz.open("pdf", pdfbytes)
            imgrect = imgpdf.page_cropbox(0)
            page.set_mediabox(fitz.Rect(0,0,1000,imgrect.y1/imgrect.x1*1000))
            r = fitz.Rect(0,0,page.rect.width,page.rect.height)#这里对图片的大小进行了统一的调节
            page.show_pdf_page(r,imgpdf,0,rotate = 0)

        filepath,name = os.path.split(pics[0])
        self.savepath = filepath+"/"+filepath.split("/")[-1]+".pdf"
        doc.save(self.savepath)
        doc.close()
        self.look.setEnabled(True)
        os.startfile(filepath)#打开pdf生成的文件夹

这里的合成方法来自fitz的示例文档。
1.创建一个PDF对象。
2.为每一张图片创建一个新的页面。
3.页面统一设置为同一宽度。
4.将图片放入页面,并按照宽度相等的原则缩放图片。
(这里打开PDF虽然图片的大小有变化,但是信息是没有压缩的,合成的PDF文件大小基本等于图片大小之和)

PDF文件预览

PDF预览功能这一部分的代码主要参考自:@Tiny_Feng 链接: PyQt5(利用QWidget和QPainter类)实现图片的鼠标左键移动、中键缩放、右键还原等.

class showpdf(QWidget):
    def __init__(self,path):
        self.pdfpath = path
        super().__init__()
        self.setup_UI()
    def setup_UI(self):       
        doc = fitz.open(self.pdfpath)
        self.setWindowTitle('PDF预览(清晰度与原图一致,此图仅供预览)--by klxy')
        #self.setWindowIcon(QIcon(':/klxy.ico'))
        self.ispressed = bool(False)  
        self.ULpositon = QPoint(0,0)
        self.setGeometry(800,50,500,1000)
        self.piclist = []        
        pos = QPoint(0,0)   
        for i,per in enumerate(doc):#
            #print(per)
            pix = per.getPixmap()
            #print(pix)
            fmt = QImage.Format_RGBA8888 if pix.alpha else QImage.Format_RGB888
            qtimg = QImage(pix.samples, pix.width, pix.height, pix.stride, fmt)
            self.showpic = QPixmap()
            self.showpic = self.showpic.fromImage(qtimg)
            self.piclist.append(self.showpic)
            if i == 0:
                pos = self.showpic.rect().bottomRight()+QPoint(1,1)
            else:
                pos += self.showpic.rect().bottomLeft()+QPoint(0,1)
        #print(pos)
        self.mainpix = QPixmap(pos.x(),pos.y())
        self.scaledImg = self.mainpix.scaled(self.size())
        self.hwscale = pos.y()/pos.x()
        #print(self.hwscale)
        self.pixpainter = QPainter(self.mainpix)
        self.scalespeed = 15
        pos = QPoint(0,0)
        for pic in self.piclist:
            self.pixpainter.drawPixmap(pos,pic)
            pos = pos+pic.rect().bottomLeft()
        self.pixpainter.end()
        box = QBoxLayout(QBoxLayout.TopToBottom)
        self.setLayout(box)
    def paintEvent(self,e):
        painter = QPainter(self)
        painter.drawPixmap(self.ULpositon,self.scaledImg)
        painter.end()
        #print('yeah')
    def get_path(self,path):
        self.path = path
    #鼠标事件
    def mousePressEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:                            # 左键按下
            #print("鼠标左键单击")  # 响应测试语句
            self.isPressed = True;                                         # 左键按下(图片被点住),置Ture
            self.preMousePosition = event.pos()                                # 获取鼠标当前位置
                    
    '''重载一下滚轮滚动事件'''
    def wheelEvent(self, event):
        angle=event.angleDelta() / 8                                           # 返回QPoint对象,为滚轮转过的数值,单位为1/8度
        angleX=angle.x()                                                       # 水平滚过的距离(此处用不上)
        angleY=angle.y()
        self.scalespeed = self.hwscale * self.scaledImg.width()/25
        if self.scaledImg.height()>10 and self.scaledImg.width()>10:                                                # 竖直滚过的距离
            if angleY > 0:                                                         # 滚轮上滚
                #print("鼠标中键上滚")  # 响应测试语句
                self.scaledImg = self.mainpix.scaled(self.scaledImg.width()+self.scalespeed,(self.scaledImg.width()+self.scalespeed)*self.hwscale)
                newWidth = event.x() - (self.scaledImg.width() * (event.x()-self.ULpositon.x()))/ (self.scaledImg.width()-self.scalespeed)
                newHeight = event.y() - (self.scaledImg.height() * (event.y()-self.ULpositon.y())) / (self.scaledImg.height()-self.scalespeed*self.hwscale)
                self.ULpositon = QPoint(newWidth, newHeight)                    # 更新偏移量
                self.repaint()                                                     # 重绘
            else:                                                                  # 滚轮下滚
                #print("鼠标中键下滚")  # 响应测试语句
                self.scaledImg = self.mainpix.scaled(self.scaledImg.width()-self.scalespeed,(self.scaledImg.width()-self.scalespeed)*self.hwscale)
                newWidth = event.x() - (self.scaledImg.width() * (event.x()-self.ULpositon.x()))/ (self.scaledImg.width()+self.scalespeed)
                newHeight = event.y() - (self.scaledImg.height() * (event.y()-self.ULpositon.y()))/ (self.scaledImg.height()+self.scalespeed*self.hwscale)
                self.ULpositon = QPoint(newWidth, newHeight)                    # 更新偏移量
                self.repaint()
        else:                                                    # 重绘
            self.scaledImg=self.mainpix.scaled(self.scaledImg.width()+10,self.scaledImg.height()+10*self.hwscale)
    '''重载一下鼠标键松开事件'''
    def mouseReleaseEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:                            # 左键释放
            self.isLeftPressed = False;  # 左键释放(图片被点住),置False
            #print("鼠标左键松开")  # 响应测试语句
        elif event.button() == Qt.RightButton:                                 # 右键释放
            self.ULpositon = QPoint(0, 0)                                   # 置为初值
            self.scaledImg = self.mainpix.scaled(self.size())                # 置为初值
            self.repaint()                                                     # 重绘
            #print("鼠标右键松开")  # 响应测试语句
 
    '''重载一下鼠标移动事件'''
    def mouseMoveEvent(self,event):
        if self.isPressed:                                                 # 左键按下
            #print("鼠标左键按下,移动鼠标")  # 响应测试语句
            self.endMousePosition = event.pos() - self.preMousePosition        # 鼠标当前位置-先前位置=单次偏移量
            self.ULpositon = self.ULpositon + self.endMousePosition      # 更新偏移量
            self.preMousePosition = event.pos()                                # 更新当前鼠标在窗口上的位置,下次移动用
            self.repaint()                 

说一下思路:
1.PDF是很多张图片组成的,用fitz读取每一页的大小。
2.将每一页的大小加起来,就是画布的大小。
3.在画布上依次绘制出图像。
4.使用鼠标对图像进行缩放拖拽。
4.1.缩放时,鼠标所指位置不动,绘图的坐标和图像大小发生改变,坐标变换原理可以仔细阅读代码。

self.scalespeed = self.hwscale * self.scaledImg.width()/25#确保缩放速度一致,图像越大缩的越快

4.2.拖动只改变绘图的起点坐标。
4.3.需要注意的是对于图像不是正方形的情况,坐标的变换要根据比例来调整,这里有一个坑:

self.scalespeed是指每转一次,像素的缩放值,如果图像是正方形,那这个值就是确定的。但是对于矩形,每次缩放的值就需要调整了,由于坐标点的值是int类型,所以每次缩放都会有一些数据丢失,最后会导致图像变形,所以在每次重绘时,要根据图像的比例(self.hwscale)调整每次的缩放值以抵消数据丢失导致的图像变扁或者变高的问题。

self.scaledImg = self.mainpix.scaled(self.scaledImg.width()+self.scalespeed,(self.scaledImg.width()+self.scalespeed)*self.hwscale)
                newWidth = event.x() - (self.scaledImg.width() * (event.x()-self.ULpositon.x()))/ (self.scaledImg.width()-self.scalespeed)
                newHeight = event.y() - (self.scaledImg.height() * (event.y()-self.ULpositon.y())) / (self.scaledImg.height()-self.scalespeed*self.hwscale)

这里的高度变化是根据图像比例进行调节的,确保图像不会变形

PDF转图片

界面如下:
在这里插入图片描述
这一部分的功能相较于图片转PDF要简单得多,UI也不复杂。
一个QPushButton用于导入文件;
一个Qlabel显示导入文件名称;
一个滑动条(slider)调节图像质量,默认最高;
界面代码如下:

        vbox = QVBoxLayout()
        self.setGeometry(300,300,100,100)
        self.setWindowTitle('PTI')
        ptiAct.setStatusTip('Pdf file to Pictures')
        ptiAct.triggered.connect(qApp.quit)
        self.pti_btn = QPushButton('打开文件',self)
        self.pti_btn.setToolTip('打开需要提取的PDF文件')
        self.label = QLabel(self)

        self.overed = QProgressBar(self)#设置进度条
        self.overed.setMinimum(0)
        self.overed.setMaximum(100)
        self.overed.setValue(0)
        
        self.pti_btn.clicked.connect(self.open_file)#打开文件夹选取
        
        self.slider = QSlider(Qt.Horizontal)#滑条的设置
        self.slider.setMinimum(1)
        self.slider.setMaximum(5)
        self.slider.setSingleStep(1)
        self.slider.setValue(5)
        self.slider.setTickPosition(QSlider.TicksBelow)
        self.slider.setTickInterval(1)
        self.sliderlabel = QLabel(self)
        self.sliderlabel.setText("质量等级:5")
        self.slider.valueChanged.connect(self.zl_changed)
        
        self.deal = QPushButton('开始操作',self)
        self.deal.setEnabled(False)
        self.deal.clicked.connect(self.dealpdf)
        
        self.aimfiles = QPushButton('文件已生成',self)
        self.aimfiles.clicked.connect(self.openaim)
        self.aimfiles.setVisible(False)
        
        vbox.addWidget(self.pti_btn)
        vbox.addWidget(self.label)
        vbox.addWidget(self.slider)
        vbox.addWidget(self.sliderlabel)
        vbox.addWidget(self.overed)
        vbox.addWidget(self.deal)
        vbox.addWidget(self.aimfiles)
        self.setLayout(vbox)
        #self.setStyleSheet("background-color:green")
    def open_file(self):
        global dir,file

        self.aimfiles.setVisible(False)
        self.label.setText('')
        self.overed.setValue(0)

        self.PdfFilepath,type = QFileDialog.getOpenFileName(self,"打开PDF文件",os.getcwd(),"PDF Files(*.pdf)")   
        dir,file = os.path.split(self.PdfFilepath)
        self.label.setText("文件名称:"+file)
        self.deal.setEnabled(True)
        def dealpdf(self):
        self.pdf_image(self.PdfFilepath,dir,self.slider.value(),self.slider.value(),0)
    def pdf_image(self,pdfPath,imgPath,zoom_x,zoom_y,rotation_angle):
        print("sb")
        print(pdfPath,imgPath)
        # 打开PDF文件
        pdf = fitz.open(pdfPath)
        # 逐页读取PDF
        #创建文件夹
        if not os.path.exists(imgPath+'/'+file[0:-4]):
                os.mkdir(imgPath+'/'+file[0:-4])
        for i,page in enumerate(pdf):
            # 设置缩放和旋转系数
            trans = fitz.Matrix(zoom_x, zoom_y).preRotate(rotation_angle)
            pm = page.getPixmap(matrix=trans, alpha=False)
            # 开始写图像
            pm.writePNG(imgPath+'/'+file[0:-4]+'/'+str(i)+".png")
            self.overed.setValue((i+1)/len(pdf)*100)
        pdf.close() 
        self.aimfiles.setVisible(True)
    def zl_changed(self):
        self.sliderlabel.setText("质量等级:"+str(self.slider.value()))
    def openaim(self):
        os.startfile(self.PdfFilepath[0:-4])

PDF转图片的代码参考自fitz
演示一下效果:
在这里插入图片描述在提取完成后,最下方会出现文件夹的按钮,按下可以打开文件夹,这里主要使用*setVisible(False)*先隐藏,等待处理完成后在出现。

结语

代码比较简单,写的比较简陋,充分发挥了缝合怪的特点,但功能上我觉得比较合适,简单好用就是好工具(🙄)。
源码:https://download.csdn.net/download/qq_42521874/16822356 🤣给点。。。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用Python3、PyQT5和Pyserial实现串口工具的方法: 1. 安装Pyserial和PyQT5库 在终端或命令行输入以下命令来安装Pyserial和PyQT5: ``` pip install pyserial pip install pyqt5 ``` 2. 创建GUI界面 使用PyQT5创建一个简单的GUI界面。在这个界面,你需要添加一个下拉菜单来选择串口、一个文本框来显示串口数据和一些按钮来打开/关闭串口和发送数据。 下面是一个简单的PyQT5串口工具界面: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) ``` 3. 实现串口操作 使用Pyserial库来创建串口连接。在打开串口之前,你需要获取可用的串口列表并将它们添加到下拉菜单。在发送数据之前,你需要从文本框获取数据。 下面是一个简单的Pyserial串口操作代码: ```python import serial class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) ``` 4. 运行程序 将GUI界面和串口操作代码组合起来,并在主函数运行程序。 下面是一个完整的PyQT5串口工具代码: ```python import sys import serial from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) # 绑定按钮事件 self.openButton.clicked.connect(self.openSerial) self.closeButton.clicked.connect(self.closeSerial) self.sendButton.clicked.connect(self.sendSerial) self.clearButton.clicked.connect(self.textEdit.clear) def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) if __name__ == '__main__': app = QApplication(sys.argv) serialTool = SerialTool() serialTool.show() sys.exit(app.exec_()) ``` 运行程序后,你应该可以打开串口并发送数据了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值