示例#1
0
    def __init__(self, parent_control):
        super(TimelineWidget, self).__init__()

        self.parent_control = parent_control

        # self.setFocusPolicy(QtCore.Qt.StrongFocus)
        # self.grabKeyboard()
        self.setMouseTracking(True)
        self.setMinimumWidth(300000)
        # self.setMinimumHeight(30)

        # Timeline background color
        palette = self.palette()
        palette.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(palette)

        self._chartsColors = [
            QtGui.QColor(255, 0, 0),
            QtGui.QColor(0, 100, 0),
            QtGui.QColor(0, 0, 255),
            QtGui.QColor(100, 100, 0),
            QtGui.QColor(100, 0, 100),
            QtGui.QColor(0, 100, 100)
        ]
        self._charts = []
        self._tracks = [Track(parent=self)]

        self._scale = 1.0
        self._lastMouseY = None

        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False
        self._creating_event = False
        self._creating_event_start = None
        self._creating_event_end = None
        self._n_tracks = 1

        self._selected = None
        self._selected_track = 0
        self._pointer = TimelinePointer(0, self)

        # Video playback controls
        self._video_playing = False
        self._video_fps = None
        self._video_fps_min = None
        self._video_fps_max = None
        self._video_fps_inc = None
	def __init__(self, parent_control):
		super(TimelineWidget, self).__init__()

		self.parent_control = parent_control 

		# self.setFocusPolicy(QtCore.Qt.StrongFocus)
		# self.grabKeyboard()
		self.setMouseTracking(True)
		self.setMinimumWidth(300000)
		# self.setMinimumHeight(30)

		# Timeline background color
		palette = self.palette()
		palette.setColor(self.backgroundRole(), QtCore.Qt.white)
		self.setPalette(palette)

		self._chartsColors = [
			QtGui.QColor(255, 0, 0), QtGui.QColor(0, 100, 0),
			QtGui.QColor(0, 0, 255), QtGui.QColor(100, 100, 0),
			QtGui.QColor(100, 0, 100), QtGui.QColor(0, 100, 100)
		]
		self._charts = []
		self._tracks = [Track(parent=self)]

		self._scale = 1.0
		self._lastMouseY = None

		self._moving = False
		self._resizingBegin = False
		self._resizingEnd = False
		self._creating_event = False
		self._creating_event_start = None
		self._creating_event_end = None
		self._n_tracks = 1
	   
		self._selected = None
		self._selected_track = 0
		self._pointer = TimelinePointer(0, self)

		# Video playback controls
		self._video_playing = False
		self._video_fps = None
		self._video_fps_min = None
		self._video_fps_max = None
		self._video_fps_inc = None
