Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 04, 2019 9:00 pm

Hi there

TL: DR: Is it possible to make interface like the pictures, use tab, enter and F-keys to select, play audio, and have it start at boot?

So, for a gift I am recreating a moment from Wrongfully Accused. This involves a the interaction with a hospital computer.
Unfortunately, I have no experience with Python, and even less with making GUIs. I understand you can do quite a bit with Tkinter and have tried following a few examples, I even found this a possible solution for “changing the screens” at https://stackoverflow.com/questions/754 ... in-tkinter.
It is fairly basic, text boxes that require correct text to proceed to next screen. If possible, also play audio. The scene I would like to recreate (well the interaction with the computer anyway) can be seen here https://youtu.be/rsk517WH4XE?t=247

Anyways, it might be easier just to walk you through my desired product with these pictures I have made for planning. See all at https://imgur.com/a/6D7iEdL

Image
So, it is a very simple setup, with minimal graphics at the top. Following the movie’s plot the login is of course “login” and likewise for password with “password” So boxes for entering text, switch between the two with Tab ↹ key, and enter to submit the login and password. Upon entering correctly on to next screen.

Image
Here I would like a very cheesy “main menu” audio file to be played. F keys to select, green box around selected option, and I think enter to proceed.

Image
Again, green box on selected option “SEARCH”, this provides the “KEYWORD SEARCH” at the bottom. Plays audio file of computer saying “searching” Upon entering the correct search “ONE EYED, ONE ARMED, ONE LEGGED GUY” and hitting enter, it will proceed to the next screen.

Image
Here “SEAN LAUGHREA” must be entered in “PATIENT :” and the press F2 for print, before proceeding to next screen. Plays audio file.

Image
Plays audio file. Here correct answer is “YES”

Image
Plays audio file. Again “YES”.

Image
Plays audio file. And the final answer is “JUST PRINT!”

Image
I have taken the liberty to censor the last screen. Also plays audio file here. (Dream scenario would be this final screen also starts a Arduino program, but that is for another time)


Is anyone perhaps able to assist me in creating this silly interface? Is it even possible in python? Finally, I do apologise for the long post, and indeed if this is the wrong place to post.

Edit: for some reason images are not working, put in link to all images on Imgur.

User avatar
rpdom
Posts: 15168
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 04, 2019 9:42 pm

Sorry I can't help you with your project. I just want to say it sounds like fun. I watched Wrongfully Accused the other day. It was very silly :-)

Good luck!

Andyroo
Posts: 4465
Joined: Sat Jun 16, 2018 12:49 am
Location: Lincs U.K.

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 04, 2019 10:26 pm

The guys at the education arm of the Foundation are currently running a GUI training course at https://www.futurelearn.com/courses/pro ... -with-guis

Looks like it's free if you do not want a bit of paper and can do it in five weeks...
Need Pi spray - these things are breeding in my house...

User avatar
MrYsLab
Posts: 372
Joined: Mon Dec 15, 2014 7:14 pm
Location: Noo Joysey, USA

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 04, 2019 11:51 pm

I was going to recommend that you use guizero instead of tkinter (guizero is built on top of tkinter), but Andyroo's suggestion is a good one. I took a quick look and the course is using guizero. If you have the time, the course will probably give you all that you need, plus you can get some help if you should need it.

If you are in a hurry and can't take course, the guizero docs can be found here: https://lawsie.github.io/guizero/.

ghp
Posts: 1408
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Mon Aug 05, 2019 5:05 am

Block graphics can be build with "curses", a python library. But the large font for the 'company name' is possibly not supported.

User avatar
rpiMike
Posts: 901
Joined: Fri Aug 10, 2012 12:38 pm
Location: Cumbria, UK

Re: Old-school GUI - Possible? (Beware, silly purpose)

Mon Aug 05, 2019 6:15 am

Use the pygame library - it’s perfect for that type of full screen gui.

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Tue Aug 06, 2019 8:09 pm

All great suggestions, thank you. I will will look into them and give it a go

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Tue Aug 06, 2019 8:09 pm

rpdom wrote:
Sun Aug 04, 2019 9:42 pm
Sorry I can't help you with your project. I just want to say it sounds like fun. I watched Wrongfully Accused the other day. It was very silly :-)

Good luck!
That's more than enough for me :D

User avatar
Gavinmc42
Posts: 3715
Joined: Wed Aug 28, 2013 3:31 am

Re: Old-school GUI - Possible? (Beware, silly purpose)

Wed Aug 07, 2019 2:28 am

AJ Starks got some good looking stuff.
https://github.com/ajstarks
I have used this OpenVG for displays but not as a GUI yet.
C, Go and Pascal all worked.

I prefer the OpenVG method as it is easy to make it autoscale to any display.
Truetype/vector fonts look good at any size.
Messing about with bitmaps in Pygame takes much longer.

OpenVG also means you don't need x11, so you can run it on Lite versions of Linux.
I am not sure if there is a GUI toolset that uses OpenVG on Pi's?

Been doing lots of OpenGL on Pi 4's recently and FreeGLUT has mouse/keyboard interfaces.
Could it do a 2D text GUI?
Is it only x11 based?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

