I am new in Qt (PyQt) and I am trying to make an app whose functions will be executed from menubars/system trays. A perfect example is show here:
I cannot find a good resource on how I can do this. Can someone advice.
Thanks.
I think you are looking for working with QMenu and QMainWindow for the menu part, at least.
Here you can find a C++ example:
Menus Example
and here a PyQt4 example:
Menus and Toolbars in PyQt4
Here is the code inline for your convenience:
import sys
from PyQt4 import QtGui
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(QtGui.qApp.quit)
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAction)
self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Menubar')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
As for the QSystemTrayIcon part, you could write something like this:
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon("Bomb.xpm"), w)
menu = QtGui.QMenu(parent)
exitAction = menu.addAction("Foo")
trayIcon.setContextMenu(menu)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Related
I am trying to package my GUI application into an application using FMan FBS. I am able to create and open the basic plain application; however, when I try to integrate my own code into the default code, once I try to run the app it closes instantly without running.
Here is the default code:
from fbs_runtime.application_context.PyQt5 import ApplicationContext
from PyQt5.QtWidgets import QMainWindow
import sys
if __name__ == '__main__':
# 1. Instantiate ApplicationContext
appctxt = ApplicationContext()
window = QMainWindow()
window.resize(250, 150)
window.show()
# 2. Invoke appctxt.app.exec_()
exit_code = appctxt.app.exec_()
sys.exit(exit_code)
and this works. However, my application works a lot with layouts and so I use a QWidget as my window instead of a QMainWindow. I believe this may be why the program can't be opened when packaged.
Here is a sample of my code:
class Interface:
def __init__(self):
self.app = QApplication([])
def main(self):
window = QWidget()
window.setGeometry(550, 300, 850, 550)
window.setWindowTitle("GUI")
layout = QGridLayout()
self.app.setStyle("Fusion")
tabs = QTabWidget()
tab1 = QWidget()
tab2 = QWidget()
tab3 = QWidget()
tabs.addTab(tab1, "Tab1")
tabs.addTab(tab2, "Tab2")
layout1 = QGridLayout()
layout2 = QGridLayout()
# ...
tab1.setLayout(layout1)
tab2.setLayout(layout2)
window.setLayout(layout)
window.show()
self.app.exec_()
I am able to run my program fine with "FBS run"; however, when actually packing the application with "FBS freeze/ FBS installer", it doesn't open properly. It does work with the default code which leads me to believe that changing it from QMainWindow to QWidget is causing it to not work
The logic is similar the fbs API already has a QApplication created so you must create it, in this case you just have to make the following modification to the example provided by fbs:
from fbs_runtime.application_context.PyQt5 import ApplicationContext
from PyQt5.QtWidgets import QWidget, QTabWidget, QGridLayout
import sys
class Interface:
def main(self):
self.window = QWidget()
self.window.setGeometry(550, 300, 850, 550)
self.window.setWindowTitle("GUI")
layout = QGridLayout()
tabs = QTabWidget()
tab1 = QWidget()
tab2 = QWidget()
tab3 = QWidget()
tabs.addTab(tab1, "Tab1")
tabs.addTab(tab2, "Tab2")
layout1 = QGridLayout()
layout2 = QGridLayout()
# ...
tab1.setLayout(layout1)
tab2.setLayout(layout2)
self.window.setLayout(layout)
self.window.show()
if __name__ == '__main__':
# 1. Instantiate ApplicationContext
appctxt = ApplicationContext()
interface = InterFace()
inteface.main()
appctxt.app.setStyle("Fusion")
# 2. Invoke appctxt.app.exec_()
exit_code = appctxt.app.exec_()
sys.exit(exit_code)
I've written the following PyQt4 snippet:
#!/usr/bin/env python3
import sys
from typing import Callable, Optional
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self._createMenuBar()
def _createMenuBar(self):
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu("File")
fileMenu.addAction(self._createFileNewAction())
def _createFileNewAction(self) -> QtGui.QAction:
return self._createAction(
self.style().standardIcon(QtGui.QStyle.SP_DialogSaveButton),
"New",
QtGui.QKeySequence.New,
"Load new image to be annotated",
self._fileNew
)
def _createAction(
self,
icon: Optional[QtGui.QIcon],
name: str,
shortcut: Optional[QtGui.QKeySequence],
tooltip: Optional[str],
callback: Callable[[], None]
) -> QtGui.QAction:
if icon is not None:
action = QtGui.QAction(icon, name, self)
else:
action = QtGui.QAction(name, self)
if shortcut is not None:
action.setShortcut(shortcut)
action.setToolTip(tooltip)
action.setToolTip(tooltip)
self.connect(action, QtCore.SIGNAL('triggered()'), callback)
return action
def _fileNew(self):
pass
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
app.exec_()
When I execute this program, I would expect the SP_DialogSaveButton icon to be displayed next to the "New" field in the "File" drop-down menu but it is not. PyQt is definitely able to find the icon itself, I have tried obtaining it the same way as above and displaying it in a separate QLabel which works fine.
Can someone tell what is going on here?
The problem is not standardIcon, if you use any icon you will observe the same behavior.
In Qt4 the icons of the QActions are hidden in menus, to make them visible there are 2 possibilities:
Disable the Qt::AA_DontShowIconsInMenus attribute:
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
app.setAttribute(QtCore.Qt.AA_DontShowIconsInMenus, False)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
Use the setIconVisibleInMenu() method of QAction:
action = QtGui.QAction(icon, name, self)
action.setIconVisibleInMenu(True)
I have generated my UI with Qt Designer:
like this
I have used the .ui file with the following python code:
Ui_MainWindow, QtBaseClass = uic.loadUiType("vault.ui")
Ui_Credentials, QtBaseClass = uic.loadUiType("credentials.ui")
class Credentials(QMainWindow):
def __init__(self):
super(Credentials, self).__init__()
self.ui = Ui_Credentials()
self.ui.setupUi(self)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.load.clicked.connect(self.loadVault)
self.ui.next.clicked.connect(self.next)
self.controller = CLI(....)
self.loadVault()
def loadVault(self):
self.ui.vault.clear()
vaults = self.controller.listVaults()
for vault in vaults:
item = QListWidgetItem(vault)
self.ui.vault.addItem(item)
def next(self):
print(self.ui.vault.currentItem().text())
window = Credentials()
window.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I have tried to change the window when the button next is pressed by created a new class and using a different ui file.
I found this stackoverflow post where this is a similar problem but the code is this post doesn't use a .ui and I did not manage to have a working code with .ui file. I have succeeded to have a new window when I don't use my ui file.
Someone know how can I deal with this ? Is it not adviced to use .ui file?
The solution that I propose is similar to my previous answer, the objective is to change the graphical part so we will use the function setupUI () that generates that part.
When we press the next button, you must change it back with that function.
Ui_MainWindow, _ = uic.loadUiType("vault.ui")
Ui_Credentials, _ = uic.loadUiType("credentials.ui")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.startMainWindow()
def startMainWindow(self):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.next.clicked.connect(self.startCredentials)
def startCredentials(self):
self.ui = Ui_Credentials()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I'm having a problem with PyQt5 where i have a separate ui file(still a python file not .ui) I'm trying to connect a button which would be located in that file however this doesn't work for me for some reason.
Here's my code.
from PyQt5 import QtCore, QtGui, QtWidgets
from gui import Ui_Form
class Main(QtWidgets.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.show()
self.Ui_Form.exit.clicked.connect(self.handle)
def handle(self):
self.print("hello")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
and here's some code from my auto generated gui file using pyuic:
self.exit = QtWidgets.QPushButton(Form)
self.exit.setGeometry(QtCore.QRect(375, 270, 115, 27))
self.exit.setObjectName("exit")
this same exact procedure has worked for me before in Qt4 so i don't see why it wouldn't work here?
You must use the ui attribute to access the button. You must change:
self.Ui_Form.exit.clicked.connect(self.handle)
to:
self.ui.exit.clicked.connect(self.handle)
Note: Typically when using a Widget template, it names that element as a form and the design class as Ui_Form, so you should use QWidget as a class base.
Complete code:
class Main(QtWidgets.QWidget):
def __init__(self):
super(Main, self).__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.show()
self.ui.exit.clicked.connect(self.handle)
def handle(self):
self.print("hello")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
I created 2 windows on QT4.
In Idle i managed to show Window 1 and adding a closing action once button is clicked.
I would like to call and display window 2 by clicking this button. My code is:
import os
import shlex
import sys, Tkinter
#import Converted Python UI File
from W0 import Ui_MainWindow1
If i include from W import Ui_MainWindow2 for calling my 2nd windows, it shows directly my window2 and skip window 1!!
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Button close the window1(would like to show window 2 instead now) .
self.ui.pushButton.clicked.connect(self.close)
def main():
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
main()
Try this
from W0 import Ui_MainWindow1
from W import Ui_MainWindow2
class Main1(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.ui = Ui_MainWindow1()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.close)
class Main2(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.ui = Ui_MainWindow2()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.close)
def main():
app = QtGui.QApplication(sys.argv)
window1 = Main1()
window1.show()
window2 = Main2()
window2.show()
sys.exit(app.exec_())
main()