PyQt6 - Open QWidget by left clicking the SystemTrayIcon - python

I am new to PyQt6 and python. I created a class SystemTrayIcon where I am trying to open a QWidget win which was created outside the SystemTrayIcon-class, to show up when the SystemTrayIcon got left-clicked.
I got this error: "name 'win' is not defined"
How can I fix this, that the win-QWidget which was created outside the class SystemTrayIcon will open when the SystemTrayIcon got left-clicked?
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
import sys
import ui_main
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon, win, parent=None):
QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip("Test SystemTray")
menu = QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
self.setContextMenu(menu)
self.activated.connect(self.trayiconclicked)
def trayiconclicked(self, reason):
if reason == self.ActivationReason.Trigger:
print("SysTrayIcon left clicked")
win.show() ###### -> ERROR: (<class 'NameError'>, NameError("name 'win' is not defined"), <traceback object at 0x000001B04F5C9580>)
def run():
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
win = QWidget()
tray_icon = SystemTrayIcon(QIcon("images/logo.png"), win)
tray_icon.setVisible(True)
tray_icon.show()
ui = ui_main.Ui_Form()
ui.setupUi(win, tray_icon.geometry().right(), tray_icon.geometry().bottom(),
tray_icon.geometry().width(), tray_icon.geometry().height())
ui.btn_close.clicked.connect(lambda: win.close())
sys.exit(app.exec())
if __name__ == '__main__':
run()

It should work now you need to make win its attribute before you call it
class SystemTrayIcon(QSystemTrayIcon):
def __init__(self, icon, win, parent=None):
QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip("Test SystemTray")
self.win = win # <----- name it whatever you want ie self.abc will also work
menu = QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
self.setContextMenu(menu)
self.activated.connect(self.trayiconclicked)
def trayiconclicked(self, reason):
if reason == self.ActivationReason.Trigger:
print("SysTrayIcon left clicked")
self.win.show() # <--- if you named you attribute self.abc call self.abc here instead of self.win
NOTE:
to make a variable a attribute of a class you need to define it with self eg, here I want to have age as a attribute
class Person:
def __init__(self, name, age):
# assigning attributes
self.age = age
def some_method(self):
# here calling self.name will give you error because you didn't assign it as a attribute
print(self.name)
def some_method_1(self):
# here this will not give error as you have assigned it earlier as a attribute
print(self.age)
p = Person("Bob", 16)
p.some_method_1()
p.some_method()

Related

PyQt5 insertPlainText from Another Class Python 3.8 [duplicate]

This question already has answers here:
Variable scopes in Python classes
(4 answers)
Closed 2 years ago.
Clicking button "OK" on "otherWindow" should cause the MainWindow's QTextEdit to insert text "WORKS!".
Problem is, it does execute print("Print Works"), but insertPlainText seems to do nothing when called from another function.
The def printText(self, message): function itself isn't broken, it works as it's supposed to, as you can verify by clicking on the "Message" button on Main Window.
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.myLayout = QVBoxLayout()
self.status = QTextEdit()
self.status.setStyleSheet("QTextEdit {min-width:500px;min-height:200px;}")
self.status.insertPlainText("test")
self.btnYes = QPushButton("other window")
self.btnPrint = QPushButton("Message")
self.btnYes.clicked.connect(self.showOtherWindow)
self.btnPrint.clicked.connect(self.btnPrintClick)
self.myLayout.addWidget(self.btnPrint)
self.myLayout.addWidget(self.btnYes)
self.myLayout.addWidget(self.status)
self.setLayout(self.myLayout)
def setMainText(self, message):
self.status.insertPlainText("test")
def showOtherWindow(self):
self.otherWindow = otherWindow()
self.otherWindow.show()
def btnPrintClick(self):
self.printText("button clicked")
def printText(self, message):
self.status.insertPlainText("\n" + message)
print("Print Works")
class otherWindow(QWidget):
def __init__(self):
super(otherWindow, self).__init__()
self.button = QPushButton("OK")
self.layout2 = QVBoxLayout()
self.button.clicked.connect(self.btnClick)
self.layout2.addWidget(self.button)
self.setLayout(self.layout2)
self.setFixedSize(200,150)
def btnClick(self):
MainWindow().printText("WORKS!")
self.close()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
It doesn't work because you're setting the text on a new window (which is closed immediately).
def btnClick(self):
MainWindow().printText("WORKS!")
When you call MainWindow(), you're actually creating a NEW instance of MainWindow, and the text is actually updated for that window, but you can't see it as it is immediately garbage collected and deleted right after the function returns.
You need to access the existing instance, or find a way to communicate with it (usually using signals).
In the following example, I'm adding a reference to the main window to the OtherWindow constructor, and then access it's method afterwards:
class MainWindow(QWidget):
# ...
def showOtherWindow(self):
self.otherWindow = OtherWindow(self)
self.otherWindow.show()
class OtherWindow(QWidget):
def __init__(self, mainWindow=None):
super(OtherWindow, self).__init__()
self.mainWindow = mainWindow
# ...
def btnClick(self):
if self.mainWindow:
self.mainWindow.printText("WORKS!")
self.close()
Note: I capitalized the OtherWindow class name, lower case names should be only used for variables and attributes.

