Show close button (x) after hiding it (QTabBar) - python

I'm creating a method to hide and show the close button of a tab. I found a way to hide it. However, I don't know how to do it in reverse.
This is my existing code for hiding the close button. Using the same lines of codes, how can I show the close button of the tab?
def disable_close_button(self):
self.ui.tab_widget.tabBar().setTabButton(self.current_index(), QTabBar.RightSide, None)
def enable_close_button(self):
pass
Thanks in advance!

You are not hiding the button, you are eliminating it. So in my solution I get the button and then I hide it or show it as needed.
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
show_button = QtWidgets.QPushButton(
text="show",
clicked=self.enable_close_button
)
hide_button = QtWidgets.QPushButton(
text="hide",
clicked=self.disable_close_button
)
self.tab_widget = QtWidgets.QTabWidget(tabsClosable=True)
for i in range(4):
label = QtWidgets.QLabel(
text="label {}".format(i),
alignment=QtCore.Qt.AlignCenter
)
self.tab_widget.addTab(label , "tab-{}".format(i))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(show_button)
lay.addWidget(hide_button)
lay.addWidget(self.tab_widget)
#QtCore.pyqtSlot()
def enable_close_button(self):
ix = self.tab_widget.currentIndex()
button = self.tab_widget.tabBar().tabButton(ix, QtWidgets.QTabBar.RightSide)
if button is not None:
button.show()
#QtCore.pyqtSlot()
def disable_close_button(self):
ix = self.tab_widget.currentIndex()
button = self.tab_widget.tabBar().tabButton(ix, QtWidgets.QTabBar.RightSide)
if button is not None:
button.hide()
if __name__ == '__main__':
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
app.setStyle("fusion")
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

Related

How to open a new MDI sub-window in PyQt5?

