So I currently have a list of video clips being displayed in a QListView and have created a custom Delegate to paint preview thumbnails for them using data from a QStandardItemModel.
Ultimately I want to be able to animate the thumbnails as you mouse over them so they play a preview of the clip (by showing only a couple frames). I want two versions of this. One that just plays, and another that shows frames based upon your mouse position (further towards the left is the beginning of the clip, and as you move towards the right, it scrubs through).
Right now I am trying to figure out how to implement the animation piece. Should I be using the Delegate to draw frames and be updating a custom frame data on my model that the Delegate will use to know what frames to draw with a reimplemented paint function (already have the framework of a paint function there)? Or will this be too resource intensive? And then what is the best way to do that? I looked into editor widgets, but those seem to be not seem to edit model data/update delegates in realtime and instead only upon finishing editing. Also I would like this to initialize on mouseover and that doesn't seem to be an option in the built-in edit triggers.
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)
This delegate paints a portion of a strip image that I am using for preview frames and does so by referencing framedata. FootageItem is just a QStandardItem class that helps construct the data I want to store for these clips and I am just using indices from it here. It fetches a QPixmap from my model. The filmstrip image I am using looks like this:
Can I use an editor widget to update values and force a repaint on the delegate object based upon mouseMoveEvents? And then can I make editors appear on mouseovers? Or should I look at reimplementing QListView to update the delegate with mouse events? Or is there another way I haven't discovered?
I made a widget that behaves roughly how I would want the updating frames portion of this to work, but was hoping to port it over to a delegate class instead of a QWidget so I could have it display data from a table and utilize the Model/View programming that QT has to offer.
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()
It uses a threading timer as a cheap hacked way of playing frames in a loop. Probably will look more into better ways to achieve this (possibly using QThread?) Also a gif of it in action as far as desired behavior: i.imgur.com/aKoKs3m.gifv
Cheers, Alex