Dynamically created QActions creating class object

I have a simple pyside QMenu which is being populated with QActions when the application starts. Each menu action represents a class object. How can I create a new instance of the class object based on the Menu Item clicked, and append that new object to a list, which in this example is called
ACTIVE_BAKERS = []
import sys
from PySide import QtGui, QtCore
################################################################################
# Bakers
################################################################################
class Baker(QtGui.QWidget):
def __init__(self, name):
self.name = name
class Baker_John(Baker):
def __init__(self):
Baker.__init__(self, name='John')
class Baker_Amy(Baker):
def __init__(self):
Baker.__init__(self, name='Amy')
class Baker_Makela(Baker):
def __init__(self):
Baker.__init__(self, name='Makela')
class Baker_Jeff(Baker):
def __init__(self):
Baker.__init__(self, name='Jeff')
################################################################################
# Action
################################################################################
class MyAction(QtGui.QAction):
on_action = QtCore.Signal(dict)
def __init__(self, user_info, *args, **kwargs):
super(MyAction, self).__init__(*args, **kwargs)
self.ui = user_info
self.triggered.connect(self.on_triggered)
def on_triggered(self):
print('UI:', self.ui)
self.on_action.emit(self.ui)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(200, 300)
# OBJECTS - variable containing list of class objects created
ACTIVE_BAKERS = []
# CONTROLS
self.ui_items = QtGui.QListView()
self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
self.setCentralWidget(self.ui_items)
self.create_context_menu_ui()
# dynamically create the menu
def create_context_menu_ui(self):
self.add_baker = QtGui.QMenu("Add")
AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
for x in AVAILABLE_BAKERS:
new_action = MyAction(x, x.name, self)
self.add_baker.addAction(new_action)
self._cmenu = QtGui.QMenu()
self._cmenu.addMenu(self.add_baker)
def open_tasks_contextmenu(self, position):
self._cmenu.exec_(QtGui.QCursor.pos())
def main():
app = QtGui.QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
the exec_() method of QMenu returns the selected QAction, through that QAction that is a MyAction that has as attribute ui that gives us the associated Barker object, using the Barker we can access the class through __class__ and create another one object:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(200, 300)
# OBJECTS - variable containing list of class objects created
self.ACTIVE_BAKERS = []
# CONTROLS
self.ui_items = QtGui.QListView()
self.ui_items.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui_items.customContextMenuRequested.connect(self.open_tasks_contextmenu)
self.setCentralWidget(self.ui_items)
self.create_context_menu_ui()
# dynamically create the menu
def create_context_menu_ui(self):
self.add_baker = QtGui.QMenu("Add")
AVAILABLE_BAKERS = [Baker_John(), Baker_Amy(), Baker_Makela(), Baker_Jeff()]
for x in AVAILABLE_BAKERS:
new_action = MyAction(x, x.name, self)
self.add_baker.addAction(new_action)
self._cmenu = QtGui.QMenu()
self._cmenu.addMenu(self.add_baker)
def open_tasks_contextmenu(self, position):
action = self._cmenu.exec_(QtGui.QCursor.pos())
if isinstance(action, MyAction):
obj = action.ui.__class__()
if obj not in self.ACTIVE_BAKERS:
self.ACTIVE_BAKERS.append(obj)

