-1

I am developing a small program that will communicate to serial port. I have developed a GUI using pyQt5.

First of all, the program will read & list the available serial ports. When the user will select any particular port on the list & click on a "Open port" button,the corresponding port will be opened. The Button text will now be changed to "Close port" so the user can close it after use. I am using a state variable to record the current state of the port(portOpen).

The problem happens when the button is clicked & I am trying to save the port open status (1 = Open, 0 = Close) inside the variable by writting portOpen = 1, the program is getting reset. Please take a look where it is getting wrong!

My code is as follows:

import PyQt5
from PyQt5 import QtCore, QtGui, uic, QtWidgets
from PyQt5.QtWidgets import QWidget
import serial
import serial.tools.list_ports

portOpen = 0 #variable to store the port open/close status.If open,value         will be 1
comPorts = list() #create a list named comPorts

mySer = serial.Serial()
mySer.baudrate = 9600
mySer.bytesize = 8
mySer.parity = 'N'
mySer.stopbits = 1
ports = serial.tools.list_ports.comports()

#read the available com ports & save to a list so the list will be like -    [COM0,COM1,COM2---]
def ReadComPorts():
    comPorts.clear() #clear the values of the list
    for port, desc, hwid in sorted(ports):
        comPorts.append(("{}".format(port)))

#These below mentioned part is used to adjust the view of the app to high resolution DPI of windows
if hasattr(QtCore.Qt, 'AA_EnableHighDpiScaling'):
    PyQt5.QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)

if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'):
    PyQt5.QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)

#pyQt5 generated code
class Ui_MainWindow(object):

    def setupUi(self, MainWindow):
#read the com ports first
        ReadComPorts()
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(273, 200)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(10, 10, 91, 121))
        self.listWidget.setObjectName("listWidget")

#Now,add the read com ports into the list         
        for x in range(len(comPorts)):
            self.listWidget.addItem(comPorts[x])

#When,the an item on list will be clicked,call the item_click function
        self.listWidget.itemClicked.connect(self.item_click)

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(30, 150, 56, 17))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

#When the pushbutton will be clicked,call portOpen/close function        
        self.pushButton.clicked.connect(self.PortOpenClose)

    def item_click(self, item):
        comPorts[0] = str(item.text()) #Store the selected port to comPorts  0 location
        print(comPorts[0])

#Open/close the port depending on the current port status
    def PortOpenClose(self):
        if(not(portOpen)):
            try:
                mySer.port = comPorts[0]
                mySer.open()
                self.pushButton.setText("Close Port")
                print('Port opened')
                print(portOpen)
                portOpen = 1 #this line is creating forcing the whole program to close
                                #If i omit this line,port is getting opened properly          
            except:
                print('Error occured')                



    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Open Port"))


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_())
Dmitrii Sidenko
  • 660
  • 6
  • 19
S.Das
  • 1
  • 1
  • 1
    You need to show that you're using a global variable, you can't just assign a value to it. Add `global portOpen` at the beginning of the function and it should work. – h4z3 Jun 10 '19 at 09:13
  • Thanks a ton...that solved my issue!! – S.Das Jun 10 '19 at 09:27
  • Added a full answer explaining *why* it happens. :) – h4z3 Jun 10 '19 at 09:38
  • Another thing I would like to ask.This is a very stupid question basically.The question is,where to put the main code that will be placed in a loop.For say,now i want to read data & show it continiously if the port is open. – S.Das Jun 10 '19 at 09:40
  • @S.Das Do not use global variables, the solution is to make portOpen member of the class, for it removes `portOpen = 0`, and add `self.portOpen = 0` in setupUi, and then changes `portOpen` to `self.portOpen` – eyllanesc Jun 10 '19 at 10:04
  • Thanks a lot.I tried this...This is also working!! – S.Das Jun 10 '19 at 10:19

1 Answers1

0

I already answered in the comments, but I didn't have time to post a long explanation why my suggestion worked:

The problem is with how Python handle global and local variables.

Basically, global variables should be avoided if they are not necessary. In many languages it's too easy to accidentally change the value of a global variable, so Python makes us explicitly state that we want to change it - with global keyword.

If global variable was only read from, the code worked because Python looked for the variable in order: not local? check further... and so on, until it hit global scope.

But with added assignment (without global keyword), Python saw it in a local scope and assumed the variable is local in the whole function - and made it fail because the variable was read from before the assignment - it was print(portOpen) that failed, not the assignment itself(!). (Thankfully they were so close together that the error message accidentally helped.)

global portOpen shows the interpreter that the assignment is not creating a new local variable, so all reading and assigning started to work correctly.

h4z3
  • 5,265
  • 1
  • 15
  • 29