PyQt5 入门

换了VSCODE开发,感觉比sublime好点,可能是由于第三版老弹框烦人吧。VSCODE看着也挺好看的。

学习 PyQt5 中文教程

0. 安装完之后错误

pip 安装了 pyqt5

from PyQt5.QtWidgets import QApplication, QWidget

这句错误:E0611:No name ‘QApplication‘ in module ‘PyQt5.QtWidgets‘

搜到是要sip,卸载python5重新安装了sip后再试还是错误,但是直接在命令行运行是好的。说明现在是外部环境的问题,把当前Anaconda路径换为python3路径,还是有错误。

最后换google一次解决,在VSCODE用户配置里加

"python.linting.pylintArgs": [
        "--extension-pkg-whitelist=PyQt5"
    ]

ok.

当前版本

Python==3.7.0
PyQt5==5.11.2
PyQt5-sip==4.19.12
操作系统:win10 64位

1. Hello World

加图标的时候,没有反应,根据原作者overflow的链接看的更模糊了。

图片目录在F:\py\py-snippet\hominid\music.png

,换了个绝对路径就好了。

因为

如果你在C:\test目录下执行python getpath\getpath.py,那么os.getcwd()会输出“C:\test”,sys.path[0]会输出“C:\test\getpath”。

我工作目录是在F:\py\py-snippet,python执行时候的目录下没有图片,所以下面两种方式都可以:

self.setWindowIcon(QIcon(sys.path[0]+‘/music.png‘))
self.setWindowIcon(QIcon(‘hominid/music.png‘))

