Help Needed with PyQt5 Menu Bar:
I just started getting into PyQt5, and I'm currently making a menu bar for an GUI Application. Below is the code I made for the Menu so far:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, qApp
class MenuDemo(QMainWindow):
def __init__(self):
super().__init__()
####################################################
# This Creates the initial Menu Bar
bar = self.menuBar()
# This creates the Menu Bar tabs & names them
file = bar.addMenu('File')
edit = bar.addMenu('Edit')
helpme = bar.addMenu('Help')
# This creates the actions for 'File' section
new_action = QAction('New File', self)
new_action.setShortcut('Ctrl+N')
open_action = QAction('Open File', self)
open_action.setShortcut('Ctrl+O')
save_action = QAction('Save', self)
save_action.setShortcut('Ctrl+S')
save_as_action = QAction('Save as...', self)
save_as_action.setShortcut('Ctrl+Shift+S')
quit_action = QAction('Quit', self)
quit_action.setShortcut('Ctrl+Q')
# This creates the actions for 'Edit' section
undo_action = QAction('Undo', self)
undo_action.setShortcut('Ctrl+Z')
redo_action = QAction('Redo', self)
redo_action.setShortcut('Ctrl+Y')
# This creates the actions for "Help" section
doc_action = QAction('Documentation', self)
# No Shortcut Needed
about_action = QAction('About', self)
# No Shortcut Needed
####################################################
# This adds actions to Menu Bar under 'File' Tab
file.addAction(new_action)
file.addAction(open_action)
file.addAction(save_action)
file.addAction(save_as_action)
file.addAction(quit_action)
# This adds actions to Menu Bar under 'Edit' Tab
edit.addAction(undo_action)
edit.addAction(redo_action)
# This adds actions to Menu Bar under 'Help' Tab
helpme.addAction(doc_action)
helpme.addAction(about_action)
# Events
# This Sets the initial window size and title
self.setWindowTitle('My Menu')
self.resize(750, 500)
self.show()
####################################################
def quit_trigger(self):
pass
def selected(self, q):
pass
app = QApplication(sys.argv)
menus = MenuDemo()
sys.exit(app.exec_())
Problem:
I have the Menu bar displayed, and everything seems to run fine, however there is one thing that is bugging me. Under the 'File' tab of the menu, there is an option called 'Quit'. Above the 'Quit' option, I need to have some sort of line break to seperate that option from the rest of the other options. In Tkinter this is a simple task to do, but I can't seem to find how to do it with PyQt5. Any help with this is greatly appreciated!
P.S: The menu items are currently just buttons, they dont do anything yet.
addSeparator method of QMenu is just for that occasion:
# ...
file.addAction(save_as_action)
file.addSeparator()
file.addAction(quit_action)
# ...
Related
In QMidArea how to open a SubWindow? My Programs as follows. My intention to open/attach my second program in SubWindow. But Nothing Will happen. visible Only blank Window. How to resolve it?
How to attach my file in QMdi Sub-window ? and after my work, how to close the sub-window properly?
Main Programme
import sys,os
from PyQt5.QtWidgets import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
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.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3,5,5,3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addStretch()
self.setLayout(self.main_layout)
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
# subwindow.setFixedSize(500,500)
subwindow.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("fusion")
mainwindow.show()
sys.exit(app.exec_())
Second Program
import sys,os
from PyQt5.QtWidgets import *
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Country Page")
self.btn1 = QPushButton("Accept")
self.btn1.clicked.connect(self.result)
self.btn2 = QPushButton("Re Enter")
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_btn.addWidget(self.btn2)
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)
def result(self):
print("bye")
exec .close()
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())
First of all, there are two main issues with your code:
You never added the mdi area to the main layout (and you also tried to set the layout for the QMainWindow, which is forbidden);
exec is a python builtin, and has no close attribute; if you want to close the widget, you have to call self.close();
Then, the setWidget() method of QMdiSubWindow reparents the widget:
QMdiSubWindow takes temporary ownership of widget;
This means that if you want to close the sub window that contains the widget from that widget, you have to check the parent and eventually close it, as soon as you verify that it's an instance of QMdiSubWindow.
class Countrypage(QWidget):
# ...
def result(self):
print("bye")
# ensure that the parent is actually a subwindow
if isinstance(self.parent(), QMdiSubWindow):
self.parent().close()
else:
self.close()
Alternatively, you can use a custom signal and connect that when creating the subwindow.
class Countrypage(QWidget):
closeRequested = pyqtSignal()
# ...
def result(self):
print("bye")
self.closeRequested.emit()
class MainPage(QMainWindow):
# ...
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
subwindow.show()
self.countrywindow.closerequested.connect(subwindow.close)
If you want to close the active subwindow from the mdi area (or outside of it) and no matter what that sub window is, just call self.mdi.closeActiveSubWindow().
Note that if you're going to create multiple Countrypage instances, there's no point in creating an instance attribute (self.countrywindow) as it will always be overwritten as soon as another instance will be created. Adding the widget to the subwindow and that subwindow to the mdi area will automatically create a persistent reference (due to the parenting); if you need a python reference to existing pages, then create a list as an instance member in the __init__ (eg. self.pages = []) and add the new instances to that list.
I have a basic GUI window, created with pyqt5 package, that contain a button that once clicked opens a map window thanks to Python's pyqtlet package.
My program does open the map window on first click, but here my issue:
If I close the map window and click again on the button it only show me a white window.
SOURCE CODE
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QGridLayout
from PyQt5.QtCore import Qt
from pyqtlet import L, MapWidget
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# set window title
self.setWindowTitle('Windowtitle')
# set layout
self.layout = QGridLayout()
self.layout.setAlignment(Qt.AlignCenter)
# create button to show the map
self.show_map_button = QPushButton('Show Map')
self.show_map_button.clicked.connect(self.show_map)
# add button to layout
self.layout.addWidget(self.show_map_button, 0, 1)
# show layout
self.setLayout(self.layout)
self.my_map = None
def show_map(self):
# create map window instance
self.my_map = MapWindow()
class MapWindow(QWidget):
def __init__(self):
# Setting up the widgets and layout
super().__init__()
self.mapWidget = MapWidget()
self.layout = QGridLayout()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.')
self.map.addLayer(self.marker)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I click the button for the first time (works as expected):
The second time I click it doesn't work as expected:
Please, can you help me find out where the issue is?
You are not trying to reopen the window but you are creating a new window but pyqtleft only allows you to register a "map" object. So if you want to reopen the window after the user closed it then check that if the object exists then just use the show method:
def show_map(self):
if self.my_map is None:
# create map window instance
self.my_map = MapWindow()
else:
self.my_map.show()
I want to add buttons to the tabs in the QTabWidget.
My first instinct was to try to get the position of each tab and then add the button ontop of the tab, but I cant figure out how to get the position of the tab! Only the entire tab widget.
I was looking around and now what I think I should do is make a custom TabBar class where I can place buttons on each tab like the standard Qt close button.
Anyone here who can send me in the right direction?
Okay so I found out how to make it work like I want it. It was actually quite simple, I made a QWidget class with a horizontal layout and two buttons and passed it to the setTabButton function. For anyone interested see the code below.
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
class TabExample(QMainWindow):
def __init__(self):
super(TabExample, self).__init__()
self.setWindowTitle("Tab example")
# Create widgets
self.tab_widget = QtWidgets.QTabWidget()
self.setCentralWidget(self.tab_widget)
# Label's to fill widget
self.label1 = QtWidgets.QLabel("Tab 1")
self.label2 = QtWidgets.QLabel("Tab 2")
# Adding tab's
self.tab_widget.addTab(self.label1, "Tab 1")
self.tab_widget.addTab(self.label2, "Tab 2")
# Tab button's
self.right = self.tab_widget.tabBar().RightSide
self.tab_widget.tabBar().setTabButton(0, self.right, TabButtonWidget())
self.tab_widget.tabBar().setTabButton(1, self.right, TabButtonWidget())
# Tab settings
self.tab_widget.tabBar().setMovable(True)
self.show()
class TabButtonWidget(QtWidgets.QWidget):
def __init__(self):
super(TabButtonWidget, self).__init__()
# Create button's
self.button_add = QtWidgets.QPushButton("+")
self.button_remove = QtWidgets.QPushButton("-")
# Set button size
self.button_add.setFixedSize(16, 16)
self.button_remove.setFixedSize(16, 16)
# Create layout
self.layout = QtWidgets.QVBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0, 0, 0, 0)
# Add button's to layout
self.layout.addWidget(self.button_add)
self.layout.addWidget(self.button_remove)
# Use layout in widget
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
gui = TabExample()
sys.exit(app.exec_())
I'm setting up an "options" dialog in a program, where I can change some values and close the dialog with Ok/Cancel to accept of reject my changes. After closing the dialog with cancel and reopening it, i would like the last accepted values to be displayed, however I am know sure how to implement this.
Below is a very simplified version of my code. I chose to instanciate the dialog only once (as opposed to creating a new instance each time I call the dialog), mainly to avoid having to call the __init__ and import data from save files each time I open the dialog.
from PyQt5.QtWidgets import QMainWindow, QPushButton,\
QApplication, QTextEdit, QDialog, QDialogButtonBox
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
btn = QPushButton('open text 1', self)
btn.move(10, 10)
btn.clicked.connect(self.open_dlg)
self.txtdlg = TextDialog()
def open_dlg(self):
if self.txtdlg.exec_() == QDialog.Accepted:
print(self.txtdlg.preferences)
class TextDialog(QDialog):
def __init__(self):
super().__init__()
self.preferences = "text here"
self.resize(200, 150)
self.textedit = QTextEdit(self)
self.textedit.resize(200, 100)
self.textedit.setText(self.preferences)
btns = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
btns.move(20, 100)
btns.accepted.connect(self.save_and_close)
btns.rejected.connect(self.reject)
def save_and_close(self):
self.preferences = self.textedit.toPlainText()
self.accept()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
MW = MainWindow()
MW.show()
sys.exit(app.exec_())
As it is, after clicking Cancel the dialog keeps any unsaved changes to its widgets if I reopen it. My fist idea was to connect the cancel button to a close_without_saving method that updates the dialog to the last saved values before closing, but the displayed values will not be up to date if preferences is changed for some reason while the dialog is invisible. Can I run some code when i call exec_ ? Or is the logic behind my implementation wrong somehow?
You have to implement a method that sets the values of the dialog to the default values:
# ...
class MainWindow(QMainWindow):
# ...
def open_dlg(self):
self.txtdlg.reset()
if self.txtdlg.exec_() == QDialog.Accepted:
print(self.txtdlg.preferences)
class TextDialog(QDialog):
# ...
def reset(self):
self.preferences = "text here"
self.textedit.setText(self.preferences)
def save_and_close(self):
self.preferences = self.textedit.toPlainText()
self.accept()
# ...
I have been trying to implement a menu bar in my program for a few days now and i cant seem to get one running. I would like someone to look at my code and give me a template to follow to making a menu bar.
class MainWindow(QMainWindow):
def __init__(self, databaseFilePath, userFilePath):
super(MainWindow,self).__init__()
self.moviesFilePath = moviesFilePath
self.currentUserFilePath = currentUserFilePath
self.createWindow()
def changeFilePath(self):
self.currentUserFilePath = functions_classes.changeFP()
functions_classes.storeFP(self.currentUserFilePath, 1)
def createWindow(self):
self.setWindowTitle('Movies')
#Menu Bar
fileMenuBar = QMenuBar().addMenu('File')
The method changeFilePath is what I would like to be called when a menu option called 'Change user database location' is called from the menu bar File. I have read that actions are the key to this but when every i have tried to implement them they haven't worked.
The QMainWindow class already has a menu-bar.
So you just need to add a menu to it, and then add an action to that menu, like this:
def createUI(self):
...
menu = self.menuBar().addMenu('File')
action = menu.addAction('Change File Path')
action.triggered.connect(self.changeFilePath)
EDIT:
Here's a full, working example based on your example class:
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, databaseFilePath, userFilePath):
super(MainWindow,self).__init__()
self.databaseFilePath = databaseFilePath
self.userFilePath = userFilePath
self.createUI()
def changeFilePath(self):
print('changeFilePath')
# self.userFilePath = functions_classes.changeFilePath()
# functions_classes.storeFilePath(self.userFilePath, 1)
def createUI(self):
self.setWindowTitle('Equipment Manager 0.3')
menu = self.menuBar().addMenu('File')
action = menu.addAction('Change File Path')
action.triggered.connect(self.changeFilePath)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow('some/path', 'some/other/path')
window.show()
window.setGeometry(500, 300, 300, 300)
sys.exit(app.exec_())
The logic to add a Menubar with usable items is something like this
def createUI(self):
self.setWindowTitle('Equipment Manager 0.3')
#Menu Bar
fileMenuBar = QMenuBar(self)
menuFile = QMenu(fileMenuBar)
actionChangePath = QAction(tr("Change Path"), self)
fileMenuBar.addMenu(menuFile)
menuFile.addAction(actionChangePath)
Then you just need to connect the action actionChangePath to the signal triggered() with something like
connect(actionChangePath,SIGNAL("triggered()"), changeFilePath)
Probably there are some better solution (but why did you not use the Designer ?), but this one should be work