PageRenderTime 153ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/src/epp/ui/add_shot.py

https://bitbucket.org/bfloch/epp
Python | 381 lines | 308 code | 35 blank | 38 comment | 18 complexity | e5e6fea66c87a27c8d52d834f3276140 MD5 | raw file
  1. #!/usr/bin/env python
  2. #coding=utf-8
  3. #? py
  4. '''
  5. add_shot.py
  6. ####################################
  7. add_shot PySide interface controller. Makes use of the Designer generated _ui file.
  8. :copyright: 2013 by eyeon Software Inc., eyeon Software Inc., see AUTHORS for more details
  9. :license: Some rights reserved, see LICENSE for more details
  10. '''
  11. import glob
  12. import os
  13. import re
  14. import sys
  15. from PySide.QtCore import *
  16. from PySide.QtGui import *
  17. import epp.helper.osplus as osplus
  18. import epp.helper.xml.settings as settings
  19. from epp.ui.add_shot_ui import Ui_dlgAddShot
  20. from epp.__INFO__ import DEBUG, __VERSION__
  21. from epp.model.format import Format
  22. import epp.bin.epp_addshot as control
  23. from epp.gen.controller import gen_controller
  24. from epp.helper.xml.dir_query import DirQuery
  25. import epp.helper.log as log
  26. class AddShotDialog(Ui_dlgAddShot, QDialog):
  27. """
  28. This class represents
  29. """
  30. # -------------------------------------------------------------------------------------
  31. def PROJECTNAME():
  32. doc = "The PROJECTNAME property."
  33. def fget(self):
  34. return self._PROJECTNAME
  35. def fset(self, value):
  36. if isinstance(value, basestring):
  37. self.lblProject.setText(value)
  38. self._PROJECTNAME = value
  39. return locals()
  40. PROJECTNAME = property(**PROJECTNAME())
  41. def __init__(self, parent=None):
  42. """ Constructor.
  43. """
  44. super(AddShotDialog, self).__init__()
  45. self.setupUi(self)
  46. self.STATUS = self._setup()
  47. try:
  48. if DEBUG:
  49. self._setup_debug()
  50. except NameError:
  51. pass
  52. # -------------------------------------------------------------------------------------
  53. def _setup_debug(self):
  54. """
  55. """
  56. pass
  57. # -------------------------------------------------------------------------------------
  58. def _setup(self):
  59. """
  60. Setup the widgets
  61. """
  62. epp_root = osplus.get_env("EPP_ROOT")
  63. if not os.path.isdir(epp_root):
  64. log.error("Could not find EPP ROOT directory.\n{0}".format(epp_root), True)
  65. return False
  66. self.settings = settings.XMLSettings(os.path.join(epp_root, "config.xml") )
  67. self.PROJECTROOT = self.settings.get("paths", "projectdir")
  68. if not self.PROJECTROOT.endswith(os.path.sep):
  69. self.PROJECTROOT = self.PROJECTROOT + os.path.sep
  70. if self.PROJECTROOT is None:
  71. log.error("No Project Directory configured in config.xml.", True)
  72. return False
  73. self._setup_header(epp_root)
  74. self._setup_name_widget(epp_root)
  75. self._setup_status()
  76. # Require Generation
  77. if not self._setup_generation():
  78. msg = "Could not establish connection with Generation. Make sure it is running."
  79. log.error(msg, True)
  80. return False
  81. # Need edit rights for this one!
  82. if not self._gen.allow_changes(True):
  83. return False
  84. if not self._setup_project():
  85. msg = "Project directory from Generation Project invalid: {0}".format(self.PROJECTDIR)
  86. log.error(msg, True)
  87. return False
  88. if not self._setup_shot():
  89. msg = "Project directory from Generation Project invald: {0}".format(self.PROJECTDIR)
  90. log.error(msg, True)
  91. return False
  92. # We can't work without structs
  93. if not self._setup_structs(epp_root):
  94. msg = "Could not find directory structure files in templates.\n{0}".format(os.path.join(epp_root, "templates", "shot_dirs"))
  95. log.error(msg, True)
  96. return False
  97. return True
  98. # -------------------------------------------------------------------------------------
  99. def _setup_project(self):
  100. """docstring for set_project"""
  101. proj = self._gen.app.ActiveProject
  102. self.PROJECTNAME = proj.Name
  103. self.PROJECTDIR = ""
  104. self.GENFILEPATH = proj.Filename
  105. # BUG? X:dir\dir instead of X:\dir\dir
  106. pa_frontslash = re.compile(r"(\w:)([^\\].*)$")
  107. self.GENFILEPATH = pa_frontslash.subn(r'\1\\\2', self.GENFILEPATH)[0]
  108. if self.GENFILEPATH is None:
  109. return False
  110. # Find the projectname in the filename
  111. index = self.GENFILEPATH.lower().find(self.PROJECTNAME.lower().split("_")[0])
  112. if index < 0:
  113. return False
  114. index += len(self.PROJECTNAME)
  115. self.PROJECTDIR = self.GENFILEPATH[:index].rstrip(os.path.sep)
  116. if not self.PROJECTDIR.lower().startswith(self.PROJECTROOT.lower()):
  117. return False
  118. # proj:SaveAs does not set the proj.Filename
  119. if not os.path.isdir(self.PROJECTDIR): #or not self.GENFILEPATH.lower().startswith(self.PROJECTDIR.lower()):
  120. return False
  121. return True
  122. # -------------------------------------------------------------------------------------
  123. def _setup_shot(self):
  124. """Setup the shot directory."""
  125. project_template = os.path.join(self.PROJECTDIR, "project_template.xml")
  126. project_vars = os.path.join(self.PROJECTDIR, "project_vars.xml")
  127. if not os.path.isfile(project_template):
  128. return False
  129. # Optional
  130. if not os.path.isfile(project_vars):
  131. project_vars = None
  132. else:
  133. # The project name right now does not include \\ so query it!
  134. projectvars = settings.XMLSettings(project_vars)
  135. self.PROJECTNAME = projectvars.get("creationvars", "projectname")
  136. dq = DirQuery(project_template, project_vars)
  137. try:
  138. self.SHOTDIR = dq.get_path("shots", self.PROJECTROOT)
  139. except:
  140. self.SHOTDIR = None
  141. return False
  142. if not self.SHOTDIR.lower().startswith(self.PROJECTDIR.lower()):
  143. return False
  144. return True
  145. # -------------------------------------------------------------------------------------
  146. def _setup_generation(self):
  147. """docstring for _setup_generation"""
  148. self._gen = gen_controller()
  149. return self._gen.status()
  150. # -------------------------------------------------------------------------------------
  151. def _setup_header(self, epp_root):
  152. """docstring for _setup_header"""
  153. header_filepath = os.path.join(epp_root, "templates", "images", "header.png")
  154. if os.path.isfile(header_filepath):
  155. """
  156. pix = QPixmap()
  157. pix.load(header_filepath)
  158. self.lblHeader.setPixmap(pix)
  159. """
  160. self.lblHeader.setStyleSheet("QLabel {{ background-image: url({0});}}".format(header_filepath.replace("\\", "/")))
  161. self.lblHeader.setMinimumHeight(96)
  162. self.setMaximumWidth(540)
  163. # -------------------------------------------------------------------------------------
  164. def _setup_status(self):
  165. """docstring for """
  166. self.lblStatus.setText("")
  167. fx = QGraphicsOpacityEffect(self.lblStatus)
  168. self.lblStatus.setGraphicsEffect(fx)
  169. self._status_fade = QPropertyAnimation(fx, "opacity");
  170. self._status_fade.setDuration(250)
  171. self._status_fade.setStartValue(0.0)
  172. self._status_fade.setEndValue(1.0)
  173. self._last_valid_name = True
  174. # -------------------------------------------------------------------------------------
  175. def _validate_name(self, text):
  176. """docstring for _validate_name"""
  177. valid_name = len(text) > 0
  178. text = self.plugName.text()
  179. if not valid_name:
  180. self.lblStatus.setText("<font color='#ffaa00'>Invalid Shot Name</font>")
  181. self._status_fade.setDirection(QAbstractAnimation.Forward)
  182. else:
  183. self._status_fade.setDirection(QAbstractAnimation.Backward)
  184. shot_dir = os.path.join(self.SHOTDIR, text)
  185. if os.path.isdir(shot_dir):
  186. self.lblStatus.setText("<html><head><style type=text/css>a {{ color: white; }}\n a:link {{color:white; text-decoration:none; }}\n a:hover {{ color:#ffcc00; text-decoration:underline; }}</style></head><body><font color='#ffaa00'>Shot Directory already exists:</font> <a href='file:///{0}'>{0}</a></body></html>".format(shot_dir, shot_dir.replace("\\", "/")))
  187. valid_name = False
  188. self._status_fade.setDirection(QAbstractAnimation.Forward)
  189. else:
  190. self._status_fade.setDirection(QAbstractAnimation.Backward)
  191. if self._last_valid_name != valid_name:
  192. self._status_fade.start()
  193. self._last_valid_name = valid_name
  194. self.butAdd.setEnabled(valid_name)
  195. # -------------------------------------------------------------------------------------
  196. def _setup_name_widget(self, epp_root):
  197. """docstring for _setup_name_widget"""
  198. #namewidget = self.settings.get("project", "namewidget", "QLineEdit", create=True)
  199. validation = self.settings.get("shotname", "validation", r"[a-zA-Z0-9_ \\]+", create=True)
  200. replace_whitespaces = self.settings.get("shotname", "replacewhitespace", "True", create=True).lower() in ["true", "on", "yes", "1"]
  201. mask = self.settings.get("shotname", "mask", "", create=True)
  202. try:
  203. maxlength = int(self.settings.get("shotname", "maxlength", "0", create=True))
  204. except ValueError:
  205. maxlength = 0
  206. # TODO Load Plugin
  207. self.plugName = None
  208. if False: #namewidget == "QLineEdit":
  209. pass
  210. else: #namewidget == "QLineEditValid": # Default
  211. self.plugName = QLineEdit(self)
  212. # Sanity helper: Return converted str instead of unicode to make gen happy
  213. # Normally should have inherited from QLineEdit but for this cheat I use this
  214. # instead.
  215. self.plugName.text = osplus.unitostr_decorator(self.plugName.text)
  216. # Mask overrides others
  217. if len(mask.strip()) > 0:
  218. self.plugName.setInputMask(mask)
  219. # Get the best monospaced thing you can get
  220. font = QFont("Monospace");
  221. font.setStyleHint(QFont.TypeWriter)
  222. self.plugName.setFont(font)
  223. else:
  224. if maxlength > 0:
  225. self.plugName.setMaxLength(maxlength)
  226. if len(validation.strip()) > 0:
  227. rx = QRegExp(validation)
  228. self.plugName.setValidator(QRegExpValidator(rx, self))
  229. if replace_whitespaces:
  230. # Replace whitespace with _
  231. self.plugName.textEdited.connect(self._replace_whitespaces)
  232. if self.plugName is not None:
  233. self.plugName.setToolTip(self._lblName.toolTip())
  234. self.plugName.setWhatsThis(self._lblName.whatsThis())
  235. self.butAdd.setEnabled(False)
  236. self.plugName.textChanged.connect(self._validate_name)
  237. self.gridLayout1.addWidget(self.plugName, 3, 1)
  238. # -------------------------------------------------------------------------------------
  239. def _replace_whitespaces(self, text):
  240. """docstring for _replace_whitespaces"""
  241. cur = self.plugName.cursorPosition()
  242. self.plugName.setText(text.replace(" ", "_"))
  243. self.plugName.setCursorPosition(cur)
  244. # -------------------------------------------------------------------------------------
  245. def _setup_structs(self, epp_root):
  246. """Setup controls for Directory Strucutre"""
  247. xml_shot_dir_pat = os.path.join(epp_root, "templates", "shot_dirs", "*.xml")
  248. xml_shot_dirs = glob.glob(xml_shot_dir_pat)
  249. if len(xml_shot_dirs) < 1:
  250. # No shot Dirs defined
  251. return False
  252. for template in sorted(xml_shot_dirs):
  253. template_name = os.path.splitext(os.path.basename(template))[0]
  254. self.cmbDirStructure.addItem(template_name)
  255. return True
  256. # -------------------------------------------------------------------------------------
  257. def accept(self):
  258. """
  259. Check the connection.
  260. """
  261. if not self._gen.status(True):
  262. log.error("Could not establish connection with Generation. Make sure it is running.", True)
  263. return
  264. # Need edit rights for this one!
  265. if not self._gen.allow_changes(True):
  266. return
  267. ret, msg = control.add_shot(self.PROJECTNAME, self.plugName.text(), self.cmbDirStructure.currentText()+".xml", self._gen, self.cmbInsertPosition.currentIndex())
  268. if not ret:
  269. log.error(msg, True)
  270. return
  271. # Keep the dialog.
  272. if not self.chkAnother.isChecked():
  273. super(AddShotDialog, self).accept()
  274. else:
  275. shot_dir = os.path.join(self.SHOTDIR, self.plugName.text())
  276. self.plugName.setText("")
  277. self.lblStatus.setText("<html><head><style type=text/css>a {{ color: white; }}\n a:link {{color:white; text-decoration:none; }}\n a:hover {{ color:#ffcc00; text-decoration:underline; }}</style></head><body><font color='#00ee00'>Shot successfully created:</font> <a href='file:///{0}'>{0}</a></body></html>".format(shot_dir, shot_dir.replace("\\", "/")))
  278. self._status_fade.setDirection(QAbstractAnimation.Forward)
  279. self._status_fade.start()
  280. self.plugName.setFocus()
  281. # -------------------------------------------------------------------------------------
  282. def main():
  283. """docstring for main"""
  284. app = QApplication([])
  285. app.setStyle(QStyleFactory.create("plastique") )
  286. palette = QPalette(QColor(62, 62, 62), QColor(62, 62, 62))
  287. palette.setColor(palette.Highlight, QColor(255*0.6, 198*0.6, 0))
  288. app.setPalette(palette)
  289. apd = AddShotDialog()
  290. if apd.STATUS:
  291. if DEBUG:
  292. apd.setWindowTitle(apd.windowTitle() + " v" + __VERSION__)
  293. apd.show()
  294. sys.exit(app.exec_())
  295. if __name__ == '__main__':
  296. main()