├── AnimRef.zip ├── AnimRef ├── Contents │ ├── 2020 │ │ └── animref.py │ ├── 2021 │ │ └── animref.py │ ├── 2025 │ │ └── animref.py │ ├── 2020.ms │ ├── 2021.ms │ ├── 2025.ms │ ├── converter │ │ └── video_to_sequence.exe │ ├── icons │ │ ├── e_frame.png │ │ ├── icon.png │ │ ├── load_images.png │ │ ├── loop.png │ │ ├── n_frame.png │ │ ├── no_data.png │ │ ├── p_frame.png │ │ ├── pause.png │ │ ├── play.png │ │ ├── s_frame.png │ │ ├── seq.png │ │ └── soft-icon.png │ └── interface │ │ └── interface.ui └── PackageContents.xml ├── LICENSE ├── One-Click Installation.bat ├── README.md └── screen ├── converter.gif └── interface.jpg /AnimRef.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef.zip -------------------------------------------------------------------------------- /AnimRef/Contents/2020.ms: -------------------------------------------------------------------------------- 1 | python.Init() 2 | 3 | macroScript AnimRef category:"CGcenter" buttonText: "AnimRef" 4 | ( 5 | python.ExecuteFile(getDir #publicExchangeStoreInstallPath + "\AnimRef\Contents\2020\animref.py") 6 | ) 7 | -------------------------------------------------------------------------------- /AnimRef/Contents/2020/animref.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import urllib 4 | 5 | import MaxPlus 6 | from PySide2 import QtCore, QtGui 7 | from PySide2 import QtWidgets 8 | from PySide2.QtCore import QFile 9 | from PySide2.QtGui import QIcon, QColor 10 | from PySide2.QtUiTools import QUiLoader 11 | from PySide2.QtWidgets import QMessageBox, QVBoxLayout, QSizePolicy, QFileDialog, QPushButton, QDialog, QWidget, QLabel 12 | from pymxs import runtime as mxs 13 | 14 | 15 | # Define Dialog Window 16 | class AnimRef(QDialog): 17 | def __init__(self, parent=MaxPlus.GetQMaxMainWindow()): 18 | super(AnimRef, self).__init__(parent) 19 | 20 | self.init() 21 | 22 | self.setWindowFlags(QtCore.Qt.WindowType.Window) 23 | self.resize(720, 460) 24 | self.setWindowTitle("AnimRef v1.5.2") 25 | 26 | self.defineVariables() 27 | self.defineSignals() 28 | self.defineIcons() 29 | self.start() 30 | 31 | self.setWindowIcon(self.icon) 32 | self.timer = QtCore.QTimer(self) 33 | 34 | def downloadConverter(self): 35 | 36 | converter_path = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter', 'video_to_sequence.exe') 37 | download_path = "https://raw.githubusercontent.com/ShirzadBh/AnimRef/main/AnimRef/Contents/converter/video_to_sequence.exe" 38 | 39 | try: 40 | 41 | # urllib.request.urlretrieve(download_path, converter_path) 42 | urllib.urlretrieve(download_path, converter_path) 43 | 44 | self.ui.state.setStyleSheet('''color : #98fc03; 45 | font-size: 12px; 46 | font-family:"Comic Sans MS", cursive, sans-serif;''') 47 | 48 | self.ui.state.setText("video_to_sequence.exe is ready!") 49 | self.time_counting = True 50 | self.startTime() 51 | except: 52 | self.ui.state.setStyleSheet('''color : #fc5203; 53 | font-size: 12px; 54 | font-family:"Comic Sans MS", cursive, sans-serif;''') 55 | 56 | self.ui.state.setText("Download failed...") 57 | self.time_counting = True 58 | self.startTime() 59 | 60 | def convertedExist(self): 61 | 62 | FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe') 63 | path = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter', 'video_to_sequence.exe') 64 | converterPath = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter') 65 | 66 | if os.path.exists(path): 67 | subprocess.call([FILEBROWSER_PATH, converterPath]) 68 | else: 69 | msgBox = QMessageBox() 70 | msgBox.setText("Do You Want To Download video_to_sequence.exe") 71 | msgBox.setWindowTitle("Sequence Converter") 72 | msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) 73 | 74 | returnValue = msgBox.exec_() 75 | 76 | if returnValue == QMessageBox.Ok: 77 | self.downloadConverter() 78 | 79 | def startTime(self): 80 | if self.time_counting: 81 | self.timer.timeout.connect(self.stopTime) 82 | self.timer.start(3000) 83 | 84 | def stopTime(self): 85 | self.ui.state.clear() 86 | self.timer.stop() 87 | self.time_counting = False 88 | 89 | def init(self): 90 | 91 | self.dir = mxs.getDir(mxs.name('publicExchangeStoreInstallPath')) 92 | ui_file = QFile( 93 | os.path.join(self.dir, 'AnimRef', 'Contents', 'interface', 'interface.ui')) 94 | ui_file.open(QFile.ReadOnly) 95 | self.ui = QUiLoader().load(ui_file, self) 96 | ui_file.close() 97 | layout = QtWidgets.QHBoxLayout() 98 | layout.addWidget(self.ui) 99 | layout.setMargin(4) 100 | self.setLayout(layout) 101 | 102 | def start(self): 103 | self.ui.viewer.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) 104 | self.pixmap = self.no_image.scaled(400, 200, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation) 105 | self.ui.viewer.setPixmap(self.pixmap) 106 | mxs.registerTimeCallback(self.changeTime) 107 | 108 | def changeOpacity(self): 109 | self.opacity = float(self.ui.sl_opacity.value()) 110 | self.setWindowOpacity(self.opacity / 100) 111 | 112 | def defineVariables(self): 113 | self.last_valid_frame = 0 114 | self.time_counting = False 115 | self.out_of_range = False 116 | self.pixmap = None 117 | self.isLoaded = False 118 | self.current_time = int(mxs.currentTime) 119 | self.time_shift = self.ui.sb_time_shift.value() 120 | self.time = self.current_time + self.time_shift 121 | self.height = self.ui.viewer.height() 122 | self.width = self.ui.viewer.width() 123 | self.images_backup = {} 124 | self.images = {} 125 | self.opacity = 1 126 | self.images_path = None 127 | self.last_frame = 0 128 | self.previous_frame = 0 129 | self.no_image = QtGui.QPixmap( 130 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'no_data.png')) 131 | 132 | def defineSignals(self): 133 | # self.ui.timeSlider.valueChanged.connect(self.goToFrame) 134 | self.ui.btn_converter.clicked.connect(self.convertedExist) 135 | 136 | self.ui.sl_opacity.valueChanged.connect(self.changeOpacity) 137 | self.ui.btn_load_seq.clicked.connect(self.load_seq) 138 | self.ui.sb_time_shift.valueChanged.connect(self.updateTimeShift) 139 | self.ui.btn_n_frame.clicked.connect(self.nextFrame) 140 | self.ui.btn_p_frame.clicked.connect(self.previousFrame) 141 | self.ui.btn_play.clicked.connect(self.playFrame) 142 | self.ui.btn_s_frame.clicked.connect(self.startFrame) 143 | self.ui.btn_e_frame.clicked.connect(self.endFrame) 144 | 145 | def nextFrame(self): 146 | mxs.stopAnimation() 147 | self.ui.btn_play.setChecked(False) 148 | mxs.sliderTime += 1 149 | self.ui.sb_time_shift.setEnabled(True) 150 | 151 | def previousFrame(self): 152 | mxs.stopAnimation() 153 | self.ui.btn_play.setChecked(False) 154 | mxs.sliderTime -= 1 155 | self.ui.sb_time_shift.setEnabled(True) 156 | 157 | def playFrame(self): 158 | if mxs.isAnimPlaying(): 159 | self.ui.sb_time_shift.setEnabled(True) 160 | mxs.stopAnimation() 161 | 162 | elif not mxs.isAnimPlaying(): 163 | self.ui.sb_time_shift.setEnabled(False) 164 | mxs.playAnimation() 165 | 166 | def startFrame(self): 167 | # mxs.execute("max time start") 168 | mxs.stopAnimation() 169 | mxs.sliderTime = self.time_shift 170 | self.ui.btn_play.setIcon(self.play_icon) 171 | self.ui.btn_play.setChecked(False) 172 | self.ui.sb_time_shift.setEnabled(True) 173 | 174 | def endFrame(self): 175 | # mxs.execute("max time end") 176 | mxs.stopAnimation() 177 | mxs.sliderTime = self.time_shift + (self.last_frame - 1) 178 | self.ui.btn_play.setIcon(self.play_icon) 179 | self.ui.btn_play.setChecked(False) 180 | self.ui.sb_time_shift.setEnabled(True) 181 | 182 | def updateTimeShift(self): 183 | self.time_shift = self.ui.sb_time_shift.value() 184 | self.changeTime() 185 | 186 | def load_seq(self): 187 | self.height = self.ui.viewer.height() 188 | self.width = self.ui.viewer.width() 189 | 190 | try: 191 | 192 | fname = list(QFileDialog.getOpenFileNames(self, 'Select Range OF Sequences', 193 | filter="Images (*.jpeg *.jpg *.png *.bmp)", )) 194 | 195 | if len(fname[0]) > 0: 196 | self.images = {} 197 | self.images_path = os.path.dirname(os.path.realpath(fname[0][0])) 198 | 199 | self.test = {} 200 | for i in range(int(len(fname[0]))): 201 | self.images[i] = QtGui.QPixmap(fname[0][i]) 202 | self.test[i] = fname[0][i] 203 | 204 | self.last_frame = len(fname[0]) 205 | self.isLoaded = True 206 | self.ui.btn_play.setEnabled(True) 207 | self.ui.btn_s_frame.setEnabled(True) 208 | self.ui.btn_p_frame.setEnabled(True) 209 | self.ui.btn_n_frame.setEnabled(True) 210 | self.ui.btn_e_frame.setEnabled(True) 211 | self.ui.sb_time_shift.setEnabled(True) 212 | self.ui.btn_loop.setEnabled(True) 213 | self.status_1() 214 | self.changeTime() 215 | 216 | else: 217 | self.status_3() 218 | self.changeTime() 219 | 220 | except: 221 | self.status_3() 222 | self.changeTime() 223 | 224 | def changeTime(self): 225 | if self.isLoaded: 226 | 227 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 228 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 229 | 230 | try: 231 | self.pixmap = self.images[int(mxs.currentTime) - self.time_shift].scaled(self.width, self.height, 232 | QtCore.Qt.KeepAspectRatio, 233 | QtCore.Qt.FastTransformation) 234 | self.ui.viewer.setPixmap(self.pixmap) 235 | self.ui.viewer.repaint() 236 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 237 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 238 | self.out_of_range = False 239 | self.last_valid_frame = int(mxs.currentTime) - self.time_shift 240 | except: 241 | out = True 242 | is_playing = mxs.isAnimPlaying() 243 | if self.isLoaded and not self.ui.btn_loop.isChecked(): 244 | self.status_2() 245 | 246 | if self.isLoaded: 247 | 248 | if self.ui.btn_loop.isChecked(): 249 | mxs.stopAnimation() 250 | mxs.sliderTime = self.time_shift 251 | if is_playing and out: 252 | mxs.playAnimation() 253 | self.out_of_range = True 254 | 255 | def defineIcons(self): 256 | self.icon = QtGui.QIcon( 257 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'icon.png')) 258 | self.play_icon = QtGui.QIcon( 259 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'play.png')) 260 | self.n_frame_icon = QtGui.QIcon( 261 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'n_frame.png')) 262 | self.p_frame_icon = QtGui.QIcon( 263 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'p_frame.png')) 264 | self.s_frame_icon = QtGui.QIcon( 265 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 's_frame.png')) 266 | self.e_frame_icon = QtGui.QIcon( 267 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'e_frame.png')) 268 | self.load_images_icon = QtGui.QIcon( 269 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'load_images.png')) 270 | self.loop_icon = QtGui.QIcon( 271 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'loop.png')) 272 | self.pause_icon = QtGui.QIcon( 273 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'pause.png')) 274 | self.seq_icon = QtGui.QIcon( 275 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'seq.png')) 276 | 277 | self.ui.btn_play.setIcon(self.play_icon) 278 | self.ui.btn_n_frame.setIcon(self.n_frame_icon) 279 | self.ui.btn_p_frame.setIcon(self.p_frame_icon) 280 | self.ui.btn_s_frame.setIcon(self.s_frame_icon) 281 | self.ui.btn_e_frame.setIcon(self.e_frame_icon) 282 | self.ui.btn_load_seq.setIcon(self.load_images_icon) 283 | self.ui.btn_loop.setIcon(self.loop_icon) 284 | self.ui.btn_converter.setIcon(self.seq_icon) 285 | 286 | def wheelEvent(self, event): 287 | if self.isLoaded: 288 | mxs.sliderTime += (event.delta() / 120) 289 | 290 | def resizeEvent(self, event): 291 | self.updateFrame() 292 | self.changeTime() 293 | 294 | def closeEvent(self, event): 295 | mxs.unregisterTimeCallback(self.changeTime) 296 | self.timer.stop() 297 | 298 | def updateFrame(self): 299 | if self.isLoaded: 300 | self.height = self.ui.viewer.height() 301 | self.width = self.ui.viewer.width() 302 | 303 | self.pixmap = self.images[self.last_valid_frame].scaled(self.width, self.height, QtCore.Qt.KeepAspectRatio, 304 | QtCore.Qt.FastTransformation) 305 | self.ui.viewer.setPixmap(self.pixmap) 306 | 307 | def status_1(self): 308 | self.ui.state.clear() 309 | self.ui.state.setStyleSheet('''color : #98fc03; 310 | font-size: 12px; 311 | font-family:"Comic Sans MS", cursive, sans-serif;''') 312 | 313 | self.ui.state.setText("{0} images were imported".format(self.last_frame)) 314 | self.time_counting = True 315 | self.startTime() 316 | 317 | def status_2(self): 318 | self.ui.state.clear() 319 | self.ui.state.setStyleSheet('''color : #fcbe03; 320 | font-size: 12px; 321 | font-family:"Comic Sans MS", cursive, sans-serif;''') 322 | 323 | self.ui.state.setText("Out of range") 324 | self.time_counting = True 325 | self.startTime() 326 | 327 | def status_3(self): 328 | self.ui.state.clear() 329 | self.ui.state.setStyleSheet('''color : #fc5203; 330 | font-size: 12px; 331 | font-family:"Comic Sans MS", cursive, sans-serif;''') 332 | 333 | self.ui.state.setText("Import was canceled") 334 | self.time_counting = True 335 | self.startTime() 336 | 337 | 338 | def main(): 339 | dlg = AnimRef() 340 | dlg.show() 341 | 342 | 343 | if __name__ == '__main__': 344 | main() 345 | -------------------------------------------------------------------------------- /AnimRef/Contents/2021.ms: -------------------------------------------------------------------------------- 1 | python.Init() 2 | 3 | macroScript AnimRef category:"CGcenter" buttonText: "AnimRef" 4 | ( 5 | python.ExecuteFile(getDir #publicExchangeStoreInstallPath + "\AnimRef\Contents\2021\animref.py") 6 | ) 7 | -------------------------------------------------------------------------------- /AnimRef/Contents/2021/animref.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import urllib.request 4 | 5 | from PySide2 import QtCore, QtGui 6 | from PySide2.QtCore import QFile 7 | from PySide2.QtGui import QIcon, QColor 8 | from PySide2.QtUiTools import QUiLoader 9 | from PySide2.QtWidgets import QMessageBox, QVBoxLayout, QSizePolicy, QFileDialog, QPushButton, QDialog, QWidget, QLabel 10 | from pymxs import runtime as mxs 11 | 12 | 13 | class AnimRef(QDialog): 14 | def __init__(self, parent=QWidget.find(mxs.windows.getMAXHWND())): 15 | QDialog.__init__(self, parent) 16 | 17 | self.init() 18 | 19 | self.setWindowFlags(QtCore.Qt.WindowType.Window) 20 | self.resize(720, 460) 21 | self.setWindowTitle("AnimRef v1.5.2") 22 | 23 | self.defineVariables() 24 | self.defineSignals() 25 | self.defineIcons() 26 | self.start() 27 | 28 | self.setWindowIcon(self.icon) 29 | 30 | self.timer = QtCore.QTimer(self) 31 | 32 | def downloadConverter(self): 33 | 34 | converter_path = os.path.join(self.dir, 'ApplicationPlugins', 'AnimRef', 'Contents', 'converter', 35 | 'video_to_sequence.exe') 36 | download_path = "https://raw.githubusercontent.com/ShirzadBh/AnimRef/main/AnimRef/Contents/converter/video_to_sequence.exe" 37 | 38 | try: 39 | urllib.request.urlretrieve(download_path, converter_path) 40 | self.ui.state.setStyleSheet('''color : #98fc03; 41 | font-size: 12px; 42 | font-family:"Comic Sans MS", cursive, sans-serif;''') 43 | 44 | self.ui.state.setText("video_to_sequence.exe is ready!") 45 | self.time_counting = True 46 | self.startTime() 47 | except: 48 | self.ui.state.setStyleSheet('''color : #fc5203; 49 | font-size: 12px; 50 | font-family:"Comic Sans MS", cursive, sans-serif;''') 51 | 52 | self.ui.state.setText("Download failed...") 53 | self.time_counting = True 54 | self.startTime() 55 | 56 | def convertedExist(self): 57 | 58 | FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe') 59 | path = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter', 'video_to_sequence.exe') 60 | converterPath = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter') 61 | 62 | if os.path.exists(path): 63 | subprocess.run([FILEBROWSER_PATH, converterPath]) 64 | else: 65 | msgBox = QMessageBox() 66 | msgBox.setText("Do You Want To Download video_to_sequence.exe") 67 | msgBox.setWindowTitle("Sequence Converter") 68 | msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) 69 | 70 | returnValue = msgBox.exec() 71 | if returnValue == QMessageBox.Ok: 72 | self.downloadConverter() 73 | 74 | def startTime(self): 75 | if self.time_counting: 76 | self.timer.timeout.connect(self.stopTime) 77 | self.timer.start(3000) 78 | 79 | def stopTime(self): 80 | self.ui.state.clear() 81 | self.timer.stop() 82 | self.time_counting = False 83 | 84 | def init(self): 85 | 86 | self.dir = mxs.getDir(mxs.name('publicExchangeStoreInstallPath')) 87 | loader = QUiLoader() 88 | ui_file_path = os.path.join(self.dir, 'AnimRef', 'Contents', 'interface', 'interface.ui') 89 | ui_file = QFile(ui_file_path) 90 | ui_file.open(QFile.ReadOnly) 91 | self.ui = loader.load(ui_file, self) 92 | ui_file.close() 93 | layout = QVBoxLayout() 94 | layout.addWidget(self.ui) 95 | layout.setMargin(4) 96 | self.setLayout(layout) 97 | 98 | def start(self): 99 | self.ui.viewer.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) 100 | self.pixmap = self.no_image.scaled(400, 200, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation) 101 | self.ui.viewer.setPixmap(self.pixmap) 102 | mxs.registerTimeCallback(self.changeTime) 103 | 104 | def changeOpacity(self): 105 | self.opacity = self.ui.sl_opacity.value() / 100 106 | self.setWindowOpacity(self.opacity) 107 | 108 | def defineVariables(self): 109 | self.last_valid_frame = 0 110 | self.time_counting = False 111 | self.out_of_range = False 112 | self.pixmap = None 113 | self.isLoaded = False 114 | self.current_time = int(mxs.currentTime) 115 | self.time_shift = self.ui.sb_time_shift.value() 116 | self.time = self.current_time + self.time_shift 117 | self.height = self.ui.viewer.height() 118 | self.width = self.ui.viewer.width() 119 | self.images_backup = {} 120 | self.images = {} 121 | self.opacity = 1 122 | self.images_path = None 123 | self.last_frame = 0 124 | self.previous_frame = 0 125 | self.no_image = QtGui.QPixmap( 126 | os.path.join(os.path.dirname(os.path.realpath(__file__)) + "\\icons\\no_data.png")) 127 | 128 | def defineSignals(self): 129 | self.ui.btn_converter.clicked.connect(self.convertedExist) 130 | 131 | # self.ui.timeSlider.valueChanged.connect(self.goToFrame) 132 | self.ui.sl_opacity.valueChanged.connect(self.changeOpacity) 133 | self.ui.btn_load_seq.clicked.connect(self.load_seq) 134 | self.ui.sb_time_shift.valueChanged.connect(self.updateTimeShift) 135 | self.ui.btn_n_frame.clicked.connect(self.nextFrame) 136 | self.ui.btn_p_frame.clicked.connect(self.previousFrame) 137 | self.ui.btn_play.clicked.connect(self.playFrame) 138 | self.ui.btn_s_frame.clicked.connect(self.startFrame) 139 | self.ui.btn_e_frame.clicked.connect(self.endFrame) 140 | 141 | def nextFrame(self): 142 | mxs.stopAnimation() 143 | self.ui.btn_play.setChecked(False) 144 | mxs.sliderTime += 1 145 | self.ui.sb_time_shift.setEnabled(True) 146 | 147 | def previousFrame(self): 148 | mxs.stopAnimation() 149 | self.ui.btn_play.setChecked(False) 150 | mxs.sliderTime -= 1 151 | self.ui.sb_time_shift.setEnabled(True) 152 | 153 | def playFrame(self): 154 | if mxs.isAnimPlaying(): 155 | self.ui.sb_time_shift.setEnabled(True) 156 | mxs.stopAnimation() 157 | 158 | elif not mxs.isAnimPlaying(): 159 | self.ui.sb_time_shift.setEnabled(False) 160 | mxs.playAnimation() 161 | 162 | def startFrame(self): 163 | # mxs.execute("max time start") 164 | mxs.stopAnimation() 165 | mxs.sliderTime = self.time_shift 166 | self.ui.btn_play.setIcon(self.play_icon) 167 | self.ui.btn_play.setChecked(False) 168 | self.ui.sb_time_shift.setEnabled(True) 169 | 170 | def endFrame(self): 171 | # mxs.execute("max time end") 172 | mxs.stopAnimation() 173 | mxs.sliderTime = self.time_shift + (self.last_frame - 1) 174 | self.ui.btn_play.setIcon(self.play_icon) 175 | self.ui.btn_play.setChecked(False) 176 | self.ui.sb_time_shift.setEnabled(True) 177 | 178 | def updateTimeShift(self): 179 | self.time_shift = self.ui.sb_time_shift.value() 180 | self.changeTime() 181 | 182 | def load_seq(self): 183 | self.height = self.ui.viewer.height() 184 | self.width = self.ui.viewer.width() 185 | 186 | try: 187 | 188 | fname = list(QFileDialog.getOpenFileNames(self, 'Select Range OF Sequences', 189 | filter="Images (*.jpeg *.jpg *.png *.bmp)", )) 190 | 191 | if len(fname[0]) > 0: 192 | self.images = {} 193 | self.images_path = os.path.dirname(os.path.realpath(fname[0][0])) 194 | 195 | self.test = {} 196 | for i in range(int(len(fname[0]))): 197 | self.images[i] = QtGui.QPixmap(fname[0][i]) 198 | self.test[i] = fname[0][i] 199 | 200 | self.last_frame = len(fname[0]) 201 | self.isLoaded = True 202 | self.ui.btn_play.setEnabled(True) 203 | self.ui.btn_s_frame.setEnabled(True) 204 | self.ui.btn_p_frame.setEnabled(True) 205 | self.ui.btn_n_frame.setEnabled(True) 206 | self.ui.btn_e_frame.setEnabled(True) 207 | self.ui.sb_time_shift.setEnabled(True) 208 | self.ui.btn_loop.setEnabled(True) 209 | self.status_1() 210 | self.changeTime() 211 | 212 | else: 213 | self.status_3() 214 | self.changeTime() 215 | 216 | except: 217 | self.status_3() 218 | self.changeTime() 219 | 220 | def changeTime(self): 221 | if self.isLoaded: 222 | 223 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 224 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 225 | 226 | try: 227 | self.pixmap = self.images[int(mxs.currentTime) - self.time_shift].scaled(self.width, self.height, 228 | QtCore.Qt.KeepAspectRatio, 229 | QtCore.Qt.FastTransformation) 230 | self.ui.viewer.setPixmap(self.pixmap) 231 | self.ui.viewer.repaint() 232 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 233 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 234 | self.out_of_range = False 235 | self.last_valid_frame = int(mxs.currentTime) - self.time_shift 236 | except: 237 | out = True 238 | is_playing = mxs.isAnimPlaying() 239 | if self.isLoaded and not self.ui.btn_loop.isChecked(): 240 | self.status_2() 241 | 242 | if self.isLoaded: 243 | 244 | if self.ui.btn_loop.isChecked(): 245 | mxs.stopAnimation() 246 | mxs.sliderTime = self.time_shift 247 | if is_playing and out: 248 | mxs.playAnimation() 249 | self.out_of_range = True 250 | 251 | def defineIcons(self): 252 | self.icon = QtGui.QIcon( 253 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'icon.png')) 254 | self.play_icon = QtGui.QIcon( 255 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'play.png')) 256 | self.n_frame_icon = QtGui.QIcon( 257 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'n_frame.png')) 258 | self.p_frame_icon = QtGui.QIcon( 259 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'p_frame.png')) 260 | self.s_frame_icon = QtGui.QIcon( 261 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 's_frame.png')) 262 | self.e_frame_icon = QtGui.QIcon( 263 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'e_frame.png')) 264 | self.load_images_icon = QtGui.QIcon( 265 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'load_images.png')) 266 | self.loop_icon = QtGui.QIcon( 267 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'loop.png')) 268 | self.pause_icon = QtGui.QIcon( 269 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'pause.png')) 270 | self.seq_icon = QtGui.QIcon( 271 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'seq.png')) 272 | 273 | self.ui.btn_play.setIcon(self.play_icon) 274 | self.ui.btn_n_frame.setIcon(self.n_frame_icon) 275 | self.ui.btn_p_frame.setIcon(self.p_frame_icon) 276 | self.ui.btn_s_frame.setIcon(self.s_frame_icon) 277 | self.ui.btn_e_frame.setIcon(self.e_frame_icon) 278 | self.ui.btn_load_seq.setIcon(self.load_images_icon) 279 | self.ui.btn_loop.setIcon(self.loop_icon) 280 | self.ui.btn_converter.setIcon(self.seq_icon) 281 | 282 | def wheelEvent(self, event): 283 | if self.isLoaded: 284 | mxs.sliderTime += (event.delta() / 120) 285 | 286 | def resizeEvent(self, event): 287 | self.updateFrame() 288 | self.changeTime() 289 | 290 | def closeEvent(self, event): 291 | mxs.unregisterTimeCallback(self.changeTime) 292 | self.timer.stop() 293 | 294 | def updateFrame(self): 295 | if self.isLoaded: 296 | self.height = self.ui.viewer.height() 297 | self.width = self.ui.viewer.width() 298 | 299 | self.pixmap = self.images[self.last_valid_frame].scaled(self.width, self.height, 300 | QtCore.Qt.KeepAspectRatio, 301 | QtCore.Qt.FastTransformation) 302 | self.ui.viewer.setPixmap(self.pixmap) 303 | 304 | def status_1(self): 305 | self.ui.state.clear() 306 | self.ui.state.setStyleSheet('''color : #98fc03; 307 | font-size: 12px; 308 | font-family:"Comic Sans MS", cursive, sans-serif;''') 309 | 310 | self.ui.state.setText(f"{self.last_frame} images were imported") 311 | self.time_counting = True 312 | self.startTime() 313 | 314 | def status_2(self): 315 | self.ui.state.clear() 316 | self.ui.state.setStyleSheet('''color : #fcbe03; 317 | font-size: 12px; 318 | font-family:"Comic Sans MS", cursive, sans-serif;''') 319 | 320 | self.ui.state.setText(f"Out of range") 321 | self.time_counting = True 322 | self.startTime() 323 | 324 | def status_3(self): 325 | self.ui.state.clear() 326 | self.ui.state.setStyleSheet('''color : #fc5203; 327 | font-size: 12px; 328 | font-family:"Comic Sans MS", cursive, sans-serif;''') 329 | 330 | self.ui.state.setText(f"Import was canceled") 331 | self.time_counting = True 332 | self.startTime() 333 | 334 | 335 | def main(): 336 | dlg = AnimRef() 337 | dlg.show() 338 | 339 | 340 | if __name__ == '__main__': 341 | main() 342 | -------------------------------------------------------------------------------- /AnimRef/Contents/2025.ms: -------------------------------------------------------------------------------- 1 | python.Init() 2 | 3 | macroScript AnimRef category:"CGcenter" buttonText: "AnimRef" 4 | ( 5 | python.ExecuteFile(getDir #publicExchangeStoreInstallPath + "\AnimRef\Contents\2025\animref.py") 6 | ) 7 | -------------------------------------------------------------------------------- /AnimRef/Contents/2025/animref.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import urllib.request 4 | 5 | from PySide6 import QtCore, QtGui 6 | from PySide6.QtCore import QFile 7 | from PySide6.QtGui import QIcon, QColor 8 | from PySide6.QtUiTools import QUiLoader 9 | from PySide6.QtWidgets import QMessageBox, QVBoxLayout, QSizePolicy, QFileDialog, QPushButton, QDialog, QWidget, QLabel 10 | from pymxs import runtime as mxs 11 | 12 | 13 | class AnimRef(QDialog): 14 | def __init__(self, parent=QWidget.find(mxs.windows.getMAXHWND())): 15 | QDialog.__init__(self, parent) 16 | 17 | self.init() 18 | 19 | self.setWindowFlags(QtCore.Qt.WindowType.Window) 20 | self.resize(720, 460) 21 | self.setWindowTitle("AnimRef v1.5.2") 22 | 23 | self.defineVariables() 24 | self.defineSignals() 25 | self.defineIcons() 26 | self.start() 27 | 28 | self.setWindowIcon(self.icon) 29 | 30 | self.timer = QtCore.QTimer(self) 31 | 32 | def downloadConverter(self): 33 | 34 | converter_path = os.path.join(self.dir, 'ApplicationPlugins', 'AnimRef', 'Contents', 'converter', 35 | 'video_to_sequence.exe') 36 | download_path = "https://raw.githubusercontent.com/ShirzadBh/AnimRef/main/AnimRef/Contents/converter/video_to_sequence.exe" 37 | 38 | try: 39 | urllib.request.urlretrieve(download_path, converter_path) 40 | self.ui.state.setStyleSheet('''color : #98fc03; 41 | font-size: 12px; 42 | font-family:"Comic Sans MS", cursive, sans-serif;''') 43 | 44 | self.ui.state.setText("video_to_sequence.exe is ready!") 45 | self.time_counting = True 46 | self.startTime() 47 | except: 48 | self.ui.state.setStyleSheet('''color : #fc5203; 49 | font-size: 12px; 50 | font-family:"Comic Sans MS", cursive, sans-serif;''') 51 | 52 | self.ui.state.setText("Download failed...") 53 | self.time_counting = True 54 | self.startTime() 55 | 56 | def convertedExist(self): 57 | 58 | FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe') 59 | path = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter', 'video_to_sequence.exe') 60 | converterPath = os.path.join(self.dir, 'AnimRef', 'Contents', 'converter') 61 | 62 | if os.path.exists(path): 63 | subprocess.run([FILEBROWSER_PATH, converterPath]) 64 | else: 65 | msgBox = QMessageBox() 66 | msgBox.setText("Do You Want To Download video_to_sequence.exe") 67 | msgBox.setWindowTitle("Sequence Converter") 68 | msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) 69 | 70 | returnValue = msgBox.exec() 71 | if returnValue == QMessageBox.Ok: 72 | self.downloadConverter() 73 | 74 | def startTime(self): 75 | if self.time_counting: 76 | self.timer.timeout.connect(self.stopTime) 77 | self.timer.start(3000) 78 | 79 | def stopTime(self): 80 | self.ui.state.clear() 81 | self.timer.stop() 82 | self.time_counting = False 83 | 84 | def init(self): 85 | 86 | self.dir = mxs.getDir(mxs.name('publicExchangeStoreInstallPath')) 87 | loader = QUiLoader() 88 | ui_file_path = os.path.join(self.dir, 'AnimRef', 'Contents', 'interface', 'interface.ui') 89 | ui_file = QFile(ui_file_path) 90 | ui_file.open(QFile.ReadOnly) 91 | self.ui = loader.load(ui_file, self) 92 | ui_file.close() 93 | layout = QVBoxLayout() 94 | layout.addWidget(self.ui) 95 | layout.setContentsMargins(4, 4, 4, 4) 96 | self.setLayout(layout) 97 | 98 | def start(self): 99 | self.ui.viewer.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) 100 | self.pixmap = self.no_image.scaled(400, 200, QtCore.Qt.KeepAspectRatio, QtCore.Qt.FastTransformation) 101 | self.ui.viewer.setPixmap(self.pixmap) 102 | mxs.registerTimeCallback(self.changeTime) 103 | 104 | def changeOpacity(self): 105 | self.opacity = self.ui.sl_opacity.value() / 100 106 | self.setWindowOpacity(self.opacity) 107 | 108 | def defineVariables(self): 109 | self.last_valid_frame = 0 110 | self.time_counting = False 111 | self.out_of_range = False 112 | self.pixmap = None 113 | self.isLoaded = False 114 | self.current_time = int(mxs.currentTime) 115 | self.time_shift = self.ui.sb_time_shift.value() 116 | self.time = self.current_time + self.time_shift 117 | self.height = self.ui.viewer.height() 118 | self.width = self.ui.viewer.width() 119 | self.images_backup = {} 120 | self.images = {} 121 | self.opacity = 1 122 | self.images_path = None 123 | self.last_frame = 0 124 | self.previous_frame = 0 125 | self.no_image = QtGui.QPixmap( 126 | os.path.join(os.path.dirname(os.path.realpath(__file__)) + "\\icons\\no_data.png")) 127 | 128 | def defineSignals(self): 129 | self.ui.btn_converter.clicked.connect(self.convertedExist) 130 | 131 | # self.ui.timeSlider.valueChanged.connect(self.goToFrame) 132 | self.ui.sl_opacity.valueChanged.connect(self.changeOpacity) 133 | self.ui.btn_load_seq.clicked.connect(self.load_seq) 134 | self.ui.sb_time_shift.valueChanged.connect(self.updateTimeShift) 135 | self.ui.btn_n_frame.clicked.connect(self.nextFrame) 136 | self.ui.btn_p_frame.clicked.connect(self.previousFrame) 137 | self.ui.btn_play.clicked.connect(self.playFrame) 138 | self.ui.btn_s_frame.clicked.connect(self.startFrame) 139 | self.ui.btn_e_frame.clicked.connect(self.endFrame) 140 | 141 | def nextFrame(self): 142 | mxs.stopAnimation() 143 | self.ui.btn_play.setChecked(False) 144 | mxs.sliderTime += 1 145 | self.ui.sb_time_shift.setEnabled(True) 146 | 147 | def previousFrame(self): 148 | mxs.stopAnimation() 149 | self.ui.btn_play.setChecked(False) 150 | mxs.sliderTime -= 1 151 | self.ui.sb_time_shift.setEnabled(True) 152 | 153 | def playFrame(self): 154 | if mxs.isAnimPlaying(): 155 | self.ui.sb_time_shift.setEnabled(True) 156 | mxs.stopAnimation() 157 | 158 | elif not mxs.isAnimPlaying(): 159 | self.ui.sb_time_shift.setEnabled(False) 160 | mxs.playAnimation() 161 | 162 | def startFrame(self): 163 | # mxs.execute("max time start") 164 | mxs.stopAnimation() 165 | mxs.sliderTime = self.time_shift 166 | self.ui.btn_play.setIcon(self.play_icon) 167 | self.ui.btn_play.setChecked(False) 168 | self.ui.sb_time_shift.setEnabled(True) 169 | 170 | def endFrame(self): 171 | # mxs.execute("max time end") 172 | mxs.stopAnimation() 173 | mxs.sliderTime = self.time_shift + (self.last_frame - 1) 174 | self.ui.btn_play.setIcon(self.play_icon) 175 | self.ui.btn_play.setChecked(False) 176 | self.ui.sb_time_shift.setEnabled(True) 177 | 178 | def updateTimeShift(self): 179 | self.time_shift = self.ui.sb_time_shift.value() 180 | self.changeTime() 181 | 182 | def load_seq(self): 183 | self.height = self.ui.viewer.height() 184 | self.width = self.ui.viewer.width() 185 | 186 | try: 187 | 188 | fname = list(QFileDialog.getOpenFileNames(self, 'Select Range OF Sequences', 189 | filter="Images (*.jpeg *.jpg *.png *.bmp)", )) 190 | 191 | if len(fname[0]) > 0: 192 | self.images = {} 193 | self.images_path = os.path.dirname(os.path.realpath(fname[0][0])) 194 | 195 | self.test = {} 196 | for i in range(int(len(fname[0]))): 197 | self.images[i] = QtGui.QPixmap(fname[0][i]) 198 | self.test[i] = fname[0][i] 199 | 200 | self.last_frame = len(fname[0]) 201 | self.isLoaded = True 202 | self.ui.btn_play.setEnabled(True) 203 | self.ui.btn_s_frame.setEnabled(True) 204 | self.ui.btn_p_frame.setEnabled(True) 205 | self.ui.btn_n_frame.setEnabled(True) 206 | self.ui.btn_e_frame.setEnabled(True) 207 | self.ui.sb_time_shift.setEnabled(True) 208 | self.ui.btn_loop.setEnabled(True) 209 | self.status_1() 210 | self.changeTime() 211 | 212 | else: 213 | self.status_3() 214 | self.changeTime() 215 | 216 | except: 217 | self.status_3() 218 | self.changeTime() 219 | 220 | def changeTime(self): 221 | if self.isLoaded: 222 | 223 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 224 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 225 | 226 | try: 227 | self.pixmap = self.images[int(mxs.currentTime) - self.time_shift].scaled(self.width, self.height, 228 | QtCore.Qt.KeepAspectRatio, 229 | QtCore.Qt.FastTransformation) 230 | self.ui.viewer.setPixmap(self.pixmap) 231 | self.ui.viewer.repaint() 232 | self.ui.maxframe.setText(str(int(mxs.currentTime))) 233 | self.ui.refframe.setText(str(int(mxs.currentTime) - self.time_shift)) 234 | self.out_of_range = False 235 | self.last_valid_frame = int(mxs.currentTime) - self.time_shift 236 | except: 237 | out = True 238 | is_playing = mxs.isAnimPlaying() 239 | if self.isLoaded and not self.ui.btn_loop.isChecked(): 240 | self.status_2() 241 | 242 | if self.isLoaded: 243 | 244 | if self.ui.btn_loop.isChecked(): 245 | mxs.stopAnimation() 246 | mxs.sliderTime = self.time_shift 247 | if is_playing and out: 248 | mxs.playAnimation() 249 | self.out_of_range = True 250 | 251 | def defineIcons(self): 252 | self.icon = QtGui.QIcon( 253 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'icon.png')) 254 | self.play_icon = QtGui.QIcon( 255 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'play.png')) 256 | self.n_frame_icon = QtGui.QIcon( 257 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'n_frame.png')) 258 | self.p_frame_icon = QtGui.QIcon( 259 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'p_frame.png')) 260 | self.s_frame_icon = QtGui.QIcon( 261 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 's_frame.png')) 262 | self.e_frame_icon = QtGui.QIcon( 263 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'e_frame.png')) 264 | self.load_images_icon = QtGui.QIcon( 265 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'load_images.png')) 266 | self.loop_icon = QtGui.QIcon( 267 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'loop.png')) 268 | self.pause_icon = QtGui.QIcon( 269 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'pause.png')) 270 | self.seq_icon = QtGui.QIcon( 271 | os.path.join(self.dir, 'AnimRef', 'Contents', 'icons', 'seq.png')) 272 | 273 | self.ui.btn_play.setIcon(self.play_icon) 274 | self.ui.btn_n_frame.setIcon(self.n_frame_icon) 275 | self.ui.btn_p_frame.setIcon(self.p_frame_icon) 276 | self.ui.btn_s_frame.setIcon(self.s_frame_icon) 277 | self.ui.btn_e_frame.setIcon(self.e_frame_icon) 278 | self.ui.btn_load_seq.setIcon(self.load_images_icon) 279 | self.ui.btn_loop.setIcon(self.loop_icon) 280 | self.ui.btn_converter.setIcon(self.seq_icon) 281 | 282 | def wheelEvent(self, event): 283 | if self.isLoaded: 284 | mxs.sliderTime += (event.delta() / 120) 285 | 286 | def resizeEvent(self, event): 287 | self.updateFrame() 288 | self.changeTime() 289 | 290 | def closeEvent(self, event): 291 | mxs.unregisterTimeCallback(self.changeTime) 292 | self.timer.stop() 293 | 294 | def updateFrame(self): 295 | if self.isLoaded: 296 | self.height = self.ui.viewer.height() 297 | self.width = self.ui.viewer.width() 298 | 299 | self.pixmap = self.images[self.last_valid_frame].scaled(self.width, self.height, 300 | QtCore.Qt.KeepAspectRatio, 301 | QtCore.Qt.FastTransformation) 302 | self.ui.viewer.setPixmap(self.pixmap) 303 | 304 | def status_1(self): 305 | self.ui.state.clear() 306 | self.ui.state.setStyleSheet('''color : #98fc03; 307 | font-size: 12px; 308 | font-family:"Comic Sans MS", cursive, sans-serif;''') 309 | 310 | self.ui.state.setText(f"{self.last_frame} images were imported") 311 | self.time_counting = True 312 | self.startTime() 313 | 314 | def status_2(self): 315 | self.ui.state.clear() 316 | self.ui.state.setStyleSheet('''color : #fcbe03; 317 | font-size: 12px; 318 | font-family:"Comic Sans MS", cursive, sans-serif;''') 319 | 320 | self.ui.state.setText(f"Out of range") 321 | self.time_counting = True 322 | self.startTime() 323 | 324 | def status_3(self): 325 | self.ui.state.clear() 326 | self.ui.state.setStyleSheet('''color : #fc5203; 327 | font-size: 12px; 328 | font-family:"Comic Sans MS", cursive, sans-serif;''') 329 | 330 | self.ui.state.setText(f"Import was canceled") 331 | self.time_counting = True 332 | self.startTime() 333 | 334 | 335 | def main(): 336 | dlg = AnimRef() 337 | dlg.show() 338 | 339 | 340 | if __name__ == '__main__': 341 | main() 342 | -------------------------------------------------------------------------------- /AnimRef/Contents/converter/video_to_sequence.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/converter/video_to_sequence.exe -------------------------------------------------------------------------------- /AnimRef/Contents/icons/e_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/e_frame.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/icon.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/load_images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/load_images.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/loop.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/n_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/n_frame.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/no_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/no_data.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/p_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/p_frame.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/pause.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/play.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/s_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/s_frame.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/seq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/seq.png -------------------------------------------------------------------------------- /AnimRef/Contents/icons/soft-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/AnimRef/Contents/icons/soft-icon.png -------------------------------------------------------------------------------- /AnimRef/Contents/interface/interface.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 722 10 | 617 11 | 12 | 13 | 14 | 15 | 340 16 | 240 17 | 18 | 19 | 20 | Form 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | QTabWidget::pane { /* The tab widget frame */ 41 | } 42 | 43 | QTabWidget::tab-bar { 44 | /* move to the right by 5px */ 45 | background: black; 46 | 47 | } 48 | 49 | /* Style the tab using the tab sub-control. Note that 50 | it reads QTabBar _not_ QTabWidget */ 51 | QTabBar::tab { 52 | color: white; 53 | background: #363636; 54 | padding: 6px 26px 6px 26px; 55 | } 56 | 57 | 58 | QTabBar::tab:selected, QTabBar::tab:hover { 59 | background: #5b5b5b; 60 | } 61 | 62 | QTabBar::tab:selected { 63 | border-color: #9B9B9B; 64 | } 65 | 66 | QTabBar::tab:!selected { 67 | border-color: #474747; 68 | 69 | } 70 | 71 | /* make use of negative margins for overlapping tabs */ 72 | QTabBar::tab:selected { 73 | background: 474747; 74 | 75 | /* expand/overlap to the left and right by 4px */ 76 | 77 | } 78 | 79 | QTabBar::tab:first:selected { 80 | margin-left: 0; /* the first selected tab has nothing to overlap with on the left */ 81 | } 82 | 83 | QTabBar::tab:last:selected { 84 | margin-right: 0; /* the last selected tab has nothing to overlap with on the right */ 85 | } 86 | 87 | QTabBar::tab:only-one { 88 | margin: 0; /* if there is only one tab, we don't want overlapping margins */ 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | QMenuBar 107 | { 108 | color: #fff; 109 | 110 | background-color: #363636; 111 | } 112 | 113 | QMenuBar::item 114 | { 115 | color: #fff; 116 | 117 | background-color: #474747; 118 | } 119 | 120 | QMenuBar::item:selected 121 | { 122 | color: #fff; 123 | 124 | background-color: #363636; 125 | } 126 | 127 | QMenu 128 | { 129 | background-color: #363636; 130 | color: #fff; 131 | 132 | } 133 | 134 | 135 | QMenu::item:selected 136 | { 137 | background-color: #fff; 138 | color: #34495e; 139 | 140 | } 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | QToolBar { 159 | background: #363636; 160 | spacing: 3px; /* spacing between items in the tool bar */ 161 | } 162 | 163 | QToolBar::handle { 164 | /* 165 | background: #363636; 166 | */ 167 | } 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 0 177 | 0 178 | 179 | 180 | 181 | background-color: #333333 182 | 183 | 184 | 185 | 186 | 187 | false 188 | 189 | 190 | Qt::AlignCenter 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | false 202 | 203 | 204 | 205 | 0 206 | 26 207 | 208 | 209 | 210 | 211 | 16777215 212 | 26 213 | 214 | 215 | 216 | QPushButton 217 | { 218 | color: white; 219 | font-size: 12px; 220 | padding: 20px 4px 20px 4px; 221 | border-radius: 2px; 222 | background-color: #5b5b5b; 223 | } 224 | 225 | QPushButton::hover 226 | { 227 | background-color: #888888; 228 | } 229 | 230 | QPushButton::pressed 231 | { 232 | background-color: #4b4b4b; 233 | } 234 | 235 | 236 | 237 | QPushButton::checked 238 | { 239 | background-color: #E84A5F; 240 | } 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | false 258 | 259 | 260 | 261 | 0 262 | 26 263 | 264 | 265 | 266 | 267 | 16777215 268 | 26 269 | 270 | 271 | 272 | QPushButton 273 | { 274 | color: white; 275 | font-size: 12px; 276 | padding: 20px 4px 20px 4px; 277 | border-radius: 2px; 278 | background-color: #5b5b5b; 279 | } 280 | 281 | QPushButton::hover 282 | { 283 | background-color: #888888; 284 | } 285 | 286 | QPushButton::pressed 287 | { 288 | background-color: #4b4b4b; 289 | } 290 | 291 | 292 | 293 | QPushButton::checked 294 | { 295 | background-color: #E84A5F; 296 | } 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | false 314 | 315 | 316 | 317 | 0 318 | 26 319 | 320 | 321 | 322 | 323 | 16777215 324 | 26 325 | 326 | 327 | 328 | QPushButton 329 | { 330 | color: white; 331 | font-size: 12px; 332 | padding: 20px 4px 20px 4px; 333 | border-radius: 2px; 334 | background-color: #5b5b5b; 335 | } 336 | 337 | QPushButton::hover 338 | { 339 | background-color: #888888; 340 | } 341 | 342 | QPushButton::pressed 343 | { 344 | background-color: #4b4b4b; 345 | } 346 | 347 | 348 | 349 | QPushButton::checked 350 | { 351 | background-color: #E84A5F; 352 | } 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | false 366 | 367 | 368 | 369 | 370 | 371 | 372 | false 373 | 374 | 375 | 376 | 0 377 | 26 378 | 379 | 380 | 381 | 382 | 16777215 383 | 26 384 | 385 | 386 | 387 | QPushButton 388 | { 389 | color: white; 390 | font-size: 12px; 391 | padding: 20px 4px 20px 4px; 392 | border-radius: 2px; 393 | background-color: #5b5b5b; 394 | } 395 | 396 | QPushButton::hover 397 | { 398 | background-color: #888888; 399 | } 400 | 401 | QPushButton::pressed 402 | { 403 | background-color: #4b4b4b; 404 | } 405 | 406 | 407 | 408 | QPushButton::checked 409 | { 410 | background-color: #E84A5F; 411 | } 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | false 429 | 430 | 431 | 432 | 0 433 | 26 434 | 435 | 436 | 437 | 438 | 16777215 439 | 26 440 | 441 | 442 | 443 | QPushButton 444 | { 445 | color: white; 446 | font-size: 12px; 447 | padding: 20px 4px 20px 4px; 448 | border-radius: 2px; 449 | background-color: #5b5b5b; 450 | } 451 | 452 | QPushButton::hover 453 | { 454 | background-color: #888888; 455 | } 456 | 457 | QPushButton::pressed 458 | { 459 | background-color: #4b4b4b; 460 | } 461 | 462 | 463 | 464 | QPushButton::checked 465 | { 466 | background-color: #E84A5F; 467 | } 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | false 485 | 486 | 487 | 488 | 0 489 | 26 490 | 491 | 492 | 493 | 494 | 16777215 495 | 26 496 | 497 | 498 | 499 | QPushButton 500 | { 501 | color: white; 502 | font-size: 12px; 503 | padding: 20px 4px 20px 4px; 504 | border-radius: 2px; 505 | background-color: #5b5b5b; 506 | } 507 | 508 | QPushButton::hover 509 | { 510 | background-color: #888888; 511 | } 512 | 513 | QPushButton::pressed 514 | { 515 | background-color: #424242; 516 | } 517 | 518 | 519 | 520 | QPushButton::checked 521 | { 522 | background-color:#333333; 523 | } 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | true 537 | 538 | 539 | 540 | 541 | 542 | 543 | false 544 | 545 | 546 | 547 | 46 548 | 26 549 | 550 | 551 | 552 | 553 | 46 554 | 26 555 | 556 | 557 | 558 | Set Start Frame Of Reference 559 | 560 | 561 | QSpinBox { 562 | background-color: #333333; 563 | 564 | font-size: 12px; 565 | font-family:"Comic Sans MS", cursive, sans-serif; 566 | } 567 | 568 | 569 | 570 | QSpinBox::selected { 571 | background-color: #333333; 572 | border-radius: 5px; 573 | } 574 | 575 | 576 | 577 | 578 | 579 | false 580 | 581 | 582 | Qt::AlignCenter 583 | 584 | 585 | QAbstractSpinBox::NoButtons 586 | 587 | 588 | 589 | 590 | 591 | -10000000 592 | 593 | 594 | 10000000 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 0 605 | 0 606 | 607 | 608 | 609 | font-size: 12px; 610 | font-family:"Comic Sans MS", cursive, sans-serif; 611 | 612 | padding-left: 4px; 613 | 614 | 615 | 0 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 0 624 | 0 625 | 626 | 627 | 628 | font-size: 12px; 629 | font-family:"Comic Sans MS", cursive, sans-serif; 630 | 631 | 632 | : 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 0 641 | 0 642 | 643 | 644 | 645 | font-size: 12px; 646 | font-family:"Comic Sans MS", cursive, sans-serif; 647 | 648 | 649 | padding-right: 4px; 650 | 651 | 652 | 653 | 0 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 40 662 | 0 663 | 664 | 665 | 666 | font-size: 14px; 667 | font-family:"Comic Sans MS", cursive, sans-serif; 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | Qt::Horizontal 680 | 681 | 682 | 683 | 80 684 | 20 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 50 694 | 26 695 | 696 | 697 | 698 | 699 | 100 700 | 26 701 | 702 | 703 | 704 | Change The Window Opacity 705 | 706 | 707 | QSlider::groove:horizontal { 708 | background: #333333; 709 | } 710 | 711 | QSlider::handle:horizontal { 712 | background: #5b5b5b; 713 | width: 16px; 714 | 715 | } 716 | 717 | 718 | 30 719 | 720 | 721 | 100 722 | 723 | 724 | 2 725 | 726 | 727 | 100 728 | 729 | 730 | Qt::Horizontal 731 | 732 | 733 | 734 | 735 | 736 | 737 | true 738 | 739 | 740 | 741 | 0 742 | 26 743 | 744 | 745 | 746 | 747 | 16777215 748 | 26 749 | 750 | 751 | 752 | QPushButton 753 | { 754 | color: white; 755 | font-size: 12px; 756 | padding: 20px 4px 20px 4px; 757 | border-radius: 2px; 758 | background-color: #5b5b5b; 759 | } 760 | 761 | QPushButton::hover 762 | { 763 | background-color: #888888; 764 | } 765 | 766 | QPushButton::pressed 767 | { 768 | background-color: #4b4b4b; 769 | } 770 | 771 | 772 | 773 | QPushButton::checked 774 | { 775 | background-color: #E84A5F; 776 | } 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | true 794 | 795 | 796 | 797 | 0 798 | 26 799 | 800 | 801 | 802 | 803 | 16777215 804 | 26 805 | 806 | 807 | 808 | QPushButton 809 | { 810 | color: white; 811 | font-size: 12px; 812 | padding: 20px 4px 20px 4px; 813 | border-radius: 2px; 814 | background-color: #5b5b5b; 815 | } 816 | 817 | QPushButton::hover 818 | { 819 | background-color: #888888; 820 | } 821 | 822 | QPushButton::pressed 823 | { 824 | background-color: #4b4b4b; 825 | } 826 | 827 | 828 | 829 | QPushButton::checked 830 | { 831 | background-color: #E84A5F; 832 | } 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | false 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | -------------------------------------------------------------------------------- /AnimRef/PackageContents.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shirzad Bahrami 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /One-Click Installation.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set productName=AnimRef 4 | set developerName=Shirzad Bahrami 5 | set websiteAddress=www.shirzadbahrami.com 6 | 7 | set minVersion=2018 8 | set maxVersion=2025 9 | 10 | set productVer=1.5.2 11 | 12 | set releaseDate=2020-10-17 13 | set lastUpdate=2024-09-02 14 | 15 | set fileName=AnimRef.zip 16 | set installationPath=/Autodesk/ApplicationPlugins 17 | 18 | echo %productName% Developed By: %developerName% 19 | echo Version: %productVer% 20 | echo Website: %websiteAddress% 21 | echo. 22 | echo Supported Version: %minVersion% - %maxVersion% 23 | echo Release: %releaseDate% 24 | echo Update: %lastUpdate% 25 | 26 | echo. 27 | pause>nul|set/p =Press Any Key To Install "%productName%" On 3Ds Max From "%minVersion%" to "%maxVersion%"... 28 | echo. 29 | cls 30 | 31 | powershell -Command "Expand-Archive -Force "%batdir%%fileName% "%ProgramData%%installationPath%" " 32 | 33 | echo "%productName%" Installed successfully, Hope You Enjoy... 34 | ping -n 4 localhost >nul 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AnimRef - Sequence Loader For 3Ds Max 2 | 3 | 4 | ![AnimRef v1.0 User-Interface](screen/interface.jpg) 5 | 6 | --- 7 | 8 | With AnimRef you can load and play image sequences directly in the 3Ds Max user-interface. There are a few parameters to keep it simple and fast for animators: 9 | 10 | --- 11 | ### AnimRef 12 | - Video to Sequence Converter: 13 | ![](screen/converter.gif) 14 | - Viewer Update 15 | - New UI design 16 | - MessageBox Timer 17 | - Load image sequences in the desired range 18 | - Time shift value 19 | - 3Ds Max synced time slider 20 | - Window opacity slider 21 | --- 22 | 23 | | Supported Formats | Description | 24 | | ----------- | ----------- | 25 | | BMP | Windows Bitmap | 26 | | JPG | Joint Photographic Experts Group | 27 | | JPEG | Joint Photographic Experts Group | 28 | | PNG | Portable Network Graphics | 29 | 30 | 31 | >Easy installation: 32 | Run "One-Click Installation.bat". 33 | 34 | >installation: 35 | Unzip the "AnimRef.zip" inside "C://ProgramData//Autodesk//ApplicationPlugins". 36 | 37 | 38 | >How To Run: 39 | Now, it's a MacroScript, which you can assign a shortcut to it or search it using "X" in 3Ds Max. 40 | 41 | >Supported Version: 42 | 43 | 2018 - 2019 - 2020 - 2021 - 2022 - 2023 - 2024 - 2025 44 | -------------------------------------------------------------------------------- /screen/converter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/screen/converter.gif -------------------------------------------------------------------------------- /screen/interface.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShirzadBh/AnimRef/c1a1d9088f804ad9913951613542a61f6cd98f30/screen/interface.jpg --------------------------------------------------------------------------------