Source code for jukeboxcore.addons.genesis.genesis

import os

from PySide import QtGui
from django.core.exceptions import ValidationError

from jukeboxcore.log import get_logger
log = get_logger(__name__)

import jukedj
from jukeboxcore import djadapter
from jukeboxcore.filesys import JB_File, TaskFileInfo
from jukeboxcore.plugins import JB_CorePlugin
from jukeboxcore.gui.main import JB_MainWindow, get_icon
from jukeboxcore.gui.widgets.filebrowser import FileBrowser
from jukeboxcore.gui.widgets.textedit import JB_PlainTextEdit
import genesis_ui


[docs]class GenesisWin(JB_MainWindow, genesis_ui.Ui_genesis_mwin): """The abstract genesis tool window The window uses the genesis.ui for it's layout. It has a tab widget with two browsers to select shots and assets. There is a field to write a comment and buttons for opening and saving. This is should be subclassed and :meth:`GenesisWin.open_shot`, :meth:`GenesisWin.open_asset`, :meth:`GenesisWin.save_shot`, :meth:`GenesisWin.save_asset`, :meth:`GenesisWin.get_current_file` should be reimplemented. Before creating an instance, call :meth:`GenesisWin.set_filetype` at least once. """ _filetype = None def __init__(self, parent=None, flags=0): """Constructs a new GenesisWin with the given parent :param parent: Optional - the parent of the window - default is None :type parent: QWidget :param flags: the window flags :type flags: QtCore.Qt.WindowFlags :raises: None """ super(GenesisWin, self).__init__(parent, flags) self.setupUi(self) self.setup_ui() self.setup_signals() if not self._filetype: log.warning('No Filetypes are allowed. Genesis will not show any files! Call set_filetype before instancing!') @classmethod
[docs] def set_filetype(cls, filetype): """Set the allowed filetypes for the taskfiles that should be handled by Genesis :param filetype: the filetype from :data:`djadapter.FILETYPES` :type filetypes: str :returns: None :rtype: None :raises: None """ cls._filetype = filetype
@classmethod
[docs] def get_filetype(cls, ): """Return the allowed filetype :returns: filetype from :data:`djadapter.FILETYPES` :rtype: str :raises: None """ return cls.filetype
[docs] def setup_ui(self, ): """Create the browsers and all necessary ui elements for the tool :returns: None :rtype: None :raises: None """ w = QtGui.QWidget(self) w.setLayout(self.central_vbox) self.setCentralWidget(w) releasetypes = [djadapter.RELEASETYPES['work'], djadapter.RELEASETYPES['release'], djadapter.RELEASETYPES['handoff']] self.browser = FileBrowser(self._filetype, releasetypes, self.get_current_file, self) self.central_vbox.insertWidget(0, self.browser) self.asset_comment_pte = self.create_comment_edit() self.browser.asset_vbox.addWidget(self.asset_open_pb) self.asset_new_hbox = QtGui.QHBoxLayout() self.asset_new_hbox.addWidget(self.asset_save_pb) self.asset_new_hbox.addWidget(self.asset_descriptor_lb) self.asset_new_hbox.addWidget(self.asset_descriptor_le) self.browser.asset_vbox.addLayout(self.asset_new_hbox) self.browser.asset_vbox.addWidget(self.asset_comment_pte) self.shot_comment_pte = self.create_comment_edit() self.browser.shot_vbox.addWidget(self.shot_open_pb) self.shot_new_hbox = QtGui.QHBoxLayout() self.shot_new_hbox.addWidget(self.shot_save_pb) self.shot_new_hbox.addWidget(self.shot_descriptor_lb) self.shot_new_hbox.addWidget(self.shot_descriptor_le) self.browser.shot_vbox.addLayout(self.shot_new_hbox) self.browser.shot_vbox.addWidget(self.shot_comment_pte) ph = "Enter New Descriptor" self.asset_descriptor_le.setPlaceholderText(ph) self.shot_descriptor_le.setPlaceholderText(ph) self.setup_icons()
[docs] def setup_icons(self, ): """Set all icons on buttons :returns: None :rtype: None :raises: None """ folder_icon = get_icon('glyphicons_144_folder_open.png', asicon=True) self.asset_open_pb.setIcon(folder_icon) self.shot_open_pb.setIcon(folder_icon) floppy_icon = get_icon('glyphicons_446_floppy_save.png', asicon=True) self.asset_save_pb.setIcon(floppy_icon) self.shot_save_pb.setIcon(floppy_icon)
[docs] def setup_signals(self, ): """Connect the signals with the slots to make the ui functional :returns: None :rtype: None :raises: None """ self.browser.shot_taskfile_sel_changed.connect(self.shot_taskfile_sel_changed) self.browser.asset_taskfile_sel_changed.connect(self.asset_taskfile_sel_changed) self.shot_open_pb.clicked.connect(self.shot_open_callback) self.asset_open_pb.clicked.connect(self.asset_open_callback) self.shot_save_pb.clicked.connect(self.shot_save_callback) self.asset_save_pb.clicked.connect(self.asset_save_callback)
[docs] def create_comment_edit(self, ): """Create a text edit for comments :returns: the created text edit :rtype: :class:`jukeboxcore.gui.widgets.textedit.JB_PlainTextEdit` :raises: None """ pte = JB_PlainTextEdit(parent=self) pte.set_placeholder("Enter a comment before saving...") pte.setMaximumHeight(120) return pte
[docs] def asset_taskfile_sel_changed(self, tf): """Callback for when the version selection has changed :param tf: the selected taskfileinfo :type tf: :class:`TaskFileInfo` | None :returns: None :rtype: None :raises: None """ self.asset_open_pb.setEnabled(bool(tf)) # only allow new, when there is an asset. if there is an asset, there should always be a task enablenew = bool(self.browser.assetbrws.selected_indexes(1)) and self.browser.get_releasetype() == djadapter.RELEASETYPES['work'] self.asset_save_pb.setEnabled(enablenew) self.asset_descriptor_le.setEnabled(enablenew) self.asset_comment_pte.setEnabled(enablenew) self.update_descriptor_le(self.asset_descriptor_le, tf)
[docs] def shot_taskfile_sel_changed(self, tf): """Callback for when the version selection has changed :param tf: the selected taskfileinfo :type tf: :class:`TaskFileInfo` | None :returns: None :rtype: None :raises: None """ self.shot_open_pb.setEnabled(bool(tf)) # only allow new, if the releasetype is work # only allow new, if there is a shot. if there is a shot, there should always be a task. enablenew = bool(self.browser.shotbrws.selected_indexes(1)) and self.browser.get_releasetype() == djadapter.RELEASETYPES['work'] self.shot_save_pb.setEnabled(enablenew) self.shot_descriptor_le.setEnabled(enablenew) self.shot_comment_pte.setEnabled(enablenew) self.update_descriptor_le(self.shot_descriptor_le, tf)
[docs] def update_descriptor_le(self, lineedit, tf): """Update the given line edit to show the descriptor that is stored in the index :param lineedit: the line edit to update with the descriptor :type lineedit: QLineEdit :param tf: the selected taskfileinfo :type tf: :class:`TaskFileInfo` | None :returns: None :rtype: None :raises: None """ if tf: descriptor = tf.descriptor lineedit.setText(descriptor) else: lineedit.setText("")
[docs] def get_current_file(self, ): """Return the taskfile that is currently open or None if no taskfile is open :returns: the open taskfile or None if no taskfile is open :rtype: :class:`djadapter.models.TaskFile` | None :raises: NotImplementedError """ raise NotImplementedError
[docs] def shot_open_callback(self, *args, **kwargs): """Callback for the shot open button :returns: None :rtype: None :raises: None """ tf = self.browser.get_current_selection(1) if not tf: return if not os.path.exists(tf.path): msg = 'The selected shot does not exist: %s' % tf.path log.error(msg) self.statusbar.showMessage(msg) return self.open_shot(tf)
[docs] def asset_open_callback(self, *args, **kwargs): """Callback for the shot open button :returns: None :rtype: None :raises: None """ tf = self.browser.get_current_selection(0) if not tf: return if not os.path.exists(tf.path): msg = 'The selected asset does not exist: %s' % tf.path log.error(msg) self.statusbar.showMessage(msg) return self.open_asset(tf)
[docs] def shot_save_callback(self, *args, **kwargs): """Callback for the shot open button :returns: None :rtype: None :raises: None """ tasksel = self.browser.shotbrws.selected_indexes(2) if not tasksel or not tasksel[0].isValid(): self.statusbar.showMessage('No task selected! Cannot save!') return taskitem = tasksel[0].internalPointer() task = taskitem.internal_data() rtype = djadapter.RELEASETYPES['work'] descriptor = self.shot_descriptor_le.text() if not self.check_selection_for_save(task, rtype, descriptor): return tfi = TaskFileInfo.get_next(task=task, releasetype=rtype, typ=self._filetype, descriptor=descriptor) self._save_tfi(tfi)
[docs] def asset_save_callback(self, *args, **kwargs): """Callback for the shot open button :returns: None :rtype: None :raises: None """ tasksel = self.browser.assetbrws.selected_indexes(2) if not tasksel or not tasksel[0].isValid(): self.statusbar.showMessage('No task selected! Cannot save!') return taskitem = tasksel[0].internalPointer() task = taskitem.internal_data() rtype = djadapter.RELEASETYPES['work'] descriptor = self.asset_descriptor_le.text() if not self.check_selection_for_save(task, rtype, descriptor): return tfi = TaskFileInfo.get_next(task=task, releasetype=rtype, typ=self._filetype, descriptor=descriptor) self._save_tfi(tfi)
def _save_tfi(self, tfi): """Save currently open scene with the information in the given taskfile info :param tfi: taskfile info :type tfi: :class:`TaskFileInfo` :returns: None :rtype: None :raises: None """ jbfile = JB_File(tfi) self.create_dir(jbfile) tf, note = self.create_db_entry(tfi) try: self.save_shot(jbfile, tf) except: tf.delete() note.delete() self.statusbar.showMessage('Saving failed!') log.exception("Saving failed!") return self.browser.update_model(tfi)
[docs] def create_dir(self, jbfile): """Create a dir for the given dirfile and display an error message, if it fails. :param jbfile: the jb file to make the directory for :type jbfile: class:`JB_File` :returns: None :rtype: None :raises: None """ try: jbfile.create_directory() except os.error: self.statusbar.showMessage('Could not create path: %s' % jbfile.get_path())
[docs] def open_shot(self, taskfile): """Open the given taskfile :param taskfile: the taskfile for the shot :type taskfile: :class:`djadapter.models.TaskFile` :returns: True if opening was successful :rtype: bool :raises: NotImplementedError """ raise NotImplementedError
[docs] def save_shot(self, jbfile): """Save the shot to the location of jbfile :param jbfile: the jbfile that can be used to query the location :type jbfile: :class:`jukeboxcore.filesys.JB_File` :returns: None :rtype: None :raises: NotImplementedError """ raise NotImplementedError
[docs] def open_asset(self, taskfile): """Open the given taskfile :param taskfile: the taskfile for the asset :type taskfile: :class:`djadapter.models.TaskFile` :returns: True if opening was successful :rtype: bool :raises: NotImplementedError """ raise NotImplementedError
[docs] def save_asset(self, taskfile): """Save the shot to the location of jbfile :param jbfile: the jbfile that can be used to query the location :type jbfile: :class:`jukeboxcore.filesys.JB_File` :returns: None :rtype: None :raises: NotImplementedError """ raise NotImplementedError
[docs] def check_selection_for_save(self, task, releasetype, descriptor): """Emit warnings if the descriptor is None or the current file is of a different task. :param task: the selected task :type task: :class:`djadapter.models.Task` :param releasetype: the releasetype to save (probably work) :type releasetype: str :param descriptor: the descriptor :type descriptor: str :returns: True if check was successfull. :rtype: bool :raises: None """ if not descriptor: self.statusbar.showMessage("Please provide a descriptor!") return False try: jukedj.validators.alphanum_vld(descriptor) except ValidationError: self.statusbar.showMessage("Descriptor contains characters other than alphanumerical ones.") return False cur = self.get_current_file() if cur and task != cur.task: self.statusbar.showMessage("Task is different. Not supported atm!") return False elif cur and releasetype != cur.releasetype: self.statusbar.showMessage("Releasetype is different. Not supported atm!") return False return True
[docs] def create_db_entry(self, tfi): """Create a db entry for the given task file info :param tfi: the info for a TaskFile entry in the db :type tfi: :class:`jukeboxcore.filesys.TaskFileInfo` :returns: the created taskfile and note :rtype: tuple :raises: ValidationError """ if tfi.task.department.assetflag: comment = self.asset_comment_pte.toPlainText() else: comment = self.shot_comment_pte.toPlainText() return tfi.create_db_entry(comment)
[docs]class Genesis(JB_CorePlugin): """Core plugin for all tools that implement opening and saving shots and assets in a software. """ author = "David Zuber" copyright = "2014" version = "0.1" description = "A abstract tool for saving and opening shots and assets."
[docs] def init(self, ): """Do nothing :returns: None :rtype: None :raises: None """ pass
[docs] def uninit(self): """Do nothing :returns: None :rtype: None :raises: None """ pass
@property
[docs] def GenesisWin(self, ): """Return the GenesisWin class :returns: the genesis win class :rtype: :class:`GenesisWin` :raises: None """ return GenesisWin