第一篇很简单,放个窗口,控制大小和位置,退出

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QToolTip, QPushButton, QMessageBox, QDesktopWidget
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import QCoreApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # location and size
        self.setGeometry(300,300,300,300)

        # title
        self.setWindowTitle(‘Icon‘)

        # icon
        self.setWindowIcon(QIcon(sys.path[0]+‘/music.png‘))

        # 提示消息字体
        QToolTip.setFont(QFont(‘Georgia‘,10))

        # 提示框
        self.setToolTip(‘this is <br/><i>python</i> and <i>pyqt5</i><hr/> program‘)

        # 按钮以及提示框
        btn=QPushButton(‘退出‘,self)
        # 退出事件
        btn.clicked.connect(QCoreApplication.instance().quit)
        btn.setToolTip(‘quick, quick click‘)
        #btn.resize(btn.sizeHint())
        btn.move(50,50)

        self.center()
        self.show()

    # 关闭事件
    def closeEvent(self,event):
        print(‘close‘)
        reply = QMessageBox.question(self,"answer","To be or not to be?",QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            #print(QMessageBox.Yes)
            event.accept()
        else:
            reply = QMessageBox.question(self,‘??‘,‘are you sure? must quit‘,QMessageBox.Yes, QMessageBox.Yes)
            #event.ignore()
            event.accept() 

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        print(cp)
        qr.moveCenter(cp)
        self.move(qr.topLeft())

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

2. 状态工具和菜单栏

有意思,对文本框更多操作的话,可以做记事本啊。一直期望的打字字母烟花效果,可以考虑之后做。

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, qApp, QMenu, QTextEdit
from PyQt5.QtGui import QIcon

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 状态栏
        self.statusbar=self.statusBar()
        self.statusbar.showMessage(‘loading...‘)

        # 动作
        exitAct = QAction(QIcon(sys.path[0]+‘/music.png‘),‘&Exit‘,self)
        exitAct.setShortcut(‘Ctrl+1‘)
        exitAct.setStatusTip(‘load failed‘)
        exitAct.triggered.connect(qApp.quit)

        # 菜单
        menubar = self.menuBar()
        exitMenu = menubar.addMenu(‘&Exit‘)
        exitMenu.addAction(exitAct)

        # 多级菜单和动作
        ## 菜单栏加一项
        fileMenu = menubar.addMenu(‘File‘)
        ## 添加Add动作
        addAct = QAction(‘Add‘,self)
        fileMenu.addAction(addAct)
        ## 添加‘save as‘动作和‘save‘菜单,并将‘Save as‘动作加到‘Save‘菜单上
        saveAsAct = QAction(‘Save as‘,self)
        saveMenu = QMenu(‘save‘,self)
        saveMenu.addAction(saveAsAct)
        fileMenu.addMenu(saveMenu)

        # 勾选菜单
        viewMenu = menubar.addMenu(‘View‘)
        viewStatAct = QAction(‘View statusbar‘,self,checkable=True)
        viewStatAct.setStatusTip(‘..check me.‘)
        viewStatAct.setChecked(True)
        viewStatAct.triggered.connect(self.toggleMenu)
        viewMenu.addAction(viewStatAct)

        # 工具栏,用到了上面的exitAct
        self.toolbar = self.addToolBar(‘Exit‘)
        self.toolbar.addAction(exitAct)

        # 文本框
        textEdit = QTextEdit()
        self.setCentralWidget(textEdit)

        # 窗口
        #self.setGeometry(300,300,250,150)
        self.setWindowTitle(‘原始人‘)
        self.show()

    # 勾选(取消勾选)动作处理
    def toggleMenu(self,state):
        if state:
            self.statusbar.show()
        else:
            self.statusbar.hide()

    # 右键菜单事件
    def contextMenuEvent(self,event):
        #print(‘right‘)
        cmenu = QMenu(self)
        act1 = cmenu.addAction(‘原‘)
        act2 = cmenu.addAction(‘生‘)
        act3 = cmenu.addAction(‘右‘)
        act4 = cmenu.addAction(‘键‘)
        quitAct = cmenu.addAction(‘X‘)
        action = cmenu.exec_(self.mapToGlobal(event.pos()))

        if action==quitAct:
            #print(action,quitAct)
            qApp.quit()
        else:
            self.statusbar.show()
            self.statusbar.showMessage("缘,妙不可言")

if __name__ == ‘__main__‘:
    app = QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

3. 布局管理

不会重绘界面或者其他操作,布局显得略枯燥

import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication, QPushButton, QHBoxLayout, QVBoxLayout, QGridLayout,  QLineEdit, QTextEdit

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        select=‘feedback‘

        if select==‘box‘:
            # 绝对定位
            for i in range(1,20):
                lb1=QLabel(‘(。???)ノ‘,self)
                lb1.move(20*i,15*i)

            # 盒布局
            okButton = QPushButton("ok")
            cancelButton = QPushButton("cancel")

            hbox = QHBoxLayout()
            hbox.addStretch(1)
            hbox.addWidget(okButton)
            hbox.addWidget(cancelButton)

            vbox = QVBoxLayout()
            vbox.addStretch(1)
            vbox.addLayout(hbox)

            self.setLayout(vbox)

            self.setWindowTitle(‘绝对定位与盒布局‘)
        elif select==‘grid‘:
            # 栅格布局
            grid = QGridLayout()
            self.setLayout(grid)
            names = [‘Cls‘, ‘Bck‘, ‘‘, ‘Close‘,
                    ‘7‘, ‘8‘, ‘9‘, ‘/‘,
                    ‘4‘, ‘5‘, ‘6‘, ‘*‘,
                    ‘1‘, ‘2‘, ‘3‘, ‘-‘,
                    ‘0‘, ‘.‘, ‘=‘, ‘+‘]
            positions=[(i,j) for i in range(5) for j in range(4)]

            for position,name in zip(positions,names):
                if name==‘‘:
                    continue
                button = QPushButton(name)
                grid.addWidget(button,*position)
                #print(position,*position)

            self.setWindowTitle(‘栅格布局‘)
        elif select==‘feedback‘:
            title = QLabel(‘Title‘)
            author = QLabel(‘author‘)
            review = QLabel(‘review‘)

            titleEdit = QLineEdit()
            authorEdit = QLineEdit()
            reviewEdit = QTextEdit()

            grid = QGridLayout()
            grid.setSpacing(15)

            grid.addWidget(title,1,0)
            grid.addWidget(titleEdit,1,1)

            grid.addWidget(author,2,0)
            grid.addWidget(authorEdit,2,1)

            grid.addWidget(review,3,0)
            grid.addWidget(reviewEdit,3,1,5,1)

            self.setLayout(grid)

            self.setWindowTitle(‘基于栅格布局的反馈界面‘)
        # 窗口
        self.setGeometry(300,300,500,300)

        self.show()
if __name__==‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

4. 事件和信号

sender是信号的发送者,receiver是信号的接收者,slot是对这个信号应该做出的反应

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QLCDNumber, QSlider, QVBoxLayout, QApplication, QGridLayout, QLabel

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        # 基础
        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal,self)
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addWidget(lcd)
        vbox.addWidget(sld)
        self.setLayout(vbox)
        sld.valueChanged.connect(lcd.display)

        # 添加一个label组件,用来显示(通过鼠标移动事件对象产生的)坐标
        grid = QGridLayout()
        grid.setSpacing(10)
        x=0
        y=0
        self.text="({0},{1})".format(x,y)
        self.label=QLabel(self.text,self)
        grid.addWidget(self.label,0,0,Qt.AlignTop)
        ## 开启鼠标追踪
        self.setMouseTracking(True)
        self.setLayout(grid)

        #窗口
        self.setGeometry(300,300,250,350)
        self.setWindowTitle(‘Signal and slot‘)
        self.show()

    # 重构事件处理器,按键事件
    def keyPressEvent(self,e):
        # print(e.key())
        # wasd 87 65 83 68
        if e.key()==Qt.Key_Escape:
            self.close()

    # 鼠标移动事件
    def mouseMoveEvent(self,e):
        x=e.x()
        y=e.y()
        text="({0},{1})".format(x,y)
        self.label.setText(text)

    def buttonClicked(self):
        sender = self.sender()
        self.statusBar().showMessage(sender.text()+"was pressed")

