2

I have a python app with pyqt5 that change mac address in linux machine. I have two files. The first file is the main python program and second file is gui window with empty line that you have to fill in mac address then press ok.

Actually when I fill the mac address it say NameError: name 'connection' is not defined I cannot pass data from second file to main python file. In mac_2.py file there is a global var with name connection that cannot read from second file mac_specific.py. How can I do that?

I reduce much code from two files, because it is easiest for you to help me.

mac_2.py

#!/usr/bin/python3
import os
import subprocess
from PyQt5 import QtCore, QtGui, QtWidgets

from mac_specific import *

class Ui_MainWindow(object):
    def specificWindow(self):
        self.window = QtWidgets.QDialog()
        self.ui = Ui_macSpecific()
        self.ui.setupUi(self.window, self) # <--- assign MainWindow as parent to second window
        self.window.show()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setFixedSize(430, 500)

# ...

        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget, clicked = lambda: self.specificWindow())

# ...

    def select(self):
        global connection

        if self.radioButton.isChecked():
            self.wired = config.get('ethernet', 'wired')
            self.label.setText(str(self.wired))
            connection = self.wired
            self.mac_address = config.get('ethernet', 'wired_mac_address')
            self.label_2.setText(str(self.mac_address))


        if self.radioButton_2.isChecked():
            self.wireless = config.get('wifi', 'wireless')
            self.label.setText(str(self.wireless))
            connection = self.wireless
            self.mac_address = config.get('wifi', 'wireless_mac_address')
            self.label_2.setText(str(self.mac_address))

mac_specific.py

import subprocess

from PyQt5 import QtCore, QtGui, QtWidgets


# read values from a section
wired = config.get('ethernet', 'wired')
wireless = config.get('wifi', 'wireless')


class Ui_macSpecific(object):
    def setupUi(self, macSpecific, master):
        self.master = master
        self.window = macSpecific


        macSpecific.setObjectName("macSpecific")
        macSpecific.resize(301, 140)
        self.gridLayout = QtWidgets.QGridLayout(macSpecific)

