How to populate several QComboBox from a QFileSystemModel? - python

How does one use a QFileSystemModel to populate several QComboBox with subdirectories?
I have built a project management tool that allows me to create and manage my projects. I am currently using a combination of os.listdir and json to populate and validate my QComboboxes. But I am trying to learn a more modelview approach with QFileSystemModel.
So this is what I have:
class FileSystemModel(QW.QFileSystemModel):
def __init__(self, root, parent=None):
QW.QFileSystemModel.__init__(self, parent)
self.root = root
self.rootIndex = self.setRootPath(root)
class Window(QW.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__()
self.init()
def init(self):
layout = QW.QVBoxLayout()
self.cbox = QW.QComboBox()
self.cbox2 = QW.QComboBox()
self.model = FileSystemModel("C:\\projects\\")
self.cbox.setModel(self.model)
self.cbox2.setModel(self.model)
self.cbox.setRootModelIndex(self.model.rootIndex)
self.cbox.currentIndexChanged.connect(self._indexChanged)
layout.addWidget(self.cbox)
layout.addWidget(self.cbox2)
self.setLayout(layout)
def _indexChanged(self):
row = self.sender().currentIndex()
index = self.sender().rootModelIndex().child(row, 0)
self.cbox2.setRootModelIndex(index)
def main():
app = QW.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I was attempting to repopulate the cbox2 using the index from cbox, but with my code it doesn't seem to work - it just stays empty.

Okay here is modified version of what you had:
from sys import exit as sysExit
from PyQt5.QtCore import QDir, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QFileSystemModel, QHBoxLayout, QComboBox
class SysDirModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
class SysFileModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.Files)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
def ResetPath(self, DirPath):
self.setRootPath(DirPath)
self.RootIndex = self.index(DirPath)
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setGeometry(150, 150, 450, 100)
# If you use forward slash this works in Windows as well and it is cleaner
self.SysDirs = SysDirModel('C:/projects/')
self.SysFils = SysFileModel('C:/projects/')
# Setup first ComboBox
self.cbxDirs = QComboBox()
self.cbxDirs.setMinimumWidth(200)
self.cbxDirs.setModel(self.SysDirs)
self.cbxDirs.setRootModelIndex(self.SysDirs.RootIndex)
# This sends a Signal to a predefined Slot
self.cbxDirs.currentIndexChanged.connect(self.IndexChanged)
self.cbxFiles = QComboBox()
self.cbxFiles.setMinimumWidth(200)
self.cbxFiles.setModel(self.SysFils)
self.cbxFiles.setRootModelIndex(self.SysFils.RootIndex)
HBox = QHBoxLayout()
HBox.addWidget(self.cbxDirs)
HBox.addStretch(1)
HBox.addWidget(self.cbxFiles)
self.setLayout(HBox)
# This is the receiver of a Signal (aka Slot) so it ought to be used as such
#pyqtSlot(int)
def IndexChanged(self, RowIdx):
# Get your Current DirPath based on the Selected Value
index = self.cbxDirs.rootModelIndex().child(RowIdx, 0)
DirPath = self.cbxDirs.model().filePath(index)
# Reset what ComboBox 2's Model and what it is looking at
self.cbxFiles.clear()
self.SysFils.ResetPath(DirPath)
self.cbxFiles.setModel(self.SysFils)
if __name__ == '__main__':
MainThred = QApplication([])
MainGui = MainWindow()
MainGui.show()
sysExit(MainThred.exec_())

Related

How do I connect three or more windows in PyQt

