Python qt: widgets
Buttons
btn = QtGui.QPushButton('Button', self) ## (label, parent) btn.setToolTip( 'This is a <b>QPushButton</b> widget') ## Tooltip (Annotation) (Can use QSS!!!) btn.move(50, 50) ## (x,y) from top-left btn.clicked.connect( QtCore.QCoreApplication.instance().quit ) ## Quit Button Function
StatusBar
A helpline that can show messages. By default, it displays
statusTips
set on widgets when you mouse over themwidget.setStatusTip('help message')
on widgets. This is the equivalent to mel'shelpLine
.statusbar = QtWidgets.QStatusBar() mainwindow.setStatusBar(statusbar) statusbar.showMessage('Ready')
MenuBar
#### Gui Controllers ## Create Menubar, Menus menubar = self.menuBar() # Create Menubar fileMenu = menubar.addMenu('File') # create new dropdownMenu 'File' prefs = menubar.addMenu('Preferences') # create new dropdownMenu 'Preferences' styles = menubar.addMenu('Styles') ## Create QActions, (menuItems) quitMenu = QtGui.QAction( QtGui.QIcon('exit.png'), 'Exit', self) # New QAction (quit) (icon, label, parent) menuA = QtGui.QAction( 'menuA', self) # New QAction (menuA) (icon, label, parent) menuB = QtGui.QAction( 'menuB', self) # New QAction (menuB) (icon, label, parent) ## Assign Commands to Qactions quitMenu.setShortcut( 'Ctrl+Q') # assign Hotkey quitMenu.setStatusTip( 'Exit application') # Use Statustip quitMenu.triggered.connect( self.close ) # QAction's command ## Add Actions to MenuBar fileMenu.addAction( quitMenu ) fileMenu.addAction( menuA ) fileMenu.addAction( menuB ) ## StatusBar self.statusBar() # Any QActions with statusTips attached to them will display tips here self.show()
QMenu
Dropdown File Menus.
# QMenus in Qt4 do not support having tooltips. # (in Qt5 this can be enabled using QMenu.setToolTipsVisible(True) ) # you can hack them in however: class ToolTipQMenu( QtWidgets.QMenu ): def __init__(self,*args,**kwds): QtWidgets.QMenu.__init__(self) self.hovered.connect( self.handleMenuHovered ) def handleMenuHovered(self, action): action.parent().setToolTip( action.toolTip() ) # tooltips can be added to qmenu actions menu = ToolTipQMenu() action = menu.addAction('do something', do_something ) action.setToolTip('does something that takes a long time.') menu.addSeparator()NOTE:
You can colourize qmenu items by providing a QWidgetAction() class (which lets you set the rendered widget).
Toolbar
Toolbars store actions. Think of the tool-buttons on the left-hand side of Photoshop.
## Create Actions (for toolbar) quitAction = QtGui.QAction( 'Quit', self ) # Build QtActions (to add to toolbar) tbAction = QtGui.QAction( 'toolB', self ) tcAction = QtGui.QAction( 'toolC', self ) quitAction.triggered.connect( self.close ) ## Create Toolbar toolbar = QtGui.QToolBar( 'FileToolBar', self ) # Build Toolbar toolbar.setOrientation( QtCore.Qt.Vertical ) # Make Vertical toolbar.addAction( quitAction ) # Add QtActions to toolbar toolbar.addAction( tbAction ) toolbar.addAction( tcAction )
QLineEdit, QTextEdit (Editable Text)
## QLineEdit produces a textBox you can type into that is only a single line # mergeTitle = QtGui.QLineEdit() mergeTitle.setText('abc') mergeTitle.setReadOnly(1) mergeTitle.text() # QLineEdit's Contents ## QTextEdit produces a textbox that you can type in # textEdit = QtGui.QTextEdit() textEdit.setReadOnly(1) textEdit.setFont( QtGui.QFont('Lucida Console', 9) ) textEdit.setFocus() # Set Mouse Focus in a Particular Control textEdit.toPlainText() # textEdit's ContentsQTextEdit with modified line-height
#### this must be performed every time after text contents change textedit = QtWidgets.QTextEdit() # set 1.5x line height for readability cursor = textedit.textCursor() blockfmt = cursor.blockFormat() blockfmt.setLineHeight(25, QtGui.QTextBlockFormat.FixedHeight ) cursor.setBlockFormat(blockfmt)
QLabel (text, image, movie)
### Text msg = QtGui.QLabel( 'mylabel' ) msg.setText( '<font color="red">abcd</font> def' ) msg.setIndent(80) msg.setAlignment( QtCore.Qt.AlignCenter ) msg.setTextInteractionFlags( QtCore.Qt.TextSelectableByMouse )### Image img = QtGui.QLabel() pixmap = QtGui.QPixmap('myfile.png') pixmap = pixmap.scaled( 10, 10 ) ## resize image img.setPixmap( pixmap )### Image from WebServer def print_reply( label, reply ): pixmap = QtGui.QPixmap() pixmap.loadFromData( reply.readAll() ) label.setPixmap( pixmap ) label = QtWidgets.QLabel() label.show() manager = QtNetwork.QNetworkAccessManager() manager.finished.connect( functools.partial(print_reply, label) ) manager.get( QtNetwork.QNetworkRequest('http://mercuryfilmworks.com/wp-content/themes/mercuryfilmworks/library/images/headerlogo.png'))
WARNING:
Qt expects you to use a single QNetworkAccessManager for the entire application
WARNING:
The QNetworkAccessManager's NetworkReply object must be deleted by the user (using reply.deleteLater())
QComboBox (DropdownMenu)
drop = QtGui.QComboBox() itemList = ['A','B','C','D'] drop.addItems( itemList ) drop.setCurrentIndex( 2 ) drop.currentIndex() ## Get Current Index drop.currentText() ## Get Current Text allDropItems = [drop.itemText(i) for i in range(itemText.count())] drop.currentIndexChanged.connect( lambda: self.my_function( drop ) ) # connect function when index is changed (index is -1 if no items remain)
QFileDialog
dialog = QtWidgets.QFileDialog() dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) # QFileDialog.FileMode dialog.setViewMode(QtWidgets.QFileDialog.Detail) # QFileDialog.ViewMode dialog.setNameFilter('Images (*.png *.jpg *.bmp)') # only files with these extensions will be displayed dialog.setNameFilter('PNG File (*.png);;TGA File (*.tga *.targa);;JPEG File (*.jpg *.jpeg)') # multiple filters separated by ';;' dialog.setDirectory('/path/to/dir') if dialog.exec_(): filepaths = dialog.selectedFiles()
QListWidget, QListView
QListWidget
## This is the equivalent to a textScrollList in MEL. ## individual QListWidget entries cannot be colourized with html nodesList = QtGui.QListWidget() nodesList.addItems( ['a_node','b_node','c_node','d_node','e_node','f_node','g_node'] ) nodesList.clear() sel_items = nodesList.selectedItems() for item in sel_items: print item.text() sel_indexes = nodesList.selectedIndexes() nodesList.itemSelectionChanged.connect( self._handle_itemSelectionChanged ) nodesList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) ## multiselect
QListView#### QListView ##
QListWidget with Custom Delegateclass ListWidget_LineEdit_Delegate( QtWidgets.QStyledItemDelegate ): def createEditor(self, parent, option, index ): editor = QtWidgets.QLineEdit( parent=parent ) editor.setValidator( QtGui.QIntValidator ) editor.editingFinished.connect( self.commitAndCloseEditor ) return editor def updateEditorGeometry(self, editor, option, index ): editor.setGeometry( option.rect ) def commitAndCloseEditor(self): editor = self.sender() self.commitData.emit( editor ) listwid = QtWidgets.QListWidget() delegate = ListWidget_LineEdit_Delegate() listwid.setItemDelegate( delegate )
See Also:
http://falsinsoft.blogspot.ca/2013/11/qlistwidget-and-item-edit-event.html signals from QListWidget while being edited https://stackoverflow.com/questions/22049129/qt-signal-for-when-qlistwidget-row-is-edited signals from QListWidget while being edited
QTableWidget
table = QtGui.QTableWidget() table.setRowCount(0) ## row/column count MUST be done before adding widgets/items to the table table.setColumnCount(2) ## Widget Attributes columns = OrderedDict([ ## name ## ## width ## ('Names' , 100 ), ('Numbers', 80 ), ('Other' , 80 ), ('Notes' , 500 ), ]) table.setHorizontalHeaderLabels([column for column in columns]) # column titles column_index = 0 for width in columns.values(): table.setColumnWidth( column_index, width ) column_index +=1 table.setAlternatingRowColors(1) table.setSortingEnabled(1) table.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows ) ## select entire rows instead of single cell table.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection ) ## maya-style multi-selection ## Contents row_num = 0 for item in ('A','B','C','D'): widgetA = QtGui.QTableWidgetItem( item ) table.setItem( row_num, 0, widgetA) ## adds QTableWidgetItem table.setCellWidget( row_num, 1, QtGui.QLabel('blah') ) ## adds any Widgetcustom sorting
class CustomSortTableItem( QTableWidgetItem ): class __lt__(self, other): """ returns True if item is less than other """ return self.sort_key < other.sort_keysorting QWidgets
## you can sort QWidgets by setting both QTableWidgetItem __AND___ a widget ## to a particular cell. The QTableWidgetItem will be used for sorting. table = QTableWidget() table.setSortingEnabled(1) for item in ('apple','orange','banana','pear'): table.setItem( 0,0, QTableWidgetItem( item ) ) table.setCellWidget( 0,0, QPushButton( item ) )column stretch/resize
# qt5 table.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.ResizeToContents ) # qt4 table = QTableWidget() table.setHorizontalHeaderLabels( ['long','short'] ) table.horizontalHeader().setResizeMode( 0, QtWidgets.QHeaderView.Stretch )table.itemSelectionChagned.connect( self._handle_selection ) ## handle selection
QTreeWidget
Working with a QTreeWidget is very similiar to working with a QTableWidget.
#!/usr/bin/env python from PySide import QtGui, QtCore from libpyside import newUI from collections import OrderedDict # the majority of the manipulation of the QTreeWidget # is done on the QTreeWidgetItems (rather than the tree itself) # # QTreeWidgetItem.addChild # QTreeWidgetItem( parent, [colA,colB,...] ) # ... class TestTreeWidget( newUI.NewWindow ): def __init__(self): newUI.NewWindow.__init__(self) def main(self): layout = QtGui.QVBoxLayout() tree = QtGui.QTreeWidget() ## setup columns columns = OrderedDict([ ('A', 200), ('B', 120), ('C', 120), ('D', 120), ]) tree.setColumnCount(len(columns)) tree.setHeaderLabels([ column for column in columns ]) ## add `root` items (each item in list is a new column) root1 = QtGui.QTreeWidgetItem( tree, ['root1','a']) root2 = QtGui.QTreeWidgetItem( tree, ['root2','a']) root1.setText(2,'b') root1.setText(3,'c') ## add `child` items to those root items r1_sub1 = QtGui.QTreeWidgetItem( root1, ['r1_sub1','a','b']) r1_sub2 = QtGui.QTreeWidgetItem( root1, ['r1_sub2','a','b']) r1_sub3 = QtGui.QTreeWidgetItem( ['r1_sub3','a','b'] ) root1.addChild( r1_sub3 ) root3 = TestCustomTreeWidgetItem( tree ) layout.addWidget( tree ) self.setLayout(layout) ## in order to add custom widgets to a QTreeWidgetItem, ## the widget *MUST* either be an attribute, or have some ## other measure taken so that it is not garbage-collected. ## (otherwise you will get a segmentation fault) ## ## This makes it cleanest to create your own TreeWidgetItem ## subclasses. class TestCustomTreeWidgetItem( QtGui.QTreeWidgetItem ): def __init__(self, treewidget ): QtGui.QTreeWidgetItem.__init__(self, treewidget ) ## column 1 self.setText( 0, 'column1' ) self.setText( 1, 'column2' ) self.button = QtGui.QPushButton("Press Me") self.treeWidget().setItemWidget( self, 2, self.button ) with newUI.WithQApp(): win = TestTreeWidget() win.display()
QTreeWidgets can also be nested meaning subtrees can have variable columns.from mfw2.lib.qt import QApplication, QBaseWindow, QBaseObject from qtpy import QtWidgets class Tree( QtWidgets.QTreeWidget ): """ single column TreeWidget """ def __init__(self): super( Tree, self ).__init__() self.setColumnCount(1) self.setHeaderLabels(['A']) class TreeNested_TreeWidgetItem( QtWidgets.QTreeWidget ): """ 5x column TreeWidget (will be nested into `Tree`) """ def __init__(self): super( TreeNested_TreeWidgetItem, self ).__init__() self.setColumnCount(5) self.setHeaderLabels(['1','2','3','4','5']) class TreeNested_TreeWidgetItem( QtWidgets.QTreeWidgetItem ): """ Widget containing `TreeNested_TreeWidgetItem`, and gets added to `Tree` """ def __init__(self, treewidget ): super( TreeNested_TreeWidgetItem, self ).__init__(treewidget) self.subtree = TreeNested_TreeWidgetItem() QtWidgets.QTreeWidgetItem( self.subtree, ['a','b','c','d','e'] ) self.treeWidget().setItemWidget( self, 0, self.subtree ) with QApplication(): win = QBaseWindow() tree_A = Tree() win.layout().addWidget( tree_A ) root1 = QtWidgets.QTreeWidgetItem( tree_A, ['test_1'] ) root2 = QtWidgets.QTreeWidgetItem( tree_A, ['test_2'] ) root3 = QtWidgets.QTreeWidgetItem( tree_A, ['expand me'] ) root4 = QtWidgets.QTreeWidgetItem( tree_A, ['expand me'] ) sub1 = TreeNested_TreeWidgetItem(root3) sub2 = TreeNested_TreeWidgetItem(root4) win.show()
QTreeWidgetItem
QTreeWidgetItems are special, and they do not inherit from QWidgets. This unfortunately makes them a gigantic pain in the ass to work with if you are trying to do anything outside of their vanilla implementation. Here are some of the strategies I have figured out for dealing with their abysmal retardation.
resizing
# set a predetermined size on your treeWidget's stylesheet # so that you can predictably handle the sizes of your dynamically # sized TreeWidgetItems _TREEWIDGETITEM_HEIGHT = 23 class MyTree( QtWidgets.QTreeWidget ): def __init__(self): QtWidgets.QTreeWidget.__init__(self) self.setStyleSheet('QTreeView::item { height:%s; }' % _TREEWIDGETITEM_HEIGHT ) # use a custom class for sub-treewidgets, or their items. # instead of using setSizeHint() directly, create your # own custom setFixedHeight(), and in addition to resizing # the widget, # # ALSO RESIZE THE WINDOW TO FORCE IT TO RECALCULATE ALL WIDGET SIZES # class SubTreeWidgetItem( QtWidgets.QTreeWidgetItem ): def __init__(self, subtree_class, parent, parent_index=0, *args, **kwds ): QtWidgets.QTreeWidgetItem.__init__(self, parent) self.subtree = subtree_class( *args, **kwds ) self.treeWidget().setItemWidget( self, parent_index, self.subtree ) # disable selection of this widget... seems impractical self.setFlags( QtCore.Qt.ItemIsEnabled ) def setFixedHeight(self, height): self.setSizeHint( 0, QtCore.QSize(-1, height) ) if self.treeWidget().window(): size = self.treeWidget().window().size() height = size.height() width = size.width() self.treeWidget().window().resize( width, height-1 ) self.treeWidget().window().resize( width, height )
Tips/Tricks
Custom Popup Widget
See https://forum.qt.io/topic/85056/custom-popup-widget/6
Position of QCalendarWidget will be determined by parent within
popup
method.class _PopupCalendarWidget(QtWidgets.QCalendarWidget): date_set = QtCore.Signal(QtCore.QDate) def __init__(self, parent): super(_PopupCalendarWidget, self).__init__(parent) # Widget Attrs self.setWindowFlags(QtCore.Qt.Popup) # Connections self.activated.connect(self._handle_activated) def popup(self): parent = self.parent() parent_global_pos = parent.mapToGlobal(parent.rect().topLeft()) global_pos = QtCore.QPoint( parent_global_pos.x(), parent_global_pos.y() + parent.rect().height() ) local_pos = self.mapFromGlobal(global_pos) size = QtCore.QSize(350, 200) rect = QtCore.QRect(local_pos, size) self.setGeometry(rect) self.show() def _handle_activated(self, qdate): self.date_set.emit(qdate) self.deleteLater()