# Online Python compiler (interpreter) to run Python online. # Write Python 3 code in this online editor and run it. # WeatherAppClient.py code: - # This Python file uses the following encoding: utf-8 import sys from PySide6.QtCore import Slot, QThreadPool, QSize, QResource, Qt, QMargins from PySide6.QtGui import QIcon, QPainter, QFont, QFontDatabase from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis from network import UDPReceiver from ui_weather_app_ui import Ui_WeatherAppUi from ui_parameter import Ui_parameter from ui_settings_form import Ui_SettingsForm from ui_parameter_settings import Ui_ParameterSettings from SideMenu import SideMenu from config import Config from app_signals import AppSignals from database import Database import datetime # Important: # I need to run the following command to generate the ui_form.py file # pyside6-uic form.ui -o ui_form.py, or # pyside2-uic form.ui -o ui_form.py # For compiling resources: # pyside6-rcc resources.qrc -o resources.py class WeatherAppUI(Ui_WeatherAppUi): def __init__(self, mainWindow, config): super().__init__() self.mainWindow = mainWindow self.config = config self.parameters = {} # Create all required widgets self.setupUi(mainWindow) # Set up the pages self.pages = { "Home": { "icon": ":/icons/icons/home.svg", "slot": self.showPage, "page": self.home, }, "Settings": { "icon": ":/icons/icons/settings-5-line.svg", "slot": self.showPage, "page": SettingsForm(self.config) }, } # Create the side menu self.sideMenu = SideMenu(self.pages, 400) # Add the parameters to "Home" page for paramName, paramConfig in self.config["parameters"].items(): self.parameters[paramName] = Parameter(paramConfig) self.parameterListLayout.addWidget(self.parameters[paramName]) # Create settings page self.settingsForm = SettingsForm(self.config) self.widgetStack = QStackedLayout(self.bodyContentLayout) self.widgetStack.setStackingMode(QStackedLayout.StackAll) # Add the side menu to the top of the stack self.widgetStack.addWidget(self.sideMenu) # Add each page below it for pageInfo in self.pages.values(): self.widgetStack.addWidget(pageInfo["page"]) # Close the menu which was open by default self.sideMenu.toggle() self.setupSignalsAndSlots() def addParameter(self, parameter): self.parameterListLayout.addWidget(parameter) def showPage(self, pageName): self.widgetStack.setCurrentWidget(self.pages[pageName]["page"]) self.widgetStack.setCurrentWidget(self.sideMenu) self.sideMenu.toggle() def setupSignalsAndSlots(self): self.menuButton.clicked.connect(self.sideMenu.toggle) class Graph(QChart): def __init__(self, xWidth, yMin, yMax, name): super().__init__() self.name = name self.series = QLineSeries() self.xWidth = xWidth self.xMin, self.xMax = 0, xWidth - 1 self.xSeries = QLineSeries() # Set up the axes and their appearence self.xAxis = QValueAxis() self.yAxis = QValueAxis() self.xAxis.setRange(self.xMin, self.xMax) self.yMin, self.yMax = yMin, yMax self.yAxis.setRange(yMin, yMax) self.addAxis(self.xAxis, Qt.AlignBottom) self.addAxis(self.yAxis, Qt.AlignLeft) self.addSeries(self.series) self.series.attachAxis(self.xAxis) self.series.attachAxis(self.yAxis) self.xAxis.setTickCount(6) self.yAxis.setTickCount(3) robotoRegularId = QFontDatabase.addApplicationFont(":/fonts/fonts/Roboto-Black.ttf") robotoRegularFamily = QFontDatabase.applicationFontFamilies(robotoRegularId)[0] RobotoRegular = QFont(robotoRegularFamily) self.yAxis.setLabelsFont(RobotoRegular) self.xAxis.setLabelsFont(RobotoRegular) self.legend().hide() self.setMargins(QMargins(0, 0, 0, 0)) self.chartView = QChartView(self) self.chartView.setRenderHint(QPainter.Antialiasing) def setValue(self, x, y): if x >= 0: # x axis negative overflow is ignored # Handle x axis positive overflow if x > self.xMax: self.xMin += (x - self.xMax) self.xMax = x self.xAxis.setRange(self.xMin, self.xMax) # Handle y axis overflow if y < self.yMin: self.yMin = y if y > self.yMax: self.yMax = y + ((y-self.yMin) * 0.1) # Leave some margin up top self.yAxis.setRange(self.yMin, self.yMax) self.series.append(x, y) newSeries = QLineSeries() for point in self.series.points(): newSeries.append(point) self.removeAllSeries() self.addSeries(newSeries) self.series = newSeries self.series.attachAxis(self.xAxis) self.series.attachAxis(self.yAxis) def view(self): return self.chartView class Parameter(Ui_parameter, QWidget): def __init__(self, paramConfig): super().__init__() self.setupUi(self) self.paramConfig = paramConfig self.loadConfig() self.setupGraph() AppSignals.dataReceived.connect(self.displayData) AppSignals.reloadApp.connect(self.loadConfig) @Slot() def loadConfig(self): paramConfig = self.paramConfig self.icon = QIcon(paramConfig['icon']) self.paramName = paramConfig["display_name"] self.paramUnit = paramConfig["unit"] self.paramNameButton.setIcon(self.icon) self.paramNameButton.setIconSize(QSize(32, 32)) self.paramNameButton.setText(self.paramName) self.paramUnitLabel.setText(self.paramUnit) self.paramConfig = paramConfig def setupGraph(self): self.graph = Graph( 50, \ float(self.paramConfig.min_limit), \ # float(self.paramConfig.max_limit), \ 1.0, # Because the graph will self adjust self.paramName ) self.paramGraphLayout.addWidget(self.graph.view()) @Slot() def displayData(self, dataDict): if self.paramConfig.file_key in dataDict: value = float(dataDict[self.paramConfig.file_key]) # Display the value in LCD Display self.paramValueDisplay.display(value) # Display the value in the Graph self.graph.setValue(x=dataDict["recordNum"], y = value) class ParameterSettings(Ui_ParameterSettings, QWidget): def __init__(self, config): self.config = config super(ParameterSettings, self).__init__() self.iconPrefix = ":/icons/icons" self.setupUi() def setupUi(self): super().setupUi(self) table = self.parameterSettingsTable paramConfig = self.config.parameters # Add the column names firstParam, firstParamSettings = list(paramConfig.items())[0] colHeaders = [ key.replace("_", " ").title() for key in firstParamSettings.keys()] + ["Remove"] table.setColumnCount(len(colHeaders)) table.setHorizontalHeaderLabels(colHeaders) header = table.horizontalHeader() header.setSectionResizeMode(QHeaderView.ResizeToContents) header.setSectionResizeMode(7, QHeaderView.Stretch) # The dict which holds the widget items containing the settings values, populated by calling self.load self.settings = {} self.loadConfig() def loadConfig(self): paramConfig = self.config.parameters table = self.parameterSettingsTable # Clear the table table.clearContents() table.setRowCount(0) self.settings = {} # Create a list of available icons in resource file iconList = [] iconResource = QResource(self.iconPrefix) for icon in iconResource.children(): iconList.append(icon) for paramName, paramSettings in paramConfig.items(): self.settings[paramName] = {} self.settings[paramName]['display_name'] = QTableWidgetItem(str(paramSettings.display_name)) # Create a combobox for choosing icons iconChooser = QComboBox() for icon in iconList: iconImage = QIcon(self.iconPrefix + "/" + icon) iconChooser.addItem(iconImage, icon[:-4]) # Set the chosen icon in the dropdown as that given in config iconChooser.setCurrentIndex(iconChooser.findText(paramSettings.icon[len(self.iconPrefix)+1:-4])) self.settings[paramName]['icon'] = iconChooser self.settings[paramName]['unit'] = QTableWidgetItem(str(paramSettings.unit)) self.settings[paramName]['unit'].setTextAlignment(Qt.AlignCenter) self.settings[paramName]['min_limit'] = QTableWidgetItem(str(paramSettings.min_limit)) self.settings[paramName]['min_limit'].setTextAlignment(Qt.AlignRight) self.settings[paramName]['max_limit'] = QTableWidgetItem(str(paramSettings.max_limit)) self.settings[paramName]['max_limit'].setTextAlignment(Qt.AlignRight) self.settings[paramName]['threshold_limit'] = QTableWidgetItem(str(paramSettings.threshold_limit)) self.settings[paramName]['threshold_limit'].setTextAlignment(Qt.AlignRight) self.settings[paramName]['file_key'] = QTableWidgetItem(str(paramSettings.file_key)) self.settings[paramName]['file_key'].setTextAlignment(Qt.AlignCenter) self.settings[paramName]['conversion_function'] = QTableWidgetItem(paramSettings.conversion_function) self.settings[paramName]['conversion_function'].setTextAlignment(Qt.AlignCenter) row = col = 0 # Add the widgets to the table table.setRowCount(len(self.settings)) for paramName, paramSettings in self.settings.items(): col = 0 for widget in paramSettings.values(): if type(widget) is QTableWidgetItem: table.setItem(row, col, widget) else: table.setCellWidget(row, col, widget) col += 1 row += 1 def updateConfig(self): if self.settings: for paramName, paramSettings in self.settings.items(): for settingName, settingWidget in paramSettings.items(): if type(settingWidget) is QTableWidgetItem: value = settingWidget.text() elif type(settingWidget) is QComboBox: # Icon path value = self.iconPrefix + "/" + settingWidget.currentText() + ".svg" self.config.parameters[paramName][settingName] = value class SettingsForm(Ui_SettingsForm, QWidget): def __init__(self, config): super(SettingsForm, self).__init__() self.config = config self.setupUi() self.loadConfig() def setupUi(self): super().setupUi(self) self.saveButton.clicked.connect(self.saveSettings) self.revertButton.clicked.connect(self.revertSettings) def loadConfig(self): self.appTitleLineEdit.setText(str(self.config.app_title)) self.uDPIPLineEdit.setText(str(self.config.network.udp_ip)) self.uDPPortLineEdit.setText(str(self.config.network.udp_port)) self.bufferSizeLineEdit.setText(str(self.config.network.buffer_size)) def updateConfig(self): self.config.app_title = self.appTitleLineEdit.text() self.config.network.udp_ip = self.uDPIPLineEdit.text() self.config.network.udp_port = self.uDPPortLineEdit.text() self.config.network.buffer_size = self.bufferSizeLineEdit.text() @Slot() def saveSettings(self): saveDialog = QMessageBox(self) saveDialog.setWindowTitle("Save the settings") saveDialog.setText("Are you sure you want to save the settings?") saveDialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No) saveDialog.setIcon(QMessageBox.Question) button = saveDialog.exec() if button == QMessageBox.Yes: self.updateConfig() # Update master settings config self.config.save() # Persist the config in file AppSignals.reloadApp.emit() # Signal the reload of application @Slot() def revertSettings(self): saveDialog = QMessageBox(self) saveDialog.setWindowTitle("Revert the settings") saveDialog.setText("Are you sure you want to revert to previous settings? All changes made by you will be lost!") saveDialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No) saveDialog.setIcon(QMessageBox.Question) button = saveDialog.exec() if button == QMessageBox.Yes: self.loadConfig() # Load existing config values class WeatherAppClient(QMainWindow): def __init__(self, parent=None): self.config = Config("config.json") self.db = Database("weather_data_dump.db", list(self.config.parameters.keys())) super().__init__(parent) self.loadConfig() AppSignals.reloadApp.connect(self.loadConfig) AppSignals.dataReceived.connect(self.saveToDB) def saveToDB(self, data): timestamp = str(datetime.datetime.today()) dataToBeSaved = { "TIMESTAMP": timestamp, "RECORD_TYPE": data["recordType"], } for key, value in data.items(): for paramName, paramProps in self.config.parameters.items(): if paramProps.file_key == key: dataToBeSaved[paramName] = value break print("About to save: ", dataToBeSaved) self.db.addRow(dataToBeSaved) @Slot() def loadConfig(self): # Remove old thread pool if created if hasattr(self, "threadPool"): self.threadPool.clear() # Delete old UDP Receiver thread if hasattr(self, "udpReceiver"): self.udpReceiver.endReception() del self.udpReceiver # Create a UDP receiver program self.udpReceiver = UDPReceiver(self.config.network.udp_ip, \ int(self.config.network.udp_port), \ int(self.config.network.buffer_size)) self.threadPool = QThreadPool() self.threadPool.start(self.udpReceiver) # Create the main UI if not created already if not hasattr(self, "ui"): self.ui = WeatherAppUI(self, self.config) self.ui.appTitle.setText(self.config.app_title) @Slot() def closeEvent(self, event): self.udpReceiver.endReception() del self.udpReceiver self.threadPool.clear() print("Closed app") if __name__ == "__main__": app = QApplication(sys.argv) widget = WeatherAppClient() widget.show() ret = app.exec() sys.exit(ret) # WeatherDataSimulator.py code: - # This Python file uses the following encoding: utf-8 import sys, time, json, re, os from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QWidget, QMessageBox from PySide6.QtGui import QIcon, QCloseEvent from PySide6.QtCore import QObject, Signal, Slot, QRunnable, QThreadPool from network import UDPSender # Important: # I need to run the following command to generate the ui_form.py file # pyside6-uic form.ui -o ui_form.py, or # pyside2-uic form.ui -o ui_form.py from ui_weather_data_simulator import Ui_WeatherDataSimulator class Modes: PLAYING = "Play" PAUSED = "Pause" STEP = "Step" # Create custom signals class Signals(QObject): TransmissionOver = Signal(int) Transmit = Signal(int, int) # Record number and Total Records signals = Signals() # Create a Sender Thread class class SenderThread(QRunnable): def __init__(self, tInfo): super(SenderThread, self).__init__() self.tInfo = tInfo @Slot() def run(self): while self.tInfo["transmit"]: if self.tInfo["currentMode"] == Modes.PLAYING and \ "fileLines" in self.tInfo: if self.tInfo["curRecordNum"] >= self.tInfo["numRecords"]: self.tInfo["currentMode"] = Modes.PAUSED # Prevent this loop's if block from being processed until re-play signals.TransmissionOver.emit(self.tInfo["curRecordNum"]) # Signal that the last record has been sent continue # Take the current line and send it curRecord = self.tInfo["fileLines"][self.tInfo["curRecordNum"]].strip() self.tInfo["sender"].send(curRecord, self.tInfo["IP"], int(self.tInfo["Port"])) print("Sent record:", curRecord) # Notify whoever is concerned that current record has been sent signals.Transmit.emit(self.tInfo["curRecordNum"]+1, self.tInfo["numRecords"]) print("Sleeping for {} msec".format(round(1000 / self.tInfo["freq"], 2))) time.sleep(1 / self.tInfo["freq"]) self.tInfo["curRecordNum"] += 1 class WeatherDataSimulator(Ui_WeatherDataSimulator, QWidget): def __init__(self, MainWindow, config): super().__init__() self.simFileTypes = "Text file (*.txt, *.TXT)" self.setupUi(MainWindow) self.config = config self.loadConfig() self.setSignalsAndSlots() self.loadSender() def setupUi(self, MainWindow): super().setupUi(MainWindow) # Set up the rest of the UI self.playIcon = QIcon(":/icons/res/icons/play.svg") self.pauseIcon = QIcon(":/icons/res/icons/pause.svg") def loadConfig(self): self.transmissionInfo = {} self.transmissionInfo["transmit"] = True # Flag to Sender thread self.transmissionInfo["sender"] = UDPSender() self.transmissionInfo["currentMode"] = Modes.PAUSED self.setTransmissionFrequency(int(self.config["freq"])) self.transmissionIPLineEdit.setText(self.config["udp_ip"]) self.transmissionPortLineEdit.setText(self.config["udp_port"]) self.speedSelector.setValue(int(self.config["freq"])) # Read the file content and store in transmissionInfo if not self.setFileContent(self.config["file_last_opened"]): self.fileChooserButton.setText("Choose file...") def setSignalsAndSlots(self): self.playPauseButton.clicked.connect(self.playPause) self.fileChooserButton.clicked.connect(self.chooseFile) self.speedSelector.valueChanged.connect(lambda freq: self.setTransmissionFrequency(freq)) self.speedSelector.valueChanged.connect(lambda freq: self.updateConfig(freq=freq)) self.transmissionIPLineEdit.textChanged.connect(lambda ip: self.updateConfig(udp_ip=ip)) self.transmissionPortLineEdit.textChanged.connect(lambda ip: self.updateConfig(udp_port=ip)) signals.Transmit.connect(self.moveSlider) signals.Transmit.connect(self.setTransmittedCount) signals.TransmissionOver.connect(self.showTransmissionOverMessage) self.progressSlider.sliderMoved.connect(self.setNextRecordNumToBeSent) self.progressSlider.sliderMoved.connect(self.setTransmittedCount) def loadSender(self): # Create a sender thread and start it self.senderThread = SenderThread(self.transmissionInfo) self.threadpool = QThreadPool() self.threadpool.start(self.senderThread) def updateConfig(self, **configValue): for key, value in configValue.items(): self.config[key] = value @Slot() def showTransmissionOverMessage (self, numRecords): self.pause() transmissionEndDlg = QMessageBox(self) transmissionEndDlg.setWindowTitle("Transmission Ended") transmissionEndDlg.setText(f"All {numRecords} records have been sent. You may rewind using the slider to send again.") transmissionEndDlg.setStandardButtons(QMessageBox.Ok) transmissionEndDlg.exec() @Slot() def moveSlider(self, value, _): self.progressSlider.setValue(value) @Slot() def setNextRecordNumToBeSent(self, recordNum): """Use the slider to set the next record number to be sent""" self.transmissionInfo["curRecordNum"] = recordNum @Slot() def setTransmittedCount(self, currentRecord): self.sentCount.setText(f"Sent: {currentRecord}/{self.transmissionInfo['numRecords']} {'records' if currentRecord != 1 else 'record'}") def setTransmissionFrequency(self, freq): self.transmissionInfo["freq"] = freq self.currentFreqLabel.setText(f"Freq: {freq} {'records' if int(freq) != 1 else 'record'}/s") def validateConfigValues(self): ipIsValid = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$").match(self.config["udp_ip"]) portIsValid = re.compile("^\d{4}").match(self.config["udp_port"]) filePathIsValid = os.path.isfile(self.config["file_last_opened"]) freqIsValid = (1 <= int(self.config["freq"]) <= 100) return (ipIsValid and portIsValid and filePathIsValid and freqIsValid) def play(self): if not self.validateConfigValues(): saveDialog = QMessageBox(self) saveDialog.setWindowTitle("Error in settings") saveDialog.setText("There are errors in your configuration. Please correct!") saveDialog.setStandardButtons(QMessageBox.Ok) saveDialog.setIcon(QMessageBox.Warning) saveDialog.exec() return self.transmissionInfo["currentMode"] = Modes.PLAYING self.transmissionInfo["IP"] = self.transmissionIPLineEdit.text() self.transmissionInfo["Port"] = self.transmissionPortLineEdit.text() self.playPauseButton.setIcon(self.pauseIcon) self.transmissionIPLineEdit.setEnabled(False) self.transmissionPortLineEdit.setEnabled(False) self.fileChooserButton.setEnabled(False) self.progressSlider.setEnabled(False) def pause(self): self.transmissionInfo["currentMode"] = Modes.PAUSED self.playPauseButton.setIcon(self.playIcon) self.transmissionIPLineEdit.setEnabled(True) self.transmissionPortLineEdit.setEnabled(True) self.fileChooserButton.setEnabled(True) self.progressSlider.setEnabled(True) @Slot() def playPause(self): if self.transmissionInfo["currentMode"] == Modes.PAUSED: self.play() elif self.transmissionInfo["currentMode"] == Modes.PLAYING: self.pause() @Slot() def chooseFile(self): file = QFileDialog.getOpenFileName(self, "Open Simulation File", "", self.simFileTypes) self.setFileContent(file[0]) def setFileContent(self, filePath): """This function does n things: 1. Set the file chooser button's text to the file path 2. Read the file content and store it in memory. 3. Calculate the total number of records, a.k.a lines in the file 4. Set current record number to 1 (start of transmission) 5. Set the minimum and maximum value of the progress slider.""" if filePath and filePath.lower().endswith(".txt") and os.path.exists(filePath) and os.path.isfile(filePath): self.filePath = filePath self.fileChooserButton.setText(filePath) self.updateConfig(file_last_opened = filePath) with open(filePath) as simFile: self.transmissionInfo["fileLines"] = simFile.readlines() self.transmissionInfo["numRecords"] = len(self.transmissionInfo["fileLines"]) self.transmissionInfo["curRecordNum"] = 0 self.progressSlider.setMinimum(1) self.progressSlider.setMaximum(self.transmissionInfo["numRecords"]) self.sentCount.setText(f"Sent: 0/{self.transmissionInfo['numRecords']} records") return True else: return False def stopTransmission(self): self.transmissionInfo['transmit'] = False class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) with open("config.json") as configFile: self.config = json.loads(configFile.read()) self.ui = WeatherDataSimulator(self, self.config) self.setFixedSize(800, 438) def closeEvent(self, event: QCloseEvent) -> None: # Store the config in config.json with open("config.json", "w") as configFile: configFile.write(json.dumps(self.config, indent = 4)) self.ui.stopTransmission() print("Bye!") if __name__ == "__main__": app = QApplication(sys.argv) # apply_stylesheet(app, theme='light_red.xml') widget = MainWindow() widget.show() #Sidemenu.py code: - # This Python file uses the following encoding: utf-8 from PySide6.QtCore import QPropertyAnimation, QEasingCurve, QSize from PySide6.QtGui import QIcon, QFont from PySide6.QtWidgets import QVBoxLayout, QPushButton, QSpacerItem, QSizePolicy, QWidget, QFrame import functools class SideMenu(QFrame): def __init__(self, menuItems, maxWidth): super(SideMenu, self).__init__() self.maxWidth = maxWidth self.isOpen = True # Starts as open despite me trying to set its width to 0 self._setupUI(menuItems) def _setupUI(self, buttonsAndSlots): self.setAutoFillBackground(True) self.setMaximumWidth(self.maxWidth) self.menuLayout = QVBoxLayout(self) for buttonName, buttonProperties in buttonsAndSlots.items(): menuButton = QPushButton(self) menuButton.setIcon(QIcon(buttonProperties['icon'])) menuButton.clicked.connect(functools.partial(buttonProperties['slot'], buttonName)) menuButton.setText(buttonName) font1 = QFont() font1.setPointSize(16) menuButton.setFont(font1) menuButton.setIconSize(QSize(32, 32)) menuButton.setMinimumHeight(50) self.menuLayout.addWidget(menuButton) menuSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.menuLayout.addItem(menuSpacer) # Create the animation for opening and closing self.animation = QPropertyAnimation(self, b"maximumWidth") self.animation.setEasingCurve(QEasingCurve.InOutCubic) # Create open and closed states for feeding the animation self.openState = self.maxWidth self.closedState = 0 self.resize(self.maxWidth, self.height()) self.animation.setDuration(200) def toggle(self): if not self.isOpen: self.animation.setStartValue(self.closedState) self.animation.setEndValue(self.openState) self.animation.start() self.isOpen = True else: self.animation.setStartValue(self.openState) self.animation.setEndValue(self.closedState) self.animation.start() self.isOpen = False # Database.py code for client: - import sqlite3 class Database: def __init__(self, dbName, parameterNames): self.dbName = dbName if not dbName.endswith(".db"): raise Exception("SQLite Database name must end with .db!") self.dbCon = sqlite3.connect(dbName) self.cursor = self.dbCon.cursor() self.tableName = "WeatherData" self.createTable(parameterNames) def createTable(self, parameterNames): res = self.cursor.execute("SELECT name FROM sqlite_master") tableNames = res.fetchall() if not tableNames: # Table doesn't exist. Create query = "CREATE TABLE WeatherData(ID INTEGER PRIMARY KEY, TIMESTAMP TEXT NOT NULL, RECORD_TYPE TEXT NOT NULL, " for paramName in parameterNames: query += (paramName + " REAL, ") query = query[:-2] + ")" # Remove the last comma and close the paranthesis self.cursor.execute(query) elif len(tableNames) == 1 and tableNames[0][0] == self.tableName: # Table already exists. Fetch the existing columns existingColumns = [row[1] for row in self.cursor.execute("PRAGMA table_info(WeatherData)").fetchall() if (row[1] != "ID" and row[1] != "TIMESTAMP")] # Find the symmetric difference between both missingCols = list(set(parameterNames).difference(set(existingColumns))) # Add the missing columns to the existing table for colName in missingCols: self.cursor.execute("ALTER TABLE {} ADD {} REAL".format(self.tableName, colName)) self.dbCon.commit() def addRow(self, paramsAndValues): """paramsAndValues: a dictionary where keys are parameter names and values represent their corresponding value. """ query = "INSERT INTO " + self.tableName + "(" + \ ",".join(str(paramName) for paramName in paramsAndValues.keys()) + ") VALUES (" + \ ",".join("?" * len(paramsAndValues)) + ")" self.cursor.execute(query, tuple(paramsAndValues.values())) self.dbCon.commit() def describe(self): print("Table:", self.tableName) print("Columns: ", [row[1] for row in self.cursor.execute("PRAGMA table_info(WeatherData)").fetchall()]) def show(self): res = self.cursor.execute("SELECT * FROM " + self.tableName) for row in res.fetchall(): print(row) def __del__(self): self.dbCon.commit() self.dbCon.close()
Write, Run & Share Python code online using OneCompiler's Python online compiler for free. It's one of the robust, feature-rich online compilers for python language, supporting both the versions which are Python 3 and Python 2.7. Getting started with the OneCompiler's Python editor is easy and fast. The editor shows sample boilerplate code when you choose language as Python or Python2 and start coding.
OneCompiler's python online editor supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample python program which takes name as input and print your name with hello.
import sys
name = sys.stdin.readline()
print("Hello "+ name)
Python is a very popular general-purpose programming language which was created by Guido van Rossum, and released in 1991. It is very popular for web development and you can build almost anything like mobile apps, web apps, tools, data analytics, machine learning etc. It is designed to be simple and easy like english language. It's is highly productive and efficient making it a very popular language.
When ever you want to perform a set of operations based on a condition IF-ELSE is used.
if conditional-expression
#code
elif conditional-expression
#code
else:
#code
Indentation is very important in Python, make sure the indentation is followed correctly
For loop is used to iterate over arrays(list, tuple, set, dictionary) or strings.
mylist=("Iphone","Pixel","Samsung")
for i in mylist:
print(i)
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.
while condition
#code
There are four types of collections in Python.
List is a collection which is ordered and can be changed. Lists are specified in square brackets.
mylist=["iPhone","Pixel","Samsung"]
print(mylist)
Tuple is a collection which is ordered and can not be changed. Tuples are specified in round brackets.
myTuple=("iPhone","Pixel","Samsung")
print(myTuple)
Below throws an error if you assign another value to tuple again.
myTuple=("iPhone","Pixel","Samsung")
print(myTuple)
myTuple[1]="onePlus"
print(myTuple)
Set is a collection which is unordered and unindexed. Sets are specified in curly brackets.
myset = {"iPhone","Pixel","Samsung"}
print(myset)
Dictionary is a collection of key value pairs which is unordered, can be changed, and indexed. They are written in curly brackets with key - value pairs.
mydict = {
"brand" :"iPhone",
"model": "iPhone 11"
}
print(mydict)
Following are the libraries supported by OneCompiler's Python compiler
Name | Description |
---|---|
NumPy | NumPy python library helps users to work on arrays with ease |
SciPy | SciPy is a scientific computation library which depends on NumPy for convenient and fast N-dimensional array manipulation |
SKLearn/Scikit-learn | Scikit-learn or Scikit-learn is the most useful library for machine learning in Python |
Pandas | Pandas is the most efficient Python library for data manipulation and analysis |
DOcplex | DOcplex is IBM Decision Optimization CPLEX Modeling for Python, is a library composed of Mathematical Programming Modeling and Constraint Programming Modeling |