PyQt实时编辑/绘制代表

时间:2018-02-13 04:21:34

标签: python pyqt real-time-updates qitemdelegate

所以我目前有一个在QListView中显示的视频剪辑列表,并创建了一个自定义委托,使用来自QStandardItemModel的数据为它们绘制预览缩略图。

最终,我希望能够在鼠标悬停在缩略图上时为其设置动画,以便他们播放剪辑的预览(仅显示几帧)。我想要两个版本。一个只是播放,另一个根据你的鼠标位置显示帧(进一步向左是剪辑的开头,当你向右移动时,它会擦过)。

现在我正试图弄清楚如何实现动画片段。我是否应该使用Delegate绘制框架并更新我的模型上的自定义框架数据,委托将使用它来知道要使用重新实现的绘制功能绘制哪些框架(已经具有绘制功能的框架)?或者这会太耗费资源?那么最好的方法是什么?我查看了编辑器小部件,但似乎似乎没有实时编辑模型数据/更新代理,而只是在完成编辑后。此外,我希望在mouseover上进行初始化,这似乎不是内置编辑触发器中的一个选项。

class AnimatedThumbDelegate(QItemDelegate):

def __init__(self,parent=None):
    super().__init__(parent)
    self.height=200
    self.width=self.height*1.77
    self.frames=10

def paint(self,painter,option,index):
    painter.save()

    image=index.data(FootageItem.FILMSTRIP)
    if not image.isNull():
        painter.drawPixmap(QRect(option.rect),image,QRect(image.width()/self.frames*index.data(FootageItem.FRAME),0,image.width()/self.frames,image.height()))

    painter.restore()

def sizeHint(self,option,index):
    return QSize(self.width,self.height)

此委托描绘了我用于预览帧的条形图像的一部分,并通过引用framedata来实现。 FootageItem只是一个QStandardItem类,它有助于构建我想为这些剪辑存储的数据,我只是在这里使用它的索引。它从我的模型中获取QPixmap。我使用的幻灯片图像如下所示:FilmstripExample

我可以使用编辑器小部件更新值并根据mouseMoveEvents强制重新绘制委托对象吗?然后我可以让编辑器出现在鼠标悬停上吗?或者我应该重新实现QListView以使用鼠标事件更新委托?或者还有另一种我没有发现的方式吗?

我创建了一个小部件,其行为大致如我希望更新框架部分能够工作,但是希望将其移植到委托类而不是QWidget,这样我就可以让它显示来自表的数据并利用它QT必须提供的模型/视图编程。

class ScrollingThumbnail(QWidget):

def __init__(self,parent,image,rect):
    super().__init__(parent)
    self.image=image
    self.paused=False
    self.frame=5
    self.frames=10
    self.bar=False
    self.vis=True
    self.thumbRect=rect
    self.setMouseTracking(True)

def leaveEvent(self):
    self.stop()

def mouseMoveEvent(self,e):
    if(e.pos().y()>self.height*0.6):
        self.bar=True
        self.pause()
        self.frame=int(e.pos().x()/(self.width/self.frames))
        self.repaint()
    elif self.paused:
        self.paused=False
        self.bar=False
        self.play()

def paintEvent(self,e):
    qP=QPainter()
    qP.begin(self)
    if self.vis:
        self.drawWidget(qP)
    qP.end()

def drawWidget(self,qP):
    if not self.image.isNull():
        qP.drawPixmap(self.thumbRect,self.image,QRect(self.image.width()/self.frames*self.frame,0,self.image.width()/self.frames,self.image.height()))
        if self.bar:
            pen=QPen(QColor(255,255,255,50),self.height/60,cap=Qt.RoundCap)
            qP.setPen(pen)
            off=self.height/20
            inc=((self.width-(off*2))/(self.frames-1))
            qP.drawLine(off,self.height-off-20,off+(inc)*(self.frame),self.height-off-20)

def play(self):
    if not self.paused:
        self.frame=(self.frame+1)%self.frames
        self.repaint()
        self.timer=threading.Timer(0.1,self.play)
        self.timer.start()

def pause(self):
    try:
        self.timer.cancel()
    except:
        pass
    self.paused=True

def stop(self):
    try:
        self.timer.cancel()
    except:
        pass
    self.frame=5
    self.repaint()

它使用线程计时器作为在循环中播放帧的廉价黑客方式。可能会更多地寻找更好的方法来实现这一目标(可能使用QThread?)另外还有一个关于期望行为的gif:i.imgur.com/aKoKs3m.gifv

干杯, 亚历

1 个答案:

答案 0 :(得分:0)

想想我找到了一种方法来使用一些信号并连接一些插槽。以下是概念类的证明。

创建此编辑器类,它将根据鼠标位置通过信号更新帧数据,然后在光标离开窗口小部件/项目时销毁编辑器。它并不需要一个痛苦的尝试,但我只是把一个放在那里,所以我可以看到编辑活动时。

class TestEditor(QWidget):

editingFinished = Signal()
updateFrame = Signal()

def __init__(self,parent):
    super().__init__(parent)
    self.setMouseTracking(True)
    self.frame=5

def mouseMoveEvent(self,e):
    currentFrame=int(e.pos().x()/(self.width()/10))
    if self.frame!=currentFrame:
        self.frame=currentFrame
        self.updateFrame.emit()

def leaveEvent(self,e):
    self.frame=5
    self.updateFrame.emit()
    self.editingFinished.emit()

def paintEvent(self,e):
    painter=QPainter()
    painter.begin(self)
    painter.setBrush(QColor(255,0,0,100))
    painter.drawRect(self.rect())
    painter.end()

我的代表将编辑器信号连接到一些基本功能。一个将关闭编辑器,另一个将更新我的模型上的帧数据。

class TestDelegate(QItemDelegate):
def __init__(self,parent):
    super().__init__(parent)
    self.height=200
    self.width=300

def createEditor(self,parent,option,index):
    editor=TestEditor(parent)
    editor.editingFinished.connect(self.commitEditor)
    editor.updateFrame.connect(self.updateFrames)
    return editor

def setModelData(self, editor, model, index):
    model.setData(index,editor.frame,TestData.FRAME)

def paint(self,painter,option,index):
    painter.save()
    painter.setBrush(QColor(0,255,0))
    painter.drawRect(option.rect)
    painter.setPen(QColor(255,255,255))
    painter.setFont(QFont('Ariel',20,QFont.Bold))
    painter.drawText(option.rect,Qt.AlignCenter,str(index.data(TestData.FRAME)))
    painter.restore()

def sizeHint(self,option,index):
    return QSize(self.width,self.height)

def commitEditor(self):
    editor = self.sender()
    self.closeEditor.emit(editor)

def updateFrames(self):
    editor=self.sender()
    self.commitData.emit(editor)

然后我所要做的就是启用鼠标跟踪并将“输入”信号连接到我的查看器上的“编辑()”插槽

    dataView=QListView()
    dataView.setViewMode(1)
    dataView.setMovement(0)
    dataView.setMouseTracking(True)
    dataView.entered.connect(dataView.edit)

还有一个用于构建测试数据项的超级简单类。基本上只需将空角色设置为5即帧数据。

class TestData(QStandardItem):
FRAME=15
def __init__(self,data=None):
    super().__init__(data)
    self.setData(5,self.FRAME)

它目前还没有完全发挥作用,包括一个自动擦除帧的“播放”功能。但我认为应该很容易设置。还需要弄清楚如何处理选择,因为当您移动某个项目时,编辑器会变为活动状态,从而阻止它被选中。目前正在考虑实施一个鼠标注册事件,然后将更新附加到我的查看器的选择模型。