I am a student who is new to Python. For my project, I am tasked to integrate a python program onto my GUI interface at the top left corner of it.
Basically, my program is ran using the CMD terminal with python OSM_extension_FINALIZED.py.
To run my GUI, all I have to do is run this command ./spf.sh and my GUI will appear.
What I am trying to achieve to integrate OSM_extension_FINALIZED.py into the GUI program. Such that the GUI can display a live map of the GPS location trackers at the top left corner as seen in the picture below. As of now, the picture at the top left is only a placeholder.
My GUI code can be viewed here,
The basic idea behind OSM_extension_FINALIZED.py is to process coordinate data output by a GPS receiver and display a corresponding map with a tracker on it to show the position of the receiver. The map has been downloaded onto the workstation. Thus, there is no need for an internet connection.
Below is the OSM_extension_FINALIZED.py,
Basically, my program is ran using the CMD terminal with python OSM_extension_FINALIZED.py.
To run my GUI, all I have to do is run this command ./spf.sh and my GUI will appear.
What I am trying to achieve to integrate OSM_extension_FINALIZED.py into the GUI program. Such that the GUI can display a live map of the GPS location trackers at the top left corner as seen in the picture below. As of now, the picture at the top left is only a placeholder.
My GUI code can be viewed here,
#!/usr/bin/env python #------------------------------------------------------------------------------- # Camera display using PyQt and PiCamera # Copyright (c) 2019 Ver 2.0 #------------------------------------------------------------------------------- VERSION = "SPF_Display v2.00" import sys, time, threading from PyQt5 import QtCore from PyQt5.QtCore import QTimer, QPoint, pyqtSignal, QRect, QSize from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QLabel,QPushButton,QMessageBox from PyQt5.QtWidgets import QWidget, QAction, QVBoxLayout, QHBoxLayout from PyQt5.QtGui import QFont, QPainter, QImage, QTextCursor, QPixmap, QIcon from picamera.array import PiRGBArray from picamera import PiCamera import Queue as Queue #------------------------------------------------------------------------------- # Global Settings #------------------------------------------------------------------------------- IMG_SIZE = 640,480 # Fix by the PiCapture SD1 device TEXT_FONT = QFont("Courier", 16) DISP_SCALE = 1 # Scaling factor for display image DISP_MSEC = 50 # Delay between display cycles image_queue = Queue.Queue() # Queue to hold images capturing = True # Flag to indicate capturing #------------------------------------------------------------------------------- # Grab images from the camera (separate thread) #------------------------------------------------------------------------------- def grab_images(queue): camera=PiCamera() camera.framerate= 60 camera.resolution= (IMG_SIZE[0], IMG_SIZE[1]) rawCapture=PiRGBArray(camera,size=(IMG_SIZE[0], IMG_SIZE[1])) time.sleep(0.5) try: while capturing: camera.capture(rawCapture, format='rgb', use_video_port=True) image=rawCapture.array if image is not None and queue.qsize() < 2: queue.put(image) else: time.sleep(DISP_MSEC / 1000.0) rawCapture.truncate(0) finally: camera.close() #------------------------------------------------------------------------------- # Image widget #------------------------------------------------------------------------------- class ImageWidget(QWidget): def __init__(self, parent=None): super(ImageWidget, self).__init__(parent) self.image = None def setImage(self, image): self.image = image self.setMinimumSize(image.size()) self.update() def paintEvent(self, event): qp = QPainter() qp.begin(self) if self.image: qp.drawImage(QPoint(0, 0), self.image) qp.end() #------------------------------------------------------------------------------- # Minimap image widget #------------------------------------------------------------------------------- class MapWidget(QLabel): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) pixmap = QPixmap('/home/ubuntu/catkin_ws/src/controller/config/map.png') self.setMinimumSize(100, 140) self.setPixmap(pixmap) #------------------------------------------------------------------------------- # Main window #------------------------------------------------------------------------------- class MyWindow(QMainWindow): text_update = pyqtSignal(str) # Create main window def __init__(self, parent=None): QMainWindow.__init__(self, parent) # Window settings self.setWindowTitle(VERSION) flags = QtCore.Qt.WindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) self.setWindowFlags(flags) # Widgets settings self.central = QWidget(self) self.textbox = QTextEdit(self.central) self.textbox.setFont(TEXT_FONT) self.textbox.setMinimumSize(300, 50) self.text_update.connect(self.append_text) sys.stdout = self print("Image size %u x %u" % IMG_SIZE) self.mainvlayout = QVBoxLayout() self.vlayout = QVBoxLayout() # Window layout self.displays = QHBoxLayout() self.dispMap = MapWidget(self) # Map display self.vlayout.addWidget(self.dispMap) self.buttonStart = QPushButton(' Start') #Start Button self.vlayout.addWidget(self.buttonStart) self.buttonStart.clicked.connect(self.startClicked) self.buttonStart.show() self.buttonStart.setIcon(QIcon(QPixmap('/home/ubuntu/catkin_ws/src/controller/config/start.png'))) self.buttonStart.setIconSize(QtCore.QSize(60,60)) self.buttonStart.setStyleSheet("text-align: left;font-size: 24px") self.buttonReturn = QPushButton(' Return') #Return button self.vlayout.addWidget(self.buttonReturn) self.buttonReturn.clicked.connect(self.returnclicked) self.buttonReturn.show() self.buttonReturn.setIcon(QIcon(QPixmap('/home/ubuntu/catkin_ws/src/controller/config/return.jpg'))) self.buttonReturn.setIconSize(QtCore.QSize(60,60)) self.buttonReturn.setStyleSheet("text-align: left;font-size: 24px") self.buttonReturn.setEnabled(False) self.buttonStop = QPushButton(' STOP') #Stop button self.vlayout.addWidget(self.buttonStop) self.displays.addLayout(self.vlayout) self.mainvlayout.addLayout(self.displays) self.buttonStop.clicked.connect(self.stopclicked) self.buttonStop.show() self.buttonStop.setIcon(QIcon(QPixmap('/home/ubuntu/catkin_ws/src/controller/config/stop.jpg'))) self.buttonStop.setIconSize(QtCore.QSize(60,60)) self.buttonStop.setStyleSheet("text-align: left; font-size: 24px;") self.buttonStop.setEnabled(False) self.mainvlayout.addWidget(self.textbox) #Textbox display self.disp = ImageWidget(self) self.displays.addWidget(self.disp) self.central.setLayout(self.mainvlayout) self.setCentralWidget(self.central) # self.mainMenu = self.menuBar() # Menu bar # exitAction = QAction('&Exit', self) # exitAction.setShortcut('Ctrl+Q') # exitAction.triggered.connect(self.close) # self.fileMenu = self.mainMenu.addMenu('&File') # self.fileMenu.addAction(exitAction) self.w = None print "To set current location as Home, select Start" def startClicked(self): QWidget.__init__(self) buttonReply = QMessageBox.question(self, 'start', " Current location will be set as Home. Continue?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) self.text_update.connect(self.append_text) ### sys.stdout = self ### if buttonReply == QMessageBox.Yes: print('Drive robot to desired destination. To return Home, select Return') self.buttonStart.setEnabled(False) self.buttonReturn.setEnabled(True) self.buttonStop.setEnabled(True) self.buttonStop.setStyleSheet("text-align: left;background-color: red; font-size: 36px; color: white") else: print('Do nothing.') def returnclicked(self): QWidget.__init__(self) buttonReply = QMessageBox.question(self, 'Return to Home', "Robot will return Home. Continue?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) self.text_update.connect(self.append_text) ### sys.stdout = self ### if buttonReply == QMessageBox.Yes: print('Robot is returning home') self.buttonStart.setEnabled(False) self.buttonReturn.setEnabled(False) self.buttonStop.setEnabled(True) self.buttonStop.setStyleSheet("text-align: left;background-color: red; font-size: 36px; color: white") else: print('Do nothing.') def stopclicked(self): QWidget.__init__(self) self.text_update.connect(self.append_text) ### sys.stdout = self ### print('Stop.') self.buttonStart.setEnabled(True) self.buttonReturn.setEnabled(False) self.buttonStop.setEnabled(False) self.buttonStop.setStyleSheet("text-align: left; font-size: 36px;") # Handle sys.stdout.write: update text display def write(self, text): self.text_update.emit(str(text)) def flush(self): pass # Append to text display def append_text(self, text): cur = self.textbox.textCursor() # Move cursor to end of text cur.movePosition(QTextCursor.End) s = str(text) while s: head,sep,s = s.partition("\n") # Split line at LF cur.insertText(head) # Insert text at cursor if sep: # New line if LF cur.insertBlock() self.textbox.setTextCursor(cur) # Update visible cursor # Start image capture & display def start(self): self.timer = QTimer(self) # Timer to trigger display self.timer.timeout.connect(lambda: self.show_image(image_queue, self.disp, DISP_SCALE)) self.timer.start(DISP_MSEC) self.capture_thread = threading.Thread(target=grab_images, args=(image_queue, )) # must pass as tuples ;) the magic self.capture_thread.start() # Thread to grab images # Fetch camera image from queue, and display it def show_image(self, imageq, display, scale): if not imageq.empty(): image = imageq.get() if image is not None and len(image) > 0: self.display_image(image, display, scale) # Display an image, reduce size if required def display_image(self, img, display, scale=1): disp_size = img.shape[1]//scale, img.shape[0]//scale disp_bpl = disp_size[0] * 3 #if scale > 1: # img = cv2.resize(img, disp_size, # interpolation=cv2.INTER_CUBIC) qimg = QImage(img.data, disp_size[0], disp_size[1], disp_bpl, QImage.Format_RGB888) display.setImage(qimg) # Window is closing: stop video capture def closeEvent(self, event): global capturing capturing = False self.capture_thread.join() #------------------------------------------------------------------------------- if __name__ == '__main__': app = QApplication(sys.argv) win = MyWindow() win.showFullScreen() # Occupied the full screen win.start() sys.exit(app.exec_()) #------------------------------------------------------------------------------- #EOF #-------------------------------------------------------------------------------I know that I have to edit from line 75 to 80, but I just do not know how to start I have zero knowledge on python programming language.
The basic idea behind OSM_extension_FINALIZED.py is to process coordinate data output by a GPS receiver and display a corresponding map with a tracker on it to show the position of the receiver. The map has been downloaded onto the workstation. Thus, there is no need for an internet connection.
Below is the OSM_extension_FINALIZED.py,
from mpl_toolkits.basemap import Basemap import matplotlib.pyplot as plt from matplotlib.patches import Circle import numpy as np import datetime import shutil import sys import threading import time import math import urllib2 import StringIO import requests import serial from pynmea import nmea import pynmea2 from io import BytesIO from PIL import Image i=0 alt = 0 def deg2num(lat_deg, lon_deg, zoom): #converts lat_deg and lon_deg to tile number lat_rad = math.radians(lat_deg) n = 2.0 ** zoom xtile = int((lon_deg + 180.0) / 360.0 * n) ytile = int((1.0 - math.log(math.tan(lat_rad) + (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n) return (xtile, ytile) def num2deg(xtile, ytile, zoom): #converts tile numer to lat_deg and lon_deg n = 2.0 ** zoom lon_deg = xtile / n * 360.0 - 180.0 lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n))) lat_deg = math.degrees(lat_rad) return (lat_deg, lon_deg) def getImageCluster(lat_deg, lon_deg, delta_lat, delta_long, zoom): headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"} smurl = r"http://a.tile.openstreetmap.org/{0}/{1}/{2}.png" xmin, ymax = deg2num(lat_deg -0.0025, lon_deg -0.002, zoom) #Adjust lat/lon_deg to change the position of main tile xmax, ymin = deg2num(lat_deg + delta_lat, lon_deg +0.001 + delta_long, zoom) bbox_ul = num2deg(xmin, ymin, zoom) bbox_ll = num2deg(xmin, ymax + 1, zoom) print '(UPPER LEFT), (LOWER LEFT)' print bbox_ul, bbox_ll print ' ' bbox_ur = num2deg(xmax + 1, ymin, zoom) bbox_lr = num2deg(xmax + 1, ymax + 1, zoom) print '(UPPER RIGHT), (LOWER RIGHT)' print bbox_ur, bbox_lr print ' ' Cluster = Image.new('RGB',((xmax-xmin+1)*256-1,(ymax-ymin+1)*256-1) ) for xtile in range(xmin-1, xmax+1): for ytile in range(ymin-1, ymax+1): try: imgurl = smurl.format(zoom, xtile, ytile) print("Opening: " + imgurl) tile = Image.open('SG_'+str(xtile)+'_'+str(ytile)+'.png') #SG_(column)_(row) Cluster.paste(tile, box = ((xtile-xmin)*256 , (ytile-ymin)*255)) except: print("Couldn't open image") tile = None return Cluster, [bbox_ll[1], bbox_ll[0], bbox_ur[1], bbox_ur[0]] def check_serial(): init_serial() def init_serial(): #To configure and open the serial port global ser ser = serial.Serial( #port = '/dev/ttyACM0', #Use this for Linux port = 'COM10', #Use this for Windows baudrate = 38400, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, ) ser.timeout = 1 ser.isOpen() print 'Opening ' + ser.name print ' ' thread() def stream_serial(): line = ser.readline() # print "This is to annoy you." def thread(): #threads - run idependent of main loop thread1 = threading.Thread(target = save_raw) #saves the raw GPS data over serial while the main program runs # thread2 = threading.Thread(target = user_input) #optional second thread thread1.start() # thread2.start() def save_raw(): #this fxn creates a txt file and saves only GPGGA sentences while 1: line = ser.readline() line_str = str(line) if(line_str[4] == 'G'): # $GPGGA if(len(line_str) > 50): # open txt file and log data f = open('nmea.txt', 'a') try: f.write('{0:}'.format(line_str)) finally: f.close() else: stream_serial() def position(): f1 = open('temp.txt', 'w') f1.truncate() shutil.copyfile('nmea.txt', 'temp.txt') f1.close() f1 = open('temp.txt', 'r') try: for line in f1: if(line[2] == 'N'): # forth character in $GNGGA if(len(line) > 50): print line msg = pynmea2.parse(line) lat_deg = msg.latitude #msg is the object, latitude is the attribute lon_deg = msg.longitude #msg is the object, longitude is the attribute #shows that we are reading through this loop print lat_deg print lon_deg finally: f1.close() delta_lat, delta_long, zoom = 0.0012, 0.0015, 18 #Adjust delta_lat/lon to change the size of map a, bbox = getImageCluster(lat_deg, lon_deg, delta_lat, delta_long, zoom) fig = plt.figure(figsize=(10, 10)) ax = plt.subplot(111) m = Basemap( llcrnrlon=bbox[0], llcrnrlat=bbox[1], urcrnrlon=bbox[2], urcrnrlat=bbox[3], projection='merc', ax=ax ) # list of points to display (long, lat) ls_points = [m(x,y) for x,y in [(lon_deg, lat_deg)]] #(base_lon,base_lat)]] m.imshow(a, interpolation='none', origin='upper') # origin = 'upper' reverse the Vertical axis direction. ax.scatter([point[0] for point in ls_points], [point[1] for point in ls_points], alpha = 0.9, color = 'r') print ' ' print '(c) OpenStreetMap contributors' print ' ' plt.show() check_serial() while 1: position()