PyQt5 context menu for QTableWidget column head - python

is there a way to get a context menu on a tables column head.
Find nothing about that in PyQt5's tuts.
the table's context menu is simple but the column heads don't affect.
# dlg is a QDialog object
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.setContextMenuPolicy( Qt.CustomContextMenu )

You have to use the QHeaderView of the QTableWidget:
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
for w in (self.tbl.horizontalHeader(), self.tbl.verticalHeader(), self.tbl):
w.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
w.customContextMenuRequested.connect(self.on_customContextMenuRequested)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, pos):
widget = self.sender()
if isinstance(widget, QtWidgets.QAbstractItemView):
widget = widget.viewport()
menu = QtWidgets.QMenu()
menu.addAction("Foo Action")
menu.exec_(widget.mapToGlobal(pos))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
Update:
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
self.tbl.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.customContextMenuRequested.connect(self.on_customContextMenuRequested_tw)
self.tbl.verticalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.verticalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_vh)
self.tbl.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_hh)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_tw(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action TW")
menu.exec_(self.tbl.viewport().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_vh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action VH")
menu.exec_(self.tbl.verticalHeader().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_hh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action HH")
menu.exec_(self.tbl.horizontalHeader().mapToGlobal(pos))

You need to set the context menu policy on the header itself (if I've understood correctly), so...
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu)
and connect to the `QHeaderView::customContextMenuRequested signal...
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.handle_context_menu_request)

Related

How to show another window

The project occurs loging in and signing in.
Im trying a transition from registerwindow to mainwindow and when we submit the window is automatically transit to mainwindow. There is only way to do this (at least for me) i have to import two python doc which named mainwindow.py and register.py, they are in same doc by the way.
This is the mainmenu.py
from PyQt5 import QtCore,QtGui,QtWidgets
from window.register import Ui_Form
class Ui_MainWindow(object):
def login(self):
self.window = QtWidgets.QWidget()
self.ui = Ui_Form()
self.ui.setupUi(self.window)
self.window.show()
MainWindow.hide()
and this is register.py
from PyQt5 import QtCore, QtGui, QtWidgets
from window.mainmenu import Ui_MainWindow
import sqlite3
class Ui_Form(object):
def submit(self):
sorgu2 = "Select * From users where nickname = ?"
sorgu = "INSERT INTO users values(?,?)"
self.cursor.execute(sorgu,(self.lineEdit.text(),self.lineEdit.text()))
self.connect.commit()
Form.hide()
self.window2 = QtWidgets.QMainWindow()
self.ui2 = Ui_MainWindow()
self.ui2.setupUi(self.window2)
self.window2.show()
Its supposed to be when i clicked to the button the register window will be hidden and mainmenu window will be show. Same thing for the mainmenu but the direct opposite
I know i am doing circular dependent imports but there is no other way but importing them to each other
If second window will be QDialog then you can hide main window, use exec() for QDialog and main window will wait till you close QDialog, and when it returns to main window then you can show it again.
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton("Show Second Window", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.hide() # hide main window
self.second = SecondWindow()
self.second.exec() # will wait till you close second window
self.show() # show main window again
class SecondWindow(QtWidgets.QDialog): # it has to be dialog
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton("Close It", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close() # go back to main window
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()
The other popular method is to create two widgets with all contents and replace widgets in one window.
from PyQt5 import QtWidgets
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.button = QtWidgets.QPushButton("Show Second Window", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close()
self.parent.set_content("Second")
class SecondWidget(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.button = QtWidgets.QPushButton("Close It", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close()
self.parent.set_content("Main")
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QVBoxLayout(self)
self.set_content("Main")
self.show()
def set_content(self, new_content):
if new_content == "Main":
self.content = MainWidget(self)
self.layout.addWidget(self.content)
elif new_content == "Second":
self.content = SecondWidget(self)
self.layout.addWidget(self.content)
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()
EDIT: Change window's content using QStackedLayout
from PyQt5 import QtWidgets
class FirstWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton("Show Second Stack", self)
self.button.clicked.connect(self.change_stack)
layout.addWidget(self.button)
def change_stack(self):
self.parent().stack.setCurrentIndex(1)
class SecondWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton("Show First Stack", self)
self.button.clicked.connect(self.change_stack)
layout.addWidget(self.button)
def change_stack(self):
self.parent().stack.setCurrentIndex(0)
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.stack = QtWidgets.QStackedLayout(self)
self.stack1 = FirstWidget(self)
self.stack2 = SecondWidget(self)
self.stack.addWidget(self.stack1)
self.stack.addWidget(self.stack2)
self.show()
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()

PyQt Options for each Tab Widget

I have a tab widget that contains table widgets for each tab, and I also have a dock widget with options. One of the options is the column count, and I want to change it as soon as the column count spin box value changes. But when switching to a different tab, I'd like the spin box value (and all other options) to reset / switch to that specific tab's settings.
My question is how to best do this and still have the options as a dock widget. I could store all settings as variables for each tab widget and then change the value each time a new tab is opened, I guess, but maybe there is a better solution.
from PyQt5 import QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
self.__setup__()
def __setup__(self):
self.resize(400, 400)
tabWidget = TabWidget(self)
self.setCentralWidget(tabWidget)
options = Options(self)
optionsDock = QtWidgets.QDockWidget()
optionsDock.setWidget(options)
optionsDock.setWindowTitle("Options")
self.addDockWidget(QtCore.Qt.TopDockWidgetArea, optionsDock)
options.spinBox_columns.valueChanged.connect(lambda: tabWidget.tabWidget.currentWidget().
setColumnCount(options.spinBox_columns.value()))
class Options(QtWidgets.QWidget):
def __init__(self, parent):
super(Options, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.spinBox_columns = QtWidgets.QSpinBox()
self.spinBox_columns.setValue(1)
self.spinBox_columns.setMinimum(1)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.spinBox_columns)
self.setLayout(layout)
class TabWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(TabWidget, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.tabWidget = QtWidgets.QTabWidget()
for i in range(3):
widget = QtWidgets.QTableWidget()
widget.setColumnCount(1)
widget.setRowCount(3)
self.tabWidget.addTab(widget, "Column " + str(i))
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.tabWidget)
self.setLayout(layout)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You have to connect the currentChanged signal provided by the index of the tab, then use the widget() method to obtain the index associated with that index, then access its QTabWidget and obtain the number of columns using it to place the value to the QSpinBox.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.__setup__()
def __setup__(self):
self.resize(400, 400)
tabWidget = TabWidget(self)
self.setCentralWidget(tabWidget)
options = Options(self)
optionsDock = QtWidgets.QDockWidget()
optionsDock.setWidget(options)
optionsDock.setWindowTitle("Options")
self.addDockWidget(QtCore.Qt.TopDockWidgetArea, optionsDock)
tabWidget.tabWidget.currentChanged.connect(lambda index: options.spinBox_columns.
setValue(tabWidget.tabWidget.widget(index).columnCount()))
options.spinBox_columns.valueChanged.connect(lambda value: tabWidget.tabWidget.currentWidget().
setColumnCount(value))

How to switch widgets visibility by request

The code below creates a single dialog window with three rows of widgets:
a combo, lineEdit and dateEdit. When combobox shows 'Show LineEdit' I would like lineEdit to be visible and dateEdit to be hidden. When "Show DateEdit" is selected I would like to hide LineEdit and show DateEdit instead. How to achieve it?
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
for i in range(3):
row = QtGui.QHBoxLayout()
combo = QtGui.QComboBox()
combo.addItems(['Show LineEdit','Show DateEdit'])
combo.activated.connect(self.activated)
row.addWidget(combo)
self.lineEdit = QtGui.QLineEdit()
self.dateEdit = QtGui.QDateEdit()
self.dateEdit.setVisible(False)
row.addWidget(self.lineEdit)
row.addWidget(self.dateEdit)
self.layout().insertLayout(i, row)
def activated(self):
print self.sender()
panel=Dialog()
panel.show()
app.exec_()
The strategy is to create a dictionary that has as a key a combobox and values ​​a dictionary of the corresponding other widget, then use the activated method that can return a text or a number.
from PyQt4 import QtGui
app = QtGui.QApplication([])
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
self.widgets = {}
for i in range(3):
row = QtGui.QHBoxLayout()
combo = QtGui.QComboBox()
combo.addItems(['Show LineEdit', 'Show DateEdit'])
combo.activated[str].connect(self.activated)
row.addWidget(combo)
lineEdit = QtGui.QLineEdit()
dateEdit = QtGui.QDateEdit()
self.widgets[combo] = [lineEdit, dateEdit]
self.changeWidget(combo.currentText(), lineEdit, dateEdit)
row.addWidget(lineEdit)
row.addWidget(dateEdit)
self.layout().insertLayout(i, row)
def activated(self, text):
linedit, dateEdit = self.widgets[self.sender()]
self.changeWidget(text, linedit, dateEdit)
def changeWidget(self, text, linedit, dateEdit):
if text == 'Show LineEdit':
linedit.setVisible(True)
dateEdit.setVisible(False)
elif text == 'Show DateEdit':
linedit.setVisible(False)
dateEdit.setVisible(True)
panel = Dialog()
panel.show()
app.exec_()
Inspired by eyllanesc answer posted above but with no dictionary.
from PyQt4 import QtGui
app = QtGui.QApplication([])
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setLayout(QtGui.QVBoxLayout())
for i in range(3):
row = QtGui.QHBoxLayout()
combo = QtGui.QComboBox()
combo.addItems(['Show LineEdit', 'Show DateEdit'])
combo.activated[str].connect(self.activated)
row.addWidget(combo)
combo.lineEdit = QtGui.QLineEdit()
combo.dateEdit = QtGui.QDateEdit()
row.addWidget(combo.lineEdit)
row.addWidget(combo.dateEdit)
self.layout().insertLayout(i, row)
def activated(self, title):
combo = self.sender()
if title == 'Show LineEdit':
combo.lineEdit.setVisible(True)
combo.dateEdit.setVisible(False)
else:
combo.lineEdit.setVisible(False)
combo.dateEdit.setVisible(True)
panel = Dialog()
panel.show()
app.exec_()

PyQt Right click Menu for QComboBox

I'm trying to extend the QComboClass with a right click menu, and offer it an option to set the current index to -1 (clearing the selection). I'm having trouble invoking the context menu or even the right click event.
class ComboBox(QComboBox):
def __init__(self, *args, **kwargs):
super(ComboBox, self).__init__()
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.showMenu)
def showMenu(self, pos):
menu = QMenu()
clear_action = menu.addAction("Clear Selection", self.clearSelection)
action = menu.exec_(self.mapToGlobal(pos))
def clearSelection(self):
self.setCurrentIndex(-1)
Could someone tell me what I'm doing wrong?
can you try this,
def showMenu(self,event):
menu = QMenu()
clear_action = menu.addAction("Clear Selection", self)
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == clear_action:
self.clearSelection()
You can try this
import sys
from PyQt4 import QtGui
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QMenu
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.lbl = QtGui.QLabel("Ubuntu", self)
self.combo = QtGui.QComboBox(self)
self.combo.setContextMenuPolicy(Qt.CustomContextMenu)
self.combo.customContextMenuRequested.connect(self.showMenu)
self.combo.addItem("Ubuntu")
self.combo.addItem("Mandriva")
self.combo.addItem("Fedora")
self.combo.addItem("Red Hat")
self.combo.addItem("Gentoo")
self.combo.move(50, 50)
self.lbl.move(50, 150)
self.combo.activated[str].connect(self.onActivated)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QtGui.QComboBox')
self.show()
def showMenu(self,pos):
menu = QMenu()
clear_action = menu.addAction("Clear Selection")
action = menu.exec_(self.mapToGlobal(pos))
if action == clear_action:
self.combo.setCurrentIndex(0)
def onActivated(self, text):
self.lbl.setText(text)
self.lbl.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQt: How to hide QMainWindow

Clicking Dialog_01's button hides its window and opens Dialog_02. Clicking Dialog_02's button should close its windows and unhide Dialog_01. How to achieve it?
import sys, os
from PyQt4 import QtCore, QtGui
class Dialog_02(QtGui.QMainWindow):
def __init__(self):
super(Dialog_02, self).__init__()
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
Button_02 = QtGui.QPushButton("Close THIS and Unhide Dialog 01")
Button_02.clicked.connect(self.closeAndReturn)
myBoxLayout.addWidget(Button_02)
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.setWindowTitle('Dialog 02')
def closeAndReturn(self):
self.close()
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(Dialog_01, self).__init__()
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
Button_01 = QtGui.QPushButton("Hide THIS and Open Dialog 02")
Button_01.clicked.connect(self.callAnotherQMainWindow)
myBoxLayout.addWidget(Button_01)
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.setWindowTitle('Dialog 01')
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02()
self.dialog_02.show()
self.dialog_02.raise_()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
Make the first window a parent of the second window:
class Dialog_02(QtGui.QMainWindow):
def __init__(self, parent):
super(Dialog_02, self).__init__(parent)
# ensure this window gets garbage-collected when closed
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
...
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtGui.QMainWindow):
...
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
If you want the same dialog to be shown each time, do something like:
def callAnotherQMainWindow(self):
self.hide()
if not hassattr(self, 'dialog_02'):
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
and hide() the child window, rather than closing it.

Categories