User avatar
Gavinmc42
Posts: 3715
Joined: Wed Aug 28, 2013 3:31 am

Re: Old-school GUI - Possible? (Beware, silly purpose)

Wed Aug 07, 2019 2:52 am

Just remembered a trick I used with Ncurses based Dialog menus.
If you tell the terminal it only has x, y rows, columns, then the fonts are displayed bigger.
Been years but I think I used something like a 20 x 15 terminal setting.

You could do it all with shell script and dialog.
Shell script can be very powerful and it is as old school as it gets.
Alternatives to Dialog?
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Wed Aug 07, 2019 3:16 pm

Gavinmc42 wrote:
Wed Aug 07, 2019 2:52 am
Just remembered a trick I used with Ncurses based Dialog menus.
If you tell the terminal it only has x, y rows, columns, then the fonts are displayed bigger.
Been years but I think I used something like a 20 x 15 terminal setting.

You could do it all with shell script and dialog.
Shell script can be very powerful and it is as old school as it gets.
Alternatives to Dialog?
Oh old school was meerly the look, I'm all for easy peasy solution to reach that goal :D

User avatar
Gavinmc42
Posts: 3715
Joined: Wed Aug 28, 2013 3:31 am

Re: Old-school GUI - Possible? (Beware, silly purpose)

Thu Aug 08, 2019 12:37 am

Oh old school was meerly the look, I'm all for easy peasy solution to reach that goal
The look is easy, the GUI less so.
I have done SteamPunk and LCARS looks but don't have a UI.
This sort of stuff is in QT5's arena but who wants to learn that?

If you find an easy peasy solution let us know.
Lots of GUI tools require x11, don't need that for simple stuff.

Some people use Kivy
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

ghp
Posts: 1408
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sat Aug 10, 2019 3:21 pm

still looking for technologies or solutions ?
Screenshot_2019-08-10.png
Screenshot_2019-08-10.png (4.42 KiB) Viewed 2537 times
produced in html, using embedded svg. The generating code is python in tornado simulating block graphics. The input field is embedded html input field.

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sat Aug 10, 2019 10:22 pm

ghp wrote:
Sat Aug 10, 2019 3:21 pm
still looking for technologies or solutions ?
I am yes, been on and off with all the suggested solutions. I had though about html as I can remember a litlle (not much) Could you elaborate on python in tornado simulating block graphics?

ghp
Posts: 1408
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 11, 2019 12:38 pm

svg is handy to produce lines, blocks, text.
svg can be included in html.
<html><body><svg>...elements...</svg></body></html>
In tornado, the Handler methods can produce svg on the fly, using Elementtree and then convert to string and paste into the html.
Assume a set of methods which build labels, input fields, borders, all using block coodinates x[0..39], y[0..19] and internally converting these to pixel coordinates.
The F1,F2,..,Enter keys can be captured with javascript event handler.
The input field is tricky, as svg does not provide something appropriate. Embedded Elements allow to use html input there. Input field text is appended to server requests.
Writing text is complicated too. Aligning monospaced font to box sizes does not work really good. So print one char after the other into single text elements is a solution.
Tornado provides the web things, url handling, request to handler method mapping and providing the parameters.
There some things not working good:
- input field text baseline is too low, glyphs like "gyp" are cut off at the moment.
- And of course a password field instead of input would be appropriate.
- the element styles are taken out from inkscape output and possibly too many are provided.

Code: Select all

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Sample code for a block graphics monitor display
#
# prerequisites
#
#   install tornado 'sudo pip3 install tornado'
#                                                                                        
import tornado.ioloop
import tornado.web

import xml.etree.ElementTree as ET

""" switch debug messages on and make background squared"""
debug = False
scale = 4.0

class SVG_Block_Handler (tornado.web.RequestHandler):
    """ Provides SVG helper methods"""
    
    def __init__(self, application, request, **kwargs):
        tornado.web.RequestHandler.__init__(self, application, request, **kwargs)
        
        """ Width of screen in Blocks """
        self.N_WIDTH  = 40
        """ Height of screen in Blocks"""
        self.N_HEIGHT = 20
        
        """ Pixel sizes for single Blocks"""
        self.N_X_BOX = 5
        self.N_Y_BOX = 8
        
        self.bg_style = ";".join( [
                   "display:inline",
                   "overflow:visible",
                   "visibility:visible",
                   "fill:#000000",
                   "fill-rule:evenodd",
                   "marker:none",
                   "enable-background:accumulate",
                   "fill-opacity:1" ])
        
        self.bg_light_style = ";".join( [
                   "display:inline",
                   "overflow:visible",
                   "visibility:visible",
                   "fill:#444444",
                   "fill-rule:evenodd",
                   "marker:none",
                   "enable-background:accumulate",
                   "fill-opacity:1" ])
        
        self.font_family = "'Lucida Console',monospace,sans-serif"
         
        """ text single line height text"""   
        self.text_style= ";".join( [
                   "font-style:normal",
                   "font-variant:normal",
                   "font-weight:normal",
                   "font-stretch:normal",
                   "font-size:{font_size:d}px".format( font_size=self.N_Y_BOX),
                   "line-height:25px",
                   "font-family:{ff:s}".format(ff=self.font_family),
                   "font-variant-ligatures:normal",
                   "font-variant-caps:normal",
                   "font-variant-numeric:normal",
                   "font-feature-settings:normal",
                   "text-align:start",
                   "letter-spacing:0px",
                   "word-spacing:0px",
                   "writing-mode:lr-tb",
                   "text-anchor:start",
                   "fill:#3e86caff",
                   "fill-opacity:1",
                   "stroke:none",
                   "stroke-width:1px",
                   "stroke-linecap:butt",
                   "stroke-linejoin:miter",
                   "stroke-opacity:1",
            ] )
        """ text double line height"""
        self.text_2_style = ";".join( [
                   "font-style:normal",
                   "font-variant:normal",
                   "font-weight:bold",
                   "font-stretch:normal",
                   "font-size:{font_size:d}px".format( font_size=self.N_Y_BOX*2),
                   
                   "line-height:25px",
                   
                   "font-family:{ff:s}".format(ff=self.font_family),
                   "font-variant-ligatures:normal",
                   "font-variant-caps:normal",
                   "font-variant-numeric:normal",
                   "font-feature-settings:normal",
                   
                   "text-align:start",
                   
                   "letter-spacing:0px",
                   "word-spacing:0px",
                   "writing-mode:lr-tb",
                   "text-anchor:start",
                   "fill:#3e86caff",
                   "fill-opacity:1",
                   "stroke:none",
                   "stroke-width:1px",
                   "stroke-linecap:butt",
                   "stroke-linejoin:miter",
                   "stroke-opacity:1",
            ] )
        
        
        self.editable_text_style = ";".join( 
                  ["font-style:normal",
                   "font-variant:normal",
                   "font-weight:normal",
                   "font-stretch:normal",
                   "font-size:{font_size:d}px".format( font_size=self.N_Y_BOX),
                   "font-family:{ff:s}".format(ff=self.font_family),
                   "font-variant-ligatures:normal",
                   "font-variant-caps:normal",
                   "font-variant-numeric:normal",
                   "font-feature-settings:normal",
                   
                   "text-align:start",
                   "letter-spacing:0px",
                   "line-height:25px",
                   "word-spacing:0px",
                   "writing-mode:lr-tb",
                   "text-anchor:start",
                   
                   "fill:#ffffffff",
                   "fill-opacity:1",
                   
                   "stroke:none",
                   "stroke-width:1px",
                   "stroke-linecap:butt",
                   "stroke-linejoin:miter",
                   "stroke-opacity:1",] ) 
        

    def create_frame(self ):
        width = self.N_WIDTH * self.N_X_BOX
        height = self.N_HEIGHT * self.N_Y_BOX
        
        svg = ET.Element('svg')
        svg.set('x', '0')
        svg.set('y', '0')
        # increase size by 4
        svg.set('height', str( height*scale))
        svg.set('width', str( width*scale ))
        svg.set('viewbox', "{x:f} {y:f} {width:f} {height:f}".format(x=0, y=0, width=width, height=height)) 

        return svg
    
    def create_background(self, g):    
        if debug:
            # checker board style background
            for x in range( self.N_WIDTH ):
                for y in range( self.N_HEIGHT):
                    block  = ET.SubElement(g, 'rect')
                    block.set('x', str( x * self.N_X_BOX ))
                    block.set('y', str( y * self.N_Y_BOX ))
                    block.set('height', str(self.N_Y_BOX ))
                    block.set('width', str(  self.N_X_BOX ))
                    
                    if (x + y) %2 == 0:
                        block.set("style", self.bg_style)
                    else:
                        block.set("style", self.bg_light_style)
        else:
            width = self.N_WIDTH * self.N_X_BOX
            height = self.N_HEIGHT * self.N_Y_BOX
            
            frame = ET.SubElement(g, 'rect')
            frame.set('x', '0')
            frame.set('y', '0')
            frame.set('height', str(height))
            frame.set('width', str(width))
            
            frame.set('style', self.bg_style)

    
    def createBox(self, parent, nX, nY, nWidth, nHeight, width = 4.):
        """ create a line box """
        
        assert 0 < width < self.N_X_BOX
        assert 0 < width < self.N_Y_BOX
        
        half_a_block_x = self.N_X_BOX /2.
        half_a_block_y = self.N_Y_BOX /2.
        points = "M{p0x:f},{p0y:f} L{p1x:f},{p1y:f} L{p2x:f},{p2y:f} L{p3x:f},{p3y:f} Z".format( 
                                     p0x=(nX +     0) * self.N_X_BOX + half_a_block_x, p0y=(nY +       0) * self.N_Y_BOX + half_a_block_y,
                                     p1x=(nX +nWidth) * self.N_X_BOX - half_a_block_x, p1y=(nY +       0) * self.N_Y_BOX + half_a_block_y,
                                     p2x=(nX +nWidth) * self.N_X_BOX - half_a_block_x, p2y=(nY + nHeight) * self.N_Y_BOX - half_a_block_y,
                                     p3x=(nX +     0) * self.N_X_BOX + half_a_block_x, p3y=(nY + nHeight) * self.N_Y_BOX - half_a_block_y
                                     )
        style = "stroke-width:{w:f}px;stroke:#3e86caff;fill:none;".format(w=width)
        
        if debug:
            print(points)
            print(style)
        
        frame = ET.SubElement(parent, 'path')
        frame.set('d', points)
        frame.set('style', style)
        
    def blockLine(self, parent, nX,nY, nWidth):
        """create a single line, one block height"""
        elemnt = ET.SubElement(parent, 'rect')
        elemnt.set('x', str( nX * MyFormHandler.N_X_BOX))
        elemnt.set('y', str(nY * MyFormHandler.N_Y_BOX))
            
        elemnt.set("width", str( nWidth * MyFormHandler.N_X_BOX))
        elemnt.set("height", str( MyFormHandler.N_Y_BOX))
        elemnt.set("style", "fill:#3e86caff;")
        
    def label(self, parent, nx, ny, text_):
        """label single sized"""
        return self._label( parent, nx, ny, text_, 1)
                
    def label2(self, parent, nx, ny, text_):
        """label double sized"""
        return self._label( parent, nx, ny, text_, 2)

    def _label(self, parent, nx, ny, text_, scale=1):
        """label any sized"""
        elemnt =  ET.SubElement(parent, 'text')
        elemnt.set('style', self.text_style)
        
        ## real block text
        i = 0
        for c in text_:
            elemnt =  ET.SubElement(parent, 'text')
            if scale == 1:
                elemnt.set('style', self.text_style)
            elif scale == 2:
                elemnt.set('style', self.text_2_style)
            else:
                print("scale ??")
                elemnt.set('style', self.text_style)
                    
            elemnt.set('x', str( (nx + i) * self.N_X_BOX))
            # adjust for baseline offset
            elemnt.set('y', str( (ny +1)* self.N_Y_BOX  - 2 * scale))

            i += scale
    
            tspan = ET.SubElement(elemnt, 'tspan')
            # tspan.set('style"', tspan_style)
            tspan.text = c 
        return elemnt
    
            
    def input(self, parent, nx, ny, nwidth, id_):
        """produce an input field with an embedded html element"""
        ipf = ET.SubElement(parent, 'foreignObject') 
        ipf.set( 'x', str( nx*self.N_X_BOX))
        ipf.set( "y", str( ny*self.N_Y_BOX))
        ipf.set( "width", str( nwidth*self.N_X_BOX))
        ipf.set( "height", str( 1*self.N_Y_BOX))
        
        
        div = ET.SubElement( ipf, "{http://www.w3.org/1999/xhtml}div")
        input_ = ET.SubElement( div, "input")
        input_.set("style", ";".join( ["color:#000000ff",
                                      "background-color:#3e86caff",
                                      "font-size:{fs:d}px;".format(fs=self.N_Y_BOX),
                                      "border:0",
                                      "font-family:{ff:s}".format(ff=self.font_family) ]
                                   ) )
        input_.set("id", id_)
        return ipf 

    def javascript_events(self, events, input_fields = []):
        
        js = """
                            document.addEventListener('keydown', (event) => {
                                  const keyName = event.key;
                                
                                if (keyName === 'Control') {
                                    // not alert when only Control key is pressed.
                                    return;
                                  }
                                
                                  if (event.ctrlKey) {
                                    // Even though event.key is not 'Control' (i.e. 'a' is pressed),
                                    // event.ctrlKey may be true if Ctrl key is pressed at the time.
                                    
                                    // alert(`Combination of ctrlKey + ${keyName}`);
                                  } else {
          """
        if len(input_fields ) > 0:
            """there are input fields, query and add to request"""
            for i in input_fields:
                js += """         const ret = [];
                                  {
                                      var input_field = document.getElementById('${id}');
                                      var value = input_field.value;
                                      ret.push(encodeURIComponent('${id}') + 
                                               '=' + encodeURIComponent(value));
                                  }
                      """  .replace('${id}', i)
            
            for e in events:
                js += """               if ( '${key}' == keyName)
                                        {
                                             window.location = '${url}' + '?' + ret.join('&');  
                                        }
                    """.replace('${key}', e) \
                       .replace('${url}',  events[e])
        else:
            
            for e in events:
                js += """               if ( '${key}' == keyName)
                                        {
                                             window.location = '${url}';  
                                        }
                    """.replace('${key}', e) \
                       .replace('${url}',  events[e])
                       
        js += """               }
                                }, false);
        """
        return js

class MyLogin_Handler(tornado.web.RequestHandler):
    """not producing any page, but just checking login"""
    
    def __init__(self, application, request, **kwargs):
        tornado.web.RequestHandler.__init__(self, application, request, **kwargs)
    
    def get(self):
        print("MyLogin_Handler.get()")
        
        input_field_value = self.get_arguments("myid")
        if input_field_value == []:
            # Handle me
            self.set_status(400)
            return self.finish("missing field value")
        
        if input_field_value[0] == 'ABC':
            self.redirect("/login_success")

        else:
            print("give ABC")
            self.redirect("/")

class MyApplicationStart_Handler(SVG_Block_Handler):  
    
    def __init__(self, application, request, **kwargs):
        SVG_Block_Handler.__init__(self, application, request, **kwargs)
    
    def get(self):
        print("MyApplicationStart_Handler.get()")

        svg = self.create_frame( )
        
        g = ET.SubElement(svg, 'g')
        self.create_background(g)
            
        box = self.createBox(g, 0, 0, self.N_WIDTH, self.N_HEIGHT-1, width=2)
        
        self.label2( g, 5,  3, "here starts the fun")
        self.label ( g,  2, self.N_HEIGHT-1, "F1 Exit")
        self.label ( g, 12, self.N_HEIGHT-1, "F5 Menu")

        if debug: print(svg)
        svg_text = ET.tostring(svg, encoding='unicode')
        
        events = {  
                    "F1": "/",
                 }
        
        javascript = """        'use strict'; 
                     """
                     
        javascript += self.javascript_events( events)
        
        html = """<html> <body> 
                           <script>
                               ${javascript}
                           </script>
                           ${svg}
                           </body>
                      </html>""".replace( "${svg}"        , svg_text ) \
                                .replace( "${javascript}" , javascript)
                      
        self.write( html) 
        
