1- from PyQt5 .QtWidgets import QWidget , QScrollArea , QLabel , QVBoxLayout , QApplication , QHBoxLayout , QSpacerItem ,\
2- QSizePolicy , QPushButton ,QFrame ,QMessageBox ,QFormLayout ,QProgressDialog ,QTextEdit ,QComboBox
3- from githubApi import GitHub , Release ,SourceManager ,PingThread
4- from PyQt5 .QtCore import QObject ,pyqtSlot ,Qt ,pyqtSignal ,QUrl
5- from PyQt5 .QtGui import QDesktopServices ,QFont
1+ from PyQt5 .QtWidgets import QWidget , QScrollArea , QLabel , QVBoxLayout , QApplication , QHBoxLayout , QSpacerItem , \
2+ QSizePolicy , QPushButton , QFrame , QMessageBox , QFormLayout , QProgressDialog , QTextEdit , QComboBox
3+ from githubApi import GitHub , Release , SourceManager , PingThread
4+ from PyQt5 .QtCore import QObject , pyqtSlot , Qt , pyqtSignal , QUrl , QPropertyAnimation , \
5+ QRect , QSize , pyqtProperty , QVariantAnimation
6+ from PyQt5 .QtGui import QDesktopServices , QFont , QIcon , QPainter , QPixmap , QPaintEvent
67
78
9+ class AnimationButton (QPushButton ):
10+ def __init__ (self , parent = None ):
11+ super ().__init__ (parent )
12+ self .pixmap : QPixmap = None
13+ self .clicked .connect (self .animationStart )
14+ self .animation = QVariantAnimation (self )
15+ self .animation .valueChanged .connect (self .setRotate )
16+ self .__rotate = 0
17+
18+ def rotate (self ):
19+ return self .__rotate
20+
21+ def setRotate (self , rotate ):
22+ self .__rotate = rotate
23+ self .update ()
24+ # rotate = property(int,rotate, setRotate)
25+
26+ def paintEvent (self , event ):
27+ super ().paintEvent (event )
28+ if self .pixmap is not None :
29+ painter = QPainter (self )
30+ painter .setRenderHint (QPainter .RenderHint .Antialiasing )
31+ painter .translate (self .width () / 2 , self .height () / 2 )
32+ painter .rotate (self .rotate ())
33+ painter .drawPixmap (- (self .width ()- 10 ) // 2 , - (self .height ()- 10 ) //
34+ 2 , self .pixmap .scaled (self .width () - 10 , self .height () - 10 ))
35+
36+ def animationStart (self , check ):
37+ if check :
38+ self .animation .setStartValue (0 )
39+ self .animation .setEndValue (90 )
40+ else :
41+ self .animation .setStartValue (90 )
42+ self .animation .setEndValue (0 )
43+ self .animation .setDuration (300 )
44+ self .animation .start ()
45+
846
947class ReleaseFrame (QFrame ):
1048 downLoadFile = pyqtSignal (Release )
11- def __init__ (self , release : Release ,mode = ">" , parent = None ):
49+
50+ def __init__ (self , release : Release , mode = ">" , parent = None ):
1251 super ().__init__ (parent )
1352 self .release : Release = release
14- self .showButton = QPushButton ( QObject . tr ( self , "Show" ) )
53+ self .showButton = AnimationButton ( )
1554 self .showButton .setCheckable (True )
55+ self .showButton .pixmap = QPixmap ("media/unfold.png" )
56+ self .titleWidget = QWidget ()
1657 self .formWidget = QWidget ()
1758 self .downloadButton = QPushButton (QObject .tr (self , "Download" ))
18- self .formWidget .hide ()
1959 self .bodyEdit = QTextEdit ()
2060 self .mode = mode
2161 self .initUi ()
2262 self .initConnect ()
63+ self .formWidget .setVisible (False )
2364
2465 def initUi (self ):
2566
2667 # self.setFrameShape(QFrame.StyledPanel)
2768 layout = QVBoxLayout ()
69+ layout .setContentsMargins (0 , 0 , 0 , 0 )
2870 row1 = QHBoxLayout ()
2971 row1 .addWidget (self .showButton )
3072 row1 .addWidget (QLabel (self .release .tag_name ))
3173 row1 .addItem (QSpacerItem (
3274 20 , 20 , QSizePolicy .Expanding , QSizePolicy .Minimum ))
3375 self .downloadButton .setEnabled (self .mode == ">" )
3476 row1 .addWidget (self .downloadButton )
35-
77+ self .titleWidget .setLayout (row1 )
78+ self .titleWidget .setContentsMargins (0 , 0 , 0 , 0 )
3679 formLayout = QFormLayout ()
37- formLayout .setContentsMargins (0 , 0 , 0 , 0 )
3880 urlLabel = QLabel ()
39- urlLabel .setText ("<a href='" + self .release .html_url + "'>" + QObject .tr (self ,"open external links" )+ "</a>" )
81+ urlLabel .setText ("<a href='" + self .release .html_url +
82+ "'>" + QObject .tr (self , "open external links" ) + "</a>" )
4083 urlLabel .setOpenExternalLinks (True )
4184 dataLayout = QVBoxLayout ()
42- dataLayout .setContentsMargins (0 , 0 , 0 , 0 )
4385 formLayout .setLabelAlignment (Qt .AlignmentFlag .AlignLeft )
4486 formLayout .addRow (QObject .tr (self , "html_url" ), urlLabel )
45- formLayout .addRow (QObject .tr (self , "name" ), QLabel (self .release .assets_name ))
46- formLayout .addRow (QObject .tr (self , "content_type" ), QLabel (self .release .assets_content_type ))
47- formLayout .addRow (QObject .tr (self , "size" ), QLabel (str (f"{ self .release .assets_size / 1000000 :.2f} MB" )))
48- formLayout .addRow (QObject .tr (self , "download_count" ), QLabel (str (self .release .assets_download_count )))
49- formLayout .addRow (QObject .tr (self , "created_at" ), QLabel (self .release .assets_created_at ))
87+ formLayout .addRow (QObject .tr (self , "name" ),
88+ QLabel (self .release .assets_name ))
89+ formLayout .addRow (QObject .tr (self , "content_type" ),
90+ QLabel (self .release .assets_content_type ))
91+ formLayout .addRow (QObject .tr (self , "size" ), QLabel (
92+ str (f"{ self .release .assets_size / 1000000 :.2f} MB" )))
93+ formLayout .addRow (QObject .tr (self , "download_count" ),
94+ QLabel (str (self .release .assets_download_count )))
95+ formLayout .addRow (QObject .tr (self , "created_at" ),
96+ QLabel (self .release .assets_created_at ))
5097 downloadUrlLabel = QLabel ()
51- downloadUrlLabel .setText ("<a href='" + self .release .assets_browser_download_url + "'>" + QObject .tr (self ,"open download links" )+ "</a>" )
98+ downloadUrlLabel .setText ("<a href='" + self .release .assets_browser_download_url +
99+ "'>" + QObject .tr (self , "open download links" ) + "</a>" )
52100 downloadUrlLabel .setOpenExternalLinks (True )
53- formLayout .addRow (QObject .tr (self , "browser_download_url" ), downloadUrlLabel )
101+ formLayout .addRow (QObject .tr (
102+ self , "browser_download_url" ), downloadUrlLabel )
54103 dataLayout .addLayout (formLayout )
55104 self .bodyEdit .setMarkdown (self .release .body )
56105 self .bodyEdit .setReadOnly (True )
57106 font = QFont ("Microsoft YaHei UI" , 13 )
58107 font .setBold (True )
59108 self .bodyEdit .setFont (font )
60- self .bodyEdit .setVerticalScrollBarPolicy (Qt .ScrollBarPolicy .ScrollBarAlwaysOff )
109+ self .bodyEdit .setVerticalScrollBarPolicy (
110+ Qt .ScrollBarPolicy .ScrollBarAlwaysOff )
61111 dataLayout .addWidget (self .bodyEdit )
62112 self .formWidget .setLayout (dataLayout )
63113
64- layout .addLayout ( row1 )
65- layout .addWidget (self .formWidget )
114+ layout .addWidget ( self . titleWidget )
115+ layout .addWidget (self .formWidget )
66116 self .setLayout (layout )
67117 rgbStr = ""
68118 if self .mode == ">" :
@@ -75,22 +125,47 @@ def initUi(self):
75125 # 样式表红色背景
76126 rgbStr = "rgb(249, 179, 163)"
77127 # label字体微软雅黑Ui,大小13
78- self .setStyleSheet (f"QFrame{{background-color:{ rgbStr } ; font-family:Microsoft YaHei UI; font-size:14px;}}" )
79-
128+ self .setStyleSheet (
129+ f"QFrame{{background-color:{ rgbStr } ; font-family:Microsoft YaHei UI; font-size:14px;}}" )
130+
80131 def initConnect (self ):
81132 self .showButton .clicked .connect (self .showButtonClicked )
82133 self .downloadButton .clicked .connect (self .downLoadButtonClicked )
83- def showButtonClicked (self ,checked :bool ):
134+
135+ def showButtonClicked (self , checked : bool ):
136+ self .formWidget .setVisible (True )
137+ animation = QPropertyAnimation (self .formWidget , b"size" , self )
138+ animation2 = QPropertyAnimation (self , b"size" , self )
139+ animation .setDuration (300 )
140+ animation2 .setDuration (300 )
141+ start : QSize = None
142+ end : QSize = None
143+ start1 : QSize = None
144+ end1 : QSize = None
84145 if checked :
85- self .formWidget .show ()
146+ start = QSize (self .width (), 0 )
147+ start1 = QSize (self .width (), self .titleWidget .height ())
148+ end = QSize (self .width (), self .formWidget .sizeHint ().height ())
149+ end1 = QSize (self .width (), self .sizeHint ().height ())
86150 else :
87- self .formWidget .hide ()
151+ start = QSize (self .width (), self .formWidget .sizeHint ().height ())
152+ start1 = QSize (self .width (), self .sizeHint ().height ())
153+ end = QSize (self .width (), 0 )
154+ end1 = QSize (self .width (), self .titleWidget .height ())
155+ animation .setStartValue (start )
156+ animation .setEndValue (end )
157+ animation2 .setStartValue (start1 )
158+ animation2 .setEndValue (end1 )
159+ animation2 .start (QPropertyAnimation .DeletionPolicy .DeleteWhenStopped )
160+ animation .start (QPropertyAnimation .DeletionPolicy .DeleteWhenStopped )
161+ animation .finished .connect (lambda : self .formWidget .setVisible (checked ))
162+
88163 def downLoadButtonClicked (self ):
89164 self .downLoadFile .emit (self .release )
90-
165+
91166
92167class CheckUpdateGui (QWidget ):
93- def __init__ (self ,github :GitHub , parent = None ):
168+ def __init__ (self , github : GitHub , parent = None ):
94169 super ().__init__ (parent )
95170 self .github : GitHub = github
96171 self .github .setParent (self )
@@ -102,26 +177,36 @@ def __init__(self,github:GitHub, parent=None):
102177 self .releaseArea .setHorizontalScrollBarPolicy (
103178 Qt .ScrollBarPolicy .ScrollBarAlwaysOff
104179 )
180+ self .pingThread = None
181+ self .sourceSpeedLabel = QLabel ()
105182 self .sourceCombo = QComboBox ()
106183 self .sourceCombo .addItems (self .github .sourceManager .sources .keys ())
107- self .sourceCombo .setCurrentText (self .github .sourceManager .currentSource )
108- self .currentVersionLabel = QLabel (f"Current Version:{ self .github .version } " )
184+ self .sourceCombo .setCurrentText (
185+ self .github .sourceManager .currentSource )
186+ self .currentVersionLabel = QLabel (
187+ f"Current Version:{ self .github .version } " )
109188 self .processDialog = None
110189 self .initUi ()
111190 self .initConnect ()
112191 self .resize (450 , 600 )
113192 self .checkUpdateButton .click ()
193+ self .changeSource (self .github .sourceManager .currentSource )
194+
114195 def initUi (self ):
115196 font = QFont ("Microsoft YaHei UI" , 13 )
116197 font .setBold (True )
117198 self .currentVersionLabel .setFont (font )
199+ font .setBold (False )
200+ font .setPointSize (12 )
201+ self .sourceSpeedLabel .setFont (font )
118202 layout = QVBoxLayout ()
119203
120204 row1 = QHBoxLayout ()
121205 row1 .setContentsMargins (0 , 0 , 0 , 0 )
122206 row1 .addWidget (self .currentVersionLabel )
123207 row1 .addItem (QSpacerItem (
124208 20 , 20 , QSizePolicy .Expanding , QSizePolicy .Minimum ))
209+ row1 .addWidget (self .sourceSpeedLabel )
125210 row1 .addWidget (self .sourceCombo )
126211 row1 .addWidget (self .checkUpdateButton )
127212
@@ -130,67 +215,83 @@ def initUi(self):
130215
131216 row2 .addWidget (self .releaseArea )
132217
133-
134218 layout .addLayout (row1 )
135219 layout .addLayout (row2 )
136220
137221 self .setLayout (layout )
138222
139223 def initConnect (self ):
140- self .checkUpdateButton .clicked .connect (lambda : self .github .releases ())
224+ self .checkUpdateButton .clicked .connect (lambda : self .github .releases ())
141225 self .github .releasesAsyncSignal .connect (self .checkUpdate )
142226 self .github .errorSignal .connect (self .showError )
143- self .github .downloadReleaseAsyncStartSignal .connect (self .showDownloadDialog )
144- self .github .downloadReleaseAsyncProgressSignal .connect (self .updateDownloadDialog )
145- self .github .downloadReleaseAsyncFinishSignal .connect (self .hideDownloadDialog )
146-
227+ self .github .downloadReleaseAsyncStartSignal .connect (
228+ self .showDownloadDialog )
229+ self .github .downloadReleaseAsyncProgressSignal .connect (
230+ self .updateDownloadDialog )
231+ self .github .downloadReleaseAsyncFinishSignal .connect (
232+ self .hideDownloadDialog )
233+ self .sourceCombo .currentTextChanged .connect (self .changeSource )
234+ def changeSource (self , source : str ):
235+ self .pingThread = PingThread (source , self .github .sourceManager .sources [source ])
236+ self .sourceSpeedLabel .setText ("---ms" )
237+ self .pingThread .pingSignal .connect (lambda x ,y : self .sourceSpeedLabel .setText (f"{ int (y )} ms" ))
238+ self .pingThread .start ()
147239 @pyqtSlot (list )
148- def checkUpdate (self ,releases :list ):
240+ def checkUpdate (self , releases : list ):
149241 widget = self .releaseArea .widget ()
150242 if widget is not None :
151243 widget .deleteLater ()
152244 widget = QWidget ()
153245 layout = QVBoxLayout ()
154246 layout .setContentsMargins (0 , 0 , 0 , 0 )
155247 for release in releases :
156- frame = ReleaseFrame (release ,self .github .compareVersion (release .tag_name ))
248+ frame = ReleaseFrame (
249+ release , self .github .compareVersion (release .tag_name ))
157250 layout .addWidget (frame )
158251 frame .downLoadFile .connect (self .github .downloadRelease )
159252 # 底部加一个空白区域
160253 panel = QWidget ()
161254 panel .setContentsMargins (0 , 0 , 0 , 0 )
162255 panel .setFixedHeight (100 )
163256 layout .addWidget (panel )
164- layout .addItem (QSpacerItem (20 , 20 , QSizePolicy .Minimum , QSizePolicy .Expanding ))
257+ layout .addItem (QSpacerItem (
258+ 20 , 20 , QSizePolicy .Minimum , QSizePolicy .Expanding ))
165259 widget .setLayout (layout )
166260 self .releaseArea .setWidget (widget )
167- def showError (self ,msg :str ):
168- QMessageBox .critical (self ,QObject .tr (self , "Error" ),msg )
169261
170- def showDownloadDialog (self ,release :Release ):
262+ def showError (self , msg : str ):
263+ QMessageBox .critical (self , QObject .tr (self , "Error" ), msg )
264+
265+ def showDownloadDialog (self , release : Release ):
171266 if self .processDialog is not None :
172267 self .processDialog .close ()
173268 self .processDialog = QProgressDialog (self )
174- self .processDialog .setWindowTitle (QObject .tr (self , f"{ release .tag_name } Downloading..." ))
175- def updateDownloadDialog (self ,a :int ,b :int ):
269+ self .processDialog .setWindowTitle (QObject .tr (
270+ self , f"{ release .tag_name } Downloading..." ))
271+
272+ def updateDownloadDialog (self , a : int , b : int ):
176273 if self .processDialog is not None :
177274 self .processDialog .setValue (a )
178275 self .processDialog .setMaximum (b )
179- self .processDialog .setLabelText (f'{ a / 1000000 : .2f} /{ b / 1000000 : .2f} MB' )
180- def hideDownloadDialog (self ,path :str ):
276+ self .processDialog .setLabelText (
277+ f'{ a / 1000000 : .2f} /{ b / 1000000 : .2f} MB' )
278+
279+ def hideDownloadDialog (self , path : str ):
181280 if self .processDialog is not None :
182281 self .processDialog .close ()
183282 self .processDialog = None
184283 # 使用系统默认方式打开文件
185284 QDesktopServices .openUrl (QUrl .fromLocalFile (path ))
186285
286+
187287if __name__ == '__main__' :
188288 import sys
189289 app = QApplication (sys .argv )
190290 data = {
191291 "Github" : "https://api.github.com/repos/" ,
192292 "fff666" : "https://fff666.top/" ,
193293 }
194- w = CheckUpdateGui (GitHub (SourceManager (data ),"eee555" , "Solvable-Minesweeper" , "3.1.9" , "(\d+\.\d+\.\d+)" ))
294+ w = CheckUpdateGui (GitHub (SourceManager (data ), "eee555" ,
295+ "Solvable-Minesweeper" , "3.1.9" , "(\d+\.\d+\.\d+)" ))
195296 w .show ()
196297 sys .exit (app .exec_ ())
0 commit comments