I have a complex program in which I need to connect multiple windows. Unfortunately, I seem to be not fully understanding the concept/steps necessary to do this so bonus points if anyone can explain the steps/process well. In my current program, I have a list of items. Once I select them by moving them over to the right list widget, I need them to go to the third window. The third window should be activated by clicking the dots on the second window. The program runs and shows the second window appropriately but the signal/slot connection of the dots button does not work. However, the rest of the code is working because if I switch the toolkit to show the third window, that part is performing as expected. My code is below, and again, no errors are being returned, but clicking the dots button on the second window does nothing.
Also, a question - do I instantiate the third window within the second class, or only within the main window? Again, struggling to fully understand the process and I will need to do this multiple more times.
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QListWidget, QLineEdit, QTextEdit, QGridLayout, QHBoxLayout, QVBoxLayout, QSizePolicy, QFileDialog, QTabWidget, QCheckBox
import PyQt5.QtGui as qtg
import glob
import os
from PyQt5.QtCore import Qt, QSettings
import inspect
from PyQt5 import QtCore
import pandas as pd
import pathlib
import pyreadstat
import json
class ThirdWindow(QWidget):
def __init__(self):
super().__init__()
self.layout = QGridLayout()
self.setLayout(self.layout)
self.allVariables = QListWidget()
self.variablesSelected = QListWidget()
#self.allVariables.insertItem(0, 'Hello')
self.layout.addWidget(self.allVariables, 1,0)
self.layout.addWidget(self.variablesSelected, 1, 1)
def setItems(self, items):
self.allVariables.clear()
for item in items:
self.allVariables.addItem(item)
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
##not sure if I am supposed to instantiate this here or only in the main window class
self.thirdWindow = ThirdWindow()
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
def seconddWindowConnections(self):
self.byButton.clicked.connect(self.show_third_window)
#self.buttons['Toolkit'].clicked.connect(self.show_new_window)
def show_third_window(self):
self.thirdWindow.show()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = SecondWindow()
self.tw = ThirdWindow()
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
# Buttons
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
self.sw.show()
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
self.tw.setItems(items)
if __name__ == "__main__":
import sys
app = QApplication([])
mw = MainWindow()
mw.show()
app.exec()
The main issue with your code is that secondWindowConnections is never called so the button actually does nothing. I corrected that and fixed a few other issues I found in my example below. I left out the bits where I made no changes and all the changes I did make I made inline notes explaining them:
class SecondWindow(QWidget):
def __init__(self):
super().__init__()
self.thirdWindow = None # dont initialize until neccessary
self.thirdWindowItems = []
self.layout = QGridLayout(self)
self.by = QLabel("By")
self.byVariables = QLineEdit()
self.byButton = QPushButton("...")
self.layout.addWidget(self.by, 1, 0)
self.layout.addWidget(self.byVariables, 2, 0)
self.layout.addWidget(self.byButton, 2, 1)
self.secondWindowConnections() # Run this to setup the
# signal for the third window.
def secondWindowConnections(self): # this had a typo
self.byButton.clicked.connect(self.show_third_window)
def show_third_window(self):
if self.thirdWindow is None: # if window has been created yet
self.thirdWindow = ThirdWindow() # create window
if not self.thirdWindow.isVisible(): # if window is showing
self.thirdWindow.show() # show window
self.thirdWindow.setItems(self.thirdWindowItems) # send items to window
def send_items(self, items): # this is to collect the variable that
self.thirdWindowItems = items # move to the third window
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# Add a title
self.setWindowTitle("GUI Querying Program")
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.initUI()
self.setButtonConnections()
self.sw = None # dont initialize until neccessary.
def initUI(self):
subLayouts = {}
subLayouts['LeftColumn'] = QGridLayout()
self.layout.addLayout(subLayouts['LeftColumn'],1)
self.buttons = {}
self.buttons['addVariable'] = QPushButton('>')
self.buttons['removeVariable'] = QPushButton('<')
self.buttons['Toolkit'] = QPushButton('Toolkit')
self.variables = QListWidget()
self.selectedVariables = QListWidget()
subLayouts['LeftColumn'].addWidget(self.variables, 7,0,4,1)
subLayouts['LeftColumn'].addWidget(self.selectedVariables, 7,1,4,1)
subLayouts['LeftColumn'].addWidget(self.buttons['addVariable'], 10,0,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['removeVariable'], 10,1,1,1)
subLayouts['LeftColumn'].addWidget(self.buttons['Toolkit'], 11,1,1,1)
names = ['apple', 'banana', 'Cherry']
self.variables.insertItems(0, names)
def setButtonConnections(self):
self.buttons['addVariable'].clicked.connect(self.add_variable)
self.buttons['Toolkit'].clicked.connect(self.show_new_window)
# self.buttons['Toolkit'].clicked.connect(self.add_selected_variables)
# only use one connnect slot
def add_variable(self):
for item in self.variables.selectedItems():
self.selectedVariables.addItem(item.clone())
def show_new_window(self):
if self.sw is None: # check if window has been constructed
self.sw = SecondWindow() # construct window
if not self.sw.isVisible(): # If winow is not showing
self.sw.show() # show window
self.sw.send_items(self.add_selected_variables()) # send selected
# variables to second window
def add_selected_variables(self):
items = []
for i in range(self.selectedVariables.count()):
items.append(self.selectedVariables.item(i).clone())
# self.tw.setItems(items) ... self.tw doesnt exist so return them
return items

