QTabWidget insert a QSplitter can't switch when the splitter disabled - python

I insert a QFrame and QTabWidget in the QSplitter. And I wanna forbidden to adjust the size of elements in QSplitter. So I call method of 'setDisabled' in QSplitter. It's useful for disabling resizing the elements. But I also can't switch tab of QTabWidget. Who can give me some suggestions? Thanks very much......
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QSplitter, QHBoxLayout, QFrame, QTabWidget
from PyQt5.QtCore import Qt
class Example1(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle("Demo")
self.layout = QHBoxLayout()
top_frame = QFrame()
top_frame.setFrameShape(QFrame.StyledPanel)
bottom_frame = QTabWidget(self)
tab1 = QWidget()
tab2 = QWidget()
bottom_frame.setTabText(0, "Generic")
bottom_frame.setTabText(1, "Other")
bottom_frame.addTab(tab1, "Tab 1")
bottom_frame.addTab(tab2, "Tab 2")
splitter = QSplitter()
splitter.setOrientation(Qt.Vertical)
splitter.addWidget(top_frame)
splitter.addWidget(bottom_frame)
splitter.setSizes([300, 300])
**splitter.setDisabled(True)**
self.layout.addWidget(splitter)
self.setLayout(self.layout)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example1()
sys.exit(app.exec_())
the running result of the program

When you disable a widget you also disable its children, so disabling the QSplitter also disables the QTabWidget.
A possible solution is enable or disable the handles:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QApplication,
QFrame,
QHBoxLayout,
QSplitter,
QSplitterHandle,
QTabWidget,
QWidget,
)
class CustomSplitter(QSplitter):
#property
def enabled(self):
if not hasattr(self, "_enabled"):
self._enabled = True
return self._enabled
#enabled.setter
def enabled(self, d):
self._enabled = d
for i in range(self.count()):
self.handle(i).setEnabled(self.enabled)
def createHandle(self):
handle = super().createHandle()
handle.setEnabled(self.enabled)
return handle
class Example1(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle("Demo")
self.layout = QHBoxLayout()
top_frame = QFrame()
top_frame.setFrameShape(QFrame.StyledPanel)
bottom_frame = QTabWidget(self)
tab1 = QWidget()
tab2 = QWidget()
bottom_frame.setTabText(0, "Generic")
bottom_frame.setTabText(1, "Other")
bottom_frame.addTab(tab1, "Tab 1")
bottom_frame.addTab(tab2, "Tab 2")
splitter = CustomSplitter()
splitter.setOrientation(Qt.Vertical)
splitter.addWidget(top_frame)
splitter.addWidget(bottom_frame)
splitter.setSizes([300, 300])
splitter.enabled = False
self.layout.addWidget(splitter)
self.setLayout(self.layout)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = Example1()
sys.exit(app.exec_())

I haven't used a QSplitter before but are the .setFixedHeight(300) .setFixedWidth(300), or .setFixedSize(300, 300) methods not applicable here?

QSplitter lets you get hold of its handles, which are the GUI element visible to the user. If you have two widgets in a splitter, you have a single visible handle; the handle at index 0 is always invisible.
You can manipulate that widget explicitely, e.g. disable it. Try:
splitter.handle(1).enabled = False
This disables only said GUI element, while the rest of the splitter (your two content widgets) will stay enabled.

Related

PyQT layouts: why does QVBoxLayout stack the buttons instead of creating the layout? [duplicate]

I'm making an application in PyQt4 and this is my code so far:
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUi()
def initUi(self):
self.setWindowTitle('Main Menu')
self.setFixedSize(1200, 625)
self.firstWidgets()
self.show()
def firstWidgets(self):
self.vbox1 = QtGui.QVBoxLayout()
self.task1 = QtGui.QLabel('Check 1', self)
self.task1CB = QtGui.QCheckBox(self)
self.hbox1 = QtGui.QHBoxLayout()
self.hbox1.addWidget(self.task1)
self.hbox1.addWidget(self.task1CB)
self.vbox1.addLayout(self.hbox1)
self.setLayout(self.vbox1)
def main():
application = QtGui.QApplication(sys.argv)
gui = MainWindow()
sys.exit(application.exec_())
if __name__=='__main__':
main()
My problem is in MainWindow.firstWidgets(). I try to set a layout but I get an error even though that's my first time using .setLayout for that form, which confuses me.
QWidget::setLayout: Attempting to set QLayout "" on MainWindow "",
which already has a layout
You can't set a QLayout directly on the QMainWindow. You need to create a QWidget and set it as the central widget on the QMainWindow and assign the QLayout to that.
wid = QtGui.QWidget(self)
self.setCentralWidget(wid)
layout = QtGui.QVBoxLayout()
wid.setLayout(layout)
NOTE: This is for Qt4 -- see the other answer on this question for the Qt5 updated code.
Just an update to Brenden Abel's answer:
QWidget and QVBoxLayout (for Python3, PyQt5) are now contained in the PyQt5.QtWidgets module and not the PyQt5.QtGui module.
So updated code:
wid = QtWidgets.QWidget(self)
self.setCentralWidget(wid)
layout = QtWidgets.QVBoxLayout()
wid.setLayout(layout)
This is an example using PyQt5
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('My App')
# Cannot set QxxLayout directly on the QMainWindow
# Need to create a QWidget and set it as the central widget
widget = QWidget()
layout = QVBoxLayout()
b1 = QPushButton('Red' ); b1.setStyleSheet("background-color: red;")
b2 = QPushButton('Blue' ); b2.setStyleSheet("background-color: blue;")
b3 = QPushButton('Yellow'); b3.setStyleSheet("background-color: yellow;")
layout.addWidget(b1)
layout.addWidget(b2)
layout.addWidget(b3)
widget.setLayout(layout)
self.setCentralWidget(widget)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

PyQT5 QTabbar Expand tab header

I am writing a QTabwidget with only two tabs. But the tab headers (name) are not fitting the QTabwidget width. I want to fit the length of the tab bar (two tab headers)
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QWidget, QAction, QTabWidget,QVBoxLayout
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
class App(QMainWindow):
def __init__(self):
super().__init__()
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.show()
class MyTableWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.tabs = QTabWidget()
""" Here I want to fit the two tab
headers withthe QTabwidget width
"""
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.resize(300,200)
self.tabs.addTab(self.tab1,"Tab 1")
self.tabs.addTab(self.tab2,"Tab 2")
# Create first tab
self.tab1.layout = QVBoxLayout(self)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
The size of tabs is computed using the hints given by the current QStyle.
Since QTabWidget uses the sizeHint of the tab bar to set the tab bar size and the sizeHint is usually based on the tabSizeHint(), you have to reimplement both:
sizeHint() is required in order to provide a width (or height) that is the same as the parent;
tabSizeHint() takes into account the base implementation of sizeHint() to compute the hint based on the contents of the tabs, and if it's less than the current size it suggests a size based on the available space divided by the tab count;
class TabBar(QtWidgets.QTabBar):
def sizeHint(self):
hint = super().sizeHint()
if self.isVisible() and self.parent():
if not self.shape() & self.RoundedEast:
# horizontal
hint.setWidth(self.parent().width())
else:
# vertical
hint.setHeight(self.parent().height())
return hint
def tabSizeHint(self, index):
hint = super().tabSizeHint(index)
if not self.shape() & self.RoundedEast:
averageSize = self.width() / self.count()
if super().sizeHint().width() < self.width() and hint.width() < averageSize:
hint.setWidth(averageSize)
else:
averageSize = self.height() / self.count()
if super().sizeHint().height() < self.height() and hint.height() < averageSize:
hint.setHeight(averageSize)
return hint
# ...
self.tabWidget = QtWidgets.QTabWidget()
self.tabWidget.setTabBar(TabBar(self.tabWidget))
Do note that this is a very basic implementation, there are some situations for which you might see the scroll buttons with very long tab names, even if theoretically there should be enough space to see them.
Inspired by this answer, I think you can override showEvent (and even resizeEvent) to calculate the new width and set it through stylesheets.
It is not canonical but it does the job.
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTabWidget, QVBoxLayout
class App(QMainWindow):
def __init__(self):
super().__init__()
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.show()
class MyTableWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.tabs = QTabWidget()
self.tabs.tabBar().setExpanding(True)
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.resize(300, 200)
self.tabs.addTab(self.tab1, "Tab 1")
self.tabs.addTab(self.tab2, "Tab 2")
# Create first tab
self.tab1.layout = QVBoxLayout(self)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
def resizeEvent(self, event):
super().resizeEvent(event)
self._set_tabs_width()
def showEvent(self, event):
super().showEvent(event)
self._set_tabs_width()
def _set_tabs_width(self):
tabs_count = self.tabs.count()
tabs_width = self.tabs.width()
tab_width = tabs_width / tabs_count
css = "QTabBar::tab {width: %spx;}" % tab_width
self.tabs.setStyleSheet(css)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

PyQt5 Layouts not moving button [duplicate]

I'm making an application in PyQt4 and this is my code so far:
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUi()
def initUi(self):
self.setWindowTitle('Main Menu')
self.setFixedSize(1200, 625)
self.firstWidgets()
self.show()
def firstWidgets(self):
self.vbox1 = QtGui.QVBoxLayout()
self.task1 = QtGui.QLabel('Check 1', self)
self.task1CB = QtGui.QCheckBox(self)
self.hbox1 = QtGui.QHBoxLayout()
self.hbox1.addWidget(self.task1)
self.hbox1.addWidget(self.task1CB)
self.vbox1.addLayout(self.hbox1)
self.setLayout(self.vbox1)
def main():
application = QtGui.QApplication(sys.argv)
gui = MainWindow()
sys.exit(application.exec_())
if __name__=='__main__':
main()
My problem is in MainWindow.firstWidgets(). I try to set a layout but I get an error even though that's my first time using .setLayout for that form, which confuses me.
QWidget::setLayout: Attempting to set QLayout "" on MainWindow "",
which already has a layout
You can't set a QLayout directly on the QMainWindow. You need to create a QWidget and set it as the central widget on the QMainWindow and assign the QLayout to that.
wid = QtGui.QWidget(self)
self.setCentralWidget(wid)
layout = QtGui.QVBoxLayout()
wid.setLayout(layout)
NOTE: This is for Qt4 -- see the other answer on this question for the Qt5 updated code.
Just an update to Brenden Abel's answer:
QWidget and QVBoxLayout (for Python3, PyQt5) are now contained in the PyQt5.QtWidgets module and not the PyQt5.QtGui module.
So updated code:
wid = QtWidgets.QWidget(self)
self.setCentralWidget(wid)
layout = QtWidgets.QVBoxLayout()
wid.setLayout(layout)
This is an example using PyQt5
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('My App')
# Cannot set QxxLayout directly on the QMainWindow
# Need to create a QWidget and set it as the central widget
widget = QWidget()
layout = QVBoxLayout()
b1 = QPushButton('Red' ); b1.setStyleSheet("background-color: red;")
b2 = QPushButton('Blue' ); b2.setStyleSheet("background-color: blue;")
b3 = QPushButton('Yellow'); b3.setStyleSheet("background-color: yellow;")
layout.addWidget(b1)
layout.addWidget(b2)
layout.addWidget(b3)
widget.setLayout(layout)
self.setCentralWidget(widget)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Remember size of Dock widget on toggle view

I have two dock widgets in my QApplication widget.
On each widget toggle view action is called.So that it hides and shows when I check and uncheck the dock widget in menu options.`
On toggle, dock widget are not taking the size which they had before being toggled.
How can I go ahead and implement this?
following the minimal code which produces the same problem.
import sys
from PySide2.QtWidgets import QMainWindow, QAction, qApp,QApplication,QDockWidget,QWidget
from PySide2.QtGui import QIcon
from PySide2.QtCore import Qt
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
menubar = self.menuBar()
fileMenu = menubar.addMenu('&Funcionalities')
dockwindow1 = QDockWidget("dock1",self)
dockwindow1.setWidget(QWidget())
fileMenu.addAction(dockwindow1.toggleViewAction())
self.setCentralWidget(dockwindow1)
dockwindow2 = QDockWidget("dock2",self)
fileMenu.addAction(dockwindow2.toggleViewAction())
self.addDockWidget(Qt.RightDockWidgetArea, dockwindow2)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Simple menu')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())`

When executing thread, the first execution fails in try statement

from PyQt5.QtWidgets import QMainWindow, QApplication,QLineEdit, QPushButton, QWidget, QAction, QTabWidget,QVBoxLayout
from PyQt5.QtCore import (QCoreApplication, QObject, QRunnable, QThread,
QThreadPool, pyqtSignal)
import sys
import os
from shutil import copy2
import _thread
import time
class AThread(QThread):
def run(self):
count = 0
while count < 5:
time.sleep(1)
print("A Increasing")
count += 1
class Example(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.setWindowTitle('Learn')
self.setGeometry(300, 300, 300, 150)
self.layout = QVBoxLayout(self)
# Initialize tab screen
self.tabs = QTabWidget()
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.resize(300,200)
# Add tabs
self.tabs.addTab(self.tab1,"Tab 1")
self.tabs.addTab(self.tab2,"Tab 2")
# Create first tab
self.tab1.layout = QVBoxLayout(self)
self.pushButton1 = QPushButton("PyQt5 button")
self.pushButton1.clicked.connect(self.ON_PRESS)
self.textbox = QLineEdit(self)
self.tab1.layout.addWidget(self.textbox )
self.tab1.layout.addWidget(self.pushButton1)
self.tab1.setLayout(self.tab1.layout)
#Create Textbox inputs
# Add tabs to widget
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
def using_q_thread(self):
app = Example()
thread = AThread()
thread.start()
sys.exit(app.exec_())
def ON_PRESS(self):
###Here is the Issue
try:
self.using_q_thread()
except:
print ("Error: unable to start thread")
###Drag and Drop files to directory
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
Hoping I am asking this correctly, but whenever using QThread there appears to be a bit of a hiccup. the first attempt to access the threaded function causes the try statement to fail, but then it immediately works. Im just curious if this is part of the functionality or if there is any issue with my code.
Avoid using try-except as you see hidden the error, in my personal case I avoid using it as far as I can for this type of problems.
I do not see it necessary to create another Example within using_q_thread, another problem is that thread is a local variable that will be eliminated, so thread must be a member of the class for its scope to increase.
import sys
import time
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTabWidget, QPushButton, QLineEdit
class AThread(QThread):
def run(self):
count = 0
while count < 5:
time.sleep(1)
print("A Increasing")
count += 1
class Example(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.setWindowTitle('Learn')
self.setGeometry(300, 300, 300, 150)
self.layout = QVBoxLayout(self)
# Initialize tab screen
self.tabs = QTabWidget()
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.resize(300,200)
# Add tabs
self.tabs.addTab(self.tab1,"Tab 1")
self.tabs.addTab(self.tab2,"Tab 2")
# Create first tab
self.tab1.layout = QVBoxLayout()
self.pushButton1 = QPushButton("PyQt5 button")
self.pushButton1.clicked.connect(self.ON_PRESS)
self.textbox = QLineEdit(self)
self.tab1.layout.addWidget(self.textbox )
self.tab1.layout.addWidget(self.pushButton1)
self.tab1.setLayout(self.tab1.layout)
#Create Textbox inputs
# Add tabs to widget
self.layout.addWidget(self.tabs)
def using_q_thread(self):
self.thread = AThread()
self.thread.start()
def ON_PRESS(self):
self.using_q_thread()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())

Categories