List View
|
@ -0,0 +1,11 @@
|
||||||
|
# QListView
|
||||||
|
|
||||||
|
## 1. Custom Widget Item
|
||||||
|
[Run](CustomWidgetItem.py)
|
||||||
|
|
||||||
|
![CustomWidgetItem](ScreenShot/CustomWidgetItem.png)
|
||||||
|
|
||||||
|
## 2. Custom Widget Sort Item
|
||||||
|
[Run](CustomWidgetSortItem.py)
|
||||||
|
|
||||||
|
![CustomWidgetSortItem](ScreenShot/CustomWidgetSortItem.gif)
|
|
@ -0,0 +1,11 @@
|
||||||
|
# QListView
|
||||||
|
|
||||||
|
## 1、显示自定义Widget
|
||||||
|
[运行](CustomWidgetItem.py)
|
||||||
|
|
||||||
|
![CustomWidgetItem](ScreenShot/CustomWidgetItem.png)
|
||||||
|
|
||||||
|
## 2、显示自定义Widget并排序
|
||||||
|
[运行](CustomWidgetSortItem.py)
|
||||||
|
|
||||||
|
![CustomWidgetSortItem](ScreenShot/CustomWidgetSortItem.gif)
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 323 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
@ -1,29 +1,29 @@
|
||||||
# 腾讯视频热播列表
|
# 腾讯视频热播列表
|
||||||
|
|
||||||
简单思路说明:
|
简单思路说明:
|
||||||
|
|
||||||
- 利用QScrollArea滚动显示,QGridLayout或者FlowLayout做布局来放置自定义的Widget
|
- 利用QScrollArea滚动显示,QGridLayout或者FlowLayout做布局来放置自定义的Widget
|
||||||
- QNetworkAccessManager异步下载网页和图片
|
- QNetworkAccessManager异步下载网页和图片
|
||||||
- QScrollArea滚动到底部触发下一页加载
|
- QScrollArea滚动到底部触发下一页加载
|
||||||
|
|
||||||
自定义控件说明:
|
自定义控件说明:
|
||||||
|
|
||||||
- 主要是多个layout和控件的结合,其中图片QLabel为自定义,通过setPixmap设置图片,重写paintEvent绘制底部渐变矩形框和白色文字
|
- 主要是多个layout和控件的结合,其中图片QLabel为自定义,通过setPixmap设置图片,重写paintEvent绘制底部渐变矩形框和白色文字
|
||||||
- 字体颜色用qss设置
|
- 字体颜色用qss设置
|
||||||
- 图标利用了QSvgWidget显示,可以是svg 动画(如圆形加载图)
|
- 图标利用了QSvgWidget显示,可以是svg 动画(如圆形加载图)
|
||||||
|
|
||||||
|
|
||||||
# 截图
|
# 截图
|
||||||
|
|
||||||
使用QGridLayout 固定列数效果图
|
使用QGridLayout 固定列数效果图
|
||||||
|
|
||||||
![截图1](ScreenShot/1.gif)
|
![截图1](ScreenShot/1.gif)
|
||||||
|
|
||||||
使用自定义布局FlowLayout 自动列数效果图
|
使用自定义布局FlowLayout 自动列数效果图
|
||||||
![截图2](ScreenShot/2.gif)
|
![截图2](ScreenShot/2.gif)
|
||||||
|
|
||||||
使用QListWidget 配合setFlow(QListWidget.LeftToRight)和
|
使用QListWidget 配合setFlow(QListWidget.LeftToRight)和
|
||||||
setWrapping(True)和
|
setWrapping(True)和
|
||||||
setResizeMode(QListWidget.Adjust)达到类似FlowLayout的效果
|
setResizeMode(QListWidget.Adjust)达到类似FlowLayout的效果
|
||||||
|
|
||||||
![截图3](ScreenShot/3.gif)
|
![截图3](ScreenShot/3.gif)
|
Before Width: | Height: | Size: 4.3 MiB After Width: | Height: | Size: 4.3 MiB |
Before Width: | Height: | Size: 5 MiB After Width: | Height: | Size: 5 MiB |
Before Width: | Height: | Size: 6.6 MiB After Width: | Height: | Size: 6.6 MiB |
|
@ -1,336 +1,336 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Created on 2018年2月4日
|
Created on 2018年2月4日
|
||||||
@author: Irony."[讽刺]
|
@author: Irony."[讽刺]
|
||||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||||
@email: 892768447@qq.com
|
@email: 892768447@qq.com
|
||||||
@file: TencentMovieHotPlay
|
@file: TencentMovieHotPlay
|
||||||
@description:
|
@description:
|
||||||
'''
|
'''
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
|
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
|
||||||
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
||||||
QBrush, QPaintEvent, QPixmap
|
QBrush, QPaintEvent, QPixmap
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
||||||
QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QGridLayout,\
|
QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QGridLayout,\
|
||||||
QAbstractSlider
|
QAbstractSlider
|
||||||
|
|
||||||
from lxml.etree import HTML # @UnresolvedImport
|
from lxml.etree import HTML # @UnresolvedImport
|
||||||
|
|
||||||
|
|
||||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||||
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
||||||
__Version__ = "Version 1.0"
|
__Version__ = "Version 1.0"
|
||||||
|
|
||||||
# offset=0,30,60,90
|
# offset=0,30,60,90
|
||||||
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
||||||
|
|
||||||
# 播放量图标
|
# 播放量图标
|
||||||
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
||||||
</svg>
|
</svg>
|
||||||
'''.encode()
|
'''.encode()
|
||||||
|
|
||||||
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
||||||
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
||||||
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
||||||
<stop stop-color="#03a9f4" offset="100%"/>
|
<stop stop-color="#03a9f4" offset="100%"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<g transform="translate(1 1)">
|
<g transform="translate(1 1)">
|
||||||
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</path>
|
</path>
|
||||||
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</circle>
|
</circle>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>'''.encode()
|
</svg>'''.encode()
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
||||||
|
|
||||||
|
|
||||||
class CoverLabel(QLabel):
|
class CoverLabel(QLabel):
|
||||||
|
|
||||||
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
||||||
super(CoverLabel, self).__init__(*args, **kwargs)
|
super(CoverLabel, self).__init__(*args, **kwargs)
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
self.setScaledContents(True)
|
self.setScaledContents(True)
|
||||||
self.setMinimumSize(220, 308)
|
self.setMinimumSize(220, 308)
|
||||||
self.setMaximumSize(220, 308)
|
self.setMaximumSize(220, 308)
|
||||||
self.cover_path = cover_path
|
self.cover_path = cover_path
|
||||||
self.cover_title = cover_title
|
self.cover_title = cover_title
|
||||||
self.video_url = video_url
|
self.video_url = video_url
|
||||||
self.setPixmap(QPixmap(cover_path))
|
self.setPixmap(QPixmap(cover_path))
|
||||||
|
|
||||||
def setCoverPath(self, path):
|
def setCoverPath(self, path):
|
||||||
self.cover_path = path
|
self.cover_path = path
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
super(CoverLabel, self).mouseReleaseEvent(event)
|
super(CoverLabel, self).mouseReleaseEvent(event)
|
||||||
webbrowser.open_new_tab(self.video_url)
|
webbrowser.open_new_tab(self.video_url)
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
super(CoverLabel, self).paintEvent(event)
|
super(CoverLabel, self).paintEvent(event)
|
||||||
if hasattr(self, "cover_title") and self.cover_title != "":
|
if hasattr(self, "cover_title") and self.cover_title != "":
|
||||||
# 底部绘制文字
|
# 底部绘制文字
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
rect = self.rect()
|
rect = self.rect()
|
||||||
# 粗略字体高度
|
# 粗略字体高度
|
||||||
painter.save()
|
painter.save()
|
||||||
fheight = self.fontMetrics().height()
|
fheight = self.fontMetrics().height()
|
||||||
# 底部矩形框背景渐变颜色
|
# 底部矩形框背景渐变颜色
|
||||||
bottomRectColor = QLinearGradient(
|
bottomRectColor = QLinearGradient(
|
||||||
rect.width() / 2, rect.height() - 24 - fheight,
|
rect.width() / 2, rect.height() - 24 - fheight,
|
||||||
rect.width() / 2, rect.height())
|
rect.width() / 2, rect.height())
|
||||||
bottomRectColor.setSpread(QGradient.PadSpread)
|
bottomRectColor.setSpread(QGradient.PadSpread)
|
||||||
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
||||||
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
||||||
# 画半透明渐变矩形框
|
# 画半透明渐变矩形框
|
||||||
painter.setPen(Qt.NoPen)
|
painter.setPen(Qt.NoPen)
|
||||||
painter.setBrush(QBrush(bottomRectColor))
|
painter.setBrush(QBrush(bottomRectColor))
|
||||||
painter.drawRect(rect.x(), rect.height() - 24 -
|
painter.drawRect(rect.x(), rect.height() - 24 -
|
||||||
fheight, rect.width(), 24 + fheight)
|
fheight, rect.width(), 24 + fheight)
|
||||||
painter.restore()
|
painter.restore()
|
||||||
# 距离底部一定高度画文字
|
# 距离底部一定高度画文字
|
||||||
font = self.font() or QFont()
|
font = self.font() or QFont()
|
||||||
font.setPointSize(8)
|
font.setPointSize(8)
|
||||||
painter.setFont(font)
|
painter.setFont(font)
|
||||||
painter.setPen(Qt.white)
|
painter.setPen(Qt.white)
|
||||||
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
||||||
painter.drawText(rect, Qt.AlignHCenter |
|
painter.drawText(rect, Qt.AlignHCenter |
|
||||||
Qt.AlignBottom, self.cover_title)
|
Qt.AlignBottom, self.cover_title)
|
||||||
|
|
||||||
|
|
||||||
class ItemWidget(QWidget):
|
class ItemWidget(QWidget):
|
||||||
|
|
||||||
def __init__(self, cover_path, figure_info, figure_title,
|
def __init__(self, cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
|
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
|
||||||
super(ItemWidget, self).__init__(*args, **kwargs)
|
super(ItemWidget, self).__init__(*args, **kwargs)
|
||||||
self.setMaximumSize(220, 380)
|
self.setMaximumSize(220, 380)
|
||||||
self.setMaximumSize(220, 380)
|
self.setMaximumSize(220, 380)
|
||||||
self.img_path = img_path
|
self.img_path = img_path
|
||||||
self.cover_url = cover_url
|
self.cover_url = cover_url
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
# 图片label
|
# 图片label
|
||||||
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
||||||
layout.addWidget(self.clabel)
|
layout.addWidget(self.clabel)
|
||||||
|
|
||||||
# 片名和分数
|
# 片名和分数
|
||||||
flayout = QHBoxLayout()
|
flayout = QHBoxLayout()
|
||||||
flayout.addWidget(QLabel(figure_title, self))
|
flayout.addWidget(QLabel(figure_title, self))
|
||||||
flayout.addItem(QSpacerItem(
|
flayout.addItem(QSpacerItem(
|
||||||
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
||||||
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
||||||
layout.addLayout(flayout)
|
layout.addLayout(flayout)
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
layout.addWidget(
|
layout.addWidget(
|
||||||
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
||||||
|
|
||||||
# 播放量
|
# 播放量
|
||||||
blayout = QHBoxLayout()
|
blayout = QHBoxLayout()
|
||||||
count_icon = QSvgWidget(self)
|
count_icon = QSvgWidget(self)
|
||||||
count_icon.setMaximumSize(16, 16)
|
count_icon.setMaximumSize(16, 16)
|
||||||
count_icon.load(Svg_icon_play_sm)
|
count_icon.load(Svg_icon_play_sm)
|
||||||
blayout.addWidget(count_icon)
|
blayout.addWidget(count_icon)
|
||||||
blayout.addWidget(
|
blayout.addWidget(
|
||||||
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
||||||
layout.addLayout(blayout)
|
layout.addLayout(blayout)
|
||||||
|
|
||||||
def setCover(self, path):
|
def setCover(self, path):
|
||||||
self.clabel.setCoverPath(path)
|
self.clabel.setCoverPath(path)
|
||||||
self.clabel.setPixmap(QPixmap(path))
|
self.clabel.setPixmap(QPixmap(path))
|
||||||
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
# 每个item控件的大小
|
# 每个item控件的大小
|
||||||
return QSize(220, 380)
|
return QSize(220, 380)
|
||||||
|
|
||||||
def event(self, event):
|
def event(self, event):
|
||||||
if isinstance(event, QPaintEvent):
|
if isinstance(event, QPaintEvent):
|
||||||
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
||||||
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
||||||
# print("start download img:", self.cover_url)
|
# print("start download img:", self.cover_url)
|
||||||
req = QNetworkRequest(QUrl(self.cover_url))
|
req = QNetworkRequest(QUrl(self.cover_url))
|
||||||
# 设置两个自定义属性方便后期reply中处理
|
# 设置两个自定义属性方便后期reply中处理
|
||||||
req.setAttribute(QNetworkRequest.User + 1, self)
|
req.setAttribute(QNetworkRequest.User + 1, self)
|
||||||
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
||||||
self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
|
self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
|
||||||
return super(ItemWidget, self).event(event)
|
return super(ItemWidget, self).event(event)
|
||||||
|
|
||||||
|
|
||||||
class GridWidget(QWidget):
|
class GridWidget(QWidget):
|
||||||
|
|
||||||
Page = 0
|
Page = 0
|
||||||
loadStarted = pyqtSignal(bool)
|
loadStarted = pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(GridWidget, self).__init__(*args, **kwargs)
|
super(GridWidget, self).__init__(*args, **kwargs)
|
||||||
self._layout = QGridLayout(self, spacing=20)
|
self._layout = QGridLayout(self, spacing=20)
|
||||||
self._layout.setContentsMargins(20, 20, 20, 20)
|
self._layout.setContentsMargins(20, 20, 20, 20)
|
||||||
# 异步网络下载管理器
|
# 异步网络下载管理器
|
||||||
self._manager = QNetworkAccessManager(self)
|
self._manager = QNetworkAccessManager(self)
|
||||||
self._manager.finished.connect(self.onFinished)
|
self._manager.finished.connect(self.onFinished)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
if self.Page == -1:
|
if self.Page == -1:
|
||||||
return
|
return
|
||||||
self.loadStarted.emit(True)
|
self.loadStarted.emit(True)
|
||||||
# 延迟一秒后调用目的在于显示进度条
|
# 延迟一秒后调用目的在于显示进度条
|
||||||
QTimer.singleShot(1000, self._load)
|
QTimer.singleShot(1000, self._load)
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
print("load url:", Url.format(self.Page * 30))
|
print("load url:", Url.format(self.Page * 30))
|
||||||
url = QUrl(Url.format(self.Page * 30))
|
url = QUrl(Url.format(self.Page * 30))
|
||||||
self._manager.get(QNetworkRequest(url))
|
self._manager.get(QNetworkRequest(url))
|
||||||
|
|
||||||
def onFinished(self, reply):
|
def onFinished(self, reply):
|
||||||
# 请求完成后会调用该函数
|
# 请求完成后会调用该函数
|
||||||
req = reply.request() # 获取请求
|
req = reply.request() # 获取请求
|
||||||
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
||||||
path = req.attribute(QNetworkRequest.User + 2, None)
|
path = req.attribute(QNetworkRequest.User + 2, None)
|
||||||
html = reply.readAll().data()
|
html = reply.readAll().data()
|
||||||
reply.deleteLater()
|
reply.deleteLater()
|
||||||
del reply
|
del reply
|
||||||
if iwidget and path and html:
|
if iwidget and path and html:
|
||||||
# 这里是图片下载完毕
|
# 这里是图片下载完毕
|
||||||
open(path, "wb").write(html)
|
open(path, "wb").write(html)
|
||||||
iwidget.setCover(path)
|
iwidget.setCover(path)
|
||||||
return
|
return
|
||||||
# 解析网页
|
# 解析网页
|
||||||
self._parseHtml(html)
|
self._parseHtml(html)
|
||||||
self.loadStarted.emit(False)
|
self.loadStarted.emit(False)
|
||||||
|
|
||||||
def splist(self, src, length):
|
def splist(self, src, length):
|
||||||
# 等分列表
|
# 等分列表
|
||||||
return (src[i:i + length] for i in range(len(src)) if i % length == 0)
|
return (src[i:i + length] for i in range(len(src)) if i % length == 0)
|
||||||
|
|
||||||
def _parseHtml(self, html):
|
def _parseHtml(self, html):
|
||||||
# encoding = chardet.detect(html) or {}
|
# encoding = chardet.detect(html) or {}
|
||||||
# html = html.decode(encoding.get("encoding","utf-8"))
|
# html = html.decode(encoding.get("encoding","utf-8"))
|
||||||
html = HTML(html)
|
html = HTML(html)
|
||||||
# 查找所有的li list_item
|
# 查找所有的li list_item
|
||||||
lis = html.xpath("//li[@class='list_item']")
|
lis = html.xpath("//li[@class='list_item']")
|
||||||
if not lis:
|
if not lis:
|
||||||
self.Page = -1 # 后面没有页面了
|
self.Page = -1 # 后面没有页面了
|
||||||
return
|
return
|
||||||
lack_count = self._layout.count() % 30 # 获取布局中上次还缺几个5行*6列的标准
|
lack_count = self._layout.count() % 30 # 获取布局中上次还缺几个5行*6列的标准
|
||||||
row_count = int(self._layout.count() / 6) # 行数
|
row_count = int(self._layout.count() / 6) # 行数
|
||||||
print("lack_count:", lack_count)
|
print("lack_count:", lack_count)
|
||||||
self.Page += 1 # 自增+1
|
self.Page += 1 # 自增+1
|
||||||
if lack_count != 0: # 上一次没有满足一行6个,需要补齐
|
if lack_count != 0: # 上一次没有满足一行6个,需要补齐
|
||||||
lack_li = lis[:lack_count]
|
lack_li = lis[:lack_count]
|
||||||
lis = lis[lack_count:]
|
lis = lis[lack_count:]
|
||||||
self._makeItem(lack_li, row_count) # 补齐
|
self._makeItem(lack_li, row_count) # 补齐
|
||||||
if lack_li and lis:
|
if lack_li and lis:
|
||||||
row_count += 1
|
row_count += 1
|
||||||
self._makeItem(lis, row_count) # 完成剩下的
|
self._makeItem(lis, row_count) # 完成剩下的
|
||||||
else:
|
else:
|
||||||
self._makeItem(lis, row_count)
|
self._makeItem(lis, row_count)
|
||||||
|
|
||||||
def _makeItem(self, li_s, row_count):
|
def _makeItem(self, li_s, row_count):
|
||||||
li_s = self.splist(li_s, 6)
|
li_s = self.splist(li_s, 6)
|
||||||
for row, lis in enumerate(li_s):
|
for row, lis in enumerate(li_s):
|
||||||
for col, li in enumerate(lis):
|
for col, li in enumerate(lis):
|
||||||
a = li.find("a")
|
a = li.find("a")
|
||||||
video_url = a.get("href") # 视频播放地址
|
video_url = a.get("href") # 视频播放地址
|
||||||
img = a.find("img")
|
img = a.find("img")
|
||||||
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
||||||
figure_title = img.get("alt") # 电影名
|
figure_title = img.get("alt") # 电影名
|
||||||
figure_info = a.find("div/span")
|
figure_info = a.find("div/span")
|
||||||
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
||||||
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
||||||
# 主演
|
# 主演
|
||||||
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
||||||
"".join([Actor.format(**dict(fd.items()))
|
"".join([Actor.format(**dict(fd.items()))
|
||||||
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
||||||
# 播放数
|
# 播放数
|
||||||
figure_count = (
|
figure_count = (
|
||||||
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
||||||
path = "cache/{0}.jpg".format(
|
path = "cache/{0}.jpg".format(
|
||||||
os.path.splitext(os.path.basename(video_url))[0])
|
os.path.splitext(os.path.basename(video_url))[0])
|
||||||
cover_path = "pic_v.png"
|
cover_path = "pic_v.png"
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
cover_path = path
|
cover_path = path
|
||||||
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
|
figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
|
||||||
self._layout.addWidget(iwidget, row_count + row, col)
|
self._layout.addWidget(iwidget, row_count + row, col)
|
||||||
|
|
||||||
|
|
||||||
class Window(QScrollArea):
|
class Window(QScrollArea):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Window, self).__init__(*args, **kwargs)
|
super(Window, self).__init__(*args, **kwargs)
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
self.setFrameShape(self.NoFrame)
|
self.setFrameShape(self.NoFrame)
|
||||||
self.setWidgetResizable(True)
|
self.setWidgetResizable(True)
|
||||||
self.setAlignment(Qt.AlignCenter)
|
self.setAlignment(Qt.AlignCenter)
|
||||||
self._loadStart = False
|
self._loadStart = False
|
||||||
# 网格窗口
|
# 网格窗口
|
||||||
self._widget = GridWidget(self)
|
self._widget = GridWidget(self)
|
||||||
self._widget.loadStarted.connect(self.setLoadStarted)
|
self._widget.loadStarted.connect(self.setLoadStarted)
|
||||||
self.setWidget(self._widget)
|
self.setWidget(self._widget)
|
||||||
# 连接竖着的滚动条滚动事件
|
# 连接竖着的滚动条滚动事件
|
||||||
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
||||||
# 进度条
|
# 进度条
|
||||||
self.loadWidget = QSvgWidget(
|
self.loadWidget = QSvgWidget(
|
||||||
self, minimumHeight=120, minimumWidth=120, visible=False)
|
self, minimumHeight=120, minimumWidth=120, visible=False)
|
||||||
self.loadWidget.load(Svg_icon_loading)
|
self.loadWidget.load(Svg_icon_loading)
|
||||||
|
|
||||||
def setLoadStarted(self, started):
|
def setLoadStarted(self, started):
|
||||||
self._loadStart = started
|
self._loadStart = started
|
||||||
self.loadWidget.setVisible(started)
|
self.loadWidget.setVisible(started)
|
||||||
|
|
||||||
def onActionTriggered(self, action):
|
def onActionTriggered(self, action):
|
||||||
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
||||||
# 同时防止多次加载同一个url
|
# 同时防止多次加载同一个url
|
||||||
if action != QAbstractSlider.SliderMove or self._loadStart:
|
if action != QAbstractSlider.SliderMove or self._loadStart:
|
||||||
return
|
return
|
||||||
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
||||||
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
||||||
# 可以下一页了
|
# 可以下一页了
|
||||||
self._widget.load()
|
self._widget.load()
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
super(Window, self).resizeEvent(event)
|
super(Window, self).resizeEvent(event)
|
||||||
self.loadWidget.setGeometry(
|
self.loadWidget.setGeometry(
|
||||||
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
||||||
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
||||||
self.loadWidget.minimumWidth(),
|
self.loadWidget.minimumWidth(),
|
||||||
self.loadWidget.minimumHeight()
|
self.loadWidget.minimumHeight()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.makedirs("cache", exist_ok=True)
|
os.makedirs("cache", exist_ok=True)
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
w = Window()
|
w = Window()
|
||||||
w.show()
|
w.show()
|
||||||
w._widget.load()
|
w._widget.load()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
|
@ -1,320 +1,320 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Created on 2018年2月4日
|
Created on 2018年2月4日
|
||||||
@author: Irony."[讽刺]
|
@author: Irony."[讽刺]
|
||||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||||
@email: 892768447@qq.com
|
@email: 892768447@qq.com
|
||||||
@file: TencentMovieHotPlay_Flow
|
@file: TencentMovieHotPlay_Flow
|
||||||
@description:
|
@description:
|
||||||
'''
|
'''
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
|
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer, pyqtSignal
|
||||||
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
||||||
QBrush, QPaintEvent, QPixmap
|
QBrush, QPaintEvent, QPixmap
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
||||||
QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QAbstractSlider
|
QHBoxLayout, QSpacerItem, QSizePolicy, QScrollArea, QAbstractSlider
|
||||||
|
|
||||||
from flowlayout import FlowLayout # @UnresolvedImport
|
from flowlayout import FlowLayout # @UnresolvedImport
|
||||||
from lxml.etree import HTML # @UnresolvedImport
|
from lxml.etree import HTML # @UnresolvedImport
|
||||||
|
|
||||||
|
|
||||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||||
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
||||||
__Version__ = "Version 1.0"
|
__Version__ = "Version 1.0"
|
||||||
|
|
||||||
# offset=0,30,60,90
|
# offset=0,30,60,90
|
||||||
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
||||||
|
|
||||||
# 播放量图标
|
# 播放量图标
|
||||||
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
||||||
</svg>
|
</svg>
|
||||||
'''.encode()
|
'''.encode()
|
||||||
|
|
||||||
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
||||||
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
||||||
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
||||||
<stop stop-color="#03a9f4" offset="100%"/>
|
<stop stop-color="#03a9f4" offset="100%"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<g transform="translate(1 1)">
|
<g transform="translate(1 1)">
|
||||||
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</path>
|
</path>
|
||||||
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</circle>
|
</circle>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>'''.encode()
|
</svg>'''.encode()
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
||||||
|
|
||||||
|
|
||||||
class CoverLabel(QLabel):
|
class CoverLabel(QLabel):
|
||||||
|
|
||||||
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
||||||
super(CoverLabel, self).__init__(*args, **kwargs)
|
super(CoverLabel, self).__init__(*args, **kwargs)
|
||||||
# super(CoverLabel, self).__init__(
|
# super(CoverLabel, self).__init__(
|
||||||
# '<html><head/><body><img src="{0}"/></body></html>'.format(os.path.abspath(cover_path)), *args, **kwargs)
|
# '<html><head/><body><img src="{0}"/></body></html>'.format(os.path.abspath(cover_path)), *args, **kwargs)
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
self.setScaledContents(True)
|
self.setScaledContents(True)
|
||||||
self.setMinimumSize(220, 308)
|
self.setMinimumSize(220, 308)
|
||||||
self.setMaximumSize(220, 308)
|
self.setMaximumSize(220, 308)
|
||||||
self.cover_path = cover_path
|
self.cover_path = cover_path
|
||||||
self.cover_title = cover_title
|
self.cover_title = cover_title
|
||||||
self.video_url = video_url
|
self.video_url = video_url
|
||||||
self.setPixmap(QPixmap(cover_path))
|
self.setPixmap(QPixmap(cover_path))
|
||||||
|
|
||||||
def setCoverPath(self, path):
|
def setCoverPath(self, path):
|
||||||
self.cover_path = path
|
self.cover_path = path
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
super(CoverLabel, self).mouseReleaseEvent(event)
|
super(CoverLabel, self).mouseReleaseEvent(event)
|
||||||
webbrowser.open_new_tab(self.video_url)
|
webbrowser.open_new_tab(self.video_url)
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
super(CoverLabel, self).paintEvent(event)
|
super(CoverLabel, self).paintEvent(event)
|
||||||
if hasattr(self, "cover_title") and self.cover_title != "":
|
if hasattr(self, "cover_title") and self.cover_title != "":
|
||||||
# 底部绘制文字
|
# 底部绘制文字
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
rect = self.rect()
|
rect = self.rect()
|
||||||
# 粗略字体高度
|
# 粗略字体高度
|
||||||
painter.save()
|
painter.save()
|
||||||
fheight = self.fontMetrics().height()
|
fheight = self.fontMetrics().height()
|
||||||
# 底部矩形框背景渐变颜色
|
# 底部矩形框背景渐变颜色
|
||||||
bottomRectColor = QLinearGradient(
|
bottomRectColor = QLinearGradient(
|
||||||
rect.width() / 2, rect.height() - 24 - fheight,
|
rect.width() / 2, rect.height() - 24 - fheight,
|
||||||
rect.width() / 2, rect.height())
|
rect.width() / 2, rect.height())
|
||||||
bottomRectColor.setSpread(QGradient.PadSpread)
|
bottomRectColor.setSpread(QGradient.PadSpread)
|
||||||
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
||||||
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
||||||
# 画半透明渐变矩形框
|
# 画半透明渐变矩形框
|
||||||
painter.setPen(Qt.NoPen)
|
painter.setPen(Qt.NoPen)
|
||||||
painter.setBrush(QBrush(bottomRectColor))
|
painter.setBrush(QBrush(bottomRectColor))
|
||||||
painter.drawRect(rect.x(), rect.height() - 24 -
|
painter.drawRect(rect.x(), rect.height() - 24 -
|
||||||
fheight, rect.width(), 24 + fheight)
|
fheight, rect.width(), 24 + fheight)
|
||||||
painter.restore()
|
painter.restore()
|
||||||
# 距离底部一定高度画文字
|
# 距离底部一定高度画文字
|
||||||
font = self.font() or QFont()
|
font = self.font() or QFont()
|
||||||
font.setPointSize(8)
|
font.setPointSize(8)
|
||||||
painter.setFont(font)
|
painter.setFont(font)
|
||||||
painter.setPen(Qt.white)
|
painter.setPen(Qt.white)
|
||||||
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
||||||
painter.drawText(rect, Qt.AlignHCenter |
|
painter.drawText(rect, Qt.AlignHCenter |
|
||||||
Qt.AlignBottom, self.cover_title)
|
Qt.AlignBottom, self.cover_title)
|
||||||
|
|
||||||
|
|
||||||
class ItemWidget(QWidget):
|
class ItemWidget(QWidget):
|
||||||
|
|
||||||
def __init__(self, cover_path, figure_info, figure_title,
|
def __init__(self, cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
|
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, *args, **kwargs):
|
||||||
super(ItemWidget, self).__init__(*args, **kwargs)
|
super(ItemWidget, self).__init__(*args, **kwargs)
|
||||||
self.setMaximumSize(220, 420)
|
self.setMaximumSize(220, 420)
|
||||||
self.setMaximumSize(220, 420)
|
self.setMaximumSize(220, 420)
|
||||||
self.img_path = img_path
|
self.img_path = img_path
|
||||||
self.cover_url = cover_url
|
self.cover_url = cover_url
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(10, 20, 10, 0)
|
layout.setContentsMargins(10, 20, 10, 0)
|
||||||
# 图片label
|
# 图片label
|
||||||
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
||||||
layout.addWidget(self.clabel)
|
layout.addWidget(self.clabel)
|
||||||
|
|
||||||
# 片名和分数
|
# 片名和分数
|
||||||
flayout = QHBoxLayout()
|
flayout = QHBoxLayout()
|
||||||
flayout.addWidget(QLabel(figure_title, self))
|
flayout.addWidget(QLabel(figure_title, self))
|
||||||
flayout.addItem(QSpacerItem(
|
flayout.addItem(QSpacerItem(
|
||||||
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
||||||
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
||||||
layout.addLayout(flayout)
|
layout.addLayout(flayout)
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
layout.addWidget(
|
layout.addWidget(
|
||||||
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
||||||
|
|
||||||
# 播放量
|
# 播放量
|
||||||
blayout = QHBoxLayout()
|
blayout = QHBoxLayout()
|
||||||
count_icon = QSvgWidget(self)
|
count_icon = QSvgWidget(self)
|
||||||
count_icon.setMaximumSize(16, 16)
|
count_icon.setMaximumSize(16, 16)
|
||||||
count_icon.load(Svg_icon_play_sm)
|
count_icon.load(Svg_icon_play_sm)
|
||||||
blayout.addWidget(count_icon)
|
blayout.addWidget(count_icon)
|
||||||
blayout.addWidget(
|
blayout.addWidget(
|
||||||
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
||||||
layout.addLayout(blayout)
|
layout.addLayout(blayout)
|
||||||
|
|
||||||
def setCover(self, path):
|
def setCover(self, path):
|
||||||
self.clabel.setCoverPath(path)
|
self.clabel.setCoverPath(path)
|
||||||
self.clabel.setPixmap(QPixmap(path))
|
self.clabel.setPixmap(QPixmap(path))
|
||||||
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
# 每个item控件的大小
|
# 每个item控件的大小
|
||||||
return QSize(220, 420)
|
return QSize(220, 420)
|
||||||
|
|
||||||
def event(self, event):
|
def event(self, event):
|
||||||
if isinstance(event, QPaintEvent):
|
if isinstance(event, QPaintEvent):
|
||||||
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
||||||
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
||||||
# print("start download img:", self.cover_url)
|
# print("start download img:", self.cover_url)
|
||||||
req = QNetworkRequest(QUrl(self.cover_url))
|
req = QNetworkRequest(QUrl(self.cover_url))
|
||||||
# 设置两个自定义属性方便后期reply中处理
|
# 设置两个自定义属性方便后期reply中处理
|
||||||
req.setAttribute(QNetworkRequest.User + 1, self)
|
req.setAttribute(QNetworkRequest.User + 1, self)
|
||||||
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
||||||
self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
|
self.parentWidget()._manager.get(req) # 调用父窗口中的下载器下载
|
||||||
return super(ItemWidget, self).event(event)
|
return super(ItemWidget, self).event(event)
|
||||||
|
|
||||||
|
|
||||||
class GridWidget(QWidget):
|
class GridWidget(QWidget):
|
||||||
|
|
||||||
Page = 0
|
Page = 0
|
||||||
loadStarted = pyqtSignal(bool)
|
loadStarted = pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(GridWidget, self).__init__(*args, **kwargs)
|
super(GridWidget, self).__init__(*args, **kwargs)
|
||||||
self._layout = FlowLayout(self) # 使用自定义流式布局
|
self._layout = FlowLayout(self) # 使用自定义流式布局
|
||||||
# 异步网络下载管理器
|
# 异步网络下载管理器
|
||||||
self._manager = QNetworkAccessManager(self)
|
self._manager = QNetworkAccessManager(self)
|
||||||
self._manager.finished.connect(self.onFinished)
|
self._manager.finished.connect(self.onFinished)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
if self.Page == -1:
|
if self.Page == -1:
|
||||||
return
|
return
|
||||||
self.loadStarted.emit(True)
|
self.loadStarted.emit(True)
|
||||||
# 延迟一秒后调用目的在于显示进度条
|
# 延迟一秒后调用目的在于显示进度条
|
||||||
QTimer.singleShot(1000, self._load)
|
QTimer.singleShot(1000, self._load)
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
print("load url:", Url.format(self.Page * 30))
|
print("load url:", Url.format(self.Page * 30))
|
||||||
url = QUrl(Url.format(self.Page * 30))
|
url = QUrl(Url.format(self.Page * 30))
|
||||||
self._manager.get(QNetworkRequest(url))
|
self._manager.get(QNetworkRequest(url))
|
||||||
|
|
||||||
def onFinished(self, reply):
|
def onFinished(self, reply):
|
||||||
# 请求完成后会调用该函数
|
# 请求完成后会调用该函数
|
||||||
req = reply.request() # 获取请求
|
req = reply.request() # 获取请求
|
||||||
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
||||||
path = req.attribute(QNetworkRequest.User + 2, None)
|
path = req.attribute(QNetworkRequest.User + 2, None)
|
||||||
html = reply.readAll().data()
|
html = reply.readAll().data()
|
||||||
reply.deleteLater()
|
reply.deleteLater()
|
||||||
del reply
|
del reply
|
||||||
if iwidget and path and html:
|
if iwidget and path and html:
|
||||||
# 这里是图片下载完毕
|
# 这里是图片下载完毕
|
||||||
open(path, "wb").write(html)
|
open(path, "wb").write(html)
|
||||||
iwidget.setCover(path)
|
iwidget.setCover(path)
|
||||||
return
|
return
|
||||||
# 解析网页
|
# 解析网页
|
||||||
self._parseHtml(html)
|
self._parseHtml(html)
|
||||||
self.loadStarted.emit(False)
|
self.loadStarted.emit(False)
|
||||||
|
|
||||||
def _parseHtml(self, html):
|
def _parseHtml(self, html):
|
||||||
# encoding = chardet.detect(html) or {}
|
# encoding = chardet.detect(html) or {}
|
||||||
# html = html.decode(encoding.get("encoding","utf-8"))
|
# html = html.decode(encoding.get("encoding","utf-8"))
|
||||||
html = HTML(html)
|
html = HTML(html)
|
||||||
# 查找所有的li list_item
|
# 查找所有的li list_item
|
||||||
lis = html.xpath("//li[@class='list_item']")
|
lis = html.xpath("//li[@class='list_item']")
|
||||||
if not lis:
|
if not lis:
|
||||||
self.Page = -1 # 后面没有页面了
|
self.Page = -1 # 后面没有页面了
|
||||||
return
|
return
|
||||||
self.Page += 1
|
self.Page += 1
|
||||||
self._makeItem(lis)
|
self._makeItem(lis)
|
||||||
|
|
||||||
def _makeItem(self, lis):
|
def _makeItem(self, lis):
|
||||||
for li in lis:
|
for li in lis:
|
||||||
a = li.find("a")
|
a = li.find("a")
|
||||||
video_url = a.get("href") # 视频播放地址
|
video_url = a.get("href") # 视频播放地址
|
||||||
img = a.find("img")
|
img = a.find("img")
|
||||||
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
||||||
figure_title = img.get("alt") # 电影名
|
figure_title = img.get("alt") # 电影名
|
||||||
figure_info = a.find("div/span")
|
figure_info = a.find("div/span")
|
||||||
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
||||||
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
||||||
# 主演
|
# 主演
|
||||||
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
||||||
"".join([Actor.format(**dict(fd.items()))
|
"".join([Actor.format(**dict(fd.items()))
|
||||||
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
||||||
# 播放数
|
# 播放数
|
||||||
figure_count = (
|
figure_count = (
|
||||||
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
||||||
path = "cache/{0}.jpg".format(
|
path = "cache/{0}.jpg".format(
|
||||||
os.path.splitext(os.path.basename(video_url))[0])
|
os.path.splitext(os.path.basename(video_url))[0])
|
||||||
cover_path = "pic_v.png"
|
cover_path = "pic_v.png"
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
cover_path = path
|
cover_path = path
|
||||||
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
|
figure_score, figure_desc, figure_count, video_url, cover_url, path, self)
|
||||||
self._layout.addWidget(iwidget)
|
self._layout.addWidget(iwidget)
|
||||||
|
|
||||||
|
|
||||||
class Window(QScrollArea):
|
class Window(QScrollArea):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Window, self).__init__(*args, **kwargs)
|
super(Window, self).__init__(*args, **kwargs)
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
self.setFrameShape(self.NoFrame)
|
self.setFrameShape(self.NoFrame)
|
||||||
self.setWidgetResizable(True)
|
self.setWidgetResizable(True)
|
||||||
self.setAlignment(Qt.AlignCenter)
|
self.setAlignment(Qt.AlignCenter)
|
||||||
self._loadStart = False
|
self._loadStart = False
|
||||||
# 网格窗口
|
# 网格窗口
|
||||||
self._widget = GridWidget(self)
|
self._widget = GridWidget(self)
|
||||||
self._widget.loadStarted.connect(self.setLoadStarted)
|
self._widget.loadStarted.connect(self.setLoadStarted)
|
||||||
self.setWidget(self._widget)
|
self.setWidget(self._widget)
|
||||||
# 连接竖着的滚动条滚动事件
|
# 连接竖着的滚动条滚动事件
|
||||||
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
||||||
# 进度条
|
# 进度条
|
||||||
self.loadWidget = QSvgWidget(
|
self.loadWidget = QSvgWidget(
|
||||||
self, minimumHeight=120, minimumWidth=120, visible=False)
|
self, minimumHeight=120, minimumWidth=120, visible=False)
|
||||||
self.loadWidget.load(Svg_icon_loading)
|
self.loadWidget.load(Svg_icon_loading)
|
||||||
|
|
||||||
def setLoadStarted(self, started):
|
def setLoadStarted(self, started):
|
||||||
self._loadStart = started
|
self._loadStart = started
|
||||||
self.loadWidget.setVisible(started)
|
self.loadWidget.setVisible(started)
|
||||||
|
|
||||||
def onActionTriggered(self, action):
|
def onActionTriggered(self, action):
|
||||||
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
||||||
# 同时防止多次加载同一个url
|
# 同时防止多次加载同一个url
|
||||||
if action != QAbstractSlider.SliderMove or self._loadStart:
|
if action != QAbstractSlider.SliderMove or self._loadStart:
|
||||||
return
|
return
|
||||||
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
||||||
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
||||||
# 可以下一页了
|
# 可以下一页了
|
||||||
self._widget.load()
|
self._widget.load()
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
super(Window, self).resizeEvent(event)
|
super(Window, self).resizeEvent(event)
|
||||||
self.loadWidget.setGeometry(
|
self.loadWidget.setGeometry(
|
||||||
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
||||||
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
||||||
self.loadWidget.minimumWidth(),
|
self.loadWidget.minimumWidth(),
|
||||||
self.loadWidget.minimumHeight()
|
self.loadWidget.minimumHeight()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.makedirs("cache", exist_ok=True)
|
os.makedirs("cache", exist_ok=True)
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
w = Window()
|
w = Window()
|
||||||
w.show()
|
w.show()
|
||||||
w._widget.load()
|
w._widget.load()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
|
@ -1,312 +1,312 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Created on 2018年2月4日
|
Created on 2018年2月4日
|
||||||
@author: Irony."[讽刺]
|
@author: Irony."[讽刺]
|
||||||
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
@site: http://alyl.vip, http://orzorz.vip, https://coding.net/u/892768447, https://github.com/892768447
|
||||||
@email: 892768447@qq.com
|
@email: 892768447@qq.com
|
||||||
@file: TencentMovieHotPlay_ListWidget
|
@file: TencentMovieHotPlay_ListWidget
|
||||||
@description:
|
@description:
|
||||||
'''
|
'''
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer
|
from PyQt5.QtCore import QSize, Qt, QUrl, QTimer
|
||||||
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
from PyQt5.QtGui import QPainter, QFont, QLinearGradient, QGradient, QColor,\
|
||||||
QBrush, QPaintEvent, QPixmap
|
QBrush, QPaintEvent, QPixmap
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||||
from PyQt5.QtSvg import QSvgWidget
|
from PyQt5.QtSvg import QSvgWidget
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel,\
|
||||||
QHBoxLayout, QSpacerItem, QSizePolicy, QAbstractSlider,\
|
QHBoxLayout, QSpacerItem, QSizePolicy, QAbstractSlider,\
|
||||||
QListWidget, QListWidgetItem
|
QListWidget, QListWidgetItem
|
||||||
|
|
||||||
from lxml.etree import HTML # @UnresolvedImport
|
from lxml.etree import HTML # @UnresolvedImport
|
||||||
|
|
||||||
|
|
||||||
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
|
||||||
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
|
||||||
__Version__ = "Version 1.0"
|
__Version__ = "Version 1.0"
|
||||||
|
|
||||||
# offset=0,30,60,90
|
# offset=0,30,60,90
|
||||||
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
Url = "http://v.qq.com/x/list/movie?pay=-1&offset={0}"
|
||||||
|
|
||||||
# 播放量图标
|
# 播放量图标
|
||||||
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
Svg_icon_play_sm = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
<path d="M10.83 8.31v.022l-4.08 2.539-.005.003-.048.03-.012-.005c-.073.051-.15.101-.246.101-.217 0-.376-.165-.413-.369l-.027-.011V5.461l.009-.005c0-.009-.009-.014-.009-.022 0-.24.197-.435.44-.435.096 0 .174.049.247.101l.031-.017 4.129 2.569v.016a.42.42 0 0 1 .153.317.418.418 0 0 1-.169.325zm3.493 2.604a.986.986 0 0 1-.948.742 1 1 0 0 1-1-1 .98.98 0 0 1 .094-.412l-.019-.01C12.79 9.559 13 8.807 13 8a5 5 0 1 0-5 5c.766 0 1.484-.186 2.133-.494l.013.03a.975.975 0 0 1 .417-.097 1 1 0 0 1 1 1 .987.987 0 0 1-.77.954A6.936 6.936 0 0 1 8 14.999a7 7 0 1 1 7-7c0 1.048-.261 2.024-.677 2.915z" fill="#999999"></path>
|
||||||
</svg>
|
</svg>
|
||||||
'''.encode()
|
'''.encode()
|
||||||
|
|
||||||
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
Svg_icon_loading = '''<svg width="100%" height="100%" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
|
||||||
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
<stop stop-color="#03a9f4" stop-opacity="0" offset="0%"/>
|
||||||
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
<stop stop-color="#03a9f4" stop-opacity=".631" offset="63.146%"/>
|
||||||
<stop stop-color="#03a9f4" offset="100%"/>
|
<stop stop-color="#03a9f4" offset="100%"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<g fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<g transform="translate(1 1)">
|
<g transform="translate(1 1)">
|
||||||
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke="url(#a)" stroke-width="2">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</path>
|
</path>
|
||||||
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
<circle fill="#03a9f4" cx="36" cy="18" r="4">
|
||||||
<animateTransform
|
<animateTransform
|
||||||
attributeName="transform"
|
attributeName="transform"
|
||||||
type="rotate"
|
type="rotate"
|
||||||
from="0 18 18"
|
from="0 18 18"
|
||||||
to="360 18 18"
|
to="360 18 18"
|
||||||
dur="0.5s"
|
dur="0.5s"
|
||||||
repeatCount="indefinite" />
|
repeatCount="indefinite" />
|
||||||
</circle>
|
</circle>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>'''.encode()
|
</svg>'''.encode()
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
Actor = '''<a href="{href}" target="_blank" title="{title}" style="text-decoration: none;font-size: 12px;color: #999999;">{title}</a> '''
|
||||||
|
|
||||||
|
|
||||||
class CoverLabel(QLabel):
|
class CoverLabel(QLabel):
|
||||||
|
|
||||||
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
def __init__(self, cover_path, cover_title, video_url, *args, **kwargs):
|
||||||
super(CoverLabel, self).__init__(*args, **kwargs)
|
super(CoverLabel, self).__init__(*args, **kwargs)
|
||||||
# super(CoverLabel, self).__init__(
|
# super(CoverLabel, self).__init__(
|
||||||
# '<html><head/><body><img src="{0}"/></body></html>'.format(os.path.abspath(cover_path)), *args, **kwargs)
|
# '<html><head/><body><img src="{0}"/></body></html>'.format(os.path.abspath(cover_path)), *args, **kwargs)
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
self.setScaledContents(True)
|
self.setScaledContents(True)
|
||||||
self.setMinimumSize(220, 308)
|
self.setMinimumSize(220, 308)
|
||||||
self.setMaximumSize(220, 308)
|
self.setMaximumSize(220, 308)
|
||||||
self.cover_path = cover_path
|
self.cover_path = cover_path
|
||||||
self.cover_title = cover_title
|
self.cover_title = cover_title
|
||||||
self.video_url = video_url
|
self.video_url = video_url
|
||||||
self.setPixmap(QPixmap(cover_path))
|
self.setPixmap(QPixmap(cover_path))
|
||||||
|
|
||||||
def setCoverPath(self, path):
|
def setCoverPath(self, path):
|
||||||
self.cover_path = path
|
self.cover_path = path
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
super(CoverLabel, self).mouseReleaseEvent(event)
|
super(CoverLabel, self).mouseReleaseEvent(event)
|
||||||
webbrowser.open_new_tab(self.video_url)
|
webbrowser.open_new_tab(self.video_url)
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def paintEvent(self, event):
|
||||||
super(CoverLabel, self).paintEvent(event)
|
super(CoverLabel, self).paintEvent(event)
|
||||||
if hasattr(self, "cover_title") and self.cover_title != "":
|
if hasattr(self, "cover_title") and self.cover_title != "":
|
||||||
# 底部绘制文字
|
# 底部绘制文字
|
||||||
painter = QPainter(self)
|
painter = QPainter(self)
|
||||||
rect = self.rect()
|
rect = self.rect()
|
||||||
# 粗略字体高度
|
# 粗略字体高度
|
||||||
painter.save()
|
painter.save()
|
||||||
fheight = self.fontMetrics().height()
|
fheight = self.fontMetrics().height()
|
||||||
# 底部矩形框背景渐变颜色
|
# 底部矩形框背景渐变颜色
|
||||||
bottomRectColor = QLinearGradient(
|
bottomRectColor = QLinearGradient(
|
||||||
rect.width() / 2, rect.height() - 24 - fheight,
|
rect.width() / 2, rect.height() - 24 - fheight,
|
||||||
rect.width() / 2, rect.height())
|
rect.width() / 2, rect.height())
|
||||||
bottomRectColor.setSpread(QGradient.PadSpread)
|
bottomRectColor.setSpread(QGradient.PadSpread)
|
||||||
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
bottomRectColor.setColorAt(0, QColor(255, 255, 255, 70))
|
||||||
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
bottomRectColor.setColorAt(1, QColor(0, 0, 0, 50))
|
||||||
# 画半透明渐变矩形框
|
# 画半透明渐变矩形框
|
||||||
painter.setPen(Qt.NoPen)
|
painter.setPen(Qt.NoPen)
|
||||||
painter.setBrush(QBrush(bottomRectColor))
|
painter.setBrush(QBrush(bottomRectColor))
|
||||||
painter.drawRect(rect.x(), rect.height() - 24 -
|
painter.drawRect(rect.x(), rect.height() - 24 -
|
||||||
fheight, rect.width(), 24 + fheight)
|
fheight, rect.width(), 24 + fheight)
|
||||||
painter.restore()
|
painter.restore()
|
||||||
# 距离底部一定高度画文字
|
# 距离底部一定高度画文字
|
||||||
font = self.font() or QFont()
|
font = self.font() or QFont()
|
||||||
font.setPointSize(8)
|
font.setPointSize(8)
|
||||||
painter.setFont(font)
|
painter.setFont(font)
|
||||||
painter.setPen(Qt.white)
|
painter.setPen(Qt.white)
|
||||||
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
rect.setHeight(rect.height() - 12) # 底部减去一定高度
|
||||||
painter.drawText(rect, Qt.AlignHCenter |
|
painter.drawText(rect, Qt.AlignHCenter |
|
||||||
Qt.AlignBottom, self.cover_title)
|
Qt.AlignBottom, self.cover_title)
|
||||||
|
|
||||||
|
|
||||||
class ItemWidget(QWidget):
|
class ItemWidget(QWidget):
|
||||||
|
|
||||||
def __init__(self, cover_path, figure_info, figure_title,
|
def __init__(self, cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, manager, *args, **kwargs):
|
figure_score, figure_desc, figure_count, video_url, cover_url, img_path, manager, *args, **kwargs):
|
||||||
super(ItemWidget, self).__init__(*args, **kwargs)
|
super(ItemWidget, self).__init__(*args, **kwargs)
|
||||||
self.setMaximumSize(220, 420)
|
self.setMaximumSize(220, 420)
|
||||||
self.setMaximumSize(220, 420)
|
self.setMaximumSize(220, 420)
|
||||||
self.img_path = img_path
|
self.img_path = img_path
|
||||||
self.cover_url = cover_url
|
self.cover_url = cover_url
|
||||||
self._manager = manager
|
self._manager = manager
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
layout.setContentsMargins(10, 20, 10, 0)
|
layout.setContentsMargins(10, 20, 10, 0)
|
||||||
# 图片label
|
# 图片label
|
||||||
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
self.clabel = CoverLabel(cover_path, figure_info, video_url, self)
|
||||||
layout.addWidget(self.clabel)
|
layout.addWidget(self.clabel)
|
||||||
|
|
||||||
# 片名和分数
|
# 片名和分数
|
||||||
flayout = QHBoxLayout()
|
flayout = QHBoxLayout()
|
||||||
flayout.addWidget(QLabel(figure_title, self))
|
flayout.addWidget(QLabel(figure_title, self))
|
||||||
flayout.addItem(QSpacerItem(
|
flayout.addItem(QSpacerItem(
|
||||||
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
20, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
||||||
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
flayout.addWidget(QLabel(figure_score, self, styleSheet="color: red;"))
|
||||||
layout.addLayout(flayout)
|
layout.addLayout(flayout)
|
||||||
|
|
||||||
# 主演
|
# 主演
|
||||||
layout.addWidget(
|
layout.addWidget(
|
||||||
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
QLabel(figure_desc, self, styleSheet="color: #999999;", openExternalLinks=True))
|
||||||
|
|
||||||
# 播放量
|
# 播放量
|
||||||
blayout = QHBoxLayout()
|
blayout = QHBoxLayout()
|
||||||
count_icon = QSvgWidget(self)
|
count_icon = QSvgWidget(self)
|
||||||
count_icon.setMaximumSize(16, 16)
|
count_icon.setMaximumSize(16, 16)
|
||||||
count_icon.load(Svg_icon_play_sm)
|
count_icon.load(Svg_icon_play_sm)
|
||||||
blayout.addWidget(count_icon)
|
blayout.addWidget(count_icon)
|
||||||
blayout.addWidget(
|
blayout.addWidget(
|
||||||
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
QLabel(figure_count, self, styleSheet="color: #999999;"))
|
||||||
layout.addLayout(blayout)
|
layout.addLayout(blayout)
|
||||||
|
|
||||||
def setCover(self, path):
|
def setCover(self, path):
|
||||||
self.clabel.setCoverPath(path)
|
self.clabel.setCoverPath(path)
|
||||||
self.clabel.setPixmap(QPixmap(path))
|
self.clabel.setPixmap(QPixmap(path))
|
||||||
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
# self.clabel.setText('<img src="{0}"/>'.format(os.path.abspath(path)))
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
# 每个item控件的大小
|
# 每个item控件的大小
|
||||||
return QSize(220, 420)
|
return QSize(220, 420)
|
||||||
|
|
||||||
def event(self, event):
|
def event(self, event):
|
||||||
if isinstance(event, QPaintEvent):
|
if isinstance(event, QPaintEvent):
|
||||||
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
if event.rect().height() > 20 and hasattr(self, "clabel"):
|
||||||
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
if self.clabel.cover_path.find("pic_v.png") > -1: # 封面未加载
|
||||||
# print("start download img:", self.cover_url)
|
# print("start download img:", self.cover_url)
|
||||||
req = QNetworkRequest(QUrl(self.cover_url))
|
req = QNetworkRequest(QUrl(self.cover_url))
|
||||||
# 设置两个自定义属性方便后期reply中处理
|
# 设置两个自定义属性方便后期reply中处理
|
||||||
req.setAttribute(QNetworkRequest.User + 1, self)
|
req.setAttribute(QNetworkRequest.User + 1, self)
|
||||||
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
req.setAttribute(QNetworkRequest.User + 2, self.img_path)
|
||||||
self._manager.get(req) # 调用父窗口中的下载器下载
|
self._manager.get(req) # 调用父窗口中的下载器下载
|
||||||
return super(ItemWidget, self).event(event)
|
return super(ItemWidget, self).event(event)
|
||||||
|
|
||||||
|
|
||||||
class Window(QListWidget):
|
class Window(QListWidget):
|
||||||
|
|
||||||
Page = 0
|
Page = 0
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Window, self).__init__(*args, **kwargs)
|
super(Window, self).__init__(*args, **kwargs)
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
self.setFrameShape(self.NoFrame) # 无边框
|
self.setFrameShape(self.NoFrame) # 无边框
|
||||||
self.setFlow(self.LeftToRight) # 从左到右
|
self.setFlow(self.LeftToRight) # 从左到右
|
||||||
self.setWrapping(True) # 这三个组合可以达到和FlowLayout一样的效果
|
self.setWrapping(True) # 这三个组合可以达到和FlowLayout一样的效果
|
||||||
self.setResizeMode(self.Adjust)
|
self.setResizeMode(self.Adjust)
|
||||||
|
|
||||||
self._loadStart = False
|
self._loadStart = False
|
||||||
# 连接竖着的滚动条滚动事件
|
# 连接竖着的滚动条滚动事件
|
||||||
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
self.verticalScrollBar().actionTriggered.connect(self.onActionTriggered)
|
||||||
# 进度条
|
# 进度条
|
||||||
self.loadWidget = QSvgWidget(
|
self.loadWidget = QSvgWidget(
|
||||||
self, minimumHeight=120, minimumWidth=120, visible=False)
|
self, minimumHeight=120, minimumWidth=120, visible=False)
|
||||||
self.loadWidget.load(Svg_icon_loading)
|
self.loadWidget.load(Svg_icon_loading)
|
||||||
|
|
||||||
# 异步网络下载管理器
|
# 异步网络下载管理器
|
||||||
self._manager = QNetworkAccessManager(self)
|
self._manager = QNetworkAccessManager(self)
|
||||||
self._manager.finished.connect(self.onFinished)
|
self._manager.finished.connect(self.onFinished)
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
if self.Page == -1:
|
if self.Page == -1:
|
||||||
return
|
return
|
||||||
self._loadStart = True
|
self._loadStart = True
|
||||||
self.loadWidget.setVisible(True)
|
self.loadWidget.setVisible(True)
|
||||||
# 延迟一秒后调用目的在于显示进度条
|
# 延迟一秒后调用目的在于显示进度条
|
||||||
QTimer.singleShot(1000, self._load)
|
QTimer.singleShot(1000, self._load)
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
print("load url:", Url.format(self.Page * 30))
|
print("load url:", Url.format(self.Page * 30))
|
||||||
url = QUrl(Url.format(self.Page * 30))
|
url = QUrl(Url.format(self.Page * 30))
|
||||||
self._manager.get(QNetworkRequest(url))
|
self._manager.get(QNetworkRequest(url))
|
||||||
|
|
||||||
def onFinished(self, reply):
|
def onFinished(self, reply):
|
||||||
# 请求完成后会调用该函数
|
# 请求完成后会调用该函数
|
||||||
req = reply.request() # 获取请求
|
req = reply.request() # 获取请求
|
||||||
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
iwidget = req.attribute(QNetworkRequest.User + 1, None)
|
||||||
path = req.attribute(QNetworkRequest.User + 2, None)
|
path = req.attribute(QNetworkRequest.User + 2, None)
|
||||||
html = reply.readAll().data()
|
html = reply.readAll().data()
|
||||||
reply.deleteLater()
|
reply.deleteLater()
|
||||||
del reply
|
del reply
|
||||||
if iwidget and path and html:
|
if iwidget and path and html:
|
||||||
# 这里是图片下载完毕
|
# 这里是图片下载完毕
|
||||||
open(path, "wb").write(html)
|
open(path, "wb").write(html)
|
||||||
iwidget.setCover(path)
|
iwidget.setCover(path)
|
||||||
return
|
return
|
||||||
# 解析网页
|
# 解析网页
|
||||||
self._parseHtml(html)
|
self._parseHtml(html)
|
||||||
self._loadStart = False
|
self._loadStart = False
|
||||||
self.loadWidget.setVisible(False)
|
self.loadWidget.setVisible(False)
|
||||||
|
|
||||||
def _parseHtml(self, html):
|
def _parseHtml(self, html):
|
||||||
# encoding = chardet.detect(html) or {}
|
# encoding = chardet.detect(html) or {}
|
||||||
# html = html.decode(encoding.get("encoding","utf-8"))
|
# html = html.decode(encoding.get("encoding","utf-8"))
|
||||||
html = HTML(html)
|
html = HTML(html)
|
||||||
# 查找所有的li list_item
|
# 查找所有的li list_item
|
||||||
lis = html.xpath("//li[@class='list_item']")
|
lis = html.xpath("//li[@class='list_item']")
|
||||||
if not lis:
|
if not lis:
|
||||||
self.Page = -1 # 后面没有页面了
|
self.Page = -1 # 后面没有页面了
|
||||||
return
|
return
|
||||||
self.Page += 1
|
self.Page += 1
|
||||||
self._makeItem(lis)
|
self._makeItem(lis)
|
||||||
|
|
||||||
def _makeItem(self, lis):
|
def _makeItem(self, lis):
|
||||||
for li in lis:
|
for li in lis:
|
||||||
a = li.find("a")
|
a = li.find("a")
|
||||||
video_url = a.get("href") # 视频播放地址
|
video_url = a.get("href") # 视频播放地址
|
||||||
img = a.find("img")
|
img = a.find("img")
|
||||||
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
cover_url = "http:" + img.get("r-lazyload") # 封面图片
|
||||||
figure_title = img.get("alt") # 电影名
|
figure_title = img.get("alt") # 电影名
|
||||||
figure_info = a.find("div/span")
|
figure_info = a.find("div/span")
|
||||||
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
figure_info = "" if figure_info is None else figure_info.text # 影片信息
|
||||||
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
figure_score = "".join(li.xpath(".//em/text()")) # 评分
|
||||||
# 主演
|
# 主演
|
||||||
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
figure_desc = "<span style=\"font-size: 12px;\">主演:</span>" + \
|
||||||
"".join([Actor.format(**dict(fd.items()))
|
"".join([Actor.format(**dict(fd.items()))
|
||||||
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
for fd in li.xpath(".//div[@class='figure_desc']/a")])
|
||||||
# 播放数
|
# 播放数
|
||||||
figure_count = (
|
figure_count = (
|
||||||
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
li.xpath(".//div[@class='figure_count']/span/text()") or [""])[0]
|
||||||
path = "cache/{0}.jpg".format(
|
path = "cache/{0}.jpg".format(
|
||||||
os.path.splitext(os.path.basename(video_url))[0])
|
os.path.splitext(os.path.basename(video_url))[0])
|
||||||
cover_path = "pic_v.png"
|
cover_path = "pic_v.png"
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
cover_path = path
|
cover_path = path
|
||||||
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
iwidget = ItemWidget(cover_path, figure_info, figure_title,
|
||||||
figure_score, figure_desc, figure_count, video_url, cover_url, path, self._manager, self)
|
figure_score, figure_desc, figure_count, video_url, cover_url, path, self._manager, self)
|
||||||
item = QListWidgetItem(self)
|
item = QListWidgetItem(self)
|
||||||
item.setSizeHint(iwidget.sizeHint())
|
item.setSizeHint(iwidget.sizeHint())
|
||||||
self.setItemWidget(item, iwidget)
|
self.setItemWidget(item, iwidget)
|
||||||
|
|
||||||
def onActionTriggered(self, action):
|
def onActionTriggered(self, action):
|
||||||
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
# 这里要判断action=QAbstractSlider.SliderMove,可以避免窗口大小改变的问题
|
||||||
# 同时防止多次加载同一个url
|
# 同时防止多次加载同一个url
|
||||||
if action != QAbstractSlider.SliderMove or self._loadStart:
|
if action != QAbstractSlider.SliderMove or self._loadStart:
|
||||||
return
|
return
|
||||||
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
# 使用sliderPosition获取值可以同时满足鼠标滑动和拖动判断
|
||||||
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
if self.verticalScrollBar().sliderPosition() == self.verticalScrollBar().maximum():
|
||||||
# 可以下一页了
|
# 可以下一页了
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
super(Window, self).resizeEvent(event)
|
super(Window, self).resizeEvent(event)
|
||||||
self.loadWidget.setGeometry(
|
self.loadWidget.setGeometry(
|
||||||
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
int((self.width() - self.loadWidget.minimumWidth()) / 2),
|
||||||
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
int((self.height() - self.loadWidget.minimumHeight()) / 2),
|
||||||
self.loadWidget.minimumWidth(),
|
self.loadWidget.minimumWidth(),
|
||||||
self.loadWidget.minimumHeight()
|
self.loadWidget.minimumHeight()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.makedirs("cache", exist_ok=True)
|
os.makedirs("cache", exist_ok=True)
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
w = Window()
|
w = Window()
|
||||||
w.show()
|
w.show()
|
||||||
w.load()
|
w.load()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
|
@ -0,0 +1,16 @@
|
||||||
|
# QListView
|
||||||
|
|
||||||
|
## 1、删除自定义Item
|
||||||
|
[运行](DeleteCustomItem.py)
|
||||||
|
|
||||||
|
1. 删除item时先要通过`QListWidget.indexFromItem(item).row()`得到它的行数
|
||||||
|
2. 通过`takeItem`函数取出该Item并删除掉,`item = self.listWidget.takeItem(row)`
|
||||||
|
3. 移除item对应的自定义控件`self.listWidget.removeItemWidget(item)`
|
||||||
|
4. 如果是清空所有Item,可以通过循环删除,但是删除的时候行号一直是0即可,原因和删除list数组一样。
|
||||||
|
|
||||||
|
![CustomWidgetItem](ScreenShot/DeleteCustomItem.gif)
|
||||||
|
|
||||||
|
## 2、自定义可拖拽Item
|
||||||
|
[运行](DragDrop.py)
|
||||||
|
|
||||||
|
![CustomWidgetSortItem](ScreenShot/DragDrop.gif)
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |