Friday, December 22, 2023

Tips on How to Use cx_Freeze and pyArmor

 Let me share the journey of refining my program, "Monitoring Data Usage with Python," and the challenges I encountered along the way. As the program evolved to meet my specific requirements—running WAMP64, saving captured data to a database, and incorporating a user-friendly pyQt6 GUI—I encountered some hitches during testing that needed creative solutions.



One of the issues arose when executing WAMP64 from within the script. As the execution of Python code is asynchronous, a timing misalignment led to errors since the script attempted to save data to the database before WAMP64 fully loaded. To address this, I employed a workaround by running WAMP64 on a separate thread, pausing the script's execution for 30 seconds to ensure synchronization. This solution proved effective and resolved the problem at hand.

However, the real challenge surfaced when attempting to convert the Python script into an executable (.exe) file. The motivation behind this conversion was to streamline program execution, replacing the manual process with a simple double-click on the desktop icon.

Initially, I turned to pyInstaller, but it fell short of expectations. Subsequently, I switched to cx_Freeze, which showed promise but introduced an unexpected behavior—re-running the program every 30 seconds. This behavior was unacceptable, so I devised a workaround by introducing a global variable and saving it in an INI file. By initializing this variable to zero during the first load, the program executed entirely. Afterward, I set it to one, ensuring that subsequent re-executions would exit immediately. This clever solution resolved the recurring execution problem, leaving me content with the setup.


By the way, I did all developments in virtual environment using pipenv.

The command used to generate the exe file:

python setup.py build

And here is the setup.py I used:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import sys
from cx_Freeze import setup, Executable

build_exe_options = {
    "packages": ["PyQt6"],
    "includes": ["PyQt6.QtWidgets", "PyQt6.QtGui", "PyQt6.QtCore"],
    "include_files": ["data_usage.ini"],
}

base = None
if sys.platform == "win32":
    base = "Win32GUI"  # Use "Win32GUI" for a GUI application

setup(
    name="DataUsageApp",
    version="1.0",
    description="Data Usage App",
    options={"build_exe": build_exe_options},
    executables=[Executable("pyqt_hourly2.py", base=base)],
)

Considering the desire to share my program with others while keeping my code private, I decided to obfuscate the script using pyArmor. Unfortunately, this decision led to numerous errors, rendering the .exe file nonfunctional. In a stroke of insight, I copied the content of the lib folder from a previous build, pre-obfuscation, and success! The .exe file functioned as intended.

The following command line is what I used to obfuscate the python script:

pyarmor obfuscate pyqt_hourly2.py

This journey taught me valuable lessons in problem-solving and adapting to unforeseen challenges, ultimately resulting in an efficient and user-friendly program ready to benefit others.


Here is how to create the database in wam64(I used phpMyAdmin):

CREATE DATABASE IF NOT EXISTS data_usage;


And to create its table:

DROP TABLE IF EXISTS `data_usage`;

CREATE TABLE IF NOT EXISTS `data_usage` (

  `date_time` datetime NOT NULL,

  `data_usage` double NOT NULL,

  `incoming` double NOT NULL,

  `outgoing` double NOT NULL

) ENGINE=MyISAM DEFAULT CHARSET=latin1;

COMMIT; 

You may download the app here Data Usage App.

No comments:

Post a Comment