From a1a901a75f1236435473f4f7fb2dcfd3a46f523a Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 11 Mar 2019 12:27:46 +0300 Subject: [PATCH 01/15] Create QT gui files for server with new gui --- Server/server_gui.py | 87 ++++++++++++++++++++ Server/server_gui.ui | 186 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 Server/server_gui.py create mode 100644 Server/server_gui.ui diff --git a/Server/server_gui.py b/Server/server_gui.py new file mode 100644 index 0000000..db8a5ae --- /dev/null +++ b/Server/server_gui.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'server_gui.ui' +# +# Created by: PyQt5 UI code generator 5.12 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(850, 460) + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) + self.pushButton.setGeometry(QtCore.QRect(680, 20, 150, 40)) + self.pushButton.setObjectName("pushButton") + self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_2.setGeometry(QtCore.QRect(680, 120, 150, 40)) + self.pushButton_2.setObjectName("pushButton_2") + self.spinBox = QtWidgets.QSpinBox(self.centralwidget) + self.spinBox.setGeometry(QtCore.QRect(760, 70, 50, 40)) + self.spinBox.setObjectName("spinBox") + self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_3.setGeometry(QtCore.QRect(680, 170, 150, 40)) + self.pushButton_3.setObjectName("pushButton_3") + self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_4.setGeometry(QtCore.QRect(680, 220, 150, 40)) + self.pushButton_4.setObjectName("pushButton_4") + self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_5.setGeometry(QtCore.QRect(680, 280, 150, 40)) + self.pushButton_5.setObjectName("pushButton_5") + self.pushButton_6 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_6.setGeometry(QtCore.QRect(680, 330, 150, 40)) + self.pushButton_6.setObjectName("pushButton_6") + self.pushButton_7 = QtWidgets.QPushButton(self.centralwidget) + self.pushButton_7.setGeometry(QtCore.QRect(680, 380, 150, 40)) + self.pushButton_7.setObjectName("pushButton_7") + self.label = QtWidgets.QLabel(self.centralwidget) + self.label.setGeometry(QtCore.QRect(680, 70, 71, 40)) + self.label.setObjectName("label") + self.label_2 = QtWidgets.QLabel(self.centralwidget) + self.label_2.setGeometry(QtCore.QRect(820, 70, 10, 40)) + self.label_2.setObjectName("label_2") + self.tableWidget = QtWidgets.QTableWidget(self.centralwidget) + self.tableWidget.setGeometry(QtCore.QRect(20, 20, 640, 400)) + self.tableWidget.setObjectName("tableWidget") + self.tableWidget.setColumnCount(0) + self.tableWidget.setRowCount(0) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 25)) + self.menubar.setObjectName("menubar") + self.menuOptions = QtWidgets.QMenu(self.menubar) + self.menuOptions.setObjectName("menuOptions") + MainWindow.setMenuBar(self.menubar) + self.actionSend_Animations = QtWidgets.QAction(MainWindow) + self.actionSend_Animations.setObjectName("actionSend_Animations") + self.actionSend_Configurations = QtWidgets.QAction(MainWindow) + self.actionSend_Configurations.setObjectName("actionSend_Configurations") + self.menuOptions.addAction(self.actionSend_Animations) + self.menuOptions.addAction(self.actionSend_Configurations) + self.menubar.addAction(self.menuOptions.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Clever Drone Animation Player")) + self.pushButton.setText(_translate("MainWindow", "Preflight check")) + self.pushButton_2.setText(_translate("MainWindow", "Start animation")) + self.pushButton_3.setText(_translate("MainWindow", "Pause")) + self.pushButton_4.setText(_translate("MainWindow", "Stop")) + self.pushButton_5.setText(_translate("MainWindow", "Takeoff")) + self.pushButton_6.setText(_translate("MainWindow", "Land")) + self.pushButton_7.setText(_translate("MainWindow", "Disarm")) + self.label.setText(_translate("MainWindow", "Start after")) + self.label_2.setText(_translate("MainWindow", "s")) + self.menuOptions.setTitle(_translate("MainWindow", "Actions")) + self.actionSend_Animations.setText(_translate("MainWindow", "Send Animations")) + self.actionSend_Configurations.setText(_translate("MainWindow", "Send Configurations")) + + diff --git a/Server/server_gui.ui b/Server/server_gui.ui new file mode 100644 index 0000000..a3d9be5 --- /dev/null +++ b/Server/server_gui.ui @@ -0,0 +1,186 @@ + + + MainWindow + + + + 0 + 0 + 850 + 460 + + + + Clever Drone Animation Player + + + + + + 680 + 20 + 150 + 40 + + + + Preflight check + + + + + + 680 + 120 + 150 + 40 + + + + Start animation + + + + + + 760 + 70 + 50 + 40 + + + + + + + 680 + 170 + 150 + 40 + + + + Pause + + + + + + 680 + 220 + 150 + 40 + + + + Stop + + + + + + 680 + 280 + 150 + 40 + + + + Takeoff + + + + + + 680 + 330 + 150 + 40 + + + + Land + + + + + + 680 + 380 + 150 + 40 + + + + Disarm + + + + + + 680 + 70 + 71 + 40 + + + + Start after + + + + + + 820 + 70 + 10 + 40 + + + + s + + + + + + 20 + 20 + 640 + 400 + + + + + + + + 0 + 0 + 850 + 25 + + + + + Actions + + + + + + + + + Send Animations + + + + + Send Configurations + + + + + + From 16fe48c9c1f7e2ae8b6251ebfc16cef2fb579134 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 11 Mar 2019 12:29:06 +0300 Subject: [PATCH 02/15] Create test program with server with new gui --- Server/server_qt.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Server/server_qt.py diff --git a/Server/server_qt.py b/Server/server_qt.py new file mode 100644 index 0000000..460f396 --- /dev/null +++ b/Server/server_qt.py @@ -0,0 +1,26 @@ +from PyQt5 import QtWidgets + +# Импортируем нашу форму. +from server_gui import Ui_MainWindow +import sys + + +class main_window(QtWidgets.QMainWindow): + def __init__(self): + super(main_window, self).__init__() + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + self.ui.tableWidget.setColumnCount(6) + self.ui.tableWidget.setRowCount(20) + self.ui.tableWidget.setHorizontalHeaderLabels( + ('copter ID', 'animation ID', 'battery V', 'battery %', 'selfcheck', 'time UTC') + ) + self.ui.tableWidget.horizontalHeader().setStretchLastSection(True) + + +app = QtWidgets.QApplication([]) +application = main_window() +application.show() + +sys.exit(app.exec()) \ No newline at end of file From eb2f929c462abddf9b5ad4737e63b51798988f13 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 11 Mar 2019 17:07:54 +0300 Subject: [PATCH 03/15] Change QTableWidget to model-based QTableView --- Server/server_gui.py | 8 +++----- Server/server_gui.ui | 2 +- Server/server_qt.py | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Server/server_gui.py b/Server/server_gui.py index db8a5ae..f8a4405 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -45,11 +45,9 @@ class Ui_MainWindow(object): self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setGeometry(QtCore.QRect(820, 70, 10, 40)) self.label_2.setObjectName("label_2") - self.tableWidget = QtWidgets.QTableWidget(self.centralwidget) - self.tableWidget.setGeometry(QtCore.QRect(20, 20, 640, 400)) - self.tableWidget.setObjectName("tableWidget") - self.tableWidget.setColumnCount(0) - self.tableWidget.setRowCount(0) + self.tableView = QtWidgets.QTableView(self.centralwidget) + self.tableView.setGeometry(QtCore.QRect(20, 20, 640, 400)) + self.tableView.setObjectName("tableView") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 25)) diff --git a/Server/server_gui.ui b/Server/server_gui.ui index a3d9be5..2d0f5d5 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -141,7 +141,7 @@ s - + 20 diff --git a/Server/server_qt.py b/Server/server_qt.py index 460f396..0bdaddd 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -1,4 +1,9 @@ from PyQt5 import QtWidgets +from PyQt5.QtGui import QStandardItem +from PyQt5.QtGui import QStandardItemModel +from PyQt5.QtCore import QModelIndex +from PyQt5.QtCore import Qt +from PyQt5.QtCore import pyqtSlot # Импортируем нашу форму. from server_gui import Ui_MainWindow @@ -10,13 +15,15 @@ class main_window(QtWidgets.QMainWindow): super(main_window, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) - - self.ui.tableWidget.setColumnCount(6) - self.ui.tableWidget.setRowCount(20) - self.ui.tableWidget.setHorizontalHeaderLabels( + model = QStandardItemModel() + item = QStandardItem() + model.setHorizontalHeaderLabels( ('copter ID', 'animation ID', 'battery V', 'battery %', 'selfcheck', 'time UTC') ) - self.ui.tableWidget.horizontalHeader().setStretchLastSection(True) + model.setColumnCount(6) + model.setRowCount(20) + self.ui.tableView.setModel(model) + self.ui.tableView.horizontalHeader().setStretchLastSection(True) app = QtWidgets.QApplication([]) From cc5bfcdbb2561efbc2d6c33b546ede6a3a5e5864 Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Mon, 11 Mar 2019 17:37:06 +0300 Subject: [PATCH 04/15] Translated server code to qt app code --- Server/server_qt.py | 305 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 292 insertions(+), 13 deletions(-) diff --git a/Server/server_qt.py b/Server/server_qt.py index 0bdaddd..99c9a3d 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -4,15 +4,264 @@ from PyQt5.QtGui import QStandardItemModel from PyQt5.QtCore import QModelIndex from PyQt5.QtCore import Qt from PyQt5.QtCore import pyqtSlot - -# Импортируем нашу форму. + +# Importing gui form from server_gui import Ui_MainWindow + + +import os import sys - - -class main_window(QtWidgets.QMainWindow): +import glob +import time +import struct +import socket +import threading +import collections +import configparser + +# All imports sorted in pyramid + +# Functions +def get_ip_address(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + ip = s.getsockname()[0] + s.close() + return ip + + +def auto_connect(): + while True: + ServerSocket.listen(1) + c, addr = ServerSocket.accept() + print("Got connection from:", str(addr)) + if not any(client_addr == addr[0] for client_addr in Client.clients.keys()): + client = Client(addr[0]) + print("New client") + else: + print("Reconnected client") + Client.clients[addr[0]].connect(c, addr) + + +def ip_broadcast(ip, port): + ip = ip + broadcast_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) + broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + while True: + msg = bytes(Client.form_command("server_ip ", ip, ), "UTF-8") + broadcast_sock.sendto(msg, ('255.255.255.255', 8181)) #TODO to config + print("Broadcast sent") + time.sleep(5) + + +NTP_DELTA = 2208988800 # 1970-01-01 00:00:00 +NTP_QUERY = b'\x1b' + bytes(47) + + +def get_ntp_time(ntp_host, ntp_port): + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.sendto(NTP_QUERY, (ntp_host, ntp_port)) + msg, _ = s.recvfrom(1024) + return int.from_bytes(msg[-8:], 'big') / 2 ** 32 - NTP_DELTA + + +def requires_connect(f): + def wrapper(*args, **kwargs): + if args[0].connected: + return f(*args, **kwargs) + else: + print("Function requires client to be connected!") + return wrapper + + +class Client: + clients = {} + + def __init__(self, ip): + self.socket = None + self.addr = None + + self._send_queue = collections.deque() + self._received_queue = collections.deque() + self._request_queue = collections.OrderedDict() + + self.copter_id = None + self.malfunction = False + + Client.clients[ip] = self + + self.connected = False + + def connect(self, client_socket, client_addr): + print("Client connected") + # self._send_queue = collections.deque() # comment for resuming queue after reconnection + + self.socket = client_socket + self.addr = client_addr + + self.socket.setblocking(0) + self.connected = True + client_thread = threading.Thread(target=self._run, args=()) + client_thread.start() + if self.copter_id is None: + self.copter_id = self.get_response("id") + print("Got copter id:", self.copter_id) + # drone_list.insert("", "end", self.addr[0], text=self.copter_id) # TODO to qt + + def _send_all(self, msg): + self.socket.sendall(struct.pack('>I', len(msg)) + msg) + + def _receive_all(self, n): + data = b'' + while len(data) < n: + packet = self.socket.recv(min(n - len(data), BUFFER_SIZE)) + if not packet: + return None + data += packet + return data + + def _receive_message(self): + raw_msglen = self._receive_all(4) + if not raw_msglen: + return None + msglen = struct.unpack('>I', raw_msglen)[0] + msg = self._receive_all(msglen) + return msg + + def _run(self): + while self.connected: + try: + if self._send_queue: + msg = self._send_queue.popleft() + print("Send", msg, "to", self.addr) + try: + self._send_all(msg) + except socket.error as e: + print("Attempt to send failed") + self._send_queue.appendleft(msg) + raise e + else: + msg = "ping" + # self._send_all(msg) + + try: # check if data in buffer + check = self.socket.recv(BUFFER_SIZE, socket.MSG_PEEK) + if check: + received = self._receive_message() + if received: + received = received.decode("UTF-8") + print("Recived", received, "from", self.addr) + command, args = Client.parse_command(received) + if command == "response": + for key, value in self._request_queue.items(): + if not value: + self._request_queue[key] = args[0] + print("Request successfully closed") + break + else: + self._received_queue.appendleft(received) + except socket.error: + pass + + except socket.error as e: + print("Client error: {}, disconnected".format(e)) + self.connected = False + self.socket.close() + break + # time.sleep(0.05) + + @staticmethod + def form_command(command: str, args=()): # Change for different protocol + return " ".join([command, *args]) + + @staticmethod + def parse_command(command_input): + args = command_input.split() + command = args.pop(0) + return command, args + + @requires_connect + def send(self, *messages): + for message in messages: + self._send_queue.append(bytes(message, "UTF-8")) + + @staticmethod + def broadcast(message, force_all=False): + if Client.clients: + for client in Client.clients.values(): + if (not client.malfunction) or force_all: + client.send(message) + else: + print("No clients were connected!") + + @requires_connect + def send_file(self, filepath, dest_filename): + print("Sending file ", dest_filename) + self.send(Client.form_command("writefile", (dest_filename,))) + file = open(filepath, 'rb') + chunk = file.read(BUFFER_SIZE) + while chunk: + self._send_queue.append(chunk) + chunk = file.read(BUFFER_SIZE) + file.close() + self.send(Client.form_command("/endoffile")) + print("File sent") + + @requires_connect + def get_response(self, requested_value): + self._request_queue[requested_value] = "" + self.send(Client.form_command("request", (requested_value, ))) + + while not self._request_queue[requested_value]: + pass + + return self._request_queue.pop(requested_value) + +# UI functions +def stop_swarm(): + Client.broadcast("stop") # для тестирования + + +def land_all(): + Client.broadcast("land") + + +def disarm_all(): + Client.broadcast("disarm") + + +def takeoff_all(): + Client.broadcast("takeoff") + + +def send_animations(): + path = filedialog.askdirectory(title="Animation directory") # TODO to QT + if path: + print("Selected directory:", path) + files = [file for file in glob.glob(path+'/*.csv')] + names = [os.path.basename(file).split(".")[0] for file in files] + print(files) + for file, name in zip(files, names): + for copter in Client.clients.values(): + if name == copter.copter_id: + copter.send_file(file, "animation.csv") # TODO config + else: + print("Filename not matches with any drone connected") + # dr = next(iter(Client.clients.values())) # костыль для тестирования + # ANS = dr.get_response("someshit") + # print(ANS) + + +def send_starttime(dt=15): + timenow = time.time() + print('Now:', time.ctime(timenow), "+ dt =", dt) + Client.broadcast(Client.form_command("starttime", (str(timenow+dt), ))) + + +class MainWindow(QtWidgets.QMainWindow): def __init__(self): - super(main_window, self).__init__() + super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) model = QStandardItemModel() @@ -24,10 +273,40 @@ class main_window(QtWidgets.QMainWindow): model.setRowCount(20) self.ui.tableView.setModel(model) self.ui.tableView.horizontalHeader().setStretchLastSection(True) - - -app = QtWidgets.QApplication([]) -application = main_window() -application.show() - -sys.exit(app.exec()) \ No newline at end of file + + +# Pre-initialization +# reading config +config = configparser.ConfigParser() +config.read("server_config.ini") + +port = int(config['SERVER']['port']) +BUFFER_SIZE = int(config['SERVER']['buffer_size']) +NTP_HOST = config['NTP']['host'] +NTP_PORT = int(config['NTP']['port']) + + +ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +host = socket.gethostname() +ip = get_ip_address() + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + window.show() + + print('Server started on', host, ip, ":", port) + # print('Now:', time.ctime(get_ntp_time(NTP_HOST, NTP_PORT))) + print('Waiting for clients...') + ServerSocket.bind((ip, port)) + + autoconnect_thread = threading.Thread(target=auto_connect) + autoconnect_thread.daemon = True + autoconnect_thread.start() + + broadcast_thread = threading.Thread(target=ip_broadcast, args=(ip, port,)) + broadcast_thread.start() + + sys.exit(app.exec_()) From 71ea0231a7f6e008bebf37847a4c6582d20a2a03 Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Mon, 11 Mar 2019 18:12:00 +0300 Subject: [PATCH 05/15] Added proper objects names --- Server/server_gui.py | 113 ++++++++++++++++++++++++------------------- Server/server_gui.ui | 41 ++++++++++------ 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/Server/server_gui.py b/Server/server_gui.py index f8a4405..459bef1 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -2,65 +2,67 @@ # Form implementation generated from reading ui file 'server_gui.ui' # -# Created by: PyQt5 UI code generator 5.12 +# Created by: PyQt5 UI code generator 5.10.1 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets - class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(850, 460) + MainWindow.resize(850, 479) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") - self.pushButton = QtWidgets.QPushButton(self.centralwidget) - self.pushButton.setGeometry(QtCore.QRect(680, 20, 150, 40)) - self.pushButton.setObjectName("pushButton") - self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_2.setGeometry(QtCore.QRect(680, 120, 150, 40)) - self.pushButton_2.setObjectName("pushButton_2") - self.spinBox = QtWidgets.QSpinBox(self.centralwidget) - self.spinBox.setGeometry(QtCore.QRect(760, 70, 50, 40)) - self.spinBox.setObjectName("spinBox") - self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_3.setGeometry(QtCore.QRect(680, 170, 150, 40)) - self.pushButton_3.setObjectName("pushButton_3") - self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_4.setGeometry(QtCore.QRect(680, 220, 150, 40)) - self.pushButton_4.setObjectName("pushButton_4") - self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_5.setGeometry(QtCore.QRect(680, 280, 150, 40)) - self.pushButton_5.setObjectName("pushButton_5") - self.pushButton_6 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_6.setGeometry(QtCore.QRect(680, 330, 150, 40)) - self.pushButton_6.setObjectName("pushButton_6") - self.pushButton_7 = QtWidgets.QPushButton(self.centralwidget) - self.pushButton_7.setGeometry(QtCore.QRect(680, 380, 150, 40)) - self.pushButton_7.setObjectName("pushButton_7") - self.label = QtWidgets.QLabel(self.centralwidget) - self.label.setGeometry(QtCore.QRect(680, 70, 71, 40)) - self.label.setObjectName("label") - self.label_2 = QtWidgets.QLabel(self.centralwidget) - self.label_2.setGeometry(QtCore.QRect(820, 70, 10, 40)) - self.label_2.setObjectName("label_2") + self.check_button = QtWidgets.QPushButton(self.centralwidget) + self.check_button.setGeometry(QtCore.QRect(680, 20, 150, 40)) + self.check_button.setObjectName("check_button") + self.start_button = QtWidgets.QPushButton(self.centralwidget) + self.start_button.setEnabled(False) + self.start_button.setGeometry(QtCore.QRect(680, 120, 150, 40)) + self.start_button.setFlat(False) + self.start_button.setObjectName("start_button") + self.start_delay_spin = QtWidgets.QSpinBox(self.centralwidget) + self.start_delay_spin.setGeometry(QtCore.QRect(760, 70, 50, 40)) + self.start_delay_spin.setObjectName("start_delay_spin") + self.pause_button = QtWidgets.QPushButton(self.centralwidget) + self.pause_button.setGeometry(QtCore.QRect(680, 170, 150, 40)) + self.pause_button.setObjectName("pause_button") + self.stop_button = QtWidgets.QPushButton(self.centralwidget) + self.stop_button.setGeometry(QtCore.QRect(680, 220, 150, 40)) + self.stop_button.setObjectName("stop_button") + self.takeoff_button = QtWidgets.QPushButton(self.centralwidget) + self.takeoff_button.setEnabled(False) + self.takeoff_button.setGeometry(QtCore.QRect(680, 280, 150, 40)) + self.takeoff_button.setObjectName("takeoff_button") + self.land_button = QtWidgets.QPushButton(self.centralwidget) + self.land_button.setGeometry(QtCore.QRect(680, 330, 150, 40)) + self.land_button.setObjectName("land_button") + self.disarm_button = QtWidgets.QPushButton(self.centralwidget) + self.disarm_button.setGeometry(QtCore.QRect(680, 380, 150, 40)) + self.disarm_button.setObjectName("disarm_button") + self.start_text = QtWidgets.QLabel(self.centralwidget) + self.start_text.setGeometry(QtCore.QRect(680, 70, 71, 40)) + self.start_text.setObjectName("start_text") + self.secs_text = QtWidgets.QLabel(self.centralwidget) + self.secs_text.setGeometry(QtCore.QRect(820, 70, 10, 40)) + self.secs_text.setObjectName("secs_text") self.tableView = QtWidgets.QTableView(self.centralwidget) self.tableView.setGeometry(QtCore.QRect(20, 20, 640, 400)) self.tableView.setObjectName("tableView") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 25)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 39)) self.menubar.setObjectName("menubar") self.menuOptions = QtWidgets.QMenu(self.menubar) self.menuOptions.setObjectName("menuOptions") MainWindow.setMenuBar(self.menubar) - self.actionSend_Animations = QtWidgets.QAction(MainWindow) - self.actionSend_Animations.setObjectName("actionSend_Animations") - self.actionSend_Configurations = QtWidgets.QAction(MainWindow) - self.actionSend_Configurations.setObjectName("actionSend_Configurations") - self.menuOptions.addAction(self.actionSend_Animations) - self.menuOptions.addAction(self.actionSend_Configurations) + self.action_send_animations = QtWidgets.QAction(MainWindow) + self.action_send_animations.setObjectName("action_send_animations") + self.action_send_configurations = QtWidgets.QAction(MainWindow) + self.action_send_configurations.setObjectName("action_send_configurations") + self.menuOptions.addAction(self.action_send_animations) + self.menuOptions.addAction(self.action_send_configurations) self.menubar.addAction(self.menuOptions.menuAction()) self.retranslateUi(MainWindow) @@ -69,17 +71,26 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Clever Drone Animation Player")) - self.pushButton.setText(_translate("MainWindow", "Preflight check")) - self.pushButton_2.setText(_translate("MainWindow", "Start animation")) - self.pushButton_3.setText(_translate("MainWindow", "Pause")) - self.pushButton_4.setText(_translate("MainWindow", "Stop")) - self.pushButton_5.setText(_translate("MainWindow", "Takeoff")) - self.pushButton_6.setText(_translate("MainWindow", "Land")) - self.pushButton_7.setText(_translate("MainWindow", "Disarm")) - self.label.setText(_translate("MainWindow", "Start after")) - self.label_2.setText(_translate("MainWindow", "s")) + self.check_button.setText(_translate("MainWindow", "Preflight check")) + self.start_button.setText(_translate("MainWindow", "Start animation")) + self.pause_button.setText(_translate("MainWindow", "Pause")) + self.stop_button.setText(_translate("MainWindow", "Stop")) + self.takeoff_button.setText(_translate("MainWindow", "Takeoff")) + self.land_button.setText(_translate("MainWindow", "Land")) + self.disarm_button.setText(_translate("MainWindow", "Disarm")) + self.start_text.setText(_translate("MainWindow", "Start after")) + self.secs_text.setText(_translate("MainWindow", "s")) self.menuOptions.setTitle(_translate("MainWindow", "Actions")) - self.actionSend_Animations.setText(_translate("MainWindow", "Send Animations")) - self.actionSend_Configurations.setText(_translate("MainWindow", "Send Configurations")) + self.action_send_animations.setText(_translate("MainWindow", "Send Animations")) + self.action_send_configurations.setText(_translate("MainWindow", "Send Configurations")) +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_()) + diff --git a/Server/server_gui.ui b/Server/server_gui.ui index 2d0f5d5..f9c8475 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -7,14 +7,14 @@ 0 0 850 - 460 + 479 Clever Drone Animation Player - + 680 @@ -27,7 +27,10 @@ Preflight check - + + + false + 680 @@ -39,8 +42,11 @@ Start animation + + false + - + 760 @@ -50,7 +56,7 @@ - + 680 @@ -63,7 +69,7 @@ Pause - + 680 @@ -76,7 +82,10 @@ Stop - + + + false + 680 @@ -89,7 +98,7 @@ Takeoff - + 680 @@ -102,7 +111,7 @@ Land - + 680 @@ -115,7 +124,7 @@ Disarm - + 680 @@ -128,7 +137,7 @@ Start after - + 820 @@ -158,24 +167,24 @@ 0 0 850 - 25 + 39 Actions - - + + - + Send Animations - + Send Configurations From 53a9901230a9f1f165a9119ca3e30fefef1eae7b Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Mon, 11 Mar 2019 18:14:57 +0300 Subject: [PATCH 06/15] Broadcast thread demonization --- Server/server_qt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/server_qt.py b/Server/server_qt.py index 99c9a3d..8eaaa03 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -307,6 +307,7 @@ if __name__ == "__main__": autoconnect_thread.start() broadcast_thread = threading.Thread(target=ip_broadcast, args=(ip, port,)) + broadcast_thread.daemon = True broadcast_thread.start() sys.exit(app.exec_()) From db5d6c454c7fd7ff4b69d94db1f3d20bd30ef6dd Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 12 Mar 2019 12:12:53 +0300 Subject: [PATCH 07/15] Modify gui to simplify first working version --- Server/server_gui.py | 19 ++++++------------- Server/server_gui.ui | 9 ++++++--- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Server/server_gui.py b/Server/server_gui.py index 459bef1..0371724 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -2,12 +2,13 @@ # Form implementation generated from reading ui file 'server_gui.ui' # -# Created by: PyQt5 UI code generator 5.10.1 +# Created by: PyQt5 UI code generator 5.12 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets + class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") @@ -15,10 +16,11 @@ class Ui_MainWindow(object): self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.check_button = QtWidgets.QPushButton(self.centralwidget) + self.check_button.setEnabled(False) self.check_button.setGeometry(QtCore.QRect(680, 20, 150, 40)) self.check_button.setObjectName("check_button") self.start_button = QtWidgets.QPushButton(self.centralwidget) - self.start_button.setEnabled(False) + self.start_button.setEnabled(True) self.start_button.setGeometry(QtCore.QRect(680, 120, 150, 40)) self.start_button.setFlat(False) self.start_button.setObjectName("start_button") @@ -32,7 +34,7 @@ class Ui_MainWindow(object): self.stop_button.setGeometry(QtCore.QRect(680, 220, 150, 40)) self.stop_button.setObjectName("stop_button") self.takeoff_button = QtWidgets.QPushButton(self.centralwidget) - self.takeoff_button.setEnabled(False) + self.takeoff_button.setEnabled(True) self.takeoff_button.setGeometry(QtCore.QRect(680, 280, 150, 40)) self.takeoff_button.setObjectName("takeoff_button") self.land_button = QtWidgets.QPushButton(self.centralwidget) @@ -52,7 +54,7 @@ class Ui_MainWindow(object): self.tableView.setObjectName("tableView") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 39)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 25)) self.menubar.setObjectName("menubar") self.menuOptions = QtWidgets.QMenu(self.menubar) self.menuOptions.setObjectName("menuOptions") @@ -85,12 +87,3 @@ class Ui_MainWindow(object): self.action_send_configurations.setText(_translate("MainWindow", "Send Configurations")) -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_()) - diff --git a/Server/server_gui.ui b/Server/server_gui.ui index f9c8475..b1e4bdc 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -15,6 +15,9 @@ + + false + 680 @@ -29,7 +32,7 @@ - false + true @@ -84,7 +87,7 @@ - false + true @@ -167,7 +170,7 @@ 0 0 850 - 39 + 25 From 00b7851228964fc9c3ae0e8cbeb60d470709ae38 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 12 Mar 2019 12:13:59 +0300 Subject: [PATCH 08/15] Connect gui buttons to server code --- Server/server_qt.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Server/server_qt.py b/Server/server_qt.py index 8eaaa03..4e9ca4d 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -273,7 +273,22 @@ class MainWindow(QtWidgets.QMainWindow): model.setRowCount(20) self.ui.tableView.setModel(model) self.ui.tableView.horizontalHeader().setStretchLastSection(True) - + self.ui.takeoff_button.clicked.connect(takeoff_all) + self.ui.land_button.clicked.connect(land_all) + self.ui.disarm_button.clicked.connect(disarm_all) + self.ui.start_button.clicked.connect(self.send_start_command) + self.ui.pause_button.clicked.connect(self.pause_resume_all) + self.ui.stop_button.clicked.connect(stop_swarm) + #self.ui.check_button.clicked.connect(self.preflight_checks) + def pause_resume_all(self): + if self.ui.pause_button.text() == 'Pause': + Client.broadcast('pause') + self.ui.pause_button.setText('Resume') + else: + Client.broadcast('resume') + self.ui.pause_button.setText('Pause') + def send_start_command(self): + send_starttime(self.ui.start_delay_spin.value()) # Pre-initialization # reading config From 1ce533c0042e5ae3ed68db57d116f7bd8a36d42e Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 12 Mar 2019 12:15:20 +0300 Subject: [PATCH 09/15] Add pause and resume commands for receive --- Drone/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Drone/client.py b/Drone/client.py index 61fae24..838659f 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -220,6 +220,10 @@ try: rospy.Timer(rospy.Duration(dt), start_animation, oneshot=True) elif command == 'takeoff': play_animation.takeoff() + elif command == 'pause' + pause_animation() + elif command == 'resume' + resume_animation() elif command == 'stop': stop_animation() #FlightLib.reach(5, 5, 2) @@ -227,7 +231,6 @@ try: FlightLib.land1() # TODO dont forget change back to land elif command == 'disarm': FlightLib.arming(False) - elif command == 'request': request_target = args[0] print("Got request for:", request_target) From ac8ff538042dcb251efce1f16536fddb310c7587 Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Tue, 12 Mar 2019 12:36:17 +0300 Subject: [PATCH 10/15] Restored all UI functionality (buttons + filedialog) --- Server/server_qt.py | 148 +++++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 51 deletions(-) diff --git a/Server/server_qt.py b/Server/server_qt.py index 8eaaa03..8ce8919 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -5,6 +5,8 @@ from PyQt5.QtCore import QModelIndex from PyQt5.QtCore import Qt from PyQt5.QtCore import pyqtSlot +from PyQt5.QtWidgets import QFileDialog + # Importing gui form from server_gui import Ui_MainWindow @@ -186,6 +188,32 @@ class Client: for message in messages: self._send_queue.append(bytes(message, "UTF-8")) + @requires_connect + def get_response(self, requested_value): + self._request_queue[requested_value] = "" + self.send(Client.form_command("request", (requested_value,))) + + while not self._request_queue[requested_value]: + pass + + return self._request_queue.pop(requested_value) + + @staticmethod + def send_to_selected(*messages): + if Client.clients: + for client in Client.clients.values(): # TODO change to selected + client.send(*messages) + else: + print("No clients were connected!") + + @staticmethod + def request_to_selected(requested_value): + if Client.clients: + for client in Client.clients.values(): # TODO change to selected + client.get_response(requested_value) + else: + print("No clients were connected!") + @staticmethod def broadcast(message, force_all=False): if Client.clients: @@ -208,62 +236,29 @@ class Client: self.send(Client.form_command("/endoffile")) print("File sent") - @requires_connect - def get_response(self, requested_value): - self._request_queue[requested_value] = "" - self.send(Client.form_command("request", (requested_value, ))) - - while not self._request_queue[requested_value]: - pass - - return self._request_queue.pop(requested_value) - -# UI functions -def stop_swarm(): - Client.broadcast("stop") # для тестирования - - -def land_all(): - Client.broadcast("land") - - -def disarm_all(): - Client.broadcast("disarm") - - -def takeoff_all(): - Client.broadcast("takeoff") - - -def send_animations(): - path = filedialog.askdirectory(title="Animation directory") # TODO to QT - if path: - print("Selected directory:", path) - files = [file for file in glob.glob(path+'/*.csv')] - names = [os.path.basename(file).split(".")[0] for file in files] - print(files) - for file, name in zip(files, names): - for copter in Client.clients.values(): - if name == copter.copter_id: - copter.send_file(file, "animation.csv") # TODO config - else: - print("Filename not matches with any drone connected") - # dr = next(iter(Client.clients.values())) # костыль для тестирования - # ANS = dr.get_response("someshit") - # print(ANS) - - -def send_starttime(dt=15): - timenow = time.time() - print('Now:', time.ctime(timenow), "+ dt =", dt) - Client.broadcast(Client.form_command("starttime", (str(timenow+dt), ))) - class MainWindow(QtWidgets.QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) + self.initUI() + self.show() + + def initUI(self): + #Connecting + self.ui.check_button.clicked.connect(self.check_selected) + self.ui.start_button.clicked.connect(self.send_starttime) + self.ui.pause_button.clicked.connect(self.pause_all) + self.ui.stop_button.clicked.connect(self.stop_all) + self.ui.takeoff_button.clicked.connect(self.takeoff_selected) + self.ui.land_button.clicked.connect(self.land_all) + self.ui.disarm_button.clicked.connect(self.disarm_all) + + self.ui.action_send_animations.triggered.connect(self.send_animations) + + + #Initing table and table model model = QStandardItemModel() item = QStandardItem() model.setHorizontalHeaderLabels( @@ -274,6 +269,58 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.tableView.setModel(model) self.ui.tableView.horizontalHeader().setStretchLastSection(True) + @pyqtSlot() + def check_selected(self): + #Client.request_to_selected("selfcheck") + self.ui.start_button.setEnabled(True) + self.ui.takeoff_button.setEnabled(True) + + @pyqtSlot() + def send_starttime(self): + dt = self.ui.start_delay_spin.value() + timenow = time.time() # TODO add NTP + print('Now:', time.ctime(timenow), "+ dt =", dt) + Client.send_to_selected(Client.form_command("starttime", (str(timenow + dt),))) + + @pyqtSlot() + def stop_all(self): + Client.broadcast("stop") + + @pyqtSlot() + def pause_all(self): + Client.broadcast("pause") + + @pyqtSlot() + def takeoff_selected(self): + Client.send_to_selected("takeoff") + + @pyqtSlot() + def land_all(self): + Client.broadcast("land") + + @pyqtSlot() + def disarm_all(self): + Client.broadcast("disarm") + + + @pyqtSlot() + def send_animations(self): + path = str(QFileDialog.getExistingDirectory(self, "Select Animation Directory")) + if path: + print("Selected directory:", path) + files = [file for file in glob.glob(path + '/*.csv')] + names = [os.path.basename(file).split(".")[0] for file in files] + print(files) + for file, name in zip(files, names): + for copter in Client.clients.values(): + if name == copter.copter_id: + copter.send_file(file, "animation.csv") # TODO config + else: + print("Filename not matches with any drone connected") + # dr = next(iter(Client.clients.values())) # костыль для тестирования + # ANS = dr.get_response("someshit") + # print(ANS) + # Pre-initialization # reading config @@ -295,7 +342,6 @@ ip = get_ip_address() if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) window = MainWindow() - window.show() print('Server started on', host, ip, ":", port) # print('Now:', time.ctime(get_ntp_time(NTP_HOST, NTP_PORT))) From 31cfbf6a6b851fc20a7f1c24cec0df7a5ed63bfe Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Tue, 12 Mar 2019 12:38:52 +0300 Subject: [PATCH 11/15] Pause improved --- Server/server_qt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Server/server_qt.py b/Server/server_qt.py index 8ce8919..a538274 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -288,7 +288,12 @@ class MainWindow(QtWidgets.QMainWindow): @pyqtSlot() def pause_all(self): - Client.broadcast("pause") + if self.ui.pause_button.text() == 'Pause': + Client.broadcast('pause') + self.ui.pause_button.setText('Resume') + else: + Client.broadcast('resume') + self.ui.pause_button.setText('Pause') @pyqtSlot() def takeoff_selected(self): From 44a8fa5c2295c5ac19bb9e19fce480797fc304f7 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 12 Mar 2019 13:26:45 +0300 Subject: [PATCH 12/15] Add row appending when client is connected --- Server/server_qt.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Server/server_qt.py b/Server/server_qt.py index a538274..c400688 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -109,7 +109,7 @@ class Client: if self.copter_id is None: self.copter_id = self.get_response("id") print("Got copter id:", self.copter_id) - # drone_list.insert("", "end", self.addr[0], text=self.copter_id) # TODO to qt + model.appendRow((QStandardItem(self.copter_id), )) # TODO: get responses for another columns def _send_all(self, msg): self.socket.sendall(struct.pack('>I', len(msg)) + msg) @@ -259,13 +259,6 @@ class MainWindow(QtWidgets.QMainWindow): #Initing table and table model - model = QStandardItemModel() - item = QStandardItem() - model.setHorizontalHeaderLabels( - ('copter ID', 'animation ID', 'battery V', 'battery %', 'selfcheck', 'time UTC') - ) - model.setColumnCount(6) - model.setRowCount(20) self.ui.tableView.setModel(model) self.ui.tableView.horizontalHeader().setStretchLastSection(True) @@ -326,6 +319,12 @@ class MainWindow(QtWidgets.QMainWindow): # ANS = dr.get_response("someshit") # print(ANS) +model = QStandardItemModel() +model.setHorizontalHeaderLabels( + ('copter ID', 'animation ID', 'battery V', 'battery %', 'selfcheck', 'time UTC') +) +model.setColumnCount(6) +model.setRowCount(0) # Pre-initialization # reading config From 92bdc7f4227d5cb42beadf03ad2845ffc1846402 Mon Sep 17 00:00:00 2001 From: "artem30801@gmail.com" Date: Tue, 12 Mar 2019 14:12:12 +0300 Subject: [PATCH 13/15] Added bash script to convert .ui to .py Now widget capable of window resizing --- Server/convert_ui.sh | 2 + Server/server_gui.py | 85 +++++++++---- Server/server_gui.ui | 276 +++++++++++++++++++------------------------ Server/server_qt.py | 1 - 4 files changed, 184 insertions(+), 180 deletions(-) create mode 100644 Server/convert_ui.sh diff --git a/Server/convert_ui.sh b/Server/convert_ui.sh new file mode 100644 index 0000000..4a97bac --- /dev/null +++ b/Server/convert_ui.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +python -m PyQt5.uic.pyuic -x server_gui.ui -o server_gui.py \ No newline at end of file diff --git a/Server/server_gui.py b/Server/server_gui.py index 0371724..143a3d1 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -2,59 +2,83 @@ # Form implementation generated from reading ui file 'server_gui.ui' # -# Created by: PyQt5 UI code generator 5.12 +# Created by: PyQt5 UI code generator 5.10.1 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets - class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(850, 479) + MainWindow.resize(1257, 770) self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setEnabled(True) self.centralwidget.setObjectName("centralwidget") + self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) + self.gridLayout.setObjectName("gridLayout") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) + self.horizontalLayout.setObjectName("horizontalLayout") + self.tableView = QtWidgets.QTableView(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth()) + self.tableView.setSizePolicy(sizePolicy) + self.tableView.setObjectName("tableView") + self.horizontalLayout.addWidget(self.tableView) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) + self.verticalLayout.setObjectName("verticalLayout") + self.formLayout = QtWidgets.QFormLayout() + self.formLayout.setObjectName("formLayout") self.check_button = QtWidgets.QPushButton(self.centralwidget) - self.check_button.setEnabled(False) - self.check_button.setGeometry(QtCore.QRect(680, 20, 150, 40)) + self.check_button.setEnabled(True) self.check_button.setObjectName("check_button") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.check_button) + self.start_text = QtWidgets.QLabel(self.centralwidget) + self.start_text.setObjectName("start_text") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.start_text) + self.start_delay_spin = QtWidgets.QSpinBox(self.centralwidget) + self.start_delay_spin.setObjectName("start_delay_spin") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.start_delay_spin) self.start_button = QtWidgets.QPushButton(self.centralwidget) self.start_button.setEnabled(True) - self.start_button.setGeometry(QtCore.QRect(680, 120, 150, 40)) self.start_button.setFlat(False) self.start_button.setObjectName("start_button") - self.start_delay_spin = QtWidgets.QSpinBox(self.centralwidget) - self.start_delay_spin.setGeometry(QtCore.QRect(760, 70, 50, 40)) - self.start_delay_spin.setObjectName("start_delay_spin") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.start_button) self.pause_button = QtWidgets.QPushButton(self.centralwidget) - self.pause_button.setGeometry(QtCore.QRect(680, 170, 150, 40)) self.pause_button.setObjectName("pause_button") + self.formLayout.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.pause_button) self.stop_button = QtWidgets.QPushButton(self.centralwidget) - self.stop_button.setGeometry(QtCore.QRect(680, 220, 150, 40)) self.stop_button.setObjectName("stop_button") + self.formLayout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.stop_button) + self.verticalLayout.addLayout(self.formLayout) + self.line = QtWidgets.QFrame(self.centralwidget) + self.line.setFrameShape(QtWidgets.QFrame.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line.setObjectName("line") + self.verticalLayout.addWidget(self.line) + self.formLayout_2 = QtWidgets.QFormLayout() + self.formLayout_2.setObjectName("formLayout_2") self.takeoff_button = QtWidgets.QPushButton(self.centralwidget) self.takeoff_button.setEnabled(True) - self.takeoff_button.setGeometry(QtCore.QRect(680, 280, 150, 40)) self.takeoff_button.setObjectName("takeoff_button") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.takeoff_button) self.land_button = QtWidgets.QPushButton(self.centralwidget) - self.land_button.setGeometry(QtCore.QRect(680, 330, 150, 40)) self.land_button.setObjectName("land_button") + self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.land_button) self.disarm_button = QtWidgets.QPushButton(self.centralwidget) - self.disarm_button.setGeometry(QtCore.QRect(680, 380, 150, 40)) self.disarm_button.setObjectName("disarm_button") - self.start_text = QtWidgets.QLabel(self.centralwidget) - self.start_text.setGeometry(QtCore.QRect(680, 70, 71, 40)) - self.start_text.setObjectName("start_text") - self.secs_text = QtWidgets.QLabel(self.centralwidget) - self.secs_text.setGeometry(QtCore.QRect(820, 70, 10, 40)) - self.secs_text.setObjectName("secs_text") - self.tableView = QtWidgets.QTableView(self.centralwidget) - self.tableView.setGeometry(QtCore.QRect(20, 20, 640, 400)) - self.tableView.setObjectName("tableView") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.disarm_button) + self.verticalLayout.addLayout(self.formLayout_2) + self.horizontalLayout.addLayout(self.verticalLayout) + self.horizontalLayout.setStretch(0, 1) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 850, 25)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1257, 39)) self.menubar.setObjectName("menubar") self.menuOptions = QtWidgets.QMenu(self.menubar) self.menuOptions.setObjectName("menuOptions") @@ -74,16 +98,25 @@ class Ui_MainWindow(object): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Clever Drone Animation Player")) self.check_button.setText(_translate("MainWindow", "Preflight check")) + self.start_text.setText(_translate("MainWindow", "Start after")) + self.start_delay_spin.setSuffix(_translate("MainWindow", " seconds")) self.start_button.setText(_translate("MainWindow", "Start animation")) self.pause_button.setText(_translate("MainWindow", "Pause")) self.stop_button.setText(_translate("MainWindow", "Stop")) self.takeoff_button.setText(_translate("MainWindow", "Takeoff")) self.land_button.setText(_translate("MainWindow", "Land")) self.disarm_button.setText(_translate("MainWindow", "Disarm")) - self.start_text.setText(_translate("MainWindow", "Start after")) - self.secs_text.setText(_translate("MainWindow", "s")) self.menuOptions.setTitle(_translate("MainWindow", "Actions")) self.action_send_animations.setText(_translate("MainWindow", "Send Animations")) self.action_send_configurations.setText(_translate("MainWindow", "Send Configurations")) +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_()) + diff --git a/Server/server_gui.ui b/Server/server_gui.ui index b1e4bdc..6060942 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -6,171 +6,141 @@ 0 0 - 850 - 479 + 1257 + 770 Clever Drone Animation Player - - - false - - - - 680 - 20 - 150 - 40 - - - - Preflight check - - - - - true - - - - 680 - 120 - 150 - 40 - - - - Start animation - - - false - - - - - - 760 - 70 - 50 - 40 - - - - - - - 680 - 170 - 150 - 40 - - - - Pause - - - - - - 680 - 220 - 150 - 40 - - - - Stop - - - - - true - - - - 680 - 280 - 150 - 40 - - - - Takeoff - - - - - - 680 - 330 - 150 - 40 - - - - Land - - - - - - 680 - 380 - 150 - 40 - - - - Disarm - - - - - - 680 - 70 - 71 - 40 - - - - Start after - - - - - - 820 - 70 - 10 - 40 - - - - s - - - - - - 20 - 20 - 640 - 400 - - - + + true + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + + + + QLayout::SetMaximumSize + + + + + + + true + + + Preflight check + + + + + + + Start after + + + + + + + seconds + + + + + + + true + + + Start animation + + + false + + + + + + + Pause + + + + + + + Stop + + + + + + + + + Qt::Horizontal + + + + + + + + + true + + + Takeoff + + + + + + + Land + + + + + + + Disarm + + + + + + + + + + 0 0 - 850 - 25 + 1257 + 39 diff --git a/Server/server_qt.py b/Server/server_qt.py index a538274..16fdaf0 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -257,7 +257,6 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.action_send_animations.triggered.connect(self.send_animations) - #Initing table and table model model = QStandardItemModel() item = QStandardItem() From 1153dbcc50f4f43ef95af9e40fcf87812061ed34 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 12 Mar 2019 18:08:49 +0300 Subject: [PATCH 14/15] Add Server/convert_ui.sh script to .gitigrone --- .gitignore | 1 + Server/convert_ui.sh | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 Server/convert_ui.sh diff --git a/.gitignore b/.gitignore index 4db4998..38f9a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -104,5 +104,6 @@ venv.bak/ .mypy_cache/ .vscode/settings.json Server/tests.py +Server/convert_ui.sh Drone/test_animation/ Drone/animation.csv diff --git a/Server/convert_ui.sh b/Server/convert_ui.sh deleted file mode 100644 index 4a97bac..0000000 --- a/Server/convert_ui.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -python -m PyQt5.uic.pyuic -x server_gui.ui -o server_gui.py \ No newline at end of file From 9e9a42be51c408375e1e9dd1948ab0e13db037c5 Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Wed, 13 Mar 2019 12:40:20 +0300 Subject: [PATCH 15/15] Animation pause fix --- Drone/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Drone/client.py b/Drone/client.py index c17d1c8..d2c6606 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -125,6 +125,7 @@ def animation_player(running_event, stop_event): for frame in frames: running_event.wait() if stop_event.is_set(): + running_animation_event.clear() break play_animation.animate_frame(frame) @@ -141,7 +142,6 @@ running_animation_event = threading.Event() def start_animation(*args, **kwargs): animation_thread = threading.Thread(target=animation_player, args=(running_animation_event, stop_animation_event)) - play_animation.read_animation_file(animation_file) print("Starting animation!") running_animation_event.set() stop_animation_event.clear() @@ -160,7 +160,6 @@ def pause_animation(): def stop_animation(): stop_animation_event.set() - running_animation_event.clear() print("Stopping animation") # animation_thread.join() @@ -231,6 +230,7 @@ try: FlightLib.land1() # TODO dont forget change back to land elif command == 'disarm': FlightLib.arming(False) + elif command == 'request': request_target = args[0] print("Got request for:", request_target)