how to save data being edited when QtableView cell loses focus on close of form

In the default behavior of editing a cell in a QtableView, when the user clicks away either to another widget or closes the form, the edits are lost. After a lot of googling, I have found a way to save the edits if the user selects another widget in the form, but if the form is closed, the edits are still lost. The blog post is here.
I attempted to call the closeEditor method from the forms closeEvent, but it requires two parameters: the editor and hint. I can provide QAbstractItemDelegate.NoHint but the editor is expecting the QlineEdit object where the editing is taking place. I am lost on how to provide this for the cell currently being edited.
Here is a gif of the current behaviour:
My question is how do I provide the QlineEdit of the cell being edited?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
from phones import *
class Main(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.resize(490, 998)
self.layoutWidget = QWidget(self)
self.layoutWidget.setObjectName("layoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.new_phone = QtWidgets.QPushButton(self.layoutWidget)
self.new_phone.setObjectName("new_phone")
self.new_phone.setText("New Phone")
self.horizontalLayout_7.addWidget(self.new_phone)
self.delete_phone = QtWidgets.QPushButton(self.layoutWidget)
self.delete_phone.setObjectName("delete_phone")
self.delete_phone.setText("Delete phone")
self.horizontalLayout_7.addWidget(self.delete_phone)
self.verticalLayout.addLayout(self.horizontalLayout_7)
self.phone_view = Syn_tableview()
self.verticalLayout.addWidget(self.phone_view)
self.cont_id = '9'
self.setCentralWidget(self.layoutWidget)
self.new_phone.clicked.connect(self.add_phone)
self.populate_phones()
def populate_phones(self):
self.phone_model = QSqlTableModel(self)
self.phone_model.setTable("contact_phones")
self.phone_model.setFilter("contact_id='{0}'".format(self.cont_id))
self.phone_model.select()
self.phone_view.setModel(self.phone_model)
self.phone_view.resizeColumnsToContents()
def add_phone(self):
self.phone_model.submitAll()
self.phone_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
row = self.phone_model.rowCount()
record = self.phone_model.record()
record.setGenerated('id', False) #primary key
record.setValue('contact_id', self.cont_id) #foreign key
self.phone_model.insertRecord(row, record)
phone_index_edit = self.phone_model.index(row, self.phone_model.fieldIndex('phone_number'))
self.phone_view.edit(phone_index_edit)
def closeEvent(self, event):
submit = self.phone_model.submitAll()
#This is the problem
self.phone_view.closeEditor("QLineEdit", QAbstractItemDelegate.NoHint)
class Syn_tableview(QTableView):
def __init__(self, *args, **kwargs):
QTableView.__init__(self, *args, **kwargs)
def closeEditor(self, editor, hint):
if hint == QAbstractItemDelegate.NoHint:
QTableView.closeEditor(self, editor,
QAbstractItemDelegate.SubmitModelCache)
if __name__=="__main__":
app=QApplication(sys.argv)
db = QSqlDatabase.addDatabase("QPSQL");
db.setHostName(server)
db.setDatabaseName(database)
db.setUserName(user)
db.setPassword(pword)
myapp = Main()
myapp.show()
sys.exit(app.exec_())
The delegate editors are children of the QTableView so you can use findChildren to get them, to make sure they are not other children you can set an objectName that allows you to filter them:
import sys
from PyQt5 import QtCore, QtSql, QtWidgets
def create_connection():
db = QtSql.QSqlDatabase.addDatabase("QPSQL")
# FIXME
db.setHostName("server")
db.setDatabaseName("database")
db.setUserName("user")
db.setPassword("pword")
if not db.open():
print(db.lastError().text())
return False
return True
class Syn_Delegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super(Syn_Delegate, self).createEditor(parent, option, index)
if isinstance(editor, QtWidgets.QWidget):
editor.setObjectName("syn_editor")
return editor
class Syn_Tableview(QtWidgets.QTableView):
def closeEditor(self, editor, hint):
if hint == QtWidgets.QAbstractItemDelegate.NoHint:
hint = QtWidgets.QAbstractItemDelegate.SubmitModelCache
super(Syn_Tableview, self).closeEditor(editor, hint)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.new_phone = QtWidgets.QPushButton(self.tr("New Phone"))
self.delete_phone = QtWidgets.QPushButton(self.tr("Delete phone"))
self.phone_view = Syn_Tableview()
self.phone_model = QtSql.QSqlTableModel()
self.phone_model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.phone_view.setModel(self.phone_model)
self.phone_view.resizeColumnsToContents()
delegate = Syn_Delegate(self)
self.phone_view.setItemDelegate(delegate)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.new_phone, 0, 0)
lay.addWidget(self.delete_phone, 0, 1)
lay.addWidget(self.phone_view, 1, 0, 1, 2)
self._contact_id = "9"
self.populate_phones()
self.new_phone.clicked.connect(self.add_phone)
#property
def contact_id(self):
return self._contact_id
def populate_phones(self):
self.phone_model.setTable("contact_phones")
self.phone_model.setFilter("contact_id='{0}'".format(self.contact_id))
self.phone_model.select()
#QtCore.pyqtSlot()
def add_phone(self):
self.phone_model.submitAll()
row = self.phone_model.rowCount()
record = self.phone_model.record()
record.setGenerated("id", False) # primary key
record.setValue("contact_id", self.contact_id) # foreign key
self.phone_model.insertRecord(row, record)
phone_index_edit = self.phone_model.index(
row, self.phone_model.fieldIndex("phone_number")
)
if phone_index_edit.isValid():
self.phone_view.edit(phone_index_edit)
def closeEvent(self, event):
for editor in self.phone_view.findChildren(QtWidgets.QWidget, "syn_editor"):
self.phone_view.commitData(editor)
submit = self.phone_model.submitAll()
super().closeEvent(event)
def main():
app = QtWidgets.QApplication(sys.argv)
if not create_connection():
sys.exit(-1)
w = MainWindow()
w.show()
w.resize(640, 480)
ret = sys.exit(app.exec_())
sys.exit(ret)
if __name__ == "__main__":
main()

Pyqt5 QMDIarea subclass with a custom signal on close

I need to create a custom signal on Qmdisubwindow close. In other word, when I closed any subwindow, a signal is emitted with the name of that window being closed. Below is my trail, but seems not right. Error occurs as:
a subwindow already created without calling
add subwindow option is not working
closable action is not working
Hope you can show me how to fix it.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MyMdi(QMdiSubWindow):
sigClosed = pyqtSignal(str)
def __init__(self, parent=None):
super(MyMdi, self).__init__(parent)
def closeEvent(self, event):
"""Get the name of active window about to close
"""
name = name
self.sigClosed.emit('{} is close'.format(name))
QMdiSubWindow.closeEvent(self, event)
class MainWindow(QMainWindow):
count = 0
def __init__(self, parent = None):
super(MainWindow, self).__init__(parent)
self.mdi = MyMdi()
self.setCentralWidget(self.mdi)
bar = self.menuBar()
file = bar.addMenu("File")
file.addAction("New")
file.triggered[QAction].connect(self.windowaction)
self.setWindowTitle("MDI demo")
# my signal
self.mdi.sigClosed.connect(self.windowclosed)
#pyqtSlot(str)
def windowclosed(self, text):
print(text)
def windowaction(self, q):
if q.text() == "New":
MainWindow.count = MainWindow.count+1
sub = QMdiSubWindow()
sub.setWidget(QTextEdit())
sub.setWindowTitle("subwindow"+str(MainWindow.count))
self.mdi.addSubWindow(sub)
sub.show()
def main():
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You have an initial error: a QMdiSubWindow must be inside a QMdiArea but there is none in your code.
On the other hand, the idea of subclassing is good but you have several drawbacks:
You are not using it initially since there is no QMdiArea, if you execute the QAction then your application will be closed because a QMdiSubWindow does not have any method called addSubWindow.
The QMdiSubWindow does not have an attribute called name, you must use windowTitle.
class MdiSubWindow(QMdiSubWindow):
sigClosed = pyqtSignal(str)
def closeEvent(self, event):
"""Get the name of active window about to close
"""
self.sigClosed.emit(self.windowTitle())
QMdiSubWindow.closeEvent(self, event)
class MainWindow(QMainWindow):
count = 0
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.mdi = QMdiArea()
self.setCentralWidget(self.mdi)
bar = self.menuBar()
file = bar.addMenu("File")
file.addAction("New")
file.triggered[QAction].connect(self.windowaction)
self.setWindowTitle("MDI demo")
#pyqtSlot(str)
def windowclosed(self, text):
print(text)
def windowaction(self, q):
if q.text() == "New":
MainWindow.count = MainWindow.count + 1
sub = MdiSubWindow()
sub.setWidget(QTextEdit())
sub.setAttribute(Qt.WA_DeleteOnClose)
sub.setWindowTitle("subwindow" + str(MainWindow.count))
sub.sigClosed.connect(self.windowclosed)
self.mdi.addSubWindow(sub)
sub.show()

QMenu does not execute methods properly

I am working with a custom QMenu which executes some methods. The menu has three options: a delete row option, a toggle variable option and a debug option, which prints the value of the toggleing variable. The code is not properly executed. Sometimes the debug button doesnt work and it suddely gets executed many times. The toggle option needs to be clicked twice to work, i dont know why. This is my MRE:
# -*- coding: utf-8 -*-
from PyQt5.QtCore import Qt, QRect, pyqtSlot
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QMainWindow, QLabel, QMenu, \
QApplication, QVBoxLayout, QListWidgetItem, QListWidget, QAction
class Punto(QWidget):
def __init__(self, parent, internal_id, name):
QWidget.__init__(self)
# Toggle variable
self.render = True
self.customContextMenuRequested.connect(self.context_menu)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.menu = QMenu()
self.borrar = QAction("Delete")
self.ver = QAction("Toggle")
self.debug = QAction("Debug")
self.ver.setCheckable(True)
self.ver.setChecked(True)
self.parent = parent
self.id = internal_id
label = QLabel(name)
hbox = QHBoxLayout()
hbox.addWidget(label)
hbox.addStretch(1)
self.setLayout(hbox)
def context_menu(self):
self.menu.addAction(self.borrar)
self.borrar.triggered.connect(self.delete)
self.menu.addAction(self.ver)
self.ver.triggered.connect(self.change)
self.menu.addAction(self.debug)
self.debug.triggered.connect(self.debugg)
self.menu.exec(QCursor.pos())
#pyqtSlot()
def debugg(self):
print(f"Render: {self.render}")
#pyqtSlot()
def change(self):
if self.ver.isChecked():
self.ver.setChecked(False)
self.render = False
else:
self.ver.setChecked(True)
self.render = True
#property
def itemid(self):
return self.id
#pyqtSlot()
def delete(self):
self.parent.delete_point(self.id)
class Ventana(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setFixedSize(200, 200)
widget_central = QWidget(self)
boton_punto = QPushButton(widget_central)
boton_punto.setGeometry(QRect(0, 0, 200, 20))
boton_punto.clicked.connect(self.crear_punto)
boton_punto.setText("Create")
widget_punto = QWidget(widget_central)
widget_punto.setGeometry(QRect(0, 20, 200, 200))
vertical_punto = QVBoxLayout(widget_punto)
vertical_punto.setContentsMargins(0, 0, 0, 0)
self.lista_puntos = QListWidget(widget_punto)
vertical_punto.addWidget(self.lista_puntos)
self.id_punto = 0
self.setCentralWidget(widget_central)
def crear_punto(self):
# Add placeholder item to List
item = QListWidgetItem()
self.lista_puntos.addItem(item)
# Create Custom Widget
punto = Punto(self, self.id_punto, "A")
self.id_punto += 1
item.setSizeHint(punto.minimumSizeHint())
# Set the punto widget to be displayed within the placeholder item
self.lista_puntos.setItemWidget(item, punto)
def delete_point(self, idd):
for indx in range(self.lista_puntos.count()):
item = self.lista_puntos.item(indx)
widget = self.lista_puntos.itemWidget(item)
if widget.id == idd:
self.lista_puntos.takeItem(self.lista_puntos.row(item))
break
if __name__ == "__main__":
MainEvent = QApplication([])
main_app = Ventana()
main_app.show()
MainEvent.exec()
You have 2 errors:
By default a QAction already makes the change of state so it is not necessary that you implement it, but you are doing it, that is, by default the QAction changes from on to off (or vice versa) but you by code change it from off a on (or vice versa) that when done in ms the change is not observed. So instead of connecting the triggered signal, use the toggled signal and just change the render.
When you connect a signal to the same slot "n" times the slot is invoked "n" times, and in your case you are connecting it every time the context_menu method is invoked, there are at least 2 solutions: make the connection only once or use the type of connection Qt::UniqueConnection, in my solution I will use the first one.
Considering the above, the solution is:
class Punto(QWidget):
def __init__(self, parent, internal_id, name):
QWidget.__init__(self)
# Toggle variable
self.render = True
self.customContextMenuRequested.connect(self.context_menu)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.menu = QMenu()
self.borrar = QAction("Delete")
self.ver = QAction("Toggle")
self.debug = QAction("Debug")
self.ver.setCheckable(True)
self.ver.setChecked(True)
self.parent = parent
self.id = internal_id
label = QLabel(name)
hbox = QHBoxLayout(self)
hbox.addWidget(label)
hbox.addStretch(1)
self.borrar.triggered.connect(self.delete)
self.ver.toggled.connect(self.change)
self.debug.triggered.connect(self.debugg)
self.menu.addAction(self.borrar)
self.menu.addAction(self.ver)
self.menu.addAction(self.debug)
def context_menu(self):
self.menu.exec(QCursor.pos())
#pyqtSlot()
def debugg(self):
print(f"Render: {self.render}")
#pyqtSlot(bool)
def change(self, state):
self.render = self.ver.isChecked()
#property
def itemid(self):
return self.id
#pyqtSlot()
def delete(self):
self.parent.delete_point(self.id)

Call setCentralWidget from other file

I have different files (main.py and layout.py) and I want to change the windows (I have shortened the example that it should change the window size) of QMainWindow from the file layout.py.
It works fine from main.py, I can change the windows from the file main.py, but it has no effect in layout.py.
UPDATE: I changed the files two a working example with the problem that the Button "Switch to Layout 2" does not work.
main.py
import sys
from PyQt5.QtWidgets import QAction, QApplication, QMainWindow
from layout import Layout1, Layout2
class MainClass(QMainWindow):
def __init__(self):
super(MainClass, self).__init__()
def initUI(self):
self.setGeometry(50, 100, 600, 500)
self.setWindowTitle('program')
self.window1Action = QAction('Window1', self)
self.window1Action.triggered.connect(self.window1)
self.window2Action = QAction('Window2', self)
self.window2Action.triggered.connect(self.window2)
self.menubar = self.menuBar()
menu = self.menubar.addMenu('&Menu')
menu.addAction(self.window1Action)
menu.addAction(self.window2Action)
self.show()
def window1(self):
wsize1 = (1200, 600)
self.resize(*wsize1)
self.form_widget = Layout1()
self.setCentralWidget(self.form_widget)
def window2(self):
wsize2 = (600, 500)
self.resize(*wsize2)
self.form_widget = Layout2()
self.setCentralWidget(self.form_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainClass()
ex.initUI()
sys.exit(app.exec_())
layout.py
from PyQt5.QtWidgets import QFormLayout, QPushButton, QWidget
class Layout1(QWidget):
def __init__(self, parent=None):
super(Layout1, self).__init__(parent)
self.form_layout = QFormLayout(self)
self.button1 = QPushButton('Button1')
self.form_layout.addRow('nonfunctional', self.button1)
self.buttonX = QPushButton('Switch to Layout2')
self.form_layout.addRow('Problem', self.buttonX)
self.buttonX.clicked.connect(self.change_layout)
self.setLayout(self.form_layout)
def change_layout(self):
from main import MainClass
self.change_window = MainClass()
self.change_window.window2()
class Layout2(QWidget):
def __init__(self, parent=None):
super(Layout2, self).__init__(parent)
self.form_layout = QFormLayout(self)
self.button2 = QPushButton('Button3')
self.form_layout.addRow('nonfunctional', self.button2)
self.setLayout(self.form_layout)
Can anybody explain to me what I have done wrong?
Your Layout classes have a parent parameter, so use it. You need to get a reference to the existing instance of the main window, not create a new one:
def window1(self):
...
self.form_widget = Layout1(self)
self.setCentralWidget(self.form_widget)
...
def change_layout(self):
self.parent().window2()

Categories

Resources