Call a function from another class by clicking a button PyQt4

I have a checkbox and a run button. When the checkbox is checked, I want to run some functions by clicking the button. The problem is that the function is in another class outside the button's class. My example codes are as below.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Tab1Widget1(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.Tab1Widget1initUI()
def Tab1Widget1initUI(self):
self.setLayout(QGridLayout())
self.T1W1checkBox1 = QCheckBox('a', self)
self.layout().addWidget(self.T1W1checkBox1, 1, 0)
def run(self):
if self.T1W1checkBox1.isChecked() == True:
pass
class Tab1Layout(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QGridLayout())
self.group1 = Tab1Widget1(self)
self.layout().addWidget(self.group1, 0, 0)
btn = QPushButton('Run', self)
self.layout().addWidget(btn, 1, 0)
btn.clicked.connect(Tab1Widget1().run()) ##the problem is in this line.
class Page1(QTabWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
self.tab1 = Tab1Layout()
self.addTab(self.tab1, "Tab1")
self.tab2 = QWidget()
self.tab3 = QWidget()
self.addTab(self.tab2, "Tab2")
self.addTab(self.tab3, "Tab3")
self.tab2_initUI()
self.tab3_initUI()
def tab2_initUI(self):
grid = QGridLayout()
self.tab2.setLayout(grid)
def tab3_initUI(self):
grid = QGridLayout()
self.tab3.setLayout(grid)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setGeometry(300, 200, 600, 370)
self.startPage1()
def startPage1(self):
x = Page1(self)
self.setWindowTitle("Auto Benchmark")
self.setCentralWidget(x)
self.show()
def main():
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
As you can see, I want to run the "run" function in "Tab1Widget1" class. However, the button is in "Tab1Layout" class.
When I run the codes, it returns to me "TypeError: connect() slot argument should be a callable or a signal, not 'NoneType'"
If anyone knows how to solve this, pls let me know. Appreciated!
There is no problem in connecting any callable to a button click regardless of what object it is in. But your code has two specific problems. You write
btn.clicked.connect(Tab1Widget1().run())
The first problem here is that Tab1Widget1() is creating a new Tab1Widget1 but presumably you don't want that. You want to call run on the Tab1Widget1 you have already created and stored in self.group.
The second problem is that when you connect a signal you need to connect it to a callable: the method you want to call. Instead here you are calling the run method at connect time and trying to connect to the result of that call (which is None). So you are trying to connect the signal to None which will of course fail. You need to refer to the method without calling it: just remove the calling brackets.
Putting it together:
btn.clicked.connect(self.group1.run)
That seems to work.

"QStackedWidget.setCurrentIndex": It does not work or error mark

I'm doing a program with graphical interface using PyQt5 . I want to do is that when the user presses certain button, this change widget and show other options.
For this I decided to use QStackedWidget, and all my interface build it from the QT5 designer.
However, in my code, wanting to determine that my name button "btfr" show me "page_2" of my stackedWidget when pressed, using the QStackedWidget.setCurrentIndex method, this does nothing or make any error.
the code is as follows:
import sys
from PyQt5 import uic
from PyQt5.QtCore import QTimeLine
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
infz = uic.loadUiType("main.ui")[0]
class FaderWidget(QWidget):
def __init__(self, old_widget, new_widget):
QWidget.__init__(self, new_widget)
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.pixmap_opacity = 1.0
self.timeline = QTimeLine()
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
self.timeline.setDuration(333)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
painter.end()
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.repaint()
class StackedWidget(QStackedWidget):
def __init__(self, parent=None):
QStackedWidget.__init__(self, parent)
def setCurrentIndex(self, index):
self.stack = MyWindowClass()
self.a = self.stack.stackedWidget.currentWidget()
self.b = self.stack.stackedWidget.widget(index)
self.fader_widget = FaderWidget(self.a, self.b)
QStackedWidget.setCurrentIndex(self, index)
print(self, index)
def setPage1(self):
self.setCurrentIndex(0)
def setPage2(self):
self.setCurrentIndex(1)
class MyWindowClass(QStackedWidget, infz):
def __init__(self, parent=None):
global pos, c, f
self.pos = 0
self.c = []
self.f = False
QStackedWidget.__init__(self, parent)
self.setupUi(self)
self.setWindowTitle('SkR')
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyWindowClass()
window.resize(788, 518)
stack = StackedWidget()
window.btfr.clicked.connect(stack.setPage2)
window.btnpx.clicked.connect(stack.setPage1)
window.show()
sys.exit(app.exec_())
What I intend with this code is that the change of widget does so with an effect: "fade out".
If I print the "self " and the "index " receiving QStackedWidget.setCurrentIndex shows the following:
<__main__.StackedWidget object at 0x7fc2eb6b5c18> 0
The number zero is index, and the other element is self
Thank you for your attention, I hope someone can help.
Your question isn't completely clear, but don't you just want:
def setIndex(self, index):
self.setCurrentIndex(index)
However, this is a little redundant as you should able to link the button directly to the setCurrentIndex method and use lambda to pass the index value:
btfr.clicked.connect(lambda: self.setCurrentIndex(2))

Calling QMainWindow From Second QDialog

My PyQt application starts with Login screen. If password OK, a module-screen (with icons) appears. When user click some button, a QMainWindow will appears. But I can't do this because of qmainwindow object has no attribute '_exec' error. This is my code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
...
...
class Login(QDialog):
def __init__(self, parent=None):
super(Login, self).__init__(parent)
...
...
uyg=QApplication(sys.argv)
class icons(QDialog):
def __init__(self, parent=None):
super(icons, self).__init__(parent)
...
self.buton = QPushButton()
self.buton.pressed.connect(self.open)
...
def open(self):
dialogmain = Main()
dialogmain._exec() #or dialogmain.show() ???
self.accept()
self.close()
uyg.exec_()
if Login().exec_() == QDialog.Accepted:
dialog = icons()
dialog.exec_()
else:
uyg.quit()
What am I doing wrong? Thank you.
Lately i have done the similar work:I have a loging window and a main window ,and I used something like a FSM to switch between the loging and main window.
Let's say we have 3 state:loging,main,quit.
STATE_LOGING = 0
STATE_MAIN = 1
STATE_QUIT = 2
STATE_DESTROY = 3 #this is a flag
class CState():
sigSwitchState = pyqtSignal(int)
def __init__(self):
super(CState,self).__init__()
def start(self):
pass
def sendQuit(self,nextstate):
self.sigSwitch.emit(nextstate)
class CLoginState(CState):
def __init__(self):
super(CLoginState,self).__init__()
def start(self):
w = Loging()
w.show()
def whenPasswdOk(self):
self.sendQuit(STATE_MAIN)
class CMainState(CState):
def __init__(self):
super(CMainState,self).__init__()
def start(self):
w = MainWindow()
w.show()
def whenMainWindowQuit(self):
self.sendQuit(STATE_QUIT)
class CQuitState(CState):
def __init__(self):
super(CQuitState,self).__init__()
def start(self):
#do some clean stuff...
pass
def whenCleanDone(self):
self.sendQuit(STATE_DESTROY)
class CMainApp():
def __init__(self):
self.app = QApplication(sys.argv)
def __CreateState(state):
if state == STATE_LOGING:
s = CLoginState()
if state == STATE_MAIN:
s = CMainState()
#... same as other state
s.sigSwitchState.connect(self.procNextState)
def procNextState(self,state):
if state == STATE_DESTROY:
QApplication().exit()
s = self.__CreateState(state)
s.start()
def run(self):
self.procNextState(STATE_LOGING)
sys.exit(self.app.exec_())
if __name__ == "__main__":
app = CMainApp()
app.run()
Apart from the application object and QDrag, please pretend that exec() doesn't exist. It is an utterly confusing method that essentially never has to be used. Especially not by anyone new to Qt.
If you want to display any widget, simply show() it. If you want to be notified when a dialog was accepted, connect some code to its accepted() signal. That's all.

Categories