class MyApplicationERROR_Handler(SVG_Block_Handler):  
    
    def __init__(self, application, request, **kwargs):
        SVG_Block_Handler.__init__(self, application, request, **kwargs)
    
    def get(self):
        print("MyApplicationERROR_Handler.get()")

        svg = self.create_frame( )
        
        g = ET.SubElement(svg, 'g')
        self.create_background(g)
            
        box = self.createBox(g, 0, 0, self.N_WIDTH, self.N_HEIGHT-1, width=1)
        
        self.label2( g, 5,  3, "UNEXPECTED ERROR OCCURRERD")
        
        self.label ( g,  2, self.N_HEIGHT-1, "F1 Exit")
        self.label ( g, 12, self.N_HEIGHT-1, "F5 Menu")

        if debug: print(svg)
        svg_text = ET.tostring(svg, encoding='unicode')
        
        events = {  
                    "F1": "/",
                    "F5": "/",
                 }
        
        javascript = """        'use strict'; 
                     """
                     
        javascript += self.javascript_events( events)
        
        html = """<html> <body> 
                           <script>
                               ${javascript}
                           </script>
                           ${svg}
                           </body>
                      </html>""".replace( "${svg}"        , svg_text ) \
                                .replace( "${javascript}" , javascript)
                      
        self.write( html) 
          
class MyFormHandler(SVG_Block_Handler):
    def __init__(self, application, request, **kwargs):
        SVG_Block_Handler.__init__(self, application, request, **kwargs)
    
               
    def get(self):
        print("MyFormHandler.get()")
        
        svg = self.create_frame( )
        
        g = ET.SubElement(svg, 'g')
        self.create_background(g)
            
        box = self.createBox(g, 5, 5, 20, 10)
     
        self.label2( g, 5,  3, "COMPANY DOUBLE")
        self.label ( g, 5,  4, "consultants")
        self.label ( g, 10, 10, "LOgIN:")
        self.label ( g,  2, self.N_HEIGHT-1, "F1 Exit")
        self.label ( g, 12, self.N_HEIGHT-1, "F5 Menu")
        
        self.input(g, 16, 10, 10, "myid")
        
        if debug: print(svg)
        svg_text = ET.tostring(svg, encoding='unicode')
        
                 
        if debug: print("svg_text", svg_text)
        
        events = {  
                    "F1": "/F1_shortcut",
                    "F5": "/F5_shortcut",
                    
                    "Enter": "/login",
                 }
        
        javascript = """        'use strict'; 
                     """
                     
        javascript += self.javascript_events( events, [ 'myid' ] )
        
        html = """<html> <body> 
                           <script>
                               ${javascript}
                           </script>
                           ${svg}
                           </body>
                      </html>""".replace( "${svg}"        , svg_text ) \
                                .replace( "${javascript}" , javascript)
                      
        self.write( html) 

def make_app():
    return tornado.web.Application([
        ( r"/"             , MyFormHandler),
        ( r"/myform"       , MyFormHandler),
        
        ( r"/login"        , MyLogin_Handler), 
        ( r"/login_success", MyApplicationStart_Handler), 
        
        ( r"/F1_shortcut"  , MyApplicationERROR_Handler), 
        ( r"/F5_shortcut"  , MyApplicationERROR_Handler), 
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

knute
Posts: 472
Joined: Thu Oct 23, 2014 12:14 am
Location: Texas
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sun Aug 11, 2019 3:50 pm

This is a perfect job for Java. Graphics are easy and so is the logic. This is down and dirty with no attempt to streamline.

Code: Select all

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class McCarty extends JFrame {
    public McCarty() {
        setLayout(new GridBagLayout());
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        getContentPane().setBackground(Color.BLACK);
        add(new mainMenu());
        setSize(800,600);
        setVisible(true);
    }

    public class mainMenu extends JPanel {
        private int sel = -1;
        public mainMenu() {
            super(new GridBagLayout());
            setBackground(Color.BLACK);

            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(0,40,0,40);
            c.gridx = 0;
            c.weightx = 1.0;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.anchor = GridBagConstraints.CENTER;

            JPanel p1 = new JPanel(new GridBagLayout());
            p1.setBackground(new Color(0x3e86ca));

            JLabel l1 = new JLabel("McCarty",JLabel.CENTER);
            l1.setFont(new Font(Font.SANS_SERIF,Font.BOLD,80));
            l1.setForeground(Color.BLACK);
            p1.add(l1,c);
            JLabel l2 = new JLabel("H  O  S  P  I  T  A  L",JLabel.CENTER);
            l2.setFont(new Font(Font.SANS_SERIF,Font.BOLD,40));
            l2.setForeground(Color.BLACK);
            p1.add(l2,c);

            c.gridy = 0;
            add(p1,c);

            ++c.gridy;
            c.insets = new Insets(10,10,10,10);
            c.fill = GridBagConstraints.NONE;
            JLabel l3 = new JLabel("MAIN MENU",JLabel.CENTER);
            l3.setOpaque(true);
            l3.setFont(new Font(Font.SANS_SERIF,Font.BOLD,50));
            l3.setBackground(new Color(0x3e86ca));
            l3.setForeground(Color.BLACK);
            add(l3,c);
            
            c.gridy = GridBagConstraints.RELATIVE;
            c.anchor = GridBagConstraints.WEST;
            JPanel p2 = new JPanel(new GridBagLayout());
            p2.setFocusTraversalKeysEnabled(false);
            p2.setBackground(Color.BLACK);
            java.util.List<JLabel> list = new ArrayList<>();
            JLabel l4 = new JLabel("F1 > MEDICAL RESOURCE CENTER");
            l4.setOpaque(true);
            l4.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l4.setForeground(new Color(0x3e86ca));
            l4.setBackground(Color.BLACK);
            JLabel l5 = new JLabel("F2 > LIMB REPLACEMENT");
            l5.setOpaque(true);
            l5.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l5.setForeground(new Color(0x3e86ca));
            l5.setBackground(Color.BLACK);
            JLabel l6 = new JLabel("F3 > MEMBER ASSISTANCE");
            l6.setOpaque(true);
            l6.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l6.setForeground(new Color(0x3e86ca));
            l6.setBackground(Color.BLACK);
            list.add(l4);
            list.add(l5);
            list.add(l6);
            p2.setBorder(BorderFactory.createLineBorder(new Color(0x3e86ca),5));
            p2.add(l4,c);
            p2.add(l5,c);
            p2.add(l6,c);
            add(p2,c);

            class TabAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    sel = (sel + 1) % list.size();
                    for (int i=0; i<list.size(); i++) {
                        list.get(i).setBackground(
                         i == sel ? new Color(0x0a6221) : Color.BLACK);
                        list.get(i).setForeground(
                         i == sel ? Color.BLACK : new Color(0x3e86ca));
                    }
                }
            }
            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
                }
            }
            p2.getInputMap().put(KeyStroke.getKeyStroke("TAB"),"TAB");
            p2.getActionMap().put("TAB",new TabAction());
            p2.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"SPACE");
            p2.getActionMap().put("SPACE",new SpaceAction());
        }
    }

    public static void main(String... args) {
        EventQueue.invokeLater(() -> new McCarty());
    }
}
Attachments
mccarty.jpg
mccarty.jpg (81.29 KiB) Viewed 2483 times

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Mon Aug 12, 2019 5:59 pm

ghp wrote:
Sun Aug 11, 2019 12:38 pm
svg is handy to produce lines, blocks, text.
svg can be included in html.
Very nice, thanks a lot

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Mon Aug 12, 2019 6:01 pm

knute wrote:
Sun Aug 11, 2019 3:50 pm
This is a perfect job for Java. Graphics are easy and so is the logic. This is down and dirty with no attempt to streamline.

