def __init__(self, *args, **kwargs): super(Widget_scriptPath, self).__init__(*args, **kwargs) self.installEventFilter(self) mainLayout = QHBoxLayout(self) mainLayout.setContentsMargins(0, 0, 0, 0) label = QLabel("Script Path : ") lineEdit = QLineEdit() button = QPushButton("...") button.setFixedWidth(40) button.clicked.connect(self.cmd_getPath) mainLayout.addWidget(label) mainLayout.addWidget(lineEdit) mainLayout.addWidget(button) self.lineEdit = lineEdit self.load_lineEdit_text(self.lineEdit, Widget_scriptPath.path_uiInfo) sizePolicy = QSizePolicy() sizePolicy.setVerticalPolicy(QSizePolicy.Fixed) sizePolicy.setHorizontalPolicy(QSizePolicy.Expanding) self.setSizePolicy(sizePolicy) self.lineEdit = lineEdit
def __init__(self, *args, **kwargs): title = "" if kwargs.has_key("title"): title = kwargs.pop("title") super(Widget_mesh, self).__init__(*args, **kwargs) label = QLabel(title) label.setFixedWidth(90) lineEdit = QLineEdit() button = QPushButton("Load") button.setFixedWidth(60) mainLayout = QHBoxLayout(self) mainLayout.setContentsMargins(0, 0, 0, 0) mainLayout.addWidget(label) mainLayout.addWidget(lineEdit) mainLayout.addWidget(button) self.title = title self.lineEdit = lineEdit QtCore.QObject.connect(button, QtCore.SIGNAL("clicked()"), self.loadSelected) WidgetInfo(self.lineEdit).loadText(Window.infoPath, 'Widget_mesh_%s_lineEdit' % title)
def __init__(self, *args, **kwargs): existing_widgets = args[0].findChildren(QDialog, Dialog_ReplacePath.objectName) if existing_widgets: map(lambda x: x.deleteLater(), existing_widgets) super(Dialog_ReplacePath, self).__init__(*args, **kwargs) self.installEventFilter(self) self.setObjectName(Dialog_ReplacePath.objectName) self.setWindowTitle(Window.title) mainLayout = QVBoxLayout(self) w_path = QWidget() lay_path = QHBoxLayout(w_path) comboBox = QComboBox() comboBox.setEditable(True) comboBox.setSizeAdjustPolicy(QComboBox.AdjustToContents) button_search = QPushButton("...") button_search.setFixedWidth(30) lay_path.addWidget(comboBox) lay_path.addWidget(button_search) button_replace = QPushButton("Replace Path") mainLayout.addWidget(w_path) mainLayout.addWidget(button_replace) self.resize(self.defaultWidth, self.defaultHeight) self.comboBox = comboBox QtCore.QObject.connect(button_search, QtCore.SIGNAL("clicked()"), self.searchFolder) QtCore.QObject.connect(button_replace, QtCore.SIGNAL("clicked()"), self.replaceAndMovePath) self.w_tree = Widget_FileTree()
def __init__(self, *args, **kwargs): self.saveInfo = False self.title = '' if kwargs.has_key('title'): self.title = kwargs.pop('title') if kwargs.has_key('saveInfo'): self.saveInfo = kwargs.pop('saveInfo') self.path_uiInfo = path_basedir + "/Widget_Controller_%s.json" % self.title super(Widget_loadObject, self).__init__(*args, **kwargs) self.installEventFilter( self ) mainLayout = QHBoxLayout(self) self.setStyleSheet( "font:12px;" ) label = QLabel( "%s : " % self.title ); label.setFixedWidth( 80 ) lineEdit = QLineEdit(); lineEdit.setStyleSheet( "padding:2px; padding-bottom:1px" ) button = QPushButton( "Load" ); button.setFixedWidth( 70 ) button.setStyleSheet( "padding:3px;padding-left:6px;padding-right:6px" ) mainLayout.addWidget( label ) mainLayout.addWidget( lineEdit ) mainLayout.addWidget( button ) button.clicked.connect( self.load_target ) self.lineEdit = lineEdit if self.saveInfo : self.load_lineEdit_text( self.lineEdit, self.path_uiInfo ) self.button = button
def __init__(self, *args, **kwargs ): QWidget.__init__( self, *args ) title = "" if kwargs.has_key( 'title' ): title = kwargs['title'] self.infoPath = cmds.about(pd=True) + "/sg/fingerWeightCopy/Widget_LoadVertex_%s.txt" % title sgCmds.makeFile( self.infoPath ) vLayout = QVBoxLayout( self ); vLayout.setContentsMargins(0,0,0,0) groupBox = QGroupBox( title ) groupBox.setAlignment( QtCore.Qt.AlignCenter ) vLayout.addWidget( groupBox ) hLayout = QHBoxLayout() lineEdit = QLineEdit() button = QPushButton("Load"); button.setFixedWidth( 50 ) hLayout.addWidget( lineEdit ) hLayout.addWidget( button ) groupBox.setLayout( hLayout ) self.lineEdit = lineEdit QtCore.QObject.connect( button, QtCore.SIGNAL( "clicked()" ), self.loadVertex ) self.loadInfo()
def __init__(self): super(ReplaceTool, self).__init__() self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowTitle('Replace Tool') self.setFixedHeight(100) self.setFixedWidth(320) lyt_main = QVBoxLayout() lbl_find = QLabel('Find:') lbl_find.setFixedWidth(55) self.ledit_find = QLineEdit() lbl_replace = QLabel('Replace:') lbl_replace.setFixedWidth(55) self.ledit_replace = QLineEdit() reg_ex = QRegExp("[a-zA-Z_]+") text_validator = QRegExpValidator(reg_ex, self.ledit_find) self.ledit_find.setValidator(text_validator) self.ledit_replace.setValidator(text_validator) lyt_find = QHBoxLayout() lyt_find.addWidget(lbl_find) lyt_find.addWidget(self.ledit_find) lyt_main.addLayout(lyt_find) lyt_replace = QHBoxLayout() lyt_replace.addWidget(lbl_replace) lyt_replace.addWidget(self.ledit_replace) lyt_main.addLayout(lyt_replace) btn_submit = QPushButton('Submit') btn_submit.setFixedHeight(20) btn_submit.setFixedWidth(55) lyt_submit_button = QHBoxLayout() lyt_submit_button.setAlignment(Qt.AlignRight) lyt_submit_button.addWidget(btn_submit) lyt_main.addLayout(lyt_submit_button) self.setLayout(lyt_main) btn_submit.clicked.connect(self.submit)
class MainWindow(QDialog): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.h0 = QHBoxLayout() self.lineArray = [] labelArray = [u'時間', u'值班', u'救護勤務', u'備勤', u'待命服勤', u'水源查察', u'消防查察', u'宣導勤務', u'訓(演)練', u'專案勤務', u'南山救護站'] for i in labelArray: label = QLabel(i) label.setFixedWidth(100) label.setAlignment(Qt.AlignCenter) self.h0.addWidget(label) self.h0.addSpacing(15) self.v0 = QVBoxLayout() self.v0.addLayout(self.h0) for i in xrange(24): hn = QHBoxLayout() for j in xrange(len(labelArray)): if j == 0: time = i+8 if (i+8)< 24 else i+8-24 line = QLabel("%s:00~%s:00" % (str(time).zfill(2), str(time+1).zfill(2))) else: line = QLineEdit() self.lineArray.append(line) line.setFixedWidth(100) hn.addSpacing(15) hn.addWidget(line) self.v0.addLayout(hn) self.h1 = QHBoxLayout() self.clearPB = QPushButton("Clear All") self.clearPB.setFixedWidth(200) self.clearPB.setFixedHeight(50) self.clearPB.clicked.connect(self.clear) self.h1.addStretch() self.h1.addWidget(self.clearPB) self.h1.addStretch() self.v0.addLayout(self.h1) self.setLayout(self.v0) def clear(self): for each in self.lineArray: each.setText('')
def firstPage(self): for i in range( self.mainLayout.count() ): item = self.mainLayout.itemAt(0) item.widget().setParent( None ) title = QLabel( "<p style='color:rgb( 137,129,120 )'>Welcome to the PingoTools Installer</p>" ) title.setFixedHeight( 50 ) titleFont = QFont() titleFont.setPixelSize( 18 ) titleFont.setBold( True ) titleFont.setFamily( "Helvetica [Cronyx]" ) title.setAlignment( QtCore.Qt.AlignCenter ) title.setFont( titleFont ) description = QLabel() description.setAlignment( QtCore.Qt.AlignCenter ) buttonsWidget = QWidget(); buttonsWidget.setMaximumHeight( 50 ) buttonsLayout = QHBoxLayout( buttonsWidget ) emptyArea = QLabel() buttonNext = QPushButton( 'Next > ' ) buttonCancel = QPushButton( 'Cancel' ) buttonsLayout.addWidget( emptyArea ) buttonsLayout.addWidget( buttonNext ); buttonNext.setFixedWidth( 100 ) buttonsLayout.addWidget( buttonCancel ); buttonCancel.setFixedWidth( 100 ) self.mainLayout.addWidget( title ) self.mainLayout.addWidget( description ) self.mainLayout.addWidget( buttonsWidget ) origWidth = 500 frontImage = QImage() frontImage.load( os.path.dirname( __file__ ) + '/images/pingoTools_main.jpg' ) trValue = QTransform().scale( float(origWidth)/frontImage.width(), float(origWidth)/frontImage.width() ) transformedImage = frontImage.transformed( trValue ) pixmap = QPixmap.fromImage( transformedImage ) description.setPixmap( pixmap ) description.setGeometry( 0,0, transformedImage.width() , transformedImage.height() ) description.paintEvent(QPaintEvent(QtCore.QRect( 0,0,self.width(), self.height() ))) QtCore.QObject.connect( buttonNext, QtCore.SIGNAL( 'clicked()' ), self.secondPage ) QtCore.QObject.connect( buttonCancel, QtCore.SIGNAL( 'clicked()' ), self.cmd_cancel )
def __init__(self, *args, **kwargs): existing_widgets = args[0].findChildren( QDialog, Dialog_ReplacePath_last.objectName) if existing_widgets: map(lambda x: x.deleteLater(), existing_widgets) super(Dialog_ReplacePath_last, self).__init__(*args, **kwargs) self.installEventFilter(self) self.setObjectName(Dialog_ReplacePath_last.objectName) self.setWindowTitle(Window.title) mainLayout = QVBoxLayout(self) w_msg = QLabel("0 Items aleady exists. Do you want to replace it?") w_list = QListWidget() w_buttons = QWidget() lay_buttons = QHBoxLayout(w_buttons) button_replace = QPushButton("Replace All") button_noReplace = QPushButton("Do not Replace") button_cancel = QPushButton("Cancel") lay_buttons.addWidget(button_replace) button_replace.setFixedWidth(120) lay_buttons.addWidget(button_noReplace) lay_buttons.addWidget(button_cancel) mainLayout.addWidget(w_msg) mainLayout.addWidget(w_list) mainLayout.addWidget(w_buttons) self.button_replace = button_replace self.w_list = w_list self.w_msg = w_msg self.w_list.setSelectionMode(QAbstractItemView.ExtendedSelection) QtCore.QObject.connect(button_replace, QtCore.SIGNAL("clicked()"), self.cmd_replace) QtCore.QObject.connect(button_noReplace, QtCore.SIGNAL("clicked()"), self.cmd_noReplace) QtCore.QObject.connect(button_cancel, QtCore.SIGNAL("clicked()"), self.deleteLater) QtCore.QObject.connect(self.w_list, QtCore.SIGNAL("itemSelectionChanged()"), self.changeButtonCondition) self.items = []
def lastPage(self): for i in range( self.mainLayout.count() ): item = self.mainLayout.itemAt(0) item.widget().setParent( None ) title = QLabel( "".decode( 'utf-8' ) ) title.setFixedHeight( 50 ) description = QLabel( "<p style='line-height:150%';'><b>PingoTools</b> 인스톨이 완료되었습니다.<p>".decode( 'utf-8' ) ) description.setAlignment( QtCore.Qt.AlignCenter ) buttonsWidget = QWidget(); buttonsWidget.setMaximumHeight( 50 ) buttonsLayout = QHBoxLayout( buttonsWidget ) emptyArea = QLabel() buttonClose = QPushButton( 'Close' ); buttonClose.setFixedWidth( 100 ) buttonsLayout.addWidget( emptyArea ) buttonsLayout.addWidget( buttonClose ) self.mainLayout.addWidget( title ) self.mainLayout.addWidget( description ) self.mainLayout.addWidget( buttonsWidget ) QtCore.QObject.connect( buttonClose, QtCore.SIGNAL( 'clicked()' ), self.cmd_close )
def secondPage(self): for i in range( self.mainLayout.count() ): item = self.mainLayout.itemAt(0) item.widget().setParent( None ) title = QLabel( "설치할 플러그인을 선택하십시오.".decode( 'utf-8' ) ) title.setFixedHeight( 50 ) listWidget = QListWidget() listWidget.setFixedHeight( 273 ) widgetItem_for2015 = QListWidgetItem("PingoTools for Maya2015", listWidget ) widgetItem_for2016 = QListWidgetItem("PingoTools for Maya2016", listWidget ) widgetItem_for2017 = QListWidgetItem("PingoTools for Maya2017", listWidget ) widgetItem_for2015.setCheckState( QtCore.Qt.Checked ) widgetItem_for2016.setCheckState( QtCore.Qt.Checked ) widgetItem_for2017.setCheckState( QtCore.Qt.Checked ) #widgetItem_for2015.setFlags( not QtCore.Qt.ItemIsSelectable ) buttonsWidget = QWidget(); buttonsWidget.setMaximumHeight( 50 ) buttonsLayout = QHBoxLayout( buttonsWidget ) emptyArea = QLabel() buttonBack = QPushButton( 'Back < ' ) buttonNext = QPushButton( 'Install' ) buttonCancel = QPushButton( 'Cancel' ) buttonsLayout.addWidget( emptyArea ) buttonsLayout.addWidget( buttonBack ); buttonBack.setFixedWidth( 100 ) buttonsLayout.addWidget( buttonNext ); buttonNext.setFixedWidth( 100 ) buttonsLayout.addWidget( buttonCancel ); buttonCancel.setFixedWidth( 100 ) self.mainLayout.addWidget( title ) self.mainLayout.addWidget( listWidget ) self.mainLayout.addWidget( buttonsWidget ) QtCore.QObject.connect( buttonBack, QtCore.SIGNAL( 'clicked()' ), self.firstPage ) QtCore.QObject.connect( buttonNext, QtCore.SIGNAL( 'clicked()' ), self.lastPage ) QtCore.QObject.connect( buttonCancel, QtCore.SIGNAL( 'clicked()' ), self.cmd_cancel )
class ToolBox(QVBoxLayout): sig = QtCore.Signal(object) listThread = None groupBoxThreadInfo = None threadvbox = None mode = None def __init__(self, mode, parentQWidget=None): QVBoxLayout.__init__(self) self.sig.connect(self.addThreadList) self.mode = mode self.sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding) self.groupBoxSearch = QGroupBox() self.groupBoxSearch.setStyleSheet( "QGroupBox {border: 1px solid gray; border-radius: 4px; };") vboxSearch = QVBoxLayout() self.searchTitle = QLabel("Search Messages") vboxSearch.addWidget(self.searchTitle) self.searchHLayout = QHBoxLayout() self.editTextSearch = QTextEdit('') self.editTextSearch.setFixedSize(200, 30) self.buttonSearch = QPushButton('Search') self.buttonSearch.setFixedSize(100, 30) self.buttonSearch.clicked.connect(self.searchMsg) vboxSearch.addWidget(self.editTextSearch) self.searchHLayout.addWidget(self.buttonSearch) self.searchCursor = QLabel() self.searchHLayout.addWidget(self.searchCursor) vboxSearch.addLayout(self.searchHLayout) self.browseHLayout = QHBoxLayout() self.buttonLookUp = QPushButton('\u21e7') #Arrow up self.buttonLookUp.setFixedWidth(100) self.buttonLookUp.clicked.connect(self.moveToPrev) self.buttonLookDown = QPushButton('\u21e9') #Arrow down self.buttonLookDown.setFixedWidth(100) self.buttonLookDown.clicked.connect(self.moveToNext) self.browseHLayout.addWidget(self.buttonLookUp) self.browseHLayout.addWidget(self.buttonLookDown) vboxSearch.addLayout(self.browseHLayout) self.groupBoxSearch.setLayout(vboxSearch) self.addWidget(self.groupBoxSearch) self.groupBoxSearch.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)) self.buttonHiddenLifelines = QPushButton('Show hidden life-lines') self.buttonHiddenLifelines.setFixedWidth(200) self.buttonHiddenLifelines.clicked.connect(self.showHiddenLifelines) self.addWidget(self.buttonHiddenLifelines) self.buttonHiddenMessages = QPushButton('Show hidden Messages') self.buttonHiddenMessages.setFixedWidth(200) self.buttonHiddenMessages.clicked.connect(self.showHiddenMessages) self.addWidget(self.buttonHiddenMessages) if const.mode_interactive == mode: self.buttonCapture = QPushButton('Capture') self.buttonCapture.setFixedWidth(200) self.buttonCapture.clicked.connect(self.notifyCapture) self.addWidget(self.buttonCapture) self.msgRcv = [] self.msgInfo = QLabel("Message Info.") self.groupBoxMessageInfo = QGroupBox() self.groupBoxMessageInfo.setStyleSheet( "QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;" ) vbox = QVBoxLayout() vbox.addWidget(self.msgInfo) self.tableTime = QtGui.QTableWidget(3, 2) self.tableTime.setHorizontalHeaderLabels(['-', 'time']) self.tableTime.setColumnWidth(0, 80) self.tableTime.setColumnWidth(1, 150) vwidth = self.tableTime.verticalHeader().length() hwidth = self.tableTime.horizontalHeader().height() fwidth = self.tableTime.frameWidth() * 2 self.tableTime.setFixedHeight(vwidth + hwidth + fwidth) self.tableTime.horizontalHeader().setStretchLastSection(True) self.tableTime.setItem(0, 0, QTableWidgetItem('begin')) self.tableTime.setItem(0, 1, QTableWidgetItem(' - ')) self.tableTime.setItem(1, 0, QTableWidgetItem('end')) self.tableTime.setItem(1, 1, QTableWidgetItem(' - ')) self.tableTime.setItem(2, 0, QTableWidgetItem('duration')) self.tableTime.setItem(2, 1, QTableWidgetItem(' - ')) vbox.addWidget(self.tableTime) self.titleArg = QLabel('Argument List') vbox.addWidget(self.titleArg) max_arg_num = 10 self.tableArgs = QtGui.QTableWidget(max_arg_num, 2) self.tableArgs.setHorizontalHeaderLabels(['type', 'value']) for idx in range(0, max_arg_num): self.tableArgs.setItem(idx, 0, QTableWidgetItem()) self.tableArgs.setItem(idx, 1, QTableWidgetItem()) self.tableArgs.horizontalHeader().setStretchLastSection(True) vbox.addWidget(self.tableArgs) self.titleArg = QLabel('Return Value List') vbox.addWidget(self.titleArg) max_ret_num = 4 self.tableRet = QtGui.QTableWidget(max_ret_num, 2) self.tableRet.setHorizontalHeaderLabels(['type', 'value']) for idx in range(0, max_ret_num): self.tableRet.setItem(idx, 0, QTableWidgetItem()) self.tableRet.setItem(idx, 1, QTableWidgetItem()) self.tableRet.horizontalHeader().setStretchLastSection(True) vwidth = self.tableRet.verticalHeader().length() hwidth = self.tableRet.horizontalHeader().height() fwidth = self.tableRet.frameWidth() * 2 self.tableRet.setFixedHeight(vwidth + hwidth + fwidth) vbox.addWidget(self.tableRet) self.buttonSrcView = QPushButton('view code') self.buttonSrcView.setFixedWidth(200) self.buttonSrcView.clicked.connect(self.openSourceViewer) self.buttonHide = QPushButton('Hide') self.buttonHide.setFixedWidth(200) self.buttonHide.clicked.connect(self.notifyHide) self.buttonHideAllMsg = QPushButton('Hide All') self.buttonHideAllMsg.setFixedWidth(200) self.buttonHideAllMsg.clicked.connect(self.hideAllMsgNamedAsSelected) self.groupBoxMessageInfo.setLayout(vbox) self.checkHideCircular = QCheckBox('Hide Circular Messages') self.checkHideCircular.setCheckState(QtCore.Qt.Unchecked) self.checkHideCircular.stateChanged.connect( self.changeHideCircularMessage) self.addWidget(self.checkHideCircular) self.addWidget(self.groupBoxMessageInfo) self.groupBoxMessageInfo.setSizePolicy(self.sizePolicy) def reset(self): for idx in reversed(range(0, self.listThread.count())): self.listThread.takeItem(idx) def setMsgInfoMessage(self, msg): self.strMessage = msg def changeHideCircularMessage(self, state): if state == QtCore.Qt.Unchecked: self.diagramView.hideCircularChanged(False) elif state == QtCore.Qt.Checked: self.diagramView.hideCircularChanged(True) def setMsgInfoModule(self, module): self.strModule = module def updateSearchStatus(self, curr, number): self.searchCursor.setText("%d/%d" % (curr, number)) def connectSourceViewer(self, viewer): self.srcViewer = viewer def openSourceViewer(self): self.srcViewer.openViewer(self.strModule, self.strMessage) def setMessageInfoTime(self, begin, end, duration): self.tableTime.item(0, 1).setText(begin) self.tableTime.item(1, 1).setText(end) self.tableTime.item(2, 1).setText(duration + ' msec') def setMessageInfoArg(self, listParam, listArg): if listArg: for idx, text in enumerate(listArg): self.tableArgs.item(idx, 1).setText(text) for idx, text in enumerate(listParam): self.tableArgs.item(idx, 0).setText(text) else: for idx in range(0, self.tableArgs.rowCount()): self.tableArgs.item(idx, 1).setText('') self.tableArgs.item(idx, 0).setText('') def setMessageInfoRet(self, listRet): if listRet: for idx, text in enumerate(listRet): self.tableRet.item(idx, 1).setText(text) else: for idx in range(0, self.tableRet.rowCount()): self.tableRet.item(idx, 1).setText('') self.tableRet.item(idx, 0).setText('') def notifyInteractiveStateChanged(self, state): if const.mode_interactive != self.mode: return if const.STATE_INTERACTIVE_CAPTURING == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Stop Capture') if const.STATE_INTERACTIVE_PROCESSING == state: self.buttonCapture.setEnabled(False) if const.STATE_INTERACTIVE_IDLE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') if const.STATE_INTERACTIVE_RESET == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') elif const.STATE_INTERACTIVE_ACTIVE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') def setMessageInfo(self, info): self.msgInfo.setText(info) def setAvailable(self, threads): self.sig.emit(threads) def toggleThreadDisplay(self, item): print(self.listThread.currentRow()) #if item.isSelected(): # print(item.text() + " is selected") #else: # print(item.text() + " is not selected") self.diagramView.showThread(self.listThread.currentRow(), item.isSelected()) def hideAllMsgNamedAsSelected(self): self.diagramView.hideAllMessageSelected() def addThreadList(self, threads): if not self.groupBoxThreadInfo: self.groupBoxThreadInfo = QGroupBox() self.threadInfo = QLabel("Thread Info.") self.groupBoxThreadInfo.setStyleSheet( "QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;" ) if not self.threadvbox: self.threadvbox = QVBoxLayout() if not self.listThread: self.listThread = QListWidget() self.listThread.setFixedWidth(200) self.listThread.setSelectionMode( QtGui.QAbstractItemView.MultiSelection) QtCore.QObject.connect(self.listThread, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), self.toggleThreadDisplay) self.threadvbox.addWidget(self.threadInfo) self.threadvbox.addWidget(self.listThread) self.groupBoxThreadInfo.setLayout(self.threadvbox) self.addWidget(self.groupBoxThreadInfo) self.groupBoxThreadInfo.setSizePolicy(self.sizePolicy) for id in threads: item = QtGui.QListWidgetItem(id) self.listThread.addItem(item) def connectController(self, controller): self.controller = controller self.connect(controller, QtCore.SIGNAL('setAvailable()'), self.setAvailable) def connectDiagramView(self, view): self.diagramView = view def disconnectMsgRcv(self, receiver): print("Implement this method !!! disconnectMsgRcv") def connectMsgRcv(self, receiver): self.msgRcv.append(receiver) def notifyHide(self): for rcv in self.msgRcv: rcv.activateHide(True) def showHiddenLifelines(self): response, selected_items = HiddenDialog.HiddenDialog.getSelectedItems( self.diagramView.getHiddenLifeLines()) if response: self.diagramView.showLifelines(selected_items) def showHiddenMessages(self): response, selected_items = HiddenMessageDialog.HiddenMessageDialog.getSelectedItems( self.diagramView.getHiddenMessages(), self.diagramView.getHiddenLifeLines()) if response: if selected_items[3] in self.diagramView.getHiddenLifeLines(): confirmation = ShowLifeLineDialog.ShowLifeLineDialog.confirmToShowLifeLine( selected_items[3]) if confirmation: self.diagramView.showLifelines([selected_items[3]]) self.diagramView.showMessages(selected_items) else: self.diagramView.showMessages(selected_items) def notifyCapture(self): for rcv in self.msgRcv: rcv.activateCapture(True) def moveToPrev(self): for rcv in self.msgRcv: rcv.moveToPrev() def moveToNext(self): for rcv in self.msgRcv: rcv.moveToNext() def searchMsg(self): str = self.editTextSearch.toPlainText() for rcv in self.msgRcv: rcv.searchMessage(str)
class LoginView(View): """`View` derived class. Defines the log in widget""" login = Signal(( str, str, str, bool, )) def __init__(self, parent=None): """ Init method. Initializes parent classes :param parent: Reference to a `QWidget` object to be used as parent """ super(LoginView, self).__init__(parent) self.createWidgets() self.createLayouts() self.setFixedSize(250, 340) def createLayouts(self): """Put widgets into layouts, thus creating the widget""" mainLayout = QHBoxLayout() fieldsLayout = QVBoxLayout() ftpInfoLayout = QHBoxLayout() buttonLayout = QHBoxLayout() mainLayout.addStretch(20) fieldsLayout.addStretch(80) fieldsLayout.addWidget(self.linkLabel) fieldsLayout.addWidget(self.line) fieldsLayout.addStretch(20) ftpInfoLayout.addWidget(self.hostLabel, 50, Qt.AlignLeft) ftpInfoLayout.addStretch(20) ftpInfoLayout.addWidget(self.sslLabel, 20, Qt.AlignRight) ftpInfoLayout.addWidget(self.sslCheck, 10, Qt.AlignRight) fieldsLayout.addLayout(ftpInfoLayout) fieldsLayout.addWidget(self.hostEdit) fieldsLayout.addWidget(self.usernameLabel) fieldsLayout.addWidget(self.usernameEdit) fieldsLayout.addWidget(self.passwdLabel) fieldsLayout.addWidget(self.passwdEdit) fieldsLayout.addStretch(30) buttonLayout.addStretch(50) buttonLayout.addWidget(self.loginButton, 50, Qt.AlignRight) fieldsLayout.addLayout(buttonLayout) fieldsLayout.addStretch(20) mainLayout.addLayout(fieldsLayout, 30) mainLayout.addStretch(20) self.setLayout(mainLayout) def createWidgets(self): """Create children widgets needed by this view""" fieldsWidth = 200 labelsFont = View.labelsFont() editsFont = View.editsFont() self.setLogo() self.hostLabel = QLabel(self) self.hostEdit = QLineEdit(self) self.sslLabel = QLabel(self) self.sslCheck = QCheckBox(self) self.hostLabel.setText('FTP Location') self.hostLabel.setFont(labelsFont) self.hostEdit.setFixedWidth(fieldsWidth) self.hostEdit.setFont(editsFont) self.sslLabel.setText('SSL') self.sslLabel.setFont(labelsFont) self.usernameLabel = QLabel(self) self.usernameEdit = QLineEdit(self) self.usernameLabel.setText('Username') self.usernameLabel.setFont(labelsFont) self.usernameEdit.setFixedWidth(fieldsWidth) self.usernameEdit.setFont(editsFont) self.passwdLabel = QLabel(self) self.passwdEdit = QLineEdit(self) self.passwdLabel.setText('Password') self.passwdLabel.setFont(labelsFont) self.passwdEdit.setFixedWidth(fieldsWidth) self.passwdEdit.setEchoMode(QLineEdit.Password) self.passwdEdit.setFont(editsFont) self.passwdEdit.returnPressed.connect(self.onLoginClicked) self.loginButton = QPushButton(self) self.loginButton.setText('Login') self.loginButton.setFont(labelsFont) self.loginButton.setFixedWidth(fieldsWidth / 2) self.loginButton.clicked.connect(self.onLoginClicked) # Sets previously stored values into the fields, if any settings = get_settings() self.hostEdit.setText(settings.value(SettingsKeys['host'], '')) self.usernameEdit.setText(settings.value(SettingsKeys['username'], '')) self.passwdEdit.setText( crypt.decrypt(settings.value(SettingsKeys['passwd'], ''))) # Unicode to boolean conversion ssl = settings.value(SettingsKeys['ssl'], u'true') ssl = True if ssl == u'true' else False self.sslCheck.setChecked(ssl) @Slot() def onLoginClicked(self): """ Slot. Called on the user clicks on the `loginButton` button """ # Takes out the user input from the fields host = self.hostEdit.text() username = self.usernameEdit.text() passwd = self.passwdEdit.text() ssl = self.sslCheck.isChecked() print 'Logging in: %s, %s, %s' % (host, username, '*' * len(passwd)) if len(host) > 0: # If the fields are valid, store them using a `QSettings` object # and triggers a log in request settings = get_settings() settings.setValue(SettingsKeys['host'], host) settings.setValue(SettingsKeys['username'], username) settings.setValue(SettingsKeys['passwd'], crypt.encrypt(passwd)) settings.setValue(SettingsKeys['ssl'], ssl) self.setEnabled(False) self.login.emit(host.strip(), username, passwd, ssl) @Slot() def onFailedLogIn(self): """ Slot. Called when the log in request fails """ # Enables the fields again for user input self.setEnabled(True)
class BookEditForm(QScrollArea, Ui_BookForm): """ Interface for book edit """ column = { 'id':0, 'barcode':1, 'title':2, 'author':3, 's_author':4, 'publisher':5, 'year':6, 'price':7, 'description':8, 'stock':9, 'image':10, 'availability':11 } IMG_SIZE = (150, 150) def __init__(self, record_id, parent=None): super(BookEditForm, self).__init__(parent) self.setupUi(self) # had to subclass this spinbox to support return grabbing self.edYear = ReturnKeySpinBox(self) self.edYearHolder.addWidget(self.edYear) # configuring id's for radio group self.radioAvailability.setId(self.rdSell,0) self.radioAvailability.setId(self.rdRent,1) self.radioAvailability.setId(self.rdInactive,2) # overlaying a clean button over the image (couldn't do it in designer) self.btnCleanImage = QPushButton() self.btnCleanImage.setIcon(QIcon(":icons/clean.png")) self.btnCleanImage.setFixedWidth(35) self.btnCleanImage.clicked.connect(self.clear_image) self.btnCleanImage.setVisible(False) clean_img_layout = QVBoxLayout(self.edImage) clean_img_layout.addWidget(self.btnCleanImage) clean_img_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) clean_img_layout.setContentsMargins(2,2,0,0) self._access = statics.access_level # for currency formatting self._locale = QLocale() self._record_id = record_id # had to hardcode these, wouldn't work otherwise: self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop) self.log = logging.getLogger('BookEditForm') self._subject_list = [] self._removed_subjects = [] self._added_subjects = [] self.setup_model() self.fill_form() self.setup_fields() self._old_data = self.extract_input(text_only=True) # flag to indicate whether there were changes to the fields self._dirty = False # user did input an image self._image_set = False # image changed during edit self._image_changed = False def is_dirty(self): return self._dirty def setup_fields(self): """ setting up validators and stuff """ # validators # forcing uppercasing on these fields self.edTitle.setValidator(UppercaseValidator()) self.edAuthor.setValidator(UppercaseValidator()) self.edSAuthor.setValidator(UppercaseValidator()) self.edPublisher.setValidator(UppercaseValidator()) self.edPrice.setValidator(CurrencyValidator(self.edPrice)) self.edBarcode.setValidator(NumericValidator()) self.edYear.setMinimum(1900) self.edYear.setMaximum(date.today().year) self.edYear.setValue(date.today().year) # fixing tab order self.setTabOrder(self.edPublisher, self.edYear) self.setTabOrder(self.edYear, self.edPrice) # connecting return key to tab lineEditList = self.findChildren(QLineEdit) for lineEdit in lineEditList: # had some problem with C++ originated objects if lineEdit.objectName() not in ['qt_spinbox_lineedit', 'edSubject']: lineEdit.returnPressed.connect(lineEdit.focusNextChild) # detect changes on line edits lineEdit.textChanged.connect(self.check_changes) # different behaviour for these self.edBarcode.textChanged.connect(self.check_barcode) self.edSubject.returnPressed.connect(self.on_btnAddSubject_clicked) # completers config_completer(self.edSubject, self._subject_model, "name") config_completer(self.edAuthor, self._author_model, "name") config_completer(self.edSAuthor, self._s_author_model, "name") config_completer(self.edPublisher, self._publisher_model, "name") # making image clickable clickable(self.edImage).connect(self.handle_image) def fill_form(self): # retrieving book info self.edBarcode.setText(self._record.value("barcode")) self.edTitle.setText(self._record.value("title")) self.edYear.setValue(self._record.value("year")) self.edDescription.setPlainText(self._record.value("description")) self.radioAvailability.button(self._record.value("availability")).setChecked(True) # retrieving image ba = QByteArray(self._record.value("image")) if ba: self._image_set = True img = qbytearray_to_qimage(ba) self.set_image(img, clean_visible=True) # currency # TODO: ARRUMAR self.edPrice.setText(self._locale.toString(self._record.value("price"), 'f', 2).replace('.','')) # qcompleter fields self.edAuthor.setText(self._get_name_from_id("author", self._record.value("author_id"))) self.edSAuthor.setText(self._get_name_from_id("s_author", self._record.value("s_author_id"))) self.edPublisher.setText(self._get_name_from_id("publisher", self._record.value("publisher_id"))) # retrieving subjects for subj_record in self._subj_records: self.add_subject([subj_record.value("id"),subj_record.value("name")]) # clearing changes self._added_subjects[:] = [] self.refresh_tableSubjects() def clear_image(self): img = QImage(":icons/no_image.png") self.set_image(img, clean_visible=False) if self._image_set: self._image_set = False self._image_changed = True def handle_image(self): image_path = QFileDialog.getOpenFileName(self, "Escolha uma imagem", os.getenv("HOME"), "Imagens (*.png, *.jpg *.bmp)")[0] if os.path.exists(image_path): self.set_image(QImage(image_path), clean_visible=True) self._image_set = True self._image_changed = True def set_image(self, img, clean_visible=False): pix = QPixmap.fromImage(img) pix = pix.scaled(self.IMG_SIZE[0], self.IMG_SIZE[1], Qt.KeepAspectRatio) self.edImage.setPixmap(pix) self.edImage.setScaledContents(True) self.btnCleanImage.setVisible(clean_visible) def check_changes(self, txt): # getting sender info sender = self.sender().objectName().split('ed')[1].lower() if sender != 'subject' and self._old_data[sender] != txt: self._dirty = True def check_barcode(self, txt): if len(txt) == self.edBarcode.maxLength(): self.edBarcode.focusNextChild() def extract_input(self, text_only=False): data = {} data['barcode'] = self.edBarcode.text() data['title'] = self.edTitle.text() # completer fields for c_field, line_edit in [("author", self.edAuthor), ("s_author", self.edSAuthor), ("publisher", self.edPublisher)]: if not text_only: data[c_field] = self._get_cfield_value(c_field, line_edit.text()) else: data[c_field] = line_edit.text() data['year'] = self.edYear.value() data['price'] = self._locale.toDouble(self.edPrice.text())[0] data['description'] = self.edDescription.toPlainText() if not text_only and self._image_changed and self._image_set: data['image'] = qpixmap_to_qbytearray(self.edImage.pixmap()) data['availability'] = self.radioAvailability.checkedId() return data def setup_model(self): db = Db_Instance("edit_book").get_instance() if not db.open(): self.log.error(db.lastError().text()) message = unicode("Erro de conexão\n\n""Banco de dados indisponível".decode('utf-8')) QMessageBox.critical(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) else: # book self._model = QSqlRelationalTableModel(self, db=db) self._model.setTable("book") self._model.setFilter("id = " + str(self._record_id)) # self._model.setRelation(self.column["author"], QSqlRelation("author", "id", "name")) # self._model.setRelation(self.column["s_author"], QSqlRelation("s_author", "id", "name")) # self._model.setRelation(self.column["publisher"], QSqlRelation("publisher", "id", "name")) self._model.select() self._record = self._model.record(0) # models for setting up qcompleters: # book_in_subject self._book_in_subj_model = QSqlTableModel(self, db=db) self._book_in_subj_model.setTable("book_in_subject") # subject self._subject_model = QSqlTableModel(self, db=db) self._subject_model.setTable("subject") self._subject_model.select() # author self._author_model = QSqlTableModel(self, db=db) self._author_model.setTable("author") self._author_model.select() # s_author self._s_author_model = QSqlTableModel(self, db=db) self._s_author_model.setTable("s_author") self._s_author_model.select() # publisher self._publisher_model = QSqlTableModel(self, db=db) self._publisher_model.setTable("publisher") self._publisher_model.select() # retrieving current subjects, should probably place this elsewhere but it's related to models self._subject_records = [] sql_statement = """SELECT id, name FROM subject s, book_in_subject b_s WHERE s.id = b_s.subject_id AND b_s.book_id = %s """ % str(self._record_id) read_only_subject_model = QSqlQueryModel() read_only_subject_model.setQuery(sql_statement, db) # checking query validity if not read_only_subject_model.lastError().isValid(): self._subj_records = iterate_model(read_only_subject_model) def update_data(self): data = self.extract_input() # checking fields that aren't inserted yet for val, model in [('author', self._author_model), ('s_author', self._s_author_model), ('publisher', self._publisher_model)]: if isinstance(data[val], unicode): # needs to be inserted model.insertRow(0) model.setData(model.index(0,1), data[val]) data[val] = submit_and_get_id(self, model, self.log) if not data[val]: # won't proceed if this fails return False for key,val in data.items(): self._model.setData(self._model.index(0, self.column[key]), val) if 'image' not in data and self._image_changed: # user cleared the image ok = self._model.setData(self._model.index(0, self.column['image']), None) print ok # try to commit changes if not self._model.submitAll(): self.log.error(self._model.lastError().text()) message = unicode("Erro de transação\n\n""Não foi possível salvar no banco de dados".decode('utf-8')) QMessageBox.critical(self, "Seareiros - Edição de Livro", message) return False else: # updating subjects error = False # added subjects for subj in self._added_subjects: # the list has the format [id, text] for existing subjects or [None, text] otherwise if not subj[0]: # need to insert the subject before associating it with the book self._subject_model.insertRow(0) self._subject_model.setData(self._subject_model.index(0,1), subj[1]) subj[0] = submit_and_get_id(self, self._subject_model, self.log) if not subj[0]: error = True break # have a valid record id for the subject to be associated self._book_in_subj_model.insertRow(0) self._book_in_subj_model.setData(self._book_in_subj_model.index(0,0), self._record_id) self._book_in_subj_model.setData(self._book_in_subj_model.index(0,1), subj[0]) ok = self._book_in_subj_model.submitAll() if not ok: error = True self.log.error(self._book_in_subj_model.setLastError().text()) break # removed subjects for removed_id in self._removed_subjects: self._book_in_subj_model.setFilter("book_id = %s AND subject_id = %s" % (str(self._record_id),str(removed_id))) self._book_in_subj_model.select() self._book_in_subj_model.removeRow(0) if self._book_in_subj_model.lastError().isValid(): error = True self.log.error(self._book_in_subj_model.lastError().text()) break if not error: message = unicode("Sucesso!\n\n""O livro foi atualizado com êxito no banco de dados".decode('utf-8')) QMessageBox.information(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) else: message = unicode("Erro\n\n""Associado alterado, " "porém ocorreu um problema ao salvar suas atividades".decode('utf-8')) QMessageBox.warning(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) # if I don't set this flag here it'll trigger a warning for altering data on the form self._dirty = False return True def _get_id_from_name(self, table, name): db = Db_Instance(table + "_fetch_" + name + "_id").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT id FROM %s WHERE name = :name" % table) query.bindValue(":name", name) query.exec_() if query.next(): return query.record().value("id") else: return None def _get_name_from_id(self, table, id): db = Db_Instance(table + "_fetch_" + str(id) + "_name").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT name FROM %s WHERE id = :id" % table) query.bindValue(":name", id) query.exec_() if query.next(): return query.record().value("name") else: return None @QtCore.Slot() def on_btnAddSubject_clicked(self): txt = self.edSubject.text() if txt != '': id = self._get_id_from_name('subject', self.edSubject.text()) if id: # known register data = [id, txt] else: # new data data = [None, txt] not_a_duplicate = self.add_subject(data) if not_a_duplicate: self.refresh_tableSubjects() self.edSubject.clear() self.edSubject.setFocus() @QtCore.Slot() def on_btnCleanSubjects_clicked(self): self.clear_table() self.edSubject.setFocus() def clear_table(self): # can't directly change activity_list here itens = [i for i in self._subject_list] for item in itens: self.remove_subject(item) self._added_subjects[:] = [] def add_subject(self, data): """ adds a subject to the list except for duplicates """ if data in self._subject_list: return False else: if self.is_in_del_queue(data[0]): self._removed_subjects.remove(data[0]) else: self._added_subjects.append(data) self._subject_list.append(data) # sorts by name self._subject_list.sort(key=operator.itemgetter(1)) return True def refresh_tableSubjects(self): if len(self._subject_list) > 0: self.tableSubjects.setColumnCount(len(self._subject_list[0])+1) col_labels = ["", "Nome", ""] self.tableSubjects.setHorizontalHeaderLabels(col_labels) self.tableSubjects.setColumnHidden(0, True) else: self.tableSubjects.setColumnCount(0) self.tableSubjects.setRowCount(len(self._subject_list)) for i, row in enumerate(self._subject_list): for j, col in enumerate(row): item = QTableWidgetItem(col) self.tableSubjects.setItem(i, j, item) # icon to remove rows individually remove_icon = QIcon(":icons/conn_failed.png") remove_btn = QPushButton(remove_icon, "") remove_btn.clicked.connect(partial(self.remove_subject, subject=row)) self.tableSubjects.setCellWidget(i, len(row), remove_btn) self.tableSubjects.resizeColumnsToContents() self.tableSubjects.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) def is_in_del_queue(self, id): return id in self._removed_subjects def is_in_add_queue(self, data): return data in self._added_subjects def remove_subject(self, subject): # remove a row based on its value self._subject_list.remove(subject) if self.is_in_add_queue(subject): # unqueue previously added activity self._added_subjects.remove(subject) else: id = subject[0] if id: self._removed_subjects.append(id) self.refresh_tableSubjects() def _get_cfield_value(self, c_field, text): if text == '': return None id = self._get_id_from_name(c_field, text) if id: return id else: return text
class BookAddForm(QScrollArea, Ui_BookForm): """ Interface for book input """ column = { "barcode": 1, "title": 2, "author": 3, "s_author": 4, "publisher": 5, "year": 6, "price": 7, "description": 8, "stock": 9, "image": 10, "availability": 11, } IMG_SIZE = (150, 150) def __init__(self, parent=None): super(BookAddForm, self).__init__(parent) self.setupUi(self) # had to subclass this spinbox to support return grabbing self.edYear = ReturnKeySpinBox(self) self.edYearHolder.addWidget(self.edYear) # configuring id's for radio group self.radioAvailability.setId(self.rdSell, 0) self.radioAvailability.setId(self.rdRent, 1) self.radioAvailability.setId(self.rdInactive, 2) self._access = statics.access_level # for currency formatting self._locale = QLocale() # had to hardcode these, wouldn't work otherwise: self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop) self.log = logging.getLogger("BookForm") self.setup_model() self.setup_fields() self._subject_list = [] # flag to indicate whether there were changes to the fields self._dirty = False # for use in selection docks, indicates a saved record self._book_id = None # user did input an image self._image_set = False # overlaying a clean button over the image (couldn't do it in designer) self.btnCleanImage = QPushButton() self.btnCleanImage.setIcon(QIcon(":icons/clean.png")) self.btnCleanImage.setFixedWidth(35) self.btnCleanImage.clicked.connect(self.clear_image) self.btnCleanImage.setVisible(False) clean_img_layout = QVBoxLayout(self.edImage) clean_img_layout.addWidget(self.btnCleanImage) clean_img_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) clean_img_layout.setContentsMargins(2, 2, 0, 0) def is_dirty(self): return self._dirty def setup_model(self): db = Db_Instance("form_book").get_instance() if not db.open(): self.log.error(db.lastError().text()) message = unicode("Erro de conexão\n\n" "Banco de dados indisponível".decode("utf-8")) QMessageBox.critical(self, "Seareiros - Cadastro de Livro", message) else: # book self._model = QSqlTableModel(self, db=db) self._model.setTable("book") # subject self._subject_model = QSqlTableModel(self, db=db) self._subject_model.setTable("subject") self._subject_model.select() # author self._author_model = QSqlTableModel(self, db=db) self._author_model.setTable("author") self._author_model.select() # sauthor self._s_author_model = QSqlTableModel(self, db=db) self._s_author_model.setTable("s_author") self._s_author_model.select() # publisher self._publisher_model = QSqlTableModel(self, db=db) self._publisher_model.setTable("publisher") self._publisher_model.select() # book subjects self._book_in_subj_model = QSqlTableModel(self, db=db) self._book_in_subj_model.setTable("book_in_subject") def setup_fields(self): """ setting up validators and stuff """ # validators # forcing uppercasing on these fields self.edTitle.setValidator(UppercaseValidator()) self.edAuthor.setValidator(UppercaseValidator()) self.edSAuthor.setValidator(UppercaseValidator()) self.edPublisher.setValidator(UppercaseValidator()) self.edPrice.setValidator(CurrencyValidator(self.edPrice)) self.edBarcode.setValidator(NumericValidator()) self.edYear.setMinimum(1900) self.edYear.setMaximum(date.today().year) self.edYear.setValue(date.today().year) # fixing tab order self.setTabOrder(self.edPublisher, self.edYear) self.setTabOrder(self.edYear, self.edPrice) # connecting return key to tab lineEditList = self.findChildren(QLineEdit) for lineEdit in lineEditList: # had some problem with C++ originated objects if lineEdit.objectName() not in ["qt_spinbox_lineedit", "edSubject"]: lineEdit.returnPressed.connect(lineEdit.focusNextChild) # detect changes on line edits lineEdit.textChanged.connect(self.check_changes) # different behaviour for these self.edBarcode.textChanged.connect(self.check_barcode) self.edSubject.returnPressed.connect(self.on_btnAddSubject_clicked) # completers config_completer(self.edSubject, self._subject_model, "name") config_completer(self.edAuthor, self._author_model, "name") config_completer(self.edSAuthor, self._s_author_model, "name") config_completer(self.edPublisher, self._publisher_model, "name") # making image clickable clickable(self.edImage).connect(self.handle_image) def clear_image(self): img = QImage(":icons/no_image.png") self.set_image(img) self._image_set = False self.btnCleanImage.setVisible(False) def handle_image(self): image_path = QFileDialog.getOpenFileName( self, "Escolha uma imagem", os.getenv("HOME"), "Imagens (*.png, *.jpg *.bmp)" )[0] if os.path.exists(image_path): self.set_image(QImage(image_path)) self._image_set = True self.btnCleanImage.setVisible(True) def set_image(self, img): pix = QPixmap.fromImage(img) pix = pix.scaled(self.IMG_SIZE[0], self.IMG_SIZE[1], Qt.KeepAspectRatio) self.edImage.setPixmap(pix) self.edImage.setScaledContents(True) def check_changes(self, txt): if txt != "": self._dirty = True def check_barcode(self, txt): if len(txt) == self.edBarcode.maxLength(): self.edBarcode.focusNextChild() def _get_id_from_name(self, table, name): db = Db_Instance(table + "_fetch_" + name + "_id").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT id FROM %s WHERE name = :name" % table) query.bindValue(":name", name) query.exec_() if query.next(): return query.record().value("id") else: return None def submit_data(self): data = self.extract_input() # checking fields that aren't inserted yet for val, model in [ ("author", self._author_model), ("s_author", self._s_author_model), ("publisher", self._publisher_model), ]: if isinstance(data[val], unicode): # needs to be inserted model.insertRow(0) model.setData(model.index(0, 1), data[val]) data[val] = submit_and_get_id(self, model, self.log) if not data[val]: # won't proceed if this fails return False # filling a book row self._model.insertRow(0) for key, val in data.items(): self._model.setData(self._model.index(0, self.column[key]), val) book_id = submit_and_get_id(self, self._model, self.log) if book_id: # for use in selection docks self.setBookId(book_id) # book sucessfully added, now associating related subjects subjects, new_subjects = self.extract_subjects_input() for subj in new_subjects: self._subject_model.insertRow(0) self._subject_model.setData(self._subject_model.index(0, 1), subj) id = submit_and_get_id(self, self._subject_model, self.log) if not id: # issue saving new subject return False subjects.append(int(id)) # associating book and it's subjects error = False for subj_id in subjects: self._book_in_subj_model.insertRow(0) self._book_in_subj_model.setData(self._book_in_subj_model.index(0, 0), book_id) self._book_in_subj_model.setData(self._book_in_subj_model.index(0, 1), subj_id) ok = self._book_in_subj_model.submitAll() if not ok: error = True break if error: self.log.error(self._book_in_subj_model.lastError.text()) message = unicode( "Erro\n\n" "Livro cadastrado, porém ocorreu um problema ao" " salvar os temas a que está associado".decode("utf-8") ) QMessageBox.warning(self, "Seareiros - Cadastro de Livro", message) return False else: message = unicode("Sucesso!\n\n" "O livro foi salvo com êxito no banco de dados".decode("utf-8")) QMessageBox.information(self, "Seareiros - Cadastro de Livro", message) return True # failed to insert a row return False def setBookId(self, id): self._book_id = id def get_added_record(self): db = Db_Instance("added_book_record").get_instance() if db.open() and self._book_id: query = QSqlQuery(db) query.prepare("SELECT * FROM book WHERE id = :id") query.bindValue(":id", self._book_id) query.exec_() if query.next(): return query.record() else: return None else: return None def clear(self): self._dirty = False lineEditList = self.findChildren(QLineEdit) for lineEdit in lineEditList: lineEdit.clear() self.clear_table() self.clear_image() self.edBarcode.setFocus() @QtCore.Slot() def on_btnAddSubject_clicked(self): txt = self.edSubject.text() if txt != "": id = self._get_id_from_name("subject", self.edSubject.text()) if id: # known register data = [id, txt] else: # new data data = [None, txt] not_a_duplicate = self.add_subject(data) if not_a_duplicate: self.refresh_tableSubjects() self.edSubject.clear() self.edSubject.setFocus() @QtCore.Slot() def on_btnCleanSubjects_clicked(self): self.clear_table() self.edSubject.setFocus() def clear_table(self): self._subject_list = [] self.tableSubjects.clear() self.refresh_tableSubjects() def add_subject(self, data): """ adds a subject to the list except for duplicates """ if data in self._subject_list: return False else: self._subject_list.append(data) # sorts by name self._subject_list.sort(key=operator.itemgetter(1)) return True def refresh_tableSubjects(self): if len(self._subject_list) > 0: self.tableSubjects.setColumnCount(len(self._subject_list[0]) + 1) col_labels = ["", "Nome", ""] self.tableSubjects.setHorizontalHeaderLabels(col_labels) self.tableSubjects.setColumnHidden(0, True) else: self.tableSubjects.setColumnCount(0) self.tableSubjects.setRowCount(len(self._subject_list)) for i, row in enumerate(self._subject_list): for j, col in enumerate(row): item = QTableWidgetItem(col) self.tableSubjects.setItem(i, j, item) # icon to remove rows individually remove_icon = QIcon(":icons/conn_failed.png") remove_btn = QPushButton(remove_icon, "") remove_btn.clicked.connect(partial(self.remove_subject, subject=row)) self.tableSubjects.setCellWidget(i, len(row), remove_btn) self.tableSubjects.resizeColumnsToContents() self.tableSubjects.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) def remove_subject(self, subject): # remove a row based on its value self._subject_list.remove(subject) self.refresh_tableSubjects() def extract_input(self): data = {} data["barcode"] = self.edBarcode.text() data["title"] = self.edTitle.text() # completer fields for c_field, line_edit in [ ("author", self.edAuthor), ("s_author", self.edSAuthor), ("publisher", self.edPublisher), ]: data[c_field] = self._get_cfield_value(c_field, line_edit.text()) data["year"] = self.edYear.value() data["price"] = self._locale.toDouble(self.edPrice.text())[0] data["description"] = self.edDescription.toPlainText() if self._image_set: data["image"] = qpixmap_to_qbytearray(self.edImage.pixmap()) data["availability"] = self.radioAvailability.checkedId() return data def _get_cfield_value(self, c_field, text): if text == "": return None id = self._get_id_from_name(c_field, text) if id: return id else: return text def extract_subjects_input(self): # grab id of selected activities subjects = [] new_subjects = [] for subj in self._subject_list: if subj[0]: # selected from previously added subjects subjects.append(subj[0]) else: # new subject new_subjects.append(subj[1]) return (subjects, new_subjects)
class MassAttribute_UI(QDialog): """ The main UI """ class Applikator(QObject): """ This is the core applier which toggle the display of the corresponding widget and handling events' connections """ def __init__(self, parent=None): super(MassAttribute_UI.Applikator, self).__init__() self.root = parent def widget_event(self, t): """ Return the correct widget's event depending on attribute's type :param t: the attribute's type :type t: str :return: the event :rtype : Signal """ return { 'float': self.root.W_EDI_float.valueChanged, 'enum': self.root.W_EDI_enum.currentIndexChanged, 'int': self.root.W_EDI_int.valueChanged, 'bool': self.root.W_EDI_bool.stateChanged, 'str': self.root.W_EDI_str.textChanged, 'd3': self.root.W_EDI_d3.valuesChanged, 'd4': self.root.W_EDI_d4.valuesChanged, 'color': self.root.W_EDI_color.colorChanged }[t] def unset_editors(self): """ Toggle off all editors and disconnect the current one """ for widget in (self.root.W_EDI_float, self.root.W_EDI_int, self.root.W_EDI_enum, self.root.W_EDI_bool, self.root.W_EDI_str, self.root.W_EDI_d3, self.root.W_EDI_d4, self.root.W_EDI_color): widget.setVisible(False) # trying to force disconnection try: self.widget_event(self.root.ctx).disconnect( self.root.apply_value) except (KeyError, RuntimeError): pass def prepare(applier_name): """ A decorator to prepare the attribute depending on type for the corresponding widget and getting the attribute's value :param applier_name: attribute's type :type applier_name: str """ def sub_wrapper(func): def wrapper(self, attr_path): self.unset_editors() self.root.ctx = applier_name self.root.__getattribute__('W_EDI_%s' % applier_name).setVisible(True) ret = func(self, cmds.getAttr(attr_path), attr_path) return ret return wrapper return sub_wrapper @staticmethod def get_bounds(obj, attr, min_default, max_default): """ Try to retrieve the range for the given attribute, if min or max fail it'll set default values :param obj: the object's name :type obj: str :param attr: attribute's name :type attr: str :param min_default: minimum default value :param max_default: max default value :type min_default: float | int :type max_default: float | int :return: minimum, maximum :rtype : tuple """ try: assert cmds.attributeQuery(attr, n=obj, mxe=True) maxi = cmds.attributeQuery(attr, n=obj, max=True)[0] except (RuntimeError, AssertionError): maxi = max_default try: assert cmds.attributeQuery(attr, n=obj, mne=True) mini = cmds.attributeQuery(attr, n=obj, min=True)[0] except (RuntimeError, AssertionError): mini = min_default return mini, maxi @prepare('float') def apply_float(self, value, path): """ Float attribute case :param value: attribute's value :param path: attribute's path = obj.attr """ obj, attr = path.split('.', 1) self.root.W_EDI_float.setRange( *self.get_bounds(obj, attr, -100.0, 100.0)) self.root.W_EDI_float.setValue(value) @prepare('enum') def apply_enum(self, value, path): """Enum case""" self.root.W_EDI_enum.clear() obj, attr = path.split('.', 1) try: enums = [ enum.split('=')[0] for enum in cmds.attributeQuery( attr, n=obj, listEnum=True)[0].split(':') ] except RuntimeError: self.apply_int(path) else: self.root.W_EDI_enum.addItems(enums) self.root.W_EDI_enum.setCurrentIndex( enums.index(cmds.getAttr(path, asString=True))) @prepare('int') def apply_int(self, value, path): """Integer case""" obj, attr = path.split('.', 1) self.root.W_EDI_int.setRange( *self.get_bounds(obj, attr, -1000, 1000)) self.root.W_EDI_int.setValue(value) @prepare('bool') def apply_bool(self, value, path): """Boolean case""" self.root.W_EDI_bool.setChecked(value) self.root.W_EDI_bool.setText(path.split('.', 1)[1]) @prepare('str') def apply_str(self, value, path): """String case""" self.root.W_EDI_str.setText(value) @prepare('d3') def apply_d3(self, value, path): """3D array case""" self.root.W_EDI_d3.setValues(value[0]) @prepare('d4') def apply_d4(self, value, path): """4D array case""" self.root.W_EDI_d4.setValues(value[0]) @prepare('color') def apply_color(self, value, path): """Color case""" try: colors = value[0] self.root.W_EDI_color.setColor([int(c * 255) for c in colors]) except TypeError: self.apply_int(value, path) class Attribute(str): """ A custom string attribute class to ship more information into the string variable """ def __new__(cls, path='', super_type=Object): obj, attr = path.split('.', 1) str_obj = str.__new__(cls, attr) str_obj.obj, str_obj.attr = obj, attr str_obj.path = path str_obj.super_type = super_type str_obj.type = None return str_obj # static variables to pre-load icons and attributes short names ctx_icons = { 'float': QIcon(':render_decomposeMatrix.png'), 'enum': QIcon(':showLineNumbers.png'), 'bool': QIcon(':out_decomposeMatrix.png'), 'time': QIcon(':time.svg'), 'byte': QIcon(':out_defaultTextureList.png'), 'angle': QIcon(':angleDim.png'), 'string': QIcon(':text.png'), 'float3': QIcon(':animCurveTA.svg'), 'float4': QIcon(':animCurveTA.svg'), 'color': QIcon(':clampColors.svg') } for ctx in ('doubleLinear', 'double', 'long', 'short'): ctx_icons[ctx] = ctx_icons['float'] ctx_icons['double3'] = ctx_icons['float3'] ctx_icons['double4'] = ctx_icons['float4'] ctx_wide = { 'float': ('float', 'doubleLinear', 'double', 'long', 'short'), 'enum': ('enum', ), 'bool': ('bool', ), 'time': ('time', ), 'byte': ('byte', ), 'angle': ('doubleAngle', ), 'string': ('string', ), 'float3': ('double3', 'float3'), 'float4': ('double4', 'float4'), 'color': ('color', ) } def __init__(self, parent=None): super(MassAttribute_UI, self).__init__(parent) # Abstract self.applier = self.Applikator(self) self.selection = [] self.callback = None self.ctx = None # storing found attributes' types to avoid double check self.solved = {} self.setLocale(QLocale.C) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_QuitOnClose) self.setFixedWidth(300) self.setWindowTitle('Massive Attribute Modifier') # UI L_main = QVBoxLayout() self.WV_title = QLabel('') self.WV_title.setVisible(False) self.WV_title.setFont(QFont('Verdana', 10)) self.WV_title.setContentsMargins(0, 0, 0, 7) self.WB_select = QPushButton('Select') self.WB_select.setVisible(False) self.WB_select.setFixedWidth(50) self.WB_select.clicked.connect(lambda: cmds.select(self.selection)) self.WB_update = QPushButton('Update') self.WB_update.setFixedWidth(50) self.WB_update.clicked.connect( lambda: self.update_attributes(cmds.ls(sl=True))) self.WV_search = Filter() self.WV_search.textChanged.connect(self.filter) self.WC_cases = QCheckBox('Case sensitive') self.WC_cases.stateChanged.connect(self.filter) self.WC_types = QCheckBox('Type filtering') self.WL_attrtype = QComboBox() self.WL_attrtype.setEnabled(False) for i, ctx in enumerate(sorted(self.ctx_wide)): self.WL_attrtype.addItem(ctx.title()) self.WL_attrtype.setItemIcon(i, self.ctx_icons[ctx]) L_attrtype = line(self.WC_types, self.WL_attrtype) self.WC_types.stateChanged.connect( partial(self.update_attributes, self.selection)) self.WC_types.stateChanged.connect(self.WL_attrtype.setEnabled) self.WL_attrtype.currentIndexChanged.connect(self.filter) self.WC_liveu = QCheckBox('Live') self.WC_liveu.stateChanged.connect(self.WB_update.setDisabled) self.WC_liveu.stateChanged.connect(self.set_callback) self.WC_histo = QCheckBox('Load history') self.WC_histo.setChecked(True) self.WC_histo.stateChanged.connect( partial(self.update_attributes, self.selection)) self.WC_child = QCheckBox('Children') self.WC_child.stateChanged.connect( partial(self.update_attributes, self.selection)) options = group( 'Options', line(self.WC_cases, L_attrtype), line(self.WC_child, self.WC_histo, self.WC_liveu, self.WB_update)) options.layout().setSpacing(2) self.WL_attributes = QTreeWidget() self.WL_attributes.setStyleSheet( 'QTreeView {alternate-background-color: #1b1b1b;}') self.WL_attributes.setAlternatingRowColors(True) self.WL_attributes.setHeaderHidden(True) self.WL_attributes.setRootIsDecorated(False) self.objs_attr = set() self.shps_attr = set() self.W_EDI_float = FloatBox() self.W_EDI_int = IntBox() self.W_EDI_enum = QComboBox() self.W_EDI_bool = QCheckBox() self.W_EDI_str = QLineEdit() self.W_EDI_d3 = Double3() self.W_EDI_d4 = Double4() self.W_EDI_color = ColorPicker() # Final layout L_title = line(self.WV_title, self.WB_select) L_title.setStretch(0, 1) L_main.addLayout(L_title) L_main.setAlignment(Qt.AlignLeft) L_main.addWidget(self.WV_search) L_main.addWidget(options) L_main.addWidget(self.WL_attributes) L_edits = col(self.W_EDI_bool, self.W_EDI_int, self.W_EDI_float, self.W_EDI_enum, self.W_EDI_str, self.W_EDI_d3, self.W_EDI_d4, self.W_EDI_color) L_edits.setContentsMargins(0, 8, 0, 0) L_main.addLayout(L_edits) L_main.setStretch(3, 1) L_main.setSpacing(2) self.appliers = { 'float': self.applier.apply_float, 'enum': self.applier.apply_enum, 'bool': self.applier.apply_bool, 'time': self.applier.apply_float, 'byte': self.applier.apply_int, 'angle': self.applier.apply_float, 'string': self.applier.apply_str, 'float3': self.applier.apply_d3, 'float4': self.applier.apply_d4, 'color': self.applier.apply_color } self.setLayout(L_main) # final settings self.WL_attributes.itemSelectionChanged.connect(self.update_setter) self.applier.unset_editors() def closeEvent(self, *args, **kwargs): self.set_callback(False) def set_callback(self, state): """ Toggle selection event callback :param state: checkbox's state :type state: bool | int """ if state and not self.callback: self.callback = MEventMessage.addEventCallback( 'SelectionChanged', self.update_attributes) self.update_attributes(cmds.ls(sl=True)) elif not state and self.callback: MMessage.removeCallback(self.callback) self.callback = None @staticmethod def format_title(nodes): """ Extract the matching characters from a given nodes selection, if begin matches it will return "joint*" with a wildcard when names don't match :param nodes: objects' list :type nodes: list | tuple :return: the formatted name with the corresponding characters :rtype : str """ res = None if nodes: # we get the first node as a reference node = nodes[0] # and compare with the other nodes subs = [w for w in nodes if w != node] l = 1 valid = True # will continue until l (length) match the full name's length or until names don't match while l < len(node) and valid: for sub in subs: if not sub.startswith(node[:l]): valid = False break else: l += 1 # if matching characters isn't long enough we only display the number of nodes selected if l <= 3: res = '%i objects' % len(nodes) # otherwise showing matching pattern elif l < len(node) or len(nodes) > 1: res = node[:l - 1] + '* (%i objects)' % len(nodes) else: res = node return res @staticmethod def get_history(node): """ Extract history for the given node :rtype: list """ return cmds.listHistory(node, il=2, pdo=True) or [] @staticmethod def get_shapes(node): """ Extract shape(s) for the given node :rtype: list """ return cmds.listRelatives(node, s=True, ni=True, f=True) def get_attributes_type(self, attrs): """ For a given list of attributes of type Attribute, will loop through and fill the type parameter of the attribute with the corresponding type, if type is invalid or not handled, it'll remove it :param attrs: attributes' list :type attrs: [MassAttribute_UI.Attribute] :return: cleaned and filled attributes' list :rtype: [MassAttribute_UI.Attribute] """ attrs = list(attrs) # first we sort the attributes' list attrs.sort() # then we try to extract the attribute's type for i, attr in enumerate(attrs): try: if attr.attr in self.solved: attr.type = self.solved[attr.attr] raise RuntimeError tpe = cmds.getAttr(attr.path, typ=True) assert tpe attr.type = tpe self.solved[attr.attr] = tpe except (AssertionError, ValueError, RuntimeError): pass # defining a to-remove list rm_list = set() layers = {'3': 'XYZ', '4': 'XYZW'} for i, attr in enumerate(attrs): if i in rm_list: continue # we handle some special cases here, if ever the attribute list contains RGB and separate R, G and B we # assume it's a color, if it's a double3 or float3 and we find the corresponding XYZ, we remove then to # avoid duplicates if attr.endswith('RGB'): if '%sR' % attr[:-3] in attrs: attr.type = 'color' for chan in 'RGB': rm_list.add(attrs.index('%s%s' % (attr[:-3], chan))) # if the attribute's type isn't in the list, we remove elif attr.type not in MassAttribute_UI.ctx_icons: rm_list.add(i) elif attr.endswith('R'): if '%sG' % attr[:-1] in attrs and attr[:-1] in attrs: attr.type = 'color' for chan in 'RGB': rm_list.add(attrs.index('%s%s' % (attr[:-1], chan))) elif attr.type in ('double3', 'double4', 'float3', 'float4'): if '%sX' % attr in attrs: for chan in layers[attr.type[-1]]: rm_list.add(attrs.index('%s%s' % (attr, chan))) # finally cleaning the list for i in sorted(rm_list, reverse=True): attrs.pop(i) return attrs def apply_value(self, value): """ When the value is modified in the UI, we forward the given value and applies to the object's :param value: attribute's value, mixed type :type value: mixed """ # We get the only selected object in list and get it's super type (Shape, History or Object) and # type (float, int, string) item = self.WL_attributes.selectedItems()[0] attr = item.attribute shape = attr.super_type == Shape histo = attr.super_type == History tpe = item.attribute.type # eq dict for each context value = { 'bool': bool, 'int': int, 'float': float, 'enum': int, 'str': str, 'd3': list, 'd4': list, 'color': list }[self.ctx](value) # converting the selection into a set cmds.undoInfo(openChunk=True) targets = set(self.selection) # we propagate to children if 'Children' checkbox is on if self.WC_child.isChecked(): for obj in list(targets): targets |= set(cmds.listRelatives(obj, ad=True)) # if the target attribute is on the history, we add all selection's history to the list if histo: for obj in list(targets): targets.remove(obj) targets |= set(self.get_history(obj)) # then we loop through target objects for obj in targets: # if the target is on the shape we get object's shape if shape and not histo: shapes = self.get_shapes(obj) if obj in shapes: continue else: obj = shapes[0] # then we try to apply depending on attribute's type try: correct_path = attr.path.replace(attr.obj, obj) if tpe == 'string': cmds.setAttr(correct_path, value, type='string') elif tpe in ('double3', 'double4', 'float3', 'float4', 'color'): cmds.setAttr(correct_path, *value, type='double%d' % len(value)) else: cmds.setAttr(correct_path, value) except RuntimeError: pass cmds.undoInfo(closeChunk=True) def update_setter(self): """ When the list's selection changes we update the applier widget """ item = self.WL_attributes.selectedItems() # abort if no item is selected if not len(item): return # getting attribute's parameter attr = item[0].attribute if len(self.selection): try: # looping until we find a context having the current attribute's type for applier in self.ctx_wide: if attr.type in self.ctx_wide[applier]: break # then we apply for the given path (obj.attribute) self.appliers[applier](attr.path) # and connecting event to the self.apply_value function self.applier.widget_event(self.ctx).connect(self.apply_value) # otherwise selection or type is invalid except IndexError: self.ctx = None def update_attributes(self, selection=None, *args): """ Update the attributes for the given selection, looping through objects' attributes, finding attr in common between all objects then cleaning the lists, doing the same for shapes and / or histories :param selection: object's selection """ # redefining lists as set to intersect union etc self.objs_attr = set() self.shps_attr = set() # pre init self.WL_attributes.clear() self.applier.unset_editors() self.selection = selection or (cmds.ls( sl=True) if self.WC_liveu.isChecked() else self.selection) self.WV_title.setText(self.format_title(self.selection)) self.WV_title.setVisible(bool(len(self.selection))) self.WB_select.setVisible(bool(len(self.selection))) if not len(self.selection): return def get_usable_attrs(obj, super_type): """ Small internal function to get a compatible attributes' list for the given object and assign the given super_type to it (Object, Shape or History) :param obj: object's name :type obj: str :param super_type: attribute's main type :type super_type: Object | Shape | History :return: """ return set([ MassAttribute_UI.Attribute('%s.%s' % (obj, attr), super_type) for attr in cmds.listAttr( obj, se=True, ro=False, m=True, w=True) ]) if len(self.selection): self.objs_attr = get_usable_attrs(self.selection[0], Object) # if we also want the object's history we add it to the initial set if self.WC_histo.isChecked(): for histo in self.get_history(self.selection[0]): self.objs_attr |= get_usable_attrs(histo, History) # filling the shape's set for shape in (self.get_shapes(self.selection[0]) or []): self.shps_attr |= get_usable_attrs(shape, Shape) # if selection's length bigger than one we compare by intersection with the other sets if len(self.selection) > 1: for obj in self.selection: sub_attr = get_usable_attrs(obj, Object) if self.WC_histo.isChecked(): for histo in self.get_history(obj): sub_attr |= get_usable_attrs(histo, History) self.objs_attr.intersection_update(sub_attr) for shape in (self.get_shapes(self.selection[0]) or []): self.shps_attr.intersection_update( get_usable_attrs(shape, Shape)) # finally getting all intersecting attributes' types self.objs_attr = self.get_attributes_type(self.objs_attr) self.shps_attr = self.get_attributes_type(self.shps_attr) # and filtering the list self.filter() def add_set(self, iterable, title=None): """ Adding the given iterable to the list with a first Separator object with given title :param iterable: list of item's attributes :param title: Separator's name """ if len(iterable): # if title is given we first add a Separator item to indicate coming list title if title: self.WL_attributes.addTopLevelItem( QTreeWidget_Separator(title)) items = [] for attr in sorted(iterable): item = QTreeWidgetItem([attr]) # assigning the attribute itself inside a custom parameter item.attribute = attr items.append(item) # finally adding all the items to the list self.WL_attributes.addTopLevelItems(items) def filter(self): """ Filter the list with UI's parameters, such as name or type filtering, etc """ # pre cleaning self.WL_attributes.clear() # using regex compile to avoid re execution over many attributes mask = self.WV_search.text() case = 0 if self.WC_cases.isChecked() else re.IGNORECASE re_start = re.compile(r'^%s.*?' % mask, case) re_cont = re.compile(r'.*?%s.*?' % mask, case) # getting the four different lists obj_start = set([at for at in self.objs_attr if re_start.search(at)]) shp_start = set([at for at in self.shps_attr if re_start.search(at)]) # if type filtering is one we only extract the wanted attribute's type if self.WC_types.isChecked(): obj_start = set([ at for at in obj_start if at.type in self.ctx_wide[ self.WL_attrtype.currentText().lower()] ]) shp_start = set([ at for at in shp_start if at.type in self.ctx_wide[ self.WL_attrtype.currentText().lower()] ]) # finally adding the current sets if there is a mask we add the also the containing matches if mask: # getting contains filtering and type containers filtering obj_contains = obj_start.symmetric_difference( set([at for at in self.objs_attr if re_cont.search(at)])) shp_contains = shp_start.symmetric_difference( set([at for at in self.shps_attr if re_cont.search(at)])) if self.WC_types.isChecked(): obj_contains = set([ at for at in obj_contains if at.type in self.ctx_wide[ self.WL_attrtype.currentText().lower()] ]) shp_contains = set([ at for at in shp_contains if at.type in self.ctx_wide[ self.WL_attrtype.currentText().lower()] ]) # adding the sets self.add_set(obj_start, 'Obj attributes starting with') self.add_set(obj_contains, 'Obj attributes containing') self.add_set(shp_start, 'Shape attributes starting with') self.add_set(shp_contains, 'Shape attributes containing') else: self.add_set(obj_start, 'Object\'s attributes') self.add_set(shp_start, 'Shape\'s attributes') # and we select the first one if ever there is something in the list if self.WL_attributes.topLevelItemCount(): self.WL_attributes.setItemSelected( self.WL_attributes.topLevelItem(1), True)
class MassAttribute_UI(QDialog): """ The main UI """ class Applikator(QObject): """ This is the core applier which toggle the display of the corresponding widget and handling events' connections """ def __init__(self, parent=None): super(MassAttribute_UI.Applikator, self).__init__() self.root = parent def widget_event(self, t): """ Return the correct widget's event depending on attribute's type :param t: the attribute's type :type t: str :return: the event :rtype : Signal """ return {'float': self.root.W_EDI_float.valueChanged, 'enum': self.root.W_EDI_enum.currentIndexChanged, 'int': self.root.W_EDI_int.valueChanged, 'bool': self.root.W_EDI_bool.stateChanged, 'str': self.root.W_EDI_str.textChanged, 'd3': self.root.W_EDI_d3.valuesChanged, 'd4': self.root.W_EDI_d4.valuesChanged, 'color': self.root.W_EDI_color.colorChanged}[t] def unset_editors(self): """ Toggle off all editors and disconnect the current one """ for widget in (self.root.W_EDI_float, self.root.W_EDI_int, self.root.W_EDI_enum, self.root.W_EDI_bool, self.root.W_EDI_str, self.root.W_EDI_d3, self.root.W_EDI_d4, self.root.W_EDI_color): widget.setVisible(False) # trying to force disconnection try: self.widget_event(self.root.ctx).disconnect(self.root.apply_value) except (KeyError, RuntimeError): pass def prepare(applier_name): """ A decorator to prepare the attribute depending on type for the corresponding widget and getting the attribute's value :param applier_name: attribute's type :type applier_name: str """ def sub_wrapper(func): def wrapper(self, attr_path): self.unset_editors() self.root.ctx = applier_name self.root.__getattribute__('W_EDI_%s' % applier_name).setVisible(True) ret = func(self, cmds.getAttr(attr_path), attr_path) return ret return wrapper return sub_wrapper @staticmethod def get_bounds(obj, attr, min_default, max_default): """ Try to retrieve the range for the given attribute, if min or max fail it'll set default values :param obj: the object's name :type obj: str :param attr: attribute's name :type attr: str :param min_default: minimum default value :param max_default: max default value :type min_default: float | int :type max_default: float | int :return: minimum, maximum :rtype : tuple """ try: assert cmds.attributeQuery(attr, n=obj, mxe=True) maxi = cmds.attributeQuery(attr, n=obj, max=True)[0] except (RuntimeError, AssertionError): maxi = max_default try: assert cmds.attributeQuery(attr, n=obj, mne=True) mini = cmds.attributeQuery(attr, n=obj, min=True)[0] except (RuntimeError, AssertionError): mini = min_default return mini, maxi @prepare('float') def apply_float(self, value, path): """ Float attribute case :param value: attribute's value :param path: attribute's path = obj.attr """ obj, attr = path.split('.', 1) self.root.W_EDI_float.setRange(*self.get_bounds(obj, attr, -100.0, 100.0)) self.root.W_EDI_float.setValue(value) @prepare('enum') def apply_enum(self, value, path): """Enum case""" self.root.W_EDI_enum.clear() obj, attr = path.split('.', 1) try: enums = [enum.split('=')[0] for enum in cmds.attributeQuery(attr, n=obj, listEnum=True)[0].split(':')] except RuntimeError: self.apply_int(path) else: self.root.W_EDI_enum.addItems(enums) self.root.W_EDI_enum.setCurrentIndex(enums.index(cmds.getAttr(path, asString=True))) @prepare('int') def apply_int(self, value, path): """Integer case""" obj, attr = path.split('.', 1) self.root.W_EDI_int.setRange(*self.get_bounds(obj, attr, -1000, 1000)) self.root.W_EDI_int.setValue(value) @prepare('bool') def apply_bool(self, value, path): """Boolean case""" self.root.W_EDI_bool.setChecked(value) self.root.W_EDI_bool.setText(path.split('.', 1)[1]) @prepare('str') def apply_str(self, value, path): """String case""" self.root.W_EDI_str.setText(value) @prepare('d3') def apply_d3(self, value, path): """3D array case""" self.root.W_EDI_d3.setValues(value[0]) @prepare('d4') def apply_d4(self, value, path): """4D array case""" self.root.W_EDI_d4.setValues(value[0]) @prepare('color') def apply_color(self, value, path): """Color case""" try: colors = value[0] self.root.W_EDI_color.setColor([int(c * 255) for c in colors]) except TypeError: self.apply_int(value, path) class Attribute(str): """ A custom string attribute class to ship more information into the string variable """ def __new__(cls, path='', super_type=Object): obj, attr = path.split('.', 1) str_obj = str.__new__(cls, attr) str_obj.obj, str_obj.attr = obj, attr str_obj.path = path str_obj.super_type = super_type str_obj.type = None return str_obj # static variables to pre-load icons and attributes short names ctx_icons = {'float': QIcon(':render_decomposeMatrix.png'), 'enum': QIcon(':showLineNumbers.png'), 'bool': QIcon(':out_decomposeMatrix.png'), 'time': QIcon(':time.svg'), 'byte': QIcon(':out_defaultTextureList.png'), 'angle': QIcon(':angleDim.png'), 'string': QIcon(':text.png'), 'float3': QIcon(':animCurveTA.svg'), 'float4': QIcon(':animCurveTA.svg'), 'color': QIcon(':clampColors.svg')} for ctx in ('doubleLinear', 'double', 'long', 'short'): ctx_icons[ctx] = ctx_icons['float'] ctx_icons['double3'] = ctx_icons['float3'] ctx_icons['double4'] = ctx_icons['float4'] ctx_wide = {'float': ('float', 'doubleLinear', 'double', 'long', 'short'), 'enum': ('enum',), 'bool': ('bool',), 'time': ('time',), 'byte': ('byte',), 'angle': ('doubleAngle',), 'string': ('string',), 'float3': ('double3', 'float3'), 'float4': ('double4', 'float4'), 'color': ('color',)} def __init__(self, parent=None): super(MassAttribute_UI, self).__init__(parent) # Abstract self.applier = self.Applikator(self) self.selection = [] self.callback = None self.ctx = None # storing found attributes' types to avoid double check self.solved = {} self.setLocale(QLocale.C) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_QuitOnClose) self.setFixedWidth(300) self.setWindowTitle('Massive Attribute Modifier') # UI L_main = QVBoxLayout() self.WV_title = QLabel('') self.WV_title.setVisible(False) self.WV_title.setFont(QFont('Verdana', 10)) self.WV_title.setContentsMargins(0, 0, 0, 7) self.WB_select = QPushButton('Select') self.WB_select.setVisible(False) self.WB_select.setFixedWidth(50) self.WB_select.clicked.connect(lambda: cmds.select(self.selection)) self.WB_update = QPushButton('Update') self.WB_update.setFixedWidth(50) self.WB_update.clicked.connect(lambda:self.update_attributes(cmds.ls(sl=True))) self.WV_search = Filter() self.WV_search.textChanged.connect(self.filter) self.WC_cases = QCheckBox('Case sensitive') self.WC_cases.stateChanged.connect(self.filter) self.WC_types = QCheckBox('Type filtering') self.WL_attrtype = QComboBox() self.WL_attrtype.setEnabled(False) for i, ctx in enumerate(sorted(self.ctx_wide)): self.WL_attrtype.addItem(ctx.title()) self.WL_attrtype.setItemIcon(i, self.ctx_icons[ctx]) L_attrtype = line(self.WC_types, self.WL_attrtype) self.WC_types.stateChanged.connect(partial(self.update_attributes, self.selection)) self.WC_types.stateChanged.connect(self.WL_attrtype.setEnabled) self.WL_attrtype.currentIndexChanged.connect(self.filter) self.WC_liveu = QCheckBox('Live') self.WC_liveu.stateChanged.connect(self.WB_update.setDisabled) self.WC_liveu.stateChanged.connect(self.set_callback) self.WC_histo = QCheckBox('Load history') self.WC_histo.setChecked(True) self.WC_histo.stateChanged.connect(partial(self.update_attributes, self.selection)) self.WC_child = QCheckBox('Children') self.WC_child.stateChanged.connect(partial(self.update_attributes, self.selection)) options = group('Options', line(self.WC_cases, L_attrtype), line(self.WC_child, self.WC_histo, self.WC_liveu, self.WB_update)) options.layout().setSpacing(2) self.WL_attributes = QTreeWidget() self.WL_attributes.setStyleSheet('QTreeView {alternate-background-color: #1b1b1b;}') self.WL_attributes.setAlternatingRowColors(True) self.WL_attributes.setHeaderHidden(True) self.WL_attributes.setRootIsDecorated(False) self.objs_attr = set() self.shps_attr = set() self.W_EDI_float = FloatBox() self.W_EDI_int = IntBox() self.W_EDI_enum = QComboBox() self.W_EDI_bool = QCheckBox() self.W_EDI_str = QLineEdit() self.W_EDI_d3 = Double3() self.W_EDI_d4 = Double4() self.W_EDI_color = ColorPicker() # Final layout L_title = line(self.WV_title, self.WB_select) L_title.setStretch(0, 1) L_main.addLayout(L_title) L_main.setAlignment(Qt.AlignLeft) L_main.addWidget(self.WV_search) L_main.addWidget(options) L_main.addWidget(self.WL_attributes) L_edits = col(self.W_EDI_bool, self.W_EDI_int, self.W_EDI_float, self.W_EDI_enum, self.W_EDI_str, self.W_EDI_d3, self.W_EDI_d4, self.W_EDI_color) L_edits.setContentsMargins(0, 8, 0, 0) L_main.addLayout(L_edits) L_main.setStretch(3, 1) L_main.setSpacing(2) self.appliers = {'float': self.applier.apply_float, 'enum': self.applier.apply_enum, 'bool': self.applier.apply_bool, 'time': self.applier.apply_float, 'byte': self.applier.apply_int, 'angle': self.applier.apply_float, 'string': self.applier.apply_str, 'float3': self.applier.apply_d3, 'float4': self.applier.apply_d4, 'color': self.applier.apply_color} self.setLayout(L_main) # final settings self.WL_attributes.itemSelectionChanged.connect(self.update_setter) self.applier.unset_editors() def closeEvent(self, *args, **kwargs): self.set_callback(False) def set_callback(self, state): """ Toggle selection event callback :param state: checkbox's state :type state: bool | int """ if state and not self.callback: self.callback = MEventMessage.addEventCallback('SelectionChanged', self.update_attributes) self.update_attributes(cmds.ls(sl=True)) elif not state and self.callback: MMessage.removeCallback(self.callback) self.callback = None @staticmethod def format_title(nodes): """ Extract the matching characters from a given nodes selection, if begin matches it will return "joint*" with a wildcard when names don't match :param nodes: objects' list :type nodes: list | tuple :return: the formatted name with the corresponding characters :rtype : str """ res = None if nodes: # we get the first node as a reference node = nodes[0] # and compare with the other nodes subs = [w for w in nodes if w != node] l = 1 valid = True # will continue until l (length) match the full name's length or until names don't match while l < len(node) and valid: for sub in subs: if not sub.startswith(node[:l]): valid = False break else: l += 1 # if matching characters isn't long enough we only display the number of nodes selected if l <= 3: res = '%i objects' % len(nodes) # otherwise showing matching pattern elif l < len(node) or len(nodes) > 1: res = node[:l - 1] + '* (%i objects)' % len(nodes) else: res = node return res @staticmethod def get_history(node): """ Extract history for the given node :rtype: list """ return cmds.listHistory(node, il=2, pdo=True) or [] @staticmethod def get_shapes(node): """ Extract shape(s) for the given node :rtype: list """ return cmds.listRelatives(node, s=True, ni=True, f=True) def get_attributes_type(self, attrs): """ For a given list of attributes of type Attribute, will loop through and fill the type parameter of the attribute with the corresponding type, if type is invalid or not handled, it'll remove it :param attrs: attributes' list :type attrs: [MassAttribute_UI.Attribute] :return: cleaned and filled attributes' list :rtype: [MassAttribute_UI.Attribute] """ attrs = list(attrs) # first we sort the attributes' list attrs.sort() # then we try to extract the attribute's type for i, attr in enumerate(attrs): try: if attr.attr in self.solved: attr.type = self.solved[attr.attr] raise RuntimeError tpe = cmds.getAttr(attr.path, typ=True) assert tpe attr.type = tpe self.solved[attr.attr] = tpe except (AssertionError, ValueError, RuntimeError): pass # defining a to-remove list rm_list = set() layers = {'3': 'XYZ', '4': 'XYZW'} for i, attr in enumerate(attrs): if i in rm_list: continue # we handle some special cases here, if ever the attribute list contains RGB and separate R, G and B we # assume it's a color, if it's a double3 or float3 and we find the corresponding XYZ, we remove then to # avoid duplicates if attr.endswith('RGB'): if '%sR' % attr[:-3] in attrs: attr.type = 'color' for chan in 'RGB': rm_list.add(attrs.index('%s%s' % (attr[:-3], chan))) # if the attribute's type isn't in the list, we remove elif attr.type not in MassAttribute_UI.ctx_icons: rm_list.add(i) elif attr.endswith('R'): if '%sG' % attr[:-1] in attrs and attr[:-1] in attrs: attr.type = 'color' for chan in 'RGB': rm_list.add(attrs.index('%s%s' % (attr[:-1], chan))) elif attr.type in ('double3', 'double4', 'float3', 'float4'): if '%sX' % attr in attrs: for chan in layers[attr.type[-1]]: rm_list.add(attrs.index('%s%s' % (attr, chan))) # finally cleaning the list for i in sorted(rm_list, reverse=True): attrs.pop(i) return attrs def apply_value(self, value): """ When the value is modified in the UI, we forward the given value and applies to the object's :param value: attribute's value, mixed type :type value: mixed """ # We get the only selected object in list and get it's super type (Shape, History or Object) and # type (float, int, string) item = self.WL_attributes.selectedItems()[0] attr = item.attribute shape = attr.super_type == Shape histo = attr.super_type == History tpe = item.attribute.type # eq dict for each context value = {'bool': bool, 'int': int, 'float': float, 'enum': int, 'str': str, 'd3': list, 'd4': list, 'color': list}[self.ctx](value) # converting the selection into a set cmds.undoInfo(openChunk=True) targets = set(self.selection) # we propagate to children if 'Children' checkbox is on if self.WC_child.isChecked(): for obj in list(targets): targets |= set(cmds.listRelatives(obj, ad=True)) # if the target attribute is on the history, we add all selection's history to the list if histo: for obj in list(targets): targets.remove(obj) targets |= set(self.get_history(obj)) # then we loop through target objects for obj in targets: # if the target is on the shape we get object's shape if shape and not histo: shapes = self.get_shapes(obj) if obj in shapes: continue else: obj = shapes[0] # then we try to apply depending on attribute's type try: correct_path = attr.path.replace(attr.obj, obj) if tpe == 'string': cmds.setAttr(correct_path, value, type='string') elif tpe in ('double3', 'double4', 'float3', 'float4', 'color'): cmds.setAttr(correct_path, *value, type='double%d' % len(value)) else: cmds.setAttr(correct_path, value) except RuntimeError: pass cmds.undoInfo(closeChunk=True) def update_setter(self): """ When the list's selection changes we update the applier widget """ item = self.WL_attributes.selectedItems() # abort if no item is selected if not len(item): return # getting attribute's parameter attr = item[0].attribute if len(self.selection): try: # looping until we find a context having the current attribute's type for applier in self.ctx_wide: if attr.type in self.ctx_wide[applier]: break # then we apply for the given path (obj.attribute) self.appliers[applier](attr.path) # and connecting event to the self.apply_value function self.applier.widget_event(self.ctx).connect(self.apply_value) # otherwise selection or type is invalid except IndexError: self.ctx = None def update_attributes(self, selection=None, *args): """ Update the attributes for the given selection, looping through objects' attributes, finding attr in common between all objects then cleaning the lists, doing the same for shapes and / or histories :param selection: object's selection """ # redefining lists as set to intersect union etc self.objs_attr = set() self.shps_attr = set() # pre init self.WL_attributes.clear() self.applier.unset_editors() self.selection = selection or (cmds.ls(sl=True) if self.WC_liveu.isChecked() else self.selection) self.WV_title.setText(self.format_title(self.selection)) self.WV_title.setVisible(bool(len(self.selection))) self.WB_select.setVisible(bool(len(self.selection))) if not len(self.selection): return def get_usable_attrs(obj, super_type): """ Small internal function to get a compatible attributes' list for the given object and assign the given super_type to it (Object, Shape or History) :param obj: object's name :type obj: str :param super_type: attribute's main type :type super_type: Object | Shape | History :return: """ return set([MassAttribute_UI.Attribute('%s.%s' % (obj, attr), super_type) for attr in cmds.listAttr(obj, se=True, ro=False, m=True, w=True)]) if len(self.selection): self.objs_attr = get_usable_attrs(self.selection[0], Object) # if we also want the object's history we add it to the initial set if self.WC_histo.isChecked(): for histo in self.get_history(self.selection[0]): self.objs_attr |= get_usable_attrs(histo, History) # filling the shape's set for shape in (self.get_shapes(self.selection[0]) or []): self.shps_attr |= get_usable_attrs(shape, Shape) # if selection's length bigger than one we compare by intersection with the other sets if len(self.selection) > 1: for obj in self.selection: sub_attr = get_usable_attrs(obj, Object) if self.WC_histo.isChecked(): for histo in self.get_history(obj): sub_attr |= get_usable_attrs(histo, History) self.objs_attr.intersection_update(sub_attr) for shape in (self.get_shapes(self.selection[0]) or []): self.shps_attr.intersection_update(get_usable_attrs(shape, Shape)) # finally getting all intersecting attributes' types self.objs_attr = self.get_attributes_type(self.objs_attr) self.shps_attr = self.get_attributes_type(self.shps_attr) # and filtering the list self.filter() def add_set(self, iterable, title=None): """ Adding the given iterable to the list with a first Separator object with given title :param iterable: list of item's attributes :param title: Separator's name """ if len(iterable): # if title is given we first add a Separator item to indicate coming list title if title: self.WL_attributes.addTopLevelItem(QTreeWidget_Separator(title)) items = [] for attr in sorted(iterable): item = QTreeWidgetItem([attr]) # assigning the attribute itself inside a custom parameter item.attribute = attr items.append(item) # finally adding all the items to the list self.WL_attributes.addTopLevelItems(items) def filter(self): """ Filter the list with UI's parameters, such as name or type filtering, etc """ # pre cleaning self.WL_attributes.clear() # using regex compile to avoid re execution over many attributes mask = self.WV_search.text() case = 0 if self.WC_cases.isChecked() else re.IGNORECASE re_start = re.compile(r'^%s.*?' % mask, case) re_cont = re.compile(r'.*?%s.*?' % mask, case) # getting the four different lists obj_start = set([at for at in self.objs_attr if re_start.search(at)]) shp_start = set([at for at in self.shps_attr if re_start.search(at)]) # if type filtering is one we only extract the wanted attribute's type if self.WC_types.isChecked(): obj_start = set([at for at in obj_start if at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]]) shp_start = set([at for at in shp_start if at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]]) # finally adding the current sets if there is a mask we add the also the containing matches if mask: # getting contains filtering and type containers filtering obj_contains = obj_start.symmetric_difference(set([at for at in self.objs_attr if re_cont.search(at)])) shp_contains = shp_start.symmetric_difference(set([at for at in self.shps_attr if re_cont.search(at)])) if self.WC_types.isChecked(): obj_contains = set([at for at in obj_contains if at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]]) shp_contains = set([at for at in shp_contains if at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]]) # adding the sets self.add_set(obj_start, 'Obj attributes starting with') self.add_set(obj_contains, 'Obj attributes containing') self.add_set(shp_start, 'Shape attributes starting with') self.add_set(shp_contains, 'Shape attributes containing') else: self.add_set(obj_start, 'Object\'s attributes') self.add_set(shp_start, 'Shape\'s attributes') # and we select the first one if ever there is something in the list if self.WL_attributes.topLevelItemCount(): self.WL_attributes.setItemSelected(self.WL_attributes.topLevelItem(1), True)
class ToolBox(QVBoxLayout): sig = QtCore.Signal(object) listThread = None groupBoxThreadInfo = None threadvbox = None mode = None def __init__(self, mode, parentQWidget = None): QVBoxLayout.__init__(self) self.sig.connect(self.addThreadList) self.mode = mode self.sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding) self.groupBoxSearch = QGroupBox() self.groupBoxSearch.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 4px; };") vboxSearch = QVBoxLayout() self.searchTitle = QLabel("Search Messages") vboxSearch.addWidget(self.searchTitle) self.searchHLayout = QHBoxLayout() self.editTextSearch = QTextEdit('') self.editTextSearch.setFixedSize(200,30) self.buttonSearch = QPushButton('Search') self.buttonSearch.setFixedSize(100,30) self.buttonSearch.clicked.connect(self.searchMsg) vboxSearch.addWidget(self.editTextSearch) self.searchHLayout.addWidget(self.buttonSearch) self.searchCursor = QLabel() self.searchHLayout.addWidget(self.searchCursor) vboxSearch.addLayout(self.searchHLayout) self.browseHLayout = QHBoxLayout() self.buttonLookUp = QPushButton('\u21e7') #Arrow up self.buttonLookUp.setFixedWidth(100) self.buttonLookUp.clicked.connect(self.moveToPrev) self.buttonLookDown = QPushButton('\u21e9') #Arrow down self.buttonLookDown.setFixedWidth(100) self.buttonLookDown.clicked.connect(self.moveToNext) self.browseHLayout.addWidget(self.buttonLookUp) self.browseHLayout.addWidget(self.buttonLookDown) vboxSearch.addLayout(self.browseHLayout) self.groupBoxSearch.setLayout(vboxSearch) self.addWidget(self.groupBoxSearch) self.groupBoxSearch.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)) self.buttonHiddenLifelines = QPushButton('Show hidden life-lines') self.buttonHiddenLifelines.setFixedWidth(200) self.buttonHiddenLifelines.clicked.connect(self.showHiddenLifelines) self.addWidget(self.buttonHiddenLifelines) self.buttonHiddenMessages = QPushButton('Show hidden Messages') self.buttonHiddenMessages.setFixedWidth(200) self.buttonHiddenMessages.clicked.connect(self.showHiddenMessages) self.addWidget(self.buttonHiddenMessages) if const.mode_interactive == mode: self.buttonCapture = QPushButton('Capture') self.buttonCapture.setFixedWidth(200) self.buttonCapture.clicked.connect(self.notifyCapture) self.addWidget(self.buttonCapture) self.msgRcv = [] self.msgInfo = QLabel("Message Info.") self.groupBoxMessageInfo = QGroupBox() self.groupBoxMessageInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") vbox = QVBoxLayout() vbox.addWidget(self.msgInfo) self.tableTime = QtGui.QTableWidget(3,2) self.tableTime.setHorizontalHeaderLabels(['-','time']) self.tableTime.setColumnWidth(0,80) self.tableTime.setColumnWidth(1,150) vwidth = self.tableTime.verticalHeader().length() hwidth = self.tableTime.horizontalHeader().height() fwidth = self.tableTime.frameWidth() * 2 self.tableTime.setFixedHeight(vwidth + hwidth + fwidth) self.tableTime.horizontalHeader().setStretchLastSection(True) self.tableTime.setItem(0,0,QTableWidgetItem('begin')) self.tableTime.setItem(0,1,QTableWidgetItem(' - ')) self.tableTime.setItem(1,0,QTableWidgetItem('end')) self.tableTime.setItem(1,1,QTableWidgetItem(' - ')) self.tableTime.setItem(2,0,QTableWidgetItem('duration')) self.tableTime.setItem(2,1,QTableWidgetItem(' - ')) vbox.addWidget(self.tableTime) self.titleArg = QLabel('Argument List') vbox.addWidget(self.titleArg) max_arg_num = 10 self.tableArgs = QtGui.QTableWidget(max_arg_num,2) self.tableArgs.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_arg_num): self.tableArgs.setItem(idx,0,QTableWidgetItem()) self.tableArgs.setItem(idx,1,QTableWidgetItem()) self.tableArgs.horizontalHeader().setStretchLastSection(True) vbox.addWidget(self.tableArgs) self.titleArg = QLabel('Return Value List') vbox.addWidget(self.titleArg) max_ret_num = 4 self.tableRet = QtGui.QTableWidget(max_ret_num,2) self.tableRet.setHorizontalHeaderLabels(['type','value']) for idx in range(0,max_ret_num): self.tableRet.setItem(idx,0,QTableWidgetItem()) self.tableRet.setItem(idx,1,QTableWidgetItem()) self.tableRet.horizontalHeader().setStretchLastSection(True) vwidth = self.tableRet.verticalHeader().length() hwidth = self.tableRet.horizontalHeader().height() fwidth = self.tableRet.frameWidth() * 2 self.tableRet.setFixedHeight(vwidth + hwidth + fwidth) vbox.addWidget(self.tableRet) self.buttonSrcView = QPushButton('view code') self.buttonSrcView.setFixedWidth(200) self.buttonSrcView.clicked.connect(self.openSourceViewer) self.buttonHide = QPushButton('Hide') self.buttonHide.setFixedWidth(200) self.buttonHide.clicked.connect(self.notifyHide) self.buttonHideAllMsg = QPushButton('Hide All') self.buttonHideAllMsg.setFixedWidth(200) self.buttonHideAllMsg.clicked.connect(self.hideAllMsgNamedAsSelected) self.groupBoxMessageInfo.setLayout(vbox) self.checkHideCircular = QCheckBox('Hide Circular Messages') self.checkHideCircular.setCheckState(QtCore.Qt.Unchecked) self.checkHideCircular.stateChanged.connect(self.changeHideCircularMessage) self.addWidget(self.checkHideCircular) self.addWidget(self.groupBoxMessageInfo) self.groupBoxMessageInfo.setSizePolicy(self.sizePolicy) def reset(self): for idx in reversed(range(0,self.listThread.count())): self.listThread.takeItem(idx) def setMsgInfoMessage(self,msg): self.strMessage = msg def changeHideCircularMessage(self,state): if state == QtCore.Qt.Unchecked: self.diagramView.hideCircularChanged(False) elif state == QtCore.Qt.Checked: self.diagramView.hideCircularChanged(True) def setMsgInfoModule(self,module): self.strModule = module def updateSearchStatus(self,curr,number): self.searchCursor.setText("%d/%d" % (curr,number)) def connectSourceViewer(self,viewer): self.srcViewer = viewer def openSourceViewer(self): self.srcViewer.openViewer(self.strModule,self.strMessage) def setMessageInfoTime(self,begin,end,duration): self.tableTime.item(0,1).setText(begin) self.tableTime.item(1,1).setText(end) self.tableTime.item(2,1).setText(duration + ' msec') def setMessageInfoArg(self,listParam,listArg): if listArg: for idx, text in enumerate(listArg): self.tableArgs.item(idx,1).setText(text) for idx, text in enumerate(listParam): self.tableArgs.item(idx,0).setText(text) else: for idx in range(0,self.tableArgs.rowCount()): self.tableArgs.item(idx,1).setText('') self.tableArgs.item(idx,0).setText('') def setMessageInfoRet(self,listRet): if listRet: for idx, text in enumerate(listRet): self.tableRet.item(idx,1).setText(text) else: for idx in range(0,self.tableRet.rowCount()): self.tableRet.item(idx,1).setText('') self.tableRet.item(idx,0).setText('') def notifyInteractiveStateChanged(self,state): if const.mode_interactive != self.mode: return if const.STATE_INTERACTIVE_CAPTURING == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Stop Capture') if const.STATE_INTERACTIVE_PROCESSING == state: self.buttonCapture.setEnabled(False) if const.STATE_INTERACTIVE_IDLE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') if const.STATE_INTERACTIVE_RESET == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') elif const.STATE_INTERACTIVE_ACTIVE == state: self.buttonCapture.setEnabled(True) self.buttonCapture.setText('Capture') def setMessageInfo(self,info): self.msgInfo.setText(info) def setAvailable(self,threads): self.sig.emit(threads) def toggleThreadDisplay(self,item): print(self.listThread.currentRow()) #if item.isSelected(): # print(item.text() + " is selected") #else: # print(item.text() + " is not selected") self.diagramView.showThread(self.listThread.currentRow(),item.isSelected()) def hideAllMsgNamedAsSelected(self): self.diagramView.hideAllMessageSelected() def addThreadList(self,threads): if not self.groupBoxThreadInfo: self.groupBoxThreadInfo = QGroupBox() self.threadInfo = QLabel("Thread Info.") self.groupBoxThreadInfo.setStyleSheet("QGroupBox {border: 1px solid gray; border-radius: 9px; margin-top: 0.5em} QGroupBox::title {subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;") if not self.threadvbox: self.threadvbox = QVBoxLayout() if not self.listThread: self.listThread = QListWidget() self.listThread.setFixedWidth(200) self.listThread.setSelectionMode(QtGui.QAbstractItemView.MultiSelection) QtCore.QObject.connect(self.listThread, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), self.toggleThreadDisplay) self.threadvbox.addWidget(self.threadInfo) self.threadvbox.addWidget(self.listThread) self.groupBoxThreadInfo.setLayout(self.threadvbox) self.addWidget(self.groupBoxThreadInfo) self.groupBoxThreadInfo.setSizePolicy(self.sizePolicy) for id in threads: item = QtGui.QListWidgetItem(id) self.listThread.addItem(item) def connectController(self,controller): self.controller = controller self.connect(controller,QtCore.SIGNAL('setAvailable()'),self.setAvailable) def connectDiagramView(self,view): self.diagramView = view def disconnectMsgRcv(self,receiver): print("Implement this method !!! disconnectMsgRcv") def connectMsgRcv(self,receiver): self.msgRcv.append(receiver) def notifyHide(self): for rcv in self.msgRcv: rcv.activateHide(True) def showHiddenLifelines(self): response, selected_items = HiddenDialog.HiddenDialog.getSelectedItems(self.diagramView.getHiddenLifeLines()) if response: self.diagramView.showLifelines(selected_items) def showHiddenMessages(self): response, selected_items = HiddenMessageDialog.HiddenMessageDialog.getSelectedItems(self.diagramView.getHiddenMessages(),self.diagramView.getHiddenLifeLines()) if response: if selected_items[3] in self.diagramView.getHiddenLifeLines(): confirmation = ShowLifeLineDialog.ShowLifeLineDialog.confirmToShowLifeLine(selected_items[3]) if confirmation: self.diagramView.showLifelines([selected_items[3]]) self.diagramView.showMessages(selected_items) else: self.diagramView.showMessages(selected_items) def notifyCapture(self): for rcv in self.msgRcv: rcv.activateCapture(True) def moveToPrev(self): for rcv in self.msgRcv: rcv.moveToPrev() def moveToNext(self): for rcv in self.msgRcv: rcv.moveToNext() def searchMsg(self): str = self.editTextSearch.toPlainText() for rcv in self.msgRcv: rcv.searchMessage(str)
class LoginView(View): """`View` derived class. Defines the log in widget""" login = Signal((str, str, str, bool,)) def __init__(self, parent=None): """ Init method. Initializes parent classes :param parent: Reference to a `QWidget` object to be used as parent """ super(LoginView, self).__init__(parent) self.createWidgets() self.createLayouts() self.setFixedSize(250, 340) def createLayouts(self): """Put widgets into layouts, thus creating the widget""" mainLayout = QHBoxLayout() fieldsLayout = QVBoxLayout() ftpInfoLayout = QHBoxLayout() buttonLayout = QHBoxLayout() mainLayout.addStretch(20) fieldsLayout.addStretch(80) fieldsLayout.addWidget(self.linkLabel) fieldsLayout.addWidget(self.line) fieldsLayout.addStretch(20) ftpInfoLayout.addWidget(self.hostLabel, 50, Qt.AlignLeft) ftpInfoLayout.addStretch(20) ftpInfoLayout.addWidget(self.sslLabel, 20, Qt.AlignRight) ftpInfoLayout.addWidget(self.sslCheck, 10, Qt.AlignRight) fieldsLayout.addLayout(ftpInfoLayout) fieldsLayout.addWidget(self.hostEdit) fieldsLayout.addWidget(self.usernameLabel) fieldsLayout.addWidget(self.usernameEdit) fieldsLayout.addWidget(self.passwdLabel) fieldsLayout.addWidget(self.passwdEdit) fieldsLayout.addStretch(30) buttonLayout.addStretch(50) buttonLayout.addWidget(self.loginButton, 50, Qt.AlignRight) fieldsLayout.addLayout(buttonLayout) fieldsLayout.addStretch(20) mainLayout.addLayout(fieldsLayout, 30) mainLayout.addStretch(20) self.setLayout(mainLayout) def createWidgets(self): """Create children widgets needed by this view""" fieldsWidth = 200 labelsFont = View.labelsFont() editsFont = View.editsFont() self.setLogo() self.hostLabel = QLabel(self) self.hostEdit = QLineEdit(self) self.sslLabel = QLabel(self) self.sslCheck = QCheckBox(self) self.hostLabel.setText('FTP Location') self.hostLabel.setFont(labelsFont) self.hostEdit.setFixedWidth(fieldsWidth) self.hostEdit.setFont(editsFont) self.sslLabel.setText('SSL') self.sslLabel.setFont(labelsFont) self.usernameLabel = QLabel(self) self.usernameEdit = QLineEdit(self) self.usernameLabel.setText('Username') self.usernameLabel.setFont(labelsFont) self.usernameEdit.setFixedWidth(fieldsWidth) self.usernameEdit.setFont(editsFont) self.passwdLabel = QLabel(self) self.passwdEdit = QLineEdit(self) self.passwdLabel.setText('Password') self.passwdLabel.setFont(labelsFont) self.passwdEdit.setFixedWidth(fieldsWidth) self.passwdEdit.setEchoMode(QLineEdit.Password) self.passwdEdit.setFont(editsFont) self.passwdEdit.returnPressed.connect(self.onLoginClicked) self.loginButton = QPushButton(self) self.loginButton.setText('Login') self.loginButton.setFont(labelsFont) self.loginButton.setFixedWidth(fieldsWidth / 2) self.loginButton.clicked.connect(self.onLoginClicked) # Sets previously stored values into the fields, if any settings = get_settings() self.hostEdit.setText(settings.value(SettingsKeys['host'], '')) self.usernameEdit.setText(settings.value(SettingsKeys['username'], '')) self.passwdEdit.setText(crypt.decrypt(settings.value(SettingsKeys['passwd'], ''))) # Unicode to boolean conversion ssl = settings.value(SettingsKeys['ssl'], u'true') ssl = True if ssl == u'true' else False self.sslCheck.setChecked(ssl) @Slot() def onLoginClicked(self): """ Slot. Called on the user clicks on the `loginButton` button """ # Takes out the user input from the fields host = self.hostEdit.text() username = self.usernameEdit.text() passwd = self.passwdEdit.text() ssl = self.sslCheck.isChecked() print 'Logging in: %s, %s, %s' % (host, username, '*' * len(passwd)) if len(host) > 0: # If the fields are valid, store them using a `QSettings` object # and triggers a log in request settings = get_settings() settings.setValue(SettingsKeys['host'], host) settings.setValue(SettingsKeys['username'], username) settings.setValue(SettingsKeys['passwd'], crypt.encrypt(passwd)) settings.setValue(SettingsKeys['ssl'], ssl) self.setEnabled(False) self.login.emit(host.strip(), username, passwd, ssl) @Slot() def onFailedLogIn(self): """ Slot. Called when the log in request fails """ # Enables the fields again for user input self.setEnabled(True)
class CompareSequences(QMainWindow): def __init__(self): super(CompareSequences, self).__init__() self.setWindowTitle('Compare Sequences') self.setFixedHeight(125) self._ledit1 = QLineEdit() self._btn_pick_1 = QPushButton('...') self._ledit2 = QLineEdit() self._btn_pick_2 = QPushButton('...') self._btn_compare = QPushButton('Compare') self._progress_bar = QProgressBar(self.statusBar()) self._setup_ui() self._set_connections() def _compare(self): self._toggle_ui() src_one = self._ledit1.text() src_two = self._ledit2.text() # TODO: put in some checks for valid sequences if not (src_one and src_two): msg = 'Please pick proper sequences.' print msg nuke.message(msg) else: read1 = nuke.createNode('Read', inpanel=False) read1.knob('file').fromUserText(src_one) read2 = nuke.createNode('Read', inpanel=False) read2.knob('file').fromUserText(src_two) read2.setXYpos(read1.xpos() + 100, read1.ypos()) if not (read1.width() == read2.width() and read1.height() == read2.height()): msg = 'Sequences are not the same resolution.' print msg nuke.message(msg) else: # TODO: check for same resolution m = nuke.createNode('Merge2', inpanel=False) m.knob('operation').setValue(6) m.setXYpos(read1.xpos(), read2.ypos() + 100) m.setInput(0, read1) m.setInput(1, read2) c = nuke.createNode('CurveTool', inpanel=False) c.knob('operation').setValue(3) c.knob('ROI').fromDict( { 'x': 0, 'y': 0, 'r': read1.width(), 't': read1.height() } ) v = nuke.createNode('Viewer') check = False frame = None first = read1.knob('first').value() last = read1.knob('last').value() self._progress_bar.setRange(first, last) for i in range(first, last + 1): self._progress_bar.setValue(i) nuke.execute(c, i, i) data = c.knob('maxlumapixvalue').animations() check = False for curve in data: if not curve.constant(): check = True frame = i break if check: break if not check: msg = 'There is no difference.' else: msg = 'There is a difference at frame %d.' % frame nuke.message(msg) self._progress_bar.reset() self._toggle_ui() def _pick_sequence(self): btn = self.sender() le_btn_pair = { self._btn_pick_1: self._ledit1, self._btn_pick_2: self._ledit2 } clip_path = nuke.getClipname('Pick Sequence to compare') le_btn_pair[btn].setText(clip_path) def _set_connections(self): self._btn_pick_1.released.connect(self._pick_sequence) self._btn_pick_2.released.connect(self._pick_sequence) self._btn_compare.released.connect(self._compare) def _setup_ui(self): self._btn_pick_1.setFixedWidth(25) self._btn_pick_1.setToolTip('Pick first sequence.') self._btn_pick_2.setFixedWidth(25) self._btn_pick_2.setToolTip('Pick second sequence.') self._btn_compare.setToolTip('Compare sequences.') self._progress_bar.setFixedHeight(10) lyt_seq1 = QHBoxLayout() lyt_seq1.addWidget(self._ledit1) lyt_seq1.addWidget(self._btn_pick_1) lyt_seq2 = QHBoxLayout() lyt_seq2.addWidget(self._ledit2) lyt_seq2.addWidget(self._btn_pick_2) lyt_main = QVBoxLayout() lyt_main.addLayout(lyt_seq1) lyt_main.addLayout(lyt_seq2) lyt_main.addWidget(self._btn_compare) main_widget = QWidget() main_widget.setLayout(lyt_main) self.setCentralWidget(main_widget) def _toggle_ui(self): self._ledit1.setEnabled(not self._ledit1.isEnabled()) self._ledit2.setEnabled(not self._ledit2.isEnabled()) self._btn_pick_1.setEnabled(not self._btn_pick_1.isEnabled()) self._btn_pick_2.setEnabled(not self._btn_pick_2.isEnabled()) self._btn_compare.setEnabled(not self._btn_compare.isEnabled())
def __init__(self, *args, **kwargs ): QWidget.__init__( self, *args ) self.infoPath = cmds.about(pd=True) + "/sg/fingerWeightCopy/Widget_SelectionGrow.txt" sgCmds.makeFile( self.infoPath ) validator = QIntValidator() layout = QHBoxLayout( self ); layout.setContentsMargins(0,0,0,0) groupBox = QGroupBox() layout.addWidget( groupBox ) hLayout = QHBoxLayout() labelTitle = QLabel( "Grow Selection : " ) buttonGrow = QPushButton( "Grow" ); buttonGrow.setFixedWidth( 50 ) buttonShrink = QPushButton( "Shrink" ); buttonShrink.setFixedWidth( 50 ) lineEdit = QLineEdit(); lineEdit.setValidator( validator );lineEdit.setText( '0' ) hLayout.addWidget( labelTitle ) hLayout.addWidget( buttonGrow ) hLayout.addWidget( buttonShrink ) hLayout.addWidget( lineEdit ) groupBox.setLayout( hLayout ) self.lineEdit = lineEdit QtCore.QObject.connect( buttonGrow, QtCore.SIGNAL("clicked()"), self.growNum ) QtCore.QObject.connect( buttonShrink, QtCore.SIGNAL("clicked()"), self.shrinkNum ) self.vtxLineEditList = [] self.loadInfo()