timeSliderをスケールしたい　続

さて前回からの続きですね。

https://qiita.com/9boz/items/d78e74f92d779b8a6ea6

しばらく使って頂いた後にリクエストが来ました。

- windowが小さいうえに、端っこにでるので見失いがち
- 現在のフレームを中心に、frameRange一定のフレーム幅に設定したい
- そのフレーム幅は任意に変えたい


順番にやってく
--


**windowが小さいうえに、端っこにでるので見失いがち**

これに関しては別途検証していた内容を反映させてみました。

https://qiita.com/9boz/items/648da0d81a5745d3a1db


**現在のフレームを中心に、frameRange一定のフレーム幅に設定したい**

これについてもう少し詳しく詰めてみますと、

- 基本的に前後均等で問題ない
- 現在のフレームの前・後に余白が足りない分は反対側に回す

という事だそうでした。

それを元に考えるとこんな感じでしょうか？

```python

def templateExtendTimeSlider(expandFrame):
    currentTime = cmds.currentTime(q = True)
    startLimit = cmds.playbackOptions(q = True, animationStartTime =True)
    endLimit = cmds.playbackOptions(q = True, animationEndTime=True)
    
    newStartTime = currentTime + 1 - (expandFrame / 2)
    newEndTime = currentTime + (expandFrame / 2)
    
    offset = 0
    if newStartTime < startLimit:
        offset = startLimit - newStartTime

    elif newEndTime > endLimit:
        offset = endLimit - newEndTime
        
    newStartTime = newStartTime + offset
    newEndTime = newEndTime + offset
    
    resetTimeSlider(newStartTime,newEndTime)

    return newStartTime, newEndTime

```

まとめる
--

もろもろまとめるとこんな風になりました。