示例#3
0
class TimelineWidget(QtGui.QWidget):
    """
    Timeline widget definition to be used in the ControlEventTimeline
    """

    _defautcolor = QtGui.QColor(100, 100, 255)

    def __init__(self, *args, **kwargs):
        super(TimelineWidget, self).__init__(*args, **kwargs)

        # self.setFocusPolicy(QtCore.Qt.StrongFocus)
        # self.grabKeyboard()
        self.setMouseTracking(True)
        self.setMinimumWidth(300000)
        # self.setMinimumHeight(30)

        # Timeline background color
        palette = self.palette()
        palette.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(palette)

        self._chartsColors = [
            QtGui.QColor(255, 0, 0), QtGui.QColor(0, 100, 0),
            QtGui.QColor(0, 0, 255), QtGui.QColor(100, 100, 0),
            QtGui.QColor(100, 0, 100), QtGui.QColor(0, 100, 100)
        ]
        self._charts = []
        self._tracks = [Track(parent=self)]

        self._scale = 1.0
        self._lastMouseY = None

        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False
        self._creating_event = False
        self._creating_event_start = None
        self._creating_event_end = None
        self._n_tracks = 1

        self._selected = None
        self._selected_track = 0
        self._pointer = TimelinePointer(0, self)


        # Video playback controls
        self._video_playing = False
        self._video_fps = None
        self._video_fps_min = None
        self._video_fps_max = None
        self._video_fps_inc = None


    ##########################################################################
    #### HELPERS/FUNCTIONS ###################################################
    ##########################################################################

    def x2frame(self, x): return int(x / self._scale)

    def frame2x(self, frame): return int(frame * self._scale)

    def removeSelected(self):
        if self._selected != None and not self._selected.lock:
            self._selected.remove()
            self._selected = None
            self.repaint()

    def lockSelected(self):
        if self._selected != None:
            self._selected.lock = not self._selected.lock
            self.repaint()

    def selectDelta(self, x, y):
        # Check if the timeline pointer was selected
        if y <= 20:
            if self._pointer.collide(x, y):
                return self._pointer
            else:
                return None
        # Check if the timeline periods were selected
        i = Track.whichTrack(y)
        if i>=len(self._tracks): return None
        
        return self._tracks[i].selectDelta(x,y)



    def __drawTrackLines(self, painter, start, end):
        # Draw only from pixel start to end
        painter.setPen(QtCore.Qt.DashLine)
        painter.setOpacity(0.3)
        # Draw horizontal lines
        #for track in range(0, self.numberoftracks + 1):
        #    y = (track * 34) + 18
        #    painter.drawLine(start, y, end, y)
        for i, track in enumerate(self._tracks): track.draw(painter, start,end, i)


        # Draw vertical lines
        for x in range(start - (start % 100), end, 100):
            painter.drawLine(x, 20, x, self.height())
            string = "%d" % (x / self._scale)
            boundtext = painter.boundingRect(QtCore.QRectF(), string)
            painter.drawText(x - boundtext.width() / 2, 15, string)
        painter.setOpacity(1.0)

        for index, track in enumerate(self._tracks): track.drawLabels(painter, index)
    

    def importchart_csv(self, csvfileobject):
        chart = TimelineChart(
            self, csvfileobject, color=self._chartsColors[len(self._charts)])
        self._charts.append(chart)

    def import_csv(self, csvfileobject):
        """
        Extracts info from a file object and stores it in memory in
        order to display it on the timeline.

        Refer to the `export` method to learn about input file format
        and structure.
        """
        # Clear previously stored info
        self._tracks = []
        self._selected = None

        for row in csvfileobject:
            if row[0] == "T":
                track = Track(parent=self)
                track.properties = row
                self._tracks.append(track)
            elif row[0] == "P":
                period = TimelineDelta(0, parent=self)
                period.properties = row
                
            

    def export_csv(self, csvfileobject):
        """
        Processes all timeline data in an arranged format to be written
        in a CSV file.


        Current file structure:
        =======================

        --- CSV FILE BEGIN ---
        Track info line
        Event info line
        Event info line
        ...
        Track info line
        Event info line
        Event info line
        ...
        Track info line
        Event info line
        Event info line
        ...
        --- CSV FILE END ---


        Track info line format:
        =======================

        | T | Total # of events in this track |  |  | Color | Label |


        Event info line format:
        =======================

        | P | Lock status | Begin frame | End frame | Comment | Color |  |
        """
        for index, track in enumerate(self._tracks):
            csvfileobject.writerow(track.properties)
            for delta in track.periods: 
                csvfileobject.writerow(delta.properties)


    def cleanCharts(self):
        self._charts = []
        self.repaint()

    def clean(self):
        self._charts = []
        self._selected = None
        for track in self._tracks: track.clear()
        del self._tracks[:]
        self._tracks = []
        self.repaint()

    def cleanLine(self):
        if self._selected is not None:
            self._tracks[self._selected.track].clear()
            self._selected = None
        else:
            QtGui.QMessageBox.about(
                self, "Error", "You must select a timebar first")
            return

    def addTrack(self):
        t = Track(parent=self)
        self._tracks.append(t)
        return t

    def addPeriod(self, value, track=0, color=None):
        """Adds an annotated interval."""
        begin, end, title = value
        period       = TimelineDelta(begin, end, title=title, parent=self, top=Track.whichTop(track))
        self._tracks[period.track].periods.append(period)


    ##########################################################################
    #### EVENTS ##############################################################
    ##########################################################################

    def paintEvent(self, e):
        super(TimelineWidget, self).paintEvent(e)

        painter = QtGui.QPainter()
        painter.begin(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setFont(QtGui.QFont('Decorative', 8))

        start = self._scroll.horizontalScrollBar().sliderPosition()
        end = start + self.parent().width() + 50

        #Draw graphs ##########################################################
        if len(self._charts) > 0:
            painter.setPen(QtCore.Qt.black)
            middle = self.height() // 2
            painter.setOpacity(0.1)
            painter.drawLine(start, middle, end, middle)

        for chart in self._charts:
            chart.draw(painter, start, end, 0, self.height())
        #End draw graph #######################################################

        self.__drawTrackLines(painter, start, end)
        
        for track in self._tracks: track.drawPeriods(painter, start, end)

        # Draw the selected element
        if self._selected != None:
            painter.setBrush(QtGui.QColor(255, 0, 0))
            self._selected.draw(painter, showvalues=True)

        # Draw the time pointer
        self._pointer.draw(painter)

        painter.end()

    def mouseDoubleClickEvent(self, event):
        if self._selected is not None and self._selected != self._pointer and self._selected.collide(event.x(), event.y()):
            self._selected.showEditWindow()
        elif event.y() > 20:
            top = (event.y() - 20) // 34
            y = top * 34 + 20
            x = event.x() / self._scale
            # time = TimelineDelta(x, x + 50 / self._scale, title='', top=y, parent=self)
            time = TimelineDelta(x, x + 10, title='', top=y, parent=self)
            self._tracks[time.track].periods.append(time)
            
            self._selected = time
            self._selected_track = self._selected.track
            self.repaint()

    def keyReleaseEvent(self, event):
        super(TimelineWidget, self).keyReleaseEvent(event)

        # Control video playback using the space bar to Play/Pause
        if event.key() == QtCore.Qt.Key_Space:
            self._video_playing = not self._video_playing
            self.playVideoEvent()
            self.repaint()

        # Increase video frame rate (FPS) by 5
        if event.key() == QtCore.Qt.Key_Plus:
            if self._video_fps_min <= (self.fps + self._video_fps_inc) <= self._video_fps_max:
                self.fps += self._video_fps_inc
            else:
                pass
            self.fpsChangeEvent()
            self.repaint()

        # Decrease video frame rate (FPS) by 5
        if event.key() == QtCore.Qt.Key_Minus:
            if self._video_fps_min <= (self.fps - self._video_fps_inc) <= self._video_fps_max:
                self.fps -= self._video_fps_inc
            else:
                pass
            self.fpsChangeEvent()
            self.repaint()

        # Jumps 20 seconds forward
        if event.key() == QtCore.Qt.Key_C:
            self._pointer.position += 10 * self.fps
            self.__checkPositionIsVisible(self.position)
            self.repaint()

        # Jumps 20 seconds backwards
        if event.key() == QtCore.Qt.Key_Z:
            self._pointer.position -= 10 * self.fps
            self.__checkPositionIsVisible(self.position)
            self.repaint()

        # Jumps 1 frame forward
        if event.key() == QtCore.Qt.Key_D:
            self._pointer.position += 1
            self.__checkPositionIsVisible(self.position)
            self.repaint()

        # Jumps 1 frame backwards
        if event.key() == QtCore.Qt.Key_A:
            self._pointer.position -= 1
            self.__checkPositionIsVisible(self.position)
            self.repaint()

        if self._selected is not None:
            modifier = int(event.modifiers())

            # Move the event (or the pointer) left
            if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Left:
                self._selected.move(-1, 0)
                self.repaint()

            # Move the event (or the pointer) right
            if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Right:
                self._selected.move(1, 0)
                self.repaint()

            if self._selected != self._pointer:
                # Delete the selected event
                if event.key() == QtCore.Qt.Key_Delete:
                    self.removeSelected()

                # Lock or unlock an event
                if event.key() == QtCore.Qt.Key_L:
                    self.lockSelected()

                # Move the event up
                if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Up:
                    self._selected.move(0, self._selected._top - 34)
                    self.repaint()

                # Move the event down
                if modifier == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Down:
                    self._selected.move(0, self._selected._top + 34)
                    self.repaint()

                # Move the event end left
                if modifier == int(QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key() == QtCore.Qt.Key_Left:
                    self._selected.moveEnd(-1)
                    self.repaint()

                # Move the event end right
                if modifier == int(QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier) and event.key() == QtCore.Qt.Key_Right:
                    self._selected.moveEnd(1)
                    self.repaint()

                # Move the event begin left
                if modifier == QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Left:
                    self._selected.moveBegin(-1)
                    self.repaint()

                # Move the event begin right
                if modifier == QtCore.Qt.ShiftModifier and event.key() == QtCore.Qt.Key_Right:
                    self._selected.moveBegin(1)
                    self.repaint()

        else:
            # Keybinds to create an event at current frame
            if event.key() == QtCore.Qt.Key_S and not self._creating_event:
                # Start
                self._creating_event_start = self._pointer.frame
                self._creating_event = True

                # TODO Add some indicator that an event is being recorded, like
                # using the track selector circle to become red

                return

            if event.key() == QtCore.Qt.Key_S and self._creating_event:
                # End, must be followed right after Start key and have no
                # effect otherwise
                self._creating_event_end = self._pointer.frame

                start = self._creating_event_start
                end = self._creating_event_end
                comment = ""

                if end > start:
                    self.addPeriod((start, end, comment), self._selected_track)
                    self.repaint()
                    self._creating_event = False
                else:
                    print("Event auto creation aborted.")
                    self._creating_event = False

    def mousePressEvent(self, event):
        # Select the track
        selected_track = Track.whichTrack(event.y())
        if selected_track<=len(self._tracks): self._selected_track = selected_track

        # Select the period bar
        self._selected = self.selectDelta(event.x(), event.y())
        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False

        if self._selected is not None:
            # Select the action
            if event.buttons() == QtCore.Qt.LeftButton:
                if self._selected.canSlideEnd(event.x(), event.y()):
                    self._resizingEnd = True
                elif self._selected.canSlideBegin(event.x(), event.y()):
                    self._resizingBegin = True
                elif self._selected.collide(event.x(), event.y()):
                    self._moving = True
        if event.y() <= 20 and not self._moving:
            self._pointer.position = event.x()

        self.repaint()

    def mouseMoveEvent(self, event):
        super(TimelineWidget, self).mouseMoveEvent(event)

        # Do nothing if no event bar is selected
        if self._selected is None:
            return

        # Set cursors
        if self._selected.canSlideBegin(event.x(), event.y()) or self._selected.canSlideEnd(event.x(), event.y()):
            self.setCursor(QtGui.QCursor(QtCore.Qt.SizeHorCursor))
        elif self._selected.collide(event.x(), event.y()):
            self.setCursor(QtGui.QCursor(QtCore.Qt.SizeAllCursor))
        else:
            self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))

        if self._selected is None:
            return

        if event.buttons() == QtCore.Qt.LeftButton:
            if self._lastMouseY is not None:
                diff = event.x() - self._lastMouseY
                if diff != 0:
                    if self._moving:
                        self._selected.move(diff, event.y())
                    elif self._resizingBegin:
                        self._selected.moveBegin(diff)
                    elif self._resizingEnd:
                        self._selected.moveEnd(diff)
                    self.repaint()
            self._lastMouseY = event.x()

    def mouseReleaseEvent(self, event):
        self._lastMouseY = None

    def trackInPosition(self, x, y):
        return (y - 30) // 34

    def playVideoEvent(self):
        pass

    def fpsChangeEvent(self):
        pass

    ##########################################################################
    #### PROPERTIES ##########################################################
    ##########################################################################

    def __checkPositionIsVisible(self, value):
        playerPos = self.frame2x(value)
        scrollLimit = self._scroll.horizontalScrollBar(
        ).sliderPosition() + self.parent().width() - 50
        if playerPos > scrollLimit:
            newPos = playerPos - self.parent().width() + 50
            self._scroll.horizontalScrollBar().setSliderPosition(newPos)
        if playerPos < self._scroll.horizontalScrollBar().sliderPosition():
            self._scroll.horizontalScrollBar().setSliderPosition(playerPos)

    @property
    def scroll(self): return self._scroll.horizontalScrollBar()

    @property
    def position(self): return self._pointer._position

    @position.setter
    def position(self, value):
        self._pointer._position = value
        #######################################################################
        # Check if the player position is inside the scroll
        # if is not in, update the scroll position
        self.__checkPositionIsVisible(value)
        #######################################################################
        self.repaint()

    @property
    def scale(self): return self._scale

    @scale.setter
    def scale(self, value):
        self._scale = value
        self.repaint()

    @property
    def color(self): return self._defautcolor

    @color.setter
    def color(self, value): self._defautcolor = value

    @property
    def numberoftracks(self): return len(self._tracks)#self._n_tracks

    @numberoftracks.setter
    def numberoftracks(self, value):
        #self._n_tracks = value
        if value<len(self._tracks):
            for i in range(value, self._tracks+1): self.addTrack()
        y = value * 34 + 20
        if y + 40 > self.height():
            self.setMinimumHeight(y + 40)

    # Video playback properties
    @property
    def isPlaying(self): return self._video_playing

    @property
    def fps(self): return self._video_fps

    @fps.setter
    def fps(self, value): self._video_fps = value