What I want to do is to open a new Countrypage sub-window by clicking on the "New" button which is in Countrypage itself.
For example, if I click the "New" button in a CountryPage window (window title: "Country page"), one more new Countrypage window will be opened in the MDI area (window title: "Country Page 1"). Now if we click the "New" button in "Country Page 1", one more new window will open in the MDI area (window title: "Country page 2") and so on - and I want to close the windows one by one by pressing the corresponding "Close" button in Countrypage. New window are opened only by pressing a "New" button.
And if we close the last opened window by pressing the "Close" button, the text item in the "Country" text-box will be automatically updated in the previous window's "Country" text-box and so on.
Main Script :
import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
count = 0
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.mdi.setFixedSize(1000,400)
self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWindowTitle(" Sample Programme")
self.setGeometry(100,100,1600,600)
self.Ui()
self.show()
def Ui(self):
self.btn1=QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.right_layout = QHBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3,5,5,3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.right_layout.addWidget(self.mdi)
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addLayout(self.right_layout)
self.main_layout.addStretch()
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
self.subwindow1 = QMdiSubWindow()
self.subwindow1.setObjectName("SubWindow_1")
# self.subwindow1.setWindowFlag(Qt.FramelessWindowHint)
print(Countrypage.btn2click)
def countrypage(self):
self.countrywindow = Countrypage()
self.subwindow1.setWidget(self.countrywindow)
self.subwindow1.setWindowTitle("Create Country")
self.subwindow1.setFixedWidth(300)
self.mdi.addSubWindow(self.subwindow1)
self.subwindow1.show()
self.mdi.cascadeSubWindows()
self.countrywindow.closeRequsted.connect(self.subwindow1close)
def subwindow1close(self):
print("close activated from mdi programme")
self.subwindow1.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("Windows")
mainwindow.show()
sys.exit(app.exec_())
Countrypage.py
import sys,os
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import pyqtSignal
class Countrypage(QWidget):
closeRequsted = pyqtSignal()
def __init__(self):
super().__init__()
self.btn1 = QPushButton("close")
self.btn2 = QPushButton("New")
self.btn1.clicked.connect(self.result)
self.btn2.clicked.connect(self.btn2click)
self.tb_country = QLineEdit()
self.tb_continent =QLineEdit()
self.form_layout = QFormLayout()
self.form_layout.addRow("Country",self.tb_country)
self.form_layout.addRow("continent",self.tb_continent)
self.form_layout.addRow("",self.btn2)
self.form_layout.addRow("",self.btn1)
self.setLayout(self.form_layout)
def result(self):
self.closeRequsted.emit()
def btn2click(self):
btn2text = (self.btn2.text())
print(btn2text)
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())
The adding and closing of sub-windows is best handled by the main-window. The CountryPage class doesn't need to know anything about the sub-windows. The new/close buttons can be directly connected to methods of the main-window. This makes it easier to manage the sub-windows via the functions of the mdi-area.
Below is a re-write of your example which should do what you asked for:
Main Script:
import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.mdi.setFixedSize(1000, 400)
self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWindowTitle("Sample Programme")
self.setGeometry(100, 100, 1600, 600)
self.Ui()
def Ui(self):
self.btn1 = QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.right_layout = QHBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3, 5, 5, 3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.right_layout.addWidget(self.mdi)
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addLayout(self.right_layout)
self.main_layout.addStretch()
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
page = Countrypage()
subwindow = self.mdi.addSubWindow(page)
subwindow.setWindowTitle("Create Country")
subwindow.setFixedWidth(300)
page.btn_close.clicked.connect(self.subwindowclose)
page.btn_new.clicked.connect(self.countrypage)
subwindow.show()
self.mdi.cascadeSubWindows()
def subwindowclose(self):
print("close activated from mdi programme")
current = self.mdi.activeSubWindow()
if current is not None:
self.mdi.activatePreviousSubWindow()
previous = self.mdi.activeSubWindow()
if previous is not None:
previous.widget().update_fields(current.widget())
current.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("Windows")
mainwindow.show()
sys.exit(app.exec_())
Countrypage.py:
import sys,os
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import pyqtSignal
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.btn_close = QPushButton("Close")
self.btn_new = QPushButton("New")
self.tb_country = QLineEdit()
self.tb_continent = QLineEdit()
self.form_layout = QFormLayout()
self.form_layout.addRow("Country", self.tb_country)
self.form_layout.addRow("Continent", self.tb_continent)
self.form_layout.addRow("", self.btn_close)
self.form_layout.addRow("", self.btn_new)
self.setLayout(self.form_layout)
def update_fields(self, other):
if isinstance(other, Countrypage):
self.tb_country.setText(other.tb_country.text())
self.tb_continent.setText(other.tb_continent.text())
else:
raise TypeError('invalid page type')

How to avoid to get special characters on QInputDialog? [duplicate]

