python - PyQt - 拖放到不同的 QLabel 小部件中
问题描述
我正在使用 Windows、Python 3.6、OpenCV3 和 PyQt5。我有一个有两个 QLabel 小部件(label1
和label2
)的主窗口。我想将不同的视频文件拖放到两个 QLabel 小部件中。我的脚本显示两个视频文件中的每一个的第一帧。
如果我将文件拖入然后释放鼠标,则会显示第一个视频帧( 不是我想要的)。
label1
label2
label1
如果我将文件拖入然后释放鼠标, 则会显示第一个视频帧(所需效果)。
label1
label2
label2
如果我将文件拖入然后释放鼠标, 则会显示第一个视频帧(所需效果)。
label2
label1
label1
label2
无论我是拖动文件还是通过 label1
. 建议?
import sys, cv2
from PyQt5.QtWidgets import QApplication, QLabel, QFrame, QMainWindow
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import Qt
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(200, 300, 800, 600)
self.setAcceptDrops(True)
self.setMouseTracking(True)
self.label1 = QLabel(self)
self.label1.move(10, 10)
self.label1.resize(780, 280)
self.label1.setFrameShape(QFrame.Box)
self.label1.setAcceptDrops(True)
self.label2 = QLabel(self)
self.label2.move(10, 310)
self.label2.resize(780, 280)
self.label2.setFrameShape(QFrame.Box)
self.label2.setAcceptDrops(True)
self.label1.setText("Label 1")
self.label2.setText("Label 2")
self.show()
def dragEnterEvent(self, e):
if e.mimeData().hasUrls:
e.accept()
else:
e.ignore()
def dropEvent(self, e):
if e.mimeData().hasUrls:
e.accept()
for url in e.mimeData().urls():
if self.label1.underMouse():
fname = str(url.toLocalFile())
self.openFile1(fname)
elif self.label2.underMouse():
fname = str(url.toLocalFile())
self.openFile2(fname)
else:
e.ignore()
def openFile1(self, filename):
self.cap1 = cv2.VideoCapture(str(filename))
self.cap1.set(cv2.CAP_PROP_POS_FRAMES, 0)
width = self.cap1.get(cv2.CAP_PROP_FRAME_WIDTH)
height = self.cap1.get(cv2.CAP_PROP_FRAME_HEIGHT)
ret, frame = self.cap1.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
pix = QPixmap.fromImage(img)
pix = self.scalePix(self.label1, pix, width, height)
self.label1.setPixmap(pix)
def openFile2(self, filename):
self.cap2 = cv2.VideoCapture(str(filename))
self.cap2.set(cv2.CAP_PROP_POS_FRAMES, 0)
width = self.cap2.get(cv2.CAP_PROP_FRAME_WIDTH)
height = self.cap2.get(cv2.CAP_PROP_FRAME_HEIGHT)
ret, frame = self.cap2.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
pix = QPixmap.fromImage(img)
pix = self.scalePix(self.label1, pix, width, height)
self.label2.setPixmap(pix)
def scalePix(self, label, p, width, height):
window_width = label.width()
ratio = height / width
window_height = int(ratio * window_width)
window_height = label.height()
window_width = int(1 / ratio * window_height)
p = p.scaledToWidth(window_width, Qt.SmoothTransformation)
p = p.scaledToHeight(window_height, Qt.SmoothTransformation)
return p
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
解决方案
文档表明underMouse()
在拖放过程中可能会失败:
布尔 QWidget::underMouse() 常量
如果小部件在鼠标光标下,则返回 true;否则返回假。
此值在拖放操作期间未正确更新。
所以最好不要使用,相反我们可以创建一个自定义的 QLabel 来实现拖放:
import sys, cv2
from PyQt5 import QtCore, QtGui, QtWidgets
class OpenCVLabel(QtWidgets.QLabel):
def __init__(self, *args, **kwargs):
super(OpenCVLabel, self).__init__(*args, **kwargs)
self.setFrameShape(QtWidgets.QFrame.Box)
self.setAcceptDrops(True)
def dragEnterEvent(self, e):
if e.mimeData().hasUrls():
e.accept()
else:
e.ignore()
def dropEvent(self, e):
if e.mimeData().hasUrls():
e.accept()
for url in e.mimeData().urls():
self.openFile(url.toLocalFile())
else:
e.ignore()
def openFile(self, filename):
cap = cv2.VideoCapture(str(filename))
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
ret, frame = cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
pix = QtGui.QPixmap.fromImage(img)
pix = pix.scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
self.setPixmap(pix)
class Example(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
label1 = OpenCVLabel("label1")
label2 = OpenCVLabel("label2")
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addWidget(label1)
lay.addWidget(label2)
self.resize(780, 560)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
推荐阅读
- c# - 如何在 2D 游戏中使用 WASD 键移动精灵?
- javascript - Angular-如何在 Angular html 模板中使用扩展运算符
- c# - 为什么 C# TcpListener 需要 IP 地址
- python - 如何使用 Flask 和 PyMongo 创建合适的过滤器?
- hyperledger-fabric - Hyperledger - 资源管理器:并非所有对等点都显示在资源管理器中
- javascript - 我正在尝试获取我的 json 站点以启用 API,但出现错误“Unexpected token < in JSON at position 0”
- ios - [iOS 13] 上 StatusBar 中的子视图数据
- sql - SQL 包含“俄亥俄州”不返回“俄亥俄州”
- proget - ProGet 资产注册表无法识别 API 密钥
- javascript - 如果 CSS 中存在 3 个以上的元素,如何在新行中自动格式化?