The clip below shows dragging QDockWidgets between docking areas by dragging the tabs (not the title bar) - but when I try this with PyQt 5.15.0 it doesn't work, the tabs won't detach. How can I enable this behavior?
What I want:
https://www.screencast.com/t/lv83SoyVUhbd (from https://woboq.com/blog/qdockwidget-changes-in-56.html)
What I get:
https://www.screencast.com/t/bIUj4vLNTF
My code:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
if __name__ == "__main__":
app = QtWidgets.QApplication([])
main = QtWidgets.QMainWindow()
dock1 = QtWidgets.QDockWidget("Blue")
dock2 = QtWidgets.QDockWidget("Green")
dock3 = QtWidgets.QDockWidget("Red")
content1 = QtWidgets.QWidget()
content1.setStyleSheet("background-color:blue;")
content2 = QtWidgets.QWidget()
content2.setStyleSheet("background-color:green;")
content3 = QtWidgets.QWidget()
content3.setStyleSheet("background-color:red;")
dock1.setWidget(content1)
dock2.setWidget(content2)
dock3.setWidget(content3)
dock1.setAllowedAreas(Qt.AllDockWidgetAreas)
dock2.setAllowedAreas(Qt.AllDockWidgetAreas)
dock3.setAllowedAreas(Qt.AllDockWidgetAreas)
main.addDockWidget(Qt.LeftDockWidgetArea, dock1)
main.tabifyDockWidget(dock1, dock2)
main.addDockWidget(Qt.RightDockWidgetArea, dock3)
main.resize(400, 200)
main.show()
app.exec_()
The solution to my question was enabling the GroupedDragging with setDockOptions on the QMainWindow. I managed to get a really nice appearance & behavior exactly like I wanted with the code below.
Demo: https://www.screencast.com/t/GU5Z2ysT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
class DockWidget(QtWidgets.QDockWidget):
def __init__(self, title: str):
super().__init__(title)
self.setTitleBarWidget(QtWidgets.QWidget())
self.dockLocationChanged.connect(self.on_dockLocationChanged)
def on_dockLocationChanged(self):
main: QtWidgets.QMainWindow = self.parent()
all_dock_widgets = main.findChildren(QtWidgets.QDockWidget)
for dock_widget in all_dock_widgets:
sibling_tabs = main.tabifiedDockWidgets(dock_widget)
# If you pull a tab out of a group the other tabs still see it as a sibling while dragging...
sibling_tabs = [s for s in sibling_tabs if not s.isFloating()]
if len(sibling_tabs) != 0:
# Hide title bar
dock_widget.setTitleBarWidget(QtWidgets.QWidget())
else:
# Re-enable title bar
dock_widget.setTitleBarWidget(None)
def minimumSizeHint(self) -> QtCore.QSize:
return QtCore.QSize(100, 100)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
main = QtWidgets.QMainWindow()
dock1 = DockWidget("Blue")
dock2 = DockWidget("Green")
dock3 = DockWidget("Red")
content1 = QtWidgets.QWidget()
content1.setStyleSheet("background-color:blue;")
content1.setMinimumSize(QtCore.QSize(50, 50))
content2 = QtWidgets.QWidget()
content2.setStyleSheet("background-color:green;")
content2.setMinimumSize(QtCore.QSize(50, 50))
content3 = QtWidgets.QWidget()
content3.setStyleSheet("background-color:red;")
content3.setMinimumSize(QtCore.QSize(50, 50))
dock1.setWidget(content1)
dock2.setWidget(content2)
dock3.setWidget(content3)
dock1.setAllowedAreas(Qt.AllDockWidgetAreas)
dock2.setAllowedAreas(Qt.AllDockWidgetAreas)
dock3.setAllowedAreas(Qt.AllDockWidgetAreas)
main.addDockWidget(Qt.LeftDockWidgetArea, dock1)
main.tabifyDockWidget(dock1, dock2)
main.addDockWidget(Qt.RightDockWidgetArea, dock3)
main.setDockOptions(main.GroupedDragging | main.AllowTabbedDocks | main.AllowNestedDocks)
main.setTabPosition(Qt.AllDockWidgetAreas, QtWidgets.QTabWidget.North)
main.resize(400, 200)
main.show()
app.exec_()
Posting this as a little extra to the accepted answer.
This version works with Qt5 and Qt6 and is able to detect corner cases like pulling out a tab and dropping it in the same group or merging a floating tab group with a docked window:
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Qt
from typing import TypeVar, List, Optional
TDockWidget = TypeVar('TDockWidget', bound='DockWidget')
class DockWidget(QtWidgets.QDockWidget):
def __init__(self: TDockWidget, title: str, parent: Optional[QtWidgets.QWidget] = None) -> None:
super(_DockWidget, self).__init__(title, parent)
self.setTitleBarWidget(QtWidgets.QWidget())
self.visibilityChanged.connect(self.on_visibility_changed)
self.dockLocationChanged.connect(self.on_dock_location_changed)
#QtCore.Slot(bool)
def on_visibility_changed(self: TDockWidget, is_visible: bool) -> None:
# this visibility monitor is really only needed to detect merges of
# tabbed, floating windows with existing docked windows
if not is_visible and isinstance(self.parent(), QtWidgets.QMainWindow):
main_window: QtWidgets.QMainWindow = self.parent()
all_dockwidgets: List[QtWidgets.QDockWidget] = main_window.findChildren(QtWidgets.QDockWidget)
for dockwidget in all_dockwidgets:
if hasattr(dockwidget, 'on_dock_location_changed'):
dockwidget.on_dock_location_changed(main_window.dockWidgetArea(dockwidget), False)
#QtCore.Slot(Qt.DockWidgetArea)
def on_dock_location_changed(self: TDockWidget, area: Qt.DockWidgetArea, update_others: bool = True) -> None:
if not isinstance(self.parent(), QtWidgets.QMainWindow):
# mysterious parents call for a title
self.setTitleBarWidget(None)
return
main_window: QtWidgets.QMainWindow = self.parent()
if not main_window.tabifiedDockWidgets(self):
# if there's no siblings we ain't a tab!
self.setTitleBarWidget(None)
if not update_others:
# prevent infinite recursion
return
# force an update to all other docks that may now no longer be tabs
all_dockwidgets: List[QtWidgets.QDockWidget] = main_window.findChildren(QtWidgets.QDockWidget)
for dockwidget in all_dockwidgets:
if dockwidget != self and hasattr(dockwidget, 'on_dock_location_changed'):
dockwidget.on_dock_location_changed(main_window.dockWidgetArea(dockwidget), False)
return
# at this point the dockwidget is either a resting tab or a tab
# that is being dragged and hasn't been dropped yet (siblings are updated post-drop)
# collect all siblings of this dockwidget...
tab_siblings: List[QtWidgets.QDockWidget] = main_window.tabifiedDockWidgets(self)
# and filter for non-floating siblings in the same area
tab_siblings = [x for x in tab_siblings if main_window.dockWidgetArea(x) == area and not x.isFloating()]
if tab_siblings:
if self.titleBarWidget() is not None:
# no changes needed, prevent infinite recursion
return
# show a title if we're not floating (this tab is settled),
# hide it otherwise (this tab just became floating but wasn't dropped)
self.setTitleBarWidget(QtWidgets.QWidget() if not self.isFloating() else None)
# in this case it's also a good idea to tell to reconsider their situation
# since Qt won't notify them separately
for sibling in tab_siblings:
if hasattr(sibling, 'on_dock_location_changed'):
sibling.on_dock_location_changed(main_window.dockWidgetArea(sibling), True)
else:
self.setTitleBarWidget(None)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
main = QtWidgets.QMainWindow()
dock1 = DockWidget("Blue")
dock2 = DockWidget("Green")
dock3 = DockWidget("Red")
content1 = QtWidgets.QWidget()
content1.setStyleSheet("background-color:blue;")
content1.setMinimumSize(QtCore.QSize(50, 50))
content2 = QtWidgets.QWidget()
content2.setStyleSheet("background-color:green;")
content2.setMinimumSize(QtCore.QSize(50, 50))
content3 = QtWidgets.QWidget()
content3.setStyleSheet("background-color:red;")
content3.setMinimumSize(QtCore.QSize(50, 50))
dock1.setWidget(content1)
dock2.setWidget(content2)
dock3.setWidget(content3)
dock1.setAllowedAreas(Qt.AllDockWidgetAreas)
dock2.setAllowedAreas(Qt.AllDockWidgetAreas)
dock3.setAllowedAreas(Qt.AllDockWidgetAreas)
main.addDockWidget(Qt.LeftDockWidgetArea, dock1)
main.tabifyDockWidget(dock1, dock2)
main.addDockWidget(Qt.RightDockWidgetArea, dock3)
main.setDockOptions(main.GroupedDragging | main.AllowTabbedDocks | main.AllowNestedDocks)
main.setTabPosition(Qt.AllDockWidgetAreas, QtWidgets.QTabWidget.North)
main.resize(400, 200)
main.show()
app.exec_()
Related
In my QMainWindow i have a button which opens a new QDialog in the bottom right monitorcorner with a successmessage when i click it.
Now, if i move the QMainWindow to another monitor (i have 3 monitor) and click the button the successmessage popup appears in the monitor where the QMainWindow was opened. What i want is that the popup message appears in the monitor where my QMainWindow actually is. So if i move the QMainWindow to Monitor 1 and click the button, the successpopup should opens in monitor 1. If the QMainWindow is in monitor 2, the successpopup should open in monitor 2 an same for monitor 3.
with
screenNumber = QDesktopWidget().screenNumber(self)
i can read the screennumber where the mainwindow is. and this works fine. Evertime i click the button i read out the screennumber. But i don't found a way, to set the screennumber to my notification.
Any ideas?
Edit:
maybe it helps if i show my notify class
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication, QDesktopWidget
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
## Some helping stuff
############################################################
parent_sSize = QDesktopWidget().screenGeometry(parent)
parent_screenNumber = QDesktopWidget().screenNumber(parent)
sSize = QDesktopWidget().screenGeometry()
screenNumber = QDesktopWidget().screenNumber()
print("notification ScreenNumber = " + str(screenNumber))
print(sSize.width())
print(sSize.height())
print("Parents ScreenNumber = " + str(parent_screenNumber))
print(parent_sSize.width())
print(parent_sSize.height())
self.Note_Exit.clicked.connect(self.close)
## ScreenSize from parent
############################################################
self.hidedPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()+200,
self.frameGeometry().width(),
self.frameGeometry().height())
self.showPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()-50,
self.frameGeometry().width(),
self.frameGeometry().height())
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.setGeometry(self.hidedPos)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(self.showPos)
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(self.hidedPos)
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())
You cannot use the size of the widget during its construction, as at that moment it has a default size (640x480 for top level widgets, 100x30 for widgets created with a parent, including dialogs): the only reliable option is to use the sizeHint() or ensure that the layout has been properly activated with adjustSize().
Then, you don't need the screen to get the target position, as the parent geometry will suffice, but you do need it for the start position, otherwise the dialog will "pop up" at an arbitrary point below the window. Note that QDesktopWidget is considered obsolete, and you should use QScreen instead.
Finally, since you might want to reuse the notification, the start position should be set when the popup is actually being shown, not before. The same goes for the position when hiding (in case the notification could be moved).
class Notify(QDialog, Ui_Notification):
def __init__(self, parent):
super().__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.showAnim = QPropertyAnimation(self, b'geometry')
self.showAnim.setDuration(700)
self.showAnim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.hideAnim = QPropertyAnimation(self, b'geometry')
self.hideAnim.setDuration(700)
self.hideAnim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.hideTimer = QTimer(self, singleShot=True)
self.hideTimer.setInterval(4000)
self.hideTimer.timeout.connect(self.hideNote)
self.showAnim.finished.connect(self.hideTimer.start)
self.hideAnim.finished.connect(self.close)
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.adjustSize() # important!
endRect = self.rect()
center = self.parent().geometry().center()
endRect.moveCenter(center)
screen = QApplication.screenAt(center)
startRect = QRect(endRect)
startRect.moveTop(screen.geometry().bottom())
self.setGeometry(startRect)
self.showAnim.setStartValue(startRect)
self.showAnim.setEndValue(endRect)
self.showAnim.start()
self.show()
def hideNote(self):
rect = self.geometry()
self.hideAnim.setStartValue(rect)
screen = QApplication.screenAt(rect.center())
rect.moveTop(screen.geometry().bottom())
self.hideAnim.setEndValue(rect)
self.hideAnim.start()
if __name__ == "__main__":
notes = QApplication(sys.argv)
notes.setStyle('fusion')
w = QMainWindow()
b = QPushButton('click!')
w.setCentralWidget(b)
w.show()
notify = Notify(w)
b.clicked.connect(lambda: notify.setNote())
sys.exit(notes.exec())
Be aware that if you're going to create the popup every time, you should also set the WA_DeleteOnClose attribute in order to destroy it when closed, otherwise it will be kept in memory.
Note: QTimer.singleShot() is a static function, creating an instance of QTimer to use it is pointless, as that instance won't be used and a new QTimer would be created anyway.
Thank you.
In the meantime i found a solution which works for me.
Look a little dirty but works.
could you tell me, whats the difference between my code an yours?
Why should i use your code, except the fact that you are a better programmer ;-)
I set the WA_DeleteOnClose Attribute.
Good to know that. Thanks
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
clock = "clock"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.setWindowModality(QtCore.Qt.NonModal)
self.Note_Exit.clicked.connect(self.close)
self.parent_h = self.parent().geometry().height()
self.parent_w = self.parent().geometry().width()
self.parent_x = self.parent().geometry().x()
self.parent_y = self.parent().geometry().y()
self.dialog_w = self.width()
self.dialog_h = self.height()
self.setGeometry(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h)
## ScreenSize from parent
############################################################
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h-20, self.dialog_w, self.dialog_h))
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h))
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())
I am implement my project using pyqt5. Currently, I have a window including many widget. Now, I want to remove some widgets. The window looks like:
Now, I want to remove the 'name1' widget including the QLabel and QPushButton.
However, after removing all 'name1' widgets, the 'name2' widgets including QLabel and QPushButton can not self-adapte with the window, like:
All my code is:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subText = QLabel(name)
subText.setObjectName(name)
subBtn = QPushButton(name)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
def remove(self, checked=False):
name = self.__removeText.text()
while True:
child = self.__ROIsLayout.takeAt(0)
if child == None:
break
while True:
subChild = child.takeAt(0)
if subChild == None:
break
obName = subChild.widget().objectName()
if name == obName:
widget = subChild.widget()
widget.setParent(None)
child.removeWidget(widget)
self.__ROIsLayout.removeWidget(widget)
del widget
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
update:
Actually, the issue may be the takeAt. The following code is workable:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subLayout.setObjectName(name)
subText = QLabel(name, parent=self)
subText.setObjectName(name)
subBtn = QPushButton(name, parent=self)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
print(name, subLayout, subText, subBtn)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
self.record = [subLayout, subText, subBtn]
def remove(self, checked=False):
layout = self.record[0]
txt = self.record[1]
btn = self.record[2]
layout.removeWidget(txt)
txt.setParent(None)
txt.deleteLater()
layout.removeWidget(btn)
btn.setParent(None)
btn.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
But, I have printed the QLabel/QPushButton in the self.record, and I find it is the same with that from child.takeAt(0).widget().
The main issue in your code is that you're constantly using takeAt(). The result is that all items in the __ROIsLayout layout will be removed from it (but not deleted), which, in your case, are the sub layouts. This is clearly not a good approach: only the widgets with the corresponding object name will be actually deleted, while the others will still be "owned" by their previous parent, will still be visible at their previous position and their geometries won't be updated since they're not managed by the layout anymore.
There are multiple solutions to your question, all depending on your needs.
If you need to remove rows from a layout, I'd consider setting the object name on the layout instead, and look for it using self.findChild().
Also consider that, while Qt allows setting the same object name for more than one object, that's not suggested.
Finally, while using del is normally enough, it's usually better to call deleteLater() for all Qt objects, which ensures that Qt correctly removes all objects (and related parentship/connections).
Another possibility, for this specific case, is to use a QFormLayout.
I have a usb barcode scanner which I am connecting to my computer. Everytime it scans a barcode, it types the data into the computer like a keyboard. My goal was to have the data be typed into PyQT5 Table widget.
I have created the table below and I simply scan the items into it. The problem is that when I scan an item, it edits the first cell, but the cursor does not move automatically to the next row so I can scan a new item into the table. I have to click on the second cell and then scan the item. Then click on the third cell and scan the item and so on.
I was wondering how I can automate it so that after an item is scanned into the first cell, it automatically moves to the next cell and waits for input from the scanner?
import sys
from PyQt5.QtWidgets import *
#Main Window
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'Specimen Dashboard'
self.setWindowTitle(self.title)
self.tableWidget = QTableWidget()
self.createTable()
self.tableWidget.itemChanged.connect(self.go_to_next_row)
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
self.show()
def go_to_next_row(self):
#Not working
#Trying to see if I can automatically move to next cell, but editing it
self.tableWidget.setItem(1,0, QTableWidgetItem("Name"))
#Create table
def createTable(self):
self.tableWidget.setRowCount(4)
self.tableWidget.setColumnCount(2)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
By default the scanners send an endline("\n") that is translated a Return or Enter key and this by default closes the editor, in this case that event must be intercepted, move the cursor and open the editor:
import sys
from PyQt5 import QtCore, QtWidgets
class TableWidget(QtWidgets.QTableWidget):
def keyPressEvent(self, event):
if (
event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)
and self.state() == QtWidgets.QAbstractItemView.EditingState
):
index = self.moveCursor(
QtWidgets.QAbstractItemView.MoveNext, QtCore.Qt.NoModifier
)
self.selectionModel().setCurrentIndex(
index, QtCore.QItemSelectionModel.ClearAndSelect
)
self.edit(index)
else:
super().keyPressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.tableWidget = TableWidget(4, 2)
self.setCentralWidget(self.tableWidget)
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
You can subclass the table and overwrite closeEditor(): the hint argument tells the view what should happen when the editor has been closed; by default, when pressing Enter the current cell data is submitted, but you can override this behavior like this:
from PyQt5 import QtGui, QtWidgets
class Table(QtWidgets.QTableView):
# leave to False for the default behavior (the next cell is the one at the
# right of the current, or the first of the next row; when set to True it
# will always go to the next row, while keeping the same column
useNextRow = False
def closeEditor(self, editor, hint):
if hint == QtWidgets.QAbstractItemDelegate.SubmitModelCache:
if self.useNextRow:
super().closeEditor(editor, hint)
current = self.currentIndex()
newIndex = current.sibling(current.row() + 1, current.column())
if newIndex.isValid():
self.setCurrentIndex(newIndex)
self.edit(newIndex)
return
else:
hint = QtWidgets.QAbstractItemDelegate.EditNextItem
super().closeEditor(editor, hint)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = Table()
test.show()
model = QtGui.QStandardItemModel(10, 5)
test.setModel(model)
sys.exit(app.exec_())
How can I make it so the icon of a QListView's item resizes when i change the slider? It appears to resize the item but not it's icon.
I tried calling both of these, neither of them worked. I would ideally like to not call setGridSize since that causes the widget to ignore the setSpacing(5) which i intend on using.
self.uiListView.setGridSize(iconSize)
self.uiListView.setIconSize(iconSize)
import os, sys, re
from Qt import QtWidgets, QtGui, QtCore
from . import StyleUtils
values = ['MomBod','Colonel','Tater','Tot','Ginger','Donut','Sport','LaLa','Itchy','Bruiser','Cotton','Cumulus','Toodles','Salt','Ghoulie','Cat','Dirty','Harry','Buckeye','Flyby','Swiss','Miss','Buddy','Pecan','Sunny','Jet','Thor','Gingersnap','Cuddle','Pig','Turkey','Foxy','Mini','Me','Dolly','Stud','Music','Man','Barbie','Munchkin','Bubba','Hammer','Twizzler','Bebe']
class ViewerWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.resize(500,500)
self.uiIconSize = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.uiIconSize.setRange(32,256)
self.uiIconSize.setValue(128)
self.uiIconSize.setMinimumWidth(100)
self.viewerModel = QtGui.QStandardItemModel()
self.viewerProxyModel = QtCore.QSortFilterProxyModel()
self.viewerProxyModel.setSourceModel(self.viewerModel)
self.uiListView = QtWidgets.QListView()
self.uiListView.setSpacing(5)
self.uiListView.setMovement(QtWidgets.QListView.Static)
self.uiListView.setViewMode(QtWidgets.QListView.IconMode)
self.uiListView.setLayoutMode(QtWidgets.QListView.Batched)
self.uiListView.setBatchSize(100)
self.uiListView.setFlow(QtWidgets.QListView.LeftToRight)
self.uiListView.setWrapping(True)
self.uiListView.setResizeMode(QtWidgets.QListView.Adjust)
self.uiListView.setDragEnabled(False)
self.uiListView.setUniformItemSizes(True)
self.uiListView.setIconSize(self.getIconSize())
self.uiListView.setSelectionMode(QtWidgets.QListView.ExtendedSelection)
self.uiListView.setModel(self.viewerProxyModel)
# main layout
self.layout = QtWidgets.QVBoxLayout()
self.layout.setContentsMargins(0,0,0,0)
self.layout.setSpacing(0)
self.layout.addWidget(self.uiListView)
self.layout.addWidget(self.uiIconSize)
self.setLayout(self.layout)
# Signals
self.uiIconSize.valueChanged.connect(self.setItemSize)
# Init
self.populateModel()
def getIconSize(self):
return QtCore.QSize(self.uiIconSize.value(), self.uiIconSize.value())
def setItemSize(self):
iconSize = self.getIconSize()
# self.uiListView.setGridSize(iconSize)
self.uiListView.setIconSize(iconSize)
def populateModel(self):
model = self.viewerModel
model.clear()
icon = QtGui.QIcon('C:/Users/jmartini/Desktop/image.png')
for x in values:
newItem = QtGui.QStandardItem(x)
newItem.setData(icon, role=QtCore.Qt.DecorationRole)
model.appendRow(newItem)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = ViewerWidget()
ex.show()
sys.exit(app.exec_())
I discovered this is a result of my icon's native size being 64x64. Qt will not resize the icon to be larger than it's original size. It will only resize the icon to be of a smaller size.
self.uiListView.setIconSize(QtCore.QSize(64,64))
I use PyQt4 for designing GUI.
I want to know is there a way to sort menu items from right to left instead left to right for languages such as Arabic or Persian?
You have to use the setLayoutDirection() method of QApplicacion to indicate the Qt.RightToLeft address as shown below:
class menudemo(QMainWindow):
def __init__(self, parent = None):
super(menudemo, self).__init__(parent)
layout = QHBoxLayout()
bar = self.menuBar()
file = bar.addMenu("ملف")
file.addAction("الجديد")
save = QAction("حفظ",self)
file.addAction(save)
edit = file.addMenu("تصحيح")
edit.addAction("نسخ")
edit.addAction("معجون")
quit = QAction("استقال",self)
file.addAction(quit)
file.triggered[QAction].connect(self.processtrigger)
self.setLayout(layout)
self.setWindowTitle("RTL")
def processtrigger(self,q):
print(q.text()+" is triggered")
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setLayoutDirection(Qt.RightToLeft)
ex = menudemo()
ex.show()
sys.exit(app.exec_())
Screenshot: