还记得我之前说过布局什么的用Qt Designer就行了,还强力推荐过。用了一段时间后,现在想来,Qt Designer就是个垃圾。布局什么的,还是使用代码进行管理吧,其实也不难。而且也不用分两个文件,一个写QWidget
,另一个写QMainWindow
了。在QMainWindow
中创建一个QWidget
的实例,并且加入即可。
本文收录:
如何美化布局,使界面更优美
StackLayout布局管理,模仿常用软件功能,使界面更人性化
Qspliter,像一个IDE一个自由拖动
综合实例,实际开发一个软件,用到以上功能。
软件主体功能:调用天气网的API,爬取数据,将数据放到软件的QTableWidget
中,预测未来天气,对天气走向进行绘图pyqtgraph
。功能简易,但用到了Qt开发中的大部分控件和常见功能。
软件结果如下图所示:
代码如下:
本文全部代码下载链接,建议在这里下载代码,因为需要辅助文件
天气API参考:https://www.sojson.com/api/weather.html
高级布局管理 QHBoxLayout
, QVBoxLayout
, QGridLayout
, 这几个基本的布局管理的入门教程如下,也是我目前看到过的最好的:https://www.learnpyqt.com/courses/start/layouts/
此外,还有网上盛传的布局美化代码,网上清一色是这个: 虽然也不知道谁是原创,只要一搜索:PyQt5美化开发
,全部都是这个代码。
我知道写的美它好,但我不想抄袭。不过从以上网址的代码确实学到了点东西,那就是设立总体布局和子布局,子布局管理控件,总体布局管理子布局,这样布置出来的界面是很好看的。
接下来,一点一点的写出来我上面那个天气预报的软件。
需要导入的库如下:
1 2 3 4 5 6 7 8 9 10 11 from PyQt5 import QtWidgetsfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QDesktopWidget, QStyleFactory, QWidget, QGridLayout, QHeaderView, QTableWidgetItem, QMessageBox, QFileDialog, QSlider, QLabel, QLineEdit, QPushButton, QTableWidget) from PyQt5.QtGui import QPalette, QColor, QBrushfrom PyQt5.QtCore import Qtfrom pyqtgraph import GraphicsLayoutWidgetimport pyqtgraph as pg import numpy as npimport pyqtgraph.exporters as peimport qdarkstyle, requests, sys, time, random, json, datetime, re
整体布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 class MainUi (QMainWindow ): def __init__ (self ): super ().__init__() pg.setConfigOption('background' , '#19232D' ) pg.setConfigOption('foreground' , 'd' ) pg.setConfigOptions(antialias = True ) self.center() self.init_ui() self.code = "" self.num = 5 self.rep = "" self.plt = [] self.filename = 1 self.status = self.statusBar() self.status.showMessage("我在主页面~" ) self.setWindowTitle("天气查询软件" ) def init_ui (self ): self.main_widget = QWidget() self.main_layout = QGridLayout() self.main_widget.setLayout(self.main_layout) self.left_widget = QWidget() self.left_widget.setObjectName('left_widget' ) self.left_layout = QGridLayout() self.left_widget.setLayout(self.left_layout) self.right_widget = QWidget() self.right_widget.setObjectName('right_widget' ) self.right_layout = QGridLayout() self.right_widget.setLayout(self.right_layout) self.main_layout.addWidget(self.left_widget, 0 , 0 , 12 , 5 ) self.main_layout.addWidget(self.right_widget, 0 , 5 , 12 , 7 ) self.setCentralWidget(self.main_widget) def center (self ): ''' 获取桌面长宽 获取窗口长宽 移动 ''' screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2 , (screen.height() - size.height()) / 2 ) def main (): app = QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_()) if __name__ == '__main__' : main()
现在只能执行出一个小窗口来,没关系,继续下面的东西。
左侧添加控件 这个时候我们再来添加左边的按钮控件。代码缩进添加至def init_ui(self):
中。
提醒:在布局的时候,button
在clicked connect
的时候找不到逻辑函数报错,加入逻辑函数就不会错了。逻辑函数在之后会加入。所以,现在只看布局,就别点按钮以至于报错了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 self.single_query = QPushButton("查询今日" ) self.single_query.clicked.connect(self.request_weather) self.single_query.setEnabled(False ) self.btn_tempa = QPushButton("温度预测(可绘图)" ) self.btn_tempa.clicked.connect(self.request_weather) self.btn_tempa.setEnabled(False ) self.btn_wind = QPushButton("风力预测(可绘图)" ) self.btn_wind.clicked.connect(self.request_weather) self.btn_wind.setEnabled(False ) self.btn_stawea = QPushButton("综合天气预测" ) self.btn_stawea.clicked.connect(self.request_weather) self.btn_stawea.setEnabled(False ) self.left_layout.addWidget(self.single_query, 2 , 0 , 1 , 5 ) self.left_layout.addWidget(self.btn_tempa, 3 , 0 , 1 , 5 ) self.left_layout.addWidget(self.btn_wind, 4 , 0 , 1 , 5 ) self.left_layout.addWidget(self.btn_stawea, 5 , 0 , 1 , 5 ) self.city_line = QLineEdit() self.city_line.setPlaceholderText("输入城市回车确认" ) self.city_line.returnPressed.connect(self.match_city) self.left_layout.addWidget(self.city_line, 1 , 0 , 1 , 5 ) self.save_fig = QPushButton("保存绘图" ) self.save_fig.setEnabled(False ) self.save_fig.clicked.connect(self.fig_save) self.left_layout.addWidget(self.save_fig, 6 , 0 , 1 , 5 ) self.load = QPushButton("写日记" ) self.left_layout.addWidget(self.load, 7 , 0 , 1 , 5 ) self.quit_btn = QPushButton("退出" ) self.quit_btn.clicked.connect(self.quit_act) self.left_layout.addWidget(self.quit_btn, 8 , 0 , 1 , 5 )
添加完按钮控件后,加入QtableWidget
控件用于观察数据。
1 2 3 4 self.query_result = QTableWidget() self.left_layout.addWidget(self.query_result, 9 , 0 , 2 , 5 ) self.query_result.verticalHeader().setVisible(False )
右侧窗口布局 最后完成右侧窗口的布局即可。
1 2 3 4 5 6 7 self.label = QLabel("预测天气情况绘图展示区" ) self.right_layout.addWidget(self.label, 0 , 6 , 1 , 7 ) self.plot_weather_wind = GraphicsLayoutWidget() self.plot_weather_temp = GraphicsLayoutWidget() self.right_layout.addWidget(self.plot_weather_temp, 1 , 6 , 4 , 7 ) self.right_layout.addWidget(self.plot_weather_wind, 6 , 6 , 4 , 7 )
整体美化 1 2 3 4 self.setWindowOpacity(0.9 ) self.main_layout.setSpacing(0 ) self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
逻辑函数 此外,还需要单独引入import read_citycode, get_weather
,这两个单独成两个文件,和主文件放在一起。
read_citycode
以字典的形式返回每个城市对应的ID。json
文件在上面提供的压缩包内,上面提到的天气API网址内也有。
有的逻辑函数需要引入外部文件,否则会报错。因此,简易使用上文提供的代码下载链接,下载代码执行。直接copy这里的代码是不能执行的,因为需要外部文件的支持。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import jsondef read_code (filename ): city_ = {} with open (filename, 'r' ) as f: temp = json.loads(f.read()) for city in temp: if 'city_code' in city: key = city['city_name' ] value = city['city_code' ] city_[key] = value sorted (city_.keys()) return city_
get_weather
是对网址发送具体城市的天气请求,获取对应的结果,结果形式为json
。
1 2 3 4 5 6 7 8 9 import requestsdef run (url ): try : rep = requests.get(url) rep.encoding = 'utf-8' return rep except : print ("network error" )
以上两份代码分别整理到两个文件中,命名为read_citycode.py
和get_weather.py
。文件结构图如下:
布局完成之后,开始增加功能,所以我把所有的逻辑函数放上来。因为之前布局的时候,button
在clicked connect
的时候找不到逻辑函数报错,加入逻辑函数就不会错了。
退出窗口函数: 1 2 3 4 5 6 7 def quit_act (self ): sender = self.sender() print (sender.text() + '键被按下' ) qApp = QApplication.instance() qApp.quit()
窗口居中函数: 1 2 3 4 5 6 7 8 9 10 def center (self ): ''' 获取桌面长宽 获取窗口长宽 移动 ''' screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2 , (screen.height() - size.height()) / 2 )
获取城市ID函数 根据json文件,将获得的输入城市转换为ID,完成进一步查询。因为API的url只能传入ID,不可传入城市名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def match_city (self ): self.btn_tempa.setEnabled(True ) self.btn_wind.setEnabled(True ) self.single_query.setEnabled(True ) self.btn_stawea.setEnabled(True ) city = read_citycode.read_code("最新_city.json" ) line_city = self.city_line.text() if line_city in city.keys(): self.code = city[line_city] else : self.code = "101010100" self.city_line.setText("北京" ) Qreply = QMessageBox.about(self, "你犯了一个粗误" , "输入城市无效,请示新输入,否则默认为北京" )
请求天气 在获得城市的ID之后,便能生成url,请求API返回天气。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def request_weather (self ): root_url = "http://t.weather.sojson.com/api/weather/city/" url = root_url + str (self.code) self.rep = get_weather.run(url) sender = self.sender() if sender.text() == "查询今日" : self.query(1 , 5 , '温度' , '风向' , '风力' , 'PM2.5' , '天气描述' ) if sender.text() == "温度预测(可绘图)" : self.btn_tempa.setEnabled(False ) self.query(self.num, 2 , '日期' , '温度' ) if sender.text() == "风力预测(可绘图)" : self.btn_wind.setEnabled(False ) self.query(self.num, 2 , '日期' , '风力' ) if sender.text() == "综合天气预测" : self.query(self.num, 4 , '温度' , '风向' , '风力' , '天气描述' )
开始查询 在得到请求城市的天气后,开始按照查询的需求输出结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 def query (self, row_num, col_num, *args ): tempature = self.rep.json()['data' ]['wendu' ] wind_power = self.rep.json()['data' ]['forecast' ][0 ]['fl' ] wind_direction = self.rep.json()['data' ]['forecast' ][0 ]['fx' ] pm = self.rep.json()['data' ]['pm25' ] type_ = self.rep.json()['data' ]['forecast' ][0 ]['type' ] pre_tempature = [] pre_wind_power = [] pre_wind_direction = [] pre_pm = [] pre_type_ = [] for i in range (self.num): pre_tempature.append(str (self.rep.json()['data' ]['forecast' ][i]['low' ])) pre_wind_power.append(str (self.rep.json()['data' ]['forecast' ][i]['fl' ])) pre_wind_direction.append(str (self.rep.json()['data' ]['forecast' ][i]['fx' ])) pre_type_.append(str (self.rep.json()['data' ]['forecast' ][i]['type' ])) self.query_result.setRowCount(row_num) self.query_result.setColumnCount(col_num) self.query_result.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) ls = [i for i in args] self.query_result.setHorizontalHeaderLabels(ls) if col_num > 2 and row_num == 1 : item = QTableWidgetItem(str (tempature) + "℃" ) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(0 , 0 , item) item = QTableWidgetItem(str (wind_direction)) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(0 , 1 , item) item = QTableWidgetItem(str (wind_power)) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(0 , 2 , item) item = QTableWidgetItem(str (pm)) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(0 , 3 , item) item = QTableWidgetItem(str (type_)) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(0 , 4 , item) if col_num > 2 and row_num > 1 : for i in range (0 , self.num): item = QTableWidgetItem("最" + str (pre_tempature[i])) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(i, 0 , item) item = QTableWidgetItem(str (pre_wind_direction[i])) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(i, 1 , item) item = QTableWidgetItem(str (pre_wind_power[i])) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(i, 2 , item) item = QTableWidgetItem(str (pre_type_[i])) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(i, 3 , item) if col_num == 2 and row_num > 1 : date = self.get_date(addDays=self.num) key = 0 for i in date: item = QTableWidgetItem(i) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(key, 0 , item) key += 1 if self.query_result.horizontalHeaderItem(1 ).text() == '温度' : key = 0 for i in pre_tempature: item = QTableWidgetItem("最" + i) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(key, 1 , item) key += 1 if self.query_result.horizontalHeaderItem(1 ).text() == '风力' : key = 0 for i in pre_wind_power: item = QTableWidgetItem(i) item.setForeground(QBrush(QColor(144 , 182 , 240 ))) self.query_result.setItem(key, 1 , item) key += 1 if col_num < 4 : ls, y = [], [] x = np.linspace(1 , self.num, self.num) for row in range (self.num): str1 = str (self.query_result.item(row, 1 ).text()) ls.extend(re.findall(r'\d+(?:\.\d+)?' , str1)) if len (ls) == 5 : y = [float (i) for i in ls] if len (ls) == 10 : lt = [float (i) for i in ls] for i in range (0 , len (lt), 2 ): y.append((lt[i] + lt[i + 1 ]) / 2 ) if len (ls) == 5 : y = [float (i) for i in ls] else : y = [float (i) for i in ls[0 :5 ]] if self.query_result.horizontalHeaderItem(1 ).text() == '温度' : title_ = "近期一个月温度变化(预测)" if self.query_result.horizontalHeaderItem(1 ).text() == '风力' : title_ = "近期一个月风力变化(预测)" if title_ == "近期一个月风力变化(预测)" : self.plot_weather_wind.clear() bg1 = pg.BarGraphItem(x=x, height=y, width=0.3 , brush=QColor(137 , 232 , 165 )) self.plt1 = self.plot_weather_wind.addPlot(title = title_) self.plt1.addItem(bg1) if title_ == "近期一个月温度变化(预测)" : self.plot_weather_temp.clear() self.plt = self.plot_weather_temp.addPlot(title = title_) bg2 = pg.BarGraphItem(x=x, height=y, width=0.3 , brush=QColor(32 , 235 , 233 )) self.plt.addItem(bg2) self.save_fig.setEnabled(True )
保存图片与显示日期 逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def pic_messagebox (self ): string = '第' + str (self.filename) + '张图片.png' Qreply = QMessageBox.information(self, string, "已经成功保存图片到当前目录, 关闭软件后请及时拷贝走" ) def fig_save (self ): ex = pe.ImageExporter(self.plt.scene()) filename = '第' + str (self.filename) + '张图片.png' self.filename += 1 ex.export(fileName = filename) self.pic_messagebox() def get_date (self, dateFormat="%Y-%m-%d" , addDays=0 ): ls = [] timeNow = datetime.datetime.now() key = 0 if (addDays != 0 ) and key < addDays - 1 : for i in range (addDays): anotherTime = timeNow + datetime.timedelta(days = key) anotherTime.strftime(dateFormat) ls.append(str (anotherTime)[0 :10 ]) key += 1 else : anotherTime = timeNow return ls
最终结果如下所示,以上代码见开头下载链接的weather.py
文件。
以上,我们见识到了层次布局管理后,软件的界面效果是可以接受的。
那么问题来了,我想要像某个IDE或者编辑器,比如Pycharm或者Vscode一样,主界面下的框能拖动自由改变大小。接下来实现这个。
QSplitter 布局 这个实现比较容易,需要实例化对象,加入frame,在添加至主布局中即可。参考:http://zetcode.com/gui/pyqt5/widgets2/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QFrame, QSplitter, QStyleFactory, QApplication) from PyQt5.QtCore import Qtimport sysclass Example (QWidget ): def __init__ (self ): super ().__init__() self.initUI() def initUI (self ): hbox = QHBoxLayout(self) 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) self.setGeometry(300 , 300 , 300 , 200 ) self.setWindowTitle('QSplitter' ) self.show() if __name__ == '__main__' : app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_())
之后,在frame里面添加控件,便可以随心所欲的玩耍。
实例 代码见本文开头给出的下载链接中的weather1.py
。
StackLayout布局 脑部网易云音乐的界面,是不是点击左侧的按钮后,右侧的窗口也会跟着改变。而实现这个技术,就需要StackLayout布局的管理。
一个Demo, stacklayout
添加完控件后,会自动设立索引,只需要按照索引访问对应项即可,以下代码实现了最简单的界面切换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 from PyQt5 import QtWidgetsimport sysfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QDesktopWidget, QStyleFactory, QWidget, QGridLayout, QHeaderView, QTableWidgetItem, QMessageBox, QFileDialog, QStackedLayout, QFrame, QLabel, QLineEdit, QPushButton, QTableWidget, QVBoxLayout, QHBoxLayout, QSplitter) from PyQt5.QtGui import QPalette, QColor, QBrushclass Color (QWidget ): def __init__ (self, color, *args, **kwargs ): super (Color, self).__init__(*args, **kwargs) self.setAutoFillBackground(True ) palette = self.palette() palette.setColor(QPalette.Window, QColor(color)) self.setPalette(palette) class MainWindow (QMainWindow ): def __init__ (self, *args, **kwargs ): super (MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("My Awesome App" ) pagelayout = QVBoxLayout() button_layout = QHBoxLayout() layout = QStackedLayout() pagelayout.addLayout(button_layout) pagelayout.addLayout(layout) for n, color in enumerate (['red' ,'green' ,'blue' ,'yellow' ]): btn = QPushButton( str (color) ) btn.pressed.connect( lambda n=n: layout.setCurrentIndex(n) ) button_layout.addWidget(btn) layout.addWidget(Color(color)) widget = QWidget() widget.setLayout(pagelayout) self.setCentralWidget(widget) def main (): app = QApplication(sys.argv) gui = MainWindow() gui.show() sys.exit(app.exec_()) if __name__ == '__main__' : main()
实例 那么反过来思考以下,如果一个软件要综合Stacklayout,splitter等,要怎么实现呢。以下给一个例子,综合以上所有的内容,实现更加综合的布局管理。(以下代码copy后可以单独运行,不需要外部文件,也没有在上文的下载链接中给出)
坑 1:整体界面resize前,先要设置splitter的大小,否则无效。 坑 2:TreeView和QWebEngineView不能放在同一个layout中,否则QWebEngineView会不显示。具体为啥,我也不知道。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 import sys, hashlibimport qdarkstylefrom PyQt5.QtWidgets import (QApplication, QSplitter, QGridLayout, QHBoxLayout, QPushButton, QTreeWidget, QFrame, QLabel, QHBoxLayout, QMainWindow, QStackedLayout, QWidget, QVBoxLayout, QLineEdit, QRadioButton, QTreeWidgetItem, QDesktopWidget) from PyQt5.QtCore import Qt, QUrlfrom PyQt5.QtWebEngineWidgets import QWebEngineViewclass MainWindow (QMainWindow ): def __init__ (self, *args, **kwargs ): super (MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("华北理工数学建模协会比赛查询" ) self.status = self.statusBar() self.status.showMessage("我在主页面~" ) self.resize(600 , 400 ) self.center() self.setWindowOpacity(0.9 ) self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) pagelayout = QGridLayout() top_left_frame = QFrame(self) top_left_frame.setFrameShape(QFrame.StyledPanel) button_layout = QVBoxLayout(top_left_frame) verifyid_btn = QPushButton(top_left_frame) verifyid_btn.setFixedSize(100 , 30 ), verifyid_btn.setText("确认身份" ) button_layout.addWidget(verifyid_btn) user_btn = QPushButton(top_left_frame) user_btn.setFixedSize(100 , 30 ), user_btn.setText("登录" ) button_layout.addWidget(user_btn) registor_btn = QPushButton(top_left_frame) registor_btn.setFixedSize(100 , 30 ), registor_btn.setText("申请帐号" ) button_layout.addWidget(registor_btn) input_btn = QPushButton(top_left_frame) input_btn.setFixedSize(100 , 30 ), input_btn.setText("录入信息" ) button_layout.addWidget(input_btn) query_btn = QPushButton(top_left_frame) query_btn.setFixedSize(100 , 30 ), query_btn.setText("查询信息" ) button_layout.addWidget(query_btn) friend_btn = QPushButton(top_left_frame) friend_btn.setFixedSize(100 , 30 ), friend_btn.setText("建模园地" ) button_layout.addWidget(friend_btn) quit_btn = QPushButton(top_left_frame) quit_btn.setFixedSize(100 , 30 ), quit_btn.setText("退出" ) button_layout.addWidget(quit_btn) bottom_left_frame = QFrame(self) blank_label = QLabel(bottom_left_frame) blank_layout = QVBoxLayout(bottom_left_frame) blank_label.setText("建模学子的博客" ) blank_label.setFixedHeight(20 ) blank_layout.addWidget(blank_label) self.webEngineView = QWebEngineView(bottom_left_frame) self.webEngineView.close() blank_layout.addWidget(self.webEngineView) right_frame = QFrame(self) right_frame.setFrameShape(QFrame.StyledPanel) self.right_layout = QStackedLayout(right_frame) radio_btn_admin = QRadioButton(right_frame) radio_btn_admin.setText("我是管理员,来输入数据的" ) radio_btn_user = QRadioButton(right_frame) radio_btn_user.setText("我是游客,就来看看" ) radio_btn_layout = QVBoxLayout() radio_btn_widget = QWidget(right_frame) radio_btn_layout.addWidget(radio_btn_admin) radio_btn_layout.addWidget(radio_btn_user) radio_btn_widget.setLayout(radio_btn_layout) self.right_layout.addWidget(radio_btn_widget) user_line = QLineEdit(right_frame) user_line.setPlaceholderText("输入账号:" ) user_line.setFixedWidth(400 ) password_line = QLineEdit(right_frame) password_line.setPlaceholderText("请输入密码:" ) password_line.setFixedWidth(400 ) login_layout = QVBoxLayout() login_widget = QWidget(right_frame) login_widget.setLayout(login_layout) login_layout.addWidget(user_line) login_layout.addWidget(password_line) self.right_layout.addWidget(login_widget) registor_id = QLineEdit(right_frame) registor_id.setPlaceholderText("请输入新帐号:" ) registor_id.setFixedWidth(400 ) registor_psd = QLineEdit(right_frame) registor_psd.setPlaceholderText("请输入密码:" ) registor_psd.setFixedWidth(400 ) registor_confirm = QLineEdit(right_frame) registor_confirm.setPlaceholderText("请确认密码:" ) registor_confirm.setFixedWidth(400 ) registor_confirm_btn = QPushButton("确认提交" ) registor_confirm_btn.setFixedSize(100 , 30 ) registor_layout = QVBoxLayout() register_widget = QWidget(right_frame) register_widget.setLayout(registor_layout) registor_layout.addWidget(registor_id) registor_layout.addWidget(registor_psd) registor_layout.addWidget(registor_confirm) registor_layout.addWidget(registor_confirm_btn) self.right_layout.addWidget(register_widget) self.friend_tree = QTreeWidget(right_frame) self.friend_tree.setColumnCount(3 ) self.friend_tree.setHeaderLabels(['年级' , '人员' , '友情链接' ]) root = QTreeWidgetItem(self.friend_tree) self.friend_tree.setColumnWidth(2 , 400 ) root.setText(0 , "年级" ) root.setText(1 , "姓名" ) root.setText(2 , "网址" ) child_16 = QTreeWidgetItem(root) child_16.setText(0 , "16级" ) child_ljw = QTreeWidgetItem(child_16) child_ljw.setText(1 , "刘佳玮" ) child_ljw.setText(2 , "https://muyuuuu.github.io" ) child_17 = QTreeWidgetItem(root) child_17.setText(0 , "17级" ) child_lqr = QTreeWidgetItem(child_17) child_lqr.setText(1 , "李秋然" ) child_lqr.setText(2 , "https://dgimoyeran.github.io" ) friend_widget = QWidget(right_frame) friend_layout = QVBoxLayout() friend_widget.setLayout(friend_layout) friend_layout.addWidget(self.friend_tree) self.right_layout.addWidget(friend_widget) self.url = '' self.splitter1 = QSplitter(Qt.Vertical) top_left_frame.setFixedHeight(250 ) self.splitter1.addWidget(top_left_frame) self.splitter1.addWidget(bottom_left_frame) self.splitter2 = QSplitter(Qt.Horizontal) self.splitter2.addWidget(self.splitter1) self.splitter2.addWidget(right_frame) widget = QWidget() pagelayout.addWidget(self.splitter2) widget.setLayout(pagelayout) self.setCentralWidget(widget) verifyid_btn.clicked.connect(self.show_verifyid_page) user_btn.clicked.connect(self.show_login_page) registor_btn.clicked.connect(self.show_register_page) friend_btn.clicked.connect(self.show_friend_page) self.friend_tree.clicked.connect(self.show_firend_web) quit_btn.clicked.connect(self.quit_act) def init (self ): self.webEngineView.close() self.splitter1.setMinimumWidth(150 ) self.splitter2.setMinimumWidth(250 ) self.resize(600 , 400 ) def show_firend_web (self ): item = self.friend_tree.currentItem() if item.text(2 )[:4 ] == "http" : self.url = item.text(2 ) self.resize(1800 , 1200 ) self.webEngineView.show() self.splitter1.setFixedWidth(1400 ) self.webEngineView.load(QUrl(self.url)) def show_friend_page (self ): self.init() self.center() self.right_layout.setCurrentIndex(3 ) def show_register_page (self ): self.init() self.center() self.right_layout.setCurrentIndex(2 ) def show_login_page (self ): self.init() self.center() self.right_layout.setCurrentIndex(1 ) def show_verifyid_page (self ): self.init() self.center() self.right_layout.setCurrentIndex(0 ) def center (self ): ''' 获取桌面长宽 获取窗口长宽 移动 ''' screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2 , (screen.height() - size.height()) / 2 ) def quit_act (self ): sender = self.sender() print (sender.text() + '键被按下' ) qApp = QApplication.instance() qApp.quit() if __name__ == '__main__' : app = QApplication(sys.argv) w = MainWindow() w.show() sys.exit(app.exec_())
结语 PyQt5 这方面的文档,国内的资源很少。而且很单一(因为普遍都在抄袭,或者说没有特意的写出来)。
出了问题去StackOverflow上或者谷歌一下,能搜出来很多、很有效的解决方案,至少国内的搜索引擎还做不到能快速的搜索出有效的科技文档来。