if __name__==‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication

# 自定义closeApp信号
class Communicate(QObject):
    closeApp = pyqtSignal()

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 事件发送
        btn1 = QPushButton("Button 1",self)
        btn1.move(30,50)
        btn2 = QPushButton("Button 2",self)
        btn2.move(150,50)
        btn1.clicked.connect(self.buttonClicked)
        btn2.clicked.connect(self.buttonClicked)
        ## 显示状态栏
        self.statusBar()

        # 自定义信号发送
        self.c = Communicate()
        self.c.closeApp.connect(self.close)

        #窗口
        self.setGeometry(300,300,250,350)
        self.setWindowTitle(‘Signal and slot‘)
        self.show()

    # slot,事件发送:发送者为button,接收者为状态栏statusBar
    def buttonClicked(self):
        sender = self.sender()
        self.statusBar().showMessage(sender.text()+" was pressed")

    # 发送closeApp信号
    def mousePressEvent(self,event):
        self.c.closeApp.emit()

if __name__==‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

5. 对话框

有意思的一节,简直都可以停下来做音乐播放器了。

注:打开中文文件时错误,加编码就行了 f=open(fname[0],‘r‘,encoding="UTF-8")

文字颜色字体对话框,并联系到一起
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QLineEdit, QInputDialog, QApplication, QFrame, QColorDialog, QHBoxLayout, QSizePolicy, QLabel, QFontDialog
from PyQt5.QtGui import QColor

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 输入文字
        self.btn = QPushButton(‘NAME‘,self)
        self.btn.move(20,20)
        self.btn.clicked.connect(self.showDialog)
        ## 文本框
        self.le=QLineEdit(self)
        self.le.move(130,22)

        # 选取颜色
        col = QColor(0,0,0)
        self.btn=QPushButton(‘Color‘,self)
        self.btn.move(20,60)
        self.btn.clicked.connect(self.showColorWheelDialog)
        ## QFrame
        self.frm = QFrame(self)
        self.frm.setStyleSheet("QWidget{background-color:%s}" % col.name())
        self.frm.setGeometry(130,60,100,100)

        # 选择字体
        btn=QPushButton("Font",self)
        btn.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
        btn.move(20,175)
        btn.clicked.connect(self.showFontDialog)

        self.lbl=QLabel("Remembrance of things past is <br/>not necessarily the remembrance of things as <br/>they were. <br/> --Marcel Proust",self)
        self.lbl.setGeometry(130,160,300,100)

        # 窗口
        self.setGeometry(300,300,500,400)
        self.setWindowTitle(‘对话框‘)
        self.show()

    # 文本对话框
    def showDialog(self):
        text,ok = QInputDialog.getText(self,‘input dialog‘,‘enter your name:‘)
        if ok:
            self.le.setText(str(text))

    # 颜色盘对话框
    def showColorWheelDialog(self):
        col = QColorDialog.getColor()
        if col.isValid():
            # 用 background也可以
            self.frm.setStyleSheet("background:{0}".format(col.name()))
            # 试着给字体加颜色,bingo!
            self.lbl.setStyleSheet("color:{0}".format(col.name()))
            self.le.setStyleSheet("color:{0}".format(col.name()))
    # 字体对话框
    def showFontDialog(self):
        font,ok=QFontDialog.getFont()
        if ok:
            self.lbl.setFont(font)
            self.le.setFont(font)

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

文件对话框
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QFileDialog, QTextEdit
from PyQt5.QtGui import QIcon

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 选择文件
        self.textEdit = QTextEdit() # 文本框
        self.setCentralWidget(self.textEdit) #置中的文本编辑框
        self.statusBar() # 状态栏
        ## 菜单action
        openFileAct = QAction(‘open‘,self)
        openFileAct.setShortcut(‘Ctrl+Q‘)
        openFileAct.setStatusTip("open new File")
        openFileAct.triggered.connect(self.showFileDialog)
        ## 菜单
        menubar = self.menuBar()
        fileMenu = menubar.addMenu("&File")
        fileMenu.addAction(openFileAct)

        # 窗口
        self.setGeometry(300,300,500,400)
        self.setWindowTitle(‘对话框‘)
        self.show()

    # 文件对话框
    def showFileDialog(self):
        fname = QFileDialog.getOpenFileName(self,"open file")
        if fname[0]:
            f=open(fname[0],‘r‘,encoding="UTF-8")

            with f:
                data = f.read()
                self.textEdit.setText(data)

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

