pyvguicom 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyvguicom might be problematic. Click here for more details.
- pyvguicom-1.0.0/PKG-INFO +90 -0
- pyvguicom-1.0.0/README.md +77 -0
- pyvguicom-1.0.0/pyvguicom/__init__.py +0 -0
- pyvguicom-1.0.0/pyvguicom/browsewin.py +263 -0
- pyvguicom-1.0.0/pyvguicom/htmledit.py +252 -0
- pyvguicom-1.0.0/pyvguicom/pgbox.py +752 -0
- pyvguicom-1.0.0/pyvguicom/pgbutt.py +221 -0
- pyvguicom-1.0.0/pyvguicom/pgentry.py +165 -0
- pyvguicom-1.0.0/pyvguicom/pggui.py +1336 -0
- pyvguicom-1.0.0/pyvguicom/pgsimp.py +493 -0
- pyvguicom-1.0.0/pyvguicom/pgtextview.py +804 -0
- pyvguicom-1.0.0/pyvguicom/pgutils.py +1103 -0
- pyvguicom-1.0.0/pyvguicom/pgwkit.py +718 -0
- pyvguicom-1.0.0/pyvguicom/plug.py +33 -0
- pyvguicom-1.0.0/pyvguicom/sutil.py +344 -0
- pyvguicom-1.0.0/pyvguicom/testbutt.py +97 -0
- pyvguicom-1.0.0/pyvguicom/testicons.py +368 -0
- pyvguicom-1.0.0/pyvguicom/testnums.py +132 -0
- pyvguicom-1.0.0/pyvguicom/testroot.py +157 -0
- pyvguicom-1.0.0/pyvguicom/testsimple.py +103 -0
- pyvguicom-1.0.0/pyvguicom/testtextv.py +205 -0
- pyvguicom-1.0.0/pyvguicom.egg-info/PKG-INFO +90 -0
- pyvguicom-1.0.0/pyvguicom.egg-info/SOURCES.txt +25 -0
- pyvguicom-1.0.0/pyvguicom.egg-info/dependency_links.txt +1 -0
- pyvguicom-1.0.0/pyvguicom.egg-info/top_level.txt +1 -0
- pyvguicom-1.0.0/setup.cfg +4 -0
- pyvguicom-1.0.0/setup.py +52 -0
pyvguicom-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyvguicom
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: High power secure server GUI utility helpers.
|
|
5
|
+
Home-page: https://github.com/pglen/pyvserv
|
|
6
|
+
Author: Peter Glen
|
|
7
|
+
Author-email: peterglen99@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# pyvguicom PyV GUI Common utilities
|
|
15
|
+
|
|
16
|
+
## Common GUI routines and classes
|
|
17
|
+
|
|
18
|
+
These classes are for python PyGobject (Gtk) development. They are used in
|
|
19
|
+
several projects. They act as a simplification front end for the PyGtk / PyGobject
|
|
20
|
+
classes.
|
|
21
|
+
|
|
22
|
+
A sampler of what is in there (pasted from code, in no particular order):
|
|
23
|
+
|
|
24
|
+
class CairoHelper():
|
|
25
|
+
class TextTable(Gtk.Table):
|
|
26
|
+
class TextRow(Gtk.HBox):
|
|
27
|
+
class RadioGroup(Gtk.Frame):
|
|
28
|
+
class Led(Gtk.DrawingArea):
|
|
29
|
+
class SeparatorMenuItem(Gtk.SeparatorMenuItem):
|
|
30
|
+
class Menu():
|
|
31
|
+
class MenuButt(Gtk.DrawingArea):
|
|
32
|
+
class Lights(Gtk.Frame):
|
|
33
|
+
class WideButt(Gtk.Button):
|
|
34
|
+
class ScrollListBox(Gtk.Frame):
|
|
35
|
+
class TextRow(Gtk.HBox):
|
|
36
|
+
class RadioGroup(Gtk.Frame):
|
|
37
|
+
class Led(Gtk.DrawingArea):
|
|
38
|
+
class Lights(Gtk.Frame):
|
|
39
|
+
class FrameTextView(Gtk.TextView):
|
|
40
|
+
class Label(Gtk.Label):
|
|
41
|
+
class Logo(Gtk.VBox):
|
|
42
|
+
class xSpacer(Gtk.HBox):
|
|
43
|
+
class ScrollListBox(Gtk.Frame):
|
|
44
|
+
class ListBox(Gtk.TreeView):
|
|
45
|
+
|
|
46
|
+
... and a lot more ...
|
|
47
|
+
|
|
48
|
+
## Also includes python / Gtk primitives:
|
|
49
|
+
|
|
50
|
+
def get_screen_wh():
|
|
51
|
+
def get_screen_xy():
|
|
52
|
+
def print_exception(xstr):
|
|
53
|
+
def message(strx, parent = None, title = None, icon = Gtk.MessageType.INFO):
|
|
54
|
+
def usleep(msec):
|
|
55
|
+
def tmpname(indir, template):
|
|
56
|
+
def mainloop():
|
|
57
|
+
def time_n2s(ttt):
|
|
58
|
+
def time_s2n(sss):
|
|
59
|
+
def yes_no_cancel(title, message, cancel = True, parent = None):
|
|
60
|
+
def yn_key(win, event, cancel):
|
|
61
|
+
def opendialog(parent=None):
|
|
62
|
+
def savedialog(resp):
|
|
63
|
+
def leadspace(strx):
|
|
64
|
+
|
|
65
|
+
... and a lot more ...
|
|
66
|
+
|
|
67
|
+
## Example:
|
|
68
|
+
|
|
69
|
+
The Label Button (smallbutt) takes a constructor, and feeds
|
|
70
|
+
the arguments with defaults as one would expect.
|
|
71
|
+
|
|
72
|
+
def __init__(self, textm="", widget=None, tooltip=None, font=None):
|
|
73
|
+
|
|
74
|
+
The simplification effect allows one to create a smallbutt with no arguments,
|
|
75
|
+
and still have a somewhat reasonable outcome. The label example is trivial,
|
|
76
|
+
the simplification takes a new dimension with classes like SimpleTree.
|
|
77
|
+
|
|
78
|
+
The defaults are set to a reasonable value, and the named argument(s) can be
|
|
79
|
+
set on one line. This makes the code look more compact and maintainable.
|
|
80
|
+
|
|
81
|
+
## Tests:
|
|
82
|
+
|
|
83
|
+
The test utilities can confirm correct operation; however being a visual
|
|
84
|
+
set of classes, the real test is seeing the generated UI.
|
|
85
|
+
|
|
86
|
+
See descendent projects for more examples. (pyedpro; pycal; pggui; ...)
|
|
87
|
+
|
|
88
|
+
Peter Glen
|
|
89
|
+
|
|
90
|
+
// EOF
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# pyvguicom PyV GUI Common utilities
|
|
2
|
+
|
|
3
|
+
## Common GUI routines and classes
|
|
4
|
+
|
|
5
|
+
These classes are for python PyGobject (Gtk) development. They are used in
|
|
6
|
+
several projects. They act as a simplification front end for the PyGtk / PyGobject
|
|
7
|
+
classes.
|
|
8
|
+
|
|
9
|
+
A sampler of what is in there (pasted from code, in no particular order):
|
|
10
|
+
|
|
11
|
+
class CairoHelper():
|
|
12
|
+
class TextTable(Gtk.Table):
|
|
13
|
+
class TextRow(Gtk.HBox):
|
|
14
|
+
class RadioGroup(Gtk.Frame):
|
|
15
|
+
class Led(Gtk.DrawingArea):
|
|
16
|
+
class SeparatorMenuItem(Gtk.SeparatorMenuItem):
|
|
17
|
+
class Menu():
|
|
18
|
+
class MenuButt(Gtk.DrawingArea):
|
|
19
|
+
class Lights(Gtk.Frame):
|
|
20
|
+
class WideButt(Gtk.Button):
|
|
21
|
+
class ScrollListBox(Gtk.Frame):
|
|
22
|
+
class TextRow(Gtk.HBox):
|
|
23
|
+
class RadioGroup(Gtk.Frame):
|
|
24
|
+
class Led(Gtk.DrawingArea):
|
|
25
|
+
class Lights(Gtk.Frame):
|
|
26
|
+
class FrameTextView(Gtk.TextView):
|
|
27
|
+
class Label(Gtk.Label):
|
|
28
|
+
class Logo(Gtk.VBox):
|
|
29
|
+
class xSpacer(Gtk.HBox):
|
|
30
|
+
class ScrollListBox(Gtk.Frame):
|
|
31
|
+
class ListBox(Gtk.TreeView):
|
|
32
|
+
|
|
33
|
+
... and a lot more ...
|
|
34
|
+
|
|
35
|
+
## Also includes python / Gtk primitives:
|
|
36
|
+
|
|
37
|
+
def get_screen_wh():
|
|
38
|
+
def get_screen_xy():
|
|
39
|
+
def print_exception(xstr):
|
|
40
|
+
def message(strx, parent = None, title = None, icon = Gtk.MessageType.INFO):
|
|
41
|
+
def usleep(msec):
|
|
42
|
+
def tmpname(indir, template):
|
|
43
|
+
def mainloop():
|
|
44
|
+
def time_n2s(ttt):
|
|
45
|
+
def time_s2n(sss):
|
|
46
|
+
def yes_no_cancel(title, message, cancel = True, parent = None):
|
|
47
|
+
def yn_key(win, event, cancel):
|
|
48
|
+
def opendialog(parent=None):
|
|
49
|
+
def savedialog(resp):
|
|
50
|
+
def leadspace(strx):
|
|
51
|
+
|
|
52
|
+
... and a lot more ...
|
|
53
|
+
|
|
54
|
+
## Example:
|
|
55
|
+
|
|
56
|
+
The Label Button (smallbutt) takes a constructor, and feeds
|
|
57
|
+
the arguments with defaults as one would expect.
|
|
58
|
+
|
|
59
|
+
def __init__(self, textm="", widget=None, tooltip=None, font=None):
|
|
60
|
+
|
|
61
|
+
The simplification effect allows one to create a smallbutt with no arguments,
|
|
62
|
+
and still have a somewhat reasonable outcome. The label example is trivial,
|
|
63
|
+
the simplification takes a new dimension with classes like SimpleTree.
|
|
64
|
+
|
|
65
|
+
The defaults are set to a reasonable value, and the named argument(s) can be
|
|
66
|
+
set on one line. This makes the code look more compact and maintainable.
|
|
67
|
+
|
|
68
|
+
## Tests:
|
|
69
|
+
|
|
70
|
+
The test utilities can confirm correct operation; however being a visual
|
|
71
|
+
set of classes, the real test is seeing the generated UI.
|
|
72
|
+
|
|
73
|
+
See descendent projects for more examples. (pyedpro; pycal; pggui; ...)
|
|
74
|
+
|
|
75
|
+
Peter Glen
|
|
76
|
+
|
|
77
|
+
// EOF
|
|
File without changes
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
''' This encapsulates the browser window wit the webkia an toolbars '''
|
|
4
|
+
|
|
5
|
+
import os, sys, getopt, signal, random, time, warnings
|
|
6
|
+
|
|
7
|
+
realinc = os.path.realpath(os.path.dirname(__file__) + os.sep + "../pycommon")
|
|
8
|
+
sys.path.append(realinc)
|
|
9
|
+
|
|
10
|
+
from pgutils import *
|
|
11
|
+
from pggui import *
|
|
12
|
+
from pgsimp import *
|
|
13
|
+
|
|
14
|
+
import gi
|
|
15
|
+
gi.require_version("Gtk", "3.0")
|
|
16
|
+
#gi.require_version('WebKit2', '4.0')
|
|
17
|
+
|
|
18
|
+
from gi.repository import Gtk
|
|
19
|
+
from gi.repository import Gdk
|
|
20
|
+
from gi.repository import GLib
|
|
21
|
+
from gi.repository import GObject
|
|
22
|
+
from gi.repository import Pango
|
|
23
|
+
|
|
24
|
+
ret = ""
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import pgwkit
|
|
28
|
+
except:
|
|
29
|
+
print("Cannot load WebKit2", sys.exc_info())
|
|
30
|
+
sys.exit(1)
|
|
31
|
+
|
|
32
|
+
class brow_win(Gtk.VBox):
|
|
33
|
+
|
|
34
|
+
''' Collection of URL bar, toolbar, status bar '''
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
Gtk.VBox.__init__(self)
|
|
40
|
+
except:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
# TEST Mnemonic Label
|
|
44
|
+
#bbb = Gtk.Button.new_with_mnemonic("_Hello")
|
|
45
|
+
#self.pack_start(bbb, 0, 0, 0)
|
|
46
|
+
|
|
47
|
+
hbox3 = self.urlbar()
|
|
48
|
+
self.pack_start(hbox3, 0, 0, 0)
|
|
49
|
+
|
|
50
|
+
#if not conf.kiosk:
|
|
51
|
+
# vbox.pack_start(hbox3, False, False, 2)
|
|
52
|
+
|
|
53
|
+
self.scroll_win = Gtk.ScrolledWindow()
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
self.webview = pgwkit.pgwebw(self)
|
|
57
|
+
except:
|
|
58
|
+
print("Please install webkit2")
|
|
59
|
+
#sys.exit(1)
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
#self.old_html = ""
|
|
63
|
+
self.scroll_win.add(self.webview)
|
|
64
|
+
self.webview.editor = self.webview
|
|
65
|
+
|
|
66
|
+
self.toolbar2 = self.webview.ui.get_widget("/toolbar_format")
|
|
67
|
+
self.pack_start(self.toolbar2, False, False, 0)
|
|
68
|
+
|
|
69
|
+
self.pack_start(self.scroll_win, 1, 1, 2)
|
|
70
|
+
|
|
71
|
+
hbox5 = Gtk.HBox()
|
|
72
|
+
hbox5.pack_start(Gtk.Label(" "), 0, 0, 0)
|
|
73
|
+
self.status = Gtk.Label(" Idle ");
|
|
74
|
+
self.status.set_xalign(0)
|
|
75
|
+
|
|
76
|
+
hbox5.pack_start(self.status, 1, 1, 0)
|
|
77
|
+
hbox5.pack_start(Gtk.Label(" "), 0, 0, 0)
|
|
78
|
+
self.set_status(" Idle State ")
|
|
79
|
+
|
|
80
|
+
self.pack_start(hbox5, 0, 0, 2)
|
|
81
|
+
|
|
82
|
+
#self.add_events(Gdk.EventMask.ALL_EVENTS_MASK)
|
|
83
|
+
#self.set_sensitive(True)
|
|
84
|
+
|
|
85
|
+
# Receive key presses
|
|
86
|
+
self.set_can_focus(True)
|
|
87
|
+
#self.grab_focus()
|
|
88
|
+
|
|
89
|
+
def load_html(self, strx):
|
|
90
|
+
self.webview.load_html(strx)
|
|
91
|
+
|
|
92
|
+
def cut(self):
|
|
93
|
+
#print("cut")
|
|
94
|
+
self.webview.on_action("cut")
|
|
95
|
+
|
|
96
|
+
def copy(self):
|
|
97
|
+
#print("copy")
|
|
98
|
+
self.webview.on_action("copy")
|
|
99
|
+
|
|
100
|
+
def paste(self):
|
|
101
|
+
#print("paste")
|
|
102
|
+
self.webview.on_paste()
|
|
103
|
+
#self.webview.on_action("paste")
|
|
104
|
+
|
|
105
|
+
def open(self):
|
|
106
|
+
dialog = Gtk.FileChooserDialog("Open an HTML file", None,
|
|
107
|
+
Gtk.FileChooserAction.OPEN,
|
|
108
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
|
109
|
+
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
110
|
+
|
|
111
|
+
ret = dialog.run()
|
|
112
|
+
fn = dialog.get_filename()
|
|
113
|
+
dialog.destroy()
|
|
114
|
+
|
|
115
|
+
if ret == Gtk.ResponseType.OK:
|
|
116
|
+
if not fn:
|
|
117
|
+
return
|
|
118
|
+
if os.path.exists(fn):
|
|
119
|
+
self.fname = fn
|
|
120
|
+
with open(fn) as fd:
|
|
121
|
+
self.webview.load_html(fd.read(), "file:///")
|
|
122
|
+
|
|
123
|
+
def save(self):
|
|
124
|
+
|
|
125
|
+
#print("brow_win", "save", self.fname)
|
|
126
|
+
def completion(html, user_data):
|
|
127
|
+
#print("len html", len(html), len(self.webview.old_html) )
|
|
128
|
+
#print("html", html, "old", self.webview.old_html )
|
|
129
|
+
#if self.webview.old_html == html:
|
|
130
|
+
|
|
131
|
+
if not self.webview.modified:
|
|
132
|
+
self.set_status("File NOT modified.") # '%s'" % self.fname)
|
|
133
|
+
else:
|
|
134
|
+
#self.webview.old_html = html
|
|
135
|
+
open_mode = user_data
|
|
136
|
+
with open(self.fname, open_mode) as fd:
|
|
137
|
+
fd.write(html)
|
|
138
|
+
self.set_status("Saved file '%s'" % self.fname)
|
|
139
|
+
self.webview.modified = False
|
|
140
|
+
self.webview.get_html(completion, 'w')
|
|
141
|
+
|
|
142
|
+
def is_modified(self):
|
|
143
|
+
return self.webview.modified
|
|
144
|
+
|
|
145
|
+
def _completion(self, html, user_data):
|
|
146
|
+
self.ret = html
|
|
147
|
+
self.done = True
|
|
148
|
+
self.webview.modified = False
|
|
149
|
+
#print("retx", ret)
|
|
150
|
+
|
|
151
|
+
def get_content(self):
|
|
152
|
+
self.done = 0; self.ret = ""
|
|
153
|
+
self.webview.get_html(self._completion, "w")
|
|
154
|
+
# Wait until done is set
|
|
155
|
+
for aa in range(1000):
|
|
156
|
+
if self.done:
|
|
157
|
+
break
|
|
158
|
+
Gtk.main_iteration_do(False)
|
|
159
|
+
return self.ret
|
|
160
|
+
|
|
161
|
+
def saveas(self):
|
|
162
|
+
def completion(html, user_data):
|
|
163
|
+
open_mode = user_data
|
|
164
|
+
with open(self.fname, open_mode) as fd:
|
|
165
|
+
fd.write(html)
|
|
166
|
+
self.set_status("Saved as '%s'" % self.fname)
|
|
167
|
+
|
|
168
|
+
dialog = Gtk.FileChooserDialog("Select an HTML file", None,
|
|
169
|
+
Gtk.FileChooserAction.SAVE,
|
|
170
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
|
171
|
+
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
172
|
+
|
|
173
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
174
|
+
self.fname = dialog.get_filename()
|
|
175
|
+
#print("Saving", self.fname)
|
|
176
|
+
self.webview.get_html(completion, "w+")
|
|
177
|
+
dialog.destroy()
|
|
178
|
+
|
|
179
|
+
def url_callb(self, xtxt):
|
|
180
|
+
self.webview.go(xtxt)
|
|
181
|
+
|
|
182
|
+
def url_callb(self, xtxt):
|
|
183
|
+
self.go(xtxt)
|
|
184
|
+
|
|
185
|
+
def backurl(self, url, parm, buff):
|
|
186
|
+
self.webview.go_back()
|
|
187
|
+
|
|
188
|
+
def baseurl(self, url, parm, buff):
|
|
189
|
+
self.webview.load_uri("file://" + self.fname)
|
|
190
|
+
|
|
191
|
+
def forwurl(self, url, parm, buff):
|
|
192
|
+
self.webview.go_forward()
|
|
193
|
+
|
|
194
|
+
#def gourl(self, url, parm, buff):
|
|
195
|
+
def gourl(self, *url):
|
|
196
|
+
print("gourl", *url)
|
|
197
|
+
self.go(self.edit.get_text())
|
|
198
|
+
|
|
199
|
+
def go(self, xstr):
|
|
200
|
+
print("go", xstr)
|
|
201
|
+
|
|
202
|
+
# Leave known URL scemes alone
|
|
203
|
+
if xstr[:7] == "file://":
|
|
204
|
+
sss = os.path.realpath(xstr[7:])
|
|
205
|
+
xstr = "file://" + sss
|
|
206
|
+
pass
|
|
207
|
+
elif xstr[:7] == "http://":
|
|
208
|
+
pass
|
|
209
|
+
elif xstr[:8] == "https://":
|
|
210
|
+
pass
|
|
211
|
+
elif xstr[:6] == "ftp://":
|
|
212
|
+
pass
|
|
213
|
+
elif str.isdecimal(xstr[0]):
|
|
214
|
+
#print("Possible IP")
|
|
215
|
+
pass
|
|
216
|
+
else:
|
|
217
|
+
# Yeah, padd it
|
|
218
|
+
xstr = "https://" + xstr
|
|
219
|
+
|
|
220
|
+
self.webview.load_uri(xstr)
|
|
221
|
+
|
|
222
|
+
def stattime(self, *arg):
|
|
223
|
+
self.status.set_text("Idle.")
|
|
224
|
+
|
|
225
|
+
def set_status(self, xtxt):
|
|
226
|
+
self.status.set_text(xtxt)
|
|
227
|
+
GLib.timeout_add(3000, self.stattime, self, 0)
|
|
228
|
+
|
|
229
|
+
def urlbar(self):
|
|
230
|
+
|
|
231
|
+
self.edit = SimpleEdit();
|
|
232
|
+
self.edit.setsavecb(self.url_callb)
|
|
233
|
+
self.edit.single_line = True
|
|
234
|
+
|
|
235
|
+
hbox3 = Gtk.HBox()
|
|
236
|
+
uuu = Gtk.Label(" URL: ")
|
|
237
|
+
uuu.set_tooltip_text("Current / New URL; press Enter to go")
|
|
238
|
+
hbox3.pack_start(uuu, 0, 0, 0)
|
|
239
|
+
|
|
240
|
+
hbox3.pack_start(self.edit, True, True, 2)
|
|
241
|
+
|
|
242
|
+
bbb = LabelButt(" _Go ", self.gourl, "Go to speified URL")
|
|
243
|
+
#bbb = Gtk.Button.new_with_mnemonic(" _Go ") #, self.gourl, "Go to speified URL")
|
|
244
|
+
#bbb.connect("clicked", self.gourl)
|
|
245
|
+
|
|
246
|
+
#bbb = LabelButt(" _Go ", self.gourl, "Go to speified URL")
|
|
247
|
+
ccc = LabelButt(" <-_Back ", self.backurl, "Go Back")
|
|
248
|
+
ddd = LabelButt(" For_w-> ", self.forwurl, "Go Forw")
|
|
249
|
+
eee = LabelButt(" B_ase ", self.baseurl, "Go to base URL")
|
|
250
|
+
|
|
251
|
+
hbox3.pack_start(Gtk.Label(" "), 0, 0, 0)
|
|
252
|
+
|
|
253
|
+
hbox3.pack_start(bbb, 0, 0, 0)
|
|
254
|
+
hbox3.pack_start(ccc, 0, 0, 0)
|
|
255
|
+
hbox3.pack_start(ddd, 0, 0, 0)
|
|
256
|
+
hbox3.pack_start(eee, 0, 0, 0)
|
|
257
|
+
|
|
258
|
+
#hbox3.pack_start(Gtk.Label(" ^ "), 0, 0, 0)
|
|
259
|
+
hbox3.pack_start(Gtk.Label(" "), 0, 0, 0)
|
|
260
|
+
|
|
261
|
+
return hbox3
|
|
262
|
+
|
|
263
|
+
# EOF
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import gi
|
|
3
|
+
|
|
4
|
+
gi.require_version("Gtk", "3.0")
|
|
5
|
+
gi.require_version('WebKit2', '4.0')
|
|
6
|
+
from gi.repository import Gtk, Gdk, WebKit2
|
|
7
|
+
|
|
8
|
+
class HtmlEditor(Gtk.Widget):
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
#self.set_title("Html Editor")
|
|
14
|
+
#self.connect("destroy", Gtk.main_quit)
|
|
15
|
+
self.resize(500, 500)
|
|
16
|
+
self.filename = None
|
|
17
|
+
|
|
18
|
+
self.editor = WebKit2.WebView()
|
|
19
|
+
self.editor.set_editable(True)
|
|
20
|
+
self.editor.load_html("", "file:///")
|
|
21
|
+
|
|
22
|
+
self.scroll = Gtk.ScrolledWindow()
|
|
23
|
+
self.scroll.add(self.editor)
|
|
24
|
+
self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
25
|
+
|
|
26
|
+
self.ui = self.generate_ui()
|
|
27
|
+
self.add_accel_group(self.ui.get_accel_group())
|
|
28
|
+
self.toolbar1 = self.ui.get_widget("/toolbar_main")
|
|
29
|
+
self.toolbar2 = self.ui.get_widget("/toolbar_format")
|
|
30
|
+
self.menubar = self.ui.get_widget("/menubar_main")
|
|
31
|
+
|
|
32
|
+
self.layout = Gtk.VBox()
|
|
33
|
+
self.layout.pack_start(self.menubar, False, False, 0)
|
|
34
|
+
self.layout.pack_start(self.toolbar1, False, False, 0)
|
|
35
|
+
self.layout.pack_start(self.toolbar2, False, False, 0)
|
|
36
|
+
self.layout.pack_start(self.scroll, True, True, 0)
|
|
37
|
+
self.add(self.layout)
|
|
38
|
+
|
|
39
|
+
#self.layout.pack_start(self.editor.get_inspector(), True, True, 0)
|
|
40
|
+
#self.inspect = WebKit2.WebInspector()
|
|
41
|
+
#self.inspect.attach()
|
|
42
|
+
#self.editor.get_inspector().attach()
|
|
43
|
+
|
|
44
|
+
self.editor.get_settings().set_property("enable_developer_extras", True)
|
|
45
|
+
#self.editor.get_inspector().set_property("height", 200)
|
|
46
|
+
#self.editor.get_inspector().show()
|
|
47
|
+
#print("hhh", self.editor.get_inspector().get_attached_height())
|
|
48
|
+
|
|
49
|
+
def generate_ui(self):
|
|
50
|
+
ui_def = """
|
|
51
|
+
<ui>
|
|
52
|
+
<menubar name="menubar_main">
|
|
53
|
+
<menu action="menuFile">
|
|
54
|
+
<menuitem action="new" />
|
|
55
|
+
<menuitem action="open" />
|
|
56
|
+
<menuitem action="save" />
|
|
57
|
+
</menu>
|
|
58
|
+
<menu action="menuEdit">
|
|
59
|
+
<menuitem action="cut" />
|
|
60
|
+
<menuitem action="copy" />
|
|
61
|
+
<menuitem action="paste" />
|
|
62
|
+
</menu>
|
|
63
|
+
<menu action="menuInsert">
|
|
64
|
+
<menuitem action="insertimage" />
|
|
65
|
+
</menu>
|
|
66
|
+
<menu action="menuFormat">
|
|
67
|
+
<menuitem action="bold" />
|
|
68
|
+
<menuitem action="italic" />
|
|
69
|
+
<menuitem action="underline" />
|
|
70
|
+
<menuitem action="strikethrough" />
|
|
71
|
+
<separator />
|
|
72
|
+
<menuitem action="font" />
|
|
73
|
+
<menuitem action="color" />
|
|
74
|
+
<separator />
|
|
75
|
+
<menuitem action="justifyleft" />
|
|
76
|
+
<menuitem action="justifyright" />
|
|
77
|
+
<menuitem action="justifycenter" />
|
|
78
|
+
<menuitem action="justifyfull" />
|
|
79
|
+
</menu>
|
|
80
|
+
</menubar>
|
|
81
|
+
<toolbar name="toolbar_main">
|
|
82
|
+
<toolitem action="new" />
|
|
83
|
+
<toolitem action="open" />
|
|
84
|
+
<toolitem action="save" />
|
|
85
|
+
<separator />
|
|
86
|
+
<toolitem action="undo" />
|
|
87
|
+
<toolitem action="redo" />
|
|
88
|
+
<separator />
|
|
89
|
+
<toolitem action="cut" />
|
|
90
|
+
<toolitem action="copy" />
|
|
91
|
+
<toolitem action="paste" />
|
|
92
|
+
</toolbar>
|
|
93
|
+
<toolbar name="toolbar_format">
|
|
94
|
+
<toolitem action="bold" />
|
|
95
|
+
<toolitem action="italic" />
|
|
96
|
+
<toolitem action="underline" />
|
|
97
|
+
<toolitem action="strikethrough" />
|
|
98
|
+
<separator />
|
|
99
|
+
<toolitem action="font" />
|
|
100
|
+
<toolitem action="color" />
|
|
101
|
+
<separator />
|
|
102
|
+
<toolitem action="justifyleft" />
|
|
103
|
+
<toolitem action="justifyright" />
|
|
104
|
+
<toolitem action="justifycenter" />
|
|
105
|
+
<toolitem action="justifyfull" />
|
|
106
|
+
<separator />
|
|
107
|
+
<toolitem action="insertimage" />
|
|
108
|
+
<toolitem action="insertlink" />
|
|
109
|
+
</toolbar>
|
|
110
|
+
</ui>
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
actions = Gtk.ActionGroup("Actions")
|
|
114
|
+
actions.add_actions([
|
|
115
|
+
("menuFile", None, "_File"),
|
|
116
|
+
("menuEdit", None, "_Edit"),
|
|
117
|
+
("menuInsert", None, "_Insert"),
|
|
118
|
+
("menuFormat", None, "_Format"),
|
|
119
|
+
|
|
120
|
+
("new", Gtk.STOCK_NEW, "_New", None, None, self.on_new),
|
|
121
|
+
("open", Gtk.STOCK_OPEN, "_Open", None, None, self.on_open),
|
|
122
|
+
("save", Gtk.STOCK_SAVE, "_Save", None, None, self.on_save),
|
|
123
|
+
|
|
124
|
+
("undo", Gtk.STOCK_UNDO, "_Undo", None, None, self.on_action),
|
|
125
|
+
("redo", Gtk.STOCK_REDO, "_Redo", None, None, self.on_action),
|
|
126
|
+
|
|
127
|
+
("cut", Gtk.STOCK_CUT, "_Cut", None, None, self.on_action),
|
|
128
|
+
("copy", Gtk.STOCK_COPY, "_Copy", None, None, self.on_action),
|
|
129
|
+
("paste", Gtk.STOCK_PASTE, "_Paste", None, None, self.on_paste),
|
|
130
|
+
|
|
131
|
+
("bold", Gtk.STOCK_BOLD, "_Bold", "<ctrl>B", None, self.on_action),
|
|
132
|
+
("italic", Gtk.STOCK_ITALIC, "_Italic", "<ctrl>I", None, self.on_action),
|
|
133
|
+
("underline", Gtk.STOCK_UNDERLINE, "_Underline", "<ctrl>U", None, self.on_action),
|
|
134
|
+
("strikethrough", Gtk.STOCK_STRIKETHROUGH, "_Strike", "<ctrl>T", None, self.on_action),
|
|
135
|
+
("font", Gtk.STOCK_SELECT_FONT, "Select _Font", "<ctrl>F", None, self.on_select_font),
|
|
136
|
+
("color", Gtk.STOCK_SELECT_COLOR, "Select _Color", None, None, self.on_select_color),
|
|
137
|
+
|
|
138
|
+
("justifyleft", Gtk.STOCK_JUSTIFY_LEFT, "Justify _Left", None, None, self.on_action),
|
|
139
|
+
("justifyright", Gtk.STOCK_JUSTIFY_RIGHT, "Justify _Right", None, None, self.on_action),
|
|
140
|
+
("justifycenter", Gtk.STOCK_JUSTIFY_CENTER, "Justify _Center", None, None, self.on_action),
|
|
141
|
+
("justifyfull", Gtk.STOCK_JUSTIFY_FILL, "Justify _Full", None, None, self.on_action),
|
|
142
|
+
|
|
143
|
+
("insertimage", "insert-image", "Insert _Image", None, None, self.on_insert_image),
|
|
144
|
+
("insertlink", "insert-link", "Insert _Link", None, None, self.on_insert_link),
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
actions.get_action("insertimage").set_property("icon-name", "insert-image")
|
|
148
|
+
actions.get_action("insertlink").set_property("icon-name", "insert-link")
|
|
149
|
+
|
|
150
|
+
ui = Gtk.UIManager()
|
|
151
|
+
ui.insert_action_group(actions)
|
|
152
|
+
ui.add_ui_from_string(ui_def)
|
|
153
|
+
return ui
|
|
154
|
+
|
|
155
|
+
def on_action(self, action):
|
|
156
|
+
self.editor.run_javascript("document.execCommand('%s', false, false);" % action.get_name())
|
|
157
|
+
|
|
158
|
+
def on_paste(self, action):
|
|
159
|
+
self.editor.execute_editing_command(WebKit2.EDITING_COMMAND_PASTE)
|
|
160
|
+
|
|
161
|
+
def on_new(self, action):
|
|
162
|
+
self.editor.load_html("", "file:///")
|
|
163
|
+
|
|
164
|
+
def on_select_font(self, action):
|
|
165
|
+
dialog = Gtk.FontChooserDialog("Select a font")
|
|
166
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
167
|
+
fname = dialog.get_font_desc().get_family()
|
|
168
|
+
fsize = dialog.get_font_desc().get_size()
|
|
169
|
+
self.editor.run_javascript("document.execCommand('fontname', null, '%s');" % fname)
|
|
170
|
+
self.editor.run_javascript("document.execCommand('fontsize', null, '%s');" % fsize)
|
|
171
|
+
dialog.destroy()
|
|
172
|
+
|
|
173
|
+
def on_select_color(self, action):
|
|
174
|
+
dialog = Gtk.ColorChooserDialog("Select Color")
|
|
175
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
176
|
+
(r, g, b, a) = dialog.get_rgba()
|
|
177
|
+
color = "#%0.2x%0.2x%0.2x%0.2x" % (
|
|
178
|
+
int(r * 255),
|
|
179
|
+
int(g * 255),
|
|
180
|
+
int(b * 255),
|
|
181
|
+
int(a * 255))
|
|
182
|
+
self.editor.run_javascript("document.execCommand('forecolor', null, '%s');" % color)
|
|
183
|
+
dialog.destroy()
|
|
184
|
+
|
|
185
|
+
def on_insert_link(self, action):
|
|
186
|
+
dialog = Gtk.Dialog("Enter a URL:", self, 0,
|
|
187
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
|
|
188
|
+
|
|
189
|
+
entry = Gtk.Entry()
|
|
190
|
+
dialog.vbox.pack_start(entry, False, False, 0)
|
|
191
|
+
dialog.show_all()
|
|
192
|
+
|
|
193
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
194
|
+
self.editor.run_javascript(
|
|
195
|
+
"document.execCommand('createLink', true, '%s');" % entry.get_text())
|
|
196
|
+
dialog.destroy()
|
|
197
|
+
|
|
198
|
+
def on_insert_image(self, action):
|
|
199
|
+
dialog = Gtk.FileChooserDialog("Select an image file", self, Gtk.FileChooserAction.OPEN,
|
|
200
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
201
|
+
|
|
202
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
203
|
+
fn = dialog.get_filename()
|
|
204
|
+
if os.path.exists(fn):
|
|
205
|
+
self.editor.run_javascript(
|
|
206
|
+
"document.execCommand('insertImage', null, '%s');" % fn)
|
|
207
|
+
dialog.destroy()
|
|
208
|
+
|
|
209
|
+
def on_open(self, action):
|
|
210
|
+
dialog = Gtk.FileChooserDialog("Select an HTML file", self, Gtk.FileChooserAction.OPEN,
|
|
211
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
212
|
+
|
|
213
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
214
|
+
fn = dialog.get_filename()
|
|
215
|
+
if os.path.exists(fn):
|
|
216
|
+
self.filename = fn
|
|
217
|
+
with open(fn) as fd:
|
|
218
|
+
self.editor.load_html(fd.read(), "file:///")
|
|
219
|
+
dialog.destroy()
|
|
220
|
+
|
|
221
|
+
def on_save(self, action):
|
|
222
|
+
def completion(html, user_data):
|
|
223
|
+
open_mode = user_data
|
|
224
|
+
with open(self.filename, open_mode) as fd:
|
|
225
|
+
fd.write(html)
|
|
226
|
+
|
|
227
|
+
if self.filename:
|
|
228
|
+
self.get_html(completion, 'w')
|
|
229
|
+
else:
|
|
230
|
+
dialog = Gtk.FileChooserDialog("Select an HTML file", self, Gtk.FileChooserAction.SAVE,
|
|
231
|
+
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
232
|
+
|
|
233
|
+
if dialog.run() == Gtk.ResponseType.OK:
|
|
234
|
+
self.filename = dialog.get_filename()
|
|
235
|
+
self.get_html(completion, "w+")
|
|
236
|
+
dialog.destroy()
|
|
237
|
+
|
|
238
|
+
def get_html(self, completion_function, user_data):
|
|
239
|
+
def javascript_completion(obj, result, user_data):
|
|
240
|
+
html = self.editor.get_title()
|
|
241
|
+
completion_function(html, user_data)
|
|
242
|
+
self.editor.run_javascript("document.title=document.documentElement.innerHTML;",
|
|
243
|
+
None,
|
|
244
|
+
javascript_completion,
|
|
245
|
+
user_data)
|
|
246
|
+
|
|
247
|
+
#e = HtmlEditor()
|
|
248
|
+
#e.show_all()
|
|
249
|
+
#e.scroll.grab_focus()
|
|
250
|
+
#Gtk.main()
|
|
251
|
+
|
|
252
|
+
# EOF
|