In the following example I have a Qt button which opens a pop-out window. However, the pop-out window takes the focus entirely. Is it possible to modify this such that I can still interact with the main window by moving the mouse over it, e.g. to change the value of QDoubleSpinBox even when the pop-out window is open? I think I may need to use the QtHoverEvent class put I can't find a good example of how to do this.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QGridLayout, QDoubleSpinBox,\
QLabel
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
btn = QPushButton('Button', self)
btn.move(50, 50)
btn2 = QDoubleSpinBox(self)
btn2.move(50,100)
self.sideWindowTest = sideWindowTest(self)
btn.clicked.connect(lambda: self.sideWindowTest.setupWindow())
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Main')
self.show()
class sideWindowTest(object):
myWdg = None
def __init__(self, parent):
super().__init__()
self.viewer = parent
def initiateMenuBar(self):
self.myWdg.setWindowTitle('Phasing')
self.myWdg.setWindowModality(Qt.ApplicationModal)
MenuBar = QVBoxLayout()
self.labels = {
'phase 0': QLabel('Phase 0', self.myWdg),
}
self.inputs = {
'phase 0': QDoubleSpinBox(self.myWdg),
}
for i in self.inputs.values():
i.installEventFilter(self.myWdg)
self.inputs['phase 0'].setValue(0)
MenuBar.addWidget(self.labels['phase 0'])
MenuBar.addWidget(self.inputs['phase 0'])
MenuBar.addStretch(1)
return MenuBar
def setupWindow(self):
if not self.myWdg:
self.myWdg = QWidget()
MenuBar = self.initiateMenuBar()
grid = QGridLayout()
grid.setSpacing(10)
grid.addLayout(MenuBar, 0, 0, 1, 2)
self.myWdg.setLayout(grid)
self.myWdg.setGeometry(0, 0, 400, 100)
self.myWdg.show()
self.myWdg.activateWindow()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Yes, but you have to explicitly wrap your QWidget's in a QDialog or a QMainWindow. By default, if you create a orphan QWidget (as you are doing), Qt will wrap it in a QDialog.
Just change your class type to QDialog. Also, you should probably pass in the initial main window as the parent.
btn.clicked.connect(lambda: self.sideWindowTest.setupWindow(self))
...
def setupWindow(self, parent=None):
if not self.myWdg:
self.myWdg = QDialog(parent)
Related
I'm creating a PyQt5 application in which I want to put some of QLineEdit widgets inside a QGroupBox.
When I run my application, GroupBox is visible and LineEdit is not.
In CreateLinesEdit() I commented a line which sets the line edit visible, but it opens lineEdit in a new window.
from PyQt5.QtWidgets import QGroupBox, QApplication, QLineEdit, QVBoxLayout, QWidget, QHBoxLayout
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.InitWindow()
def InitWindow(self):
self.BoxLayout()
self.AddBox()
self.CreateLinesEdit()
self.show()
def BoxLayout(self):
self.groupBoxScreen = QGroupBox()
self.vbox = QVBoxLayout()
self.vbox_screenGame = QVBoxLayout()
self.hbox_lineEdit = QHBoxLayout()
def AddBox(self):
self.vbox_screenGame.addItem(self.hbox_lineEdit)
self.groupBoxScreen.setLayout(self.vbox_screenGame)
self.vbox.addWidget(self.groupBoxScreen)
self.setLayout(self.vbox)
def CreateLinesEdit(self):
self.lines_edit = []
for i in range(0, 4):
LineEdit = QLineEdit()
# LineEdit.setVisible(True)
self.lines_edit.append(LineEdit)
self.hbox_lineEdit.addWidget(self.lines_edit[i])
if __name__ == "__main__":
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
I have created a custom class inherited from QCombobox() as part of my main application which works fine. I now want to add the same widget to another window upon clicking the ChangeAttributesButton() (QPushButton()). Relevant snippets of code below.
import sys, os, glob
from PyQt5.QtWidgets import (QApplication, QLabel, QMdiSubWindow,
QMainWindow, QAction, QPushButton, QFileDialog, QComboBox)
from PyQt5.QtCore import Qt, pyqtSignal, QObject
from qgis.core import *
from qgis.gui import *
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
# Save reference to the QGIS interface
self.iface = iface
self.canvas = self.iface.mapCanvas()
self.project = QgsProject.instance()
self.setWindowTitle('Marker')
#add toolbar
self.toolbar = QToolBar("Grade Marker Toolbar")
self.toolbar.setIconSize(QSize(20,20))
self.addToolBar(self.toolbar)
self.SelectCategoryComboBox = SelectCategoryComboBox()
self.toolbar.addWidget(self.SelectCategoryComboBox)
self.toolbar.addSeparator()
self.ChangeAttributesButton = ChangeAttributesButton(self.canvas)
self.ChangeAttributesButton.setIcon(QIcon(r'abc.png'))
self.ChangeAttributesButton.setIconText('Change Grade')
self.toolbar.addWidget(self.ChangeAttributesButton)
self.toolbar.addSeparator()
self.ChangeAttributesButton.clicked.connect(self.ChangeAttributesButton.on_click)
class SelectCategoryComboBox(QComboBox):
def __init__(self):
super(SelectCategoryComboBox, self).__init__()
self.setEnabled(True)
categories = ['Select Category',"A+","A", "B", "C", "F" ]
for cat in categories:
self.addItem(cat)
The bit that is causing a problem is here:
class ChangeAttributesButton(QPushButton):
def __init__(self,canvas):
super(ChangeAttributesButton, self).__init__()
self.setEnabled(True)
self.canvas = canvas
def on_click(self,state):
window = ChangeAttributesWindow()
class ChangeAttributesWindow(QWidget):
def __init__(self):
super(ChangeAttributesWindow, self).__init__()
self.title = 'Change Feature Attributes'
category_box = SelectCategoryComboBox()
self.addWidget(category_box)
self.initUI()
def initUI(self):
self.show()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
I am getting
AttributeError: 'ChangeAttributesWindow' object has no attribute 'addWidget'`. I have tried changing the parent class of `ChangeAttributesWindow()
to
QMdiSubWindow()
and QMainWindow() but I still get an error in the same vein.
How do I add an instance of SelectCategoryComboBox() to a child or pop up window?
This is the complete error:
A QWidget does not have any method called addWidget() so this error is thrown.
If you want to add a widget to a window, you can add it:
Setting the parent of the widget to the window.
class ChangeAttributesWindow(QWidget):
def __init__(self):
super(ChangeAttributesWindow, self).__init__()
self.title = 'Change Feature Attributes'
category_box = SelectCategoryComboBox()
category_box.setParent(self)
category_box.move(30, 30)
self.initUI()
Use a layout established in the window.
class ChangeAttributesWindow(QWidget):
def __init__(self):
super(ChangeAttributesWindow, self).__init__()
self.title = 'Change Feature Attributes'
category_box = SelectCategoryComboBox()
lay = QVBoxLayout(self)
lay.addWidget(category_box)
self.initUI()
I have a PyQt5 GUI application and I am trying to create a pop-out window. I have created a class sideWindow which is called by a button on the main window. The code works fine the first time the sideWindow is called from the main GUI window. However, if I close the sideWindow and reopen I get the error and the format of the window is messed up:
QWidget::setLayout: Attempting to set QLayout "" on sideWindow "", which already has a layout
I assume from this that because the layout has already been set on the first time of opening, it doesn't need to be set again. However, I'm not sure which bit of the sideWindow class I need to change to avoid this error. I've tried setting clearLayout in the sideWindow class but that didn't help.
class sideWindow(QWidget):
def __init__(self, parent):
super().__init__()
self.viewer = parent
def initiateMenuBar(self):
self.setWindowTitle('Phasing')
self.setWindowModality(Qt.ApplicationModal)
MenuBar = QVBoxLayout()
self.labels = {
'phase 0': QLabel('Phase 0', self),
}
self.inputs = {
'phase 0 sl': QSlider(Qt.Horizontal),
'phase 0': QDoubleSpinBox(self),
}
for i in self.inputs.values():
i.installEventFilter(self)
class dividor(QFrame):
def __init__(self, parent):
QFrame.__init__(self, parent)
self.setFrameShape(QFrame.HLine)
self.setLineWidth(3)
self.setFrameShadow(QFrame.Raised)
self.show()
self.inputs['phase 0'].valueChanged.connect(self.inputs['phase 0 sl'].setValue)
self.inputs['phase 0 sl'].valueChanged.connect(self.inputs['phase 0'].setValue)
MenuBar.addWidget(self.labels['phase 0'])
MenuBar.addWidget(self.inputs['phase 0'])
MenuBar.addWidget(self.inputs['phase 0 sl'])
MenuBar.addWidget(dividor(self))
MenuBar.addStretch(1)
self.inputs['phase 0'].setValue(0)
return MenuBar
def setupWindow(self):
MenuBar = self.initiateMenuBar()
grid = QGridLayout()
grid.setSpacing(10)
grid.addLayout(MenuBar, 0, 0, 1, 2)
self.setLayout(grid)
self.setGeometry(0, 0, 400, 100)
self.show()
The key, as suggested by #Sergey is that setupWindow is recreating the window each time it is called. As shown below, I have included an if statement in setupWindow. If the pop-out window has already been defined (i.e. by previously loading) then the widget is identified and only reopened, not recreated. The modified function setupWindow is:
def setupWindow(self):
if not self.myWdg:
self.myWdg = QWidget()
MenuBar = self.initiateMenuBar()
grid = QGridLayout()
grid.setSpacing(10)
grid.addLayout(MenuBar, 0, 0, 1, 2)
self.myWdg.setLayout(grid)
self.myWdg.setGeometry(0, 0, 400, 100)
self.myWdg.show()
self.myWdg.activateWindow()
A full MWE for this question is shown below. This creates a small GUI window ('Main') with a button which opens a second window 'Phasing' containing a QSpinBox.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QGridLayout, QDoubleSpinBox,\
QLabel
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
button = QPushButton('Button', self)
button.move(50, 50)
self.sideWindowTest = sideWindowTest(self)
button.clicked.connect(lambda: self.sideWindowTest.setupWindow())
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Main')
self.show()
class sideWindowTest(object):
myWdg = None
def __init__(self, parent):
super().__init__()
self.viewer = parent
def initiateMenuBar(self):
self.myWdg.setWindowTitle('Phasing')
self.myWdg.setWindowModality(Qt.ApplicationModal)
MenuBar = QVBoxLayout()
self.labels = {
'phase 0': QLabel('Phase 0', self.myWdg),
}
self.inputs = {
'phase 0': QDoubleSpinBox(self.myWdg),
}
for i in self.inputs.values():
i.installEventFilter(self.myWdg)
self.inputs['phase 0'].setValue(0)
MenuBar.addWidget(self.labels['phase 0'])
MenuBar.addWidget(self.inputs['phase 0'])
MenuBar.addStretch(1)
return MenuBar
def setupWindow(self):
if not self.myWdg:
self.myWdg = QWidget()
MenuBar = self.initiateMenuBar()
grid = QGridLayout()
grid.setSpacing(10)
grid.addLayout(MenuBar, 0, 0, 1, 2)
self.myWdg.setLayout(grid)
self.myWdg.setGeometry(0, 0, 400, 100)
self.myWdg.show()
self.myWdg.activateWindow()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
For past few days I was trying to solve the issue of widget movement. At some point I tried rewriting QComboBox classes with mouse signals but that did not work. As a work around I settled for parenting my widget to a QGraphicsWidget but once I try to add another item it does not display any more and I'm not sure what to do. Here is full test script:
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QApplication,QGraphicsItem, QGraphicsView, QGraphicsScene, QDesktopWidget, QCheckBox, QGroupBox, QPushButton, QGridLayout, QLabel, QLineEdit, QComboBox, QFont, QRadioButton, QButtonGroup, QWidget, QShortcut, QKeySequence, QIcon, QListView, QStandardItemModel, QStandardItem, QAction, QIntValidator, QListWidget, QProgressBar, QSpacerItem
from PyQt4.QtCore import QRect
from functools import partial
import sys
class node_GUI(QtGui.QWidget):
def __init__(self):
super(node_GUI, self).__init__()
class Main(QtGui.QMainWindow):
def __init__(self, *args):
super(Main, self).__init__(*args)#QtGui.QMainWindow.__init__(self)
self.init_defaults()
def init_defaults(self):
self.setGeometry(800,800,500,200)
self.lay_main = QGridLayout()
self.centralwidget = QtGui.QWidget()
self.centralwidget.setLayout(self.lay_main)
self.setCentralWidget(self.centralwidget)
btn_create_node = QPushButton("Create Node View")
btn_create_node.clicked.connect(self.create_node_view)
self.lay_main.addWidget(btn_create_node)
def showWindow(self,window):
window.show()
def printTest(self):
print "Start"
box = QGroupBox("subWidget")
box_btn = QPushButton("Test")
box_btn.clicked.connect(self.printTest)
le_edit = QLineEdit()
lay = QGridLayout()
box.setLayout(lay)
lay.addWidget(box_btn)
lay.addWidget(le_edit)
area = QtGui.QGraphicsWidget()
area.setMinimumSize(QtCore.QSizeF(400,300))
area.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
area.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
proxy = self.scene.addWidget(box)
proxy.setParentItem(area)
print "END"
def create_node_view(self):
print "creting node view"
window = node_GUI()
window.setGeometry(QRect(100, 100, 400, 200))
window.setWindowTitle("node ")
window.setObjectName("node")
show_window = QPushButton("Show Node Editor")
show_window.setObjectName("btn")
show_window.clicked.connect(partial(self.showWindow,window))
self.lay_main.addWidget(show_window)
box = QGroupBox("Widgets")
box_btn = QPushButton("Test")
box_btn.clicked.connect(self.printTest)
le_edit = QLineEdit()
lay = QGridLayout()
box.setLayout(lay)
lay.addWidget(box_btn)
lay.addWidget(le_edit)
area = QtGui.QGraphicsWidget()
area.setMinimumSize(QtCore.QSizeF(300,300))
area.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
area.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
area.setAutoFillBackground(True)
ecs = QtGui.QGraphicsEllipseItem()
ecs.setRect(QtCore.QRectF(79,79,79,79))
ecs.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
ecs.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
view = QGraphicsView()
self.scene = QGraphicsScene()
self.scene.addItem(area)
proxy = self.scene.addWidget(box)
proxy.setParentItem(area)
self.scene.addItem(ecs)
view.setScene(self.scene)
lay_window = QGridLayout()
window.setLayout(lay_window)
lay_window.addWidget(view)
def main():
app = QtGui.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
When you click on Create Node View > Show Node Editor > Test button > a new GroupBox should appear but that does not work. Not sure why.
Right so I stopped using QGraphicsWidget() and instead I just use QGraphicsRectItem(ecs for example) once I did that change everything started to work as expected.
In the following example, when you click the button, the entire form is rebuilt adding a new label each time. At the end is a resize call that doesn't appear to work. While debugging, I validated the sizeHint() is returning the correct dimensions, and internally the widget thinks it is the correct size, but what is drawn is not correct. What can I do to force the MDI window to resize correctly? Also of note, when not sized correctly, if you manually start resizing, it suddenly snaps to the appropriate size.
import sys
import os
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import PyQt4.Qt
class MdiWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.count = 0
self.buildWindow()
def buildWindow(self):
main = QVBoxLayout()
button = QPushButton('Change Count')
button.clicked.connect(self.changeCount)
main.addWidget(button)
for i in range(self.count):
main.addWidget(QLabel(str(i)))
widget = QWidget()
widget.setLayout(main)
self.setCentralWidget(widget)
self.resize(main.sizeHint())
def changeCount(self, event):
self.count += 1
self.buildWindow()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Resize Test')
self.mdiArea = QMdiArea()
self.setCentralWidget(self.mdiArea)
child = MdiWindow()
self.mdiArea.addSubWindow(child)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())