No se puede crear una nueva ventana en PyQt sin que tenga un padre

Comencé a codificar un editor de texto simple en Python con PyQt y me encontré con este problema: para el botón “Nuevo documento” quiero abrir un nuevo editor de texto vacío que permanece abierto sin importar lo que haya ocurrido en la primera ventana. El problema es que la única forma en que puedo mostrar la ventana es si me envío a mí mismo como un parámetro (por lo que es el padre), lo que lleva al cierre de la segunda ventana cuando se cierra el padre.

Aquí está mi constructor:

class Main(QtGui.QMainWindow): def __init__(self, ctrl, parent=None): QtGui.QMainWindow.__init__(self, parent) 

Y aquí está el método que abre una nueva ventana:

 def new(self): repo = Repository() ctrl = Controller(repo) new_win = Main(ctrl) new_win.show() 

Nota: cuando el código que está aquí no funciona, simplemente no muestra la segunda ventana

Edición : decidió que debería publicar todo mi código, así que aquí va:

 import sys from PyQt4 import QtGui from src.controller import Controller from src.repository import Repository class Main(QtGui.QMainWindow): nr_of_instances = -1 def __init__(self, ctrl, parent=None): QtGui.QMainWindow.__init__(self, parent) Main.nr_of_instances += 1 #ui elements self._toolbar = None self._menuBar = None self._file = None self._edit = None self._view = None self._formatBar = None self._statusBar = None self._text = None #actions self._newAction = None self._openAction = None self._saveAction = None self._saveAsAction = None # self._controller = ctrl self.init_ui() def init_ui(self): self._text = QtGui.QTextEdit(self) self.setCentralWidget(self._text) self.init_toolbar() self.init_formatBar() self.init_menuBar() self._statusBar = self.statusBar() self.setGeometry(50+(50*Main.nr_of_instances), 100+(50*Main.nr_of_instances), 800, 400) self.setWindowTitle("KekWriter") @staticmethod def new(self): repo = Repository() ctrl = Controller(repo) spawn = Main(ctrl) spawn.show() def set_title(self): if self._controller.get_file_name() != 0: new_title = self.windowTitle().split(" - ") new_title[0].strip() self.setWindowTitle(new_title[0]+" - "+self._controller.get_file_name()) def open(self): fn = QtGui.QFileDialog.getOpenFileName(self, 'Open File', ".") self._controller.set_file_name(fn) try: if fn != '': self._text.setText(self._controller.open()) self.set_title() except UnicodeDecodeError as msg: QtGui.QMessageBox.information(self, "Eroare!", "Tip de fisier invalid!") def _save_as(self): fn = QtGui.QFileDialog.getSaveFileName(self, 'Save File As', ".") print(fn) if fn != '': self._controller.set_file_name(fn) self._controller.save(self._text.toPlainText()) self.set_title() def save(self): fn = self._controller.get_file_name() if fn == '': self._save_as() else: self._controller.save(self._text.toPlainText()) self.set_title() def init_toolbar(self): self._newAction = QtGui.QAction(QtGui.QIcon("icons/new.png"), "New", self) self._newAction.setStatusTip("Creates a new document") self._newAction.setShortcut("Ctrl+N") self._newAction.triggered.connect(self.new) self._openAction = QtGui.QAction(QtGui.QIcon("icons/open.png"), "Open", self) self._openAction.setStatusTip("Opens existing document") self._openAction.setShortcut("Ctrl+O") self._openAction.triggered.connect(self.open) self._saveAction = QtGui.QAction(QtGui.QIcon("icons/save.png"), "Save", self) self._saveAction.setStatusTip("Saves current document") self._saveAction.setShortcut("Ctrl+S") self._saveAction.triggered.connect(self.save) self._saveAsAction = QtGui.QAction(QtGui.QIcon("icons/save_as.png"), "Save as", self) self._saveAsAction.setStatusTip("Saves current document with another name") self._saveAsAction.setShortcut("Ctrl+Shift+S") self._saveAsAction.triggered.connect(self._save_as) self._toolbar = self.addToolBar("Options") self._toolbar.addAction(self._newAction) self._toolbar.addAction(self._openAction) self._toolbar.addAction(self._saveAction) self._toolbar.addAction(self._saveAsAction) self.addToolBarBreak() def init_menuBar(self): self._menuBar = self.menuBar() self._file = self._menuBar.addMenu("File") self._edit = self._menuBar.addMenu("Edit") self._view = self._menuBar.addMenu("View") #file self._file.addAction(self._newAction) self._file.addAction(self._openAction) self._file.addAction(self._saveAction) self._file.addAction(self._saveAsAction) def init_formatBar(self): self._formatBar = self.addToolBar("Format") self.addToolBarBreak() def main(): app = QtGui.QApplication(sys.argv) repo = Repository() ctrl = Controller(repo) main = Main(ctrl) main.show() sys.exit(app.exec_()) if __name__ == "__main__": main() 

