How to open a new MDI sub-window in PyQt5? - python

What I want to do is to open a new Countrypage sub-window by clicking on the "New" button which is in Countrypage itself.
For example, if I click the "New" button in a CountryPage window (window title: "Country page"), one more new Countrypage window will be opened in the MDI area (window title: "Country Page 1"). Now if we click the "New" button in "Country Page 1", one more new window will open in the MDI area (window title: "Country page 2") and so on - and I want to close the windows one by one by pressing the corresponding "Close" button in Countrypage. New window are opened only by pressing a "New" button.
And if we close the last opened window by pressing the "Close" button, the text item in the "Country" text-box will be automatically updated in the previous window's "Country" text-box and so on.
Main Script :
import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
count = 0
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.mdi.setFixedSize(1000,400)
self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWindowTitle(" Sample Programme")
self.setGeometry(100,100,1600,600)
self.Ui()
self.show()
def Ui(self):
self.btn1=QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.right_layout = QHBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3,5,5,3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.right_layout.addWidget(self.mdi)
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addLayout(self.right_layout)
self.main_layout.addStretch()
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
self.subwindow1 = QMdiSubWindow()
self.subwindow1.setObjectName("SubWindow_1")
# self.subwindow1.setWindowFlag(Qt.FramelessWindowHint)
print(Countrypage.btn2click)
def countrypage(self):
self.countrywindow = Countrypage()
self.subwindow1.setWidget(self.countrywindow)
self.subwindow1.setWindowTitle("Create Country")
self.subwindow1.setFixedWidth(300)
self.mdi.addSubWindow(self.subwindow1)
self.subwindow1.show()
self.mdi.cascadeSubWindows()
self.countrywindow.closeRequsted.connect(self.subwindow1close)
def subwindow1close(self):
print("close activated from mdi programme")
self.subwindow1.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("Windows")
mainwindow.show()
sys.exit(app.exec_())
Countrypage.py
import sys,os
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import pyqtSignal
class Countrypage(QWidget):
closeRequsted = pyqtSignal()
def __init__(self):
super().__init__()
self.btn1 = QPushButton("close")
self.btn2 = QPushButton("New")
self.btn1.clicked.connect(self.result)
self.btn2.clicked.connect(self.btn2click)
self.tb_country = QLineEdit()
self.tb_continent =QLineEdit()
self.form_layout = QFormLayout()
self.form_layout.addRow("Country",self.tb_country)
self.form_layout.addRow("continent",self.tb_continent)
self.form_layout.addRow("",self.btn2)
self.form_layout.addRow("",self.btn1)
self.setLayout(self.form_layout)
def result(self):
self.closeRequsted.emit()
def btn2click(self):
btn2text = (self.btn2.text())
print(btn2text)
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())

The adding and closing of sub-windows is best handled by the main-window. The CountryPage class doesn't need to know anything about the sub-windows. The new/close buttons can be directly connected to methods of the main-window. This makes it easier to manage the sub-windows via the functions of the mdi-area.
Below is a re-write of your example which should do what you asked for:
Main Script:
import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.mdi.setFixedSize(1000, 400)
self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWindowTitle("Sample Programme")
self.setGeometry(100, 100, 1600, 600)
self.Ui()
def Ui(self):
self.btn1 = QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.right_layout = QHBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3, 5, 5, 3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.right_layout.addWidget(self.mdi)
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addLayout(self.right_layout)
self.main_layout.addStretch()
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
page = Countrypage()
subwindow = self.mdi.addSubWindow(page)
subwindow.setWindowTitle("Create Country")
subwindow.setFixedWidth(300)
page.btn_close.clicked.connect(self.subwindowclose)
page.btn_new.clicked.connect(self.countrypage)
subwindow.show()
self.mdi.cascadeSubWindows()
def subwindowclose(self):
print("close activated from mdi programme")
current = self.mdi.activeSubWindow()
if current is not None:
self.mdi.activatePreviousSubWindow()
previous = self.mdi.activeSubWindow()
if previous is not None:
previous.widget().update_fields(current.widget())
current.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("Windows")
mainwindow.show()
sys.exit(app.exec_())
Countrypage.py:
import sys,os
from PyQt5.QtWidgets import QWidget,QApplication,QPushButton,QLineEdit,QFormLayout,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import pyqtSignal
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.btn_close = QPushButton("Close")
self.btn_new = QPushButton("New")
self.tb_country = QLineEdit()
self.tb_continent = QLineEdit()
self.form_layout = QFormLayout()
self.form_layout.addRow("Country", self.tb_country)
self.form_layout.addRow("Continent", self.tb_continent)
self.form_layout.addRow("", self.btn_close)
self.form_layout.addRow("", self.btn_new)
self.setLayout(self.form_layout)
def update_fields(self, other):
if isinstance(other, Countrypage):
self.tb_country.setText(other.tb_country.text())
self.tb_continent.setText(other.tb_continent.text())
else:
raise TypeError('invalid page type')