# ...

        self.pushButton = QtWidgets.QPushButton(macSpecific, clicked = lambda: self.clicked_button())


    def clicked_button(self):
        print("submit")
        text = self.lineEdit.text()
        self.master.label_2.setText(text)
        subprocess.run(['pkexec', 'ifconfig', connection, 'down'])
        subprocess.run(['pkexec', 'ip', 'link', 'set', 'dev', connection, 'address', text])
        subprocess.run(['pkexec', 'ifconfig', wired, 'up'])
        self.window.close()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 2
    Why not pass `connection` to the `Ui_macSpecific` object in its `__init__` method, so that you can access the variable as `self.connection`? – Fractalism Mar 11 '23 at 20:40
  • @Fractalism How can I do that? – user21379959 Mar 11 '23 at 20:50
  • In the code provided, `mac_2.py` does not actually have a global variable named connection, because it not defined outside of a function or class. You need to at least put `connection = None` or some equivalent default value at the script level. passing an object would be better than using a global variable though. – nigh_anxiety Mar 11 '23 at 22:45
  • 1
    The fact is that you're supposed to have a main script that manages the logic of your program, while the pyuic files you're using should have ***never had been modified***, as clearly written in the header comments you ignored and removed. Follow the official guidelines about [using Designer](https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html), regenerate those files **and never edit them**, then create a main script that will properly use those classes. And do not use globals. – musicamante Mar 11 '23 at 23:18
  • @nigh_anxiety Do I have to put `connection = None` in mac_2.py file? this var I have to put before `class Ui_MainWindow(object):`? – user21379959 Mar 12 '23 at 09:13
  • @musicamante I make gui app with qt designer in linux. Then I work this file with pycharm. I have to edit this file. How can I make python app without edit the original file that created from qt designer? – user21379959 Mar 12 '23 at 09:17
  • @user21379959 I already and clearly told you how to do it. Did you read and understand what I wrote? Did you even *open* the link in my comment?!? – musicamante Mar 12 '23 at 12:56
  • @musicamante I new in python. I have to see an example to understand how to do it. I opened the link but it has not an clear example. Thank you – user21379959 Mar 12 '23 at 16:48
  • @user21379959 that link has 3 examples, don't try to apply them to your code: *recreate* those examples in order to understand how they work, *then*, after you've understood the concept, try to apply it to your case. Also, follow some [tutorial](https://wiki.python.org/moin/PyQt/Tutorials). – musicamante Mar 12 '23 at 17:34
  • @user21379959 It would have to be declared anywhere outside of the classes within mac_2.py to be available as a global. However, you ideally shouldn't be using a global at all. Instead, you could just save connection as an attribute of the Ui_MainWindow instance `self.connection`, and then reference it from there. It would probably actually be even better as its own object passed into the main window when its created and then modified by the main window. – nigh_anxiety Mar 12 '23 at 18:01
  • @user21379959 As for why to avoid a global, let's say you wanted your app to be able to open multiple connections at once for multiple windows - then you need connection to be a property of the window, otherwise changing the global would modify it for all windows at once. – nigh_anxiety Mar 12 '23 at 18:02
  • @nigh_anxiety Thank you but I am not able to fix it myself. I am sorry I am newbie in python. I have to see an example to fix it or You are able to fix it for me. I am very sorry. Thank you again! – user21379959 Mar 12 '23 at 18:39
  • @musicamante My english is not very well. So, In my question there are different answers. Someone say It is not recommended to use from module import * and you and your example you suggest me to use it. So I have no idea how to solve my problem without an example. I appreciate your time and your help. I am sorry but I cant fix it myself!! – user21379959 Mar 12 '23 at 18:40
  • @user21379959 I've *never* said anything about the usage of imports and wildcards, and the page I linked does not use them either. Besides, that's not an absolute rule. – musicamante Mar 12 '23 at 18:48

3 Answers3

0

Edit -- have you tried defining connection in init() of Ui_MainWindow; then, making the Ui_macSpecific class a child of Ui_MainWindow in order to inherit its attributes. Then you should just be able to call self.connection within Ui_macSpecific.

import mac_2

class Ui_macSpecific(Ui_MainWindow):       
    def clicked_button(self):
        print("submit")
        text = self.lineEdit.text()
        self.master.label_2.setText(text)
        subprocess.run(['pkexec', 'ifconfig', self.connection, 'down'])
        ...

•
•
•

class Ui_MainWindow:
    def __init__(self):
        self.connection = None

    def specificWindow(self):
        self.window = QtWidgets.QDialog()
        if self.connection:
            self.ui = Ui_macSpecific(self.connection)
        ...

    def select(self):
        if self.radioButton.isChecked():
            self.wired = config.get('ethernet', 'wired')
            self.label.setText(str(self.wired))
            self.connection = self.wired
            ...
harriet
  • 540
  • 3
  • 9
0

Just import the other module, in aplain import: it is an object. Then you can access classes, functions and variables defined on it (and variables defined on the module are "global variables") as attributes of the module, with the usual . nottation:

import mac_2

def ZZZZZ(...):
    connection = mac_2.connection
    ...

Note however this is not the most recomended way of doing things - although it is quite valid if, for example, yur project have a "config" module which will hold variables to be used as configuration by all of your other modules.

If your code is written in a way the vale can easily be passed as an argument when calling a function or instantiating a class, do that instead.

You should note you are using from module import * in your code: that should also be avoided, as it confuse readers and the tools alike about the possible origins of a name you are using.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • It doesn't work because in mac_2.py file I have `from mac_specific import *` – user21379959 Mar 11 '23 at 21:24
  • @user21379959 Wildcard imports should be avoided https://stackoverflow.com/questions/30711476/why-is-it-bad-practice-to-use-from-module-import – nigh_anxiety Mar 11 '23 at 22:37
  • So besides wildcard imports you are using circular imports: separate your stuff in extra files so that you have an import hyerarchy which does not have import cycles. While you do not do that, you can insert the `import mac_2` statement inside the _function_ or method that will make use of `mac_2.connection` - that should work, because Python will already have imported both modules in full when the function is called and its body runs. – jsbueno Mar 11 '23 at 23:50
  • @nigh_anxiety Thank you. Do I have to change from main file (mac_2.py) the line `from mac_specific import *` to `import mac_specific`?? – user21379959 Mar 12 '23 at 09:24
  • 1
    you could do something as `import mac_specific as ms` , so from that point on you can use just `ms.` as the prefix instead of `mac_specific.`. Note however that this will make things better to undestand, but won't alone fix circular imports. – jsbueno Mar 12 '23 at 14:38
  • @jsbueno I tried it but I didn't fix it. I am newbie I have not enough knowledge in Python. Thank you – user21379959 Mar 12 '23 at 18:22
0

This is how 2 files can communicate:

file 1: fi1.py

from fi2 import reference,print_var # always import the whole reference, not attributes from it.
print_var()
reference.testvar = 10000
print_var()

file 2: fi2.py

import sys
reference = sys.modules[__name__] # reference to the proper module 
reference.testvar = 0 # create a variable 

def print_var():
    print(reference.testvar)
    # it even works like this: print(testvar), but PyCharm tells you that the variable is not defined 

Output:

0
10000

Applying some changes to your code should resolve the problem. (I haven't tested the code.)

import subprocess

# create the reference 
import sys
reference = sys.modules[__name__] # reference to the proper module 


from PyQt5 import QtCore, QtGui, QtWidgets


# read values from a section


# add them to the reference 
reference.wired = config.get('ethernet', 'wired')
reference.wireless = config.get('wifi', 'wireless')

class Ui_macSpecific(object):
    def setupUi(self, macSpecific, master):
        self.master = master
        self.window = macSpecific


        macSpecific.setObjectName("macSpecific")
        macSpecific.resize(301, 140)
        self.gridLayout = QtWidgets.QGridLayout(macSpecific)

# ...

        self.pushButton = QtWidgets.QPushButton(macSpecific, clicked = lambda: self.clicked_button())


    def clicked_button(self):
        print("submit")
        text = self.lineEdit.text()
        self.master.label_2.setText(text)
        subprocess.run(['pkexec', 'ifconfig', connection, 'down'])
        subprocess.run(['pkexec', 'ip', 'link', 'set', 'dev', connection, 'address', text])
        subprocess.run(['pkexec', 'ifconfig', wired, 'up'])
        self.window.close()
        
########################################        


import os
import subprocess
from PyQt5 import QtCore, QtGui, QtWidgets

from mac_specific import reference

class Ui_MainWindow(object):
    def specificWindow(self):
        self.window = QtWidgets.QDialog()
        self.ui = Ui_macSpecific()
        self.ui.setupUi(self.window, self) # <--- assign MainWindow as parent to second window
        self.window.show()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.setFixedSize(430, 500)

# ...

        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget, clicked = lambda: self.specificWindow())

# ...

    def select(self):
        if self.radioButton.isChecked():
            self.wired = config.get('ethernet', 'wired')
            self.label.setText(str(self.wired))
            
            
            connection = reference.wired
            # or like this, depends on what you want to update
            # reference.wired= connection 
            
            self.mac_address = config.get('ethernet', 'wired_mac_address')
            self.label_2.setText(str(self.mac_address))


        if self.radioButton_2.isChecked():
            self.wireless = config.get('wifi', 'wireless')
            self.label.setText(str(self.wireless))
            connection = reference.wireless
            # or like this, depends on what you want to update
            # reference.wireless = connection 
            # I would recommend adding connection to the class 
            # or don't use it at all, only reference.wireless / reference.wired
            # The most important thing is that one file knows what the other one is doing.
            self.mac_address = config.get('wifi', 'wireless_mac_address')
            self.label_2.setText(str(self.mac_address))
Hans
  • 148
  • 2
  • 7
  • Hans my example is more complicated with much more code. Are you able to give me an example in my specific program to solve it? – user21379959 Mar 14 '23 at 20:00
  • I chose an easy example because in theory, it is always the same, no matter how big your file is. I have added the changes you have to make. I haven't tested the code, so maybe I forgot something. By the way: A while ago, I had the same problem, and this was the only solution that worked for me. :) – Hans Mar 14 '23 at 21:43
  • Hans `NameError: name 'config' is not defined` – user21379959 Mar 16 '23 at 18:26
  • It is not defined in the code snippets that you posted. Could you please post how you defined it? Is it somewhere in mac_specific? – Hans Mar 16 '23 at 18:59
  • Hans I don't know what I have to upload. I reduce much code from my app. Would you like to send you via e-mail or message? – user21379959 Mar 16 '23 at 19:41
  • Let's try it this way first: Delete this line: from mac_specific import * (always try to avoid * ) In your mac_specific.py file, you put: import sys reference_vars = sys.modules[__name __] now you add every variable (that you need in mac_2) to reference_vars for example: reference_vars.config = {'someconfig':'someconfig'} Then you import it like this: from mac_specific import reference_vars Now you are going to use, for example, reference_vars.config.get(....) instead of config.get(). If you set a new value in mac_2, mac_specific will also get the new value (vice-versa also) – Hans Mar 16 '23 at 19:56
  • I did it all as you said! It doesn't work. Would you like to send my code? – user21379959 Mar 16 '23 at 20:00
  • Upload it to GitHub and share the link with us. – Hans Mar 16 '23 at 20:30
  • Hans I upload it. Please take a look https://github.com/user32232/change-mac – user21379959 Mar 18 '23 at 09:10
  • Don't comment config out, it is not defined anymore, and will raise an Exception. Those lines need to stay. ##from configparser import ConfigParser # instantiate ##config = ConfigParser() ..... Imagine reference = sys.modules[__name__] as a container to communicate between files. The advantage is that you don't need to change much of your code. Like I said, take out all the global stuff and the import * stuff, add everything that you need to reference (your container), and import it. Run the first code snipped that I posted to get an idea of how it works. – Hans Mar 18 '23 at 14:55
  • Here is an example that helps: https://github.com/hansalemaos/threadingbatch/blob/main/__init__.py I define: _module = sys.modules[__name__] _module.results = nested_dict() _module.results["done"] = False When import threadingbatch I can access the results using threadingbatch.results - and when I update them in either file, they change everywhere. – Hans Mar 18 '23 at 15:09
  • Hans I did exactly as you said. It doesn't work. Are you sure for code? Please look at the error ` raise NoSectionError(section) from None configparser.NoSectionError: No section: 'ethernet' ` – user21379959 Mar 18 '23 at 16:18
  • Hans I made github for you. I didn't have. As I said I made changes that you said. but now there is an error. Can you help? – user21379959 Mar 19 '23 at 07:14