# 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 |