Related

pyqt5 pushbutton change according to combobox selection

If you select 'ice cream' in the combobox, 'chocolate', 'strawberry' and 'vanilla' buttons are created, and when you select 'fruit' in the combobox, 'apple' and 'persimmon' buttons are created.
I want the pushbutton to change in real time according to the combobox selection.
If you create a button directly with icecream_list or fruit_list, the button is created normally. However, if a button is created by defining a buttonlist through the combo box, the button is not created.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import pandas as pd
combobox1 = pd.DataFrame(({"name":['icecream','fruit']}))
buttonlist = pd.DataFrame({"name":[]})
icecream_list = pd.DataFrame({"name":['chocolate','strawberry','vanilla']})
fruit_list = pd.DataFrame({"name":['apple','persimmon']})
class MyApp1(QWidget):
def __init__(self):
super().__init__()
self.addbutton = addbutton()
self.initUI()
def initUI(self):
mainwin = QHBoxLayout()
comboboxSubject = QComboBox(self)
comboboxSubject.setFixedHeight(15)
for i in range(len(combobox1)):
combobox_name = combobox1.loc[i,'name']
comboboxSubject.addItem(combobox_name)
comboboxSubject.activated[str].connect(self.selectcombobox)
buttonLayout = QVBoxLayout()
buttonLayout.addWidget(self.addbutton)
buttonwin = QFrame()
buttonwin.setLayout(buttonLayout)
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(comboboxSubject)
splitter.addWidget(buttonwin)
mainwin.addWidget(splitter)
self.setLayout(mainwin)
self.setWindowTitle('combobox')
self.move(0, 100)
self.resize(1200, 800)
self.showMaximized()
def selectcombobox(self, text):
global buttonlist
if text == 'icecream':
buttonlist = icecream_list
else:
buttonlist = fruit_list
class addbutton(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
for i in range(len(buttonlist)):
buttonlist_1 = QPushButton(buttonlist.iloc[i,0], self)
buttonlist_1.resize(25,25)
buttonlist_1.move(50*i,50)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp1()
sys.exit(app.exec_())
ex1
ex2

How to re Open my closed subwindow in PyQt5 QStackWidget?

Add My second File In QStackwidget.
After pressing the "close" Button from the second file, I can't able to reopen it.
How to reopen the second file? How to refresh the code?
How to close the second/Subwindow file properly and re-open the same from the first file. or If we close the second file, How to refresh the first file fully.
First File
import sys,os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle(" Sample Programme")
self.setGeometry(100,100,1000,600)
self.Ui()
self.show()
def Ui(self):
self.countrywindow = Countrypage()
self.stackitems = QStackedWidget()
self.btn1=QPushButton("Open 2nd File")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.right_layout = QHBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.right_frame = QFrame()
self.right_layout = QHBoxLayout(self.right_frame)
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addWidget(self.right_frame)
self.main_layout.addStretch()
self.stackitems.addWidget(self.countrywindow)
self.right_layout.addWidget(self.stackitems)
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
self.stackitems.setCurrentWidget(self.countrywindow)
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("fusion")
mainwindow.show()
sys.exit(app.exec_())
SecondFile
import sys,os
from PyQt5.QtWidgets import *
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Country Page")
self.btn1 = QPushButton("close")
self.btn1.clicked.connect(lambda : self.close())
self.form_layout = QFormLayout()
self.form_layout.addRow("Country",QLineEdit())
self.form_layout.addRow("continent",QLineEdit())
self.layout_btn = QHBoxLayout()
self.layout_btn.addStretch()
self.layout_btn.addWidget(self.btn1)
self.layout_country = QVBoxLayout()
self.layout_country.addLayout(self.form_layout)
self.layout_country.addLayout(self.layout_btn)
self.layout_country.addStretch()
self.setLayout(self.layout_country)
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())
If you have hidden it then you have to call show():
def countrypage(self):
self.countrywindow.show()
self.stackitems.setCurrentWidget(self.countrywindow)
Note: It is advisable to avoid the abuse of lambda methods, if they are not strictly necessary then do not use them, in your case using a lambda to invoke the close method is unnecessary so you should use:
self.btn1.clicked.connect(self.close)
In addition, the concept of files only serves to order the project but it does not make sense in the execution of the program since the same logic applies if all the logic is in the same file.

Change QLabelText as QLineEdit text changes