```python
import maya.cmds as cmds

try:
    from PySide6 import QtWidgets,QtGui,QtCore
except:
    from PySide2 import QtWidgets,QtGui,QtCore

import math

objectName = "timeSliderScaleTool"

def resetTimeSlider(scaledStart,scaledEnd):

    startLimit = cmds.playbackOptions(q = True, animationStartTime =True)
    endLimit = cmds.playbackOptions(q = True, animationEndTime=True)
    
    curStartTime = cmds.playbackOptions(q = True, min =True)
    curEndTime = cmds.playbackOptions(q = True, max =True)

    if scaledStart == None or scaledEnd == None:
        cmds.playbackOptions(min = startLimit, max = endLimit)
        return curStartTime,curEndTime
    
    elif curStartTime == startLimit and curEndTime == endLimit:
        cmds.playbackOptions(min = scaledStart, max = scaledEnd)
        return scaledStart,scaledEnd
    
    else:
        cmds.playbackOptions(min = startLimit, max = endLimit)
        return curStartTime,curEndTime
        
def scaleTimeSlider(wheelDelta):
    currentTime = cmds.currentTime(q = True)

    startLimit = cmds.playbackOptions(q = True, animationStartTime =True)
    endLimit = cmds.playbackOptions(q = True, animationEndTime=True)
    
    newStartTime = cmds.playbackOptions(q = True, min =True)
    newEndTime = cmds.playbackOptions(q = True, max =True)
    
    startSideRange = (currentTime - newStartTime)
    endSideRange = (newEndTime - currentTime)    
    scaleFactor =  abs(wheelDelta) * 0.01    

    if wheelDelta > 0:                
        newStartTime = math.floor(currentTime - startSideRange * scaleFactor)        
        newEndTime = math.ceil(currentTime + endSideRange * scaleFactor)
    else:
        newStartTime = math.floor(currentTime - startSideRange / scaleFactor)        
        newEndTime = math.ceil(currentTime + endSideRange / scaleFactor)
    
    if newStartTime == currentTime:
        newStartTime = currentTime - 1
    
    if newEndTime == currentTime:
        newEndTime = newEndTime + 1

    newStartTime = max(int(newStartTime),startLimit)
    newEndTime = min(int(newEndTime),endLimit)
        
    cmds.playbackOptions(min = newStartTime, max = newEndTime)

    return newStartTime, newEndTime


def templateExtendTimeSlider(expandFrame):
    currentTime = cmds.currentTime(q = True)
    startLimit = cmds.playbackOptions(q = True, animationStartTime =True)
    endLimit = cmds.playbackOptions(q = True, animationEndTime=True)
    
    newStartTime = currentTime + 1 - (expandFrame / 2)
    newEndTime = currentTime + (expandFrame / 2)
    
    offset = 0
    if newStartTime < startLimit:
        offset = startLimit - newStartTime

    elif newEndTime > endLimit:
        offset = endLimit - newEndTime
        
    newStartTime = newStartTime + offset
    newEndTime = newEndTime + offset
    
    resetTimeSlider(newStartTime,newEndTime)

    return newStartTime, newEndTime

##-----------------------------------------------------------------
def getTopLevelWidget(name):
    for widget in QtWidgets.QApplication.topLevelWidgets():    
        if widget.objectName() == name:
            return widget
    return None


def windowCheck(object,parent,arrowMulti = False):
    
    if arrowMulti:        
        allChildrenName = []
        renameObject = str(object)

        for child in parent.children():
            allChildrenName.append(child.objectName())
        
        i = 1
        
        while renameObject in allChildrenName:
            renameObject = object + str(i)
            i = i+1
                
        return renameObject
            
    else:
        for child in parent.children():
            if child.objectName() == object:
                child.deleteLater()

        return object


class ApplyFunc(object):
    def __init__(self, func, *args, **kwargs):
        self.__func = func
        self.__args = args
        self.__kwargs = kwargs
                        
    def __call__(self, *args, **kwargs):
        error = None        
        try:            
            cmds.undoInfo(openChunk =True)

            self.__func(*self.__args, **self.__kwargs)			
                        
        except Exception as e:
            import traceback
            traceback.print_exc()

        finally:
            cmds.undoInfo(closeChunk =True)


class TimeScaleBtn(QtWidgets.QPushButton):
    def __init__(self,parent,*args , **kwargs):
        super(TimeScaleBtn,self).__init__(*args)

        self.clicked.connect(self.toggleTimeRange)
        self.parent = parent    
        
        self.clickPoint = None
        self.pastPoint = None
        

    def wheelEvent(self, event):
        wheelAngle = event.angleDelta().y()
        self.scaledStartTime,self.scaledEndTime = scaleTimeSlider(wheelAngle)
    
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MiddleButton:
            self.clickPoint = event.pos()
            self.pastPoint = event.pos()
            self.parent.curStartTime = cmds.playbackOptions(q = True, min =True)
            self.parent.curEndTime = cmds.playbackOptions(q = True, max =True)
        else:
            super(TimeScaleBtn, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):

        if self.clickPoint != None:
            delta = event.pos() - self.pastPoint
            offset = (abs(delta.x())//10)

            if offset != 0:
                if delta.x() > 0:
                    offset = offset * -1

                self.parent.scaledStartTime,self.parent.scaledEndTime = scaleTimeSlider(120*offset)
                self.pastPoint = event.pos()
        else:
            super(TimeScaleBtn, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if event.button() == QtCore.Qt.MiddleButton:
            self.clickPoint = None
        else:
            super(TimeScaleBtn, self).mouseReleaseEvent(event)

    def toggleTimeRange(self):        
        self.parent.scaledStartTime,self.parent.scaledEndTime = resetTimeSlider(self.parent.scaledStartTime,self.parent.scaledEndTime)

class TimeSettingDialog(QtWidgets.QDialog):
    def __init__(self,curValue,*args, **kwargs):
        super(TimeSettingDialog,self).__init__(*args,**kwargs)
        self.setValue = curValue
        mainLayout = QtWidgets.QVBoxLayout(self)

        self.inputFld = QtWidgets.QSpinBox()
        self.inputFld.setMinimum(1)
        self.inputFld.setMaximum(9999)
        self.inputFld.setSingleStep(1)
        self.inputFld.setValue(curValue)

        mainLayout.addWidget(self.inputFld)

        cmdLayout = QtWidgets.QHBoxLayout()
        mainLayout.addLayout(cmdLayout)

        applyBtn = QtWidgets.QPushButton("set")
        applyBtn.setFixedSize(80,20)
        cmdLayout.addWidget(applyBtn)
        applyBtn.clicked.connect(self.apply)

        cancelBtn = QtWidgets.QPushButton("cancel")
        cancelBtn.setFixedSize(80,20)
        cmdLayout.addWidget(cancelBtn)
        cancelBtn.clicked.connect(self.reject)

    def apply(self):
        self.setValue = self.inputFld.value()
        self.accept()


class TimeExpandBtn(QtWidgets.QPushButton):
    
    def __init__(self,parent,*args, **kwargs):
        super(TimeExpandBtn,self).__init__(*args,**kwargs)        
        self.btnDict = {}
        self.parent = parent

    def mouseReleaseEvent(self, event):
        if event.button() == QtCore.Qt.RightButton:
            self.createTempalteRangeMenu(event)
        else:
            super(TimeExpandBtn, self).mouseReleaseEvent(event)

    def createTempalteRangeMenu(self,event):        
        bookMarkMenu = QtWidgets.QMenu()
        Act = QtWidgets.QAction("expandFrame setting")
        bookMarkMenu.addAction(Act)
        Act.triggered.connect(self.parent.editFrameExpandValue)
        bookMarkMenu.exec_(self.mapToGlobal(event.pos()))

class MainGUI(QtWidgets.QMainWindow):
    def __init__(self,*args, **kwargs):
        super(MainGUI,self).__init__(*args,**kwargs)
        
        self.scaledStartTime = None
        self.scaledEndTime = None        
        self.curStartTime = None
        self.curEndTime = None
        self.expandFrame = 100
        self.setWindowFlags(QtCore.Qt.Tool)
        
        self.mainWidget = QtWidgets.QWidget(self)
        mainLayout = QtWidgets.QHBoxLayout(self.mainWidget)
        btn = TimeScaleBtn(self)
        btn.setText("< scroll scale >")
        btn.setMinimumSize(90,20)
        btn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Expanding)
        mainLayout.addWidget(btn)
        
        self.extendFrameBtn = TimeExpandBtn(self)
        self.extendFrameBtn.setMinimumSize(90,20)
        self.extendFrameBtn.setSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Expanding)

        self.extendFrameBtn.clicked.connect(ApplyFunc(self.templateToggleTimeRange))
        mainLayout.addWidget(self.extendFrameBtn)

        self.setCentralWidget(self.mainWidget)
        self.loadOption()

    def templateToggleTimeRange(self):
        self.saveOption()
        self.scaledStartTime,self.scaledEndTime = templateExtendTimeSlider(int(self.expandFrame))

    def editFrameExpandValue(self):
        dialog = TimeSettingDialog(curValue=self.expandFrame,parent= self)
        
        if dialog.exec_():
            self.setFrameExpandValue(dialog.setValue)
            
    def setFrameExpandValue(self,frame):
        self.expandFrame = frame
        self.extendFrameBtn.setText(str(frame))

    def saveOption(self):    
        cmds.optionVar(stringValue = [objectName,self.expandFrame])

    def loadOption(self):    
        if cmds.optionVar(exists = objectName):
            self.expandFrame = int(cmds.optionVar(q = objectName))
        
        self.setFrameExpandValue(self.expandFrame)

def main():
    desktop       = QtWidgets.QApplication.desktop()
    mayaMainWindow = getTopLevelWidget('MayaWindow')
    activeScreen  = desktop.screenNumber(mayaMainWindow)
    desktopRect = desktop.screenGeometry(activeScreen)
        
    windowCenter = mayaMainWindow.rect().center()
    windowCheck(objectName,mayaMainWindow)

    mainGUI = MainGUI(mayaMainWindow)
    mainGUI.setObjectName(objectName)
    mainGUI.setWindowTitle(objectName)
    mainGUI.resize(50,40)

    mainGUI.show()
    mainGUI.move(desktopRect.topLeft() + mayaMainWindow.rect().topLeft() + windowCenter)

main()
```

各状況でテストすると

95f で実行すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/bdf0f6e8-1f25-c1d7-3da2-acc04c8e89f2.png)

25f で実行すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/2926dc0a-7f5e-ee58-2b5f-9092d236d547.png)


180f で実行すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1484234/a76cb93f-ecd0-199f-e7c9-2885969649c0.png)

想定通りの挙動になりました。
フレーム幅の設定は右クリックでメニュー -> ダイアログ　という風にしました。

念のため
--

まとめて書いてはいますが、
プロトタイプを作って、触ってもらって、フィードバックを返してもらう　を数回繰り返しています。
特に今回は個人向けのツールなので、その人の趣味になるべく寄せる努力をしました。