How do i pass variables from one python module(1st module) to another(2nd module) which contains a main module and two Qdialog which appears when the buttons are pressed in the 2nd module
How can i send these values from 1st module to the Qdialog in the 2nd module
I am dealing with multiple modules and data needs to updated here and there and totally confused with Inheritance/ Creating instances of classes and Modules
# mainwindow.py(1st module)
from 2nd_module import window2, dialog1, dialog2
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.win2 =window2()
self.dia1 =dialog1()
if 1:
self.win2.bar()
# pass data
self.dia1.some_function(data)
.
# 2nd_module.py
from mainwindow import mainwindow
class dialog1 (QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
def update(self):
w = window2()
#do something
w.self.ma.foo()
.
.
class dialog2 (QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
.
.
class window2(QtGui.QMainWindow):
def __init__(self):
super(window2, self).__init__()
self.update_request = 0 # initilizing the variable here
self.ma = mainwindow()
self.connect(dialog1_bttn, Qt.SIGNAL("clicked()"), self.open_dialog1 )
def open_dialog1(self): #this is how i create the instance of dialog box
self.open1 =dialog1()
self.open1.show
def foo(self): # this updates the request value from 2 dialog boxes
#do something
self.update_request = 1
def bar(self): # this is called from 1st module to check if there are any update requests
if self.update_request ==1:
#do something
My struggles
so here when i call foo() from dialog1 it works fine and updates the value to 1. But now when i call bar() from 1st module the value self.update_request turns to be 0 all the time
my mainwindow(1st module) runs all the background tasks like exchanging data from serial port,etc. and i need to send that data to the dialog1 in 2nd_module to update the values
Can anyone please advise me on how to create proper instances of Qt Dialogs, windows and to pass data/variables/list from one class to another ..thanks in advance
Related
So normally you can overwrite a class method by doing something like this.
class A():
def test(self):
return 1+1
def edit_patch(func):
def inner(*args,**kwargs):
print('this test worked')
return inner
a=A()
a.test = edit_patch(a.test)
Now a.test will return 'this test worked' instead of 2. I'm trying to do something similar in my pyqt6 application. The function below belongs to the "main" class in my code and is connected to a button click. This function is meant to instantiate another class (which is another window in pyqt6). That part works, but I would like to alter the behavior of the select function in this instance. However the method above doesn't seem to work as the select function continues to exhibit the default behavior.
def edit_proj(self):
self.psearch=PSearch(conn=self.conn,parent=self)
self.psearch.select = edit_patch(self.psearch.select)
self.psearch.show()
Any help on this would be great
As requested, here is an MRE
from PyQt6 import QtCore, QtGui, QtWidgets
def edit_patch(func):
def inner(*args,**kwargs):
print('this test worked')
return inner
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(50, 50)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.EditProjButton = QtWidgets.QPushButton(self.centralwidget)
self.EditProjButton.setObjectName("EditProjButton")
self.EditProjButton.clicked.connect(self.nextwindow)
def nextwindow(self):
print('hello from main window')
self.newwindow=Ui_ProjSearchForm(QtWidgets.QWidget())
self.newwindow.select = edit_patch(self.newwindow.select)
self.newwindow.show()
class Ui_ProjSearchForm(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__()
self.setupUi(self)
self.SearchButton.clicked.connect(self.select)
def setupUi(self, ProjSearchForm):
ProjSearchForm.setObjectName("ProjSearchForm")
ProjSearchForm.resize(100, 100)
self.gridLayout = QtWidgets.QGridLayout(ProjSearchForm)
self.gridLayout.setObjectName("gridLayout")
self.SearchButton = QtWidgets.QPushButton(ProjSearchForm)
self.SearchButton.setObjectName("SearchButton")
self.gridLayout.addWidget(self.SearchButton, 0, 2, 1, 1)
def select(self):
print('this is default behavior')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
Signal connections work by passing a reference to a callable, and that reference is an "internal" pointer to that function. Overwriting the name of that function will have absolutely no result.
Take this example:
class Test(QPushButton):
def __init__(self):
super().__init__('Click me!')
self.clicked.connect(self.doSomething)
self.doSomething = lambda: print('bye!')
def doSomething(self):
print('hello!')
The code above will always print "hello!", because you passed the reference to the instance method doSomething that existed at the time of the connection; overwriting it will not change the result.
If you need to create a connection that can be overwritten, you have different possibilities.
Pass the function to the constructor
You can set the function as an optional argument in the __init__ and then connect it if specified, otherwise use the default behavior:
def nextwindow(self):
self.newwindow = Ui_ProjSearchForm(edit_patch(self.newwindow.select))
class Ui_ProjSearchForm(QtWidgets.QWidget):
def __init__(self, func=None):
super().__init__()
self.setupUi(self)
if func is not None:
self.SearchButton.clicked.connect(func)
else:
self.SearchButton.clicked.connect(self.select)
Create a method for the connection
In this case we pass the reference to a specific method that will create the connection, eventually disconnecting any previous connection (remember that signals can be connected to multiple functions, and even the same function multiple times). This is similar to the approach above.
def nextwindow(self):
self.newwindow = Ui_ProjSearchForm()
self.newwindow.setSelectFunc(edit_patch(self.newwindow.select))
class Ui_ProjSearchForm(QtWidgets.QWidget):
def __init__(self, func=None):
super().__init__()
self.setupUi(self)
self.SearchButton.clicked.connect(self.select)
def select(self):
print('this is default behavior')
def setSelectFunc(self, func):
try:
self.SearchButton.clicked.disconnect(self.select)
except TypeError:
pass
self.select = func
self.SearchButton.clicked.connect(self.select)
Use a lambda
As said above, the problem was in trying to overwrite the function that was connected to the signal: even if the connected function is a wrapper, the direct reference for the connection is not actually overwritten.
If you, instead, connect to a lambda that finally calls the instance method, it will work as expected, because the lambda is dynamically computed at the time of its execution and at that time self.select will be a reference to the overwritten function.
def nextwindow(self):
self.newwindow = Ui_ProjSearchForm()
self.newwindow.select = edit_patch(self.newwindow.select)
class Ui_ProjSearchForm(QtWidgets.QWidget):
def __init__(self, func=None):
super().__init__()
self.setupUi(self)
self.SearchButton.clicked.connect(lambda: self.select())
def select(self):
print('this is default behavior')
Some unrelated but still important notes:
You should never edit pyuic generated files, nor try to merge their code into your script or mimic their behavior. Instead, follow the official guidelines about using Designer.
Passing a new QWidget instance as argument is pointless (other than wrong and potentially dangerous); if you want to create a new window for the new widget, just avoid any parent at all, otherwise use QDialog for modal windows.
Only classes and constants should have capitalized names, everything else should be named starting with lowercase letters (this includes object names created in Designer); read more about this and other important topics in the official Style Guide for Python Code.
Ok I think I've figured out a solution (in the MRE posted in question). There's some shenanigans that go on in the back ground once you connect a button in the UI to a function. It's not a "live" connection like in the a.test example, so edits to the function later don't have an impact on how the button functions.
So, if we replace
self.newwindow.select = edit_patch(self.newwindow.select)
with
self.newwindow.SearchButton.clicked.disconnect()
self.newwindow.select = edit_patch(self.newwindow.select)
self.newwindow.SearchButton.clicked.connect(self.newwindow.select)
we suddenly get the desired behavoir from the button. This was entirely too frustrating.
I am building an API that I would like to use in applications using pyqt and other guis.
If a programmer is using a gui other that Qt I don't want her to have to import pyqt etc.
My problem is that I use a task to subscribe to messages and pass them through to the main task.
I accomplish this by passing a parameter (qt) which is either True or False depending on what gui we are implementing.
I then create a class that generates a SubscriberParent class of either a QtCore.QThread or a Thread. The ultimate SubscriberThread
is of type SubscriberThreadParent.
The main thread either polls the interTaskQueue or uses Qt's signals and slots to process the message.
class Gui(object):
def __init__(self, qt):
if qt:
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, pyqtSignal
class SubScriberThreadParent(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
else:
from threading import Thread
class SubScriberThreadParent(Thread):
def __init__(self):
Thread.__init__(self)
class SubscriberThread(Gui.SubScriberThreadParent):
def __init__(self, qt, dsParam, SubScriberThreadParent):
if qt:
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, pyqtSignal
pubIn = pyqtSignal(str, str)
SubScriberThreadParent.__init__(self)
self.interTaskQueue = dsParam.interTaskQueue
.
.
.
.
#
# Pass the message on to the main application
#
def alertMainApp(self, bodyTuple):
if self.qt:
btz = '{0}'.format(bodyTuple)
self.pubIn.emit(btz)
LOGGER.info("Emitted Alert ")
else:
if self.interTaskQueue != None:
self.interTaskQueue.put(bodyTuple) # Pass it to the main thread
LOGGER.info("Queued Alert.")
else:
LOGGER.error("No Inter-task data transfer method available to the subscriber task!")
The error I get with this approach is: "AttributeError: type object 'Gui' has no attribute 'SubScriberThreadParent'"
How can I make this work?
I also would like to know the scope of the conditional imports.
In order for one class to inherit from Gui.SubScriberThreadParent you will have to have created an instance of Gui and assigned something to a class variable.
Consider this code:
class Gui:
def __init__(self, qt):
if qt:
class Foo:
att = 'Foo'
Gui.Parent = Foo
else:
class Bar:
att = 'Bar'
Gui.Parent = Bar
def makeThread():
class Thread(Gui.Parent):
def __init__(self):
self.att = Gui.Parent.att
return Thread()
def main():
Gui(False)
t = makeThread()
print(t.att)
main()
Output:
With Gui(False) as above, the output is:
Bar
When changed to Gui(True), the output is:
Foo
I have written the following code that parses a text file, breaks it into tokens and inserts these tokens into the database. I want to show the current status of the process using the progress bar but the following code isn't working.
I wrote the following code based on this How to connect pyqtSignal between classes in PyQT
yast_gui.py
class YastGui(QtGui.QMainWindow):
incrementTokenSignal = QtCore.pyqtSignal(int)
...
def __init__(self):
self.incrementTokenSignal.connect(self.increment_token_count)
...
def increment_token_count(self, val):
msg = "{}/{}".format(val, self.total_db_records)
self.ui.records_processed_value_label.setText(msg)
yast.py
class LogFile(object):
def __init__(self, file_path, YastGui_object):
super(LogFile, self).__init__()
# Gui object
self.gui = YastGui_object
self.total_db_records = 0
...
def tokenize(self):
for i, record in enumerate(myfile):
...
self.gui.incrementFilterSignal.emit(i + 1)
settings.session.commit()
According to this PYQT and progress Bar during Long Process, I should create QTheads to deal with the progress bar but I'm not sure on how to do it.
Here is the entire Gui file and main file.
I found the solution to my problem here Change the value of the progress bar from a class other than my GUI class PyQt4.
The trick is to pass the progressBar object to the function as a parameter and then use progressBar.setValue() inside that function.
I have a GUI created by Qt Designer. One element ("screenshot") is used as a placeholder for another class definition. It's Python code translation looks like so:
...
class Ui_endomess(object):
def setupUi(self, endomess):
...
self.screenshot = screenshot(self.centralwidget)
...
from screenshot import screenshot
The "screenshot" class looks like so:
...
class screenshot(QGraphicsView):
...
def some_function(self):
...
Both are used by a main script with the following stucture:
...
from endomess_ui import Ui_endomess
...
class endomess(QMainWindow, Ui_endomess):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
...
def main(argv):
app = QApplication(argv, True)
wnd = endomess()
wnd.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main(sys.argv)
Of course, I can manipulate GUI object from within the "endomess" class like so:
self.calibrateButton.setEnabled(True)
What I want to do is manipulate GUI elements from a function within the "screenshot" class. I messed around with "global" calls, but I just don't know how to do it. Is this possible?
Thanks in advance for all help!
The qt-way would be to define a signal in your screenshot class and connect that to a slot in your endomess class which can then perform the modifications.
From within the screenshot class you may also be able to access the object as self.parent().parent() (self.parent() should be the centralWidget, and that's parent the endomess instance), but this may break if something in your hierarchy changes.
Μy Mainclass creates a simple QmainWindows like this:
class mcManageUiC(QtGui.QMainWindow):
def __init__(self):
super(mcManageUiC, self).__init__()
self.initUI()
def initUI(self):
self.show()
And at the end of my file I launch it like this:
def main():
app = QtGui.QApplication(sys.argv)
renderManagerVar = mcManageUiC()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
My problem is that each time i source it, it launches a new window.
I would like to know if there is a way to detect existence of previous class instance in my script (so that I close the old one or avoid launching a new one), or any other solutions?
Also, when compiling my code with py2exe, same problem with my .exe file on Windows; it launchs a new window every time. Could i add something in the setup.py for Windows to not act like this?
Is it possible, if yes then how?
Note: I'm using Windows 7 64bit compiling with eclipse.
There are a couple ways to do this, you can use a Class attribute to store all the instances -- If you do it this way, you may want to store them as weak references via the weakref module to prevent issues with garbage collecting:
class MyClass(object):
_instances=[]
def __init__(self):
if(len(self._instances) > 2):
self._instances.pop(0).kill() #kill the oldest instance
self._instances.append(self)
def kill(self):
pass #Do something to kill the instance
This is a little ugly though. You might also want to consider using some sort of Factory which (conditionally) creates a new instance. This method is a little more general.
import weakref
class Factory(object):
def __init__(self,cls,nallowed):
self.product_class=cls #What class this Factory produces
self.nallowed=nallowed #Number of instances allowed
self.products=[]
def __call__(self,*args,**kwargs):
self.products=[x for x in self.products if x() is not None] #filter out dead objects
if(len(self.products) <= self.nallowed):
newproduct=self.product_class(*args,**kwargs)
self.products.append(weakref.ref(newproduct))
return newproduct
else:
return None
#This factory will create up to 2 instances of MyClass
#and refuse to create more until at least one of those
#instances have died.
factory=Factory(MyClass,2)
i1=factory("foo","bar") #instance of MyClass
i2=factory("bar","baz") #instance of MyClass
i3=factory("baz","chicken") #None
You can limit the number of instances you want to create in your code just by adding a counter:
class A(object):
ins = 0 # This is a static counter
def __init__(self):
if A.ins >= 1: # Check if the number of instances present are more than one.
del self
print "Failed to create another instance" #if > 1, del self and return.
return
A.ins += 1
print "Success",str(self)
Try running via:
lst = []
for i in range(1,101):
a=A()
lst.append(a)
you could monopolize a socket
import socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except:
"Network Error!"
s.settimeout(30)
try:
s.connect(('localhost' , 123))
except:
"could not open...already in use socket(program already running?)"
no idea if this is a good method but I have used it in the past and it solves this problem
this was designed to prevent launching a program when it was already running not from launching a new window from within a single script that is spawning several windows...
Use a class variable:
class mcManageUiC(QtGui.QMainWindow):
singleton = None
def __init__(self):
if not mcManageUiC.singleton: #if no instance yet
super(mcManageUiC, self).__init__()
self.initUI()
...
mcManageUiC.singleton = self
else:
...
def initUI(self):
self.show()