There is this QLineEdit with setText is set to a predefined value and there is a QDialog with QLabel in it which is supposed to show whatever is in the QLineEdit. The code below shows the situation.
import sys
import os
import datetime
from PySide2.QtWidgets import *
from PySide2 import *
now = datetime.datetime.now()
now_str = now.strftime("%H.%M.%S,%d/%m/%y")
default_text = (str("Sugar_" + now_str))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(600, 500)
btn = QPushButton("show dialog")
mw_layout = QVBoxLayout()
mw_layout.addWidget(btn)
self.setLayout(mw_layout)
btn.clicked.connect(show_d)
class dialog(QDialog):
def __init__(self):
super(dialog, self).__init__()
self.resize(400, 350)
title = QLineEdit()
title.setText(default_text)
show_title = QPushButton("Show title")
cancel = QPushButton("Cancel")
d_layout = QVBoxLayout()
d_layout.addWidget(title)
d_layout.addWidget(show_title)
d_layout.addWidget(cancel)
self.setLayout(d_layout)
t = title.text()
title_dialog = QDialog()
label = QLabel()
label.setText("The title is " + title.text())
ok = QPushButton("OK!")
t_layout = QVBoxLayout()
t_layout.addWidget(label)
t_layout.addWidget(ok)
title_dialog.setLayout(t_layout)
def show_t():
title_dialog.exec_()
title_dialog.setModal(True)
def close_t():
title_dialog.accept()
show_title.clicked.connect(show_t)
ok.clicked.connect(close_t)
cancel.clicked.connect(self.close_d)
def close_d(self):
self.reject()
def show_d():
d = dialog()
d.exec_()
d.setModal(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = MainWindow()
MainWindow.show()
sys.exit(app.exec_())
But this doesn't work like I expected it to. The QLabel text just show the default text even when the text in QLineEdit is changed.
The console also shows the following error;
qt.xkb.compose: failed to create compose table.
I think there is something's obviously wrong but I can't seem to find what.
Any help appreciated.
You must update the text before a certain event, for example an instant before displaying the dialog:
class dialog(QDialog):
def __init__(self):
super(dialog, self).__init__()
self.resize(400, 350)
self.title_lineedit = QLineEdit(default_text)
show_title = QPushButton("Show title")
cancel = QPushButton("Cancel")
d_layout = QVBoxLayout(self)
d_layout.addWidget(self.title_lineedit)
d_layout.addWidget(show_title)
d_layout.addWidget(cancel)
self.title_dialog = QDialog()
self._title_label = QLabel()
ok = QPushButton("OK!")
t_layout = QVBoxLayout(self.title_dialog)
t_layout.addWidget(self._title_label)
t_layout.addWidget(ok)
show_title.clicked.connect(self.on_clicked)
ok.clicked.connect(self.title_dialog.reject)
cancel.clicked.connect(self.reject)
self.update_label()
def update_label(self):
self._title_label.setText("The title is " + self.title_lineedit.text())
def on_clicked(self):
self.update_label()
self.title_dialog.exec_()

Delete space between Combobox and label (QGridlayout)

I'm trying to create a Application form by PySide, and i follow some tutorial , but i have a problem that is a space between QLabel and QCombobox.
This is my code
import sys
from PySide import QtCore, QtGui
from PySide.QtGui import *
from PySide.QtCore import *
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.setMinimumHeight(660)
self.setMinimumWidth(700)
self.setMaximumHeight(660)
self.setMaximumWidth(900)
grid = QtGui.QGridLayout()
grid.addWidget(self.First(), 0,0,2,0)
self.setLayout(grid)
self.setWindowTitle("Library")
self.resize(700, 660)
def First(self):
groupBox = QtGui.QFrame()
groupBox.setMaximumWidth(230)
groupBox.setMaximumHeight(700)
lbFile = QtGui.QLabel("File :",self)
lbFolders = QtGui.QLabel("Folders :",self)
cbFile = QtGui.QComboBox(self)
cbFile.addItem("File 1")
cbFile.addItem("File 2")
lvFolders = QtGui.QListView(self)
lvFolders.setMaximumWidth(220)
lvFolders.setMaximumHeight(500)
vbox = QtGui.QGridLayout()
vbox.addWidget(lbFile,0,0)
vbox.addWidget(cbFile,0,1)
vbox.addWidget(lbFolders,2,0)
vbox.addWidget(lvFolders,3,0,1,2)
groupBox.setLayout(vbox)
return groupBox
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
clock = Window()
clock.show()
app.exec_()
and this is my Form :
I dont know how to delete the space between QLabel "File" and QCombobox "File 1"
Adjust the stretch factor for the second column of the layout, and possibly also make the folders label span both columns:
vbox = QtGui.QGridLayout()
vbox.addWidget(lbFile,0,0)
vbox.addWidget(cbFile,0,1)
vbox.addWidget(lbFolders,2,0,1,2)
vbox.addWidget(lvFolders,3,0,1,2)
vbox.setColumnStretch(1, 1)

PyQt window closes after launch

I'm trying to use a QPushButton to call a function that opens a new instance of QWebView. Works but as soon as the window opens it closes again.
I've read this - PyQt window closes immediately after opening but I don't understand how to reference the window to keep it open.
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtWebKit import QWebSettings
from PyQt4.QtNetwork import QNetworkAccessManager
from PyQt4.QtNetwork import *
UA_STRING = """Test Test Test"""
vidurl = ("empty")
def web1():
class YWebPage(QtWebKit.QWebPage):
def __init__(self):
super(QtWebKit.QWebPage, self).__init__()
def userAgentForUrl(self, url):
return UA_STRING
class Browser(QtGui.QMainWindow): # "Browser" window
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(800,600) # Viewport size
self.centralwidget = QtGui.QWidget(self)
self.html = QtWebKit.QWebView()
def browse(self):
self.webView = QtWebKit.QWebView()
self.yPage = YWebPage()
self.webView.setPage(self.yPage)
self.webView.load(QtCore.QUrl(vidurl)) # Video URL
self.webView.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled,True) # Enables flash player
self.webView.show()
x = Browser()
# QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.HttpProxy, "proxy.example.com", 8080)) # Proxy setting
x.browse()
def main(): # Dialog window
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.resize(200, 450)
w.setFixedSize(200, 350)
w.move(300, 300)
w.setWindowTitle('U-bot 0.1')
# Setup GUI
# Start Button
w.__button = QtGui.QPushButton(w)
w.__button.clicked.connect(lambda: web1())
# Text area
w.__qle = QtGui.QLineEdit(w)
w.__qle.setText ("http://")
vidurl = w.__qle.text # Get video url from user
# Images
pixmap1 = QtGui.QPixmap("ubot.png")
lbl1 = QtGui.QLabel(w)
lbl1.resize(200, 150)
lbl1.setPixmap(pixmap1)
lbl1.setScaledContents(True)
w.__button.setText('Start')
layout = QtGui.QVBoxLayout()
layout.addStretch(1)
layout.addWidget(w.__qle)
layout.addWidget(w.__button)
w.setLayout(layout)
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
app.exec_()
Create a MainWindow class that keeps a list of open Browsers, and every time when you open a browser, just add it to the list. And when a browser window closes, it will remove itself from the list, see closeEvent.
import sys
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtWebKit import QWebSettings
from PyQt4.QtNetwork import QNetworkAccessManager
from PyQt4.QtNetwork import *
UA_STRING = """Test Test Test"""
vidurl = ("empty")
class YWebPage(QtWebKit.QWebPage):
def __init__(self):
super(YWebPage, self).__init__()
def userAgentForUrl(self, url):
return UA_STRING
class Browser(QtGui.QMainWindow): # "Browser" window
def __init__(self, main, url):
QtGui.QMainWindow.__init__(self)
self.main = main
self.resize(800,600) # Viewport size
self.webView = QtWebKit.QWebView()
self.setCentralWidget(self.webView)
self.yPage = YWebPage()
self.webView.setPage(self.yPage)
self.webView.load(QtCore.QUrl(url)) # Video URL
self.webView.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True) # Enables flash player
def closeEvent(self, event):
self.main.browsers.remove(self)
super(Browser, self).closeEvent(event)
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.browsers = []
self.resize(200, 450)
self.setFixedSize(200, 350)
self.move(300, 300)
self.setWindowTitle('U-bot 0.1')
# Setup GUI
# Start Button
self.__button = QtGui.QPushButton('Start')
self.__button.clicked.connect(self.open)
# Text area
self.__qle = QtGui.QLineEdit()
self.__qle.setText("http://")
# Images
pixmap1 = QtGui.QPixmap("ubot.png")
lbl1 = QtGui.QLabel()
lbl1.resize(200, 150)
lbl1.setPixmap(pixmap1)
lbl1.setScaledContents(True)
layout = QtGui.QVBoxLayout()
layout.addStretch(1)
layout.addWidget(self.__qle)
layout.addWidget(self.__button)
self.setLayout(layout)
def open(self):
b = Browser(self, self.__qle.text())
b.show()
self.browsers.append(b)
def main():
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
To keep a reference to a QObject, you can either keep the variable in scope, or add it as the child of another QObject which variable already stays in scope.
And for QWidget, the parent should also be a QWidget, so, in your case, you'd want to make w as the parent of all your QMainWindows.
def web1(parent):
...
class Browser(QtGui.QMainWindow): # "Browser" window
def __init__(self, parent):
QtGui.QMainWindow.__init__(self, parent)
...
def main():
...
w.__button.clicked.connect(lambda: web1(w))
This also avoid maintaining manually a list of opened windows, since the object hierarchy can do that for you.
PS: The child windows are shown as toplevel windows and not inside the w window, because QMainWindow (and QDialog) has the Qt::Window flag set by default.

Categories