I am writing a Python Application where the user can enter a String in an QInputDialog. How can i use the QCompleter to make Inputs easier?
I've already been searching on different websites and read the doc from
https://doc.qt.io/qt-5/qcompleter.html#details
but couldn't find any help for this problem.
To me, it seems like the QCompleter is only available for QLineEdit and QComboBox. (Please proof me wrong)
ian, okPressed = QInputDialog.getText(self, "IAN", "Please enter IAN:")
It would help me a lot if anyone could show me some code examples how to deal with this problem.
If it's not possible to use the QCompleter within the QInputDialog, do you guys have an idea for a workaround?
Much thanks =)
There are 2 possible solutions:
Get the QInputDialog through the parent-child relationship between the widgets using findChild():
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button = QtWidgets.QPushButton("Press me", clicked=self.onClicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def onClicked(self):
QtCore.QTimer.singleShot(0, self.onTimeout)
ian, okPressed = QtWidgets.QInputDialog.getText(
self, "IAN", "Please enter IAN:"
)
#QtCore.pyqtSlot()
def onTimeout(self):
dialog = self.findChild(QtWidgets.QInputDialog)
if dialog is not None:
le = dialog.findChild(QtWidgets.QLineEdit)
if le is not None:
words = ["alpha", "omega", "omicron", "zeta"]
completer = QtWidgets.QCompleter(words, le)
le.setCompleter(completer)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(320, 240)
w.show()
sys.exit(app.exec_())
Do not use the static method and create the QInputDialog with the same elements:
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
button = QtWidgets.QPushButton("Press me", clicked=self.onClicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def onClicked(self):
dialog = QtWidgets.QInputDialog(self)
dialog.setWindowTitle("IAN")
dialog.setLabelText("Please enter IAN:")
dialog.setTextValue("")
le = dialog.findChild(QtWidgets.QLineEdit)
words = ["alpha", "omega", "omicron", "zeta"]
completer = QtWidgets.QCompleter(words, le)
le.setCompleter(completer)
ok, text = (
dialog.exec_() == QtWidgets.QDialog.Accepted,
dialog.textValue(),
)
if ok:
print(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(320, 240)
w.show()
sys.exit(app.exec_())

Display a dialog within the main window

The code below creates an empty dialog when Hello! is pressed:
from PyQt5 import QtWidgets, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
w.setLayout(layout)
self.setCentralWidget(w)
toyButton = QtWidgets.QPushButton("Hello!")
layout.addWidget(toyButton)
toyButton.clicked.connect(self.showdialog)
def showdialog(self):
d = QtWidgets.QDialog()
d.setWindowTitle("Dialog")
d.setWindowModality(QtCore.Qt.WindowModal)
d.exec_()
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec_()
How can I force the dialog to appear within the main window instead of having it floating around as an independent window?
You simply need to give the dialog a parent widget: d = QtWidgets.QDialog(self.centralWidget()). self is also a valid parent; pick your favorite!
You could also add d to the layout: self.centralWidget().layout().addWidget(d);
although this second one does not respect your modality+exec.
Here's the full code comparing both methods:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
w.setLayout(layout)
self.setCentralWidget(w)
toyButton = QtWidgets.QPushButton("Hello!")
layout.addWidget(toyButton)
toyButton.clicked.connect(self.showdialog)
toyButton2 = QtWidgets.QPushButton("Hello2!")
layout.addWidget(toyButton2)
toyButton2.clicked.connect(self.showdialog2)
def showdialog(self):
d = QtWidgets.QDialog(self.centralWidget())
d.setWindowTitle("Dialog")
d.setWindowModality(QtCore.Qt.WindowModal)
d.exec_()
def showdialog2(self):
d = QtWidgets.QDialog()
d.setWindowTitle("Dialog2") # window title won't be seen...
d.setWindowModality(QtCore.Qt.WindowModal)
QtWidgets.QPushButton('Dialog2', parent=d) # ... so we add a little something
self.centralWidget().layout().addWidget(d)
d.exec_()

How to make a tab pane with no title bar?

I want to make GUI like below with PyQt5, but I can't find an example to help me.
I searched for "change layout on qwidget" and "tab pane with no title bar" and "card layout" without luck. How can I make this with PyQt5?
You have to use a QStackedLayout (or a QStackedWidget) that changes pages when the buttons are pressed. And the first page should have the buttons. I have also implemented the back() method that returns to the initial page, that slot must be invoked when the Change button is pressed:
from functools import partial
from PyQt5 import QtCore, QtWidgets
class CardWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(CardWidget, self).__init__(parent)
self._layout = QtWidgets.QStackedLayout(self)
button_widget = QtWidgets.QWidget()
self.btn_lay = QtWidgets.QFormLayout(button_widget)
self._layout.addWidget(button_widget)
def add_widget(self, text, widget):
self._layout.addWidget(widget)
btn = QtWidgets.QPushButton(text)
self.btn_lay.addRow(btn)
btn.clicked.connect(partial(self._layout.setCurrentWidget, widget))
#QtCore.pyqtSlot()
def back(self):
self._layout.setCurrentIndex(0)
class Widget(QtWidgets.QWidget):
backSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.le1 = QtWidgets.QLineEdit()
self.le2 = QtWidgets.QLineEdit()
button = QtWidgets.QPushButton("Change")
button.clicked.connect(self.backSignal)
flay = QtWidgets.QFormLayout()
flay.addRow("Value 1:", self.le1)
flay.addRow("Value 2:", self.le2)
lay = QtWidgets.QVBoxLayout(self)
lay.addLayout(flay)
lay.addWidget(button)
def create_label():
label = QtWidgets.QLabel(
"Some Other Components",
alignment=QtCore.Qt.AlignCenter
)
label.setStyleSheet("background-color:blue;")
return label
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
c = CardWidget()
for i in range(3):
w = Widget()
w.backSignal.connect(c.back)
c.add_widget("Want to Change value {}".format(i+1), w)
p = QtWidgets.QWidget()
lay = QtWidgets.QGridLayout(p)
lay.addWidget(create_label(), 0, 0, 1, 2)
lay.addWidget(c, 1, 0)
lay.addWidget(create_label(), 1, 1)
lay.setColumnStretch(0, 1)
lay.setColumnStretch(1, 1)
lay.setRowStretch(0, 1)
lay.setRowStretch(1, 1)
p.resize(640, 480)
p.show()
sys.exit(app.exec_())

QToolButton clicked signal is only called once

I have attached a QMenu to the QToolButton in which the Menu is not reflecting correctly.
I am populating my QMenu from reading a .txt file. The menu is showing the correct items on the first run, but if I made changes in the .txt file and re-clicked on the button, the QMenu is still showing the item on the first run.
Adding on, it seems that the clicked signal is only being called once?
class MyWin(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyWin, self).__init__()
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
vlay = QtGui.QVBoxLayout(central_widget)
hlay = QtGui.QHBoxLayout()
vlay.addLayout(hlay)
vlay.addStretch()
self.add_button = QtGui.QToolButton()
self.tab_bar = QtGui.QTabBar(self)
self.add_button.setIcon(QtGui.QIcon('add.png'))
self.add_button.clicked.connect(self.set_menu)
#self.add_button.setMenu(self.set_menu())
#self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)
self.tab_bar.setTabButton(
0,
QtGui.QTabBar.ButtonPosition.RightSide,
self.add_button
)
hlay.addWidget(self.add_button)
hlay.addWidget(self.tab_bar)
def set_menu(self):
with open('/Desktop/item_file.txt') as f:
menu_options = f.read().splitlines()
print menu_options
qmenu = QtGui.QMenu(self.add_button)
for opt in menu_options:
qmenu.addAction(opt, partial(self.set_new_tab, opt))
self.add_button.setMenu(qmenu)
self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)
def set_new_tab(self, opt):
self.tab_bar.addTab(opt)
When a Menu is established in the QPushButton, the mousePressEvent event no longer reaches the QPushButton but is intercepted by the QMenu so the clicked signal is not emited.
One solution is to set a QMenu by default and use the aboutToShow signal to call set_menu that will add the QActions.
On the other hand they are the same because clicked is not issued and therefore set_menu is not called, and even if you call set_menu as I have established it is better to reuse than create so in this case I eliminate the previous QActions with clear method.
from functools import partial
from PyQt4 import QtCore, QtGui
class MyWin(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyWin, self).__init__()
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
vlay = QtGui.QVBoxLayout(central_widget)
hlay = QtGui.QHBoxLayout()
vlay.addLayout(hlay)
vlay.addStretch()
self.add_button = QtGui.QToolButton()
self.tab_bar = QtGui.QTabBar(self)
self.add_button.setIcon(QtGui.QIcon('add.png'))
self.qmenu = QtGui.QMenu(self.add_button)
self.add_button.setMenu(self.qmenu)
self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)
self.qmenu.aboutToShow.connect(self.set_menu)
self.tab_bar.setTabButton(
0,
QtGui.QTabBar.ButtonPosition.RightSide,
self.add_button
)
hlay.addWidget(self.add_button)
hlay.addWidget(self.tab_bar)
#QtCore.pyqtSlot()
def set_menu(self):
with open('/Desktop/item_file.txt') as f:
menu_options = f.read().splitlines()
self.qmenu.clear()
for opt in menu_options:
self.qmenu.addAction(opt, partial(self.set_new_tab, opt))
def set_new_tab(self, opt):
self.tab_bar.addTab(opt)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MyWin()
w.show()
sys.exit(app.exec_())

Categories