Related of "No se puede crear una nueva ventana en PyQt sin que tenga un padre"

La razón por la que su segunda ventana no se muestra es porque el objeto de la ventana queda fuera del scope y se recolecta la basura. Debe almacenar una referencia a la segunda ventana en algún lugar que no se eliminará hasta que se cierre la ventana.

Hay algunas formas de hacer esto en las que puedo pensar, pero realmente depende de usted cómo desea estructurar su progtwig.

  1. Tener un atributo de clase de Main que es una lista. Por lo tanto, esta lista será común a todas las instancias y puede agregar una nueva instancia a esa lista cuando se cree. Mientras exista una instancia, la ventana no debería ser recogida de basura (creo)

  2. No QMainWindow una instancia de QMainWindow inicialmente, sino que QMainWindow una clase que contenga referencias a las ventanas. Cuando se crea una nueva ventana, duele la referencia a la nueva ventana en este objeto.

Esperemos que esto le dé una idea de lo que está mal para que pueda resolverlo de una manera que se adapte al diseño de su progtwig.


Actualizar

Para una guía aproximada de cómo haría la opción 2:

 class WindowContainer(object): def __init__(self): self.window_list = [] self.add_new_window() def add_new_window(self): repo = Repository() ctrl = Controller(repo) spawn = Main(ctrl, self) self.window_list.append(spawn) spawn.show() class Main(QtGui.QMainWindow): def __init__(self, ctrl, window_container, parent=None): QtGui.QMainWindow.__init__(self, parent) ... self.window_container = window_container ... ... def init_toolbar(self): ... self._newAction.triggered.connect(self.window_container.add_new_window) ... ... def main(): app = QtGui.QApplication(sys.argv) # this variable will never go out of scope window_container = WindowContainer() sys.exit(app.exec_()) 

La mejor manera en la que estoy pensando ahora para resolver este problema es crear una clase heredada de QApplication. Cree una lista dentro de esta clase y almacene las referencias a todas las ventanas recién creadas dentro de esta lista.

Un mínimo ejemplo de trabajo:

 from PyQt4.QtGui import * import sys class Application(QApplication): def __init__(self, argv=None): super(Application, self).__init__(argv) self.windows = [] win = Window(self) win.show() self.windows.append(win) class Window(QMainWindow): def __init__(self, parentApp, parent=None): super(Window, self).__init__(parent) self.parentApp = parentApp btn = QPushButton("New Window", self) btn.clicked.connect(self.new) def new(self): win = Window(self.parentApp) win.show() self.parentApp.windows.append(win) def closeEvent(self, event): self.parentApp.windows.remove(self) if __name__ == "__main__": app = Application(sys.argv) sys.exit(app.exec_()) 

Puede tener muchas ventanas principales sin especificar el padre en PyQt sin problemas. Simplemente ejecute este código como ejemplo:

 from PyQt5 import QtWidgets import sys class MainWindow(QtWidgets.QMainWindow): pass # App q_app = QtWidgets.QApplication(sys.argv) # Add many windows main_window1 = MainWindow() main_window1.show() main_window2 = MainWindow() main_window2.show() # Run Qt app sys.exit(q_app.exec_()) 

Tenga en cuenta que para PyQt4, simplemente reemplace QtWidgets con QtGui .

Nunca he trabajado con UI en python, pero algo parece estar mal con este código:

La clase Main ctor (del primer fragmento de código) tiene 3 args: self , ctrl y parent . Como el parent es un argumento predeterminado (puede faltar) , en nuestro caso no importa.

El código del método suponiendo que estamos en la misma clase : new_win = Main() no es correcto. Debería tener 3 argumentos:

  • el tercero fue discutido
  • el primero (uno self ) se pasa automáticamente por el marco de python
  • la única cosa que le falta es la segunda: ctrl y que debe ser suministrada por usted (después de todo, definió la clase ctor de esa manera)