示例#4
0
    def __init__(self, parent_control):
        super(TimelineWidget, self).__init__()

        self.parent_control = parent_control

        # self.setFocusPolicy(QtCore.Qt.StrongFocus)
        # self.grabKeyboard()
        self.setMouseTracking(True)
        self.setMinimumWidth(300000)
        # self.setMinimumHeight(30)

        # Timeline background color
        palette = self.palette()
        palette.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(palette)

        self._chartsColors = [
            QColor(240, 163, 255),
            QColor(0, 117, 220),
            QColor(153, 63, 0),
            QColor(76, 0, 92),
            QColor(25, 25, 25),
            QColor(0, 92, 49),
            QColor(43, 206, 72),
            QColor(255, 204, 153),
            QColor(128, 128, 128),
            QColor(148, 255, 181),
            QColor(143, 124, 0),
            QColor(157, 204, 0),
            QColor(194, 0, 136),
            QColor(0, 51, 128),
            QColor(255, 164, 5),
            QColor(255, 168, 187),
            QColor(66, 102, 0),
            QColor(255, 0, 16),
            QColor(94, 241, 242),
            QColor(0, 153, 143),
            QColor(116, 10, 255),
            QColor(153, 0, 0),
            QColor(255, 255, 0),
            QColor(255, 80, 5)
        ]
        self._charts = []
        self._tracks = [Track(parent=self)]

        self._scale = 1.0
        self._lastMouseY = None
        self._mouse_current_pos = None

        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False
        self._creating_event = False
        self._creating_event_start = None
        self._creating_event_end = None
        self._n_tracks = 1

        self._selected = None
        self._selected_track = 0
        self._pointer = TimelinePointer(0, self)

        # Video playback controls
        self._video_playing = False
        self._video_fps = None
        self._video_fps_min = None
        self._video_fps_max = None
        self._video_fps_inc = None