Code: Select all

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class McCarty extends JFrame {
    public McCarty() {
        setLayout(new GridBagLayout());
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        getContentPane().setBackground(Color.BLACK);
        add(new mainMenu());
        setSize(800,600);
        setVisible(true);
    }

    public class mainMenu extends JPanel {
        private int sel = -1;
        public mainMenu() {
            super(new GridBagLayout());
            setBackground(Color.BLACK);

            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(0,40,0,40);
            c.gridx = 0;
            c.weightx = 1.0;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.anchor = GridBagConstraints.CENTER;

            JPanel p1 = new JPanel(new GridBagLayout());
            p1.setBackground(new Color(0x3e86ca));

            JLabel l1 = new JLabel("McCarty",JLabel.CENTER);
            l1.setFont(new Font(Font.SANS_SERIF,Font.BOLD,80));
            l1.setForeground(Color.BLACK);
            p1.add(l1,c);
            JLabel l2 = new JLabel("H  O  S  P  I  T  A  L",JLabel.CENTER);
            l2.setFont(new Font(Font.SANS_SERIF,Font.BOLD,40));
            l2.setForeground(Color.BLACK);
            p1.add(l2,c);

            c.gridy = 0;
            add(p1,c);

            ++c.gridy;
            c.insets = new Insets(10,10,10,10);
            c.fill = GridBagConstraints.NONE;
            JLabel l3 = new JLabel("MAIN MENU",JLabel.CENTER);
            l3.setOpaque(true);
            l3.setFont(new Font(Font.SANS_SERIF,Font.BOLD,50));
            l3.setBackground(new Color(0x3e86ca));
            l3.setForeground(Color.BLACK);
            add(l3,c);
            
            c.gridy = GridBagConstraints.RELATIVE;
            c.anchor = GridBagConstraints.WEST;
            JPanel p2 = new JPanel(new GridBagLayout());
            p2.setFocusTraversalKeysEnabled(false);
            p2.setBackground(Color.BLACK);
            java.util.List<JLabel> list = new ArrayList<>();
            JLabel l4 = new JLabel("F1 > MEDICAL RESOURCE CENTER");
            l4.setOpaque(true);
            l4.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l4.setForeground(new Color(0x3e86ca));
            l4.setBackground(Color.BLACK);
            JLabel l5 = new JLabel("F2 > LIMB REPLACEMENT");
            l5.setOpaque(true);
            l5.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l5.setForeground(new Color(0x3e86ca));
            l5.setBackground(Color.BLACK);
            JLabel l6 = new JLabel("F3 > MEMBER ASSISTANCE");
            l6.setOpaque(true);
            l6.setFont(new Font(Font.SANS_SERIF,Font.BOLD,32));
            l6.setForeground(new Color(0x3e86ca));
            l6.setBackground(Color.BLACK);
            list.add(l4);
            list.add(l5);
            list.add(l6);
            p2.setBorder(BorderFactory.createLineBorder(new Color(0x3e86ca),5));
            p2.add(l4,c);
            p2.add(l5,c);
            p2.add(l6,c);
            add(p2,c);

            class TabAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    sel = (sel + 1) % list.size();
                    for (int i=0; i<list.size(); i++) {
                        list.get(i).setBackground(
                         i == sel ? new Color(0x0a6221) : Color.BLACK);
                        list.get(i).setForeground(
                         i == sel ? Color.BLACK : new Color(0x3e86ca));
                    }
                }
            }
            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
                }
            }
            p2.getInputMap().put(KeyStroke.getKeyStroke("TAB"),"TAB");
            p2.getActionMap().put("TAB",new TabAction());
            p2.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"SPACE");
            p2.getActionMap().put("SPACE",new SpaceAction());
        }
    }

    public static void main(String... args) {
        EventQueue.invokeLater(() -> new McCarty());
    }
}
Oh my, that is also awesome! Down and dirty for the win.
Guess it's time to learn JAVA - well at least learn enough to make this.

knute
Posts: 472
Joined: Thu Oct 23, 2014 12:14 am
Location: Texas
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Mon Aug 12, 2019 6:45 pm

Spaziba wrote:
Mon Aug 12, 2019 6:01 pm

Oh my, that is also awesome! Down and dirty for the win.
Guess it's time to learn JAVA - well at least learn enough to make this.
It was kind of fun to play with. Let me know if you want some help with it. pi at knutejohnson dot com

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Tue Aug 13, 2019 8:54 pm

knute wrote:
Mon Aug 12, 2019 6:45 pm
Spaziba wrote:
Mon Aug 12, 2019 6:01 pm

Oh my, that is also awesome! Down and dirty for the win.
Guess it's time to learn JAVA - well at least learn enough to make this.
It was kind of fun to play with. Let me know if you want some help with it. pi at knutejohnson dot com
Oh all help is very much appreciated! :D My main focus is to reach the end goal, i.e. the working interface responding to keystrokes and playing sounds. It would be a secondary benefit to learn a little about JAVA. I am completely lost at the moment, but then again, I have not really had time to look too much into it. So far I'm at: java code needs to be compiled. And I guess your code example starts with including other files, yes?

knute
Posts: 472
Joined: Thu Oct 23, 2014 12:14 am
Location: Texas
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Tue Aug 13, 2019 9:38 pm

That little piece of code, creates the image and responds to keystrokes as you wanted. There (currently) is no other code. If you have Java 11 you can just run it from the command line with: java McCarty.java. If you are running Java 8 you will need to compile it and then run it.

javac McCarty.java

This creates a file called McCarty.class

java McCarty

The McCarty.class file is run with the java command specifying only the class name, McCarty without the file name extension. I know it is a bit confusing. In any case if you are running Buster you should have Java 11 installed and the first option I mentioned compiles the program in memory and then runs it. That code snippet responds to the TAB key and the SPACE bar.

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Wed Aug 14, 2019 4:43 pm

Thanks, I'll give it a look these next days and get back with the results

Spaziba
Posts: 23
Joined: Mon Apr 30, 2018 4:31 pm

Re: Old-school GUI - Possible? (Beware, silly purpose)

Fri Aug 16, 2019 6:34 pm

Okay might need a bit more help... :lol:
So, code is running fine, currently looking at on the PC just to have a better workspace - using Eclipse.
Again, never used JAVA, but some things are clear to me, like the button actions and the buttons used:

Code: Select all

            class TabAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    sel = (sel + 1) % list.size();
                    for (int i=0; i<list.size(); i++) {
                        list.get(i).setBackground(
                         i == sel ? new Color(0x0a6221) : Color.BLACK);
                        list.get(i).setForeground(
                         i == sel ? Color.BLACK : new Color(0x3e86ca));
                    }
                }
            }
            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
                }
            }
            p2.getInputMap().put(KeyStroke.getKeyStroke("TAB"),"TAB");
            p2.getActionMap().put("TAB",new TabAction());
            p2.getInputMap().put(KeyStroke.getKeyStroke("ENTER"),"ENTER");
            p2.getActionMap().put("ENTER",new SpaceAction());
Taking baby steps, my first goal would be to change

Code: Select all

            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
into opening the next "page", so a page for the F1, F2 and F3 option. So would that be a new public class and how would I redirect to it?

I hope this is not to much of a diversion from the original post, should the thread move to other topic, as it has now gone more JAVA centric?

Addendum: Your snippet of code produces such a damn perfect screen; it is going to be great when finished!

knute
Posts: 472
Joined: Thu Oct 23, 2014 12:14 am
Location: Texas
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sat Aug 17, 2019 6:37 pm

Spaziba wrote:
Fri Aug 16, 2019 6:34 pm
Okay might need a bit more help... :lol:
So, code is running fine, currently looking at on the PC just to have a better workspace - using Eclipse.
Again, never used JAVA, but some things are clear to me, like the button actions and the buttons used:

Code: Select all

            class TabAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    sel = (sel + 1) % list.size();
                    for (int i=0; i<list.size(); i++) {
                        list.get(i).setBackground(
                         i == sel ? new Color(0x0a6221) : Color.BLACK);
                        list.get(i).setForeground(
                         i == sel ? Color.BLACK : new Color(0x3e86ca));
                    }
                }
            }
            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
                }
            }
            p2.getInputMap().put(KeyStroke.getKeyStroke("TAB"),"TAB");
            p2.getActionMap().put("TAB",new TabAction());
            p2.getInputMap().put(KeyStroke.getKeyStroke("ENTER"),"ENTER");
            p2.getActionMap().put("ENTER",new SpaceAction());
Taking baby steps, my first goal would be to change

Code: Select all

            class SpaceAction extends AbstractAction {
                public void actionPerformed(ActionEvent ae) {
                    if (sel > -1)
                        JOptionPane.showMessageDialog(p2,
                         list.get(sel).getText());
into opening the next "page", so a page for the F1, F2 and F3 option. So would that be a new public class and how would I redirect to it?

I hope this is not to much of a diversion from the original post, should the thread move to other topic, as it has now gone more JAVA centric?

Addendum: Your snippet of code produces such a damn perfect screen; it is going to be great when finished!
So there are a zillion ways to attack this and there are a lot of things that could be cleaned up here but I'll just tell you a couple to get you going. First my plan was to replace the mainMenu class (which is the menu panel) with a class that contains the next page when you select it. So the action would remove the panel from the frame and create a new panel and put it in the frame. I'll work you up a simple SSCCE for that later. Second the buttons aren't really buttons. I used JLabels because I thought it would be easier to make them look like you wanted. So that created the requirement to use the input and action maps to control the keystrokes and again that was selected because you wanted to use the tab key to highlight them and the space bar to activate them. If you want to use buttons that is definitely doable, I took some short cuts to get it posted with the least effort on my part :-).

It might very well be easier to do this with buttons but I didn't have time to play with it before. Let me think about that for a bit and I will post an SSCCE using buttons too.

knute
Posts: 472
Joined: Thu Oct 23, 2014 12:14 am
Location: Texas
Contact: Website

Re: Old-school GUI - Possible? (Beware, silly purpose)

Sat Aug 17, 2019 7:01 pm

One way to swap out panels with a button:

Code: Select all

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Panels extends JFrame {
    private final Panel1 panel1;
    private final Panel2 panel2;

    public Panels() {
        super("Panels");
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setPreferredSize(new Dimension(400,300));
        panel1 = new Panel1();
        panel2 = new Panel2();
        add(panel1,BorderLayout.CENTER);
        pack();
        setVisible(true);
    }

    public class Panel1 extends JPanel {
        public Panel1() {
            super(new GridBagLayout());

            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(40,40,40,40);

            JButton button = new JButton("Select Panel2");
            button.addActionListener(event -> {
                Panels.this.remove(panel1);
                Panels.this.add(panel2,BorderLayout.CENTER);
                Panels.this.revalidate();
                Panels.this.repaint();
            });
            add(button,c);
        }
    }

    public class Panel2 extends JPanel {
        public Panel2() {
            super(new GridBagLayout());

            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(40,40,40,40);

            JButton button = new JButton("Select Panel1");
            button.addActionListener(event -> {
                Panels.this.remove(panel2);
                Panels.this.add(panel1,BorderLayout.CENTER);
                Panels.this.revalidate();
                Panels.this.repaint();
            });
            add(button,c);
        }
    }

    public static void main(String... args) {
        EventQueue.invokeLater(() -> new Panels());
    }
}

Return to “Python”