6. 控件

这一节应该会比较枯燥的样子。

滑块QSlider的时候

setPixmap放图片的时候,图片没有缩放,很大,限定区域只是截取了一小块。经群里伍兄指点以及C++API(pyqt好多类没有文档啊)先初始化,再用QPixmap scaled(const QSize &size,...)即可。

self.label = QLabel(self)
qpix=QPixmap(sys.path[0]+‘/music2.png‘).scaled(QSize(30,30))
self.label.setPixmap(qpix)
self.label.setGeometry(150,160,30,30)

懒得找喇叭变化的图标,随便用之前找的音乐图标来稍微替代一下。声音为0则反向,无关紧要的。

控件比较多,也按教程小节分两个文件写吧

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QCheckBox, QPushButton, QFrame, QSlider, QLabel, QProgressBar, QHBoxLayout, QCalendarWidget
from PyQt5.QtCore import Qt, QSize, QBasicTimer, QDate
from PyQt5.QtGui import QColor, QPixmap, QIcon

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        # 选择框 QCheckBox
        cb = QCheckBox("show title",self)
        cb.move(20,20)
        #cb.toggle()
        cb.stateChanged.connect(self.changeTitle)

        # 切换按钮
        self.col = QColor(0,0,0)
        redb=QPushButton(‘RED‘,self)
        redb.setCheckable(True)
        redb.move(20,40)
        redb.clicked[bool].connect(self.setColor) # 将点击信号转换为布尔值,并与函数关联起来

        greenb=QPushButton(‘GREEN‘,self)
        greenb.setCheckable(True)
        greenb.move(20,60)
        greenb.clicked[bool].connect(self.setColor)

        blueb=QPushButton(‘BLUE‘,self)
        blueb.setCheckable(True)
        blueb.move(20,80)
        blueb.clicked[bool].connect(self.setColor)

        self.square = QFrame(self)
        self.square.setGeometry(150,20,100,100)
        self.square.setStyleSheet("QFrame{background-color:%s}" % self.col.name())

        # 滑块 QSlider
        sld = QSlider(Qt.Horizontal,self)
        sld.setFocusPolicy(19)
        sld.setGeometry(20,160,100,30)
        sld.valueChanged[int].connect(self.changeSliderValue)

        self.label = QLabel(self)
        qpix=QPixmap(sys.path[0]+‘/music2.png‘).scaled(QSize(30,30))
        self.label.setPixmap(qpix)
        self.label.setGeometry(150,160,30,30)

        self.labelText = QLabel(self)
        self.labelText.setGeometry(190,160,13,10)

        # 进度条
        self.pbar = QProgressBar(self)
        self.pbar.setGeometry(120,200,200,25)

        self.btn = QPushButton(‘Start‘,self)
        self.btn.move(20,200)
        self.btn.clicked.connect(self.doAction)

        self.timer = QBasicTimer()
        self.step=0

        # 日历
        hbox = QHBoxLayout()
        hbox.addStretch(1)

        cal=QCalendarWidget(self)
        cal.setGridVisible(True)
        cal.clicked[QDate].connect(self.showDate)
        hbox.addWidget(cal)

        self.lbl=QLabel(self)
        date=cal.selectedDate()
        self.lbl.setText(date.toString())
        hbox.addWidget(self.lbl)

        self.setLayout(hbox)

        # 窗口
        self.setGeometry(300,300,500,400)
        self.setWindowTitle(‘Control‘)
        self.show()

    # 通过checkbox改变标题
    def changeTitle(self,state):
        if state==Qt.Checked:
            self.setWindowTitle(‘QChekcBox‘)
        else:
            self.setWindowTitle(‘Control‘)

    # 通过button改变颜色
    def setColor(self,preesed):
        source=self.sender() # 找到事件信号发送者

        if preesed:
            val=255
        else:
            val=0

        if source.text() == "RED":
            self.col.setRed(val)
        elif source.text() == ‘GREEN‘:
            self.col.setGreen(val)
        else:
            self.col.setBlue(val)

        self.square.setStyleSheet("QFrame{background-color:%s}" % self.col.name())

    # 通过QSlider滑动改变
    def changeSliderValue(self,value):
        # 置label为value
        self.labelText.setText(str(value))
        if value==0:
            self.label.setPixmap(QPixmap(sys.path[0]+‘/music2.png‘).scaled(QSize(30,30)))
        elif value>0 and value <=30:
            self.label.setPixmap(QPixmap(sys.path[0]+‘/music.png‘).scaled(QSize(30,30)))
        elif value>30 and value <80:
            self.label.setPixmap(QPixmap(sys.path[0]+‘/music.png‘).scaled(QSize(30,30)))
        else:
            self.label.setPixmap(QPixmap(sys.path[0]+‘/music.png‘).scaled(QSize(30,30)))

    # QBasicTimer的start()方法触发的事件,重载
    def timerEvent(self,e):
        print(self.step)
        if self.step>=100:
            self.timer.stop()
            self.btn.setText("Finished")
            return
        self.step=self.step+1
        self.pbar.setValue(self.step)

    # 按钮信号,控制开始/停止
    def doAction(self):
        print(self.timer.isActive())
        if self.timer.isActive():
            self.timer.stop()
            self.btn.setText("Start")
        else:
            self.timer.start(100,self)
            self.btn.setText("Stop")

    # 日期
    def showDate(self,date):
        self.lbl.setText(date.toString())

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QLineEdit, QHBoxLayout, QFrame, QSplitter, QStyleFactory, QComboBox
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # QPixmap 上节已体验过,pass

        # 行编辑
        self.lbl=QLabel(self)
        self.lbl.move(180,40)
        qle=QLineEdit(self)
        qle.move(20,40)
        qle.textChanged[str].connect(self.onChanged)

        # QSplitter 拖拽分割
        hbox = QHBoxLayout()

        topLeft = QFrame(self)
        topLeft.setFrameShape(QFrame.StyledPanel)
        topRight = QFrame(self)
        topRight.setFrameShape(QFrame.StyledPanel)
        bottom = QFrame(self)
        bottom.setFrameShape(QFrame.StyledPanel)

        splitter1 = QSplitter(Qt.Horizontal)
        splitter1.addWidget(topLeft)
        splitter1.addWidget(topRight)

        splitter2 = QSplitter(Qt.Vertical)
        splitter2.addWidget(splitter1)
        splitter2.addWidget(bottom)

        hbox.addWidget(splitter2)
        self.setLayout(hbox)

        # 下拉框
        combo = QComboBox(self)
        for i in range(1,6):
            combo.addItem(str(i))
        combo.move(20,150)

        self.comLb = QLabel("",self)
        self.comLb.move(130,157)

        combo.activated[str].connect(self.onActivated)

        # 窗口
        self.setGeometry(300, 300, 400, 300)
        self.setWindowTitle(‘QLineEdit‘)
        self.show()

    # 行编辑改变
    def onChanged(self,text):
        self.lbl.setText(text)
        self.lbl.adjustSize()

    # 下拉触发
    def onActivated(self,text):
        self.comLb.setText(text)
        self.comLb.adjustSize()

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