示例#5
0
class TimelineWidget(QWidget):
    """
	Timeline widget definition to be used in the ControlEventTimeline
	"""

    _defautcolor = QColor(100, 100, 255)

    def __init__(self, parent_control):
        super(TimelineWidget, self).__init__()

        self.parent_control = parent_control

        # self.setFocusPolicy(QtCore.Qt.StrongFocus)
        # self.grabKeyboard()
        self.setMouseTracking(True)
        self.setMinimumWidth(300000)
        # self.setMinimumHeight(30)

        # Timeline background color
        palette = self.palette()
        palette.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(palette)

        self._chartsColors = [
            QColor(240, 163, 255),
            QColor(0, 117, 220),
            QColor(153, 63, 0),
            QColor(76, 0, 92),
            QColor(25, 25, 25),
            QColor(0, 92, 49),
            QColor(43, 206, 72),
            QColor(255, 204, 153),
            QColor(128, 128, 128),
            QColor(148, 255, 181),
            QColor(143, 124, 0),
            QColor(157, 204, 0),
            QColor(194, 0, 136),
            QColor(0, 51, 128),
            QColor(255, 164, 5),
            QColor(255, 168, 187),
            QColor(66, 102, 0),
            QColor(255, 0, 16),
            QColor(94, 241, 242),
            QColor(0, 153, 143),
            QColor(116, 10, 255),
            QColor(153, 0, 0),
            QColor(255, 255, 0),
            QColor(255, 80, 5)
        ]
        self._charts = []
        self._tracks = [Track(parent=self)]

        self._scale = 1.0
        self._lastMouseY = None
        self._mouse_current_pos = None

        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False
        self._creating_event = False
        self._creating_event_start = None
        self._creating_event_end = None
        self._n_tracks = 1

        self._selected = None
        self._selected_track = 0
        self._pointer = TimelinePointer(0, self)

        # Video playback controls
        self._video_playing = False
        self._video_fps = None
        self._video_fps_min = None
        self._video_fps_max = None
        self._video_fps_inc = None

    ##########################################################################
    #### HELPERS/FUNCTIONS ###################################################
    ##########################################################################

    def __add__(self, other):
        self.parent_control.__add__(other)
        return self

    def __sub__(self, other):
        self.parent_control.__sub__(other)
        return self

    def x2frame(self, x):
        return int(x / self._scale)

    def frame2x(self, frame):
        return int(frame * self._scale)

    def remove_track(self, track):
        self._tracks.remove(track)
        self.setMinimumHeight(Track.whichTop(len(self._tracks)))

    # self.repaint()

    def rename_graph(self, graph_index, newname):
        self.parent_control.rename_graph(graph_index, newname)

    def removeSelected(self):
        if self._selected != None and not self._selected.lock:
            self._selected.remove()
            self._selected = None
            self.repaint()

    def lockSelected(self):
        if self._selected != None:
            self._selected.lock = not self._selected.lock
            self.repaint()

    def selectDelta(self, x, y):
        # Check if the timeline pointer was selected
        if y <= 20:
            if self._pointer.collide(x, y):
                return self._pointer
            else:
                return None
        # Check if the timeline periods were selected
        i = Track.whichTrack(y)
        if i >= len(self._tracks):
            return None

        return self._tracks[i].selectDelta(x, y)

    def __drawTrackLines(self, painter, start, end):
        # Draw only from pixel start to end
        painter.setPen(QtCore.Qt.DashLine)
        painter.setOpacity(0.3)
        # Draw horizontal lines
        # for track in range(0, self.numberoftracks + 1):
        #    y = (track * 34) + 18
        #    painter.drawLine(start, y, end, y)
        for i, track in enumerate(self._tracks):
            track.draw(painter, start, end, i)

        # Draw vertical lines
        for x in range(start - (start % 100), end, 100):
            painter.drawLine(x, 20, x, self.height())
            string = "{0}".format(int(round(x / self._scale)))
            boundtext = painter.boundingRect(QtCore.QRectF(), string)
            painter.drawText(x - boundtext.width() / 2, 15, string)

            if self._video_fps:
                string = "{0}".format(
                    int(round((x / self._scale) * (1000.0 / self._video_fps))))
                boundtext = painter.boundingRect(QtCore.QRectF(), string)
                painter.drawText(x - boundtext.width() / 2, 30, string)

        painter.setOpacity(1.0)

        for index, track in enumerate(self._tracks):
            track.drawLabels(painter, index)

    def add_chart(self, name, data):
        chart = TimelineChart(self,
                              color=self._chartsColors[len(self._charts)],
                              name=name)
        chart.import_data(data)
        self._charts.append(chart)
        self.repaint()

    def importchart_csv(self, csvfileobject):
        chart = TimelineChart(self,
                              color=self._chartsColors[len(self._charts)])
        chart.import_csv(csvfileobject)
        self._charts.append(chart)

        chart.name = "undefined name {0}".format(str(len(self._charts)))
        self.repaint()
        return chart

    def import_csv(self, csvfileobject):
        """
		Extracts info from a file object and stores it in memory in
		order to display it on the timeline.

		Refer to the `export` method to learn about input file format
		and structure.
		"""
        # Clear previously stored info
        self._tracks = []
        self._selected = None

        for row in csvfileobject:
            if len(row) == 0: continue
            if row[0] == "T":
                track = self.add_track()
                track.properties = row
            elif row[0] == "P":
                period = self.add_period([0, 1, '-'])
                period.properties = row

    def export_csv(self, csvfileobject):
        """
		Processes all timeline data in an arranged format to be written
		in a CSV file.


		Current file structure:
		=======================

		--- CSV FILE BEGIN ---
		Track info line
		Event info line
		Event info line
		...
		Track info line
		Event info line
		Event info line
		...
		Track info line
		Event info line
		Event info line
		...
		--- CSV FILE END ---


		Track info line format:
		=======================

		| T | Total # of events in this track |  |  | Color | Label |


		Event info line format:
		=======================

		| P | Lock status | Begin frame | End frame | Comment | Color |  |
		"""
        for index, track in enumerate(self._tracks):
            csvfileobject.writerow(track.properties)
            for delta in track.periods:
                csvfileobject.writerow(delta.properties)

    def export_2_csv_matrix(self, csvfileobject):
        for index, track in enumerate(self._tracks):
            _, track_title, _ = track.properties
            for delta in track.periods:
                _, _, begin, end, delta_title, _, _ = delta.properties
                row = [track_title, begin, end, delta_title]

                csvfileobject.writerow(row)

    def cleanCharts(self):
        for graph in self.graphs:
            self -= 0
        self._charts = []
        self.repaint()

    def clean(self):
        for graph in self.graphs:
            self -= 0
        self._charts = []
        self._selected = None
        for track in self._tracks:
            track.clear()
        del self._tracks[:]
        self._tracks = []
        self.repaint()

    def cleanLine(self, track_index=None):
        if track_index is not None or self._selected is not None:
            track_index = track_index if track_index is not None else self._selected.track
            if len(self._tracks) > track_index:
                self._tracks[track_index].clear()
            self._selected = None
            self.repaint()
        else:
            QMessageBox.about(self, "Error", "You must select a timebar first")
            return

    def add_track(self):
        t = Track(parent=self)
        self._tracks.append(t)
        self.setMinimumHeight(Track.whichTop(len(self._tracks)))
        return t

    def add_period(self, value, row=0, color=None):
        """Adds an annotated interval."""
        begin, end, title = value
        period = TimelineDelta(begin,
                               end,
                               title=title,
                               parent=self,
                               top=Track.whichTop(row))
        self._tracks[period.track].periods.append(period)
        return period

    ##########################################################################
    #### EVENTS ##############################################################
    ##########################################################################

    def paintEvent(self, e):
        super(TimelineWidget, self).paintEvent(e)

        painter = QPainter()
        painter.begin(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setFont(QFont('Decorative', 8))

        start = self._scroll.horizontalScrollBar().sliderPosition()
        end = start + self.parent().width() + 50

        # Draw graphs ##########################################################
        if len(self._charts) > 0:
            painter.setPen(QtCore.Qt.black)
            middle = self.height() // 2
            painter.setOpacity(0.1)
            painter.drawLine(start, middle, end, middle)

        for chart in self._charts:
            chart.draw(painter, start, end, 0, self.height())
        # End draw graph #######################################################

        self.__drawTrackLines(painter, start, end)

        for track in self._tracks:
            track.drawPeriods(painter, start, end)

        # Draw the selected element
        if self._selected != None:
            painter.setBrush(QColor(255, 0, 0))
            self._selected.draw(painter, showvalues=True)

        # Draw the time pointer
        self._pointer.draw(painter)

        painter.end()

    def mouseDoubleClickEvent(self, event):
        if self._selected is not None and self._selected != self._pointer and self._selected.collide(
                event.x(), event.y()):
            self._selected.showEditWindow()
        elif event.y() > 20:
            top = (event.y() - 20) // 34
            y = top * 34 + 20
            x = event.x() / self._scale
            # time = TimelineDelta(x, x + 50 / self._scale, title='', top=y, parent=self)
            time = TimelineDelta(x, x + 10, title='', top=y, parent=self)
            self._tracks[time.track].periods.append(time)

            self._selected = time
            self._selected_track = self._selected.track
            self.repaint()

    def key_release_event(self, event):
        pass

    def keyReleaseEvent(self, event):
        super(TimelineWidget, self).keyReleaseEvent(event)
        self.key_release_event(event)

        if self._selected is not None:
            modifier = int(event.modifiers())

            # Move the event (or the pointer) left
            if modifier == QtCore.Qt.ControlModifier and event.key(
            ) == QtCore.Qt.Key_Left:
                self._selected.move(-1, 0)
                self.repaint()

            # Move the event (or the pointer) right
            if modifier == QtCore.Qt.ControlModifier and event.key(
            ) == QtCore.Qt.Key_Right:
                self._selected.move(1, 0)
                self.repaint()

            if self._selected != self._pointer:
                # Delete the selected event
                if event.key() == QtCore.Qt.Key_Delete:
                    self.removeSelected()

                # Lock or unlock an event
                if event.key() == QtCore.Qt.Key_L:
                    self.lockSelected()

                # Move the event up
                if modifier == QtCore.Qt.ControlModifier and event.key(
                ) == QtCore.Qt.Key_Up:
                    self._selected.move(0, self._selected._top - 34)
                    self.repaint()

                # Move the event down
                if modifier == QtCore.Qt.ControlModifier and event.key(
                ) == QtCore.Qt.Key_Down:
                    self._selected.move(0, self._selected._top + 34)
                    self.repaint()

                # Move the event end left
                if modifier == int(QtCore.Qt.ShiftModifier
                                   | QtCore.Qt.ControlModifier) and event.key(
                                   ) == QtCore.Qt.Key_Left:
                    self._selected.moveEnd(-1)
                    self.repaint()

                # Move the event end right
                if modifier == int(QtCore.Qt.ShiftModifier
                                   | QtCore.Qt.ControlModifier) and event.key(
                                   ) == QtCore.Qt.Key_Right:
                    self._selected.moveEnd(1)
                    self.repaint()

                # Move the event begin left
                if modifier == QtCore.Qt.ShiftModifier and event.key(
                ) == QtCore.Qt.Key_Left:
                    self._selected.moveBegin(-1)
                    self.repaint()

                # Move the event begin right
                if modifier == QtCore.Qt.ShiftModifier and event.key(
                ) == QtCore.Qt.Key_Right:
                    self._selected.moveBegin(1)
                    self.repaint()

        else:
            # Keybinds to create an event at current frame
            if event.key() == QtCore.Qt.Key_S and not self._creating_event:
                # Start
                self._creating_event_start = self._pointer.frame
                self._creating_event = True

                # TODO Add some indicator that an event is being recorded, like
                # using the track selector circle to become red

                return

            if event.key() == QtCore.Qt.Key_S and self._creating_event:
                # End, must be followed right after Start key and have no
                # effect otherwise
                self._creating_event_end = self._pointer.frame

                start = self._creating_event_start
                end = self._creating_event_end
                comment = ""

                if end > start:
                    self.add_period((start, end, comment),
                                    self._selected_track)
                    self.repaint()
                    self._creating_event = False
                else:
                    self._creating_event = False

    def mousePressEvent(self, event):
        # Select the track
        selected_track = Track.whichTrack(event.y())
        if selected_track <= len(self._tracks):
            self._selected_track = selected_track

        # Select the period bar
        self._selected = self.selectDelta(event.x(), event.y())
        self._moving = False
        self._resizingBegin = False
        self._resizingEnd = False

        if self._selected is not None:
            # Select the action
            if event.buttons() == QtCore.Qt.LeftButton:
                if self._selected.canSlideEnd(event.x(), event.y()):
                    self._resizingEnd = True
                elif self._selected.canSlideBegin(event.x(), event.y()):
                    self._resizingBegin = True
                elif self._selected.collide(event.x(), event.y()):
                    self._moving = True
        if event.y() <= 20 and not self._moving:
            self._pointer.position = self.x2frame(event.x())

        self.repaint()

    def mouseMoveEvent(self, event):
        super(TimelineWidget, self).mouseMoveEvent(event)
        self.parent_control.mouse_moveover_timeline_event(event)

        self._mouse_current_pos = event.x(), event.y()

        # Do nothing if no event bar is selected
        if self._selected is None:
            return

        # Set cursors
        if self._selected.canSlideBegin(
                event.x(), event.y()) or self._selected.canSlideEnd(
                    event.x(), event.y()):
            self.setCursor(QCursor(QtCore.Qt.SizeHorCursor))
        elif self._selected.collide(event.x(), event.y()):
            self.setCursor(QCursor(QtCore.Qt.SizeAllCursor))
        else:
            self.setCursor(QCursor(QtCore.Qt.ArrowCursor))

        if self._selected is None:
            return

        if event.buttons() == QtCore.Qt.LeftButton:
            if self._lastMouseY is not None:
                diff = event.x() - self._lastMouseY
                if diff != 0:
                    if self._moving:
                        self._selected.move(diff, event.y())
                    elif self._resizingBegin:
                        self._selected.moveBegin(diff)
                    elif self._resizingEnd:
                        self._selected.moveEnd(diff)
                    self.repaint()
            self._lastMouseY = event.x()

    def mouseReleaseEvent(self, event):
        self._lastMouseY = None

    def trackInPosition(self, x, y):
        return (y - 30) // 34

    def fpsChangeEvent(self):
        pass

    ##########################################################################
    #### PROPERTIES ##########################################################
    ##########################################################################

    def __checkPositionIsVisible(self, value):
        playerPos = self.frame2x(value)
        scrollLimit = self._scroll.horizontalScrollBar().sliderPosition(
        ) + self.parent().width() - 50
        if playerPos > scrollLimit:
            newPos = playerPos - self.parent().width() + 50
            self._scroll.horizontalScrollBar().setSliderPosition(newPos)
        if playerPos < self._scroll.horizontalScrollBar().sliderPosition():
            self._scroll.horizontalScrollBar().setSliderPosition(playerPos)

    @property
    def scroll(self):
        return self._scroll.horizontalScrollBar()

    @property
    def position(self):
        return self._pointer._position

    @position.setter
    def position(self, value):
        self._pointer.position = value
        #######################################################################
        # Check if the player position is inside the scroll
        # if is not in, update the scroll position
        self.__checkPositionIsVisible(value)
        #######################################################################
        self.repaint()

    @property
    def scale(self):
        return self._scale

    @scale.setter
    def scale(self, value):
        self._scale = value
        self.repaint()

    @property
    def color(self):
        return self._defautcolor

    @color.setter
    def color(self, value):
        self._defautcolor = value

    @property
    def tracks(self):
        return self._tracks

    @property
    def numberoftracks(self):
        return len(self._tracks)  # self._n_tracks

    @numberoftracks.setter
    def numberoftracks(self, value):
        # self._n_tracks = value
        if value < len(self._tracks):
            for i in range(value, self._tracks + 1):
                self.add_track()
        y = value * 34 + 20
        if y + 40 > self.height():
            self.setMinimumHeight(y + 40)

    # Video playback properties
    @property
    def isPlaying(self):
        return self._video_playing

    @property
    def fps(self):
        return self._video_fps

    @fps.setter
    def fps(self, value):
        self._video_fps = value

    @property
    def graphs_properties(self):
        return self.parent_control._graphs_prop_win

    @property
    def current_mouseover_track(self):
        if self._mouse_current_pos is None: return None
        return self.trackInPosition(*self._mouse_current_pos)

    @property
    def graphs(self):
        return self._charts