├── .gitignore
├── Credentials.pickle
├── LICENSE
├── README.md
├── build-demo.py
├── pyqt-demo.pdy
├── pyqt-demo.py
├── pyqt5_androiddeploy_instruct.rst
└── pyqtclient.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/Credentials.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bitmessage/PyBMAndroidQt/18f723c858eb227836906f566cf2a07ee8ac0189/Credentials.pickle
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Bitmessage
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PyBMAndroidQt
2 | PyBitmessage API frontend for Android using QT5 and python 3
3 |
--------------------------------------------------------------------------------
/build-demo.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2018, Riverbank Computing Limited
2 | # All rights reserved.
3 | #
4 | # Redistribution and use in source and binary forms, with or without
5 | # modification, are permitted provided that the following conditions are met:
6 | #
7 | # 1. Redistributions of source code must retain the above copyright notice,
8 | # this list of conditions and the following disclaimer.
9 | #
10 | # 2. Redistributions in binary form must reproduce the above copyright notice,
11 | # this list of conditions and the following disclaimer in the documentation
12 | # and/or other materials provided with the distribution.
13 | #
14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 | # POSSIBILITY OF SUCH DAMAGE.
25 |
26 |
27 | import argparse
28 | import os
29 | import shutil
30 | import subprocess
31 | import sys
32 |
33 |
34 | def run(args):
35 | """ Run a command and terminate if it fails. """
36 | print(args, '##################################### args in run method. ##################')
37 | try:
38 | ec = subprocess.call(' '.join(args), shell=True)
39 | except OSError as e:
40 | print("Execution failed:", e, file=sys.stderr)
41 | ec = 1
42 |
43 | if ec:
44 | sys.exit(ec)
45 |
46 |
47 | # Parse the command line.
48 | parser = argparse.ArgumentParser()
49 | parser.add_argument('--installed-qt-dir',
50 | help="the name of a directory containing pre-built Qt installations",
51 | metavar="DIR")
52 | parser.add_argument('--no-sysroot', help="do not build the sysroot",
53 | action='store_true')
54 | parser.add_argument('--source-dir',
55 | help="a directory containing the source packages", metavar="DIR",
56 | dest='source_dirs', action='append')
57 | parser.add_argument('--target', help="the target platform", default='')
58 | parser.add_argument('--quiet', help="disable progress messages",
59 | action='store_true')
60 | parser.add_argument('--verbose', help="enable verbose progress messages",
61 | action='store_true')
62 | cmd_line_args = parser.parse_args()
63 | build_sysroot = not cmd_line_args.no_sysroot
64 | installed_qt_dir = cmd_line_args.installed_qt_dir
65 | source_dirs = cmd_line_args.source_dirs
66 | target = cmd_line_args.target
67 | quiet = cmd_line_args.quiet
68 | verbose = cmd_line_args.verbose
69 |
70 | # Pick a default target if none is specified.
71 | if not target:
72 | if sys.platform == 'win32':
73 | # MSVC2015 is v14, MSVC2017 is v15.
74 | vs_major = os.environ.get('VisualStudioVersion', '0.0').split('.')[0]
75 |
76 | if vs_major == '15':
77 | is_32 = (os.environ.get('VSCMD_ARG_TGT_ARCH') != 'x64')
78 | elif vs_major == '14':
79 | is_32 = (os.environ.get('Platform') != 'X64')
80 | else:
81 | # Default to 64 bits.
82 | is_32 = False
83 |
84 | target = 'win-' + ('32' if is_32 else '64')
85 | elif sys.platform == 'darwin':
86 | target = 'macos-64'
87 | elif sys.platform.startswith('linux'):
88 | import struct
89 |
90 | target = 'linux-{0}'.format(8 * struct.calcsize('P'))
91 | else:
92 | print("Unsupported platform:", sys.platform, file=sys.stderr)
93 | sys.exit(2)
94 |
95 | # Make sure the Qt directory was specified if it is needed.
96 | if target in ('android-32', 'ios-64') and not installed_qt_dir:
97 | print("--installed-qt-dir must be specified for", target, file=sys.stderr)
98 | sys.exit(2)
99 |
100 | # Create the list of directories to search for source packages and Qt.
101 | if not source_dirs:
102 | source_dirs = ['.']
103 |
104 | if installed_qt_dir:
105 | source_dirs.insert(0, installed_qt_dir)
106 |
107 | source_dirs = [os.path.abspath(s) for s in source_dirs]
108 |
109 | # Anchor everything from the directory containing this script.
110 | os.chdir(os.path.dirname(os.path.abspath(__file__)))
111 |
112 | sysroot_dir = 'sysroot-' + target
113 | print("sysroot_dir in build-demo.py================================",sysroot_dir)
114 | build_dir = 'build-' + target
115 | host_bin_dir = os.path.abspath(os.path.join(sysroot_dir, 'host', 'bin'))
116 | print("host_bin_dir in build-demo.py==================================",host_bin_dir)
117 |
118 | # Build sysroot.
119 | if build_sysroot:
120 | args = ['pyqtdeploy-sysroot', '--target', target, '--sysroot', sysroot_dir]
121 | print("args in build-demo.py============================",args)
122 | print("sysroot_dir in build-demo.py============================",sysroot_dir)
123 |
124 | print("source_dirs in build-demo.py=================================",source_dirs)
125 | for s in source_dirs:
126 | args.append('--source-dir')
127 | args.append(s)
128 | print("args in build-demo.py=================================",args)
129 |
130 | if quiet:
131 | args.append('--quiet')
132 |
133 | if verbose:
134 | args.append('--verbose')
135 |
136 | args.append('sysroot.json')
137 |
138 | run(args)
139 |
140 | # Build the demo.
141 | shutil.copy('pyqt-demo.py', os.path.join('data', 'pyqt-demo.py.dat'))
142 |
143 | run(['pyqtdeploy-build', '--target', target, '--sysroot', sysroot_dir,
144 | '--build-dir', build_dir, 'pyqt-demo.pdy'])
145 |
146 | # Run qmake. Use the qmake left by pyqtdeploy-sysroot.
147 | os.chdir(build_dir)
148 | run([os.path.join(host_bin_dir, 'qmake')])
149 |
150 | # Run make. (When targeting iOS we leave it to Xcode.)
151 | if target.startswith('ios'):
152 | pass
153 | else:
154 | # We only support MSVC on Windows.
155 | make = 'nmake' if sys.platform == 'win32' else 'make'
156 |
157 | run([make])
158 |
159 | if target.startswith('android'):
160 | run([make, 'INSTALL_ROOT=deploy', 'install'])
161 | run([os.path.join(host_bin_dir, 'androiddeployqt'), '--input',
162 | 'android-libpyqt-demo.so-deployment-settings.json', '--output',
163 | 'deploy'])
164 |
165 | # Tell the user where the demo is.
166 | if target.startswith('android'):
167 | apk_dir = os.path.join(build_dir, 'deploy', 'bin')
168 | print("""The QtApp-debug.apk file can be found in the '{0}'
169 | directory. Run adb to install it to a simulator.""".format(apk_dir))
170 |
171 | elif target.startswith('ios'):
172 | print("""The pyqt-demo.xcodeproj file can be found in the '{0}' directory.
173 | Run Xcode to build the app and run it in the simulator or deploy it to a
174 | device.""".format(build_dir))
175 |
176 | elif target.startswith('win') or sys.platform == 'win32':
177 | print("The pyqt-demo executable can be found in the '{0}' directory.".format(os.path.join(build_dir, 'release')))
178 |
179 | else:
180 | print("The pyqt-demo executable can be found in the '{0}' directory.".format(build_dir))
181 |
--------------------------------------------------------------------------------
/pyqt-demo.pdy:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/pyqt-demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import xmlrpc.client
3 | import base64
4 | import pickle
5 | import logging
6 | import json
7 | from PyQt5 import QtCore, QtWidgets
8 | from PyQt5.QtWidgets import (QApplication, QPushButton,QDialog,QFormLayout,QGraphicsDropShadowEffect,
9 | QHBoxLayout,QTabWidget,QMessageBox,QToolBar,QProgressBar,
10 | QFrame,QGridLayout,QSizePolicy, QSpacerItem, QToolButton,QCompleter,
11 | QVBoxLayout,QMenuBar,QStyleFactory,QMainWindow, QWidget, QTextEdit,
12 | QLineEdit,QToolButton,QAction,QSplitter,QComboBox,QFileDialog,QPlainTextEdit,
13 | QTableWidgetItem, QRadioButton,QLabel,QTableWidget,QListWidget,QListWidgetItem)
14 | from PyQt5.QtCore import QCoreApplication
15 | from PyQt5.QtCore import QSize, QRect,Qt
16 | from PyQt5.QtGui import QRegExpValidator,QIcon, QPixmap, QColor
17 |
18 | class ToolBars(QWidget):
19 | def __init__ (self):
20 | super(ToolBars, self).__init__()
21 | self.tools = QToolBar()
22 | self.tools.showMaximized()
23 | self.toolbar = QToolButton()
24 | self.toolbar.showMaximized()
25 | self.toolbar.setIcon(QIcon("images/images.png"))
26 | # self.toolbar.setIconSize(QSize(800,800))
27 | self.toolbar.setPopupMode(QToolButton.InstantPopup)
28 | self.toolbar.setAutoRaise( True )
29 | self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
30 | self.toolbar.setCheckable( True )
31 | self.tools.addWidget(self.toolbar)
32 | self.toolbar.setFixedSize(100,100)
33 |
34 | self.inbox = QAction(QIcon("images/sign.png"),"Inbox",self)
35 | self.sends = QAction(QIcon("images/sign.png"),"Send",self)
36 | self.create = QAction(QIcon("images/sign.png"),"Create",self)
37 | self.trash = QAction(QIcon("images/sign.png"),"Trash",self)
38 | self.draft = QAction(QIcon("images/sign.png"),"Draft",self)
39 | self.toolbar.addAction(self.inbox)
40 | self.toolbar.addAction(self.create)
41 | self.toolbar.addAction(self.sends)
42 | self.toolbar.addAction(self.trash)
43 | self.toolbar.addAction(self.draft)
44 | self.tools.setStyleSheet('''
45 | color: rgb(0, 0, 255);
46 | font-size: 100;
47 | font-weight: 1000;
48 | ''')
49 | self.toolbar.setStyleSheet('''
50 | background-color: rgb(255, 0, 0);
51 | ''')
52 |
53 |
54 | self.combo=QComboBox()
55 | self.combo.setMaximumWidth(800)
56 | self.combo.setFixedHeight(200)
57 | ides = api.listAddresses()
58 | new_ides = json.loads(ides)
59 | list_ides = new_ides['addresses']
60 | list_com = []
61 | for address in list_ides:
62 | ids = address['address']
63 | labels = address['label']
64 | add_label2 = labels +"<"+ids+">"
65 | self.combo.addItem(add_label2)
66 | self.tools.addWidget(self.combo)
67 |
68 |
69 |
70 | class QCustomQWidget (QWidget):
71 | def __init__ (self, parent = None):
72 | super(QCustomQWidget, self).__init__(parent)
73 | self.textQVBoxLayout = QVBoxLayout()
74 | self.textUpQLabel = QLabel()
75 | self.textDownQLabel = QLabel()
76 | self.textQVBoxLayout.addWidget(self.textUpQLabel)
77 | self.textQVBoxLayout.addWidget(self.textDownQLabel)
78 | self.allQHBoxLayout = QHBoxLayout()
79 | self.iconQLabel = QLabel()
80 | self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
81 | self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
82 | self.setLayout(self.allQHBoxLayout)
83 | # setStyleSheet
84 | self.textUpQLabel.setStyleSheet('''
85 | color: rgb(0, 0, 255);
86 | ''')
87 | self.textDownQLabel.setStyleSheet('''
88 | color: rgb(255, 0, 0);
89 | ''')
90 |
91 | def setTextUp(self, text):
92 | self.textUpQLabel.setText(text)
93 |
94 | def setTextDown(self, text):
95 | self.textDownQLabel.setText(text)
96 |
97 | def setIcon(self, imagePath):
98 | self.iconQLabel.setPixmap(QPixmap(imagePath))
99 |
100 | def cleared(self):
101 | item = self.allQHBoxLayout
102 | if item != None :
103 | widget = item.widget()
104 | if widget != None:
105 | self.allQHBoxLayout.removeWidget(widget)
106 | widget.deleteLater()
107 |
108 | class TrashWindow(ToolBars):
109 | def __init__(self, parent=None):
110 | super(TrashWindow, self).__init__()
111 | self.widget = QWidget(self)
112 |
113 | fbox = QFormLayout(self)
114 | fbox.addRow(self.tools)
115 |
116 | l1 = QLabel("To: ",self)
117 | self.add1 = QLineEdit(self)
118 | self.add1.setText("BM-2cWyUfBdY2FbgyuCb7abFZ49JYxSzUhNFe")
119 | fbox.addRow(l1,self.add1)
120 |
121 | l2 = QLabel("From: ",self)
122 | self.add2 = QLineEdit()
123 | self.add2.setText("BM-2cV5v5bqzVznBXk3fsLaAfPkCnC42Dhwc4")
124 | fbox.addRow(l2,self.add2)
125 |
126 | class DraftWindow(ToolBars):
127 | def __init__(self, parent=None):
128 | super(DraftWindow, self).__init__()
129 | fbox = QVBoxLayout(self)
130 | fbox.addWidget(self.tools)
131 |
132 | self.myQListWidget = QListWidget(self)
133 | self.myQListWidget.showMaximized()
134 | for name, message, icon in [
135 | ('Meyoko', 'hello...', 'images/sign.png'),
136 | ('Nyaruko', 'how are you..', 'images/sign.png'),
137 | ('Louise', 'Dear friend', 'images/sign.png'),
138 | ('mily', 'hello...', 'images/sign.png')]:
139 | # Create QCustomQWidget
140 | myQCustomQWidget = QCustomQWidget()
141 | myQCustomQWidget.setTextUp(name)
142 | myQCustomQWidget.setTextDown(message)
143 | myQCustomQWidget.setIcon(icon)
144 | # Create QListWidgetItem
145 | myQListWidgetItem = QListWidgetItem(self.myQListWidget)
146 | # Set size hint
147 | myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
148 | # Add QListWidgetItem into QListWidget
149 | self.myQListWidget.addItem(myQListWidgetItem)
150 | self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
151 | fbox.removeWidget(self.myQListWidget)
152 | fbox.addWidget(self.myQListWidget)
153 |
154 | class CreateWindow(ToolBars):
155 | def __init__(self, parent=None):
156 | super(CreateWindow, self).__init__()
157 | self.widget = QWidget(self)
158 | fbox = QFormLayout(self)
159 | fbox.addRow(self.tools)
160 |
161 | l1 = QLabel("To: ",self)
162 | self.add1 = QComboBox(self)
163 | self.add1.setMaximumWidth(800)
164 | self.add1.setFixedHeight(100)
165 | self.add1.setCurrentText('choose')
166 | self.add1.currentTextChanged.connect(self.selectionchange)
167 | to_add = api.listAddressBookEntries()
168 | new_to = json.loads(to_add)
169 | list_to = new_to['addresses']
170 | for address in list_to:
171 | add1 = address['address']
172 | label1 = base64.b64decode(address['label']).decode("utf-8")
173 | add_label1 = label1 +"<"+add1+">"
174 | self.add1.addItem(add_label1)
175 | fbox.addRow(l1,self.add1)
176 |
177 | l2 = QLabel("From: ",self)
178 | self.add2 = QComboBox()
179 | self.add2.setMaximumWidth(800)
180 | self.add2.setFixedHeight(100)
181 | self.add2.setCurrentText('choose')
182 | self.add2.currentTextChanged.connect(self.selectionchange1)
183 | from_add = api.listAddresses()
184 | new_from = json.loads(from_add)
185 | list_from = new_from['addresses']
186 | for address in list_from:
187 | add2 = address['address']
188 | label2 = address['label']
189 | add_label2 = label2 +"<"+add2+">"
190 | self.add2.addItem(add_label2)
191 | fbox.addRow(l2,self.add2)
192 |
193 | l4 = QLabel("Subject: ",self)
194 | self.add4 = QLineEdit(self)
195 | fbox.addRow(l4,self.add4)
196 |
197 | l3 = QLabel("Message: ",self)
198 | fbox.addRow(l3)
199 | self.add3 = QTextEdit()
200 | fbox.addRow(self.add3)
201 |
202 | self.send = QPushButton(self)
203 | self.send.setText("SEND")
204 | self.send.clicked.connect(self.addition)
205 | fbox.addRow(self.send)
206 |
207 | def selectionchange1(self,i):
208 | sub = i[i.find('<')+1:i.find('>')]
209 | self.fromAddress = sub
210 |
211 | def selectionchange(self,i):
212 | sub = i[i.find('<')+1:i.find('>')]
213 | self.toAddress = sub
214 |
215 | def addition(self):
216 | subject = base64.b64encode(bytes(self.add4.text(), 'utf-8')).decode("utf-8")
217 | message = base64.b64encode(bytes(self.add3.toPlainText(), 'utf-8')).decode("utf-8")
218 | sums = api.sendMessage(self.toAddress, self.fromAddress, subject, message)
219 |
220 |
221 | class InboxWindow(ToolBars):
222 | def __init__(self, parent=None,is_change= None,id_default = "BM-2cXtYXvA1yZ1KdjHufxqDEGPwd3Xt4JEdk"):
223 | super(InboxWindow, self).__init__()
224 | self.widget = QWidget(self)
225 |
226 | fbox = QVBoxLayout(self)
227 | fbox.addWidget(self.tools)
228 | if is_change is not None:
229 | self.combo.clear()
230 | self.combo.addItem(is_change)
231 | ides = api.listAddresses()
232 | new_ides = json.loads(ides)
233 | list_ides = new_ides['addresses']
234 | list_com = []
235 | for address in list_ides:
236 | ids = address['address']
237 | labels = address['label']
238 | add_label2 = labels +"<"+ids+">"
239 | if is_change != add_label2:
240 | self.combo.addItem(add_label2)
241 | else:
242 | pass
243 |
244 | self.myQListWidget = QListWidget(self)
245 | self.myQListWidget.showMaximized()
246 | newids = base64.b64encode(bytes(id_default, 'utf-8')).decode("utf-8")
247 | messages = api.getInboxMessagesByAddress(id_default)
248 | new_messages = json.loads(messages)
249 | inbox_list = new_messages['inboxMessages']
250 |
251 | for inbox in inbox_list:
252 | add_from = inbox['fromAddress']
253 | subject = base64.b64decode(inbox['subject']).decode("utf-8")
254 | # Create QCustomQWidget
255 | myQCustomQWidget = QCustomQWidget()
256 | myQCustomQWidget.setTextUp(add_from)
257 | myQCustomQWidget.setTextDown(subject)
258 | # Create QListWidgetItem
259 | myQListWidgetItem = QListWidgetItem(self.myQListWidget)
260 | # Set size hint
261 | myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
262 | # Add QListWidgetItem into QListWidget
263 | self.myQListWidget.addItem(myQListWidgetItem)
264 | self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
265 | fbox.addWidget(self.myQListWidget)
266 |
267 | class SendWindow(ToolBars):
268 | def __init__(self, parent=None,is_change= None,id_default = "BM-2cXtYXvA1yZ1KdjHufxqDEGPwd3Xt4JEdk"):
269 | super(SendWindow, self).__init__()
270 | self.widget = QWidget(self)
271 |
272 | fbox = QVBoxLayout(self)
273 | fbox.addWidget(self.tools)
274 | if is_change is not None:
275 | self.combo.clear()
276 | self.combo.addItem(is_change)
277 | ides = api.listAddresses()
278 | new_ides = json.loads(ides)
279 | list_ides = new_ides['addresses']
280 | list_com = []
281 | for address in list_ides:
282 | ids = address['address']
283 | labels = address['label']
284 | add_label2 = labels +"<"+ids+">"
285 | if is_change != add_label2:
286 | self.combo.addItem(add_label2)
287 | else:
288 | pass
289 |
290 | self.myQListWidget = QListWidget(self)
291 | self.myQListWidget.setVisible(False)
292 | self.myQListWidget.setUpdatesEnabled(True)
293 | self.myQListWidget.showMaximized()
294 | messages = api.getSentMessagesByAddress(id_default)
295 | new_messages = json.loads(messages)
296 | inbox_list = new_messages['sentMessages']
297 |
298 | for inbox in inbox_list:
299 | add_from = inbox['toAddress']
300 | subject = base64.b64decode(inbox['subject']).decode("utf-8")
301 | # Create QCustomQWidget
302 | myQCustomQWidget = QCustomQWidget()
303 | myQCustomQWidget.setTextUp(add_from)
304 | myQCustomQWidget.setTextDown(subject)
305 | # Create QListWidgetItem
306 | myQListWidgetItem = QListWidgetItem(self.myQListWidget)
307 | # Set size hint
308 | myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
309 | # Add QListWidgetItem into QListWidget
310 | self.myQListWidget.addItem(myQListWidgetItem)
311 | self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
312 | fbox.addWidget(self.myQListWidget)
313 |
314 |
315 | class UIToolTab(QWidget):
316 | def __init__(self, parent=None):
317 | super(UIToolTab, self).__init__()
318 | self.widget = QWidget(self)
319 | fbox = QFormLayout(self)
320 | fbox.setContentsMargins(100, 200, 100, 100)
321 |
322 |
323 |
324 | l1 = QLabel("Username: ",self)
325 | self.add1 = QLineEdit(self)
326 | fbox.addRow(l1,self.add1)
327 |
328 | l2 = QLabel("Password: ",self)
329 | self.add2 = QLineEdit(self)
330 | fbox.addRow(l2,self.add2)
331 |
332 | l4 = QLabel("IP Address: ",self)
333 | self.add4 = QLineEdit(self)
334 | fbox.addRow(l4,self.add4)
335 |
336 | l3 = QLabel("Port: ",self)
337 | self.add3 = QLineEdit(self)
338 | fbox.addRow(l3,self.add3)
339 |
340 | try:
341 | pickle_off = open("Credentials.json","r")
342 | emp = json.load(pickle_off)
343 | self.add1.setText(emp['username'])
344 | self.add2.setText(emp['password'])
345 | self.add4.setText(emp['ip'])
346 | self.add3.setText(emp['port'])
347 | except:
348 | pass
349 |
350 | self.connect = QPushButton(self)
351 | self.connect.setText("CONNECT")
352 | self.save = QPushButton(self)
353 | self.save.setText("SAVE")
354 | fbox.addRow(self.save,self.connect)
355 |
356 |
357 | class MainWindow(QMainWindow):
358 | def __init__(self, parent=None):
359 | super(MainWindow, self).__init__(parent)
360 | self.changes_menu = 'Inbox'
361 | self.ToolTab = UIToolTab(self)
362 | self.startUIToolTab()
363 |
364 | def startUIToolTab(self):
365 | self.setWindowTitle("CONNECT")
366 | self.setCentralWidget(self.ToolTab)
367 | self.ToolTab.connect.clicked.connect(self.verify)
368 | self.ToolTab.save.clicked.connect(self.save_data)
369 | self.show()
370 |
371 | def save_data(self):
372 | user = self.ToolTab.add1.text()
373 | password = self.ToolTab.add2.text()
374 | ip = self.ToolTab.add4.text()
375 | port = self.ToolTab.add3.text()
376 | emp = {'username':user,'password':password,'ip':ip,'port':port}
377 | credentials = open("Credentials.json","w")
378 | json.dump(emp, credentials)
379 |
380 | def verify(self):
381 | global api
382 | user = self.ToolTab.add1.text()
383 | password = self.ToolTab.add2.text()
384 | ip = self.ToolTab.add4.text()
385 | port = self.ToolTab.add3.text()
386 | api = xmlrpc.client.ServerProxy("http://%s:%s@%s:%s/" %(user,password,ip,port))
387 | sums = api.add(2,3)
388 | if isinstance(sums, int):
389 | self.startInbox()
390 | else:
391 | choice = QMessageBox.question(self, 'Incorrect',
392 | "You have Put Incorrect Credentials",
393 | QMessageBox.Yes)
394 | if choice == QMessageBox.Yes:
395 | self.ToolTab.add1.setText("")
396 | self.ToolTab.add2.setText("")
397 | self.ToolTab.add4.setText("")
398 | self.ToolTab.add3.setText("")
399 |
400 |
401 | def startInbox(self):
402 | self.Window = InboxWindow(self)
403 | self.setWindowTitle("Inbox")
404 | self.setCentralWidget(self.Window)
405 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
406 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
407 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
408 | self.show()
409 |
410 | def startSend(self):
411 | self.Window = SendWindow(self)
412 | self.setWindowTitle("Send")
413 | self.setCentralWidget(self.Window)
414 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
415 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
416 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
417 | self.show()
418 |
419 | def startCreate(self):
420 | self.Window = CreateWindow(self)
421 | self.setWindowTitle("Create")
422 | self.setCentralWidget(self.Window)
423 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
424 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
425 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
426 | self.show()
427 |
428 | def startDraft(self):
429 | self.Window = DraftWindow(self)
430 | self.setWindowTitle("Draft")
431 | self.setCentralWidget(self.Window)
432 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
433 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
434 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
435 | self.show()
436 |
437 | def startTrash(self):
438 | self.Window = TrashWindow(self)
439 | self.setWindowTitle("Trash")
440 | self.setCentralWidget(self.Window)
441 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
442 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
443 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
444 | self.show()
445 |
446 | def changedItem(self,folder,comb):
447 | if folder == 'Inbox':
448 | self.Window = InboxWindow(self,is_change=comb,id_default = self.select_ids)
449 | self.setWindowTitle("Inbox")
450 | if folder == 'Send':
451 | self.Window = SendWindow(self,is_change=comb,id_default = self.select_ids)
452 | self.setWindowTitle("Send")
453 | self.setCentralWidget(self.Window)
454 | QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
455 | self.Window.toolbar.triggered[QAction].connect(self.toolbtnpressed)
456 | self.Window.combo.currentTextChanged.connect(self.selectionchange_id)
457 | self.show()
458 |
459 |
460 | def toolbtnpressed(self,a):
461 | if a.text() == 'Inbox':
462 | self.changes_menu = 'Inbox'
463 | self.startInbox()
464 | if a.text() == 'Create':
465 | self.startCreate()
466 | if a.text() == 'Send':
467 | self.changes_menu = 'Send'
468 | self.startSend()
469 | if a.text() == 'Draft':
470 | self.startDraft()
471 | if a.text() == 'Trash':
472 | self.startTrash()
473 |
474 | def selectionchange_id(self,ids):
475 | self.select_ids = ids[ids.find('<')+1:ids.find('>')]
476 | if self.changes_menu == 'Inbox':
477 | self.changedItem('Inbox',ids)
478 |
479 | if self.changes_menu == 'Send':
480 | self.changedItem('Send',ids)
481 |
482 |
483 |
484 | if __name__ == '__main__':
485 | # logging.basicConfig(filename='/storage/emulated/0/example.txt',level=logging.INFO)
486 | app = QApplication(sys.argv)
487 | w = MainWindow()
488 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/pyqt5_androiddeploy_instruct.rst:
--------------------------------------------------------------------------------
1 | **Project** **Title**
2 |
3 | PyqtDeploy
4 |
5 | Getting Started:
6 |
7 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
8 |
9 | **Prerequisites**:
10 |
11 | 1. There will PyQt5, pyqtdeploy, Android Studio, Apache, NDK, SDK, python, Java and QTCreator installed
12 |
13 | **PyQt5**: pip3 install PyQt5
14 |
15 | **pyqtdeploy**: pip3 install pyqtdeploy
16 |
17 | **python**: python version 3.6.4
18 |
19 | **Java**: sudo apt-get install openjdk-8-jdk
20 |
21 | **QTCreator**: QTCreator version 5.10.0
22 |
23 | - wget http://download.qt.io/official_releases/qt/5.10/5.10.0/qt-opensource-linux-x64-5.10.0.run
24 |
25 | - chmod +x qt-opensource-linux-x64-5.10.0.run
26 |
27 | - ./qt-opensource-linux-x64-5.7.0.run
28 |
29 | **Apache**: apache verion apache-ant-1.10.5
30 |
31 | - wget http://mirror.its.dal.ca/apache//ant/binaries/apache-ant-1.10.5-bin.tar.gz
32 |
33 | - sudo tar -xf apache-ant-1.10.5-bin.tar.gz -C /usr/local
34 |
35 | - sudo ln -s /usr/local/apache-ant-1.10.3/ /usr/local/ant
36 |
37 | **Android** **Studio**: You can install Android Studio from: "https://developer.android.com/studio/"
38 |
39 | **NDK**: NDK version android-ndk-r10e
40 |
41 | **SDK**: You can configure the SDK from Android Studio
42 |
43 | 2. Configure the bashfile for export all relavent path. Like:
44 |
45 | export ANDROID_HOME=/opt/android/sdk
46 |
47 | export PATH=$PATH:/opt/android/sdk/platforms
48 |
49 | export PATH=$PATH:/opt/android/sdk/platforms-tools
50 |
51 | export PATH=$PATH:/opt/android/sdk/build-tools
52 |
53 | export PATH=$PATH:/opt/android/sdk/tools
54 |
55 | export PATH=$PATH:/home/cis/Documents/android-ndk-r10e
56 |
57 | export ANT_HOME=/home/cis/apache-ant-1.10.5
58 |
59 | export PATH=$PATH:/home/cis/apache-ant-1.10.5/bin
60 |
61 | export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
62 |
63 | export PATH=$PATH:$ANDROID_HOME/tools:apache-ant-1.10.5/bin/ant/bin:$JAVA_HOME/bin
64 |
65 | export JAVA_OPTS='-XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee'
66 |
67 | export ANDROID_NDK_ROOT=/home/cis/Documents/android-ndk-r10e
68 |
69 | export ANDROID_NDK_PLATFORM="android-21"
70 |
71 | export ANDROID_TOOLCHAIN=/home/cis/Documents/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/
72 |
73 | export ANDROID_NDK_TOOLCHAIN_VERSION='4.9'
74 |
75 | export SYSROOT=/home/cis/Qt5.10.0/5.10.0/android_armv7
76 |
77 |
78 | **Installing**
79 |
80 | 1. You need to install the pyqtdeploy tar package from the below link:
81 |
82 | "https://pypi.org/project/pyqtdeploy/#files" and untar the package.
83 |
84 | 2. Then you need to install the below tar files and need to place these tar files inside the "demo" folder of pyqtdeploy (which you have untar above)
85 | 1. openssl-1.0.2n.tar.gz
86 | 2. PyQt3D_gpl-5.10.1.tar.gz
87 | 3. PyQt5_gpl-5.10.1.tar.gz
88 | 4. PyQtChart_gpl-5.10.1.tar.gz
89 | 5. PyQtDataVisualization_gpl-5.10.1.tar.gz
90 | 6. PyQtPurchasing_gpl-5.10.1.tar.gz
91 | 7. Python-3.6.4.tar.xz
92 | 8. QScintilla_gpl-2.10.7.tar.xz
93 | 9. sip-4.19.8.tar.gz
94 |
95 | most of the tar files will get from : "https://sourceforge.net/projects/pyqt/files/"
96 |
97 | 3. Then you need to also place "android_armv7" this folder inside the "demo" folder of pyqtdeploy.You will get android_armv7 from QTCreator folder which will install earlier(Qt5.10.0/5.10.0/android_armv7)
98 |
99 | 4. You also need to change the pyqt-demo.pdy file. To make changes in it run command "pyqtdeploy pyqt-demo.pdy" this will open a window in which we need to change the python to which we are using and in Standard Library section you need to check the xmlrpc, json, base64 and ssl and then save it.
100 |
101 | 5. Then you need to copy the SDK folder which is configured earlier to the root of system in opt folder the folder.Structure will be /opt/android/sdk/ and you also need to give permission to android folder recursively, so that all inner folders get permission as well.
102 |
103 | **Deployment**
104 |
105 | After all the installation and setup, need to run the command where the build-demo.py file is kept.
106 |
107 | The command is:
108 |
109 | python3 build-demo.py --target android-32 --installed-qt-dir (/home/cis/Qt5.10.0/5.10.0)
110 |
--------------------------------------------------------------------------------
/pyqtclient.py:
--------------------------------------------------------------------------------
1 | import xmlrpc.client
2 | import sys
3 | import base64
4 | from PyQt5 import QtCore, QtWidgets, Qt
5 | from PyQt5.QtWidgets import QApplication, QPushButton,QFormLayout,QMainWindow, QWidget, QLabel, QLineEdit, QPlainTextEdit
6 | from PyQt5.QtGui import QRegExpValidator
7 |
8 | class Addition(QMainWindow):
9 | def __init__(self):
10 | QMainWindow.__init__(self)
11 |
12 | self.widget = QWidget()
13 | fbox = QFormLayout()
14 |
15 | l1 = QLabel("TO: ")
16 | self.add1 = QLineEdit()
17 | self.add1.setText("BM-2cWyUfBdY2FbgyuCb7abFZ49JYxSzUhNFe")
18 | self.add1.setReadOnly(True)
19 | fbox.addRow(l1,self.add1)
20 |
21 | l2 = QLabel("FROM: ")
22 | self.add2 = QLineEdit()
23 | self.add2.setText("BM-2cV5v5bqzVznBXk3fsLaAfPkCnC42Dhwc4")
24 | self.add2.setReadOnly(True)
25 | fbox.addRow(l2,self.add2)
26 |
27 | l4 = QLabel("SUBJECT: ")
28 | self.add4 = QLineEdit()
29 | fbox.addRow(l4,self.add4)
30 |
31 | l3 = QLabel("MESSAGE: ")
32 | self.add3 = QPlainTextEdit()
33 | fbox.addRow(l3,self.add3)
34 |
35 | b = QPushButton()
36 | b.setText("SEND")
37 | b.clicked.connect(self.addition)
38 | fbox.addRow(b)
39 |
40 |
41 | self.widget.setLayout(fbox)
42 | self.widget.show()
43 | self.setCentralWidget(self.widget)
44 | self.setWindowTitle("SEND")
45 |
46 | def addition(self):
47 | fromAddress = self.add2.text()
48 | toAddress = self.add1.text()
49 | subject = base64.b64encode(bytes(self.add4.text(), 'utf-8')).decode("utf-8")
50 | message = base64.b64encode(bytes(self.add3.toPlainText(), 'utf-8')).decode("utf-8")
51 | api = xmlrpc.client.ServerProxy("http://username:password@127.0.0.1:8442/")
52 | sums = str(api.sendMessage(toAddress, fromAddress, subject, message))
53 |
54 | if __name__ == "__main__":
55 | app = QApplication(sys.argv)
56 | apps = Addition()
57 | apps.show()
58 | sys.exit(app.exec_())
--------------------------------------------------------------------------------