7. 拖拽

比较难理解,e.buttons()Qt.RightButton的比较都疑惑了好久。原来是枚举。感谢伍兄。

后面正式使用中肯定会在这卡更久,还需加强

import sys
from PyQt5.QtWidgets import QPushButton, QWidget, QLineEdit, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag

class Button(QPushButton):
    def __init__(self,title,parent):
        super().__init__(title,parent)
        self.setAcceptDrops(True)

    # 拖放到按钮上触发的事件
    def dragEnterEvent(self,e):
        if e.mimeData().hasFormat(‘text/plain‘):
            e.accept()
        else:
            e.ignore()

    # 松开鼠标完成拖放时,把文字设置到button上
    def dropEvent(self,e):
        self.setText(e.mimeData().text())

    # 拖动
    def mouseMoveEvent(self,e):
        print(‘mouseMove‘)
        if e.buttons() != Qt.RightButton:
            return
        mimeData = QMimeData()
        drag=QDrag(self)
        drag.setMimeData(mimeData)
        #sdrag.setHotSpot(e.pos() - self.rect().topLeft())

        dropAction = drag.exec_(Qt.MoveAction)
        drag.exec_()

    # 点击
    def mousePressEvent(self,e):
        print(‘mousePress‘)
        #print(‘press-button ‘,e.button(),e.buttons())
        super().mousePressEvent(e)
        if e.button() == Qt.LeftButton:
            print(‘press‘)

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 简单拖放文字到button组件上
        edit=QLineEdit(‘‘,self)
        edit.setDragEnabled(True) # 文本框选定拖动
        edit.move(20,30)
        button=Button("BUTTON",self)
        button.move(190,30)

        # 拖放button组件
        self.setAcceptDrops(True)
        self.button = Button(‘Button‘,self)

        # 窗口
        self.setWindowTitle("drag")
        self.setGeometry(300,300,300,200)

    def dragEnterEvent(self,e):
        e.accept()

    def dropEvent(self,e):
        position=e.pos()
        self.button.move(position)

        e.setDropAction(Qt.MoveAction)
        e.accept()

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    ex.show()
    sys.exit(app.exec_())

8. 绘图

import sys, random
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QFont, QPen, QBrush, QPainterPath
from PyQt5.QtCore import Qt

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 文本涂鸦
        self.text="何须剑道争锋?\n千人指,万人封,可问江湖鼎峰;\n三尺秋水尘不染,天下无双。"

        # 窗口
        self.setGeometry(300,200,400,450)
        self.setWindowTitle(‘Drawing‘)
        self.show()

    def paintEvent(self,event):
        qp=QPainter()
        qp.begin(self)

        # 文本
        self.drawTextStr(event,qp)
        # 点
        self.drawPointers(qp)
        # color 矩形
        self.drawRectangles(qp)
        # Qpen画笔,用来画直线曲线椭圆等形状(形状没有试)
        self.drawLines(qp)
        # QBrush 画刷
        self.drawBrushes(qp)
        # 贝塞尔曲线
        self.drawBezierCurve(qp)

        qp.end()

    def drawTextStr(self,event,qp):
        qp.setPen(QColor(68,24,3))
        qp.setFont(QFont(‘Microsoft Yahei‘,19))
        qp.drawText(event.rect(),Qt.AlignCenter,self.text)

    def drawPointers(self,qp):
        qp.setPen(Qt.red)
        size=self.size()
        for i in range(3000):
            # 只绘制点到左上角
            x=random.randint(1,size.width()//2)
            y=random.randint(1,size.height()//2)
            qp.drawPoint(x,y)

    def drawRectangles(self,qp):
        col=QColor()
        col.setNamedColor(‘#00f‘)
        qp.setPen(col)

        qp.setBrush(QColor(200, 0, 0))
        qp.drawRect(10, 15, 90, 60)

        qp.setBrush(QColor(0, 200, 0, 150))
        qp.drawRect(130, 15, 90, 60)

        qp.setBrush(QColor(0, 0, 200, 100))
        qp.drawRect(250, 15, 90, 60)

    def drawLines(self,qp):
        pen = QPen(Qt.gray, 3, Qt.SolidLine)

        qp.setPen(pen)
        qp.drawLine(20, 300, 400, 0)

        pen.setStyle(Qt.DashLine)
        qp.setPen(pen)
        qp.drawLine(20, 320, 400, 0)

        pen.setStyle(Qt.DashDotLine)
        qp.setPen(pen)
        qp.drawLine(20, 340, 400, 0)

        pen.setStyle(Qt.DotLine)
        qp.setPen(pen)
        qp.drawLine(20, 360, 400, 40)

        pen.setStyle(Qt.DashDotDotLine)
        qp.setPen(pen)
        qp.drawLine(20, 380, 400, 40)

        pen.setStyle(Qt.CustomDashLine)
        pen.setDashPattern([1, 4, 5, 4])
        qp.setPen(pen)
        qp.drawLine(20, 400, 400, 40)

    def drawBrushes(self,qp):
        # 上面的Pen会影响这里的边框
        brush = QBrush(Qt.Dense1Pattern)
        qp.setBrush(brush)
        qp.drawRect(20,200,40,90)

        qp.setPen(QPen(Qt.red, 2, Qt.SolidLine)) # 重新设置Pen
        brush.setStyle(Qt.Dense6Pattern)
        qp.setBrush(brush)
        qp.drawRect(70,200,40,90)

    def drawBezierCurve(self,qp):
        path=QPainterPath()
        path.moveTo(120,200)
        path.cubicTo(120,200,200,350,350,120)
        path.cubicTo(350,120,200,400,300,400)
        path.cubicTo(300,400,200,400,120,200)
        qp.drawPath(path)

if __name__ == ‘__main__‘:
    app=QApplication(sys.argv)
    ex=Example()
    sys.exit(app.exec_())

比较耗费内存,还是低级绘图呢,图中这么多,都吃10M内存了

9. 自定义控件

这一小节没有敲多少,只写了一点注释

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QSlider, QApplication,                             QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import QObject, Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QFont, QColor, QPen

class Communicate(QObject):
    ‘‘‘
        自定义updateBW信号
    ‘‘‘
    updateBW =pyqtSignal(int)

class BurningWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 设置控件最小的宽度和高度
        self.setMinimumSize(1,30)
        self.value=75
        # 刻度值
        self.num=[75, 150, 225, 300, 375, 450, 525, 600, 675]

    def setValue(self,value):
        self.value=value

    def paintEvent(self,e):
        ‘‘‘
        更新窗口
        每次更改窗口大小,切换窗口,都会触发;单纯移动不会出触发;
        移动里面Slider也不会主动触发,所以Example.changeValue()里主动调用控件的repaint或者update,以重新绘制
        ‘‘‘
        #print("--update show --")
        qp=QPainter()
        qp.begin(self)
        self.drawWidget(qp)
        qp.end()

    def drawWidget(self,qp):
        ‘‘‘
            自定义控件主要的核心绘制
        ‘‘‘
        #(700,750)区间为烧毁范围
        MAX_CAPACITY=700
        OVER_CAPACITY=750

        font = QFont(‘Serif‘,7,QFont.Light)
        qp.setFont(font)

        # 当前窗口大小,step为宽度的1/10,刻度值self.num分割这十份宽
        size=self.size()
        w=size.width()
        h=size.height()
        step=int(round(w/10))
        #print("step:",step)

        # till当前值对应的控件渲染宽度,full正常烧录能达到的最大宽
        till = int(((w / OVER_CAPACITY) * self.value))
        full = int(((w / OVER_CAPACITY) * MAX_CAPACITY))
        #print(self.value,till,full)
        if self.value >= MAX_CAPACITY:
            # 当值大于最大MAX,则将MAX部分的全部渲染(宽度为full)
            qp.setPen(QColor(255, 255, 255))
            qp.setBrush(QColor(255, 255, 184))
            qp.drawRect(0, 0, full, h)
            # 其他部分,为烧毁的部分,用另一个颜色渲染
            qp.setPen(QColor(255, 175, 175))
            qp.setBrush(QColor(255, 175, 175))
            qp.drawRect(full, 0, till-full, h)
        else:
            # 正常烧录
            qp.setPen(QColor(255, 255, 255))
            qp.setBrush(QColor(255, 255, 184))
            qp.drawRect(0, 0, till, h)

        # 画笔设置,画刷无
        pen = QPen(Qt.black, 1, Qt.SolidLine)
        qp.setPen(pen)
        qp.setBrush(Qt.NoBrush)
        # 外层边框
        qp.drawRect(0, 0, w-1, h-1)

        # 刻度的绘制
        j = 0
        for i in range(step, 10*step, step):

            # 刻度竖线
            qp.drawLine(i, 0, i, 5)

            # 这里使用字体去渲染文本。必须要知道文本的宽度,这样才能让文本的中间点正好落在竖线上。
            metrics = qp.fontMetrics()
            fw = metrics.width(str(self.num[j]))
            qp.drawText(i-fw/2, h/2, str(self.num[j]))
            # 使用重载函数 void QPainter::drawText(int x, int y, const QString &text) 位置(x,y)处绘制text
            # i为竖线位置,fw为字体宽度(fw/2为字体中间位置),i-fw/2则把数字的左半长度移动到了刻度左边,居中!

            j = j + 1

class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):      

        OVER_CAPACITY = 750

        # 放置上侧滑块
        sld = QSlider(Qt.Horizontal, self)
        #sld.setFocusPolicy(Qt.NoFocus)
        sld.setRange(1, OVER_CAPACITY)
        sld.setValue(75)
        sld.setGeometry(30, 40, 150, 30)

        # 创建信号和控件
        self.c = Communicate()
        self.wid = BurningWidget()

        # 将滑块改变与self.changeValue方法绑定,当滑块滑动,执行changValue方法,在方法中,手动调用信号更新,所以下面的信号也会更新
        sld.valueChanged[int].connect(self.changeValue)
        # 将信号更新与控件控件setValue方法绑定,当信号更新时,控件更新
        self.c.updateBW[int].connect(self.wid.setValue)

        # 水平布局,自定义的wid控件将显示在底部
        hbox = QHBoxLayout()
        hbox.addWidget(self.wid)
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        self.setLayout(vbox)

        # 设置窗口
        self.setGeometry(300, 300, 390, 210)
        self.setWindowTitle(‘Burning widget‘)
        self.show()

    def changeValue(self, value):
        # 发送updateBW信号
        self.c.updateBW.emit(value)     

        # 重绘窗口,repaint update均可
        #self.wid.repaint()
        self.wid.update()

if __name__ == ‘__main__‘:

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

好啦,结束。算是懂了一点界面知识,开森。最后一节的俄罗斯方块之后再试

github

参考资料,致谢

原文地址:https://www.cnblogs.com/warcraft/p/9439417.html

时间: 2024-10-16 16:43:37

PyQt5 入门的相关文章

【Django】ORM数据库操作

Django-ORM数据库操作 映射关系: 表名 -------------------->类名 字段-------------------->属性 表记录----------------->类实例化对象 ORM的两大功能: 操作表: - 创建表 - 修改表 - 删除表 操作数据行: - 增 删 改 查 ORM利用pymysql第三方工具链接数据库 Django默认的是sqlite数据库 将Django数据库修改为mysql: 1.settings.py 配置 DATABASES = {

PyQt5快速入门(一)PyQt5简介

PyQt5快速入门(一)PyQt5简介 一.PyQt5简介 1.PyQt5简介 PyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一.PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏.官方网站:www.riverbankcomputing.comPyQt5提供GPL版和商业版证书,自由开发者可以使用免费的G

PyQt5快速入门(二)PyQt5信号槽机制

PyQt5快速入门(二)PyQt5信号槽机制 一.信号槽机制简介 1.信号槽简介 信号槽是Qt的核心机制,也是PyQt编程中对象进行通信的机制.在Qt中,QObject对象和PyQt中所有继承自QWidget的控件都支持信号槽机制.当信号发射时,连接的槽函数会自动执行.在PyQt5中,信号与槽函数通过object.signal.connect()方法进行连接.信号槽特点如下:(1)一个信号可以连接多个槽(2)一个信号可以连接另一个信号(3)信号参数可以是任意Python类型(4)一个槽可以监听多

PyQt5快速入门

PyQt5快速入门系列博客根据<PyQt5快速开发与实战>学习而来,请参考原书.PyQt5快速入门(一)PyQt5简介https://blog.51cto.com/9291927/2422184PyQt5快速入门(二)PyQt5信号槽机制https://blog.51cto.com/9291927/2422187 原文地址:https://blog.51cto.com/9291927/2422188

PyQt5快速入门(三)PyQt5基本窗口组件

PyQt5快速入门(三)PyQt5基本窗口组件 一.QMainWindow 1.窗口类型简介 QMainWindow.QWidget.QDialog用于创建窗口,可以直接使用,也可以派生使用.QMainWindow窗口包含菜单栏.工具栏.状态栏.标题栏等,是最常见的窗口形式.QDialog是对话框窗口的基类,主要用于执行短期任务,或与用户进行交互,可以是模态或非模态的.QDialog对话框没有菜单栏.工具栏.状态栏等.QWidget是Qt图形组件的基类,可以作为顶层窗口,也可以嵌入到其它组件中.

PyQt5快速入门(四)PyQt5高级窗口组件

PyQt5快速入门(四)PyQt5高级窗口组件 一.QTableView 1.QTableView简介 QTableView可以使用自定义的数据模型来显示内容,通过setModel绑定数据源,由QAbstractItemView类定义的接口来实现,使其能够显示由QAbstractItemModel类派生的模型提供的数据. 2.标准模型 QStringListModel 字符串链表数据模型QStandardItemModel标准数据项模型,存储任意结构层次的数据QDirModel 文件系统目录模型

PyQt5快速入门(七)PyQt5扩展

PyQt5快速入门(七)PyQt5扩展 一.PyQt5项目发布 1.PyInstaller简介 PyInstaller是一款免费易用的打包工具,支持Windows.Linux.MacOS,并且支持32位和64位系统.http://www.pyinstaller.org/PyInstaller安装:pip install pyinstaller 2.PyInstaller使用 PyInstaller使用命令如下:pyinstaller yourprogram.pyPyInstaller使用时需切换

[ PyQt入门教程 ] PyQt5中多线程模块QThread使用方法

本文主要讲解使用多线程模块QThread解决PyQt界面程序唉执行耗时操作时,程序卡顿出现的无响应以及界面输出无法实时显示的问题.用户使用工具过程中出现这些问题时会误以为程序出错,从而把程序关闭.这样,导致工具的用户使用体验不好.下面我们通过模拟上述出现的问题并讲述使用多线程QThread模块解决此类问题的方法. PyQt程序卡顿和无法实时显示问题现象 使用PyQt界面程序,点击运行按钮后,程序在显示框中每秒打印1个数字.程序代码如下: # -*- coding: utf-8 -*- impor

pyQt5新手教程 (一)通过anaconda安装pyqt包

为什么要学pyqt 你想不想开发一个基于桌面的GUI程序,让你的同事和家人刮目相看,你总是看完这个看那个,却始终不得入门,来这里吧!很多教程不适合你,难度太难,没人教,来这里吧! 为什么不是tkinter,当你下载了基于python的tkinter,写一个小程序的时候你就知道为什么要用了,比如下面这个几乎什么都没有的框子,要花30多行,让人写的云里雾里的 那和pyqt有什么关系? 因为python很好学,我需要找和python相关的GUI编程ide 怎么学pyqt,跟这我学呗!!  还有人问为什