Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,13 @@ The COM port tool is compatible with:
[![OS - Linux](https://img.shields.io/badge/OS-Linux-blue?logo=linux&logoColor=white)](https://www.linux.org/ "Go to Linux homepage")
[![OS - Windows](https://img.shields.io/badge/OS-Windows-blue?logo=windows&logoColor=white)](https://www.microsoft.com/ "Go to Microsoft homepage")

To use the tool, follow these steps:
First of all, please ensure that you installed dependencies already.
1. Run the program.
2. Select a serial port and configure parameters.
3. Start communication using the **Start** button.
4. Send or receive data as needed.
5. Save received data or clear buffers using the respective buttons.

```
python main.py
```

Then the project can be run with:

```
pip install -r requirements.txt
```

![Project SS](https://github.com/mcagriaksoy/Serial-Communication-GUI-Program/blob/master/img/Screenshot_v2024_07_2.jpg)
![Project SS](https://github.com/mcagriaksoy/Serial-Communication-GUI-Program/blob/master/img/Screenshot_v2025_04.jpg)

If you encounter any problems while using the COM port tool, try these solutions:

Expand All @@ -62,9 +55,9 @@ If you have any questions or feedback, please contact me.

## Dependencies

[![PyQt - >= 6.0](https://img.shields.io/badge/PyQt->_6.0-2ea44f)](https://wiki.python.org/moin/PyQt)
[![PyQt_sip - >= 13.0](https://img.shields.io/badge/PyQt_sip->_13.0-2ea44f)](https://pypi.org/project/PyQt6-sip/)
[![PySerial - >= 3.0](https://img.shields.io/badge/PyQt->_3.0-2ea44f)](https://pypi.org/project/pyserial/)
[![PySide - >= 6.0](https://img.shields.io/badge/PySide->_6.0-2ea44f)](https://wiki.python.org/moin/PySide)
[![PySide_sip - >= 13.0](https://img.shields.io/badge/PySide_sip->_13.0-2ea44f)](https://pypi.org/project/PySide6-sip/)
[![PySerial - >= 3.0](https://img.shields.io/badge/PySide->_3.0-2ea44f)](https://pypi.org/project/pyserial/)

<h2>Documentation</h2>
<div align="center">
Expand All @@ -79,6 +72,21 @@ pyinstaller --noconfirm --onefile --windowed --icon "ui/icon.ico" "src/main.py"

```

## Changes

### V1.4.0 - 2025 Update

Feature: Added basic_view_enabled and advanced_view_enabled methods to toggle UI layouts visibility.
Implemented start_loop and stop_loop for managing serial communication with threading.
Added on_save_txt_button_clicked to save received data to a .txt file.

Improvement: Enhanced error handling for serial communication and worker threads.
Added visual feedback for serial port selection and connection status.

Bug Fix: Fixed UI responsiveness during active serial communication.

Refactor: Organized serial communication logic into reusable methods.

</div>
<h2>License</h2>
Released under <a href="/LICENSE">GNU General Public License v3.0</a> by <a href="https://github.com/mcagriaksoy">@mcagriaksoy</a>.
Expand Down
1 change: 1 addition & 0 deletions Serial-Communication-GUI-Program.pyproject
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"src/ui_main.py",
"ui/icon.ico",
"ui/main_window.ui",
"ui/resources.qrc",
"ui/ui_main_window.py"
]
}
38 changes: 27 additions & 11 deletions Serial-Communication-GUI-Program.pyproject.user
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 14.0.2, 2024-12-04T10:44:32. -->
<!-- Written by QtCreator 16.0.0, 2025-04-15T11:04:03. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
Expand All @@ -13,8 +13,8 @@
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
Expand All @@ -33,6 +33,7 @@
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
Expand Down Expand Up @@ -151,7 +152,7 @@
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph &quot;dwarf,4096&quot; -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">mainwindow.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Projects/Qt_playground/Serial-Communication-GUI-Program/mainwindow.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Projects/Qt_playground/Serial-Communication-GUI-Program/mainwindow.py</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
Expand All @@ -168,7 +169,7 @@
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph &quot;dwarf,4096&quot; -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src\ui_config.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Projects/Qt_playground/Serial-Communication-GUI-Program/src/ui_config.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Projects/Qt_playground/Serial-Communication-GUI-Program/src/ui_config.py</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
Expand All @@ -185,13 +186,11 @@
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph &quot;dwarf,4096&quot; -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src\ui_main.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Projects/Qt_playground/Serial-Communication-GUI-Program/src/ui_main.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Projects/Qt_playground/Serial-Communication-GUI-Program/src/ui_main.py</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">C:\Projects\Qt_playground\Serial-Communication-GUI-Program\src\ui_main.py</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">C:/Projects/Qt_playground/Serial-Communication-GUI-Program/src</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.3">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
Expand All @@ -204,15 +203,32 @@
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph &quot;dwarf,4096&quot; -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">main.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Projects/Qt_playground/Serial-Communication-GUI-Program/main.py</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Projects/Qt_playground/Serial-Communication-GUI-Program/main.py</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">C:\Projects\Qt_playground\Serial-Communication-GUI-Program\main.py</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">C:/Projects/Qt_playground/Serial-Communication-GUI-Program</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">4</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.4">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph &quot;dwarf,4096&quot; -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">main.py2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Users/aksoym/Desktop/Serial-Communication-GUI-Program/main.py</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">true</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">C:\Users\aksoym\Desktop\Serial-Communication-GUI-Program\main.py</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">C:/Users/aksoym/Desktop/Serial-Communication-GUI-Program</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">5</value>
</valuemap>
</data>
<data>
Expand Down
Binary file removed img/Screenshot_v2024_07_2.jpg
Binary file not shown.
Binary file added img/Screenshot_v2025_04.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
137 changes: 137 additions & 0 deletions src/action_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
"""
@file action_ui.py
@brief Action UI for the application.
@details This module provides the Action UI for the application, allowing users to interact with the system.
"""
try:
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox
from PySide6.QtCore import QFile
except ImportError:
print("PySide6 is not installed. Please install it to use this module.")


def action_save_as(ui):
"""
Save the current state of the application as a new file.
"""
# Open the file dialog to select the save location and file name
file_name, _ = QFileDialog.getSaveFileName(
None, 'Save File', '', 'Text Files (*.txt)')
if file_name:
with open(file_name, 'w') as file:
text = ui.data_textEdit.toPlainText() # Access the textEdit element
file.write(text)

def action_save(ui):
"""
Save the current state of the application to the existing file.
"""
# Check if a file is already open
if hasattr(ui, 'current_file'):
# Save the current state to the existing file
with open(ui.current_file, 'w') as file:
file.write(ui.data_textEdit.toPlainText())
else:
# If no file is open, call action_save_as to prompt for a file name
action_save_as(ui)

def basic_view_enabled(ui):
""" Hide specific layouts in the UI for basic view """
# Hide all widgets in the verticalLayout_config
for i in range(ui.verticalLayout_config.count()):
widget = ui.verticalLayout_config.itemAt(i).widget()
if widget:
widget.setVisible(False)

# Optionally, hide all widgets in the formLayout_config
for i in range(ui.formLayout_config.count()):
widget = ui.formLayout_config.itemAt(i).widget()
if widget:
widget.setVisible(False)

def advanced_view_enabled(ui):
""" Show specific layouts in the UI for advanced view """
# Show all widgets in the verticalLayout_config
for i in range(ui.verticalLayout_config.count()):
widget = ui.verticalLayout_config.itemAt(i).widget()
if widget:
widget.setVisible(True)

# Optionally, show all widgets in the formLayout_config
for i in range(ui.formLayout_config.count()):
widget = ui.formLayout_config.itemAt(i).widget()
if widget:
widget.setVisible(True)

def clear_buffer(ui):
""" Clear the buffer """
ui.data_textEdit.clear()
ui.send_data_text.clear()

def show_about_dialog(ui):
""" Show the about dialog """
# Crete a message box to display the about information
msg_box = QMessageBox()
msg_box.setWindowTitle("About")
msg_box.setText("AFCOM Client v1.4.0.0 (C) 2020 - 2025 \r\n\r\nAuthor: Mehmet Cagri Aksoy \r\ngithub.com/mcagriaksoy")
msg_box.setIcon(QMessageBox.Information)
msg_box.setStandardButtons(QMessageBox.Ok)
msg_box.setDefaultButton(QMessageBox.Ok)
msg_box.setModal(True)
msg_box.exec() # Show the message box modally


def show_help_dialog(ui):
""" Show the help dialog """
# Load the help.ui file
file_path = "ui/help.ui" # Adjust the path if necessary
ui_file = QFile(file_path)
if not ui_file.exists():
QMessageBox.critical(None, "Error", f"Help UI file not found: {file_path}")
return

ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
help_dialog = loader.load(ui_file)
ui_file.close()

if help_dialog:
# Show the help dialog as a modal dialog
help_dialog.setWindowTitle("Help")
help_dialog.setModal(True)
help_dialog.exec()
else:
QMessageBox.critical(None, "Error", "Failed to load the help UI.")

def show_settings_dialog(ui):
""" Show the settings dialog """
# Load the settings.ui file
file_path = "ui/settings.ui" # Adjust the path if necessary
ui_file = QFile(file_path)
if not ui_file.exists():
QMessageBox.critical(None, "Error", f"Settings UI file not found: {file_path}")
return

ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
settings_dialog = loader.load(ui_file)
ui_file.close()

if settings_dialog:
# Show the settings dialog as a modal dialog
settings_dialog.setWindowTitle("Settings")
settings_dialog.setModal(True)
settings_dialog.exec()
else:
QMessageBox.critical(None, "Error", "Failed to load the settings UI.")

def check_for_updates(ui):
""" Check for updates """
# Placeholder function for checking updates
# You can implement the actual update check logic here
QMessageBox.information(ui, "Check for Updates", "No updates available at this time.")



Empty file added src/help.py
Empty file.
Loading