Hello I'm trying to embbed a qtableview in a qvbox layout
But I do not manage to keep the qtableview in the qvboxlayout
The code below gives me two windows I do not know why layout.addwidget does not behave as expected.
class QMT(QtGui.QMainWindow):
def __init__(self, parent=None):
super(QMT, self).__init__(parent)
layout=QVBoxLayout(self)
self.view = QtGui.QTableView()
self.name = QtGui.QLabel("Name:")
layout.addWidget(self.view)
layout.addWidget(self.name)
self.initUI() #Windows stuff + show()
self.setLayout(layout)
I answer myself and found basic statement I did not understand about PyQt:
There are 2 steps to using the layout system in Qt: organizing widgets into a layout, and applying a layout to a widget.
So here is something that work :
class QMT(QtGui.QMainWindow):
def __init__(self, parent=None):
super(QMT, self).__init__(parent)
# Create Qtable view widget
self.view = QtGui.QTableView(self)
# Create Canvas for graph
self.fig=Figure(figsize=(5,5), dpi=100)
self.canvas=FigureCanvas(self.fig)
self.graph=self.fig.add_subplot(111)
#Layout management
#Initiate splitter (more convenient for end user)
splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
splitter1.addWidget(self.view)
splitter1.addWidget(self.canvas)
# Create Horizontal Layout Box
hbox = QtGui.QHBoxLayout()
# Add the splitter to the hbox
hbox.addWidget(splitter1)
#initiate widget to be shown
widget = QtGui.QWidget(self)
widget.setLayout(hbox)
#Set widget as central widget
self.setCentralWidget(widget)
# Windows & Menu stuff
self.initUI()
Related
I am learning PyQt5 recently and have problems when I want to delete a widget in QScrollArea. Is there an elegant way to visit the element in QScrollArea and delete it when the "delete" button in that element is clicked? Thank you for any help!
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
self.layout.addWidget(QPushButton('Delete'))
self.setLayout(self.layout)
# connect options
connect_options()
def connect_options(self):
pass
class MyList(QScrollArea):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.widget = QWidget()
for x in range(10):
self.layout.addWidget(MyWidget(str(x)))
self.widget.setLayout(self.layout)
self.setMinimumSize(1024, 500)
self.setWidget(self.widget)
First I would suggest you look into QListWidget, It will provide you various functions to handle situations like this.
for your problem, you will need to delete the widget from its parent layout & then delete it from the GUI
class MyWidget(QWidget):
def __init__(self, id):
super().__init__()
self.layout = QHBoxLayout()
self.layout.addWidget(QPlainTextEdit(id))
del_btn = QPushButton('Delete')
del_btn.clicked.connect(self._delete) # connect the click event to your delete function
self.layout.addWidget(del_btn)
self.setLayout(self.layout)
# connect options
self.connect_options()
def connect_options(self):
pass
def _delete(self):
# here you will delete your widget
parent_layout = self.parent().layout()
parent_layout.removeWidget(self) # remove the widget from its parent layout
self.deleteLater() # lets Qt knows it needs to delete this widget from the GUI
del self
My understanding is that the in_ keyword argument to pack/grid should allow me to specify the managing widget. I want to pack arbitrary widgets inside a Frame subclass, so I passed the widgets and packed them during intialization, but the widgets didn't appear (although space in the window appears to have been allocated...). If I create the widget internally using master which is root, there is no issue and the widgets are displayed as expected.
The following working example and its output demonstrate the issue:
import tkinter as tk
from tkinter import ttk
class ItemContainerExternal(ttk.Frame):
def __init__(self, master, input_label, input_object):
ttk.Frame.__init__(self, master)
self.label = input_label
self.label.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
self.input_object = input_object
self.input_object.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
def get(self):
return variable.get()
class ItemContainerInternal(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
ttk.Label(master, text='internal').pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
self.input_object = ttk.Entry(master)
self.input_object.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
def get(self):
return variable.get()
if __name__ == '__main__':
root = tk.Tk()
inputobj = ttk.Entry(root)
inputlabel = ttk.Label(root, text='external')
ItemContainerExternal(root, inputlabel, inputobj).grid(row=0, column=0)
ItemContainerInternal(root).grid(row=1, column=0)
root.mainloop()
The problem is that you're creating the entry and label before you're creating the frame, so they have a lower stacking order. That means the frame will be on top of the entry and label and thus, obscuring them from view.
A simple fix is to call lift() on the entry and label:
class ItemContainerExternal(tk.Frame):
def __init__(self, master, input_label, input_object):
...
self.input_object.lift()
self.label.lift()
The order in which widgets get created matters. Newer widgets are "on top of" previous widgets.
Call .lower() on the Frame after you create it, assuming it's created after all the widgets that you will pack into it. If not, you'll need to either call .lower() again on the Frame after creating a new widget to go inside it, or you'll have to raise the new widget via .lift() as per Bryan's answer.
I need to customize my QTabWidget so that one of its tab bars (lets say there are 4 tabs overall) has expanding property and will fill the remaining space between other tabs. Any ideas?
you can subclass QTabBar, set it to the width of your tabwidget (the height depends on the fontsize) and overwrite tabSizeHint():
class tabBar(QTabBar):
def __init__(self, width, height, parent=None):
QTabBar.__init__(self, parent)
self.setFixedSize(width, height)
def tabSizeHint(self, i):
f = 3 # Tab3 shall be f times wider then the other tabs
tw = int(self.width()/(self.count() + f -1)) # width per Tab
if i == 2: # Tab3
# return QSize(tw*f, self.height()) edited -> rounding error possible
return QSize(self.width() - (self.count() - 1)*tw, self.height())
return QSize(tw, self.height()) # all other tabs
and set this tabBar to your tabwidget:
tb = tabBar(tabWidget.width(), 34) # tabBars height depends on fontSize
tabwidget..setTabBar(tb)
looks like this:
edit:
if the tabWidget is resized, a resizeEvent() occurs. In this moment the tabWidget already has its new size and is repainted immediatedly after the resizeEvent(),
see QT-Doc QTabWidget.resizeEvent
So if the width() of the tabBar is adapted in resizeEvent(), the tabBar will always have the same width as the tabwidget. Because the tabSizeHint() depends on the width, all tabs will have the correct width too. So You can subclass QTabWidget() and overwrite resizeEvent() for a dynamical solution:
class tabWidget(QTabWidget):
def __init__(self, parent=None):
QTabWidget.__init__(self, parent)
def resizeEvent(self, event):
self.tabBar().setFixedWidth(self.width())
QTabWidget.resizeEvent(self, event)
To do this correctly, it's necessary to work backwards from the existing sizes of the tabs. This is because the tab sizes are affected by the current style, and by other features such as tab close buttons. It's also important to set a minimum size for the tab which is exandable (otherwise it could be resized to nothing).
Here is a simple demo that does all that:
from PyQt4 import QtCore, QtGui
class TabBar(QtGui.QTabBar):
def __init__(self, expanded=-1, parent=None):
super(TabBar, self).__init__(parent)
self._expanded = expanded
def tabSizeHint(self, index):
size = super(TabBar, self).tabSizeHint(index)
if index == self._expanded:
offset = self.width()
for index in range(self.count()):
offset -= super(TabBar, self).tabSizeHint(index).width()
size.setWidth(max(size.width(), size.width() + offset))
return size
class TabWidget(QtGui.QTabWidget):
def __init__(self, expanded=-1, parent=None):
super(TabWidget, self).__init__(parent)
self.setTabBar(TabBar(expanded, self))
def resizeEvent(self, event):
self.tabBar().setMinimumWidth(self.width())
super(TabWidget, self).resizeEvent(event)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.tabs = TabWidget(2, self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.tabs)
for text in 'One Two Three Four'.split():
self.tabs.addTab(QtGui.QWidget(self), text)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 400, 200)
window.show()
sys.exit(app.exec_())
I've written a short class to create a new window, with a tab menu, and a canvas on one of them. Every time I launch the app two windows show up. One of them is the wanted one, the other one is an empty one. I'm new to OOP in python, I guess I miss something in my class.
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.tab1 = QtGui.QWidget()
self.tab2 = QtGui.QWidget()
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.canvas)
self.tab1.setLayout(self.layout)
self.tabs = QtGui.QTabWidget()
self.tabs.addTab(self.tab1, "Database")
self.tabs.addTab(self.tab2, "Current")
self.tabs.show()
My mistake was here:
if __name__=='__main__':
app=QtGui.QApplication(sys.argv)
main = GUI.Window()
#main.show()
app.exec_()
The main.show() cause the problem.
I have a mainwindow containing many dockwidget, toolbars,and menubars. I am able to toggle the visibility of dockwidgets and toolbars but not the menubars. Why does not the below code work?
Is it possible to view only the central widget of a QMainWindow as full screen in PyQt by hiding the menubars?
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.local_grview = QGraphicsView()
self.local_grview.setSceneRect(0,0,200,200)
self.setCentralWidget(self.local_grview)
def fullscreen(self):
self.hide()
self.setParent(None)
self.local_grview.showFullScreen()
self.local_grview.show()