Add documents.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
18d23e0c06
commit
7b393d4cad
|
@ -67,7 +67,7 @@ steps:
|
|||
key:
|
||||
from_secret: ssh_key
|
||||
target: /var/www/new-blog/
|
||||
source: /drone/src/resources/public/*
|
||||
source: /drone/src/bases/do-blog/resources/public/*
|
||||
strip_components: 4
|
||||
|
||||
# - name: promote-deployed-site
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
:sha "13e57f11da75516844922fe28bfd6aa7692b7c34"}
|
||||
|
||||
}
|
||||
:aliases {:dev {:extra-deps {thheller/shadow-cljs {:mvn/version "2.25.2"}
|
||||
:aliases {:dev {:extra-paths
|
||||
["components/ui-hiccup/src"]
|
||||
:extra-deps {thheller/shadow-cljs {:mvn/version "2.25.2"}
|
||||
cider/cider-nrepl {:mvn/version "0.50.2"}
|
||||
refactor-nrepl/refactor-nrepl {:mvn/version "3.10.0"}
|
||||
cider/piggieback {:mvn/version "0.5.3"}}}
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
#+TITLE: Blogging with org mode and nikola
|
||||
#+DATE: 2017-07-01 12:00:00 UTC
|
||||
#+DESCRIPTION: How I use emacs and nikola to write my blog
|
||||
#+FILETAGS: emacs:python
|
||||
#+CATEGORY: emacs
|
||||
#+SLUG: blogging-with-org-mode-and-nikola
|
||||
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Blogging with org mode and nikola
|
||||
.. slug: blogging-with-org-mode-and-nikola
|
||||
.. date: 2017-07-01 12:00:00 UTC
|
||||
.. tags: emacs, python
|
||||
.. category: emacs
|
||||
.. description: How I use emacs and nikola to write my blog
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
Below are some tips and technologies used to write and deploy this blog.
|
||||
** Getting started
|
||||
To get started creating your blog first install nikola with pip or use the docker container, then run =nikola init= to create your new blog.
|
||||
** Org header attributes
|
||||
All org files should start with this header containing the blog entry meta data, the below example is from this post.
|
||||
#+BEGIN_EXAMPLE
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Blogging with org mode and nikola
|
||||
.. slug: blogging-with-org-mode-and-nikola
|
||||
.. date: 2017-01-02 12:00:00 UTC
|
||||
.. tags: emacs, python
|
||||
.. category: emacs
|
||||
.. description: How I use emacs and nikola to write my blog
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
#+END_EXAMPLE
|
||||
|
||||
Nikola lets you create your blog entries in various formats like org, markdown, rtf and ascii doc as a few examples,
|
||||
The main advantage is that you don't need a database and can redistribute your files as plain documents, if you use orgmode format in particular this works very well because you can
|
||||
export to various formats like pdf or word documents which works great for sending to people or for making offline or printable copies.
|
||||
Basically you get a lot of power with little effort.
|
||||
|
||||
** Updating nikola configuration
|
||||
To use org you will need to adjust =conf.py= the examples below are the relevant parts to find and update with the org extensions.
|
||||
=ORG_REGEX_REPLACE= is only needed if your using my modified plugin.
|
||||
#+BEGIN_EXAMPLE
|
||||
POSTS = (
|
||||
("posts/*.rst", "posts", "post.tmpl"),
|
||||
("posts/*.txt", "posts", "post.tmpl"),
|
||||
("posts/*.org", "posts", "post.tmpl"),
|
||||
("posts/*.md", "posts", "post.tmpl"),
|
||||
)
|
||||
PAGES = (
|
||||
("stories/*.rst", "stories", "story.tmpl"),
|
||||
("stories/*.txt", "stories", "story.tmpl"),
|
||||
("stories/*.org", "stories", "story.tmpl"),
|
||||
("stories/*.md", "stories", "story.tmpl"),
|
||||
)
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
COMPILERS = {
|
||||
"rest": ('.rst', '.txt'),
|
||||
"markdown": ('.md', '.mdown', '.markdown'),
|
||||
"textile": ('.textile',),
|
||||
"txt2tags": ('.t2t',),
|
||||
"bbcode": ('.bb',),
|
||||
"wiki": ('.wiki',),
|
||||
"ipynb": ('.ipynb',),
|
||||
"html": ('.html', '.htm'),
|
||||
"orgmode": ('.org',),
|
||||
"php": ('.php',),
|
||||
}
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
ORG_REGEX_REPLACE = [(r'src\=\"(../)*', 'src="/')]
|
||||
|
||||
ORG_PRETTY_URL_ADJUST = True
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Hosting your blog
|
||||
I hast my blog on my own server and domain which is an option but you can also deploy your blog to github pages if you don't want the cost and hassle of maintaining your own environment.
|
||||
|
||||
** Containers
|
||||
The setup I use below involves using containers to generate deploy an host the container, this means the setup is portable you just need docker available on your machine the rest of the technologies are installed inside the containers.
|
||||
If you want a similar setup you can copy the docker compose and drone files below.
|
||||
|
||||
To generate your blog you just need to run the following command.
|
||||
|
||||
#+BEGIN_SRC shell
|
||||
docker-compose up
|
||||
#+END_SRC
|
||||
|
||||
** Nikola org file tips
|
||||
*** Hiding posts
|
||||
If you want to deploy but hide your posts from the index's you can tag them with draft.
|
||||
#+BEGIN_EXAMPLE
|
||||
.. tags: emacs, python, draft
|
||||
#+END_EXAMPLE
|
||||
You can also stop your posts from being deploy altogether by putting =DEPLOY_DRAFT = False= in your nikola config.
|
||||
see https://getnikola.com/handbook.html#drafts for more information.
|
||||
|
||||
*** Inline images
|
||||
Emacs lets you render your images direct in your document which is great for verifying your image paths, however there is an issue with nikola currently which means it changes the depth of your
|
||||
post so the deployed images will not display:
|
||||
|
||||
You have a couple of options
|
||||
+ Manually put in the correct path for nikola and loose the display and verification that the path is correct, you can still run the server locally to verify.
|
||||
+ The other option is to use a patched version which I am doing, this means i never actually need to test my pages I can write the post commit and be confident its correct.
|
||||
https://github.com/olymk2/plugins
|
||||
+ Bug the dev's they are not planning on fixing this currently but if enough people want it fixed they might, so if you want to get this fixed i suggest you add to the issue below or raise a new issue.
|
||||
https://github.com/getnikola/plugins/issues/188
|
||||
|
||||
** Docker images for rendering your org files
|
||||
You can use the container below to generate your blog, its a simple container with emacs added with org mode which is called to generate the html pages.
|
||||
|
||||
#+BEGIN_SRC yaml
|
||||
FROM olymk2/emacs
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y locales locales-all python3 python3-pip python3-gdbm
|
||||
|
||||
RUN dpkg-reconfigure locales
|
||||
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANG C.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
|
||||
COPY ./build/requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip3 install -r /app/requirements.txt
|
||||
RUN nikola plugin -i orgmode
|
||||
|
||||
WORKDIR /app
|
||||
ENTRYPOINT nikola clean; nikola build
|
||||
#+END_SRC
|
||||
|
||||
** Auto deploy with drone
|
||||
I use drone to deploy my blog you can use other CI services, but using drone keeps it simple as i can use the same containers I host my site with.
|
||||
|
||||
#+BEGIN_SRC yaml
|
||||
workspace:
|
||||
base: /drone
|
||||
path: src
|
||||
|
||||
pipeline:
|
||||
publish:
|
||||
pull: True
|
||||
image: appleboy/drone-ssh
|
||||
host: ${SSH_DOMAIN}
|
||||
user: ${SSH_USER}
|
||||
key: ${SSH_KEY}
|
||||
port: 22
|
||||
command_timeout: 240
|
||||
script:
|
||||
- cd /var/www/your-site-location/
|
||||
- git pull
|
||||
- docker-compose build
|
||||
- docker-compose up --remove-orphans
|
||||
- echo 'Finished deploy'
|
||||
#+END_SRC
|
||||
** Nikola Emacs plugin
|
||||
I have been working on a simple plugin for emacs to give you a magit style menu to nikola from inside nikola if you would like to try this visit the link below.
|
||||
https://github.com/olymk2/emacs-nikola
|
|
@ -0,0 +1,32 @@
|
|||
#+TITLE: org mode header arguments reference
|
||||
#+DATE: 2017-07-01 12:00:00 UTC
|
||||
#+DESCRIPTION: common headers for modifying the export output
|
||||
#+FILETAGS: emacs:orgmode
|
||||
#+CATEGORY: emacs
|
||||
#+SLUG: org-mode-header-reference
|
||||
#+BEGIN_COMMENT
|
||||
.. title: org mode header arguments reference
|
||||
.. slug:org-mode-header-reference
|
||||
.. date: 2017-07-01 12:00:00 UTC
|
||||
.. tags: emacs, orgmode
|
||||
.. category: emacs
|
||||
.. description: common headers for modifying the export output
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
|
||||
** Disable table of contents
|
||||
#+BEGIN_EXAMPLE
|
||||
#+OPTIONS: toc:nil
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Preserve line breaks
|
||||
#+BEGIN_EXAMPLE
|
||||
#+OPTIONS: \n:t
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Disable header numbers
|
||||
#+BEGIN_EXAMPLE
|
||||
#+OPTIONS: num:nil
|
||||
#+END_EXAMPLE
|
|
@ -0,0 +1,229 @@
|
|||
#+TITLE: Capturing user input with the widget library
|
||||
#+DATE: 2018-05-23 12:00:00 UTC
|
||||
#+DESCRIPTION: Exploration into capturing user data using emacs widget library
|
||||
#+FILETAGS: emacs:widgets:draft
|
||||
#+CATEGORY: emacs
|
||||
#+SLUG: emacs-user-input-via-widgets
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Capturing user input with the widget library
|
||||
.. slug: emacs-user-input-via-widgets
|
||||
.. date: 2018-05-23 12:00:00 UTC
|
||||
.. tags: emacs, widgets, draft
|
||||
.. category: emacs
|
||||
.. description: Exploration into capturing user data using emacs widget library
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
After writting a emacs plugin I had need to take a lot of optional user input.
|
||||
Seems the options are emacs widgets which is built in or using something like magit popups
|
||||
which allows the user to selectively enter values.
|
||||
|
||||
I will focus on emacs widgets because some of the input could be quite large.
|
||||
|
||||
** Getting started
|
||||
To get started creating your blog first install nikola with pip or use the docker container, then run =nikola init= to create your new blog.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :results silent
|
||||
(require 'widget)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'wid-edit))
|
||||
|
||||
(defun widget-example-01 ()
|
||||
"Create the widgets from the Widget manual."
|
||||
(interactive)
|
||||
;; create a new buffer and wrap all commands in buffer context
|
||||
(with-current-buffer (switch-to-buffer "Widget Test 1")
|
||||
|
||||
;; Reset back to defaults
|
||||
(kill-all-local-variables)
|
||||
(make-local-variable 'widget-example-repeat)
|
||||
|
||||
;; Clean up buffers
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer))
|
||||
|
||||
;; Insert description text into buffer
|
||||
(widget-insert "Fill in the form below\n\n")
|
||||
|
||||
;; Create our first widget with a text label
|
||||
(widget-insert "Server Name: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Replace Me")
|
||||
(widget-insert "\n----\n")
|
||||
|
||||
|
||||
;; Modify the current buffer to work with widgets
|
||||
(widget-setup)))
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: widget-example-01
|
||||
|
||||
** Adding submit and reset buttons
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(require 'widget)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'wid-edit))
|
||||
|
||||
(defvar widget-example-repeat)
|
||||
|
||||
(defun widget-example-02 ()
|
||||
"Create the widgets from the Widget manual."
|
||||
(interactive)
|
||||
(with-current-buffer (switch-to-buffer "Widget Test 2")
|
||||
(kill-all-local-variables)
|
||||
(make-local-variable 'widget-example-repeat)
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer))
|
||||
(widget-insert "Fill in the form below\n\n")
|
||||
(widget-insert "Server Name: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Replace Me")
|
||||
(widget-insert "\n----\n")
|
||||
|
||||
(widget-create 'push-button
|
||||
:notify (lambda (&rest ignore)
|
||||
(if (= (length (widget-value widget-example-repeat))
|
||||
3)
|
||||
(message "Congratulation!")
|
||||
(error "Three was the count!")))
|
||||
"Apply Form")
|
||||
(widget-insert " ")
|
||||
(widget-create 'push-button
|
||||
:notify (lambda (&rest ignore)
|
||||
(widget-example))
|
||||
"Reset Form")
|
||||
(widget-insert "\n----\n")
|
||||
|
||||
(widget-setup)))
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: widget-example-02
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(require 'widget)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'wid-edit))
|
||||
|
||||
(defvar widget-example-repeat)
|
||||
|
||||
|
||||
(defun widget-example ()
|
||||
"Create the widgets from the Widget manual."
|
||||
(interactive)
|
||||
(switch-to-buffer "*Digitalocean*")
|
||||
(kill-all-local-variables)
|
||||
(make-local-variable 'widget-example-repeat)
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer))
|
||||
(widget-insert "Digitalocean droplet creation")
|
||||
(widget-insert "Name: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"My Name")
|
||||
(widget-insert "Region: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Region")
|
||||
(widget-insert "Size: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Size")
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
(widget-insert "Image: ")
|
||||
(widget-create 'editable-field
|
||||
:size 13
|
||||
"Your Image")
|
||||
|
||||
(widget-create 'menu-choice
|
||||
:tag "Choose"
|
||||
:value "This"
|
||||
:help-echo "Choose me, please!"
|
||||
:notify (lambda (widget &rest ignore)
|
||||
(message "%s is a good choice!"
|
||||
(widget-value widget)))
|
||||
'(item :tag "This option" :value "This")
|
||||
'(choice-item "That option")
|
||||
'(editable-field :menu-tag "No option" "Thus option"))
|
||||
(widget-insert "Address: ")
|
||||
(widget-create 'editable-field
|
||||
"Some Place\nIn some City\nSome country.")
|
||||
(widget-insert "\nSee also ")
|
||||
(widget-create 'link
|
||||
:notify (lambda (&rest ignore)
|
||||
(widget-value-set widget-example-repeat
|
||||
'("En" "To" "Tre"))
|
||||
(widget-setup))
|
||||
"other work")
|
||||
(widget-insert " for more information.\n\nNumbers: count to three below\n")
|
||||
(setq widget-example-repeat
|
||||
(widget-create 'editable-list
|
||||
:entry-format "%i %d %v"
|
||||
:notify (lambda (widget &rest ignore)
|
||||
(let ((old (widget-get widget
|
||||
':example-length))
|
||||
(new (length (widget-value widget))))
|
||||
(unless (eq old new)
|
||||
(widget-put widget ':example-length new)
|
||||
(message "You can count to %d." new))))
|
||||
:value '("One" "Eh, two?" "Five!")
|
||||
'(editable-field :value "three")))
|
||||
(widget-insert "\n\nSelect multiple:\n\n")
|
||||
(widget-create 'checkbox t)
|
||||
(widget-insert " This\n")
|
||||
(widget-create 'checkbox nil)
|
||||
(widget-insert " That\n")
|
||||
(widget-create 'checkbox
|
||||
:notify (lambda (&rest ignore) (message "Tickle"))
|
||||
t)
|
||||
(widget-insert " Thus\n\nSelect one:\n\n")
|
||||
(widget-create 'radio-button-choice
|
||||
:value "One"
|
||||
:notify (lambda (widget &rest ignore)
|
||||
(message "You selected %s"
|
||||
(widget-value widget)))
|
||||
'(item "One") '(item "Another One.") '(item "A Final One."))
|
||||
(widget-insert "\n")
|
||||
(widget-create 'push-button
|
||||
:notify (lambda (&rest ignore)
|
||||
(if (= (length (widget-value widget-example-repeat))
|
||||
3)
|
||||
(message "Congratulation!")
|
||||
(error "Three was the count!")))
|
||||
"Apply Form")
|
||||
(widget-insert " ")
|
||||
(widget-create 'push-button
|
||||
:notify (lambda (&rest ignore)
|
||||
(widget-example))
|
||||
"Reset Form")
|
||||
(widget-insert "\n")
|
||||
(use-local-map widget-keymap)
|
||||
(widget-setup))
|
||||
#+END_SRC
|
|
@ -0,0 +1,53 @@
|
|||
#+TITLE: Auto generated update and rollback database scripts with magit and emacs
|
||||
#+DATE: 2017-07-25 12:00:00 UTC
|
||||
#+DESCRIPTION: Useful functions for creating database update and rollback scripts
|
||||
#+FILETAGS: emacs:sql:git
|
||||
#+CATEGORY: emacs
|
||||
#+SLUG: auto-generating-update-and-rollback-scripts-using-magit
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Auto generated update and rollback database scripts with magit and emacs
|
||||
.. slug: auto-generating-update-and-rollback-scripts-using-magit
|
||||
.. date: 2017-07-25 12:00:00 UTC
|
||||
.. tags: emacs, sql, git
|
||||
.. category: emacs
|
||||
.. description: Useful functions for creating database update and rollback scripts
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
This code loops over all .sql .pks and .pkb files and merges them into the current buffer, it currently need to be run from the projects root.
|
||||
|
||||
Magit has some useful methods we can utilise to get a list of staged files, from there we can join the results into the current buffer.
|
||||
We can also use magit functions to get the same list before modification to generate a rollback.
|
||||
|
||||
Get previous version of staged files and generate a rollback script, we can use =magit-find-file-noselect= to get an older version.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun generate-oracle-rollback-script ()
|
||||
(interactive)
|
||||
(insert "SET DEFINE OFF;\n\n")
|
||||
(loop for i in (cddr (magit-staged-files))
|
||||
if (string-match "\\(\.sql\\|\.pkb\\|\.pks\\)" i)
|
||||
collect (insert (concat "-- START AUTO GENERATED " i "\n" (with-current-buffer
|
||||
(magit-find-file-noselect "HEAD"
|
||||
(expand-file-name i)) (buffer-string))
|
||||
)
|
||||
"\n-- END AUTO GENERATED " i "\n\n"))
|
||||
)
|
||||
#+END_SRC
|
||||
|
||||
Get all staged files and generate an update script, we can get a list of staged files with =magit-staged-files= and insert into the current buffer with =insert-file-contents=.
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun generate-oracle-update-script ()
|
||||
(interactive)
|
||||
(insert "SET DEFINE OFF;\n\n")
|
||||
(loop for i in (cddr (magit-staged-files))
|
||||
if (string-match "\\(\.sql\\|\.pkb\\|\.pks\\)" i)
|
||||
collect (insert (concat "-- START AUTO GENERATED " i "\n" (with-temp-buffer
|
||||
(insert-file-contents
|
||||
(expand-file-name i))
|
||||
(buffer-string))
|
||||
"\n-- END AUTO GENERATED " i "\n\n"))
|
||||
)
|
||||
)
|
||||
#+END_SRC
|
||||
|
||||
Future enhancement would be to detect the project root from any buffer with in the project you have open.
|
|
@ -0,0 +1,54 @@
|
|||
#+TITLE: Running sql in org mode
|
||||
#+DATE: 2017-06-20 12:00:00 UTC
|
||||
#+DESCRIPTION: Example on how to run sql statements and display the results
|
||||
#+FILETAGS: emacs:sql:draft
|
||||
#+CATEGORY: emacs
|
||||
#+SLUG: running-sql-with-org-mode
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Running sql in org mode
|
||||
.. slug: running-sql-with-org-mode
|
||||
.. date: 2017-06-20 12:00:00 UTC
|
||||
.. tags: emacs, sql, draft
|
||||
.. category: emacs
|
||||
.. description: Example on how to run sql statements and display the results
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
When searching for this i found the information was incomplete or just plain did not work, after some trial and error and working things out i got it working and have created the examples for future reference.
|
||||
|
||||
** Specifying your connection in the document header
|
||||
This makes the connection available to all source code blocks by default
|
||||
*** Oracle example
|
||||
#+BEGIN_EXAMPLE
|
||||
#+PROPERTY: header-args :engine oracle :dbhost 172.17.0.2 :dbport 1521 :dbuser mydb_username :dbpassword mydb_password :database XE
|
||||
#+END_EXAMPLE
|
||||
|
||||
Specifying your connection in a block
|
||||
#+BEGIN_EXAMPLE
|
||||
:PROPERTIES:
|
||||
header-args: engine oracle
|
||||
header-args: dbhost 172.17.0.2
|
||||
header-args: dbport 1521
|
||||
header-args: dbuser mydb_username
|
||||
header-args: dbpassword mydb_password
|
||||
header-args: database XE
|
||||
:END:
|
||||
#+END_EXAMPLE
|
||||
|
||||
** Specifying your connection as part of your source block
|
||||
You must not have any blank lines between the connection header pram's and the starting source code block else it will not work.
|
||||
#+BEGIN_EXAMPLE
|
||||
#+name: my-query
|
||||
#+header: :engine oracle
|
||||
#+header: :dbhost 172.17.0.2
|
||||
#+header: :dbport 1521
|
||||
#+header: :dbuser mydb_username
|
||||
#+header: :dbpassword mydb_password
|
||||
#+header: :database XE
|
||||
#+begin_src sql
|
||||
select * from dual;
|
||||
#+end_src
|
||||
#+END_EXAMPLE
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
#+TITLE: GRBL CNC gcode test code
|
||||
#+DATE: 2018-10-12 12:00:00 UTC
|
||||
#+DESCRIPTION: CNC cutting test code
|
||||
#+FILETAGS: cnc:gcode
|
||||
#+CATEGORY: cnc, tech
|
||||
#+SLUG: grbl-cnc-test-gcode
|
||||
|
||||
* Usefull codes used in the generated gcode.
|
||||
Set millimeters with G21 Start spindle clockwise with M3 setting the speed.
|
||||
Set the start position with G92 specifying the cordinates, move to start position with G0 rapid or G1 for cutting speed.
|
||||
|
||||
To draw a circle we need to position the cutting bit on the circumference of the circle, we then set the I and J positions which are the distance from the center of the circle.
|
||||
|
||||
I is the distance from arc start-point to arc-center-point in the X-axis.
|
||||
J is the distance from arc start-point to arc-center-point in the Y-axis
|
||||
|
||||
* Job setup
|
||||
Initially job setup using in millimeters, start the spindle no speed because wee manually set the speed.
|
||||
Set the current position then move to the first cutting position.
|
||||
#+BEGIN_SRC conf :tangle circle-test.gcode
|
||||
G21
|
||||
M3 S0.0000
|
||||
G92 X0 Y0 Z5
|
||||
G0 X5.0 Y5.0 Z5.0000
|
||||
#+END_SRC
|
||||
|
||||
* Cut first circle 0.1mm at a time
|
||||
#+BEGIN_SRC conf :tangle circle-test.gcode
|
||||
;;Draw circle cutting down 0.1mm at a time.
|
||||
G1 X0.0 Y0.0 Z5.0000
|
||||
|
||||
G1 X5.0 Y5.0 Z3.0000
|
||||
G2 X5.0 Y5.0 Z3.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.9000
|
||||
G2 X5.0 Y5.0 Z2.9000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.8000
|
||||
G2 X5.0 Y5.0 Z2.8000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.7000
|
||||
G2 X5.0 Y5.0 Z2.7000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.6000
|
||||
G2 X5.0 Y5.0 Z2.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.5000
|
||||
G2 X5.0 Y5.0 Z2.5000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.4000
|
||||
G2 X5.0 Y5.0 Z2.4000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.3000
|
||||
G2 X5.0 Y5.0 Z2.3000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.2000
|
||||
G2 X5.0 Y5.0 Z2.2000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.1000
|
||||
G2 X5.0 Y5.0 Z2.1000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z2.0000
|
||||
G2 X5.0 Y5.0 Z2.0000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X5.0 Y5.0 Z1.9000
|
||||
G2 X5.0 Y5.0 Z1.9000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.8000
|
||||
G2 X5.0 Y5.0 Z1.8000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.7000
|
||||
G2 X5.0 Y5.0 Z1.7000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.6000
|
||||
G2 X5.0 Y5.0 Z1.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.5000
|
||||
G2 X5.0 Y5.0 Z1.5000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.4000
|
||||
G2 X5.0 Y5.0 Z1.4000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.3000
|
||||
G2 X5.0 Y5.0 Z1.3000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.2000
|
||||
G2 X5.0 Y5.0 Z1.2000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.1000
|
||||
G2 X5.0 Y5.0 Z1.1000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z1.0000
|
||||
G2 X5.0 Y5.0 Z1.0000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X5.0 Y5.0 Z0.9000
|
||||
G2 X5.0 Y5.0 Z0.9000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.8000
|
||||
G2 X5.0 Y5.0 Z0.8000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.7000
|
||||
G2 X5.0 Y5.0 Z0.7000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.6000
|
||||
G2 X5.0 Y5.0 Z0.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.5000
|
||||
G2 X5.0 Y5.0 Z0.5000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.4000
|
||||
G2 X5.0 Y5.0 Z0.4000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.3000
|
||||
G2 X5.0 Y5.0 Z0.3000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.2000
|
||||
G2 X5.0 Y5.0 Z0.2000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.1000
|
||||
G2 X5.0 Y5.0 Z0.1000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z0.0000
|
||||
G2 X5.0 Y5.0 Z0.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y5.0 Z5.0000
|
||||
#+END_SRC
|
||||
|
||||
* Cut second circle 0.2mm at a time
|
||||
#+BEGIN_SRC conf :tangle circle-test.gcode
|
||||
;;Draw circle cutting down 0.2mm at a time.
|
||||
G1 X0.0 Y0.0 Z5.0000
|
||||
|
||||
G1 X-5.0 Y5.0 Z3.0000
|
||||
G2 X-5.0 Y5.0 Z3.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z2.8000
|
||||
G2 X-5.0 Y5.0 Z2.8000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z2.6000
|
||||
G2 X-5.0 Y5.0 Z2.6000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z2.4000
|
||||
G2 X-5.0 Y5.0 Z2.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z2.2000
|
||||
G2 X-5.0 Y5.0 Z2.2000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X-5.0 Y5.0 Z2.0000
|
||||
G2 X-5.0 Y5.0 Z2.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z1.8000
|
||||
G2 X-5.0 Y5.0 Z1.8000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z1.6000
|
||||
G2 X-5.0 Y5.0 Z1.6000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z1.4000
|
||||
G2 X-5.0 Y5.0 Z1.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z1.2000
|
||||
G2 X-5.0 Y5.0 Z1.2000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z1.0000
|
||||
G2 X-5.0 Y5.0 Z1.0000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X-5.0 Y5.0 Z0.8000
|
||||
G2 X-5.0 Y5.0 Z0.8000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z0.6000
|
||||
G2 X-5.0 Y5.0 Z0.6000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z0.4000
|
||||
G2 X-5.0 Y5.0 Z0.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z0.2000
|
||||
G2 X-5.0 Y5.0 Z0.2000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z0.0000
|
||||
G2 X-5.0 Y5.0 Z0.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y5.0 Z5.0000
|
||||
#+END_SRC
|
||||
|
||||
* Cut third circle 0.3mm at a time
|
||||
#+BEGIN_SRC conf :tangle circle-test.gcode
|
||||
;;Draw circle cutting down 0.3mm at a time.
|
||||
G1 X0.0 Y0.0 Z5.0000
|
||||
|
||||
G1 X-5.0 Y-5.0 Z3.0000
|
||||
G2 X-5.0 Y-5.0 Z3.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z2.7000
|
||||
G2 X-5.0 Y-5.0 Z2.7000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z2.4000
|
||||
G2 X-5.0 Y-5.0 Z2.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z2.1000
|
||||
G2 X-5.0 Y-5.0 Z2.1000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X-5.0 Y-5.0 Z2.0000
|
||||
G2 X-5.0 Y-5.0 Z2.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z1.7000
|
||||
G2 X-5.0 Y-5.0 Z1.7000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z1.4000
|
||||
G2 X-5.0 Y-5.0 Z1.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z1.1000
|
||||
G2 X-5.0 Y-5.0 Z1.1000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X-5.0 Y-5.0 Z1.0000
|
||||
G2 X-5.0 Y-5.0 Z1.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z0.7000
|
||||
G2 X-5.0 Y-5.0 Z0.7000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z0.4000
|
||||
G2 X-5.0 Y-5.0 Z0.4000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z0.1000
|
||||
G2 X-5.0 Y-5.0 Z0.1000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z0.0000
|
||||
G2 X-5.0 Y-5.0 Z0.0000 I2.5 J2.5 F12000.00
|
||||
G1 X-5.0 Y-5.0 Z5.0000
|
||||
#+END_SRC
|
||||
|
||||
* Cut fourth circle 0.4mm at a time
|
||||
#+BEGIN_SRC conf :tangle circle-test.gcode
|
||||
;;Draw circle cutting down 0.4mm at a time.
|
||||
G1 X0.0 Y0.0 Z5.0000
|
||||
|
||||
G1 X5.0 Y-5.0 Z3.0000
|
||||
G2 X5.0 Y-5.0 Z3.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z2.6000
|
||||
G2 X5.0 Y-5.0 Z2.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z2.2000
|
||||
G2 X5.0 Y-5.0 Z2.2000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X5.0 Y-5.0 Z2.0000
|
||||
G2 X5.0 Y-5.0 Z2.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z1.6000
|
||||
G2 X5.0 Y-5.0 Z1.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z1.2000
|
||||
G2 X5.0 Y-5.0 Z1.2000 I2.5 J2.5 F12000.00
|
||||
|
||||
G1 X5.0 Y-5.0 Z1.0000
|
||||
G2 X5.0 Y-5.0 Z1.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z0.6000
|
||||
G2 X5.0 Y-5.0 Z0.6000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z0.2000
|
||||
G2 X5.0 Y-5.0 Z0.2000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z0.0000
|
||||
G2 X5.0 Y-5.0 Z0.0000 I2.5 J2.5 F12000.00
|
||||
G1 X5.0 Y-5.0 Z5.0000
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#+TITLE: GAUPS usage and setup
|
||||
#+DATE: 2016-12-04 12:00:00 UTC
|
||||
#+DESCRIPTION: Useful notes for setting up and configuring GAUPS
|
||||
#+FILETAGS: cnc:arduino:serial
|
||||
#+CATEGORY: cnc, tech
|
||||
#+SLUG: gaups-usages-and-setup
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GAUPS usage and setup
|
||||
.. slug: gaups-usages-and-setup
|
||||
.. date: 2016-12-04 12:00:00 UTC
|
||||
.. tags: cnc, arduino, serial
|
||||
.. category: cnc, tech
|
||||
.. description: Useful notes for setting up and configuring GAUPS
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
To quite screen hold down CTRL + d then press SHIFT + K and answer Y to exit the screen.
|
||||
Alternatively CTRL + a then type :quit.
|
||||
#+BEGIN_SRC sh
|
||||
screen /dev/ttyACM0 115200
|
||||
#+END_SRC
|
||||
|
||||
Running =$$= will list your current config, my current setup is in the example below.
|
||||
#+BEGIN_EXAMPLE
|
||||
$0=10 (step pulse, usec)
|
||||
$1=25 (step idle delay, msec)
|
||||
$2=0 (step port invert mask:00000000)
|
||||
$3=0 (dir port invert mask:00000000)
|
||||
$4=0 (step enable invert, bool)
|
||||
$5=0 (limit pins invert, bool)
|
||||
$6=0 (probe pin invert, bool)
|
||||
$10=19 (status report mask:00010011)
|
||||
$11=0.010 (junction deviation, mm)
|
||||
$12=0.002 (arc tolerance, mm)
|
||||
$13=0 (report inches, bool)
|
||||
$20=0 (soft limits, bool)
|
||||
$21=0 (hard limits, bool)
|
||||
$22=1 (homing cycle, bool)
|
||||
$23=0 (homing dir invert mask:00000000)
|
||||
$24=25.000 (homing feed, mm/min)
|
||||
$25=500.000 (homing seek, mm/min)
|
||||
$26=250 (homing debounce, msec)
|
||||
$27=1.000 (homing pull-off, mm)
|
||||
$100=80.000 (x, step/mm)
|
||||
$101=80.000 (y, step/mm)
|
||||
$102=500.000 (z, step/mm)
|
||||
$110=500.000 (x max rate, mm/min)
|
||||
$111=500.000 (y max rate, mm/min)
|
||||
$112=500.000 (z max rate, mm/min)
|
||||
$120=10.000 (x accel, mm/sec^2)
|
||||
$121=10.000 (y accel, mm/sec^2)
|
||||
$122=10.000 (z accel, mm/sec^2)
|
||||
$130=200.000 (x max travel, mm)
|
||||
$131=200.000 (y max travel, mm)
|
||||
$132=200.000 (z max travel, mm)
|
||||
#+END_EXAMPLE
|
|
@ -0,0 +1,58 @@
|
|||
#+TITLE: Restoring an old myford ml8 lathe
|
||||
#+DATE: 2017-09-17 12:00:00 UTC
|
||||
#+DESCRIPTION: Restoration of myford lathe part 1
|
||||
#+FILETAGS: hardware
|
||||
#+CATEGORY: hardware
|
||||
#+SLUG: restoring-an-old-myford-ml8-lathe
|
||||
#+THUMBNAIL: ../../images/hardware/lathe/lathe-01.jpg
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Restoring an old myford ml8 lathe
|
||||
.. slug: restoring-an-old-myford-ml8-lathe
|
||||
.. date: 2017-09-17 12:00:00 UTC
|
||||
.. tags: hardware, python
|
||||
.. category: hardware
|
||||
.. description: Restoration of myford lathe part 1
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
Recently i was given an old Myford ML8 lathe which was working but very rusty, i have never really tried dismantling and rebuild this type of machine before so this will be a learning experience.
|
||||
|
||||
|
||||
#+CAPTION: lathe original state 01
|
||||
[[../../images/hardware/lathe/lathe-01.jpg]]
|
||||
|
||||
#+CAPTION: lathe original state 02
|
||||
[[../../images/hardware/lathe/lathe-02.jpg]]
|
||||
|
||||
#+CAPTION: lathe original state 03
|
||||
[[../../images/hardware/lathe/lathe-03.jpg]]
|
||||
|
||||
|
||||
Been cleaning the parts removing the rust and paint with a wire brush drill attachement, and smaller versions on a dremel for the awkward bit.
|
||||
|
||||
The smaller metal parts i have soaked in vinegar then rubed down with wire wool and sprayed with oil to top them re rusting.
|
||||
|
||||
#+CAPTION: lathe cleaning 01
|
||||
[[../../images/hardware/lathe/lathe-04.jpg]]
|
||||
|
||||
#+CAPTION: lathe cleaning 02
|
||||
[[../../images/hardware/lathe/lathe-05.jpg]]
|
||||
|
||||
#+CAPTION: lathe cleaning 03
|
||||
[[../../images/hardware/lathe/lathe-06.jpg]]
|
||||
|
||||
After cleaning up the parts, i have primed them in some metal paint to help protect from rust,
|
||||
currently jut primed but i like the colour so will likely keep it bright red.
|
||||
|
||||
#+CAPTION: lathe cleaning 01
|
||||
[[../../images/hardware/lathe/lathe-07.jpg]]
|
||||
|
||||
#+CAPTION: lathe cleaning 02
|
||||
[[../../images/hardware/lathe/lathe-08.jpg]]
|
||||
|
||||
#+CAPTION: lathe cleaning 03
|
||||
[[../../images/hardware/lathe/lathe-09.jpg]]
|
||||
|
||||
Sill lots of cleaning an painting todo, got a stuck pulley which will not come free from the shaft, and need to strip back the main housing and clean.
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#+TITLE: NFC MODULE V3 Raspberry pi
|
||||
#+DATE: 2018-01-07 12:00:00 UTC
|
||||
#+DESCRIPTION: How to wire and install software to drive a PN532
|
||||
#+FILETAGS: tech:pi:nfc
|
||||
#+CATEGORY: hardware
|
||||
#+SLUG: nfc-module-v3-raspberry-pi
|
||||
#+THUMBNAIL: ../../images/hardware/PN532-PI.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: NFC MODULE V3 Raspberry pi
|
||||
.. slug: nfc-module-v3-raspberry-pi
|
||||
.. date: 2018-01-07 12:00:00 UTC
|
||||
.. tags: pi, nfc
|
||||
.. category: tech, pi, nfc
|
||||
.. description: How to wire and install software to drive a PN532
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
The below guide is to setup a pn532 nfc reader on a raspberry pi.
|
||||
|
||||
First up make surer the PN532 is setup in UART mode, and connect to the pi in the same manor as the diagram below.
|
||||
|
||||
#+CAPTION: PN532 Rfid read connected to a Raspberry PI
|
||||
[[../../images/hardware/PN532-PI.png]]
|
||||
|
||||
|
||||
Before getting started setup your pi with the latest debian image and complete your standard setup.
|
||||
Once the pi is setup run the commands below installing the necessary libraries.
|
||||
|
||||
Run the below commands to install the tools and libraries we will need for the rest of the setup.
|
||||
#+BEGIN_SRC sh
|
||||
sudo apt-get install git build-essential autoconf libtool libpcsclite-dev
|
||||
sudo apt-get install libusb-dev libcurl4-openssl-dev libjson-c-dev python-pip
|
||||
#+END_SRC
|
||||
|
||||
Download the nfc library from github.
|
||||
#+BEGIN_SRC sh
|
||||
git clone https://github.com/nfc-tools/libnfc.git
|
||||
cd libnfc
|
||||
sudo mkdir -p /etc/nfc/devices.d/
|
||||
#+END_SRC
|
||||
|
||||
|
||||
Copy the relevant config to the correct place on the pi.
|
||||
#+BEGIN_SRC sh
|
||||
sudo cp contrib/libnfc/pn532_uart_on_rpi_3.conf.sample /etc/nfc/devices.d/pn532_uart_on_rpi_3.conf
|
||||
#+END_SRC
|
||||
|
||||
Compile the libnfc library.
|
||||
#+BEGIN_SRC sh
|
||||
autoreconf -vis
|
||||
./configure --with-drivers=pn532_uart --sysconfdir=/etc --prefix=/usr
|
||||
sudo make clean && sudo make install all
|
||||
#+END_SRC
|
||||
|
||||
|
||||
Install the python library
|
||||
#+BEGIN_SRC sh
|
||||
pip install -U nfcpy
|
||||
#+END_SRC
|
||||
|
||||
|
||||
To check everything is working try running the command below to make sure your reader is detected and listed.
|
||||
#+BEGIN_SRC sh
|
||||
nfc-scan-device
|
||||
#+END_SRC
|
||||
|
||||
Now create your first python program to make sure everything is working.
|
||||
#+BEGIN_SRC python
|
||||
import nfc
|
||||
import ndef
|
||||
from nfc.tag import tt1
|
||||
from nfc.tag import tt2
|
||||
from nfc.tag import tt3
|
||||
from nfc.tag import tt4
|
||||
|
||||
|
||||
tagtypes = (
|
||||
('uid', nfc.tag.tt1.Type1Tag),
|
||||
('uid', nfc.tag.tt2.Type2Tag),
|
||||
('idm', nfc.tag.tt3.Type3Tag),
|
||||
('uid', nfc.tag.tt4.Type4Tag)
|
||||
)
|
||||
|
||||
def connected(tag):
|
||||
print tag.type
|
||||
for uid, type in tgtypes:
|
||||
if isinstance(tag, type):
|
||||
print str(attr(tag, uid)).encode("hex")
|
||||
return
|
||||
print "error: unknown tag type"
|
||||
|
||||
with nfc.ContactlessFrontend('tty:S0:pn532') as clf:
|
||||
print('waiting for connection')
|
||||
print(clf)
|
||||
tag = clf.connect(rdwr={'on-connect': lambda tag: False})
|
||||
print(str(tag.identifier).encode('hex'))
|
||||
print(tag.type)
|
||||
if not tag.ndef:
|
||||
print('no ndef data')
|
||||
else:
|
||||
for record in tag.ndef.records:
|
||||
print(record)
|
||||
#+END_SRC
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#+TITLE: Building a sumo robot ring
|
||||
#+DATE: 2017-08-21 12:00:00 UTC
|
||||
#+DESCRIPTION: Attempt at building a largish sumo ring
|
||||
#+FILETAGS: diy:robots:hackspace
|
||||
#+CATEGORY: hardware
|
||||
#+SLUG: building-a-sumo-robot-ring
|
||||
#+THUMBNAIL: ../../images/hardware/sumo-ring/IMG_20170813_093622.jpg
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Building a sumo robot ring
|
||||
.. slug: building-a-sumo-robot-ring
|
||||
.. date: 2017-08-21 12:00:00 UTC
|
||||
.. tags: diy, robots, hackspace
|
||||
.. category: hardware
|
||||
.. description: Attempt at building a largish sumo ring
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
We have decided to build some sumo robot at the hackspace, so i decided to take up this challenge here are some pictures from the build.
|
||||
|
||||
|
||||
** Problems
|
||||
Attempting to cut out a large circle with the tools i had available proved to be tricky, tried using a router attached to some ply initally but this struggled to cut well.
|
||||
|
||||
In the end i used a jigsaw this worked quite well but the curve of the circle would move the blade away from its centre guild and caused me to take some chunks out that i had not in tended.
|
||||
|
||||
** The build
|
||||
|
||||
#+CAPTION: Joining two sheets of mdf together
|
||||
[[../../images/hardware/sumo-ring/IMG_20170624_160836.jpg]]
|
||||
#+CAPTION: Cutting the circle with a jig bolted to a piece of wood
|
||||
[[../../images/hardware/sumo-ring/IMG_20170625_105122.jpg]]
|
||||
#+CAPTION: The inaccuracy
|
||||
[[../../images/hardware/sumo-ring/IMG_20170625_113520.jpg]]
|
||||
#+CAPTION: Fixing the inaccuracy
|
||||
[[../../images/hardware/sumo-ring/IMG_20170729_113420.jpg]]
|
||||
#+CAPTION: Masking for the lines
|
||||
[[../../images/hardware/sumo-ring/IMG_20170811_173114.jpg]]
|
||||
|
||||
** The final ring
|
||||
|
||||
I am reasonably pleased with the outcome it certainly could have been better, if i had cut the initial circle out more accurately also the rim could have done with being thicker to support the weight,
|
||||
this would likely have been harder to bend with the tools I had available.
|
||||
|
||||
#+CAPTION: The final ring
|
||||
[[../../images/hardware/sumo-ring/IMG_20170813_093622.jpg]]
|
||||
|
||||
|
||||
** Lets build some robots
|
||||
So now we have a ring lets build some robots, we can use them to test ideas for pi wars next year as well :)
|
||||
|
||||
I propose controlled for now, and small in size perhaps with these dimensions based on online dimensions.
|
||||
|
||||
500 millimetres high
|
||||
200 millimetres width
|
||||
200 millimetres depth
|
||||
3000g weight limit
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#+TITLE: Weather Station
|
||||
#+DATE: 2018-10-12 12:00:00 UTC
|
||||
#+DESCRIPTION: CNC cutting test code
|
||||
#+FILETAGS: cnc:gcode:draft
|
||||
#+CATEGORY: cnc, tech, draft
|
||||
#+SLUG: grbl-cnc-test-gcode
|
||||
|
||||
|
||||
Venus hottest planet in the solar system due to co2
|
||||
|
||||
docker run -it -p 1883:1883 -p 9001:9001 -v mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto
|
||||
|
||||
Amount of oxygen in the atmosphere 20.95%
|
||||
|
||||
😄, 😊, 😐, 😟, 😩
|
||||
|
||||
250-350 ppm | normal outdoor air level
|
||||
350-1,000 ppm | Indoor good air exchange
|
||||
1,000-2,000 ppm | Complaints of drowsiness and poor air
|
||||
2,000-5,000 ppm | level associated with headaches, sleepiness, and stagnant, stale, stuffy air; poor concentration, loss of attention, increased heart rate and slight nausea may also be present.
|
||||
>5,000 ppm | This indicates unusual air conditions where high levels of other gases also could be present. Toxicity or oxygen deprivation could occur. This is the permissible exposure limit for daily workplace exposures.
|
||||
|
||||
| Product Model | MH-Z19B |
|
||||
| Target Gas | CO2 |
|
||||
| Working voltage | 4.5~ 5.5 V DC |
|
||||
| Average current | < 60mA(@5V) |
|
||||
| Peak current | 150mA (@5V) |
|
||||
| Interface leve | l3.3 V(Compatiblewith 5V) |
|
||||
| Measuring range | refer to Table 2 |
|
||||
| Output signal | UART(TTL 3.3V) PWMDAC(default 0.4-2V) |
|
||||
| Preheat time | 3 min |
|
||||
| Response Time | T90< 120 s |
|
||||
| Working temperature | 0 ~ 50 °C |
|
||||
| Working humidity | 0~ 90% RH(No condensation) |
|
||||
| Dimension | 33 mm×20 mm×9 mm(L×W×H) |
|
||||
| Weight | 5g |
|
||||
| Lifespan | > 5years |
|
||||
| | |
|
||||
|
||||
* Duty cycle
|
||||
The duty cycle is the length of the on in relation to the off percentage of on over off,
|
||||
|
||||
* MQ135 Sensor
|
||||
https://www.engineeringtoolbox.com/co2-comfort-level-d_1024.html
|
||||
https://community.particle.io/t/mq135-and-spark-core/12657/23
|
||||
|
||||
* MHZ19
|
||||
https://diyprojects.io/publish-co2-concentration-mh-z19-sensor-domoticz-arduino-code-compatible-esp32-esp8266/#.XG3HIKOnyV4
|
||||
https://diyprojects.io/product/deal-self-calibrated-co2-sensor-mh-z19-uart-output-pwm-5000ppm/#.XG3HAaOnyV4
|
||||
|
||||
Static values used below instead of using magic numbers which mean nothing to some looking over the code.
|
||||
#+BEGIN_SRC C
|
||||
// Values from the dataheet
|
||||
const unsigned long mhz19_ppm_range = 5000;
|
||||
const unsigned long mhz19_pwm_cycle = 1000;
|
||||
const unsigned long mhz19_pwm_middle_cycle = 1004;
|
||||
#+END_SRC
|
||||
|
||||
The below function will read a value from the MHZ19 sensor using pwm and calculate the CO2
|
||||
based on the datasheet below.
|
||||
https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
|
||||
#+BEGIN_SRC C
|
||||
unsigned long mhz19_sensor()
|
||||
{
|
||||
// Wait for the next pulse In
|
||||
do {
|
||||
// Work out for how long the pulse was set high
|
||||
pin_high_time = pulseIn(D6, HIGH, 1004000) / mhz19_pwm_cycle;
|
||||
} while (pin_high_time == 0);
|
||||
|
||||
// work out how long the pulse was set low, by taking high from total length
|
||||
pin_low_time = mhz19_pwm_middle_cycle - pin_high_time;
|
||||
|
||||
// Calculate parts per million from formula in spec sheet
|
||||
// CO2level:ppm=2000×(time_high-2ms)/(time_high+time_low-4ms)
|
||||
ppm = mhz19_ppm_range * (pin_high_time-2)/(pin_high_time+pin_low_time-4);
|
||||
|
||||
return ppm;
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
* Mosquitto server
|
||||
|
||||
#+BEGIN_SRC config
|
||||
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC shell
|
||||
docker run -d -it -p 1883:1883 -p 9001:9001 -v /var/www/mqtt:/mosqu/config/ eclipse-mosquitto
|
||||
#+END_SRC
|
||||
|
||||
* SPS30
|
||||
** I2C connection
|
||||
Connect the SDA pin to D2 pin and SCL pin to d1 on the wemos to use I2C mode.
|
||||
** UART connection
|
||||
Connect the RX and TX pins on the SPS30 to the RX TX pins on the wemos.
|
||||
|
||||
The following UART settings haveto be used:
|
||||
Baud Rate: 115’200 bit/s
|
||||
Data Bits: 8
|
||||
Parity: None
|
||||
Stop Bit: 1
|
||||
|
||||
* Further information
|
||||
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&cad=rja&uact=8&ved=2ahUKEwj3i7CtjN_hAhXCMewKHS2iCb0Q0PADMAR6BAgAEAU&url=https%3A%2F%2Fvancouversun.com%2Fnews%2Flocal-news%2Frising-co2-on-declining-nutrition-in-food-is-big-issue-ted-talk-hears&usg=AOvVaw0GijmobCE9pyIRmQTWTLFe
|
||||
|
||||
|
||||
** Sensors
|
||||
https://hackaday.com/2019/04/16/picking-the-right-sensors-for-home-automation/
|
|
@ -0,0 +1,48 @@
|
|||
#+TITLE: Fetch jira summary from JIRA-ID under cursor with emacs
|
||||
#+DATE: 2016-11-10 10:45:13 UTC
|
||||
#+DESCRIPTION: Example to insert jira comment and link at cursor position in emacs
|
||||
#+FILETAGS: emacs:lisp
|
||||
#+CATEGORY: lisp
|
||||
#+SLUG: fetch-jira-summary-from-jira-id-under-cursor-with-emacs
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Fetch jira summary from JIRA-ID under cursor with emacs
|
||||
.. slug: fetch-jira-summary-from-jira-id-under-cursor-with-emacs
|
||||
.. date: 2016-11-10 10:45:13 UTC
|
||||
.. tags: lisp, emacs
|
||||
.. category: lisp
|
||||
.. link:
|
||||
.. description: Example to insert jira comment and link at cursor position in emacs
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
The below snippet will grab the JIRA-ID under your cursor and use it to look up the summary and past it next to the JIRA-ID.
|
||||
|
||||
You need to have org-jira installed because this makes use of org-jira-get-issue-by-id to look up the ticket details.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun org-jira-get-summary ()
|
||||
"insert summary next to ticket id from jira"
|
||||
(interactive)
|
||||
(let ((jira_id (thing-at-point 'symbol)))
|
||||
(forward-symbol 1)
|
||||
(insert (format " - %s"
|
||||
(cdr (assoc 'summary (car (org-jira-get-issue-by-id jira_id))))))))
|
||||
#+END_SRC
|
||||
|
||||
This next snippet does the same as the first except it also replaces the ID with a link back to jira
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun org-jira-get-summary-url ()
|
||||
"insert summary next to ticket id from jira with url link"
|
||||
(interactive)
|
||||
(let ((jira_id (thing-at-point 'symbol)))
|
||||
(sp-kill-symbol 1)
|
||||
(insert (format "[[%s][%s]] - %s" (concatenate 'string jiralib-url "browse/" jira_id) jira_id
|
||||
(cdr (assoc 'summary (car (org-jira-get-issue-by-id jira_id))))))))
|
||||
#+END_SRC
|
||||
|
||||
You will need to also place the code below some where to make the jira methods available, hopefully these will be merged directly into org jira soon.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(require 'org-jira)
|
||||
#+END_SRC
|
|
@ -0,0 +1,38 @@
|
|||
#+TITLE: Run drone test suite from emacs
|
||||
#+DATE: 2016-11-15 12:00:00 UTC
|
||||
#+DESCRIPTION: Example lisp code that run drone in a shell and outputs into a buffer the results.
|
||||
#+FILETAGS: emacs:lisp
|
||||
#+CATEGORY: lisp
|
||||
#+SLUG: run-drone-test-suite-from-emacs
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Run drone test suite from emacs
|
||||
.. slug: run-drone-test-suite-from-emacs
|
||||
.. date: 2016-11-15 12:00:00 UTC
|
||||
.. tags: lisp, emacs
|
||||
.. category: lisp
|
||||
.. link:
|
||||
.. description: Example lisp code that run drone in a shell and outputs into a buffer the results.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
The first function =drone-root= simply searches for =.drone.yml= by searching up the file system tree from the current files path.
|
||||
|
||||
The second function =drone-exec= runs =drone exec= and output the results to a new buffer called =*drone:=.
|
||||
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun drone-root ()
|
||||
(or (locate-dominating-file default-directory ".drone.yml")
|
||||
(error "Missing .drone.yml not found in directory tree")))
|
||||
|
||||
;;;###autoload
|
||||
(defun drone-exec ()
|
||||
"Run \"drone exec\" where .drone.yml is found."
|
||||
(interactive)
|
||||
(let ((default-directory (drone-root)))
|
||||
(with-current-buffer (get-buffer-create (concat "*drone: " default-directory "*"))
|
||||
(compilation-start (format "drone exec")
|
||||
nil
|
||||
(lambda (_) (buffer-name))))))
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1 @@
|
|||
()
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
#+TITLE: Project hosting setup
|
||||
#+DATE: 2018-11-24 17:22:41 UTC
|
||||
#+DESCRIPTION: Simple static site hosting.
|
||||
#+FILETAGS: draft
|
||||
#+LATEX_CLASS: article
|
||||
#+CATEGORY: cato
|
||||
#+SLUG: project-hosting-setup
|
||||
|
||||
|
||||
Quick and simple code hosting and CI on a local rasobery pi or in the cloud I use a cheap digitalocen droplet, if you decide to sign up and do the same please use this link and help me out. https://m.do.co/c/f05b87f9fec9
|
||||
|
||||
|
||||
First create a =docker-compose.yml= file, you will need to specify some config in a separate env file.
|
||||
This compose file brings up caddy as a http server and postgres as the database to persist your data.
|
||||
|
||||
It then uses drone ci linked with gitea for code hosting and continuous integration.
|
||||
#+BEGIN_SRC yaml
|
||||
version: '2'
|
||||
|
||||
volumes:
|
||||
sockets:
|
||||
driver: local
|
||||
postgres_data:
|
||||
driver: local
|
||||
postgres_backup:
|
||||
driver: local
|
||||
|
||||
services:
|
||||
caddy:
|
||||
image: caddy
|
||||
volumes:
|
||||
- /var/www:/var/www
|
||||
- /var/www/project-hosting-setup/data/caddy/:/data
|
||||
- /var/www/project-hosting-setup/Caddyfile:/etc/caddy/Caddyfile
|
||||
- sockets:/data/sockets
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
|
||||
postgres:
|
||||
image: postgres:9.6
|
||||
mem_limit: 256m
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_backup:/backups
|
||||
env_file: .env
|
||||
|
||||
gitea:
|
||||
image: gitea/gitea:1.18.3
|
||||
depends_on:
|
||||
- postgres
|
||||
ports:
|
||||
- "10022:22"
|
||||
volumes:
|
||||
- ./data/gitea:/data
|
||||
restart: always
|
||||
|
||||
drone-server:
|
||||
image: drone/drone:2.16
|
||||
volumes:
|
||||
- /root/.docker/config.json:/root/.docker/config.json
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data/dronenew:/var/lib/drone/
|
||||
depends_on:
|
||||
- gitea
|
||||
env_file: .env
|
||||
#+END_SRC
|
||||
|
||||
Create a =Caddyfile= which will reverse proxy through to your git and ci containers.
|
||||
|
||||
#+BEGIN_SRC text
|
||||
ci.mydomain.com:443 {
|
||||
reverse_proxy * drone-server:80
|
||||
}
|
||||
|
||||
git.mydomain.com:443 {
|
||||
reverse_proxy * gitea:3000
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
https://caddy.community/t/serving-tens-of-thousands-of-domains-over-https-with-caddy/11179
|
||||
|
||||
Finally create a =.env= file to store your configuration
|
|
@ -0,0 +1,31 @@
|
|||
#+TITLE: Python CAD Tutorial - Developing a cad application Index
|
||||
#+DATE: 2013-10-10 22:00:00 UTC
|
||||
#+DESCRIPTION: Links to all the articles
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 10-cad-index
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial - Developing a cad application Index
|
||||
.. slug: 10-cad-index
|
||||
.. date: 2013-10-10 22:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Links to all the articles
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
Linux has been mising a good 3d CAD program for many years, current ones have poor interfaces are missing features or are just to unstable and slow. To help rectify this situation i thought i would write my own but realising i often loose intrest in projects or hit road blocks i decided to write the software as a series of tutorials.
|
||||
The Aim of these tutorials is to inspire other people to work on cad software using this as a base or just enhancing this software into a functional form, this could also be the base of 3d printer / CNC or laser cutter machines. We will skip the basics of gtk and not go into the maths in a high level of detail, but will aim to have clean well documented code that will be be easy to modify.
|
||||
If any graphics designers want to mock up a simple but functional interface that looks good please get in touch, or send some designs my way currently its lacks any real design of the interface.
|
||||
Here are some other suggestions for applications you could write using this code as a base, circuit design application, ui design tool, uml diagrams.
|
||||
|
||||
+ Initial Application
|
||||
+ Draw Points
|
||||
+ Setup Camera
|
||||
+ Mouse Cursor
|
||||
+ Draw Lines
|
||||
+ Draw a plane
|
||||
+ Draw a grid
|
||||
+ Create shape / polygon object
|
||||
+ Create a workspace / scene object
|
||||
+ hook up some gui elements
|
|
@ -0,0 +1,167 @@
|
|||
#+TITLE: Python CAD Tutorial 01 - Initial Application
|
||||
#+DATE: 2013-10-01 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file and grab the window and display on the screen, attach the close button so we can close the window.
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 01-cad-initial-application
|
||||
#+THUMBNAIL: ../../../images/cad/tut01-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 01 - Initial Application
|
||||
.. slug: 01-cad-initial-application
|
||||
.. date: 2013-10-01 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Load a glade file and grab the window and display on the screen, attach the close button so we can close the window.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut01][View/Code Download]]
|
||||
|
||||
|
||||
#+CAPTION: Point in 3D space
|
||||
[[../../../images/cad/tut01-interface.png]]
|
||||
|
||||
I have decided to write a series of tutorials in the hope we will see some good CAD applications appear on linux. In my experience, the current options are very unstable or difficult to use.
|
||||
By writing this program as a series of tutorials, I hope it will encourage contribution to the project or inspire you to develop your own CAD solution. This will hopefully help anyone who would like to write this style of 3D modelling application.
|
||||
These tutorials will use python, opengl and gtk3 for development. I will not be focusing on performance in this application, but focus more on readability and a clean layout.
|
||||
|
||||
Tools used in development of this application are Glade Interface Designer and Geany IDE, developed on ubuntu 13.04. Both opengl and gtk are cross platform so these examples may work on other platforms and distributions, but have not been tested on anything other than ubuntu 13.04.
|
||||
[[http://glade.gnome.org][../../../images/cad/glade-icon.png]]
|
||||
[[http://glade.gnome.org][../../../images/cad/geany.jpeg]]
|
||||
|
||||
The initial GUI has been designed in Glade and has the following layout: a standard menu and toolbar at the top of the window, then from left to right we have a toolbar like in gimp, in the centre we will display our designs and on the right we will have an object list.
|
||||
At the bottom we will embed an interactive console and a status bar for future expansion.
|
||||
|
||||
The glade interface can be found in the code repository above to open up and adjust. If anyone wants to design a nicer GUI, I would be interested to see what you come up with.
|
||||
/cad/glade-interface-01.png', title='Glade Interface'
|
||||
[[../../../images/cad/glade-icon.png]]
|
||||
|
||||
This initial program will contain just enough code to display the glade interface and display an opengl triangle on the screen to prove everything is setup and working and give us a base to build from.
|
||||
|
||||
To get started you will need to install the libraries below from synaptics or by running the command in terminal.
|
||||
|
||||
We then create a configure method which grabs the window X id and sets the size of the viewport (in this case the width and height of the drawing area widget). We then create a drawing start and finish method to call before and after we do any drawing to the screen.
|
||||
|
||||
The last test method is just a test to make sure every thing is setup and functioning correctly and displays the standard OpenGL coloured triangle in the new window.
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
sudo apt-get install python-gi python-opengl python-gobject python-xlib
|
||||
#+END_SRC
|
||||
|
||||
This first class separates the OpenGL and xlib code out from the gtk interface. We could reuse the code in another application. We have to use the c library for some xlib functions because the python implementation is limited - it's only used to grab the current xdisplay for OpenGL rendering.
|
||||
|
||||
For some of this code you will need to look at the OpenGL documentations to understand some of the parameters. In the initialise method we setup the OpenGL display parameters we would like to turn on, for example alpha channels for transparency and disable double buffering. We also create a glx context for our OpenGL scene to be drawn to.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
class gtkgl:
|
||||
""" These method do not seem to exist in python x11 library lets exploit the c methods """
|
||||
xlib = cdll.LoadLibrary('libX11.so')
|
||||
xlib.XOpenDisplay.argtypes = [c_char_p]
|
||||
xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
|
||||
xdisplay = xlib.XOpenDisplay("")
|
||||
display = Xlib.display.Display()
|
||||
attrs = []
|
||||
|
||||
xwindow_id = None
|
||||
width = height = 200
|
||||
|
||||
def __init__(self):
|
||||
""" Lets setup are opengl settings and create the context for our window """
|
||||
|
||||
self.add_attribute(GLX.GLX_RGBA, True)
|
||||
self.add_attribute(GLX.GLX_RED_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_GREEN_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_BLUE_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0)
|
||||
|
||||
xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
|
||||
configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int()))
|
||||
self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)
|
||||
|
||||
def add_attribute(self, setting, value):
|
||||
"""a simple method to nicely add opengl parameters"""
|
||||
self.attrs.append(setting)
|
||||
self.attrs.append(value)
|
||||
|
||||
def get_attributes(self):
|
||||
""" return our parameters in the expected structure"""
|
||||
attrs = self.attrs + [0, 0]
|
||||
return (c_int * len(attrs))(*attrs)
|
||||
|
||||
def configure(self, wid):
|
||||
""" setup up a opengl viewport for the current window, grab the xwindow id and store for later usage"""
|
||||
self.xwindow_id = GdkX11.X11Window.get_xid(wid)
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('configure failed running glXMakeCurrent')
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
|
||||
def draw_start(self):
|
||||
"""make cairo context current for drawing"""
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('draw failed running glXMakeCurrent')
|
||||
|
||||
def draw_finish(self):
|
||||
"""swap buffer when we have finished drawing"""
|
||||
GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)
|
||||
|
||||
def test(self):
|
||||
"""Test method to draw something so we can make sure opengl is working and we can see something"""
|
||||
self.draw_start()
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glBegin(GL_TRIANGLES)
|
||||
glIndexi(0)
|
||||
glColor3f(1.0, 0.0, 0.0)
|
||||
glVertex2i(0, 1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 1.0, 0.0)
|
||||
glVertex2i(-1, -1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 0.0, 1.0)
|
||||
glVertex2i(1, -1)
|
||||
glEnd()
|
||||
|
||||
self.draw_finish()
|
||||
#+END_SRC
|
||||
|
||||
This class will deal with the GUI interaction between GTK and OpenGL; we will capture mouse events, menu events and button events here and use them to adjust the OpenGL application.
|
||||
|
||||
The initialisation method will load in the interface from glade and grab the widgets we are interested in, at this stage we are only interested in the window and the drawing area widgets. We want to grab the close and draw events for these widgets and set a few parameters against the drawing widget.
|
||||
|
||||
Once we have the drawing area widget we create two methods: configure and draw. The first is called when setting up the drawing area or if we resize it or similar happens we may need to modify the drawing area size. The second method is called whenever the display needs to be updated; we can call this manually or it will get called if another window covers it up to redraw the OpenGL scene.
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
class gui:
|
||||
"""cad drawing application."""
|
||||
glwrap = gtkgl()
|
||||
|
||||
def __init__(self):
|
||||
""" Initialise the GTK interface grab the GTK widgets, connect the events we are interested in receiving"""
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('interface.glade')
|
||||
self.window = xml.get_object('window')
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
self.window.set_reallocate_redraws(True)
|
||||
|
||||
self.drawing_area = xml.get_object('drawingarea')
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height)
|
||||
self.drawing_area.connect('configure_event', self.on_configure_event)
|
||||
self.drawing_area.connect('draw', self.on_draw)
|
||||
self.drawing_area.connect('realize', self.on_draw)
|
||||
|
||||
self.window.show_all()
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
self.glwrap.configure(widget.get_window())
|
||||
return True
|
||||
|
||||
def on_draw(self, *args):
|
||||
self.glwrap.test()
|
||||
|
||||
def quit(self, *args):
|
||||
Gtk.main_quit()
|
||||
#+END_SRC
|
|
@ -0,0 +1,85 @@
|
|||
#+TITLE: Python CAD Tutorial 02 - Draw points in 3d Space
|
||||
#+DATE: 2013-10-02 12:00:00 UTC
|
||||
#+DESCRIPTION: How to draw points in 3D space
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 02-cad-draw-points-in-3d-space
|
||||
#+THUMBNAIL: ../../../images/cad/tut02-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 02 - Draw points in 3d Space
|
||||
.. slug: 02-cad-draw-points-in-3d-space
|
||||
.. date: 2013-10-02 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: How to draw points in 3D space
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut02][View/Download Code]]
|
||||
|
||||
#+CAPTION: Point in 3D space
|
||||
[[../../../images/cad/tut02-interface.png]]
|
||||
|
||||
In this tutorial we will create a 3D point class and position and draw it to the screen.
|
||||
|
||||
This will be a base class for storing positions in space, we will also implement code to display the points in this class. For now we will hard code a single point in the camera view. We do not have a working camera class yet, so this will be fixed with a position so we know its working.We will store the x, y and z positions and colour for the point along with the display size. The important methods here are the '__init__' and the 'draw' method, the others are helper methods which we will use later on.The helper methods include:
|
||||
|
||||
+ The __eq__ method will be usefull for testing if two points share the same location in space.
|
||||
+ __str__ will format the point into a string, which is usefull for debugging.
|
||||
+ get_position will return a numeric version of the point as a tuple.
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
class createpoint:
|
||||
x = y = z = 0.0
|
||||
display_color = (0, 0, 1)
|
||||
|
||||
def __init__(self, p, c=(0, 1, 0)):
|
||||
""" Position in 3d space as a tuple or list, and colour in tuple or list format"""
|
||||
self.point_size = 5
|
||||
self.color = c
|
||||
self.display_color = c
|
||||
self.x, self.y, self.z = p
|
||||
|
||||
def get_position(self):
|
||||
""" Return the cordinates as a tuple"""
|
||||
return self.x, self.y, self.z
|
||||
|
||||
def glvertex(self):
|
||||
""" Opengl vertex useful so we can dont have to glbegin and glend for each point"""
|
||||
glVertex3f(self.x, self.y, self.z)
|
||||
|
||||
def __getitem__(self, index):
|
||||
""" Get a cordinate handy for use in for loops where we want to calculate things"""
|
||||
return (self.x, self.y, self.z)[index]
|
||||
|
||||
def __str__(self):
|
||||
""" Print point cordinates useful for debugging"""
|
||||
return '(%s, %s, %s)' % (str(self.x), str(self.y), str(self.z))
|
||||
|
||||
def __eq__(self, point):
|
||||
""" Equality test so we can test if points occupy same space"""
|
||||
return self.x == point.x and self.y == point.y and self.z == point.z
|
||||
|
||||
def draw(self, c=(0, 1, 0)):
|
||||
""" Set the size of the point and render"""
|
||||
glPointSize(self.point_size)
|
||||
glBegin(GL_POINTS)
|
||||
glColor3f(self.color[0], self.color[1], self.color[2])
|
||||
glVertex3f(self.x, self.y, self.z)
|
||||
glEnd()
|
||||
#+END_SRC
|
||||
|
||||
Update the draw method to test out new code works.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
def on_draw(self, *args):
|
||||
""" Test code to make sure we can draw a pixel successfully can play with the parameters here"""
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
self.glwrap.draw_start()
|
||||
test_point = point.createpoint((0,0,0.5))
|
||||
test_point.draw()
|
||||
|
||||
self.glwrap.draw_finish()
|
||||
#+END_SRC
|
|
@ -0,0 +1,125 @@
|
|||
#+TITLE: Python CAD Tutorial 03 - Setup the camera for viewing our scene
|
||||
#+DATE: 2013-10-03 12:00:00 UTC
|
||||
#+DESCRIPTION: Setup the camera so we can see our models.
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 03-setup-camera-for-viewing-your-scene
|
||||
#+THUMBNAIL: ../../../images/cad/tut03-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 03 - Setup the camera for viewing our scene
|
||||
.. slug: 03-setup-camera-for-viewing-your-scene.org
|
||||
.. date: 2013-10-03 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Setup the camera so we can see our models.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
|
||||
#+END_SRC
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut03][View/Download Code]]
|
||||
|
||||
[[../../../images/cad/tut03-interface.png]]
|
||||
|
||||
|
||||
|
||||
In this tutorial we will deal with setting up the camera to view our workspace. The camera can be positioned and adjusted to change what is visible in the application.
|
||||
Look at the diagram below for the required parameters. The diagram uses the same name as the variables in the code, so you know what values to adjust to change the scene. Most should be self explanatory, for example 'camera location' and 'where we are looking'.
|
||||
The near and far planes set the size of the visible area, we can only see objects positioned inside this space. The field of view is the viewing angle and will adjust the size of the far_plane accordingly, also changing how much is visible.
|
||||
The only other parameters you need are the viewport width and height, which is basically the size of the screen or window. In our case, it's the size of the drawingarea widget.
|
||||
|
||||
|
||||
web.images.create(web.template.path_image + '/cad/camera-view.svg', title='Camera View')
|
||||
|
||||
The code for this is mainly oriented around the two functions called 'gluPerspective' and 'gluLookAt'.
|
||||
|
||||
'gluLookAt' is relatively straight forward; it takes 3 x,y,z values: the position of the camera, the point we wish to look at and an up vector. In this case we are using the y axis as the up vector.
|
||||
|
||||
'gluPerspective' is where we set what is visible in the world. We pass in our near and far planes and field of view to set the view frustrum area. We need to call the update method whenever something changes like the cameras position.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
|
||||
#+END_SRC
|
||||
|
||||
web.pre.create(load_partial(os.path.abspath(self.path_absolute + '../examples/tut03/cad/camera.py'), 0))
|
||||
web.page.section(web.pre.render())
|
||||
web.page.section(web.google_plus.render())
|
||||
|
||||
#+CAPTION: Setup the camera
|
||||
[[../../../images/cad/tut03-interface.png]]
|
||||
|
||||
|
||||
In this tutorial we will deal with setting up the camera to view our workspace. The camera can be positioned and adjusted to change what is visible in the application.
|
||||
Look at the diagram below for the required parameters. The diagram uses the same name as the variables in the code, so you know what values to adjust to change the scene.
|
||||
Most should be self explanatory, for example 'camera location' and 'where we are looking'.
|
||||
The near and far planes set the size of the visible area, we can only see objects positioned inside this space.
|
||||
The field of view is the viewing angle and will adjust the size of the far_plane accordingly, also changing how much is visible.
|
||||
The only other parameters you need are the viewport width and height, which is basically the size of the screen or window. In our case, it's the size of the drawingarea widget.
|
||||
|
||||
[[../../../images/cad/camera-view.svg]]
|
||||
|
||||
The code for this is mainly oriented around the two functions called 'gluPerspective' and 'gluLookAt'. 'gluLookAt' is relatively straight forward; it takes 3 x,y,z values: the position of the camera, the point we wish to look at and an up vector. In this case we are using the y axis as the up vector. 'gluPerspective' is where we set what is visible in the world. We pass in our near and far planes and field of view to set the view frustrum area. We need to call the update method whenever something changes like the cameras position.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
from OpenGL import GLX
|
||||
from OpenGL import GL
|
||||
from ctypes import *
|
||||
from point import createpoint
|
||||
|
||||
|
||||
class createcamera:
|
||||
#your visible area in 2d space, cordinates of screen or window view space
|
||||
viewport = 400, 400
|
||||
viewport_aspect = viewport[0] / viewport[1]
|
||||
|
||||
#how much of the world can we see, how wide is our sight
|
||||
field_of_view = 110
|
||||
|
||||
#camera parameters position look direction rotation
|
||||
lookat = createpoint((0.0, 0.0, 100.0))
|
||||
location = createpoint((0.0, 0.0, 0.0))
|
||||
camera_rotation = 0.0
|
||||
|
||||
#viewing depth of the camera, also know as the frustrum near and far planes
|
||||
near_plane = 10.0
|
||||
far_plane = 100.0
|
||||
|
||||
#view frustrum / camera visible area properties,
|
||||
def __init__(self, width, height):
|
||||
self.viewport = width, height
|
||||
self.viewport_aspect = self.viewport[0] / self.viewport[1]
|
||||
|
||||
def draw(self):
|
||||
pass
|
||||
|
||||
#the window has been resized so recalculate display
|
||||
#also used in initial setup of screen
|
||||
def update(self):
|
||||
"""call this when ever the screen is updated,
|
||||
calculates what's visible and where the camera exists in space.
|
||||
also set what we are loking at in the world"""
|
||||
glViewport(0, 0, self.viewport[0], self.viewport[1])
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
|
||||
#setup camera parameters field of view, size of camera (ie the view window size)
|
||||
#set the frustrum parameters
|
||||
gluPerspective(self.field_of_view,
|
||||
1.0 * self.viewport[0] / self.viewport[1],
|
||||
self.near_plane, self.far_plane)
|
||||
|
||||
#position the camera and look at something
|
||||
gluLookAt(self.location.x, self.location.y, self.location.z,
|
||||
self.lookat.x, self.lookat.y, self.lookat.z,
|
||||
0.0, 1.0, 0.0)
|
||||
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glLoadIdentity()
|
||||
#+END_SRC
|
||||
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
#+TITLE: Python CAD Tutorial 04 - Mouse coordinates in 3D space
|
||||
#+DATE: 2013-10-04 12:00:00 UTC
|
||||
#+DESCRIPTION: Setup the camera so we can see our models.
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 04-mouse-coordinates-in-3d-space
|
||||
#+THUMBNAIL: ../../../images/cad/tut04-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 04 - Mouse coordinates in 3D space
|
||||
.. slug: 04-mouse-coordinates-in-3d-space.org
|
||||
.. date: 2013-10-04 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Setup the camera so we can see our models.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut04][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: Mouse coordinates
|
||||
[[../../../images/cad/tut04-interface.png]]
|
||||
|
||||
We have a camera and something displayed on the screen, now let's start taking input from the mouse. We will create a new mouse handling class which will allow us to store a series of clicks. We can also use the stored positions for previews at a later date.The new class contains a few simple methods:
|
||||
|
||||
+ append() to add a new click
|
||||
+ count() to return the number of stored clicks
|
||||
+ get_click_position() returns the last clicked position
|
||||
+ get_points() returns a list of the stored locations, and resets the list
|
||||
+ clear() resets the stored coordinates.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
class mouse_state:
|
||||
""" store mouse clicks so we know where to draw and what to create """
|
||||
button1 = 0
|
||||
button2 = 0
|
||||
button3 = 0
|
||||
cordinates = []
|
||||
x = y = 0
|
||||
|
||||
def append(self, x, y, button=1):
|
||||
""" store a new mouse click """
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.cordinates.append((x, y,))
|
||||
|
||||
def get_click_position(self):
|
||||
""" return the position of the last click """
|
||||
return self.x, self.y
|
||||
|
||||
def get_points(self):
|
||||
""" return all stored points we may want to store lots of points when drawing a line for example"""
|
||||
if len(self.cordinates) != 2:
|
||||
return None
|
||||
result = self.cordinates
|
||||
self.cordinates = []
|
||||
return result
|
||||
|
||||
def count(self):
|
||||
"""return number of stored points """
|
||||
return len(self.cordinates)
|
||||
|
||||
def clear(self):
|
||||
self.x = self.y = 0
|
||||
self.cordinates = []
|
||||
#+END_SRC
|
||||
|
||||
Let's add a new method to our camera class, which will convert mouse clicks in 2 dimensional space into 3 dimensional coordinates so we can position something on the screen.The 'get_click_point' method below takes a tuple containing the x and y coordinates from the mouse. It then converts the y position because in opengl '0' is at the bottom, but drawing area widget has '0' at the top. Finally it uses gluUnProject to convert from 2d space to 3d space.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
def get_click_point(self, pos):
|
||||
""" convert 2d click in the viewport to 3d point in space"""
|
||||
viewport = glGetIntegerv(GL_VIEWPORT)
|
||||
modelview = glGetDoublev(GL_MODELVIEW_MATRIX)
|
||||
projection = glGetDoublev(GL_PROJECTION_MATRIX)
|
||||
|
||||
#convert screen ccordinate to opengl cordinates, this means modifying the y axes only
|
||||
x, y = pos[0], self.viewport[1] - pos[1]
|
||||
|
||||
#use unproject to calculate the point and store the resutl in a point object
|
||||
return createpoint(gluUnProject(x, y, 0.20, modelview, projection, viewport), (250, 0, 0))
|
||||
#+END_SRC
|
||||
|
||||
Let's handle the mouse click events in the drawing area; we will store the 2d x and y coordinates of a mouse click in our new mouse_state_class. Now we adjust the on_draw method to draw a point at the location we clicked on the page, which gives us visual feedback that everything is working correctly.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
def mouse_click(self, widget, event):
|
||||
self.mouse.append(event.x, event.y, event.button)
|
||||
self.test_point = self.camera.get_click_point(self.mouse.get_click_position())
|
||||
self.on_draw()
|
||||
|
||||
def on_draw(self, *args):
|
||||
""" Test code to make sure we can draw a pixel successfully,
|
||||
also test we can position our new camera class to lookat the pixel"""
|
||||
#lets not look directly at the point we are drawing, demonstrating we can lookat points in space
|
||||
self.camera.lookat.x = 20
|
||||
self.camera.lookat.y = 20
|
||||
self.camera.lookat.z = -20
|
||||
|
||||
#recalculate our camera based on the new settings
|
||||
self.camera.update()
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
self.glwrap.draw_start()
|
||||
|
||||
#place a point we can lookat that will be positioned in our field of view.
|
||||
self.test_point.draw()
|
||||
|
||||
self.glwrap.draw_finish()
|
||||
#+END_SRC
|
|
@ -0,0 +1,62 @@
|
|||
#+TITLE: Python CAD Tutorial 05 - Drawing lines in 3D space
|
||||
#+DATE: 2013-10-05 12:00:00 UTC
|
||||
#+DESCRIPTION: Connect our points from a previous tutorial together with lines.
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 05-cad-drawing-lines-in-3d-space
|
||||
#+THUMBNAIL: ../../../images/cad/tut05-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 05 - Drawing lines in 3D space
|
||||
.. slug: 05-cad-drawing-lines-in-3d-space.org
|
||||
.. date: 2013-10-05 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Connect our points from a previous tutorial together with lines.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut05][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: Lines
|
||||
[[../../../images/cad/tut05-interface.png]]
|
||||
|
||||
In this tutorial we will create a new line class. Initially this will be setup to create an unlimited number of lines, drawn on every second click of the mouse.Our new class will be similar to the point class, but will contain two points to store the start and stop coordinates of the lines. We will also adjust the draw method to draw lines instead of points.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from point import createpoint
|
||||
|
||||
|
||||
class createline:
|
||||
p1 = p2 = None
|
||||
color = 0, 1, 0
|
||||
display_color = (0, 1, 0)
|
||||
|
||||
def __init__(self, point1, point2):
|
||||
""" create the start and stop points and colour of the line"""
|
||||
self.p1 = createpoint(point1)
|
||||
self.p2 = createpoint(point2)
|
||||
|
||||
def glvertex(self):
|
||||
""" Opengl vertex useful so we can dont have to glbegin and glend for each point"""
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
|
||||
def get_points(self):
|
||||
""" return the start and end point of the line could be used in a drawing loop"""
|
||||
for p in (self.p1, self.p2):
|
||||
yield (p)
|
||||
|
||||
def draw(self):
|
||||
""" lets draw the line baase on the cordinates, also draw the points for now """
|
||||
glColor3f(*self.display_color)
|
||||
|
||||
glBegin(GL_LINES)
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
glEnd()
|
||||
|
||||
self.p1.draw()
|
||||
self.p2.draw()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#+TITLE: Python CAD Tutorial 06 - Drawing planes in 3D space
|
||||
#+DATE: 2013-10-06 12:00:00 UTC
|
||||
#+DESCRIPTION: Drawing planes in 3d space
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 06-cad-drawing-planes-in-3d-space
|
||||
#+THUMBANIL: ../../../images/cad/tut06-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 06 - Drawing planes in 3D space
|
||||
.. slug: 06-cad-drawing-planes-in-3d-space.org
|
||||
.. date: 2013-10-06 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Drawing planes in 3d space
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut06][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: Planes
|
||||
[[../../../images/cad/tut06-interface.png]]
|
||||
|
||||
We will now add a plane class, this is another fundamental building block. It will also be used for various collision detections at a later stage.We have created two classes below:
|
||||
|
||||
+ The first class will be used for the grid in the next tutorial. A plane does not normally have a limit of size but in this we are limiting it for display purposes.'createplanesimple' takes a point in space and a size. It will then calculate the plane by appending the size to the x and y coridinates, z will remain fixed.
|
||||
+ The second class is a more accurate way of representing a plane. Usually we only need to store a point in space and a direction, so by adding the coordinates together we can get a central point and direction this will be added at a later date.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from point import createpoint
|
||||
|
||||
class createplanesimple:
|
||||
def __init__(self, p1, plane_size=10):
|
||||
size = plane_size / 2
|
||||
self.p1 = createpoint((p1[0] + size, p1[1]+size, p1[2]))
|
||||
self.p2 = createpoint((p1[0] - size, p1[1]+size, p1[2]))
|
||||
self.p3 = createpoint((p1[0] + size, p1[1]-size, p1[2]))
|
||||
self.p4 = createpoint((p1[0] - size, p1[1]-size, p1[2]))
|
||||
self.normal = createpoint((0, 1, 0))
|
||||
|
||||
def draw(self):
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
glColor3f(0.9, 0.9, 0.9)
|
||||
glNormal3f(self.normal.x, self.normal.y, self.normal.z)
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
glVertex3f(self.p3.x, self.p3.y, self.p3.z)
|
||||
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
glVertex3f(self.p3.x, self.p3.y, self.p3.z)
|
||||
glVertex3f(self.p4.x, self.p4.y, self.p4.z)
|
||||
glEnd()
|
||||
self.p1.draw((0, 0, 1))
|
||||
self.p2.draw((0, 0, 1))
|
||||
self.p3.draw((0, 0, 1))
|
||||
self.p4.draw((0, 0, 1))
|
||||
|
||||
|
||||
class createplane:
|
||||
def __init__(self, p1, p2, p3):
|
||||
self.p1 = createpoint(p1)
|
||||
self.p2 = createpoint(p2)
|
||||
self.p3 = createpoint(p3)
|
||||
self.normal = createpoint((0, 1, 0))
|
||||
|
||||
|
||||
def draw(self):
|
||||
glBegin(GL_TRIANGLE_STRIP)
|
||||
glColor3f(0.8, 0.8, 0.8)
|
||||
glNormal3f(self.normal.x, self.normal.y, self.normal.z)
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
glVertex3f(self.p3.x, self.p3.y, self.p3.z)
|
||||
glEnd()
|
||||
self.p1.draw((1, 0, 0))
|
||||
self.p2.draw((0, 1, 0))
|
||||
self.p3.draw((0, 0, 1))
|
||||
#+END_SRC
|
|
@ -0,0 +1,81 @@
|
|||
#+TITLE: Python CAD Tutorial 07 - Drawing a grid
|
||||
#+DATE: 2013-10-07 12:00:00 UTC
|
||||
#+DESCRIPTION: Drawing a grid in 3d space using gl lines
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 07-cad-drawing-a-grid
|
||||
#+THUMBNAIL: ../../../images/cad/tut07-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 07 - Drawing a grid
|
||||
.. slug: 07-cad-drawing-a-grid.org
|
||||
.. date: 2013-10-07 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Drawing a grid in 3d space using gl lines
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut07][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: Grid
|
||||
[[../../../images/cad/tut07-interface.png]]
|
||||
|
||||
We created a plane in the previous tutorial. Now we will inherit that class to create a grid, at a later stage we will add snap to grid functionality.The new grid class will take three parameters: a centre point, a spacing value and a size value. We will set the size value to be the size of the viewport and will set a default spacing between the lines.The new methods for the class are below:
|
||||
|
||||
+ The grid_spacing method shifts the lines by the amount specified in the spacing parameter until the maximum size has been reached.
|
||||
+ The draw method draws lines to make up the grid. On every tenth line it changes the colour to a darker line.
|
||||
|
||||
Future tutorials will deal with snap to grid functionality and auto calculating based on the zoom level, but for now we have something to work with.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
import math
|
||||
|
||||
from point import createpoint
|
||||
from plane import createplanesimple
|
||||
|
||||
|
||||
class creategrid:
|
||||
display_color = (0.6, 0.6, 0.6)
|
||||
|
||||
large_grid = 10
|
||||
small_grid = 2.5
|
||||
|
||||
def __init__(self, p1, spacing=1.5, size=65):
|
||||
self.plane = createplanesimple(p1, size * 2)
|
||||
self.p1 = createpoint(p1)
|
||||
self.normal = createpoint((0, 1, 0))
|
||||
self.size = size
|
||||
self.small_grid = spacing
|
||||
self.large_grid = spacing * 10
|
||||
|
||||
def grid_spacing(self):
|
||||
#work out how many times the grid units fit inside our grid size and make it a whole number
|
||||
size = math.ceil(self.size / self.small_grid) * self.small_grid
|
||||
#adjust x by size so we can draw the lines
|
||||
x = self.p1.x - size
|
||||
#loop from start until our lines are greater than our max size
|
||||
while x < (self.p1.x + size):
|
||||
x += self.small_grid
|
||||
yield x
|
||||
|
||||
def draw(self):
|
||||
self.plane.draw()
|
||||
glColor3f(*self.display_color)
|
||||
glBegin(GL_LINES)
|
||||
|
||||
for item in self.grid_spacing():
|
||||
#coordinate modulus large_grid (returns 0 if there is no remaineder), so lets draw a different colour line
|
||||
if (item % self.large_grid) == 0:
|
||||
glColor3f(0.4, 0.4, 0.4)
|
||||
else:
|
||||
glColor3f(*self.display_color)
|
||||
|
||||
glVertex3f(item, self.p1.y - self.size, self.p1.z)
|
||||
glVertex3f(item, self.p1.y + self.size, self.p1.z)
|
||||
|
||||
glVertex3f(self.p1.x - self.size, item, self.p1.z)
|
||||
glVertex3f(self.p1.x + self.size, item, self.p1.z)
|
||||
glEnd()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#+TITLE: Python CAD Tutorial 08 - Drawing a polygon & add generic shape class
|
||||
#+DATE: 2013-10-08 12:00:00 UTC
|
||||
#+DESCRIPTION: Draw a polygon and create a generic shape base class
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 08-cad-drawing-a-polygon-adding-shape-class
|
||||
#+THUMBNAIL: ../../../images/cad/tut08-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 08 - Drawing a polygon & add generic shape class
|
||||
.. slug: 08-cad-drawing-a-polygon-adding-shape-class.org
|
||||
.. date: 2013-10-08 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Draw a polygon and create a generic shape base class
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut08][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: Polygon
|
||||
[[../../../images/cad/tut08-interface.png]]
|
||||
|
||||
We created a grid in the previous tutorial. Now let's add a polygon class and a shape object to contain our primitives.The polygon class is basically an extension of the line class. It works in the same way, however we have added an append method which keeps adding points to the polygon on each click. The draw method has then been adjusted to loop over and draw the polygon from the point list.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from point import createpoint
|
||||
|
||||
|
||||
class createpolygon:
|
||||
name = 'polygon'
|
||||
points = []
|
||||
closed = True
|
||||
|
||||
def __init__(self, point):
|
||||
self.points = []
|
||||
self.points.append(createpoint(point))
|
||||
self.color = 0, 1, 0
|
||||
self.display_color = 0, 1, 0
|
||||
self.newPoint = None
|
||||
|
||||
def append(self, point):
|
||||
self.points.append(createpoint(point))
|
||||
|
||||
def glvertex(self):
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
|
||||
#vertexs to draw a line use with glbegin(GL_LINES)
|
||||
def glvertexlist(self):
|
||||
glVertex3f(self.p1.x, self.p1.y, self.p1.z)
|
||||
glVertex3f(self.p2.x, self.p2.y, self.p2.z)
|
||||
|
||||
def get_points(self):
|
||||
for p in self.points:
|
||||
yield (p)
|
||||
|
||||
def draw(self):
|
||||
glColor3f(*self.display_color)
|
||||
if len(self.points) > 1:
|
||||
glBegin(GL_LINES)
|
||||
for n in xrange(0, len(self.points) - 1):
|
||||
glVertex3f(self.points[n].x, self.points[n].y, self.points[n].z)
|
||||
glVertex3f(self.points[n + 1].x, self.points[n + 1].y, self.points[n + 1].z)
|
||||
if self.closed is True:
|
||||
glVertex3f(self.points[0].x, self.points[0].y, self.points[0].z)
|
||||
glVertex3f(self.points[-1].x, self.points[-1].y, self.points[-1].z)
|
||||
glEnd()
|
||||
|
||||
for item in self.points:
|
||||
item.draw()
|
||||
#+END_SRC
|
||||
|
||||
The shape class below is basically just a container object for storing our various primitives like lines, circles and polygons. This class has methods to loop over and add the primitives store,. This will be useful later on for changing properties on mass.In the main mycad.py file you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from point import createpoint
|
||||
|
||||
|
||||
class createshape:
|
||||
location = createpoint((0, 0, 0))
|
||||
primitives = []
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.primitives = []
|
||||
|
||||
def append(self, item):
|
||||
self.primitives.append(item)
|
||||
|
||||
def __getitem__(self, n):
|
||||
return self.primitives[n]
|
||||
|
||||
#temporary to get last primitive added to the scene
|
||||
def update_last(self):
|
||||
return self.primitives[-1]
|
||||
|
||||
def count(self):
|
||||
return len(self.primitives) - 1
|
||||
|
||||
def draw(self):
|
||||
for item in self.primitives:
|
||||
item.draw()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#+TITLE: Python CAD Tutorial 09 - Workspace class
|
||||
#+DATE: 2013-10-09 12:00:00 UTC
|
||||
#+DESCRIPTION: Create a workspace class to hold our objects
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 09-cad-workspace-class
|
||||
#+THUMBNAIL: ../../../images/cad/tut09-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 09 - Workspace class
|
||||
.. slug: 09-cad-workspace-class.org
|
||||
.. date: 2013-10-09 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Create a workspace class to hold our objects
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut09][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: workspace class
|
||||
[[../../../images/cad/tut09-interface.png]]
|
||||
|
||||
The workspace class below is a management class. It will contain various methods for working with the visible objects. It will also manage our layers. For now we will add simple functions, for example 'append' (which will add objects to a layer).In the main mycad.py file, you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them.
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
from cad.point import createpoint
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.arrays import vbo
|
||||
from numpy import array
|
||||
|
||||
|
||||
#lets create a dict object we can name our layers from here
|
||||
class workspace(dict):
|
||||
selected_shape = 'workspace'
|
||||
selected_part = None
|
||||
|
||||
def reset(self):
|
||||
for k in self.keys():
|
||||
del(self[k])
|
||||
self['workspace'] = shape.createshape()
|
||||
|
||||
def create(self, item, name=None):
|
||||
if name is not None:
|
||||
self.selected_part = name
|
||||
if self.selected_part is not None:
|
||||
if not self.get(self.selected_part):
|
||||
self[self.selected_part] = shape.createshape()
|
||||
self[self.selected_part].append(item)
|
||||
self.selected_shape = self[self.selected_part].count()
|
||||
|
||||
def append(self, item, name=None):
|
||||
self[self.selected_part].primitives[self.selected_shape].append(item)
|
||||
|
||||
def objects_iter(self):
|
||||
for layer in self.keys():
|
||||
for shape in self[layer]:
|
||||
yield shape
|
||||
|
||||
def set(self, name, value):
|
||||
for layer in self.values():
|
||||
for item in layer.primitives:
|
||||
item.display_color = value
|
||||
self.color = value
|
||||
|
||||
#handle drawing our shapes here
|
||||
def draw(self):
|
||||
for item in self.values():
|
||||
item.draw()
|
||||
#+END_SRC
|
|
@ -0,0 +1,172 @@
|
|||
#+TITLE: Python CAD Tutorial 10 - Extending the GTK interface
|
||||
#+DATE: 2013-10-10 12:00:00 UTC
|
||||
#+DESCRIPTION: Add new GUI elements to initiate drawing of various shapes
|
||||
#+FILETAGS: GTK-3:python:cad:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 10-cad-extending-the-interface
|
||||
#+THUMBNAIL: ../../../images/cad/tut10-interface.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Python CAD Tutorial 10 - Extending the GTK interface
|
||||
.. slug: 10-cad-extending-the-interface.org
|
||||
.. date: 2013-10-10 12:00:00 UTC
|
||||
.. tags: GTK-3, python, cad, opengl
|
||||
.. category: python
|
||||
.. description: Add new GUI elements to initiate drawing of various shapes
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
[[https://code.launchpad.net/~oly/fabricad/tut10][View/Download Code]]
|
||||
|
||||
|
||||
#+CAPTION: extended gui
|
||||
[[../../../images/cad/tut10-interface.png]]
|
||||
|
||||
We have created some buttons in glade and connected the events so we can switch between the object types ie circle polygon and closed polygons.
|
||||
|
||||
Create a class for managing the treeview, this widget is very flexible and can be difficult to work with so abstract the details away. This will be used for showing the layers we have in the project and the objects under each layer.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
import os
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
|
||||
|
||||
class objectList:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, workspace, treeview):
|
||||
self.treeview = treeview
|
||||
self.workspace = workspace
|
||||
|
||||
# connect the treeview events, capture row selection and row click
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.show_menu)
|
||||
|
||||
#add a tree store model so everything has a hierarchy
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
# append colmun headers to the treeview and cell renderer
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
# populate the objects from the layers
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
for layer in self.workspace.keys():
|
||||
tree_iter = self.append(layer)
|
||||
for part in self.workspace[layer]:
|
||||
self.append(part.name, tree_iter)
|
||||
|
||||
def menu(self):
|
||||
self.layer_menu = Gtk.Menu()
|
||||
|
||||
self.layer_menu_item = Gtk.MenuItem("Display")
|
||||
#self.layer_menu_item.connect("activate", self.toggle_value_handler, 'show')
|
||||
self.layer_menu.append(self.layer_menu_item)
|
||||
|
||||
self.layer_menu_item = Gtk.MenuItem("Colour")
|
||||
self.layer_menu.append(self.layer_menu_item)
|
||||
|
||||
def show_menu(self, tv, event):
|
||||
if event.button == 2:
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
|
||||
if event.button == 3:
|
||||
self.layer_menu.show_all()
|
||||
self.layer_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def append(self, name, parent=None):
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
#+END_SRC
|
||||
|
||||
The workspace class below is a management class. It will contain various methods for working with the visible objects. It will also manage our layers. For now we will add simple functions, for example 'append' (which will add objects to a layer).In the main mycad.py file, you can see that our new polygon is contained and updated through the shape class. Later on we will attach this to our GUI so we can dynamically add primitives as we need them.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
class objectList:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, workspace, treeview):
|
||||
self.treeview = treeview
|
||||
self.workspace = workspace
|
||||
|
||||
# connect the treeview events, capture row selection and row click
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.show_menu)
|
||||
|
||||
#add a tree store model so everything has a hierarchy
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
# append colmun headers to the treeview and cell renderer
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
# populate the objects from the layers
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
for layer in self.workspace.keys():
|
||||
tree_iter = self.append(layer)
|
||||
for part in self.workspace[layer]:
|
||||
self.append(part.name, tree_iter)
|
||||
|
||||
def menu(self):
|
||||
self.layer_menu = Gtk.Menu()
|
||||
|
||||
self.layer_menu_item = Gtk.MenuItem("Display")
|
||||
#self.layer_menu_item.connect("activate", self.toggle_value_handler, 'show')
|
||||
self.layer_menu.append(self.layer_menu_item)
|
||||
|
||||
self.layer_menu_item = Gtk.MenuItem("Colour")
|
||||
self.layer_menu.append(self.layer_menu_item)
|
||||
|
||||
def show_menu(self, tv, event):
|
||||
if event.button == 2:
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
|
||||
if event.button == 3:
|
||||
self.layer_menu.show_all()
|
||||
self.layer_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def append(self, name, parent=None):
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#+TITLE: Display a GTK window
|
||||
#+DATE: 2014-01-05 12:00:00 UTC
|
||||
#+DESCRIPTION: GTK Example on loading a window from a glade fiel and displaying it on the screen.
|
||||
#+FILETAGS: GTK-3:python:window
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 01-displaying-a-gtk-window
|
||||
#+THUMBNAIL:../../../images/gtk/tut01-windows.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Display a GTK window
|
||||
.. slug: 01-displaying-a-gtk-window
|
||||
.. date: 2014-01-05 12:00:00 UTC
|
||||
.. tags: GTK-3, python, window
|
||||
.. category: python
|
||||
.. description: GTK Example on loading a window from a glade fiel and displaying it on the screen.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Display GTK-3 window
|
||||
[[../../../images/gtk/tut01-windows.png]]
|
||||
|
||||
|
||||
Simple GTK application load a window from a glade file and exit on hitting the window close button, we use the get_object method to get the window by name then use connect to attach to the close and destroy events.
|
||||
|
||||
There are lots of event we can connect to like mouse click key press and window minimise maximise check the gtk docs or glade for a list of possible event for each widget.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 01 Create and destroy a window"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut01.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
|
@ -0,0 +1,70 @@
|
|||
#+TITLE: Adding GTK-3 buttons and switches
|
||||
#+DATE: 2014-01-08 12:00:00 UTC
|
||||
#+DESCRIPTION: GTK Example on click and toggle buttons connect to there events to run code when activated by the user.
|
||||
#+FILETAGS: GTK-3:python:glade
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 02-adding-buttons-and-switches
|
||||
#+THUMBNAIL:../../../images/gtk/tut02-buttons.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Adding GTK-3 buttons and switches
|
||||
.. slug: 02-adding-buttons-and-switches
|
||||
.. date: 2014-01-08 12:00:00 UTC
|
||||
.. tags: GTK-3, python, glade
|
||||
.. category: python
|
||||
.. description: GTK Example on click and toggle buttons connect to there events to run code when activated by the user.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Display GTK-3 Buttons and switches
|
||||
[[../../../images/gtk/tut02-buttons.png]]
|
||||
|
||||
|
||||
The example below loads 4 buttons from a glade 2 standard and 2 toggle buttons, it then connects event handles to show some text when clicked or to show the toggle state if a toggle button was clicked.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 02 buttons"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut02-buttons.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons['but1'] = xml.get_object('button1')
|
||||
self.buttons['but2'] = xml.get_object('button2')
|
||||
self.buttons['but3'] = xml.get_object('togglebutton1')
|
||||
self.buttons['but4'] = xml.get_object('togglebutton2')
|
||||
|
||||
self.buttons['but1'].connect('clicked', self.button_events)
|
||||
self.buttons['but2'].connect('clicked', self.button_events)
|
||||
self.buttons['but3'].connect('clicked', self.button_events)
|
||||
self.buttons['but4'].connect('clicked', self.button_events)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def button_events(self, widget):
|
||||
toggle_value = ''
|
||||
if widget.get_name() == 'GtkToggleButton':
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#+TITLE: Adding switch and radio widgets
|
||||
#+DATE: 2014-01-10 12:00:00 UTC
|
||||
#+DESCRIPTION: GTK example demonstrating the use of switches and radio buttons.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 03-adding-radio-buttons-and-switches
|
||||
#+THUMBNAIL:../../../images/gtk/tut03-switch-radio.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Adding switch and radio widgets
|
||||
.. slug: 03-adding-radio-buttons-and-switches
|
||||
.. date: 2014-01-10 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: GTK example demonstrating the use of switches and radio buttons.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Adding radio buttons & switches
|
||||
[[../../../images/gtk/tut03-switch-radio.png]]
|
||||
|
||||
|
||||
The below sample shows loading and retrieving state values for radio buttons and switches.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 03 Radio Buttons and switches"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut03.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons['radio1'] = xml.get_object('radiobutton1')
|
||||
self.buttons['radio2'] = xml.get_object('radiobutton2')
|
||||
self.buttons['switch1'] = xml.get_object('switch1')
|
||||
self.buttons['switch2'] = xml.get_object('switch2')
|
||||
|
||||
#set a name of the switches because they dont have a label so we can use this to show selected button
|
||||
self.buttons['switch1'].set_name('switch1')
|
||||
self.buttons['switch2'].set_name('switch2')
|
||||
|
||||
#connect the interface events to functions
|
||||
self.buttons['switch1'].connect('button-press-event', self.switch_button_events)
|
||||
self.buttons['switch2'].connect('button-press-event', self.switch_button_events)
|
||||
self.buttons['radio1'].connect('toggled', self.radio_button_events)
|
||||
self.buttons['radio2'].connect('toggled', self.radio_button_events)
|
||||
|
||||
#add the second radio to the first radio button making it part of the group
|
||||
self.buttons['radio2'].join_group(self.buttons['radio1'])
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def switch_button_events(self, widget, params):
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' + toggle_value)
|
||||
|
||||
def radio_button_events(self, widget):
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#+TITLE: Adding Dropdowns and spin buttons
|
||||
#+DATE: 2014-01-15 12:00:00 UTC
|
||||
#+DESCRIPTION: GTK example of some combo / slecet drop down boxesfor user interaction.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 04-adding-dropdowns-and-spin-buttons
|
||||
#+THUMBNAIL:../../../images/gtk/tut04-text-dropdown-spin.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Adding Dropdowns and spin buttons
|
||||
.. slug: 04-adding-dropdowns-and-spin-buttons
|
||||
.. date: 2014-01-15 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: GTK example of some combo / slecet drop down boxesfor user interaction.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Adding dropdown & spin widgets
|
||||
[[../../../images/gtk/tut04-text-dropdown-spin.png]]
|
||||
|
||||
|
||||
|
||||
This example demonstrates using some drop down boxes and adding new items and retrieving the selected values.
|
||||
This snippet also demonstrates the use of spin buttons for selecting a value from a range.
|
||||
|
||||
When creating a combobbox in glade make sure you add a liststore to the widget, and also edit the combobox properties in a seperate window so you can access and the hierarchy menu and assign a cell render to the column in your listview.
|
||||
|
||||
#+CAPTION: Adding a list store
|
||||
[[../../../images/gtk/add-list-store.png]]
|
||||
|
||||
#+CAPTION: Adding a cell renderer
|
||||
[[../../../images/gtk/attach-cell-renderer-set-text-column.png]]
|
||||
|
||||
#+CAPTION: Adding a list store
|
||||
[[../../../images/gtk/attach-liststore-model.png]]
|
||||
|
||||
#+CAPTION: Adding a combobox list store
|
||||
[[../../../images/gtk/edit-combobox-liststore.png]]
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 04 text input, spin input, drop down options"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut04.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['spin1'] = xml.get_object('spinbutton1')
|
||||
self.widgets['spin2'] = xml.get_object('spinbutton2')
|
||||
|
||||
#simple dropdown just text
|
||||
self.widgets['comboboxtext'] = xml.get_object('comboboxtext1')
|
||||
|
||||
#more complex multi row multi data types in dropdown using renderers
|
||||
self.widgets['combobox'] = xml.get_object('combobox1')
|
||||
self.widgets['combobox_liststore']= xml.get_object('liststore1')
|
||||
|
||||
#add some items to the drop down
|
||||
self.widgets['combobox_liststore'].append(['new row test'])
|
||||
self.widgets['combobox_liststore'].append(['second row test'])
|
||||
|
||||
#adding new rows to the dropdown
|
||||
self.widgets['comboboxtext'].append_text('row4')
|
||||
self.widgets['comboboxtext'].append_text('row5')
|
||||
|
||||
#connect to events
|
||||
self.widgets['comboboxtext'].connect('changed', self.dropdown_event_text)
|
||||
self.widgets['spin1'].connect('value-changed', self.spin_button_event)
|
||||
self.widgets['spin2'].connect('value-changed', self.spin_button_event)
|
||||
self.widgets['combobox'].connect('changed', self.dropdown_event)
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show_all()
|
||||
|
||||
def dropdown_event_text(self, widget):
|
||||
self.text.set_text(widget.get_active_text())
|
||||
|
||||
def dropdown_event(self, widget):
|
||||
list_view_model = widget.get_model()
|
||||
active_iter_index = widget.get_active()
|
||||
row_iter = list_view_model.get_iter(active_iter_index)
|
||||
self.text.set_text(widget.get_name() + ' ' +list_view_model.get_value(row_iter, 0 ))
|
||||
|
||||
def spin_button_event(self, widget):
|
||||
self.text.set_text(widget.get_name() + ' ' + ' ' + str(widget.get_value()))
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
#+TITLE: Adding progress bars & spinners
|
||||
#+DATE: 2014-01-20 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a progress bar widget and spinner to demonstrate updating and retriving values.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 05-adding-progress-bars-and-spinners
|
||||
#+THUMBNAIL:../../../images/gtk/tut05-progress-spinners.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Adding progress bars & spinners
|
||||
.. slug: 05-adding-progress-bars-and-spinners
|
||||
.. date: 2014-01-20 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a progress bar widget and spinner to demonstrate updating and retriving values.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Progress bars and spinners
|
||||
[[../../../images/gtk/tut05-progress-spinners.png]]
|
||||
|
||||
This sample demonstrates two progress bar styles and how to update them too show progress, it also demonstrates starting and stopping a spinning graphic.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 05 demo progress bars and the spinner graphic."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut05.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['entry'] = xml.get_object('entry2')
|
||||
|
||||
self.widgets['spinner1'] = xml.get_object('spinner1')
|
||||
self.widgets['spinnerbutton'] = xml.get_object('togglebutton1')
|
||||
self.widgets['progress1'] = xml.get_object('progressbar1')
|
||||
self.widgets['progress2'] = xml.get_object('progressbar2')
|
||||
|
||||
self.widgets['spinnerbutton'].connect('clicked', self.toggle_spinner)
|
||||
|
||||
self.widgets['progress1'].set_text('active mode')
|
||||
self.widgets['progress2'].set_text('percentage mode')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
self.widgets['spinner1'].start()
|
||||
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar)
|
||||
GLib.timeout_add_seconds(1, self.update_percentage_progress_bar)
|
||||
|
||||
def update_active_progress_bar(self):
|
||||
# nudge the progress bar to show something is still happening, just updating every second in the example
|
||||
# long running processes which have an unknow finish time would use this version
|
||||
self.widgets['progress1'].pulse()
|
||||
return True
|
||||
|
||||
def update_percentage_progress_bar(self):
|
||||
self.count +=0.1
|
||||
self.widgets['progress2'].set_fraction(self.count)
|
||||
if self.count<1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def toggle_spinner(self, widget):
|
||||
if widget.get_active():
|
||||
self.widgets['spinner1'].start()
|
||||
else:
|
||||
self.widgets['spinner1'].stop()
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
#+TITLE: Displaying dialog boxes
|
||||
#+DATE: 2014-02-03 12:00:00 UTC
|
||||
#+DESCRIPTION: Demonstration of opening various dialog boxes and retrieving the selected values.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 06-dialog-boxes
|
||||
#+THUMBNAIL:../../../images/gtk/tut06-dialogs.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Displaying dialog boxes
|
||||
.. slug: 06-dialog-boxes
|
||||
.. date: 2014-02-03 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Demonstration of opening various dialog boxes and retrieving the selected values.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Dialog Boxes
|
||||
[[../../../images/gtk/tut06-dialogs.png]]
|
||||
|
||||
|
||||
This example program allows you to open the file selector dialogs and displays the selected files, it also demonstrates selecting colour and font from the built in selector dialogs.
|
||||
once a file font or colour has been selected we grab the resulting value and display it as an example of retrieving the value.
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 06 demo of file, colour and font dialogs."""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut06.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['entry'] = xml.get_object('entry2')
|
||||
self.widgets['color_button'] = xml.get_object('btnColour')
|
||||
self.widgets['font_button'] = xml.get_object('btnFont')
|
||||
self.widgets['open_button'] = xml.get_object('btnLoad')
|
||||
self.widgets['save_button'] = xml.get_object('btnSave')
|
||||
|
||||
self.widgets['color_button'].connect('clicked', self.colour_selector)
|
||||
self.widgets['font_button'].connect('clicked', self.font_selector)
|
||||
self.widgets['save_button'].connect('clicked', self.save_file_selector)
|
||||
self.widgets['open_button'].connect('clicked', self.open_file_selector)
|
||||
|
||||
self.widgets['open_dialog'] = xml.get_object('fileChooserLoad')
|
||||
self.widgets['open_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
|
||||
self.widgets['open_dialog'].add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||
|
||||
self.widgets['save_dialog'] = xml.get_object('fileChooserSave')
|
||||
self.widgets['save_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
|
||||
self.widgets['save_dialog'].add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
|
||||
|
||||
self.widgets['color_dialog'] = xml.get_object('colorSelect')
|
||||
|
||||
self.widgets['font_dialog'] = xml.get_object('fontSelect')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def colour_selector(self, widget):
|
||||
self.widgets['color_dialog'].run()
|
||||
self.colour = self.widgets['color_dialog'].get_color_selection().get_current_color()
|
||||
|
||||
#accessing the individual values as integers, converting to floats here
|
||||
colour = [self.colour.red / 65535.0,
|
||||
self.colour.green / 65535.0,
|
||||
self.colour.blue / 65535.0]
|
||||
|
||||
self.widgets['color_dialog'].hide()
|
||||
self.text.set_text(self.widgets['color_dialog'].get_name() + ' ' + self.colour.to_string())
|
||||
|
||||
def font_selector(self, widget):
|
||||
self.widgets['font_dialog'].run()
|
||||
self.font = self.widgets['font_dialog'].get_font_selection()
|
||||
self.text.set_text(self.widgets['font_dialog'].get_name() + ' ' + self.font.get_font_name() + ' ' + str(self.font.get_size()))
|
||||
self.widgets['font_dialog'].hide()
|
||||
|
||||
def open_file_selector(self, widget):
|
||||
response = self.widgets['open_dialog'].run()
|
||||
if self.widgets['open_dialog'].get_filenames():
|
||||
self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ str(self.widgets['open_dialog'].get_filenames()))
|
||||
else:
|
||||
if self.widgets['open_dialog'].get_filename():
|
||||
self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ self.widgets['open_dialog'].get_filename())
|
||||
self.widgets['open_dialog'].hide()
|
||||
|
||||
def save_file_selector(self, widget):
|
||||
response = self.widgets['save_dialog'].run()
|
||||
self.text.set_text(self.widgets['save_dialog'].get_name() + ' '+ self.widgets['save_dialog'].get_filename())
|
||||
self.widgets['save_dialog'].hide()
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#+TITLE: App chooer and scale buttons
|
||||
#+DATE: 2014-02-07 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 07-app-chooser-scale-button
|
||||
#+THUBNAIL:../../../images/gtk/tut07-appchooser-scale.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: App chooer and scale buttons
|
||||
.. slug: 07-app-chooser-scale-button
|
||||
.. date: 2014-02-07 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: App chooser & scale widgets
|
||||
[[../../../images/gtk/tut07-appchooser-scale.png]]
|
||||
|
||||
|
||||
This example program demonstrates the use of appchooser buttons when selecting an application from the drop down launch the application loading a test text file, this could be a video or mp3 or any file type.
|
||||
|
||||
you can change the application list by modify the content type value in glade this then shows all registered apps for that content type.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk, GLib, Gio
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 04 text input, spin input, drop down options"""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut07.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['scale1'] = xml.get_object('scalebutton1')
|
||||
self.widgets['scale2'] = xml.get_object('scalebutton2')
|
||||
self.widgets['appchooseraudio'] = xml.get_object('appchooserbutton1')
|
||||
self.widgets['appchoosertext'] = xml.get_object('appchooserbutton2')
|
||||
|
||||
self.widgets['appchooseraudio'].connect('changed', self.app_chooser)
|
||||
self.widgets['appchoosertext'].connect('changed', self.app_chooser)
|
||||
self.widgets['scale1'].connect('value-changed', self.scale)
|
||||
self.widgets['scale2'].connect('value-changed', self.scale)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def app_chooser(self, widget):
|
||||
list_view_model = widget.get_model()
|
||||
active_iter_index = widget.get_active()
|
||||
row_iter = list_view_model.get_iter(active_iter_index)
|
||||
app_info = list_view_model.get_value(row_iter, 0)
|
||||
|
||||
gio_file = Gio.File.new_for_path('/tmp/tut07-appchooser-test.txt')
|
||||
app_info.launch((gio_file,), None)
|
||||
self.text.set_text(widget.get_name() + ' ' + app_info.get_name())
|
||||
|
||||
def scale(self, widget, value):
|
||||
self.text.set_text(widget.get_name() + ' ' + str(value))
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#+TITLE: GTK-3 Calendars and menus
|
||||
#+DATE: 2014-03-08 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file displaying a menu bar and calendar widget.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 08-calendars-and-menus
|
||||
#+THUMBNAIL: ../../../images/gtk/tut08-menu-calendar.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Calendars and menus
|
||||
.. slug: 08-calendars-and-menus
|
||||
.. date: 2014-03-08 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying a menu bar and calendar widget.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Calendar & menus
|
||||
[[../../../images/gtk/tut08-menu-calendar.png]]
|
||||
|
||||
Simple GTK application demonstrating the use of the date picker and application menus. The menu is hooked upto to show and hide the calendar and to set the text entry field to say hello world. Double clicking a day in the calendar widget will show the date in the text entry field.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk, GLib, GObject
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 08 menu, calendar widget."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut08.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['calendar'] = xml.get_object('calendar1')
|
||||
self.widgets['menushow'] = xml.get_object('menuitem6')
|
||||
self.widgets['menuhide'] = xml.get_object('menuitem7')
|
||||
self.widgets['menuhello'] = xml.get_object('menuitem8')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
self.widgets['menushow'].connect('activate', self.showcalendar)
|
||||
self.widgets['menuhide'].connect('activate', self.hidecalendar)
|
||||
self.widgets['menuhello'].connect('activate', self.hello)
|
||||
self.widgets['calendar'].connect('day-selected', self.date_selected)
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def hidecalendar(self, *args):
|
||||
self.widgets['calendar'].hide()
|
||||
|
||||
def showcalendar(self, *args):
|
||||
self.widgets['calendar'].show()
|
||||
|
||||
def hello(self, *args):
|
||||
self.text.set_text('hello world')
|
||||
|
||||
def date_selected(self, *args):
|
||||
date = self.widgets['calendar'].get_date()
|
||||
self.text.set_text(str(date[2]) + '-'+str(date[1]) + '-' + str(date[0]))
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
|
@ -0,0 +1,141 @@
|
|||
#+TITLE: GTK-3 Treeview in liststore mode
|
||||
#+DATE: 2014-04-12 12:00:00 UTC
|
||||
#+DESCRIPTION: Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 09-treeview-liststore-mode
|
||||
#+THUMBNAIL: ../../../images/gtk/tut09-treeview-listview.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Treeview in liststore mode
|
||||
.. slug: 09-treeview-liststore-mode
|
||||
.. date: 2014-04-12 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Example of using a treeview, the treeview display can be adjusted using different store models in this case we use the liststore model.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Treeview in list mode
|
||||
[[../../../images/gtk/tut09-treeview-listview.png]]
|
||||
|
||||
The below example demonstrates using the treeview to display table style data, it generates a few rows and shows row selection from a mouse click.")
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk, GLib, GObject
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 09 treeview in list mode."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut09.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.ListStore(str, str, str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column1 = Gtk.TreeViewColumn("Column 01")
|
||||
self.treeview.append_column(column1)
|
||||
|
||||
column2 = Gtk.TreeViewColumn("Column 02")
|
||||
self.treeview.append_column(column2)
|
||||
|
||||
column3 = Gtk.TreeViewColumn("Column 03")
|
||||
self.treeview.append_column(column3)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column1.pack_start(cell, False)
|
||||
column1.add_attribute(cell, "text", 0)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column2.pack_start(cell, False)
|
||||
column2.add_attribute(cell, "text", 0)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column3.pack_start(cell, False)
|
||||
column3.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with some list items
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#+TITLE: GTK-3 Treeview in treestore mode
|
||||
#+DATE: 2014-04-14 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 10-treeview-treestore-mode
|
||||
#+THUMBNAIL: ../../../images/gtk/tut10-treeview-treestore.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Treeview in treestore mode
|
||||
.. slug: 10-treeview-treestore-mode
|
||||
.. date: 2014-04-14 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Treeview in tree mode
|
||||
[[../../../images/gtk/tut10-treeview-treestore.png]]
|
||||
|
||||
|
||||
The code below will populate the treeview widget with a tree like structure which you can expand and collapse, double clicking will retrieve the selected node in the tree.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 10 text input, display a treeview in expandable tree format."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut10.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with a largish tree
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
for item2 in range(0, 5):
|
||||
iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1)
|
||||
for item3 in range(0, 5):
|
||||
self.append_tree('Sub Sub Item ' + str(item3), iter_level_2)
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
235
bases/do-blog/resources/documents/python/gtk3/11-interactive-python-textview.org
Executable file
235
bases/do-blog/resources/documents/python/gtk3/11-interactive-python-textview.org
Executable file
|
@ -0,0 +1,235 @@
|
|||
#+TITLE: GTK-3 Textview interactions
|
||||
#+DATE: 2014-04-18 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file displaying a textview widget which runs python code on carriage return.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 11-interactive-python-textview
|
||||
#+THUMBNAIL: ../../../images/gtk/tut11-interactive-python-textview.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Textview interactions
|
||||
.. slug: 11-interactive-python-textview
|
||||
.. date: 2014-04-18 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying a textview widget which runs python code on carriage return.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Textview widget
|
||||
[[../../../images/gtk/tut11-interactive-python-textview.png]]
|
||||
|
||||
|
||||
This example shows using the text view widget to get text entered by a use, it makes use of marked text blocks so that only some text can be edited and will dynamically read the current line and run it in python interactively.
|
||||
|
||||
You can type app.test() or app.show_drawing(True) as examples of the interaction between the widget and python.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
from gi.repository import Gtk, Gdk
|
||||
import code
|
||||
import math
|
||||
|
||||
|
||||
class interactiveGtk:
|
||||
|
||||
def __init__(self):
|
||||
window = Gtk.Window()
|
||||
window.set_default_size(380, 300)
|
||||
window.connect("destroy", lambda w: Gtk.main_quit())
|
||||
|
||||
box = Gtk.VBox()
|
||||
|
||||
self.drawingarea = Gtk.DrawingArea()
|
||||
|
||||
#python console using a textview
|
||||
console = interactive_console(Gtk.TextView())
|
||||
self.drawingarea.connect("draw", self.area_expose_cb)
|
||||
|
||||
box.add(self.drawingarea)
|
||||
box.add(console.textarea)
|
||||
|
||||
window.add(box)
|
||||
window.show_all()
|
||||
|
||||
self.drawarea = False
|
||||
|
||||
def show_drawing(self, state):
|
||||
"""self.show_drawing(True) to enable showing the lines"""
|
||||
self.drawarea = state
|
||||
|
||||
def test(self):
|
||||
"""run app.test() when program is running to print this message"""
|
||||
print ('hello world')
|
||||
|
||||
def area_expose_cb(self, widget, context):
|
||||
"""expose event lets draw, lines will only display if we run self.show_lines first.
|
||||
demonstrating running state change of our program"""
|
||||
self.style = self.drawingarea.get_style()
|
||||
if self.drawarea is True:
|
||||
self.drawing(context, 210, 10)
|
||||
|
||||
def drawing(self, cr, x, y):
|
||||
""" draw a circle in the drawing area """
|
||||
cr.set_line_width(10)
|
||||
cr.set_source_rgb(0.5, 0.8, 0.0)
|
||||
|
||||
cr.translate(20 / 2, 20 / 2)
|
||||
cr.arc(50, 50, 50, 0, 2 * math.pi)
|
||||
cr.stroke_preserve()
|
||||
|
||||
cr.set_source_rgb(0.3, 0.4, 0.4)
|
||||
cr.fill()
|
||||
|
||||
|
||||
class interactive_console:
|
||||
editor_chars = '>>>'
|
||||
editor_chars_other = '...'
|
||||
editor_history = []
|
||||
editor_history_position = 0
|
||||
|
||||
def __init__(self, textview):
|
||||
#the python editor window
|
||||
self.textarea = textview
|
||||
self.textarea.connect('key-press-event', self.key_pressed)
|
||||
self.console_buffer = self.textarea.get_buffer()
|
||||
|
||||
#setup some characters which can not be changed
|
||||
self.console_buffer.set_text(self.editor_chars + 'app.show_drawing(True)')
|
||||
self.console_buffer.create_tag("uneditable", editable=False, editable_set=True)
|
||||
self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True)
|
||||
self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False)
|
||||
self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(len(self.editor_chars)))
|
||||
|
||||
#interactive mode interpreter,
|
||||
#pass locals() or globals() so we can access our programs functions and variables
|
||||
#using global here so we have access to the app object in the global scope
|
||||
self.interpreter = code.InteractiveInterpreter(globals())
|
||||
|
||||
def key_pressed(self, widget, event):
|
||||
"""
|
||||
grab key presses, run code from textview on return
|
||||
navigate history if arrow keys are pressed
|
||||
"""
|
||||
if event.keyval == Gdk.keyval_from_name('Return'):
|
||||
self.execute_line()
|
||||
return True
|
||||
|
||||
if event.keyval == Gdk.keyval_from_name('Up'):
|
||||
self.console_history(-1)
|
||||
return True
|
||||
|
||||
if event.keyval == Gdk.keyval_from_name('Down'):
|
||||
self.console_history(1)
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute_line(self):
|
||||
"""
|
||||
carriage return was captured so lets process the textview contents for code to run
|
||||
"""
|
||||
|
||||
text = self.console_buffer.get_text(self.console_buffer.get_start_iter(), self.console_buffer.get_end_iter(), False)
|
||||
source = ''
|
||||
block = False
|
||||
indent = 0
|
||||
#work out code to run if its not a block or a blank line then run what we have,
|
||||
#if its a block of code like a for loop or if condition dont run it yet unless the block has finished.
|
||||
last_line = ''
|
||||
for line in text.split("\n"):
|
||||
line = line[3:].strip('')
|
||||
if line.strip() == '':
|
||||
block = False
|
||||
indent = 0
|
||||
else:
|
||||
if line.endswith(':'):
|
||||
if block is True:
|
||||
source += line + "\n\t"
|
||||
else:
|
||||
source = line + "\n\t"
|
||||
block = True
|
||||
indent += 1
|
||||
else:
|
||||
if line.startswith("\t"):
|
||||
source += line + "\n"
|
||||
else:
|
||||
block = False
|
||||
source = line + "\n"
|
||||
indent = 0
|
||||
last_line = line
|
||||
if last_line.strip() != '':
|
||||
self.append_history(last_line)
|
||||
chars = self.editor_chars
|
||||
|
||||
|
||||
# run the code grabbed from the text buffer and execute it if its not a block
|
||||
results = ''
|
||||
if block is True:
|
||||
chars = self.editor_chars_other
|
||||
else:
|
||||
chars = self.editor_chars
|
||||
results = self.execute_code(source)
|
||||
|
||||
#build text for the editor, and workout which part of the text should be locked
|
||||
text_output = text + '\n' + results + chars
|
||||
text_output_length = len(text_output)
|
||||
if block is True:
|
||||
text_output += "\t" * indent
|
||||
self.update_editor(text_output, text_output_length)
|
||||
|
||||
def execute_code(self, source):
|
||||
"""
|
||||
run any code sent here and capture output and return the results
|
||||
"""
|
||||
# capture output from stdio so we can display the errors in our editor
|
||||
result = StringIO()
|
||||
sys.stdout = result
|
||||
sys.stderr = result
|
||||
#run code output will be put into result because we are capturing stdout
|
||||
self.interpreter.runsource(source, "<<console>>")
|
||||
# restore stdout so future output is capture to the terminal again
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stdout = sys.__stderr__
|
||||
return result.getvalue()
|
||||
|
||||
def update_editor(self, text, uneditable_end=0):
|
||||
"""
|
||||
pass in text to put in the editor and portion to be locked from editing
|
||||
"""
|
||||
self.console_buffer.set_text(text)
|
||||
self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True)
|
||||
self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False)
|
||||
self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(uneditable_end))
|
||||
|
||||
def append_history(self, line):
|
||||
"""
|
||||
Store command in history drop command if limit has been reached
|
||||
"""
|
||||
if len(self.editor_history) > 10:
|
||||
self.editor_history.pop()
|
||||
self.editor_history.append(line)
|
||||
self.editor_history_position = len(self.editor_history)
|
||||
|
||||
def console_history(self, direction=-1):
|
||||
"""
|
||||
drop any text on last line and insert text from history based on position
|
||||
"""
|
||||
# get current text excluding last line as we will update from our history
|
||||
text = self.console_buffer.get_text(
|
||||
self.console_buffer.get_start_iter(),
|
||||
self.console_buffer.get_iter_at_line_index(
|
||||
self.console_buffer.get_line_count(), 0),
|
||||
False)
|
||||
|
||||
#work out position in history and what should be displayed
|
||||
linenumber = self.editor_history_position + direction
|
||||
if linenumber >= 0 and linenumber < len(self.editor_history):
|
||||
self.editor_history_position += direction
|
||||
self.console_buffer.set_text(text + self.editor_chars + self.editor_history[self.editor_history_position])
|
||||
|
||||
|
||||
app = interactiveGtk()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#+TITLE: GTK-3 OpenGL inside a drawing area
|
||||
#+DATE: 2014-04-20 12:00:00 UTC
|
||||
#+DESCRIPTION: Load a glade file displaying a drawing area widget and setup a context for opengl.
|
||||
#+FILETAGS: GTK-3:python:widgets:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 12-dispaying-opengl-inside-drawing-area
|
||||
#+THUMBNAIL: ../../../images/gtk/tut12-mixing-opengl-with-gtk.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 OpenGL inside a drawing area
|
||||
.. slug: 12-dispaying-opengl-inside-drawing-area
|
||||
.. date: 2014-04-20 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, opengl
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying a drawing area widget and setup a context for opengl.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Textview widget
|
||||
[[../../../images/gtk/tut12-mixing-opengl-with-gtk.png]]
|
||||
|
||||
Example of rendering opengl inside a gtk drawing area. I have seperated out the opengl code from the main gtk widget this makes it simpler to use in your own applications.
|
||||
|
||||
|
||||
The example below will draw a basic opengl triangle so you know everything is setup and working, it also attaches to the drawing area resize events so you can resize the window.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
from OpenGL import GLX
|
||||
from OpenGL.raw._GLX import struct__XDisplay
|
||||
from OpenGL import GL
|
||||
from ctypes import *
|
||||
|
||||
import Xlib
|
||||
from Xlib.display import Display
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
|
||||
|
||||
class gtkgl:
|
||||
""" these method do not seem to exist in python x11 library lets exploit the c methods """
|
||||
xlib = cdll.LoadLibrary('libX11.so')
|
||||
xlib.XOpenDisplay.argtypes = [c_char_p]
|
||||
xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
|
||||
xdisplay = xlib.XOpenDisplay(None)
|
||||
display = Xlib.display.Display()
|
||||
attrs = []
|
||||
|
||||
xwindow_id = None
|
||||
width = height = 200
|
||||
|
||||
def __init__(self):
|
||||
""" lets setup are opengl settings and create the context for our window """
|
||||
self.add_attribute(GLX.GLX_RGBA, True)
|
||||
self.add_attribute(GLX.GLX_RED_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_GREEN_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_BLUE_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0)
|
||||
|
||||
xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
|
||||
configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int()))
|
||||
self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)
|
||||
|
||||
def add_attribute(self, setting, value):
|
||||
"""just to nicely add opengl parameters"""
|
||||
self.attrs.append(setting)
|
||||
self.attrs.append(value)
|
||||
|
||||
def get_attributes(self):
|
||||
""" return our parameters in the expected structure"""
|
||||
attrs = self.attrs + [0, 0]
|
||||
return (c_int * len(attrs))(*attrs)
|
||||
|
||||
def configure(self, wid):
|
||||
""" """
|
||||
self.xwindow_id = GdkX11.X11Window.get_xid(wid)
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('failed configuring context')
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
|
||||
def draw_start(self):
|
||||
"""make cairo context current for drawing"""
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ("failed to get the context for drawing")
|
||||
|
||||
def draw_finish(self):
|
||||
"""swap buffer when we have finished drawing"""
|
||||
GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)
|
||||
|
||||
def test(self):
|
||||
"""Test method to draw something so we can make sure opengl is working and we can see something"""
|
||||
self.draw_start()
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glBegin(GL_TRIANGLES)
|
||||
glIndexi(0)
|
||||
glColor3f(1.0, 0.0, 0.0)
|
||||
glVertex2i(0, 1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 1.0, 0.0)
|
||||
glVertex2i(-1, -1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 0.0, 1.0)
|
||||
glVertex2i(1, -1)
|
||||
glEnd()
|
||||
|
||||
self.draw_finish()
|
||||
|
||||
|
||||
class gui():
|
||||
glwrap = gtkgl()
|
||||
|
||||
def __init__(self):
|
||||
""" create a gtk window, attach a drawing area and connect the relevant events"""
|
||||
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(self.glwrap.width, self.glwrap.height)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with opengl")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.connect('configure_event', self.on_configure_event)
|
||||
self.drawing_area.connect('draw', self.on_draw)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
"""if we recieve a configure event for example a resize, then grab the context settings and resize our scene """
|
||||
self.glwrap.configure(widget.get_window())
|
||||
self.glwrap.test()
|
||||
return True
|
||||
|
||||
def on_draw(self, widget, context):
|
||||
"""if we recieve a draw event redraw our test triangle"""
|
||||
self.glwrap.test()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
#+END_SRC
|
191
bases/do-blog/resources/documents/python/gtk3/13-example-list-box-downloader.org
Executable file
191
bases/do-blog/resources/documents/python/gtk3/13-example-list-box-downloader.org
Executable file
|
@ -0,0 +1,191 @@
|
|||
#+TITLE: GTK-3 Example downloader using a listbox
|
||||
#+DATE: 2014-05-01 12:00:00 UTC
|
||||
#+DESCRIPTION: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer.
|
||||
#+FILETAGS: GTK-3:python:widgets:application:requests
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 13-example-list-box-downloader
|
||||
#+THUMBNAIL: ../../../images/gtk/tut13-listbox.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Example downloader using a listbox
|
||||
.. slug: 13-example-list-box-downloader
|
||||
.. date: 2014-05-01 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, application, requests
|
||||
.. category: python
|
||||
.. description: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: File downloader
|
||||
[[../../../images/gtk/tut13-listbox.png]]
|
||||
|
||||
|
||||
A slightly more complex example, loading our gui from a glade file and dynamically add and remove widgets based on an xml file.
|
||||
|
||||
This is the start of a simple file downloader, it reads an xml file and creates a gui dynamically with download buttons for each element in the xml data to retrieve files.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import requests
|
||||
from io import StringIO, BytesIO
|
||||
import subprocess
|
||||
from lxml.html import parse
|
||||
from gi.repository import Gtk, GLib, Gdk, GdkPixbuf
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 13 custom treeview list boxes"""
|
||||
count = 0
|
||||
|
||||
retrieve_job = None
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut13.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('winFetcher')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['listbox'] = xml.get_object('listbox1')
|
||||
self.widgets['progress'] = xml.get_object('listProgress')
|
||||
self.widgets['refresh'] = xml.get_object('btnRefresh')
|
||||
self.widgets['refresh'].connect('button_press_event', self.refresh)
|
||||
self.widgets['close'] = xml.get_object('btnClose')
|
||||
self.widgets['close'].connect('button_press_event', self.closeFetcher)
|
||||
|
||||
#wrap the listbox so we can reuse the code, pass in the listbox widget to our wrapper class
|
||||
self.listbox = ListBoxSelect(self.widgets['listbox'])
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.openFetcher()
|
||||
self.refresh()
|
||||
|
||||
def openFetcher(self):
|
||||
self.window.show_all()
|
||||
|
||||
def refresh(self, *args):
|
||||
""" get a new xml and start the progress bar"""
|
||||
self.listbox.clear()
|
||||
self.widgets['progress'].show()
|
||||
self.retrieve_job = subprocess.Popen(
|
||||
['curl', 'file://%s/example.xml' % os.path.abspath('./')],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar)
|
||||
|
||||
def update_active_progress_bar(self):
|
||||
""" move the progress bar, when the subprocess returns handle the xml and hide the progress bar"""
|
||||
self.widgets['progress'].pulse()
|
||||
if self.retrieve_job.poll():
|
||||
return True
|
||||
self.widgets['progress'].hide()
|
||||
self.update_list()
|
||||
return False
|
||||
|
||||
def update_list(self):
|
||||
""" parse the xmland grab the elements we are intrested in"""
|
||||
nsmap = {'media': 'http://search.yahoo.com/mrss/'}
|
||||
results = BytesIO(self.retrieve_job.communicate()[0])
|
||||
doc = parse(results).getroot()
|
||||
for item in doc.iterfind(".//item", namespaces=nsmap):
|
||||
title = item.find('title').text
|
||||
link = item.find('link').tail
|
||||
description = item.find('description').text
|
||||
image = item.find('thumbnail', namespaces=nsmap).get('url')
|
||||
self.listbox.model_append(image, title, description, link)
|
||||
|
||||
def download(self):
|
||||
""" retrieve the xml file in a subprocess using curl """
|
||||
self.retrieve_job = subprocess.Popen(
|
||||
['curl', 'file://%s/example.xml' % os.path.abspath('./')],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
def closeFetcher(self, widget):
|
||||
self.window.hide()
|
||||
|
||||
class ListBoxSelect:
|
||||
""" handle the listbox rows dynamically add and remove widgets, and handle download. """
|
||||
listbox = None
|
||||
gui_rows = [] # store widgets here so we can destroy them later.
|
||||
|
||||
def __init__(self, listbox):
|
||||
""" pass in list box to manage and connect event"""
|
||||
self.listbox = listbox
|
||||
self.listbox.connect('row-activated', self.selection)
|
||||
|
||||
def selection(self, lbox, lbrow):
|
||||
""" row selected we may want to react here"""
|
||||
boxrow = lbrow.get_children()[0]
|
||||
boxinfo = boxrow.get_children()[1]
|
||||
print(boxinfo.get_children()[1].get_text())
|
||||
|
||||
def model_append(self, image, title, description, link):
|
||||
""" create new widgets, and connect events for our new row"""
|
||||
items = {}
|
||||
items['row'] = Gtk.ListBoxRow()
|
||||
items['vbox'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
items['label1'] = Gtk.Label(title, xalign=0)
|
||||
items['label2'] = Gtk.Label(link, xalign=0)
|
||||
items['progress'] = Gtk.ProgressBar()
|
||||
items['progress'].hide()
|
||||
items['progress'].set_fraction(0)
|
||||
items['vbox'].pack_start(items['label1'], True, False, 0)
|
||||
items['vbox'].pack_start(items['label2'], True, False, 0)
|
||||
items['vbox'].pack_start(items['progress'], False, False, 0)
|
||||
|
||||
items['hbox'] = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
items['image'] = Gtk.Image.new_from_file(image)
|
||||
items['button'] = Gtk.Button(label="Download")
|
||||
items['button'].connect('button_press_event', self.download, items, link)
|
||||
|
||||
items['hbox'].pack_start(items['image'], False, False, 0)
|
||||
items['hbox'].pack_start(items['vbox'], True, True, 0)
|
||||
items['button_box'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
items['button_box'].pack_end(items['button'], False, False, 0)
|
||||
items['hbox'].pack_start(items['button_box'], False, False, 0)
|
||||
items['row'].add(items['hbox'])
|
||||
|
||||
self.listbox.add(items['row'])
|
||||
items['row'].show_all()
|
||||
|
||||
self.gui_rows.append(items)
|
||||
|
||||
def download(self, widget, args, items, link):
|
||||
""" download button click, change widgets and start the progress bar and download """
|
||||
items['button'].hide()
|
||||
items['job'] = subprocess.Popen(
|
||||
['curl', '-O', link],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar, items)
|
||||
|
||||
def update_active_progress_bar(self, widgets):
|
||||
""" update progress bar until command finished """
|
||||
widgets['progress'].pulse()
|
||||
if widgets['job'].poll():
|
||||
return True
|
||||
widgets['progress'].hide()
|
||||
return False
|
||||
|
||||
def clear(self):
|
||||
""" remove all rows so we can pre-populate"""
|
||||
for item in self.gui_rows:
|
||||
item['row'].destroy()
|
||||
self.gui_rows = []
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
|
@ -0,0 +1,86 @@
|
|||
#+TITLE: GTK-3 Touchscreen events
|
||||
#+DATE: 2014-05-20 12:00:00 UTC
|
||||
#+DESCRIPTION: Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps.
|
||||
#+FILETAGS: GTK-3:python:widgets:touchscreen
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 14-gtk-touchscreen-input
|
||||
#+THUMBNAIL: ../../../images/gtk/tut14-touchscreen-events.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Touchscreen events
|
||||
.. slug: 14-gtk-touchscreen-input
|
||||
.. date: 2014-05-20 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, touchscreen
|
||||
.. category: python
|
||||
.. description: Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: File downloader
|
||||
[[../../../images/gtk/tut14-touchscreen-events.png]]
|
||||
|
||||
The example below demonstrates filtering events for a specific device, in this case a touch screen we then capture touch begin update and end events.
|
||||
|
||||
To my knowledge gestures are not supports so you will need to count the begins to determine the number of fingers and handle the updates to determine whats happening.
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
|
||||
|
||||
class gui():
|
||||
|
||||
def __init__(self):
|
||||
self.touch_count = 0
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 touch events")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
#add the type of events we are interested in retrieving, skip this step and your events will never fire
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
self.drawing_area.connect('button_press_event', self.touched)
|
||||
self.drawing_area.connect('touch-event', self.touched)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def touched(self, widget, ev):
|
||||
# detect the type of device we can filter events using this
|
||||
# if we dont a touch event and mouse event can be triggered from one touch for example
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHPAD:
|
||||
print('touchpad device')
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.MOUSE:
|
||||
print('mouse device')
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN:
|
||||
print('touchscreen device')
|
||||
|
||||
#from what i can tell there is no support for gestures so you would need another library
|
||||
#or to handle this yourself
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_BEGIN:
|
||||
self.touch_count += 1
|
||||
print('start %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_UPDATE:
|
||||
print('UPDATE %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_END:
|
||||
self.touch_count -= 1
|
||||
print('end %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_CANCEL:
|
||||
self.touch_count -= 1
|
||||
print('cancelled')
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
#+END_SRC
|
||||
|
|
@ -0,0 +1,595 @@
|
|||
#+TITLE: GTK-3 Simple opengl app with touch events
|
||||
#+DATE: 2014-06-10 12:00:00 UTC
|
||||
#+DESCRIPTION: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment.
|
||||
#+FILETAGS: GTK-3:python:widgets:touchscreen:opengl
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 15-gtk3-opengl-touch-events
|
||||
#+THUMBNAIL: ../../../images/gtk/tut14-touchscreen-events.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Simple opengl app with touch events
|
||||
.. slug: 15-gtk3-opengl-touch-events
|
||||
.. date: 2014-06-10 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, touchscreen, opengl
|
||||
.. category: python
|
||||
.. description: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Opengl touch events
|
||||
[[../../../images/gtk/tut14-touchscreen-events.png]]
|
||||
|
||||
|
||||
|
||||
This is a much more complicated example which mashes some of the previous examples together, I have written it as an example but also to test out shaders and different pipelines to get better opengl performance.
|
||||
|
||||
Hopefully this example will help you get started writing awesome touch based apps with gtk, if anything is unclear let me know in the comments below.
|
||||
|
||||
[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][OpenGL in a drawing area]]
|
||||
|
||||
[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][Touch screen events]]
|
||||
|
||||
|
||||
#+BEGIN_SRC python :tangle 15-gtk3-opengl-touch-events.py
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
import pprint
|
||||
|
||||
import Xlib
|
||||
from Xlib.display import Display
|
||||
from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject
|
||||
from ctypes import *
|
||||
from numpy import array
|
||||
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL import shaders
|
||||
from OpenGL.GLU import gluPerspective, gluLookAt
|
||||
from OpenGL.arrays import vbo
|
||||
from OpenGL import GLX
|
||||
try:
|
||||
from OpenGL.GLX import struct__XDisplay
|
||||
except ImportError as err:
|
||||
from OpenGL.raw._GLX import struct__XDisplay
|
||||
from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
|
||||
from OpenGL.GL import shaders, glGetUniformLocation
|
||||
|
||||
from helper import shader
|
||||
from helper import cube as createcube
|
||||
|
||||
class gtkgl:
|
||||
""" these method do not seem to exist in python x11 library lets exploit the c methods
|
||||
useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib"""
|
||||
xlib = cdll.LoadLibrary('libX11.so')
|
||||
xlib.XOpenDisplay.argtypes = [c_char_p]
|
||||
xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
|
||||
xdisplay = xlib.XOpenDisplay(None)
|
||||
display = Xlib.display.Display()
|
||||
attrs = []
|
||||
|
||||
xwindow_id = None
|
||||
width = height = 500
|
||||
|
||||
def __init__(self):
|
||||
""" lets setup are opengl settings and create the context for our window """
|
||||
self.add_attribute(GLX.GLX_RGBA, True)
|
||||
self.add_attribute(GLX.GLX_RED_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_GREEN_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_BLUE_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1)
|
||||
self.add_attribute(GLX.GLX_DEPTH_SIZE, 24)
|
||||
|
||||
xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
|
||||
print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid))
|
||||
self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)
|
||||
|
||||
def add_attribute(self, setting, value):
|
||||
"""just to nicely add opengl parameters"""
|
||||
self.attrs.append(setting)
|
||||
self.attrs.append(value)
|
||||
|
||||
def get_attributes(self):
|
||||
""" return our parameters in the expected structure"""
|
||||
attrs = self.attrs + [0, 0]
|
||||
return (c_int * len(attrs))(*attrs)
|
||||
|
||||
def configure(self, wid):
|
||||
""" """
|
||||
self.xwindow_id = GdkX11.X11Window.get_xid(wid)
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('failed configuring context')
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
|
||||
def draw_start(self):
|
||||
"""make cairo context current for drawing"""
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ("failed to get the context for drawing")
|
||||
|
||||
def draw_finish(self):
|
||||
"""swap buffer when we have finished drawing"""
|
||||
GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)
|
||||
|
||||
class scene:
|
||||
width, height = 600, 600
|
||||
|
||||
rotationx = 0.0
|
||||
rotationy = 0.0
|
||||
|
||||
rotation_incx = 0.5
|
||||
rotation_incy = 0.5
|
||||
radius = 0
|
||||
|
||||
touch_count = 0
|
||||
touch_previous = 0, 0
|
||||
touch_start_one = 0, 0
|
||||
touch_start_two = 0, 0
|
||||
touch_end_one = 0, 0
|
||||
touch_end_two = 0, 0
|
||||
touch_time = 0
|
||||
|
||||
camera_distance = 25
|
||||
|
||||
cube_length = 1.0
|
||||
cube_size = cube_length / 2
|
||||
|
||||
def __init__(self):
|
||||
"""setup everything in the correct order"""
|
||||
self.glwrap = gtkgl()
|
||||
self.setup_opengl()
|
||||
self.generate()
|
||||
self.gui()
|
||||
|
||||
self.mode = 'vbo all cubes'
|
||||
|
||||
def gui(self):
|
||||
"""load in the gui and connect the events and set our properties"""
|
||||
self.start_time = time.time()
|
||||
self.frame = 1
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('gui.glade')
|
||||
|
||||
self.window = xml.get_object('window1')
|
||||
|
||||
self.mode_widget = xml.get_object('cmbmode')
|
||||
self.mode_widget.connect('changed', self.change_mode)
|
||||
|
||||
self.rotate_widget = xml.get_object('spinrotate')
|
||||
self.rotate_widget.connect('value-changed', self.change_rotate_speed)
|
||||
|
||||
self.radius_widget = xml.get_object('spinradius')
|
||||
self.radius_widget.connect('value-changed', self.change_radius)
|
||||
|
||||
self.color_widget = xml.get_object('btncolor')
|
||||
self.color_widget.connect('clicked', self.change_color)
|
||||
|
||||
self.canvas_widget = xml.get_object('canvas')
|
||||
self.canvas_widget.connect('configure_event', self.on_configure_event)
|
||||
self.canvas_widget.connect('draw', self.on_draw)
|
||||
self.canvas_widget.set_double_buffered(False)
|
||||
self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height)
|
||||
self.canvas_widget.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.canvas_widget.connect('touch-event', self.touched)
|
||||
|
||||
self.window.show_all()
|
||||
GObject.idle_add(self.loop_draw)
|
||||
|
||||
def touched(self, widget, ev):
|
||||
"""basic touch support, count the touches so we no how many fingers
|
||||
basic pinc zoom along the x, single finger slide to rotate"""
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN:
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_BEGIN:
|
||||
self.touch_start = ev.touch.x, ev.touch.y
|
||||
self.touch_count += 1
|
||||
if self.touch_count == 2:
|
||||
self.touch_start_two = ev.touch.x, ev.touch.y
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_UPDATE:
|
||||
if ev.touch.time - self.touch_time < 100:
|
||||
return True
|
||||
|
||||
if self.touch_count == 2:
|
||||
#basic pinch zoom along the x axis
|
||||
d1 = self.touch_previous[0] - ev.touch.x
|
||||
if d1 > 1:
|
||||
self.camera_distance += self.camera_distance * 0.05
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
if d1 < 1:
|
||||
self.camera_distance -= self.camera_distance * 0.05
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
self.update_camera()
|
||||
self.touch_time = ev.touch.time
|
||||
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_END:
|
||||
self.touch_end = ev.touch.x, ev.touch.y
|
||||
#set rotation when touch ends
|
||||
if self.touch_count == 1:
|
||||
self.rotation_incx = (self.touch_start[0] - self.touch_end[0]) * 0.01
|
||||
self.rotation_incy = (self.touch_start[1] - self.touch_end[1]) * 0.01
|
||||
self.touch_count = 0
|
||||
|
||||
def in_circle(self, center_x, center_y, center_z, radius, x, y, z):
|
||||
""" test if our cordinate lies inside our sphere"""
|
||||
square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 + (center_z - z) ** 2
|
||||
return square_dist <= radius ** 2
|
||||
|
||||
def change_color(self, widget):
|
||||
#regenerate the scene
|
||||
self.generate()
|
||||
|
||||
def change_mode(self, widget):
|
||||
#change whats drawn and how
|
||||
self.mode = widget.get_active_text().lower()
|
||||
print(widget.get_active_text())
|
||||
|
||||
def change_rotate_speed(self, widget):
|
||||
#handle spinner rotation speed event
|
||||
self.rotation_incx = widget.get_value()
|
||||
self.rotation_incy = widget.get_value()
|
||||
|
||||
def change_radius(self, widget):
|
||||
#increase size of circle and number of polygons
|
||||
self.radius = int(widget.get_value())
|
||||
self.generate()
|
||||
|
||||
def loop_draw(self):
|
||||
#send redraw event to drawing area widget
|
||||
self.canvas_widget.queue_draw()
|
||||
return True
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
"""if we recieve a configure event for example a resize, then grab the context settings and resize our scene """
|
||||
self.glwrap.width = widget.get_allocation().width
|
||||
self.glwrap.height = widget.get_allocation().height
|
||||
self.width, self.height = self.glwrap.width, self.glwrap.height
|
||||
|
||||
#update our states because we have reconfigured the display
|
||||
self.glwrap.configure(widget.get_window())
|
||||
self.glwrap.draw_start()
|
||||
self.update_camera()
|
||||
self.setup_shaders()
|
||||
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glDepthMask(GL_TRUE)
|
||||
glDepthFunc(GL_LEQUAL)
|
||||
glDepthRange(0.0, 1.0)
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_BACK)
|
||||
glFrontFace(GL_CW)
|
||||
|
||||
self.glwrap.draw_finish()
|
||||
return True
|
||||
|
||||
def on_draw(self, widget, context):
|
||||
"""if we recieve a draw event redraw our opengl scene"""
|
||||
self.elapsed_time = time.time() - self.start_time
|
||||
self.frame += 1
|
||||
|
||||
if self.elapsed_time > 1:
|
||||
print('fps %d' % self.frame)
|
||||
self.start_time = time.time()
|
||||
self.frame = 1
|
||||
self.glwrap.draw_start()
|
||||
self.draw()
|
||||
self.glwrap.draw_finish()
|
||||
|
||||
def generate(self):
|
||||
self.cubes = []
|
||||
#position cubes inside a sphere radius
|
||||
for shift_x in range(-self.radius, self.radius + 1):
|
||||
for shift_y in range(-self.radius, self.radius + 1):
|
||||
for shift_z in range(-self.radius, self.radius + 1):
|
||||
x = shift_x * self.cube_length
|
||||
y = shift_y * self.cube_length
|
||||
z = shift_z * self.cube_length
|
||||
if not self.in_circle(0, 0, 0, self.radius, x, y, z):
|
||||
continue
|
||||
#random colours / textures if we want
|
||||
color = random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])
|
||||
self.cubes.append(createcube((x, y, z), color, self.cube_size))
|
||||
|
||||
self.test_cube = createcube((x, y, z), (random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])), 6)
|
||||
faces = []
|
||||
for cube in self.cubes:
|
||||
faces += cube.get_data()
|
||||
|
||||
print('Generated %s Cubes' % str(len(self.cubes)))
|
||||
print('Generated %s Tringles' % str(len(faces) / 3))
|
||||
self.vbuffer = vbo.VBO(array(faces, 'f'))
|
||||
|
||||
def setup_shaders(self):
|
||||
self.shader_program = shader()
|
||||
self.shader_program.compile()
|
||||
|
||||
def setup_opengl(self):
|
||||
glShadeModel(GL_SMOOTH)
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClearDepth(1.0)
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
|
||||
glPolygonMode(GL_FRONT, GL_FILL)
|
||||
|
||||
def update_camera(self):
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0)
|
||||
gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance, # location
|
||||
0.0, 0.0, 0.0, # lookat
|
||||
0.0, 1.0, 0.0) # up direction
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glLoadIdentity()
|
||||
|
||||
def draw_test(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
#use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
#render the triangles into a virtual buffer object
|
||||
self.test_cube.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, self.test_cube.vbuffer_size)
|
||||
self.test_cube.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw_vbo_per_cube(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
# use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
# render the triangles into a virtual buffer object
|
||||
for shape in self.cubes:
|
||||
shape.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, shape.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, shape.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, shape.vbuffer_size)
|
||||
shape.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw_vbo_all_cubes(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
# use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
# render the triangles into a virtual buffer object
|
||||
self.vbuffer.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, self.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, self.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, len(self.vbuffer))
|
||||
self.vbuffer.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw(self):
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glClearDepth(1.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
if self.mode == 'vbo test cube':
|
||||
self.draw_test()
|
||||
if self.mode == 'vbo per cube':
|
||||
self.draw_vbo_per_cube()
|
||||
if self.mode == 'vbo all cubes':
|
||||
self.draw_vbo_all_cubes()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
glexample = scene()
|
||||
GLib.threads_init()
|
||||
Gdk.threads_init()
|
||||
Gdk.threads_enter()
|
||||
Gtk.main()
|
||||
Gdk.threads_leave()
|
||||
#+END_SRC
|
||||
|
||||
The file below is a simple helper to reduce the size of the files, it contains the shader and a point and cube class used in the main program above.
|
||||
|
||||
#+BEGIN_SRC python :tangle helper.py
|
||||
import numpy
|
||||
from numpy import array
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL import shaders
|
||||
from OpenGL.arrays import vbo
|
||||
|
||||
from OpenGL._bytes import bytes, _NULL_8_BYTE
|
||||
|
||||
def compileShader( source, shaderType ):
|
||||
|
||||
"""Compile shader source of given type
|
||||
source -- GLSL source-code for the shader
|
||||
shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc,
|
||||
returns GLuint compiled shader reference
|
||||
raises RuntimeError when a compilation failure occurs
|
||||
"""
|
||||
if isinstance(source, str):
|
||||
|
||||
source = [source]
|
||||
elif isinstance(source, bytes):
|
||||
|
||||
source = [source.decode('utf-8')]
|
||||
|
||||
shader = glCreateShader(shaderType)
|
||||
glShaderSource(shader, source)
|
||||
glCompileShader(shader)
|
||||
result = glGetShaderiv(shader, GL_COMPILE_STATUS)
|
||||
|
||||
if not(result):
|
||||
# TODO: this will be wrong if the user has
|
||||
# disabled traditional unpacking array support.
|
||||
raise RuntimeError(
|
||||
"""Shader compile failure (%s): %s"""%(
|
||||
result,
|
||||
glGetShaderInfoLog( shader ),
|
||||
),
|
||||
source,
|
||||
shaderType,
|
||||
)
|
||||
return shader
|
||||
|
||||
|
||||
class shader:
|
||||
vertex = """#version 120
|
||||
//attributes in values
|
||||
attribute vec3 inPosition;
|
||||
attribute vec3 inColor;
|
||||
|
||||
//varying sending to fragment shader
|
||||
varying vec4 outColor;
|
||||
void main(){
|
||||
vec4 fragmentPos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = (gl_ProjectionMatrix * fragmentPos);
|
||||
outColor = vec4(gl_Color.rgb, 1);
|
||||
}"""
|
||||
|
||||
fragment = """#version 120
|
||||
varying vec4 outColor;
|
||||
void main(){
|
||||
gl_FragColor = outColor;
|
||||
}"""
|
||||
|
||||
program = None
|
||||
def compile(self):
|
||||
self.program = shaders.compileProgram(
|
||||
compileShader(self.vertex, GL_VERTEX_SHADER),
|
||||
compileShader(self.fragment, GL_FRAGMENT_SHADER),)
|
||||
|
||||
class point:
|
||||
__slots__ = ['x', 'y', 'z', 'xyz', 'vertex']
|
||||
|
||||
def __init__(self, p, c=(1, 0, 0)):
|
||||
""" Position in 3d space as a tuple or list, and colour in tuple or list format"""
|
||||
self.x, self.y, self.z = p
|
||||
self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f')
|
||||
|
||||
|
||||
class cube:
|
||||
def __init__(self, p1, color, size=0.5):
|
||||
self.color = array([1, 0, 0], 'f')
|
||||
#self.xyz = p1
|
||||
self.points = (
|
||||
point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
|
||||
|
||||
point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
|
||||
|
||||
)
|
||||
|
||||
self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
|
||||
self.vbuffer_size = len(self.get_data())
|
||||
|
||||
def bind(self):
|
||||
self.vbuffer.bind()
|
||||
|
||||
def unbind(self):
|
||||
self.vbuffer.unbind()
|
||||
|
||||
def get_data(self):
|
||||
return (
|
||||
self.points[0].vertex, self.points[2].vertex, self.points[1].vertex,
|
||||
self.points[0].vertex, self.points[3].vertex, self.points[2].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[1].vertex, self.points[5].vertex,
|
||||
self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[7].vertex, self.points[3].vertex,
|
||||
self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[2].vertex, self.points[3].vertex,
|
||||
self.points[6].vertex, self.points[3].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
|
||||
self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
|
||||
self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
|
||||
)
|
||||
|
||||
|
||||
|
||||
class cube:
|
||||
def __init__(self, p1, color, size=0.5):
|
||||
self.color = array([1, 0, 0], 'f')
|
||||
#self.xyz = p1
|
||||
self.points = (
|
||||
point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
|
||||
|
||||
point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
|
||||
|
||||
)
|
||||
|
||||
self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
|
||||
self.vbuffer_size = len(self.get_data())
|
||||
|
||||
def bind(self):
|
||||
self.vbuffer.bind()
|
||||
|
||||
def unbind(self):
|
||||
self.vbuffer.unbind()
|
||||
|
||||
def get_data(self):
|
||||
return (
|
||||
self.points[0].vertex, self.points[2].vertex, self.points[1].vertex,
|
||||
self.points[0].vertex, self.points[3].vertex, self.points[2].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[1].vertex, self.points[5].vertex,
|
||||
self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[7].vertex, self.points[3].vertex,
|
||||
self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[2].vertex, self.points[3].vertex,
|
||||
self.points[6].vertex, self.points[3].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
|
||||
self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
|
||||
self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
|
||||
)
|
||||
|
||||
#+END_SRC
|
||||
|
209
bases/do-blog/resources/documents/python/gtk3/16-gtk3-custom-signals-example.org
Executable file
209
bases/do-blog/resources/documents/python/gtk3/16-gtk3-custom-signals-example.org
Executable file
|
@ -0,0 +1,209 @@
|
|||
#+TITLE: GTK-3 custom signals example
|
||||
#+DATE: 2014-09-11 12:00:00 UTC
|
||||
#+DESCRIPTION: Simple example on how to setup, connect to and trigger signals.
|
||||
#+FILETAGS: GTK-3:python:widgets:signals
|
||||
#+CATEGORY: python
|
||||
#+SLUG: 16-gtk3-custom-signals-example
|
||||
#+THUMBNAIL: ../../../images/gtk/tut16-signals.png
|
||||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 custom signals example
|
||||
.. slug: 16-gtk3-custom-signals-example
|
||||
.. date: 2014-09-11 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, signals
|
||||
.. category: python
|
||||
.. description: Simple example on how to setup, connect to and trigger signals.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Opengl touch events
|
||||
[[../../../images/gtk/tut16-signals.png]]
|
||||
|
||||
|
||||
In this program we create 4 custom signals, the first to are connected to and triggered on launch, the latter two are triggered when you click the buttons.
|
||||
|
||||
We can create new signals using GObject.signal_new, we can then connect the new signal and callback as we normally would using widget .connect methods as normal.
|
||||
To trigger our custom event we use the .emit() method, this method also aalows us to fake events, for example we might want to fake the user clicking a button.
|
||||
|
||||
|
||||
[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/][Gtk signals]]
|
||||
|
||||
|
||||
View python code
|
||||
#+BEGIN_SRC python :tangle 16-gtk3-custom-signals-example.py
|
||||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 02 buttons"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut16-signals.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
GObject.signal_new('custom-signal1', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new('custom-signal2', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
|
||||
self.window.connect('custom-signal1', self.custom_signal1_method)
|
||||
self.window.connect('custom-signal2', self.custom_signal2_method)
|
||||
|
||||
#emit the custom signals on launch.
|
||||
self.window.emit('custom-signal1', 'hello from signal1')
|
||||
self.window.emit('custom-signal2', None)
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons['but1'] = xml.get_object('button1')
|
||||
self.buttons['but2'] = xml.get_object('button2')
|
||||
self.buttons['but3'] = xml.get_object('togglebutton1')
|
||||
self.buttons['but4'] = xml.get_object('togglebutton2')
|
||||
|
||||
GObject.signal_new('custom-signal3', self.buttons['but1'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new('custom-signal4', self.buttons['but2'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
|
||||
self.buttons['but1'].connect('clicked', self.button_events)
|
||||
self.buttons['but1'].connect('custom-signal3', self.custom_signal3_method)
|
||||
self.buttons['but2'].connect('clicked', self.button_events)
|
||||
self.buttons['but2'].connect('custom-signal4', self.custom_signal4_method)
|
||||
self.buttons['but3'].connect('clicked', self.button_events)
|
||||
self.buttons['but4'].connect('clicked', self.button_events)
|
||||
|
||||
#fake the user clicking one of our buttons
|
||||
self.buttons['but1'].emit('clicked')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def custom_signal1_method(self, *args):
|
||||
print('custom_signal1 emitted')
|
||||
print(args)
|
||||
|
||||
def custom_signal2_method(self, *args):
|
||||
print('custom_signal2 emitted')
|
||||
print(args)
|
||||
|
||||
def custom_signal3_method(self, *args):
|
||||
print('custom_signal3 emitted')
|
||||
print(args)
|
||||
|
||||
def custom_signal4_method(self, *args):
|
||||
print('custom_signal4 emitted')
|
||||
print(args)
|
||||
|
||||
def button_events(self, widget):
|
||||
widget.emit('custom-signal3', ('1', '2', '3'))
|
||||
toggle_value = ''
|
||||
if widget.get_name() == 'GtkToggleButton':
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
||||
|
||||
View glade XML
|
||||
#+BEGIN_SRC xml :tangle tut16-signals.glade
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="label" translatable="yes">Button One Standard Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="togglebutton1">
|
||||
<property name="label" translatable="yes">Button Three Toggle Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="togglebutton2">
|
||||
<property name="label" translatable="yes">Button Four Toggle Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button2">
|
||||
<property name="label" translatable="yes">Button Two Standard Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
#+END_SRC
|
|
@ -0,0 +1 @@
|
|||
/home/omarks/Ubuntu One/python/gtk3/tut04-text-dropdown-spin.py
|
|
@ -0,0 +1,137 @@
|
|||
#+TITLE: Printing with Cairo GTK3 and python
|
||||
#+DATE: 2015-12-05 12:00:00 UTC
|
||||
#+DESCRIPTION: Example demonstrating how to draw a graphic and position it on a page for printing complete with preview and printer settings dialog.
|
||||
#+FILETAGS: GTK-3:python:widgets
|
||||
#+CATEGORY: python
|
||||
#+SLUG: printing-with-cairo-gtk3-python
|
||||
#+BEGIN_COMMENT
|
||||
.. title: Printing with Cairo GTK3 and python
|
||||
.. slug: printing-with-cairo-gtk3-python
|
||||
.. date: 2015-12-05 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Example demonstrating how to draw a graphic and position it on a page for printing complete with preview and printer settings dialog.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: You can download image used in the example below.
|
||||
[[../../../images/gtk/gtk_print_example_01.png]]
|
||||
|
||||
#+CAPTION: You can download image used in the example below.
|
||||
[[../../../images/misc/testing-256px.png]]
|
||||
|
||||
|
||||
This example uses the gtk3 toolkit the same toolkit used by popular software like gimp and inkscape, it should be quite easy to convert to gtk2 if required the advantage of using gtk is that the libraries are cross platform so you should be
|
||||
able to print on all platform however i have only tested this on ubuntu 15.04 so far.
|
||||
|
||||
|
||||
#+BEGIN_SRC python
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf8 -*-
|
||||
from gi.repository import Gtk, Pango, PangoCairo#, cairo
|
||||
import cairo
|
||||
|
||||
# a4 = 210 x 297
|
||||
page_width = 210
|
||||
page_height = 297
|
||||
page_margin_top = 20
|
||||
dpi = 600
|
||||
_mm_dpi = 72 / 25.4
|
||||
|
||||
class PrintExample(Gtk.Window):
|
||||
|
||||
def __init__(self):
|
||||
self.print_image()
|
||||
|
||||
def print_image(self):
|
||||
"""setup the paper size, print settings and kick of the print operation"""
|
||||
|
||||
ps = Gtk.PaperSize.new_custom("cc", "cc", page_width, page_height, Gtk.Unit.MM)
|
||||
print_settings = Gtk.PrintSettings()
|
||||
print_settings.set_resolution(dpi)
|
||||
|
||||
page_setup = Gtk.PageSetup()
|
||||
page_setup.set_paper_size(ps)
|
||||
page_setup.set_bottom_margin(10.0, Gtk.Unit.MM)
|
||||
page_setup.set_left_margin(5.0, Gtk.Unit.MM)
|
||||
page_setup.set_right_margin(4.5, Gtk.Unit.MM)
|
||||
page_setup.set_top_margin(10.0, Gtk.Unit.MM)
|
||||
page_setup.set_orientation(Gtk.PageOrientation.PORTRAIT)
|
||||
|
||||
|
||||
print_operation = Gtk.PrintOperation()
|
||||
print_operation.set_n_pages(1)
|
||||
print_operation.set_default_page_setup(page_setup)
|
||||
print_operation.set_print_settings(print_settings)
|
||||
print_operation.connect("draw_page", self.draw_page)
|
||||
print_operation.set_export_filename("example.pdf")
|
||||
|
||||
result = print_operation.run(Gtk.PrintOperationAction.PREVIEW, None)
|
||||
print(result)
|
||||
|
||||
|
||||
def draw_page (self, operation, print_context, page_number):
|
||||
"""draw with cairo to the print context layout for printing on sticky labels on an a4 sheet"""
|
||||
cr = print_context.get_cairo_context()
|
||||
cr.set_source_rgb(0, 0, 0)
|
||||
|
||||
ims=cairo.ImageSurface.create_from_png("test.png")
|
||||
cr.set_line_width(1)
|
||||
|
||||
desc = Pango.FontDescription("sans 5")
|
||||
|
||||
layou_title_text = PangoCairo.create_layout(cr)
|
||||
layou_title_text.set_text("Title Text", -1)
|
||||
layou_title_text.set_font_description(desc)
|
||||
layou_title_text.set_width(100 * Pango.SCALE)
|
||||
layou_title_text.set_wrap(Pango.WrapMode.WORD)
|
||||
|
||||
layou_desc_text = PangoCairo.create_layout(cr)
|
||||
layou_desc_text.set_text("Lorem ipsum .....", -1)
|
||||
layou_desc_text.set_font_description(desc)
|
||||
layou_desc_text.set_width(50 * Pango.SCALE)
|
||||
layou_desc_text.set_wrap(Pango.WrapMode.CHAR)
|
||||
|
||||
#offset is the distance from the start of one box to the next including blank space
|
||||
rectangle_x_offset = 40.5 * _mm_dpi
|
||||
rectangle_y_offset = 21.5 * _mm_dpi
|
||||
|
||||
#actual size of the box
|
||||
rectangle_width = 38 * _mm_dpi
|
||||
rectangle_height = 21.5 * _mm_dpi
|
||||
columns = int(page_width / 38)
|
||||
rows = int((page_height-page_margin_top) / 21)
|
||||
|
||||
for column in range(0, columns):
|
||||
for row in range(0, rows):
|
||||
#qr code
|
||||
cr.set_line_width(0.1)
|
||||
cr.save()
|
||||
cr.translate(column * rectangle_x_offset, row * rectangle_y_offset + 15)
|
||||
cr.scale(0.15, 0.15)
|
||||
cr.set_source_surface(ims, 0, 0)
|
||||
cr.paint()
|
||||
cr.restore()
|
||||
|
||||
#rectangle
|
||||
cr.rectangle(column * rectangle_x_offset , row * rectangle_y_offset, rectangle_width , rectangle_height )
|
||||
cr.stroke()
|
||||
|
||||
#title text
|
||||
cr.save()
|
||||
cr.move_to(column * rectangle_x_offset + 4, row * rectangle_y_offset + 4)
|
||||
PangoCairo.update_layout(cr, layou_title_text)
|
||||
PangoCairo.show_layout (cr, layou_title_text)
|
||||
cr.restore()
|
||||
|
||||
#description text
|
||||
cr.save()
|
||||
cr.move_to(column * rectangle_x_offset + 50, row * rectangle_y_offset + 14)
|
||||
PangoCairo.update_layout(cr, layou_desc_text)
|
||||
PangoCairo.show_layout (cr, layou_desc_text)
|
||||
cr.restore()
|
||||
|
||||
win = PrintExample()
|
||||
Gtk.main()
|
||||
#+END_SRC
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,67 @@
|
|||
#import required files
|
||||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold.web import web as html
|
||||
from scaffold.loaders import load_partial
|
||||
|
||||
web = html()
|
||||
|
||||
|
||||
class page:
|
||||
name = __name__
|
||||
title = 'Python GTK examples.'
|
||||
intro = 'Various examples of using gtk widgets in python to build applications.'
|
||||
date = '2013-01-01'
|
||||
path = os.path.abspath('./')
|
||||
tags = 'python', 'gtk3', 'index'
|
||||
|
||||
def create_content(self):
|
||||
web.page.section(web.list.render())
|
||||
|
||||
web.page.section(
|
||||
web.paragraph.create(
|
||||
"""Index of various code snippets demonstrating using the different gtk3 widgets, they should all run in both python 2 and 3.
|
||||
I plan to update and add to these examples, if you would like to see any specific examples let me know."""
|
||||
).render()
|
||||
)
|
||||
|
||||
web.pre.create('bzr branch lp:~oly/python-examples/trunk').render()
|
||||
web.link.create('Launchpad','View Code here','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/')
|
||||
|
||||
web.page.section(
|
||||
web.paragraph.create(
|
||||
"""You can checkout all examples and the glade files to run by using the commad below in a linke terminal or view it directly on """
|
||||
).append(
|
||||
web.link.render()
|
||||
).append(
|
||||
web.pre.render()
|
||||
).render()
|
||||
)
|
||||
|
||||
pages = (
|
||||
('GTK3 Display a window'),
|
||||
('GTK3 Buttons and switches'),
|
||||
('GTK3 Radio Buttons and switches'),
|
||||
('GTK3 Dropdowns and spin buttons'),
|
||||
('GTK3 progress bar and spinner'),
|
||||
('GTK3 Dialog boxes'),
|
||||
('GTK3 App Chooser and scale buttons'),
|
||||
('GTK3 Calendars and menus'),
|
||||
('GTK3 Treeview in liststore mode'),
|
||||
('GTK3 Treeview in treestore mode'),
|
||||
('GTK3 Textview interaction'),
|
||||
('GTK3 Displaying OpenGL inside a drawing area'),
|
||||
('GTK3 Listbox downloader'),
|
||||
('GTK3 Touch screen events'),
|
||||
('GTK3 Simple Opengl application with gtk and Touch screen events'),
|
||||
('GTK3 Creating and connecting to custom signals'),
|
||||
)
|
||||
|
||||
count = 1
|
||||
web.list.create('')
|
||||
for item in pages:
|
||||
web.link.create(item, item ,'./tutorial' + str(count) + '.htm')
|
||||
web.list.append(web.link.render())
|
||||
count += 1
|
||||
web.page.section(web.list.render())
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 01 Create and destroy a window"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut01.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="label" translatable="yes">Button One Standard Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="togglebutton1">
|
||||
<property name="label" translatable="yes">Button Three Toggle Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
<property name="yalign">0.49000000953674316</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="togglebutton2">
|
||||
<property name="label" translatable="yes">Button Four Toggle Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button2">
|
||||
<property name="label" translatable="yes">Button Two Standard Button</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 02 buttons"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut02-buttons.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons['but1'] = xml.get_object('button1')
|
||||
self.buttons['but2'] = xml.get_object('button2')
|
||||
self.buttons['but3'] = xml.get_object('togglebutton1')
|
||||
self.buttons['but4'] = xml.get_object('togglebutton2')
|
||||
|
||||
self.buttons['but1'].connect('clicked', self.button_events)
|
||||
self.buttons['but2'].connect('clicked', self.button_events)
|
||||
self.buttons['but3'].connect('clicked', self.button_events)
|
||||
self.buttons['but4'].connect('clicked', self.button_events)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def button_events(self, widget):
|
||||
toggle_value = ''
|
||||
if widget.get_name() == 'GtkToggleButton':
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 03 Radio Buttons and switches"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut03.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
self.buttons = {}
|
||||
self.buttons['radio1'] = xml.get_object('radiobutton1')
|
||||
self.buttons['radio2'] = xml.get_object('radiobutton2')
|
||||
self.buttons['switch1'] = xml.get_object('switch1')
|
||||
self.buttons['switch2'] = xml.get_object('switch2')
|
||||
|
||||
#set a name of the switches because they dont have a label so we can use this to show selected button
|
||||
self.buttons['switch1'].set_name('switch1')
|
||||
self.buttons['switch2'].set_name('switch2')
|
||||
|
||||
#connect the interface events to functions
|
||||
self.buttons['switch1'].connect('button-press-event', self.switch_button_events)
|
||||
self.buttons['switch2'].connect('button-press-event', self.switch_button_events)
|
||||
self.buttons['radio1'].connect('toggled', self.radio_button_events)
|
||||
self.buttons['radio2'].connect('toggled', self.radio_button_events)
|
||||
|
||||
#add the second radio to the first radio button making it part of the group
|
||||
self.buttons['radio2'].join_group(self.buttons['radio1'])
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def switch_button_events(self, widget, params):
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' + toggle_value)
|
||||
|
||||
def radio_button_events(self, widget):
|
||||
toggle_value = str(widget.get_active())
|
||||
self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="radiobutton1">
|
||||
<property name="label" translatable="yes">radiobutton one</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="relief">half</property>
|
||||
<property name="xalign">0.0099999997764825821</property>
|
||||
<property name="yalign">0.50999999046325684</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="radiobutton2">
|
||||
<property name="label" translatable="yes">radiobutton two</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 04 text input, spin input, drop down options"""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut04.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['spin1'] = xml.get_object('spinbutton1')
|
||||
self.widgets['spin2'] = xml.get_object('spinbutton2')
|
||||
|
||||
#simple dropdown just text
|
||||
self.widgets['comboboxtext'] = xml.get_object('comboboxtext1')
|
||||
|
||||
#more complex multi row multi data types in dropdown using renderers
|
||||
self.widgets['combobox'] = xml.get_object('combobox1')
|
||||
self.widgets['combobox_liststore']= xml.get_object('liststore1')
|
||||
#self.widgets['combobox_cell_renderer']= xml.get_object('liststore1')
|
||||
|
||||
#add some items to the drop down
|
||||
self.widgets['combobox_liststore'].append(['new row test'])
|
||||
self.widgets['combobox_liststore'].append(['second row test'])
|
||||
|
||||
#adding new rows to the dropdown
|
||||
self.widgets['comboboxtext'].append_text('row4')
|
||||
self.widgets['comboboxtext'].append_text('row5')
|
||||
|
||||
#connect to events
|
||||
self.widgets['comboboxtext'].connect('changed', self.dropdown_event_text)
|
||||
self.widgets['spin1'].connect('value-changed', self.spin_button_event)
|
||||
self.widgets['spin2'].connect('value-changed', self.spin_button_event)
|
||||
self.widgets['combobox'].connect('changed', self.dropdown_event)
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show_all()
|
||||
|
||||
def dropdown_event_text(self, widget):
|
||||
self.text.set_text(widget.get_active_text())
|
||||
|
||||
def dropdown_event(self, widget):
|
||||
list_view_model = widget.get_model()
|
||||
active_iter_index = widget.get_active()
|
||||
row_iter = list_view_model.get_iter(active_iter_index)
|
||||
self.text.set_text(widget.get_name() + ' ' +list_view_model.get_value(row_iter, 0 ))
|
||||
|
||||
def spin_button_event(self, widget):
|
||||
self.text.set_text(widget.get_name() + ' ' + ' ' + str(widget.get_value()))
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.0"/>
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">10</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">10</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">row1</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">row2</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">row3</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment2</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment1</property>
|
||||
<property name="climb_rate">0.01</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="combobox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">liststore1</property>
|
||||
<property name="active">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="comboboxtext1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<items>
|
||||
<item translatable="yes">row1</item>
|
||||
<item translatable="yes">row2</item>
|
||||
<item translatable="yes">row3</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 progress and spinner widgets]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 Spinning graphic and progress bars]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 05 demo progress bars and the spinner graphic."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut05.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['entry'] = xml.get_object('entry2')
|
||||
|
||||
self.widgets['spinner1'] = xml.get_object('spinner1')
|
||||
self.widgets['spinnerbutton'] = xml.get_object('togglebutton1')
|
||||
self.widgets['progress1'] = xml.get_object('progressbar1')
|
||||
self.widgets['progress2'] = xml.get_object('progressbar2')
|
||||
|
||||
self.widgets['spinnerbutton'].connect('clicked', self.toggle_spinner)
|
||||
|
||||
self.widgets['progress1'].set_text('active mode')
|
||||
self.widgets['progress2'].set_text('percentage mode')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
self.widgets['spinner1'].start()
|
||||
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar)
|
||||
GLib.timeout_add_seconds(1, self.update_percentage_progress_bar)
|
||||
|
||||
def update_active_progress_bar(self):
|
||||
# nudge the progress bar to show something is still happening, just updating every second in the example
|
||||
# long running processes which have an unknow finish time would use this version
|
||||
self.widgets['progress1'].pulse()
|
||||
return True
|
||||
|
||||
def update_percentage_progress_bar(self):
|
||||
self.count +=0.1
|
||||
self.widgets['progress2'].set_fraction(self.count)
|
||||
if self.count<1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def toggle_spinner(self, widget):
|
||||
if widget.get_active():
|
||||
self.widgets['spinner1'].start()
|
||||
else:
|
||||
self.widgets['spinner1'].stop()
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.0"/>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="togglebutton1">
|
||||
<property name="label" translatable="yes">Toggle Spinner</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progressbar2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="show_text">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progressbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="show_text">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 dialogs]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 font colour and file dialogs]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 06 demo of file, colour and font dialogs."""
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut06.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['entry'] = xml.get_object('entry2')
|
||||
self.widgets['color_button'] = xml.get_object('btnColour')
|
||||
self.widgets['font_button'] = xml.get_object('btnFont')
|
||||
self.widgets['open_button'] = xml.get_object('btnLoad')
|
||||
self.widgets['save_button'] = xml.get_object('btnSave')
|
||||
|
||||
self.widgets['color_button'].connect('clicked', self.colour_selector)
|
||||
self.widgets['font_button'].connect('clicked', self.font_selector)
|
||||
self.widgets['save_button'].connect('clicked', self.save_file_selector)
|
||||
self.widgets['open_button'].connect('clicked', self.open_file_selector)
|
||||
|
||||
self.widgets['open_dialog'] = xml.get_object('fileChooserLoad')
|
||||
self.widgets['open_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
|
||||
self.widgets['open_dialog'].add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
|
||||
|
||||
self.widgets['save_dialog'] = xml.get_object('fileChooserSave')
|
||||
self.widgets['save_dialog'].add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CLOSE)
|
||||
self.widgets['save_dialog'].add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
|
||||
|
||||
self.widgets['color_dialog'] = xml.get_object('colorSelect')
|
||||
|
||||
self.widgets['font_dialog'] = xml.get_object('fontSelect')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def colour_selector(self, widget):
|
||||
self.widgets['color_dialog'].run()
|
||||
self.colour = self.widgets['color_dialog'].get_color_selection().get_current_color()
|
||||
|
||||
#accessing the individual values as integers, converting to floats here
|
||||
colour = [self.colour.red / 65535.0,
|
||||
self.colour.green / 65535.0,
|
||||
self.colour.blue / 65535.0]
|
||||
|
||||
self.widgets['color_dialog'].hide()
|
||||
self.text.set_text(self.widgets['color_dialog'].get_name() + ' ' + self.colour.to_string())
|
||||
|
||||
def font_selector(self, widget):
|
||||
self.widgets['font_dialog'].run()
|
||||
self.font = self.widgets['font_dialog'].get_font_selection()
|
||||
self.text.set_text(self.widgets['font_dialog'].get_name() + ' ' + self.font.get_font_name() + ' ' + str(self.font.get_size()))
|
||||
self.widgets['font_dialog'].hide()
|
||||
|
||||
def open_file_selector(self, widget):
|
||||
response = self.widgets['open_dialog'].run()
|
||||
if self.widgets['open_dialog'].get_filenames():
|
||||
self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ str(self.widgets['open_dialog'].get_filenames()))
|
||||
else:
|
||||
if self.widgets['open_dialog'].get_filename():
|
||||
self.text.set_text(self.widgets['open_dialog'].get_name() + ' '+ self.widgets['open_dialog'].get_filename())
|
||||
self.widgets['open_dialog'].hide()
|
||||
|
||||
def save_file_selector(self, widget):
|
||||
response = self.widgets['save_dialog'].run()
|
||||
self.text.set_text(self.widgets['save_dialog'].get_name() + ' '+ self.widgets['save_dialog'].get_filename())
|
||||
self.widgets['save_dialog'].hide()
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkColorSelectionDialog" id="colorSelect">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
</object>
|
||||
<object class="GtkEntryBuffer" id="entrybuffer1"/>
|
||||
<object class="GtkFileChooserDialog" id="fileChooserLoad">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="role">GtkFileChooserDialog</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
</object>
|
||||
<object class="GtkFileChooserDialog" id="fileChooserSave">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="role">GtkFileChooserDialog</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
</object>
|
||||
<object class="GtkFontSelectionDialog" id="fontSelect">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnLoad">
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnSave">
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="image_position">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnColour">
|
||||
<property name="label">gtk-select-color</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnFont">
|
||||
<property name="label">gtk-select-font</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 app chooser and scale widget]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 scale widget and app chooser example]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk, GLib, Gio
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 04 text input, spin input, drop down options"""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut07.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['scale1'] = xml.get_object('scalebutton1')
|
||||
self.widgets['scale2'] = xml.get_object('scalebutton2')
|
||||
self.widgets['appchooseraudio'] = xml.get_object('appchooserbutton1')
|
||||
self.widgets['appchoosertext'] = xml.get_object('appchooserbutton2')
|
||||
|
||||
self.widgets['appchooseraudio'].connect('changed', self.app_chooser)
|
||||
self.widgets['appchoosertext'].connect('changed', self.app_chooser)
|
||||
self.widgets['scale1'].connect('value-changed', self.scale)
|
||||
self.widgets['scale2'].connect('value-changed', self.scale)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def app_chooser(self, widget):
|
||||
list_view_model = widget.get_model()
|
||||
active_iter_index = widget.get_active()
|
||||
row_iter = list_view_model.get_iter(active_iter_index)
|
||||
app_info = list_view_model.get_value(row_iter, 0)
|
||||
|
||||
gio_file = Gio.File.new_for_path('/tmp/tut07-appchooser-test.txt')
|
||||
app_info.launch((gio_file,), None)
|
||||
self.text.set_text(widget.get_name() + ' ' + app_info.get_name())
|
||||
|
||||
def scale(self, widget, value):
|
||||
self.text.set_text(widget.get_name() + ' ' + str(value))
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkScaleButton" id="scalebutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="yalign">0.49000000953674316</property>
|
||||
<property name="image_position">top</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScaleButton" id="scalebutton2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="size">button</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAppChooserButton" id="appchooserbutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="content_type">audio/flac</property>
|
||||
<property name="heading">select app</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAppChooserButton" id="appchooserbutton2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="content_type">text/plain</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 calendar widget with menu]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 calendar example and drop down menu example]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk, GLib, GObject
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 08 menu, calendar widget."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut08.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['calendar'] = xml.get_object('calendar1')
|
||||
self.widgets['menushow'] = xml.get_object('menuitem6')
|
||||
self.widgets['menuhide'] = xml.get_object('menuitem7')
|
||||
self.widgets['menuhello'] = xml.get_object('menuitem8')
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
self.widgets['menushow'].connect('activate', self.showcalendar)
|
||||
self.widgets['menuhide'].connect('activate', self.hidecalendar)
|
||||
self.widgets['menuhello'].connect('activate', self.hello)
|
||||
self.widgets['calendar'].connect('day-selected', self.date_selected)
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
def hidecalendar(self, *args):
|
||||
self.widgets['calendar'].hide()
|
||||
|
||||
def showcalendar(self, *args):
|
||||
self.widgets['calendar'].show()
|
||||
|
||||
def hello(self, *args):
|
||||
self.text.set_text('hello world')
|
||||
|
||||
def date_selected(self, *args):
|
||||
date = self.widgets['calendar'].get_date()
|
||||
self.text.set_text(str(date[2]) + '-'+str(date[1]) + '-' + str(date[0]))
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,235 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem1">
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem2">
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem3">
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem4">
|
||||
<property name="label">gtk-save-as</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem5">
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="menu2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem6">
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem7">
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem8">
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem9">
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="menu3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="imagemenuitem10">
|
||||
<property name="label">gtk-about</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">example</property>
|
||||
<property name="use_underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="menu4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">show</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">hide</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">hello world</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCalendar" id="calendar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="year">2014</property>
|
||||
<property name="month">2</property>
|
||||
<property name="day">31</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 treeview using a liststore]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 treeview in liststore, show a table like structure]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk, GLib, GObject
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 09 treeview in list mode."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut09.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.ListStore(str, str, str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column1 = Gtk.TreeViewColumn("Column 01")
|
||||
self.treeview.append_column(column1)
|
||||
|
||||
column2 = Gtk.TreeViewColumn("Column 02")
|
||||
self.treeview.append_column(column2)
|
||||
|
||||
column3 = Gtk.TreeViewColumn("Column 03")
|
||||
self.treeview.append_column(column3)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column1.pack_start(cell, False)
|
||||
column1.add_attribute(cell, "text", 0)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column2.pack_start(cell, False)
|
||||
column2.add_attribute(cell, "text", 0)
|
||||
|
||||
cell = Gtk.CellRendererText()
|
||||
column3.pack_start(cell, False)
|
||||
column3.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with some list items
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.0"/>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row1</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row2</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row3</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1"/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 treeview using a treestore]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 10 text input, display a treeview in expandable tree format."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut10.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with a largish tree
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
for item2 in range(0, 5):
|
||||
iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1)
|
||||
for item3 in range(0, 5):
|
||||
self.append_tree('Sub Sub Item ' + str(item3), iter_level_2)
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row1</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row2</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row3</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkEntryBuffer" id="entrybuffer1"/>
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="model">liststore1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,215 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 interactive textview]
|
||||
# [SNIPPET_CATEGORIES: gtk]
|
||||
# [SNIPPET_TAGS:interactive, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 textview run python code on running program]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
from gi.repository import Gtk, Gdk
|
||||
import code
|
||||
import math
|
||||
|
||||
|
||||
class interactiveGtk:
|
||||
|
||||
def __init__(self):
|
||||
window = Gtk.Window()
|
||||
window.set_default_size(380, 300)
|
||||
window.connect("destroy", lambda w: Gtk.main_quit())
|
||||
|
||||
box = Gtk.VBox()
|
||||
|
||||
self.drawingarea = Gtk.DrawingArea()
|
||||
|
||||
#python console using a textview
|
||||
console = interactive_console(Gtk.TextView())
|
||||
self.drawingarea.connect("draw", self.area_expose_cb)
|
||||
|
||||
box.add(self.drawingarea)
|
||||
box.add(console.textarea)
|
||||
|
||||
window.add(box)
|
||||
window.show_all()
|
||||
|
||||
self.drawarea = False
|
||||
|
||||
def show_drawing(self, state):
|
||||
"""self.show_drawing(True) to enable showing the lines"""
|
||||
self.drawarea = state
|
||||
|
||||
def test(self):
|
||||
"""run app.test() when program is running to print this message"""
|
||||
print ('hello world')
|
||||
|
||||
def area_expose_cb(self, widget, context):
|
||||
"""expose event lets draw, lines will only display if we run self.show_lines first.
|
||||
demonstrating running state change of our program"""
|
||||
self.style = self.drawingarea.get_style()
|
||||
if self.drawarea is True:
|
||||
self.drawing(context, 210, 10)
|
||||
|
||||
def drawing(self, cr, x, y):
|
||||
""" draw a circle in the drawing area """
|
||||
cr.set_line_width(10)
|
||||
cr.set_source_rgb(0.5, 0.8, 0.0)
|
||||
|
||||
cr.translate(20 / 2, 20 / 2)
|
||||
cr.arc(50, 50, 50, 0, 2 * math.pi)
|
||||
cr.stroke_preserve()
|
||||
|
||||
cr.set_source_rgb(0.3, 0.4, 0.4)
|
||||
cr.fill()
|
||||
|
||||
|
||||
class interactive_console:
|
||||
editor_chars = '>>>'
|
||||
editor_chars_other = '...'
|
||||
editor_history = []
|
||||
editor_history_position = 0
|
||||
|
||||
def __init__(self, textview):
|
||||
#the python editor window
|
||||
self.textarea = textview
|
||||
self.textarea.connect('key-press-event', self.key_pressed)
|
||||
self.console_buffer = self.textarea.get_buffer()
|
||||
|
||||
#setup some characters which can not be changed
|
||||
self.console_buffer.set_text(self.editor_chars + 'app.show_drawing(True)')
|
||||
self.console_buffer.create_tag("uneditable", editable=False, editable_set=True)
|
||||
self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True)
|
||||
self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False)
|
||||
self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(len(self.editor_chars)))
|
||||
|
||||
#interactive mode interpreter,
|
||||
#pass locals() or globals() so we can access our programs functions and variables
|
||||
#using global here so we have access to the app object in the global scope
|
||||
self.interpreter = code.InteractiveInterpreter(globals())
|
||||
|
||||
def key_pressed(self, widget, event):
|
||||
"""
|
||||
grab key presses, run code from textview on return
|
||||
navigate history if arrow keys are pressed
|
||||
"""
|
||||
if event.keyval == Gdk.keyval_from_name('Return'):
|
||||
self.execute_line()
|
||||
return True
|
||||
|
||||
if event.keyval == Gdk.keyval_from_name('Up'):
|
||||
self.console_history(-1)
|
||||
return True
|
||||
|
||||
if event.keyval == Gdk.keyval_from_name('Down'):
|
||||
self.console_history(1)
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute_line(self):
|
||||
"""
|
||||
carriage return was captured so lets process the textview contents for code to run
|
||||
"""
|
||||
|
||||
text = self.console_buffer.get_text(self.console_buffer.get_start_iter(), self.console_buffer.get_end_iter(), False)
|
||||
source = ''
|
||||
block = False
|
||||
indent = 0
|
||||
#work out code to run if its not a block or a blank line then run what we have,
|
||||
#if its a block of code like a for loop or if condition dont run it yet unless the block has finished.
|
||||
last_line = ''
|
||||
for line in text.split("\n"):
|
||||
line = line[3:].strip('')
|
||||
if line.strip() == '':
|
||||
block = False
|
||||
indent = 0
|
||||
else:
|
||||
if line.endswith(':'):
|
||||
if block is True:
|
||||
source += line + "\n\t"
|
||||
else:
|
||||
source = line + "\n\t"
|
||||
block = True
|
||||
indent += 1
|
||||
else:
|
||||
if line.startswith("\t"):
|
||||
source += line + "\n"
|
||||
else:
|
||||
block = False
|
||||
source = line + "\n"
|
||||
indent = 0
|
||||
last_line = line
|
||||
if last_line.strip() != '':
|
||||
self.append_history(last_line)
|
||||
chars = self.editor_chars
|
||||
|
||||
|
||||
# run the code grabbed from the text buffer and execute it if its not a block
|
||||
results = ''
|
||||
if block is True:
|
||||
chars = self.editor_chars_other
|
||||
else:
|
||||
chars = self.editor_chars
|
||||
results = self.execute_code(source)
|
||||
|
||||
#build text for the editor, and workout which part of the text should be locked
|
||||
text_output = text + '\n' + results + chars
|
||||
text_output_length = len(text_output)
|
||||
if block is True:
|
||||
text_output += "\t" * indent
|
||||
self.update_editor(text_output, text_output_length)
|
||||
|
||||
def execute_code(self, source):
|
||||
"""
|
||||
run any code sent here and capture output and return the results
|
||||
"""
|
||||
# capture output from stdio so we can display the errors in our editor
|
||||
result = StringIO()
|
||||
sys.stdout = result
|
||||
sys.stderr = result
|
||||
#run code output will be put into result because we are capturing stdout
|
||||
self.interpreter.runsource(source, "<<console>>")
|
||||
# restore stdout so future output is capture to the terminal again
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stdout = sys.__stderr__
|
||||
return result.getvalue()
|
||||
|
||||
def update_editor(self, text, uneditable_end=0):
|
||||
"""
|
||||
pass in text to put in the editor and portion to be locked from editing
|
||||
"""
|
||||
self.console_buffer.set_text(text)
|
||||
self.console_buffer.create_mark("input_position", self.console_buffer.get_end_iter(), True)
|
||||
self.console_buffer.create_mark("end_position", self.console_buffer.get_end_iter(), False)
|
||||
self.console_buffer.apply_tag_by_name("uneditable", self.console_buffer.get_start_iter(), self.console_buffer.get_iter_at_offset(uneditable_end))
|
||||
|
||||
def append_history(self, line):
|
||||
"""
|
||||
Store command in history drop command if limit has been reached
|
||||
"""
|
||||
if len(self.editor_history) > 10:
|
||||
self.editor_history.pop()
|
||||
self.editor_history.append(line)
|
||||
self.editor_history_position = len(self.editor_history)
|
||||
|
||||
def console_history(self, direction=-1):
|
||||
"""
|
||||
drop any text on last line and insert text from history based on position
|
||||
"""
|
||||
# get current text excluding last line as we will update from our history
|
||||
text = self.console_buffer.get_text(
|
||||
self.console_buffer.get_start_iter(),
|
||||
self.console_buffer.get_iter_at_line_index(
|
||||
self.console_buffer.get_line_count(), 0),
|
||||
False)
|
||||
|
||||
#work out position in history and what should be displayed
|
||||
linenumber = self.editor_history_position + direction
|
||||
if linenumber >= 0 and linenumber < len(self.editor_history):
|
||||
self.editor_history_position += direction
|
||||
self.console_buffer.set_text(text + self.editor_chars + self.editor_history[self.editor_history_position])
|
||||
|
||||
|
||||
app = interactiveGtk()
|
||||
Gtk.main()
|
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 opengl example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: opengl, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
from OpenGL import GLX
|
||||
from OpenGL.raw._GLX import struct__XDisplay
|
||||
from OpenGL import GL
|
||||
from ctypes import *
|
||||
|
||||
import Xlib
|
||||
from Xlib.display import Display
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
|
||||
|
||||
class gtkgl:
|
||||
""" these method do not seem to exist in python x11 library lets exploit the c methods """
|
||||
xlib = cdll.LoadLibrary('libX11.so')
|
||||
xlib.XOpenDisplay.argtypes = [c_char_p]
|
||||
xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
|
||||
xdisplay = xlib.XOpenDisplay(None)
|
||||
display = Xlib.display.Display()
|
||||
attrs = []
|
||||
|
||||
xwindow_id = None
|
||||
width = height = 200
|
||||
|
||||
def __init__(self):
|
||||
""" lets setup are opengl settings and create the context for our window """
|
||||
self.add_attribute(GLX.GLX_RGBA, True)
|
||||
self.add_attribute(GLX.GLX_RED_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_GREEN_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_BLUE_SIZE, 1)
|
||||
self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0)
|
||||
|
||||
xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
|
||||
configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int()))
|
||||
self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)
|
||||
|
||||
def add_attribute(self, setting, value):
|
||||
"""just to nicely add opengl parameters"""
|
||||
self.attrs.append(setting)
|
||||
self.attrs.append(value)
|
||||
|
||||
def get_attributes(self):
|
||||
""" return our parameters in the expected structure"""
|
||||
attrs = self.attrs + [0, 0]
|
||||
return (c_int * len(attrs))(*attrs)
|
||||
|
||||
def configure(self, wid):
|
||||
""" """
|
||||
self.xwindow_id = GdkX11.X11Window.get_xid(wid)
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('failed configuring context')
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
|
||||
def draw_start(self):
|
||||
"""make cairo context current for drawing"""
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ("failed to get the context for drawing")
|
||||
|
||||
def draw_finish(self):
|
||||
"""swap buffer when we have finished drawing"""
|
||||
GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)
|
||||
|
||||
def test(self):
|
||||
"""Test method to draw something so we can make sure opengl is working and we can see something"""
|
||||
self.draw_start()
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
glBegin(GL_TRIANGLES)
|
||||
glIndexi(0)
|
||||
glColor3f(1.0, 0.0, 0.0)
|
||||
glVertex2i(0, 1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 1.0, 0.0)
|
||||
glVertex2i(-1, -1)
|
||||
glIndexi(0)
|
||||
glColor3f(0.0, 0.0, 1.0)
|
||||
glVertex2i(1, -1)
|
||||
glEnd()
|
||||
|
||||
self.draw_finish()
|
||||
|
||||
|
||||
class gui():
|
||||
glwrap = gtkgl()
|
||||
|
||||
def __init__(self):
|
||||
""" create a gtk window, attach a drawing area and connect the relevant events"""
|
||||
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(self.glwrap.width, self.glwrap.height)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with opengl")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.connect('configure_event', self.on_configure_event)
|
||||
self.drawing_area.connect('draw', self.on_draw)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
"""if we recieve a configure event for example a resize, then grab the context settings and resize our scene """
|
||||
self.glwrap.configure(widget.get_window())
|
||||
self.glwrap.test()
|
||||
return True
|
||||
|
||||
def on_draw(self, widget, context):
|
||||
"""if we recieve a draw event redraw our test triangle"""
|
||||
self.glwrap.test()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
|
||||
<rss version="2.0">
|
||||
|
||||
<channel>
|
||||
<title>Python FAQ</title>
|
||||
<link>file:///usr/share/doc/python/FAQ.html</link>
|
||||
<description>Example Feed reading into gtk</description>
|
||||
<item>
|
||||
<title>Python README</title>
|
||||
<link>file:///usr/share/doc/python/README.Debian</link>
|
||||
<description>Example 01 Descriptive Text</description>
|
||||
<media:thumbnail url="/usr/share/icons/gnome/128x128/apps/libreoffice-draw.png" height="128" width="128" />
|
||||
</item>
|
||||
<item>
|
||||
<title>Python FAQ</title>
|
||||
<link>file:///usr/share/doc/python/FAQ.html</link>
|
||||
<description>Example 02 Descriptive Text</description>
|
||||
<media:thumbnail url="http://www.website.co.uk/img/Sunglasses-are-important-for-skiers-due-to-the-high-levels-of-UV-radiation-they-are-exposed-to_16000539_800747215_0_0_7047638_300.jpg" height="300" width="203" />
|
||||
</item>
|
||||
<item>
|
||||
<title>Python FAQ</title>
|
||||
<link>file:///usr/share/doc/python/FAQ.html</link>
|
||||
<description>Example 02 Descriptive Text</description>
|
||||
<media:thumbnail url="/usr/share/icons/gnome/128x128/apps/libreoffice-main.png" height="300" width="203" />
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
</rss>
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 listbox example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: touch, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: add rows to a listbox based on retrieved xml file]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
import os
|
||||
import requests
|
||||
from io import StringIO, BytesIO
|
||||
import subprocess
|
||||
from lxml.html import parse
|
||||
from gi.repository import Gtk, GLib, Gdk, GdkPixbuf
|
||||
|
||||
|
||||
class application_gui:
|
||||
"""Tutorial 13 custom treeview list boxes"""
|
||||
count = 0
|
||||
|
||||
retrieve_job = None
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut13.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('winFetcher')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['listbox'] = xml.get_object('listbox1')
|
||||
self.widgets['progress'] = xml.get_object('listProgress')
|
||||
self.widgets['refresh'] = xml.get_object('btnRefresh')
|
||||
self.widgets['refresh'].connect('button_press_event', self.refresh)
|
||||
self.widgets['close'] = xml.get_object('btnClose')
|
||||
self.widgets['close'].connect('button_press_event', self.closeFetcher)
|
||||
|
||||
#wrap the listbox so we can reuse the code, pass in the listbox widget to our wrapper class
|
||||
self.listbox = ListBoxSelect(self.widgets['listbox'])
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.openFetcher()
|
||||
self.refresh()
|
||||
|
||||
def openFetcher(self):
|
||||
self.window.show_all()
|
||||
|
||||
def refresh(self, *args):
|
||||
""" get a new xml and start the progress bar"""
|
||||
self.listbox.clear()
|
||||
self.widgets['progress'].show()
|
||||
self.retrieve_job = subprocess.Popen(
|
||||
['curl', 'file://%s/example.xml' % os.path.abspath('./')],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar)
|
||||
|
||||
def update_active_progress_bar(self):
|
||||
""" move the progress bar, when the subprocess returns handle the xml and hide the progress bar"""
|
||||
self.widgets['progress'].pulse()
|
||||
if self.retrieve_job.poll():
|
||||
return True
|
||||
self.widgets['progress'].hide()
|
||||
self.update_list()
|
||||
return False
|
||||
|
||||
def update_list(self):
|
||||
""" parse the xmland grab the elements we are intrested in"""
|
||||
nsmap = {'media': 'http://search.yahoo.com/mrss/'}
|
||||
results = BytesIO(self.retrieve_job.communicate()[0])
|
||||
doc = parse(results).getroot()
|
||||
for item in doc.iterfind(".//item", namespaces=nsmap):
|
||||
title = item.find('title').text
|
||||
link = item.find('link').tail
|
||||
description = item.find('description').text
|
||||
image = item.find('thumbnail', namespaces=nsmap).get('url')
|
||||
self.listbox.model_append(image, title, description, link)
|
||||
|
||||
def download(self):
|
||||
""" retrieve the xml file in a subprocess using curl """
|
||||
self.retrieve_job = subprocess.Popen(
|
||||
['curl', 'file://%s/example.xml' % os.path.abspath('./')],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
def closeFetcher(self, widget):
|
||||
self.window.hide()
|
||||
|
||||
class ListBoxSelect:
|
||||
""" handle the listbox rows dynamically add and remove widgets, and handle download. """
|
||||
listbox = None
|
||||
gui_rows = [] # store widgets here so we can destroy them later.
|
||||
|
||||
def __init__(self, listbox):
|
||||
""" pass in list box to manage and connect event"""
|
||||
self.listbox = listbox
|
||||
self.listbox.connect('row-activated', self.selection)
|
||||
|
||||
def selection(self, lbox, lbrow):
|
||||
""" row selected we may want to react here"""
|
||||
boxrow = lbrow.get_children()[0]
|
||||
boxinfo = boxrow.get_children()[1]
|
||||
print(boxinfo.get_children()[1].get_text())
|
||||
|
||||
def model_append(self, image, title, description, link):
|
||||
""" create new widgets, and connect events for our new row"""
|
||||
items = {}
|
||||
items['row'] = Gtk.ListBoxRow()
|
||||
items['vbox'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
items['label1'] = Gtk.Label(title, xalign=0)
|
||||
items['label2'] = Gtk.Label(link, xalign=0)
|
||||
items['progress'] = Gtk.ProgressBar()
|
||||
items['progress'].hide()
|
||||
items['progress'].set_fraction(0)
|
||||
items['vbox'].pack_start(items['label1'], True, False, 0)
|
||||
items['vbox'].pack_start(items['label2'], True, False, 0)
|
||||
items['vbox'].pack_start(items['progress'], False, False, 0)
|
||||
|
||||
items['hbox'] = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
items['image'] = Gtk.Image.new_from_file(image)
|
||||
items['button'] = Gtk.Button(label="Download")
|
||||
items['button'].connect('button_press_event', self.download, items, link)
|
||||
|
||||
items['hbox'].pack_start(items['image'], False, False, 0)
|
||||
items['hbox'].pack_start(items['vbox'], True, True, 0)
|
||||
items['button_box'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
items['button_box'].pack_end(items['button'], False, False, 0)
|
||||
items['hbox'].pack_start(items['button_box'], False, False, 0)
|
||||
items['row'].add(items['hbox'])
|
||||
|
||||
self.listbox.add(items['row'])
|
||||
items['row'].show_all()
|
||||
|
||||
self.gui_rows.append(items)
|
||||
|
||||
def download(self, widget, args, items, link):
|
||||
""" download button click, change widgets and start the progress bar and download """
|
||||
items['button'].hide()
|
||||
items['job'] = subprocess.Popen(
|
||||
['curl', '-O', link],
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
GLib.timeout_add_seconds(1, self.update_active_progress_bar, items)
|
||||
|
||||
def update_active_progress_bar(self, widgets):
|
||||
""" update progress bar until command finished """
|
||||
widgets['progress'].pulse()
|
||||
if widgets['job'].poll():
|
||||
return True
|
||||
widgets['progress'].hide()
|
||||
return False
|
||||
|
||||
def clear(self):
|
||||
""" remove all rows so we can pre-populate"""
|
||||
for item in self.gui_rows:
|
||||
item['row'].destroy()
|
||||
self.gui_rows = []
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkWindow" id="winFetcher">
|
||||
<property name="width_request">400</property>
|
||||
<property name="height_request">400</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnRefresh">
|
||||
<property name="label">gtk-refresh</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="listProgress">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkViewport" id="viewport1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="listbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="btnClose">
|
||||
<property name="label" translatable="yes">Close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="relief">half</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 touch example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: touch, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: handling touch events with GTK3]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
|
||||
|
||||
class gui():
|
||||
|
||||
def __init__(self):
|
||||
self.touch_count = 0
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 touch events")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
#add the type of events we are interested in retrieving, skip this step and your events will never fire
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
self.drawing_area.connect('button_press_event', self.touched)
|
||||
self.drawing_area.connect('touch-event', self.touched)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def touched(self, widget, ev):
|
||||
# detect the type of device we can filter events using this
|
||||
# if we dont a touch event and mouse event can be triggered from one touch for example
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHPAD:
|
||||
print('touchpad device')
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.MOUSE:
|
||||
print('mouse device')
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN:
|
||||
print('touchscreen device')
|
||||
|
||||
#from what i can tell there is no support for gestures so you would need another library
|
||||
#or to handle this yourself
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_BEGIN:
|
||||
self.touch_count += 1
|
||||
print('start %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_UPDATE:
|
||||
print('UPDATE %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_END:
|
||||
self.touch_count -= 1
|
||||
print('end %d %s %s' % (self.touch_count, ev.touch.x,ev.touch.y))
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_CANCEL:
|
||||
self.touch_count -= 1
|
||||
print('cancelled')
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawingarea1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_STRUCTURE_MASK | GDK_TOUCH_MASK</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="lower">0.050000000000000003</property>
|
||||
<property name="upper">10</property>
|
||||
<property name="value">0.050000000000000003</property>
|
||||
<property name="step_increment">0.050000000000000003</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">20</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Radius</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinradius">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment2</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btncolor">
|
||||
<property name="label" translatable="yes">generate</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Rotation</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinrotate">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment1</property>
|
||||
<property name="climb_rate">0.050000000000000003</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="value">0.050000000000000003</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Mode</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="cmbmode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">1</property>
|
||||
<items>
|
||||
<item id="0" translatable="yes">VBO per cube</item>
|
||||
<item id="1" translatable="yes">VBO all cubes</item>
|
||||
<item id="2" translatable="yes">VBO Test Cube</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="canvas">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,173 @@
|
|||
import numpy
|
||||
from numpy import array
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL import shaders
|
||||
from OpenGL.arrays import vbo
|
||||
|
||||
from OpenGL._bytes import bytes, _NULL_8_BYTE
|
||||
|
||||
def compileShader( source, shaderType ):
|
||||
|
||||
"""Compile shader source of given type
|
||||
source -- GLSL source-code for the shader
|
||||
shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc,
|
||||
returns GLuint compiled shader reference
|
||||
raises RuntimeError when a compilation failure occurs
|
||||
"""
|
||||
if isinstance(source, str):
|
||||
|
||||
source = [source]
|
||||
elif isinstance(source, bytes):
|
||||
|
||||
source = [source.decode('utf-8')]
|
||||
|
||||
shader = glCreateShader(shaderType)
|
||||
glShaderSource(shader, source)
|
||||
glCompileShader(shader)
|
||||
result = glGetShaderiv(shader, GL_COMPILE_STATUS)
|
||||
|
||||
if not(result):
|
||||
# TODO: this will be wrong if the user has
|
||||
# disabled traditional unpacking array support.
|
||||
raise RuntimeError(
|
||||
"""Shader compile failure (%s): %s"""%(
|
||||
result,
|
||||
glGetShaderInfoLog( shader ),
|
||||
),
|
||||
source,
|
||||
shaderType,
|
||||
)
|
||||
return shader
|
||||
|
||||
|
||||
class shader:
|
||||
vertex = """#version 120
|
||||
//attributes in values
|
||||
attribute vec3 inPosition;
|
||||
attribute vec3 inColor;
|
||||
|
||||
//varying sending to fragment shader
|
||||
varying vec4 outColor;
|
||||
void main(){
|
||||
vec4 fragmentPos = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = (gl_ProjectionMatrix * fragmentPos);
|
||||
outColor = vec4(gl_Color.rgb, 1);
|
||||
}"""
|
||||
|
||||
fragment = """#version 120
|
||||
varying vec4 outColor;
|
||||
void main(){
|
||||
gl_FragColor = outColor;
|
||||
}"""
|
||||
|
||||
program = None
|
||||
def compile(self):
|
||||
self.program = shaders.compileProgram(
|
||||
compileShader(self.vertex, GL_VERTEX_SHADER),
|
||||
compileShader(self.fragment, GL_FRAGMENT_SHADER),)
|
||||
|
||||
class point:
|
||||
__slots__ = ['x', 'y', 'z', 'xyz', 'vertex']
|
||||
|
||||
def __init__(self, p, c=(1, 0, 0)):
|
||||
""" Position in 3d space as a tuple or list, and colour in tuple or list format"""
|
||||
self.x, self.y, self.z = p
|
||||
self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f')
|
||||
|
||||
|
||||
class cube:
|
||||
def __init__(self, p1, color, size=0.5):
|
||||
self.color = array([1, 0, 0], 'f')
|
||||
#self.xyz = p1
|
||||
self.points = (
|
||||
point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
|
||||
|
||||
point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
|
||||
|
||||
)
|
||||
|
||||
self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
|
||||
self.vbuffer_size = len(self.get_data())
|
||||
|
||||
def bind(self):
|
||||
self.vbuffer.bind()
|
||||
|
||||
def unbind(self):
|
||||
self.vbuffer.unbind()
|
||||
|
||||
def get_data(self):
|
||||
return (
|
||||
self.points[0].vertex, self.points[2].vertex, self.points[1].vertex,
|
||||
self.points[0].vertex, self.points[3].vertex, self.points[2].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[1].vertex, self.points[5].vertex,
|
||||
self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[7].vertex, self.points[3].vertex,
|
||||
self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[2].vertex, self.points[3].vertex,
|
||||
self.points[6].vertex, self.points[3].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
|
||||
self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
|
||||
self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
|
||||
)
|
||||
|
||||
|
||||
|
||||
class cube:
|
||||
def __init__(self, p1, color, size=0.5):
|
||||
self.color = array([1, 0, 0], 'f')
|
||||
#self.xyz = p1
|
||||
self.points = (
|
||||
point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
|
||||
|
||||
point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
|
||||
point((p1[0] - size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
|
||||
point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
|
||||
|
||||
)
|
||||
|
||||
self.vbuffer = vbo.VBO(array(self.get_data(), 'f'))
|
||||
self.vbuffer_size = len(self.get_data())
|
||||
|
||||
def bind(self):
|
||||
self.vbuffer.bind()
|
||||
|
||||
def unbind(self):
|
||||
self.vbuffer.unbind()
|
||||
|
||||
def get_data(self):
|
||||
return (
|
||||
self.points[0].vertex, self.points[2].vertex, self.points[1].vertex,
|
||||
self.points[0].vertex, self.points[3].vertex, self.points[2].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[1].vertex, self.points[5].vertex,
|
||||
self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,
|
||||
|
||||
self.points[0].vertex, self.points[7].vertex, self.points[3].vertex,
|
||||
self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[2].vertex, self.points[3].vertex,
|
||||
self.points[6].vertex, self.points[3].vertex, self.points[7].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
|
||||
self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,
|
||||
|
||||
self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
|
||||
self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
|
||||
)
|
||||
|
382
bases/do-blog/resources/documents/python/gtk3/tut15/tut15-opengl-touch-app-01.py
Executable file
382
bases/do-blog/resources/documents/python/gtk3/tut15/tut15-opengl-touch-app-01.py
Executable file
|
@ -0,0 +1,382 @@
|
|||
#!/usr/bin/env python
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
import pprint
|
||||
|
||||
import Xlib
|
||||
from Xlib.display import Display
|
||||
from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject
|
||||
from ctypes import *
|
||||
from numpy import array
|
||||
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GL import shaders
|
||||
from OpenGL.GLU import gluPerspective, gluLookAt
|
||||
from OpenGL.arrays import vbo
|
||||
from OpenGL import GLX
|
||||
try:
|
||||
from OpenGL.GLX import struct__XDisplay
|
||||
except ImportError as err:
|
||||
from OpenGL.raw._GLX import struct__XDisplay
|
||||
from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
|
||||
from OpenGL.GL import shaders, glGetUniformLocation
|
||||
|
||||
from helper import shader
|
||||
from helper import cube as createcube
|
||||
|
||||
class gtkgl:
|
||||
""" these method do not seem to exist in python x11 library lets exploit the c methods
|
||||
useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib"""
|
||||
xlib = cdll.LoadLibrary('libX11.so')
|
||||
xlib.XOpenDisplay.argtypes = [c_char_p]
|
||||
xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
|
||||
xdisplay = xlib.XOpenDisplay(None)
|
||||
display = Xlib.display.Display()
|
||||
attrs = []
|
||||
|
||||
xwindow_id = None
|
||||
width = height = 500
|
||||
|
||||
def __init__(self):
|
||||
""" lets setup are opengl settings and create the context for our window """
|
||||
self.add_attribute(GLX.GLX_RGBA, True)
|
||||
self.add_attribute(GLX.GLX_RED_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_GREEN_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_BLUE_SIZE, 8)
|
||||
self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1)
|
||||
self.add_attribute(GLX.GLX_DEPTH_SIZE, 24)
|
||||
|
||||
xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
|
||||
print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid))
|
||||
self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)
|
||||
|
||||
def add_attribute(self, setting, value):
|
||||
"""just to nicely add opengl parameters"""
|
||||
self.attrs.append(setting)
|
||||
self.attrs.append(value)
|
||||
|
||||
def get_attributes(self):
|
||||
""" return our parameters in the expected structure"""
|
||||
attrs = self.attrs + [0, 0]
|
||||
return (c_int * len(attrs))(*attrs)
|
||||
|
||||
def configure(self, wid):
|
||||
""" """
|
||||
self.xwindow_id = GdkX11.X11Window.get_xid(wid)
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ('failed configuring context')
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
|
||||
def draw_start(self):
|
||||
"""make cairo context current for drawing"""
|
||||
if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
|
||||
print ("failed to get the context for drawing")
|
||||
|
||||
def draw_finish(self):
|
||||
"""swap buffer when we have finished drawing"""
|
||||
GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)
|
||||
|
||||
class scene:
|
||||
width, height = 600, 600
|
||||
|
||||
rotationx = 0.0
|
||||
rotationy = 0.0
|
||||
|
||||
rotation_incx = 0.5
|
||||
rotation_incy = 0.5
|
||||
radius = 0
|
||||
|
||||
touch_count = 0
|
||||
touch_previous = 0, 0
|
||||
touch_start_one = 0, 0
|
||||
touch_start_two = 0, 0
|
||||
touch_end_one = 0, 0
|
||||
touch_end_two = 0, 0
|
||||
touch_time = 0
|
||||
|
||||
camera_distance = 25
|
||||
|
||||
cube_length = 1.0
|
||||
cube_size = cube_length / 2
|
||||
|
||||
def __init__(self):
|
||||
"""setup everything in the correct order"""
|
||||
self.glwrap = gtkgl()
|
||||
self.setup_opengl()
|
||||
self.generate()
|
||||
self.gui()
|
||||
|
||||
self.mode = 'vbo all cubes'
|
||||
|
||||
def gui(self):
|
||||
"""load in the gui and connect the events and set our properties"""
|
||||
self.start_time = time.time()
|
||||
self.frame = 1
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('gui.glade')
|
||||
|
||||
self.window = xml.get_object('window1')
|
||||
|
||||
self.mode_widget = xml.get_object('cmbmode')
|
||||
self.mode_widget.connect('changed', self.change_mode)
|
||||
|
||||
self.rotate_widget = xml.get_object('spinrotate')
|
||||
self.rotate_widget.connect('value-changed', self.change_rotate_speed)
|
||||
|
||||
self.radius_widget = xml.get_object('spinradius')
|
||||
self.radius_widget.connect('value-changed', self.change_radius)
|
||||
|
||||
self.color_widget = xml.get_object('btncolor')
|
||||
self.color_widget.connect('clicked', self.change_color)
|
||||
|
||||
self.canvas_widget = xml.get_object('canvas')
|
||||
self.canvas_widget.connect('configure_event', self.on_configure_event)
|
||||
self.canvas_widget.connect('draw', self.on_draw)
|
||||
self.canvas_widget.set_double_buffered(False)
|
||||
self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height)
|
||||
self.canvas_widget.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.canvas_widget.connect('touch-event', self.touched)
|
||||
|
||||
self.window.show_all()
|
||||
GObject.idle_add(self.loop_draw)
|
||||
|
||||
def touched(self, widget, ev):
|
||||
"""basic touch support, count the touches so we no how many fingers
|
||||
basic pinc zoom along the x, single finger slide to rotate"""
|
||||
if ev.get_source_device().get_source() == Gdk.InputSource.TOUCHSCREEN:
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_BEGIN:
|
||||
self.touch_start = ev.touch.x, ev.touch.y
|
||||
self.touch_count += 1
|
||||
if self.touch_count == 2:
|
||||
self.touch_start_two = ev.touch.x, ev.touch.y
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_UPDATE:
|
||||
if ev.touch.time - self.touch_time < 100:
|
||||
return True
|
||||
|
||||
if self.touch_count == 2:
|
||||
#basic pinch zoom along the x axis
|
||||
d1 = self.touch_previous[0] - ev.touch.x
|
||||
if d1 > 1:
|
||||
self.camera_distance += self.camera_distance * 0.05
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
if d1 < 1:
|
||||
self.camera_distance -= self.camera_distance * 0.05
|
||||
self.touch_previous = ev.touch.x, ev.touch.y
|
||||
self.update_camera()
|
||||
self.touch_time = ev.touch.time
|
||||
|
||||
if ev.touch.type == Gdk.EventType.TOUCH_END:
|
||||
self.touch_end = ev.touch.x, ev.touch.y
|
||||
#set rotation when touch ends
|
||||
if self.touch_count == 1:
|
||||
self.rotation_incx = (self.touch_start[0] - self.touch_end[0]) * 0.01
|
||||
self.rotation_incy = (self.touch_start[1] - self.touch_end[1]) * 0.01
|
||||
self.touch_count = 0
|
||||
|
||||
def in_circle(self, center_x, center_y, center_z, radius, x, y, z):
|
||||
""" test if our cordinate lies inside our sphere"""
|
||||
square_dist = (center_x - x) ** 2 + (center_y - y) ** 2 + (center_z - z) ** 2
|
||||
return square_dist <= radius ** 2
|
||||
|
||||
def change_color(self, widget):
|
||||
#regenerate the scene
|
||||
self.generate()
|
||||
|
||||
def change_mode(self, widget):
|
||||
#change whats drawn and how
|
||||
self.mode = widget.get_active_text().lower()
|
||||
print(widget.get_active_text())
|
||||
|
||||
def change_rotate_speed(self, widget):
|
||||
#handle spinner rotation speed event
|
||||
self.rotation_incx = widget.get_value()
|
||||
self.rotation_incy = widget.get_value()
|
||||
|
||||
def change_radius(self, widget):
|
||||
#increase size of circle and number of polygons
|
||||
self.radius = int(widget.get_value())
|
||||
self.generate()
|
||||
|
||||
def loop_draw(self):
|
||||
#send redraw event to drawing area widget
|
||||
self.canvas_widget.queue_draw()
|
||||
return True
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
"""if we recieve a configure event for example a resize, then grab the context settings and resize our scene """
|
||||
self.glwrap.width = widget.get_allocation().width
|
||||
self.glwrap.height = widget.get_allocation().height
|
||||
self.width, self.height = self.glwrap.width, self.glwrap.height
|
||||
|
||||
#update our states because we have reconfigured the display
|
||||
self.glwrap.configure(widget.get_window())
|
||||
self.glwrap.draw_start()
|
||||
self.update_camera()
|
||||
self.setup_shaders()
|
||||
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glDepthMask(GL_TRUE)
|
||||
glDepthFunc(GL_LEQUAL)
|
||||
glDepthRange(0.0, 1.0)
|
||||
glEnable(GL_CULL_FACE)
|
||||
glCullFace(GL_BACK)
|
||||
glFrontFace(GL_CW)
|
||||
|
||||
self.glwrap.draw_finish()
|
||||
return True
|
||||
|
||||
def on_draw(self, widget, context):
|
||||
"""if we recieve a draw event redraw our opengl scene"""
|
||||
self.elapsed_time = time.time() - self.start_time
|
||||
self.frame += 1
|
||||
|
||||
if self.elapsed_time > 1:
|
||||
print('fps %d' % self.frame)
|
||||
self.start_time = time.time()
|
||||
self.frame = 1
|
||||
self.glwrap.draw_start()
|
||||
self.draw()
|
||||
self.glwrap.draw_finish()
|
||||
|
||||
def generate(self):
|
||||
self.cubes = []
|
||||
#position cubes inside a sphere radius
|
||||
for shift_x in range(-self.radius, self.radius + 1):
|
||||
for shift_y in range(-self.radius, self.radius + 1):
|
||||
for shift_z in range(-self.radius, self.radius + 1):
|
||||
x = shift_x * self.cube_length
|
||||
y = shift_y * self.cube_length
|
||||
z = shift_z * self.cube_length
|
||||
if not self.in_circle(0, 0, 0, self.radius, x, y, z):
|
||||
continue
|
||||
#random colours / textures if we want
|
||||
color = random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])
|
||||
self.cubes.append(createcube((x, y, z), color, self.cube_size))
|
||||
|
||||
self.test_cube = createcube((x, y, z), (random.choice([0.85, 0.15]), random.choice([0.85, 0.15]), random.choice([0.85, 0.15])), 6)
|
||||
faces = []
|
||||
for cube in self.cubes:
|
||||
faces += cube.get_data()
|
||||
|
||||
print('Generated %s Cubes' % str(len(self.cubes)))
|
||||
print('Generated %s Tringles' % str(len(faces) / 3))
|
||||
self.vbuffer = vbo.VBO(array(faces, 'f'))
|
||||
|
||||
def setup_shaders(self):
|
||||
self.shader_program = shader()
|
||||
self.shader_program.compile()
|
||||
|
||||
def setup_opengl(self):
|
||||
glShadeModel(GL_SMOOTH)
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
glClearDepth(1.0)
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
|
||||
glPolygonMode(GL_FRONT, GL_FILL)
|
||||
|
||||
def update_camera(self):
|
||||
glViewport(0, 0, self.width, self.height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0)
|
||||
gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance, # location
|
||||
0.0, 0.0, 0.0, # lookat
|
||||
0.0, 1.0, 0.0) # up direction
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glLoadIdentity()
|
||||
|
||||
def draw_test(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
#use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
#render the triangles into a virtual buffer object
|
||||
self.test_cube.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, self.test_cube.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, self.test_cube.vbuffer_size)
|
||||
self.test_cube.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw_vbo_per_cube(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
# use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
# render the triangles into a virtual buffer object
|
||||
for shape in self.cubes:
|
||||
shape.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, shape.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, shape.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, shape.vbuffer_size)
|
||||
shape.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw_vbo_all_cubes(self):
|
||||
#lets do a simple rotation so we can see the objects are 3d
|
||||
glRotatef(self.rotationx, 1.0, 0.0, 0.0)
|
||||
self.rotationx += self.rotation_incx
|
||||
glRotatef(self.rotationy, 0.0, 1.0, 0.0)
|
||||
self.rotationy += self.rotation_incy
|
||||
|
||||
# use our shader program and enable vertex loading
|
||||
glUseProgram(self.shader_program.program)
|
||||
glEnableClientState(GL_VERTEX_ARRAY)
|
||||
glEnableClientState(GL_COLOR_ARRAY)
|
||||
|
||||
# render the triangles into a virtual buffer object
|
||||
self.vbuffer.bind()
|
||||
glVertexPointer(3, GL_FLOAT, 24, self.vbuffer)
|
||||
glColorPointer(3, GL_FLOAT, 24, self.vbuffer + 12)
|
||||
glDrawArrays(GL_TRIANGLES, 0, len(self.vbuffer))
|
||||
self.vbuffer.unbind()
|
||||
|
||||
#restore opengl to our previous state
|
||||
glDisableClientState(GL_COLOR_ARRAY)
|
||||
glDisableClientState(GL_VERTEX_ARRAY)
|
||||
shaders.glUseProgram(0)
|
||||
|
||||
def draw(self):
|
||||
glEnable(GL_DEPTH_TEST)
|
||||
glClearDepth(1.0)
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
if self.mode == 'vbo test cube':
|
||||
self.draw_test()
|
||||
if self.mode == 'vbo per cube':
|
||||
self.draw_vbo_per_cube()
|
||||
if self.mode == 'vbo all cubes':
|
||||
self.draw_vbo_all_cubes()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
glexample = scene()
|
||||
GLib.threads_init()
|
||||
Gdk.threads_init()
|
||||
Gdk.threads_enter()
|
||||
Gtk.main()
|
||||
Gdk.threads_leave()
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 opengl example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: opengl, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
import cairo
|
||||
|
||||
|
||||
class gui():
|
||||
|
||||
def __init__(self):
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with cairo")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.set_size_request(50,50)
|
||||
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.drawing_area.connect('draw', self.expose)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def expose(self, widget, context):
|
||||
context.set_source_rgb(1, 0.5, 1)
|
||||
context.rectangle(10, 10, 40, 40)
|
||||
|
||||
context.move_to(0, 0)
|
||||
context.rel_line_to(100, 100)
|
||||
context.fill()
|
||||
context.save()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 opengl example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: opengl, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
import cairo
|
||||
|
||||
|
||||
class gui():
|
||||
|
||||
def __init__(self):
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with cairo")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.set_size_request(50,50)
|
||||
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.drawing_area.connect('draw', self.expose)
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.window.add(self.drawing_area)
|
||||
self.window.show_all()
|
||||
|
||||
def expose(self, widget, context):
|
||||
context.set_source_rgb(1, 0.5, 1)
|
||||
context.rectangle(10, 10, 40, 40)
|
||||
|
||||
context.move_to(0, 0)
|
||||
context.rel_line_to(100, 100)
|
||||
context.fill()
|
||||
context.save()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 opengl example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: opengl, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
import cairo
|
||||
|
||||
|
||||
class gui():
|
||||
|
||||
def __init__(self):
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with opengl")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.fixed_area = Gtk.Layout()
|
||||
self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE)
|
||||
self.fixed_area.connect("drag-data-received", self.on_drag_data_received)
|
||||
self.fixed_area.connect("drag-drop", self.on_drag_data_received)
|
||||
self.fixed_area.connect("drag-motion", self.on_drag_data_received)
|
||||
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.set_size_request(50,50)
|
||||
#self.drawing_area.set_border_width(50)
|
||||
self.drawing_area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],Gdk.DragAction.MOVE)
|
||||
|
||||
#self.drawing_area.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [],DRAG_ACTION)
|
||||
self.drawing_area.connect("drag-data-get", self.on_drag_data_get)
|
||||
self.drawing_area.connect("drag-begin", self.on_drag_data_get)
|
||||
self.drawing_area.connect("drag-end", self.on_drag_finish)
|
||||
|
||||
#~ self.drawing_area.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
|
||||
#~ self.drawing_area.add_events(Gdk.EventMask.SCROLL_MASK)
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
#self.drawing_area.connect('configure_event', self.on_configure_event)
|
||||
#self.drawing_area.connect('draw', self.on_draw)
|
||||
self.drawing_area.connect('draw', self.expose)
|
||||
#self.drawing_area.connect('clicked', self.clicked)
|
||||
|
||||
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.fixed_area.put(self.drawing_area,1,1)
|
||||
self.window.add(self.fixed_area)
|
||||
self.fixed_area.show_all()
|
||||
#self.window.add(self.drawing_area)
|
||||
print 'test'
|
||||
self.window.show_all()
|
||||
|
||||
def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
|
||||
print 'drop'
|
||||
|
||||
def on_drag_finish(self, widget, event):
|
||||
print event
|
||||
print dir(event.get_dest_window)
|
||||
print 'drop'
|
||||
|
||||
def on_drag_data_get(self, widget, event):
|
||||
print 'drag'
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
return True
|
||||
|
||||
def expose(self, widget, context):
|
||||
#self.context = widget.window.cairo_create()
|
||||
#context.rectangle(event.area.x, event.area.y,
|
||||
# event.area.width, event.area.height)
|
||||
#context.clip()
|
||||
context.set_source_rgb(1, 0.5, 1)
|
||||
context.rectangle(10, 10, 40, 40)
|
||||
|
||||
context.move_to(0, 0)
|
||||
context.rel_line_to(100, 100)
|
||||
context.fill()
|
||||
context.save()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 opengl example]
|
||||
# [SNIPPET_CATEGORIES: opengl]
|
||||
# [SNIPPET_TAGS: opengl, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: using gtk3 library lets draw using opengl]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
|
||||
import sys
|
||||
from gi.repository import Gtk, GdkX11, Gdk
|
||||
import cairo
|
||||
|
||||
|
||||
class gui():
|
||||
x = 10
|
||||
y = 10
|
||||
current_x = 0
|
||||
current_y = 0
|
||||
def __init__(self):
|
||||
self.window = Gtk.Window()
|
||||
self.window.realize()
|
||||
self.window.resize(300, 300)
|
||||
self.window.set_resizable(True)
|
||||
self.window.set_reallocate_redraws(True)
|
||||
self.window.set_title("GTK3 with opengl")
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
self.fixed_area = Gtk.Layout()
|
||||
self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE)
|
||||
self.fixed_area.connect("drag-data-received", self.on_drag_data_received)
|
||||
self.fixed_area.connect("drag-drop", self.on_drag_data_received)
|
||||
self.fixed_area.connect("drag-motion", self.on_drag_data_received)
|
||||
|
||||
|
||||
self.drawing_area = Gtk.DrawingArea()
|
||||
self.drawing_area.set_size_request(50,50)
|
||||
self.drawing_area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],Gdk.DragAction.MOVE)
|
||||
|
||||
self.drawing_area.connect("drag-data-get", self.on_drag_data_get)
|
||||
self.drawing_area.connect("drag-begin", self.on_drag_data_get)
|
||||
self.drawing_area.connect("drag-end", self.on_drag_finish)
|
||||
self.drawing_area.connect("motion-notify-event", self.move)
|
||||
|
||||
self.drawing_area.add_events(Gdk.EventMask.TOUCH_MASK)
|
||||
self.drawing_area.connect('draw', self.expose)
|
||||
|
||||
self.drawing_area.set_double_buffered(False)
|
||||
|
||||
self.fixed_area.put(self.drawing_area, 1, 1)
|
||||
self.window.add(self.fixed_area)
|
||||
self.fixed_area.show_all()
|
||||
self.window.show_all()
|
||||
|
||||
def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
|
||||
self.x = x
|
||||
self.y = y
|
||||
print 'drop recieve'
|
||||
print x
|
||||
print y
|
||||
|
||||
def on_drag_finish(self, widget, event):
|
||||
print event
|
||||
print dir(event.get_dest_window)
|
||||
print event.get_dest_window.get_type()
|
||||
self.x = self.current_x
|
||||
self.y = self.current_y
|
||||
print 'drop finish'
|
||||
|
||||
def on_drag_data_get(self, widget, event):
|
||||
print 'drag'
|
||||
|
||||
def on_configure_event(self, widget, event):
|
||||
return True
|
||||
|
||||
def move(self, widget, event):
|
||||
self.current_x = event.x
|
||||
self.current_y = event.y
|
||||
print self.x
|
||||
|
||||
def expose(self, widget, context):
|
||||
print 'expose'
|
||||
print self.x
|
||||
context.set_source_rgb(1, 0.5, 1)
|
||||
context.move_to(self.x, self.y)
|
||||
context.rectangle(self.x, self.y, self.x+40, self.y+40)
|
||||
|
||||
|
||||
#context.rel_line_to(100, 100)
|
||||
context.fill()
|
||||
context.save()
|
||||
|
||||
|
||||
def main():
|
||||
g = gui()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 treeview using a treestore]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gdk, Gtk, GLib
|
||||
|
||||
#http://jason.zwolak.org/technoblog/2010/08/drag-and-drop-with-gtk2-and-perl/
|
||||
class application_gui:
|
||||
"""Tutorial 10 text input, display a treeview in expandable tree format."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut10.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE)
|
||||
self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.COPY)
|
||||
self.treeview.enable_model_drag_dest([("example", 0, 0)], Gdk.DragAction.COPY)
|
||||
#treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.ACTION_COPY)
|
||||
#treeview.enable_model_drag_dest([("example", 0, 0)], gtk.gdk.ACTION_COPY)
|
||||
|
||||
self.treeview.connect("drag_data_received", self.onDragDataReceived)
|
||||
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
#~ def iterCopy(self, treeview, model, iter_to_copy, target_iter, pos):
|
||||
#~
|
||||
#~ data_column_0 = model.get_value(iter_to_copy, 0)
|
||||
#~ data_column_1 = model.get_value(iter_to_copy, 1)
|
||||
#~ if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
|
||||
#~ new_iter = model.prepend(target_iter, None)
|
||||
#~ elif pos == gtk.TREE_VIEW_DROP_BEFORE:
|
||||
#~ new_iter = model.insert_before(None, target_iter)
|
||||
#~ elif pos == gtk.TREE_VIEW_DROP_AFTER:
|
||||
#~ new_iter = model.insert_after(None, target_iter)
|
||||
#~ model.set_value(new_iter, 0, data_column_0)
|
||||
#~ model.set_value(new_iter, 1, data_column_1)
|
||||
#~ if model.iter_has_child(iter_to_copy):
|
||||
#~ for i in range(0, model.iter_n_children(iter_to_copy)):
|
||||
#~ next_iter_to_copy = model.iter_nth_child(iter_to_copy, i)
|
||||
#~ self.iterCopy(treeview, model, next_iter_to_copy, new_iter, gtk.TREE_VIEW_DROP_INTO_OR_BEFORE)
|
||||
|
||||
def walk(self, treeiter):
|
||||
if model.iter_has_child(treeiter):
|
||||
for i in range(0, model.iter_n_children(treeiter)):
|
||||
yield model.iter_nth_child(treeiter, i)
|
||||
|
||||
def onDragDataReceived(self, treeview, drag_context, x, y, selection, info, eventtime):
|
||||
"""on data recieve we remove the item and insert in the new location, the insert location must have the same depth"""
|
||||
model, iter_source = treeview.get_selection().get_selected()
|
||||
source_path = model.get_path(iter_source)
|
||||
print 'source iter path %s' % source_path
|
||||
destination_path, pos = treeview.get_dest_row_at_pos(x, y)
|
||||
print 'destination iter path %s' % destination_path
|
||||
|
||||
source_iter = model.get_iter(source_path)
|
||||
destination_iter = model.get_iter(destination_path)
|
||||
#model.remove(source_iter)
|
||||
print source_iter
|
||||
|
||||
print destination_iter
|
||||
print 'insert'
|
||||
model.move_after(source_iter, destination_iter)
|
||||
#self.append_tree(destination_iter)
|
||||
#~ if self.checkSanity(model, iter_to_copy, target_iter):
|
||||
#~ self.iterCopy(treeview, model, iter_to_copy, target_iter, pos)
|
||||
#~ drag_context.finish(gtk.TRUE, gtk.TRUE, eventtime)
|
||||
#~ treeview.expand_all()
|
||||
#~ else:
|
||||
#~ drag_context.finish(gtk.FALSE, gtk.FALSE, eventtime)
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with a largish tree
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
for item2 in range(0, 5):
|
||||
iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1)
|
||||
for item3 in range(0, 5):
|
||||
self.append_tree('Sub Sub Item ' + str(item3), iter_level_2)
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.16.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.0"/>
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkEntryBuffer" id="entrybuffer1"/>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row1</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row2</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Row3</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window1">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1"/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="input_purpose">alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</interface>
|
|
@ -0,0 +1,168 @@
|
|||
#!/usr/bin/env python
|
||||
# [SNIPPET_NAME: gtk3 treeview using a treestore]
|
||||
# [SNIPPET_CATEGORIES: gtk3]
|
||||
# [SNIPPET_TAGS: widgets, gtk3]
|
||||
# [SNIPPET_DESCRIPTION: GTK3 treeview in treestore, show the data in a expandable hierarchy]
|
||||
# [SNIPPET_AUTHOR: Oliver Marks ]
|
||||
# [SNIPPET_LICENSE: GPL]
|
||||
from gi.repository import Gdk, Gtk, GLib
|
||||
|
||||
#http://jason.zwolak.org/technoblog/2010/08/drag-and-drop-with-gtk2-and-perl/
|
||||
class application_gui:
|
||||
"""Tutorial 10 text input, display a treeview in expandable tree format."""
|
||||
count = 0
|
||||
|
||||
def __init__(self):
|
||||
#load in our glade interface
|
||||
xml = Gtk.Builder()
|
||||
xml.add_from_file('tut18.glade')
|
||||
|
||||
#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
|
||||
self.window = xml.get_object('window1')
|
||||
self.text = xml.get_object('entry1')
|
||||
|
||||
#load our widgets from the glade file
|
||||
self.widgets = {}
|
||||
self.widgets['treeview'] = xml.get_object('treeview1')
|
||||
treeview(self.widgets['treeview'], self.text)
|
||||
|
||||
|
||||
#connect to events, in this instance just quit our application
|
||||
self.window.connect('delete_event', Gtk.main_quit)
|
||||
self.window.connect('destroy', lambda quit: Gtk.main_quit())
|
||||
|
||||
#show the window else there is nothing to see :)
|
||||
self.window.show()
|
||||
|
||||
|
||||
class treeview:
|
||||
treeview = None
|
||||
treemodel = None
|
||||
|
||||
selected = 'workspace'
|
||||
|
||||
def __init__(self, treeview, entry):
|
||||
self.entry = entry
|
||||
self.treeview = treeview
|
||||
|
||||
self.treeview.connect('row-activated', self.selection)
|
||||
self.treeview.connect('button_press_event', self.mouse_click)
|
||||
|
||||
#self.fixed_area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.MOVE)
|
||||
self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.COPY)
|
||||
self.treeview.enable_model_drag_dest([("example", 0, 0)], Gdk.DragAction.COPY)
|
||||
#treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, [("example", 0, 0)], Gdk.DragAction.ACTION_COPY)
|
||||
#treeview.enable_model_drag_dest([("example", 0, 0)], gtk.gdk.ACTION_COPY)
|
||||
|
||||
self.treeview.connect("drag_data_received", self.onDragDataReceived)
|
||||
|
||||
|
||||
#create a storage model in this case a treemodel
|
||||
self.treemodel = Gtk.TreeStore(str)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
#add columns usually only one in case of the treeview
|
||||
column = Gtk.TreeViewColumn("Objects")
|
||||
self.treeview.append_column(column)
|
||||
|
||||
#add in a text renderer so we can see the items we add
|
||||
cell = Gtk.CellRendererText()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, "text", 0)
|
||||
|
||||
self.populate()
|
||||
self.menu()
|
||||
|
||||
#~ def iterCopy(self, treeview, model, iter_to_copy, target_iter, pos):
|
||||
#~
|
||||
#~ data_column_0 = model.get_value(iter_to_copy, 0)
|
||||
#~ data_column_1 = model.get_value(iter_to_copy, 1)
|
||||
#~ if (pos == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE) or (pos == gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
|
||||
#~ new_iter = model.prepend(target_iter, None)
|
||||
#~ elif pos == gtk.TREE_VIEW_DROP_BEFORE:
|
||||
#~ new_iter = model.insert_before(None, target_iter)
|
||||
#~ elif pos == gtk.TREE_VIEW_DROP_AFTER:
|
||||
#~ new_iter = model.insert_after(None, target_iter)
|
||||
#~ model.set_value(new_iter, 0, data_column_0)
|
||||
#~ model.set_value(new_iter, 1, data_column_1)
|
||||
#~ if model.iter_has_child(iter_to_copy):
|
||||
#~ for i in range(0, model.iter_n_children(iter_to_copy)):
|
||||
#~ next_iter_to_copy = model.iter_nth_child(iter_to_copy, i)
|
||||
#~ self.iterCopy(treeview, model, next_iter_to_copy, new_iter, gtk.TREE_VIEW_DROP_INTO_OR_BEFORE)
|
||||
|
||||
def walk(self, treeiter):
|
||||
if model.iter_has_child(treeiter):
|
||||
for i in range(0, model.iter_n_children(treeiter)):
|
||||
yield model.iter_nth_child(treeiter, i)
|
||||
|
||||
def onDragDataReceived(self, treeview, drag_context, x, y, selection, info, eventtime):
|
||||
"""on data recieve we remove the item and insert in the new location, the insert location must have the same depth"""
|
||||
model, iter_source = treeview.get_selection().get_selected()
|
||||
source_path = model.get_path(iter_source)
|
||||
print 'source iter path %s' % source_path
|
||||
destination_path, pos = treeview.get_dest_row_at_pos(x, y)
|
||||
print 'destination iter path %s' % destination_path
|
||||
|
||||
source_iter = model.get_iter(source_path)
|
||||
destination_iter = model.get_iter(destination_path)
|
||||
#model.remove(source_iter)
|
||||
print source_iter
|
||||
|
||||
print destination_iter
|
||||
print 'insert'
|
||||
model.move_after(source_iter, destination_iter)
|
||||
#self.append_tree(destination_iter)
|
||||
#~ if self.checkSanity(model, iter_to_copy, target_iter):
|
||||
#~ self.iterCopy(treeview, model, iter_to_copy, target_iter, pos)
|
||||
#~ drag_context.finish(gtk.TRUE, gtk.TRUE, eventtime)
|
||||
#~ treeview.expand_all()
|
||||
#~ else:
|
||||
#~ drag_context.finish(gtk.FALSE, gtk.FALSE, eventtime)
|
||||
|
||||
def populate(self):
|
||||
self.treemodel.clear()
|
||||
#populate the treeview with a largish tree
|
||||
for item1 in range(0, 5):
|
||||
iter_level_1 = self.append_tree('Item ' + str(item1))
|
||||
for item2 in range(0, 5):
|
||||
iter_level_2 = self.append_tree('Sub Item ' + str(item2), iter_level_1)
|
||||
for item3 in range(0, 5):
|
||||
self.append_tree('Sub Sub Item ' + str(item3), iter_level_2)
|
||||
|
||||
def append_tree(self, name, parent=None):
|
||||
"""
|
||||
append to the treeview if parent is null append to root level.
|
||||
if parent is a valid iter (possibly returned from previous append) then append under the parent
|
||||
"""
|
||||
myiter = self.treemodel.insert_after(parent, None)
|
||||
self.treemodel.set_value(myiter, 0, name)
|
||||
return myiter
|
||||
|
||||
def menu(self):
|
||||
"""
|
||||
popover menu shown on right clicking a treeview item.
|
||||
"""
|
||||
self.treeview_menu = Gtk.Menu()
|
||||
for item in range(0, 5):
|
||||
menu_item = Gtk.MenuItem("Menu " + str(item))
|
||||
self.treeview_menu.append(menu_item)
|
||||
|
||||
def mouse_click(self, tv, event):
|
||||
if event.button == 3:
|
||||
# right mouse button pressed popup the menu
|
||||
self.treeview_menu.show_all()
|
||||
self.treeview_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def selection(self, tv, treepath, tvcolumn):
|
||||
"""
|
||||
on double click get the value of the item we clicked
|
||||
"""
|
||||
model = tv.get_model()
|
||||
treeiter = model.get_iter(treepath)
|
||||
self.selected = model.get_value(treeiter, 0)
|
||||
self.entry.set_text(self.selected)
|
||||
|
||||
|
||||
|
||||
application = application_gui()
|
||||
Gtk.main()
|
|
@ -0,0 +1,44 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: Display a GTK window
|
||||
.. slug: displaying a gtk window
|
||||
.. date: 2014-01-05 12:00:00 UTC
|
||||
.. tags: GTK-3, python, window
|
||||
.. category: python
|
||||
.. description: GTK Example on loading a window from a glade fiel and displaying it on the screen.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#append this to module path so we can load from the parent directory
|
||||
import os
|
||||
import sys
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load
|
||||
|
||||
|
||||
class page:
|
||||
name = __name__
|
||||
title = 'Display a GTK3 window'
|
||||
tags = 'GTK3', 'Python', 'window'
|
||||
intro = 'GTK Example on loading a window from a glade fiel and displaying it on the screen.'
|
||||
date = '2014-01-05'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut01-windows.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.paragraph.render()
|
||||
).append(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""Simple GTK application load a window from a glade file and exit on hitting the window close button, we use the get_object method to get the window by name then use connect to attach to the close and destroy events.""")
|
||||
web.paragraph.append('There are lots of event we can connect to like mouse click key press and window minimise maximise check the gtk docs or glade for a list of possible event for each widget.')
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut01/tut01-windows.py')))
|
||||
web.page.section(web.pre.render())
|
|
@ -0,0 +1,37 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Treeview in treestore mode
|
||||
.. slug: 10-treeview-treestore-mode
|
||||
.. date: 2014-04-14 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying a collection of values in a treeview using the treestore hierarchy.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Treeview in tree mode
|
||||
[[../../../images/gtk/tut10-treeview-treestore.png]]
|
||||
|
||||
|
||||
class page:
|
||||
name = __name__
|
||||
title = 'GTK3 Treeview in treestore mode'
|
||||
tags = 'GTK3', 'Python', 'treestore', 'treeview'
|
||||
intro = ''
|
||||
date = '2014-04-14'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + 'gtk/tut10-treeview-treestore.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create("""The code below will populate the treeview widget with a tree like structure which you can expand and collapse, double clicking will retrieve the selected node in the tree. """)
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut10/tut10-treeview-treestore.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 textview interaction'
|
||||
tags = 'GTK3', 'Python', 'textview', 'interactive', 'cairo'
|
||||
intro = 'Load a glade file displaying a textview widget which runs python code on carriage return.'
|
||||
date = '2014-04-18'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut11-interactive-python-textview.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create("""This example shows using the text view widget to get text entered by a use, it makes use of
|
||||
marked text blocks so that only some text can be edited and will dynamically read the current line and run it in python interactively.
|
||||
You can type app.test() or app.show_drawing(True) as examples of the interaction between the widget and python.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut11/tut11-interactive-python-textview.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 Displaying OpenGL inside a drawing area'
|
||||
tags = 'GTK3', 'Python', 'opengl', 'drawingarea'
|
||||
intro = 'Load a glade file displaying a drawing area widget and setup a context for opengl.'
|
||||
date = '2014-04-20'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut12-mixing-opengl-with-gtk.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""Example of rendering opengl inside a gtk drawing area. I have seperated out the opengl code from the main gtk widget this makes it simpler to use in your own applications. """)
|
||||
|
||||
|
||||
web.paragraph.append('The example below will draw a basic opengl triangle so you know everything is setup and working, it also attaches to the drawing area resize events so you can resize the window.')
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut12/tut12-mixing-opengl-with-gtk.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Example downloader using a listbox
|
||||
.. slug: 13-example-list-box-downloader
|
||||
.. date: 2014-05-01 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, application
|
||||
.. category: python
|
||||
.. description: Populate a listbox with custom widgets, in this case an example file downloader could be the start of a plugin installer.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: File downloader
|
||||
[[../../../images/gtk/tut12-listbox.png]]
|
||||
|
||||
|
||||
A slightly more complex example, loading our gui from a glade file and dynamically add and remove widgets based on an xml file.
|
||||
|
||||
This is the start of a simple file downloader, it reads an xml file and creates a gui dynamically with download buttons for each element in the xml data to retrieve files.
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut13/tut13-listbox.py')))
|
||||
web.page.section(web.pre.render())
|
|
@ -0,0 +1,32 @@
|
|||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 Touchscreen events'
|
||||
tags = 'GTK3', 'Python', 'listbox', 'drawingarea', 'touch'
|
||||
intro = 'Example of catching touch screen events and filtering device type, helpful for making touch friendly gtk apps.'
|
||||
date = '2014-05-20'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut14-touchscreen-events.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""The example below demonstrates filtering events for a specific device, in this case a touch screen we then capture touch begin update and end events.
|
||||
To my knowledge gestures are not supports so you will need to count the begins to determine the number of fingers and handle the updates to determine whats happening""")
|
||||
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut14/tut14-touchscreen-events.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 Simple opengl app with touch events
|
||||
.. slug: 15-gtk3-opengl-touch-events
|
||||
.. date: 2014-06-10 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, touchscreen, opengl
|
||||
.. category: python
|
||||
.. description: Example application mixing gtk, opengl and touch, written as a demo but also as an opengl testing enviroment.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
#+CAPTION: Opengl touch events
|
||||
[[../../../images/gtk/tut14-touchscreen-events.png]]
|
||||
|
||||
|
||||
|
||||
"""This is a much more complicated example which mashes some of the previous examples together, I have written it as an example but also to test out shaders and different pipelines to get better opengl performance.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.paragraph.create(
|
||||
"""Hopefully this example will help you get started writing awesome touch based apps with gtk, if anything is unclear let me know in the comments below.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.link.create('Launchpad','OpenGL in a drawing area','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/')
|
||||
web.link.create('Launchpad','Touch screen events','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/')
|
||||
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut15/tut15-opengl-touch-app-01.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
||||
web.paragraph.create(
|
||||
"""The file below is a simple helper to reduce the size of the files, it contains the shader and a point and cube class used in the main program above.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut15/helper.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: GTK-3 custom signals example
|
||||
.. slug: 16-gtk3-custom-signals-example
|
||||
.. date: 2014-09-11 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets, signals
|
||||
.. category: python
|
||||
.. description: Simple example on how to setup, connect to and trigger signals.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Opengl touch events
|
||||
[[../../../images/gtk/tut16-signals.png]]
|
||||
|
||||
|
||||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name = __name__
|
||||
title = 'GTK3 custom signals example'
|
||||
tags = 'GTK3', 'Python', 'signals'
|
||||
intro = 'Simple example on how to setup, connect to and trigger signals.'
|
||||
date = '2014-09-11'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut16-signals.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""In this program we create 4 custom signals, the first to are connected to and triggered on launch, the latter two are triggered when you click the buttons.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.paragraph.create(
|
||||
"""We can create new signals using GObject.signal_new, we can then connect the new signal and callback as we normally would using widget .connect methods as normal.
|
||||
To trigger our custom event we use the .emit() method, this method also aalows us to fake events, for example we might want to fake the user clicking a button.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.link.create('Launchpad','Gtk signals','http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/gtk3/')
|
||||
|
||||
print self.files
|
||||
web.pre.create(load(self.files + '/gtk3/tut16/tut16-signals.py'))
|
||||
web.page.section(web.pre.render())
|
|
@ -0,0 +1,39 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: Adding GTK-3 buttons and switches
|
||||
.. slug: 02-adding-buttons-and-switches
|
||||
.. date: 2014-01-08 12:00:00 UTC
|
||||
.. tags: GTK-3, python, glade
|
||||
.. category: python
|
||||
.. description: GTK Example on click and toggle buttons connect to there events to run code when activated by the user.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Display GTK-3 Buttons and switches
|
||||
[[../../../images/gtk/tut02-buttons.png]]
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = ''
|
||||
tags = 'GTK3', 'Python', 'window', 'button', 'toggle'
|
||||
intro = 'GTK Example on click and toggle buttons connect to there events to run code when activated by the user.'
|
||||
date = '2014-01-08'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut02-buttons.png'
|
||||
def create_content(self):
|
||||
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""The example below loads 4 buttons from a glade 2 standard and 2 toggle buttons, it then connects event handles to show some text when clicked or to show the toggle state if a toggle button was clicked.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut02/tut02-buttons.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: Adding switch and radio widgets
|
||||
.. slug: 03-adding-radio-buttons-and-switches
|
||||
.. date: 2014-01-10 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: GTK example demonstrating the use of switches and radio buttons.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Adding radio buttons
|
||||
[[../../../images/gtk/tut03-switch-radio.png]]
|
||||
|
||||
|
||||
The below sample shows loading and retrieving state values for radio buttons and switches.
|
||||
|
||||
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut03/tut03-switch-radio.py')))
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 Dropdowns and spin buttons'
|
||||
tags = 'GTK3', 'Python', 'window', 'dropdown', 'select', 'spin'
|
||||
intro = 'GTK example of some combo / slecet drop down boxesfor user interaction.'
|
||||
date = '2014-01-15'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut04-text-dropdown-spin.png'
|
||||
|
||||
def create_content(self):
|
||||
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""This example demonstrates using some drop down boxes and adding new items and retrieving the selected values.
|
||||
This snippet also demonstrates the use of spin buttons for selecting a value from a range.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.paragraph.create(
|
||||
"""When creating a combobbox in glade make sure you add a liststore to the widget, and also edit the combobox properties in a seperate window so you can access and the hierarchy menu and assign a cell render to the column in your listview.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.paragraph.create(web.images.create(web.template.path_image + 'gtk/tut04/add-list-store.png', 'Windows 8.1 partition screenshot').render())
|
||||
web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/attach-cell-renderer-set-text-column.png', 'Windows 8.1 partition screenshot').render())
|
||||
web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/attach-liststore-model.png', 'Windows 8.1 partition screenshot').render())
|
||||
web.paragraph.append(web.images.create(web.template.path_image + 'gtk/tut04/edit-combobox-liststore.png', 'Windows 8.1 partition screenshot').render())
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut04/tut04-text-dropdown-spin.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: Adding progress bars & spinners
|
||||
.. slug: 05-adding-progress-bars-and-spinners
|
||||
.. date: 2014-01-20 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: GTK example of some combo / slecet drop down boxesfor user interaction.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Adding dropdown & spin widgets
|
||||
[[../../../images/gtk/tut04-text-dropdown-spin.png]]
|
||||
|
||||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 progress bar and spinner'
|
||||
tags = 'GTK3', 'Python', 'progress', 'spinner'
|
||||
intro = 'Load a progress bar widget and spinner to demonstrate updating and retriving values.'
|
||||
date = '2014-01-20'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut05-progress-spinners.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
web.paragraph.create(
|
||||
"""This sample demonstrates two progress bar styles and how to update them too show progress, it also demonstrates starting and stopping a spinning graphic.""")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut05/tut05-progress-spinners.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: Displaying dialog boxes
|
||||
.. slug: 06-dialog-boxes
|
||||
.. date: 2014-02-03 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Demonstration of opening various dialog boxes and retrieving the selected values.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Progress bars and spinners
|
||||
[[../../../images/gtk/tut06-dialogs.png]]
|
||||
|
||||
|
||||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 Dialog boxes'
|
||||
tags = 'GTK3', 'Python', 'dialog', 'colour', 'fonts', 'files'
|
||||
intro = ''
|
||||
date = '2014-02-03'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut06-dialogs.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""This example program allows you to open the file selector dialogs and displays the selected files, it also demonstrates selecting colour and font from the built in selector dialogs.""")
|
||||
web.paragraph.append("once a file font or colour has been selected we grab the resulting value and display it as an example of retrieving the value.")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut06/tut06-dialogs.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#+BEGIN_COMMENT
|
||||
.. title: App chooer and scale buttons
|
||||
.. slug: 07-app-chooser-scale-button
|
||||
.. date: 2014-02-07 12:00:00 UTC
|
||||
.. tags: GTK-3, python, widgets
|
||||
.. category: python
|
||||
.. description: Load a glade file displaying some scale buttons get value on change, displays some app selector widgets and open the applications.
|
||||
.. type: text
|
||||
#+END_COMMENT
|
||||
|
||||
|
||||
#+CAPTION: Dialog Boxes
|
||||
[[../../../images/gtk/tut07-appchooser-scale.png]]
|
||||
|
||||
#append this to module path so we can load from the parent directory
|
||||
import sys, os
|
||||
from scaffold import web
|
||||
from scaffold.loaders import load, load_partial
|
||||
|
||||
|
||||
class page:
|
||||
name=__name__
|
||||
title = 'GTK3 App Chooser and scale buttons'
|
||||
tags = 'GTK3', 'Python', 'scale' , 'appchooser'
|
||||
intro = ''
|
||||
date = '2014-02-07'
|
||||
path = os.path.abspath('./')
|
||||
image = web.template.path_image + '/gtk/tut07-appchooser-scale.png'
|
||||
|
||||
def create_content(self):
|
||||
web.images.create(self.image, title='GTK Window')
|
||||
web.page.section(
|
||||
web.div.create(
|
||||
web.images.render()
|
||||
).set_classes('sources').render()
|
||||
)
|
||||
|
||||
web.paragraph.create(
|
||||
"""This example program demonstrates the use of appchooser buttons when selecting an application from the drop down launch the application loading a test text file, this could be a video or mp3 or any file type.""")
|
||||
web.paragraph.append("you can change the application list by modify the content type value in glade this then shows all registered apps for that content type.")
|
||||
web.page.section(web.paragraph.render())
|
||||
|
||||
web.pre.create(load(os.path.abspath('./content/gtk3/tut07/tut07-appchooser-scale.py')))
|
||||
web.page.section(web.pre.render())
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue