LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-04-2020, 09:22 AM   #1
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,147
Blog Entries: 6

Rep: Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834
Useful scrips to work on in quarantine.


Since we are all stuck at home...good time to practice our bash, python, perl, php, C++ etc.

I'll start with one. Here is a very basic text only web browser that uses python3, tkinter, urllib, beautifulsoup, html2text, youtube_dl.
Everything is put into functions so that it's easy to find/understand. tkinter is about as easy as it gets to make a GUI with python3.

Go ahead and pick it apart, put some forward/back buttons on it, make the utube functionality better, use tkinter.ttk instead of tkinter to colorize it, use a grid instead of frames...

It shows how to make elements with tkinter, get input from them, display output in them, save the element content to file.

Code:
#! /usr/bin/python

from tkinter import *
from urllib import request
from bs4 import BeautifulSoup
from html2text import html2text, HTML2Text
from youtube_dl import YoutubeDL

#User agent for requests
'''
agent = ('Mozilla/5.0 (Windows NT 10.1; x86_64; rv:75.0)'
        ' Gecko/20100101 Firefox/75.0')  
'''
agent = ('Mozilla/5.0')
        
#Make request header
user_agent = {'User-Agent': agent,
            'Accept': 'text/html,application/xhtml+xml,'
            'application/xml;q=0.9,*/*;q=0.8',
            'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
            'Accept-Encoding': 'none',
            'Accept-Language': 'en-US,en;q=0.8',
            'Connection': 'keep-alive'}
            
#Home page            
#homePage = 'file:///path/to/Bookmarks.html'
homePage = 'https://en.wikipedia.org/wiki/Carbon'
#homePage = 'https://www.youtube.com/watch?v=5Q5g-krgDnk'


def TkBrowser():
    #Load the page with urllib
    def loadPage():
        req = request.Request(entry.get(), 
                            data=None, headers=user_agent)
        page = request.urlopen(req)
        return page
        
    #Get page source
    def viewSource():
        text.delete(1.0, END)
        pSource = loadPage()
        html = pSource.read()
        text.insert(1.0, html)
        return html
        
    #Write source code to file
    def saveSource():
        getHtml = viewSource().splitlines()
        sSource = '\n'.join(line.decode('utf-8') for line in getHtml)
        with open('TkBSource.html', 'a') as f:
            f.write(sSource)
            
    #Get text of page with soup
    def textBrowse():
        text.delete(1.0, END)
        get = loadPage()
        soup = BeautifulSoup(get, features='lxml')
        #Kill all script and style elements
        for s in soup(["script", "style"]):
            s.extract()
        txt = '\n'.join(soup.get_text().splitlines())
        text.insert(1.0, txt)
        return txt
        
    #Get text of page with html2text
    def textBrowse2():
        text.delete(1.0, END)
        pSource = loadPage()
        html = pSource.read()
        noLinks = HTML2Text()
        noLinks.ignore_links = True
        txt = noLinks.handle(html.decode('utf-8'))
        text.insert(1.0, txt)
        
    #Save text of page to file
    def savePage():
        getPage = textBrowse()
        with open('TkBText.txt', 'w') as f:
            f.write(getPage)
        
    #Get links on page with soup
    def pageLinks():
        text.delete(1.0, END)
        getP = loadPage()
        soup = BeautifulSoup(getP, 'lxml')
        hrefTags = soup.find_all(href=True) 
        links = [tag.get('href') for tag in hrefTags]
        linkList = '\n'.join(l for l in links if l)
        text.insert(1.0, linkList)
        return linkList
        
    #Save links to file
    def saveLinks():
        getLinks = pageLinks()
        with open('TkBLinks.txt', 'w') as f:
            f.write(getLinks)
            
    #Get Utube video urls
    def getUtube():
        text.delete(1.0, END)
        ulist = []
        yt = YoutubeDL()
        info = yt.extract_info(entry.get(), download=False)
        ulist.append(info['title'])
        ulist.append('')
        ulist.append(info['description'])
        ulist.append('')
        try:
            for i in info['formats']:
                print(i)
                ulist.append(i['format_id'])
                ulist.append(i['url'])
                ulist.append('')
                utList  = '\n'.join(ulist)
        except:
            pass
        text.insert(1.0, utList)
        return utList
        
    #Save Utube url's to file
    def saveUtube():
        getTube = getUtube()
        with open('TkBUtube.txt', 'w') as f:
            f.write(getTube)
            
    #Make a window
    browser = Tk()
    browser.title('PyTk Browser')
    #Window size
    browser.geometry('1200x900')
    fontSize = ('Arial', 18)
    bg='grey20'
    fg='white'
    bw=3
    
    #Make Frames
    top = Frame(browser)
    bottom = Frame(browser)
    top.pack(side=TOP)
    bottom.pack(side=BOTTOM, fill=BOTH, expand=True)
 
    #Make menubar
    menubar = Menu(browser)
    browser.config(menu=menubar, bg=bg)
    menubar.config(font=fontSize, bd=bw, bg=bg, fg=fg)
    
    #Make menu entries
    #Load menu
    loadmenu = Menu(menubar, tearoff=0)
    loadmenu.config(
        font=fontSize, bd=bw, bg=bg, fg=fg)
    loadmenu.add_command(
        label="Get page text with Beautiful Soup", command=textBrowse)
    loadmenu.add_command(
        label="Get page text with Html2text", command=textBrowse2)
    loadmenu.add_command(
        label="Save page text to file", command=savePage)
        
    #Source menu
    sourcemenu = Menu(menubar, tearoff=0)
    sourcemenu.config(
        font=fontSize, bd=bw, bg=bg, fg=fg)
    sourcemenu.add_command(
        label="View page source code", command=viewSource)
    sourcemenu.add_command(
        label="Save page source code to file", command=saveSource)
        
    #Links menu                                            
    linksmenu = Menu(menubar, tearoff=0)
    linksmenu.config(
        font=fontSize, bd=bw, bg=bg, fg=fg)
    linksmenu.add_command(
        label="View page links", command=pageLinks)
    linksmenu.add_command(
        label="Save page links to file", command=saveLinks)
        
    #UTube menu
    utubemenu = Menu(menubar, tearoff=0)
    utubemenu.config(
        font=fontSize, bd=bw, bg=bg, fg=fg) 
    utubemenu.add_command(
        label="View utube url's", command=getUtube)
    utubemenu.add_command(
        label="Save utube url's to file", command=saveUtube)

    #Make a url entry widget
    entry = Entry(browser)
    label = Label(text='url')
    label.config(font=fontSize, bg=bg, fg=fg, highlightthickness=3)
    entry.config(font=fontSize, width=80, bd=bw, 
                        highlightcolor='blue', highlightthickness=3)
    #Bind enter key to textBrowse()
    entry.bind('<Return>', lambda event=None: textBrowse())
    entry.insert(END, homePage)
    
    #Make a scroll bar
    scroll = Scrollbar(browser)
    scroll.config(width=25, bd=bw, bg=bg, troughcolor=fg)

    #Make text widget
    text = Text(browser, yscrollcommand=scroll.set)
    text.config(font=fontSize, bd=bw, bg='white', fg='black', 
                        highlightcolor='blue', highlightthickness=3)
    scroll.config(command=text.yview)
    
    #Place widgets
    menubar.add_cascade(label="Load", menu=loadmenu)
    menubar.add_cascade(label="Source", menu=sourcemenu)
    menubar.add_cascade(label="Links", menu=linksmenu)
    menubar.add_cascade(label="UTube", menu=utubemenu)
    entry.pack(in_=top, side=LEFT)
    label.pack(in_=top, side=RIGHT)
    scroll.pack(in_=bottom, side=RIGHT, fill=Y)
    text.pack(in_=bottom, side=TOP, fill="both", expand=True)

    browser.mainloop()

if __name__ == "__main__":
    TkBrowser()
https://docs.python.org/3/
https://docs.python.org/3/library/tk.html
https://docs.python.org/3/library/urllib.html
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
https://pypi.org/project/html2text/

Go ahead and post yours if you want. Something profitable to do instead of watching vids.
 
Old 04-06-2020, 06:24 AM   #2
Michael Uplawski
Senior Member
 
Registered: Dec 2015
Posts: 1,622
Blog Entries: 40

Rep: Reputation: Disabled
HTML-cleaning helper script.

HTML-Tidy in its current version 5.7.28 does not appear to support the option --tidy-mark, although it remains in the man-page.

The “problem” (what a word) I have with that, comes from the fact that a “generator” meta-tag appears to be horribly exaggerated for all the HTML I do, these days. I already use to add a comment right below the XML-declaration: “Created with nothing”. Now, the fact that Tidy itself insists in, -and cannot be persuaded otherwise-, the addition of its own generator tag provokes me to add the VI-editor in that line:
Code:
  <meta name="generator" content="vi - Vi Improved, a programmer's editor; HTML Tidy for HTML5 for Linux version 5.7.28" />
Tidy coming back later for another check on the same HTML-file will not recognize this meta-tag and adds yet another one! Irrespective of the --tidy-mark option being used or not.

So here is my solution. It is complex, it is completely useless and ... I wrote that yesterday. Do not bare with me too much, if there are obvious coding errors, but neither make a fuss about it...

This is not about XML. In deed, I started off parsing the page with nokogiri, before I woke up... Maybe that is the whole point of this post.., or maybe you find another.

1. script called from vi
Code:
!/bin/bash

html_remove_generator $1
tidy -i -m -asxhtml --drop-proprietary-attributes yes -utf8 $1
html_add_vim.rb $1
2. html_remove_generator
Code:
#!/bin/env ruby
#
# looks for the meta-tag with name=generator and removes it
# ©2020 Michael Uplawski 
require_relative 'file_checking'

FILE = ARGV[0]
if(FILE && File.exist?(FILE) )
  msg = File_Checking::magic_check(FILE, "XML 1.0 document, ASCII text")
  if msg 
    msg = File_Checking::magic_check(FILE, "XML 1.0 document, UTF-8 Unicode text")
  end 

  if !msg
    content = File.read(FILE)
    puts 'removing meta-tag' 
    it_worked = content.gsub!(/<meta name="generator".*\n.*\/>/, '')
    if it_worked
      File.open(FILE, 'w') do |ofl|
        ofl << content
      end
    else
      puts 'cannot remove the meta tag'
    end
  else # wrong magic 
    puts msg
  end
else # file.exist?
  puts 'File "%s" does not exist' %FILE
end

# Ω
3. html_add_vim
Code:
#!/bin/env ruby
#
# looks for the meta-tag with name=generator and adds vi, if html tidy has been
# mentioned, before
#
# ©2020 Michael Uplawski 
require_relative 'file_checking'

FILE = ARGV[0]
if(FILE && File.exist?(FILE) )
  msg = File_Checking::magic_check(FILE, "XML 1.0 document, ASCII text")
  if msg 
    msg = File_Checking::magic_check(FILE, "XML 1.0 document, UTF-8 Unicode text")
  end 

  if !msg
    ORIG = 'HTML Tidy for HTML5 for Linux'
    S_VIM = 'vi - Vi Improved, a programmer\'s editor; ' << ORIG
    
    content = File.read(FILE)
    if !content.include?(S_VIM)
      it_worked = content.gsub!(ORIG, S_VIM)
      if it_worked
        File.open(FILE, 'w') do |ofl|
          ofl << content
        end
      else # substitution NOT done
        puts "No substitution possible, maybe run tidy on the file, first"
      end
    else # include S_VIM
      puts 'Vim is already mentioned'
    end
  else # wrong magic 
    puts msg
  end
else # file.exist?
  puts 'File "%s" does not exist' %FILE
end

# Ω
4. Here is the file_checking module which is used in the other scripts:
Code:
#encoding: UTF-8
=begin
/***************************************************************************
 *   ©2011-2019 Michael Uplawski <michael.uplawski@uplawski.eu>            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
=end

require 'filemagic'

=begin
A module to facilitate frequently occuring checks on
file-system objects
=end
module File_Checking

  @@text_messages  = {
    :exist? => "does not exist!",
    :exist => "does not exist!",
    :readable? => "is not readable!",
    :readable => "is not readable!",
    :executable? => "is not executable!",
    :executable => "is not executable!",
    :writable? => "is not writable!",
    :writable => "is not writable!",
    :directory? => "is not a directory!",
    :directory => "is not a directory!",
    :file? => "is not a file!",
    :file => "is not a file!",
    :type => "is not of the sought file-type (not '%s', but '%s')!",
    :type? => "is not of the sought file-type (not '%s', but '%s')!",
    :mime => "does not have the sought mime-type (not '%s', but '%s')!",
    :mime? => "does not have the sought mime-type (not '%s', but '%s')!",
  }

  # Checks if the file with the name from the first
  # parameter has the properties, listed in the second. 
  # The messages parameter is an array of one or several
  # of :exist?, :readable?, :writable?, :directory?  or 
  # their string-representations, respectively. 
  # Returns nil in case of success, otherwise an 
  # informative message, describing the first negative
  # test-result.
  def file_check(file, *messages)
    File_Checking.file_check(file, *messages)
  end

  # Checks if the file with the name from the first
  # parameter has the properties, listed in the second. 
  # The messages parameter is an array of one or all
  # of :exist?, :readable?, :writable?, :directory? or
  # their string-representations, respectively. 
  # Returns nil in case of success, otherwise an 
  # informative message, describing the first negative
  # test-result.
  def self.file_check(file, *messages)
    msg = nil
    if(file && messages.respond_to?(:to_ary) && !messages.empty?)
      messages.each do |k|
        if(! k.to_s.end_with?('?'))	
          k = (k.to_s << '?').to_sym
        end
        @log.debug ('checking ' << k.to_s) if @log
        if(msg == nil && File.respond_to?(k) && ! File.send(k, file.to_s))
          msg = "#{file} #{@@text_messages[k.to_sym]}"
        end
      end
    end
    msg
  end
   
  def self.mime_check(file, mime_type)
    fm = FileMagic.mime
    fd = fm.fd(File.new(file) ).split(';')[0]
    if fd != mime_type.strip  
             return file.dup << ' ' << (@@text_messages[:mime] %[mime_type, fd])
    end
    return nil
  end

  def self.magic_check(file, magic)
    fm = FileMagic.fm
    fd = fm.fd(File.new(file) ).split(';')[0]
    if fd != magic.strip  
             return file.dup << ' ' << (@@text_messages[:type] %[magic, fd])
    end
    return nil
  end

end # module

=begin
# example

include File_Checking
msg = file_check('some_file.txt', [:exist?, :readable?, 'writable'])
# same as 
# msg = file_check('some_file.txt', [:exist, :readable, 'writable?'])

msg ||= magic_check('some_file.txt', [:type?], 'OpenDocument Text'
msg ||= mime_check('some_file.txt', [:mime?], 'application/vnd.oasis.opendocument.text'
puts msg if msg
=end
# E O F

Last edited by Michael Uplawski; 04-06-2020 at 06:49 AM. Reason: remove done by the remove-script, not the add-script. Of all choices !
 
Old 04-14-2020, 04:03 PM   #3
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,147

Original Poster
Blog Entries: 6

Rep: Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834
A color chart that tkinter can use for its widgets.
Code:
#!/usr/bin/python

#tkinter color chart.

from tkinter import *

#Change chart size here
fontSize = ('Monospace', 10)

colors = ['snow', 'ghostwhite', 'whitesmoke', 'gainsboro', 'floral white', 'old lace', 
    'linen', 'antiquewhite', 'antiquewhite1', 'antiquewhite2', 'antiquewhite3', 
    'antiquewhite4', 'papaya whip', 'blanched almond', 'bisque', 'peach puff', 'light grey', 
    'gray', 'dim gray', 'slate gray','light slate gray', 'dark slate gray', 'navajowhite', 
    'navajowhite2', 'navajowhite3', 'navajowhite4', 'lemon chiffon', 'mint cream', 'azure', 
    'alice blue', 'lavender', 'lavender blush', 'misty rose',  'midnight blue', 'navy', 
    'cornflower blue', 'dark slate blue', 'slate blue', 'medium slate blue', 'light slate blue', 
    'medium blue', 'royal blue', 'dodger blue', 'deep sky blue', 'sky blue', 'light sky blue', 
    'steel blue', 'light steel blue', 'light blue', 'powder blue', 'pale turquoise', 
    'dark turquoise', 'medium turquoise', 'turquoise', 'light cyan', 'cadet blue', 
    'medium aquamarine', 'aquamarine', 'dark green', 'dark olive green', 'dark sea green', 
    'sea green', 'medium sea green', 'light sea green', 'pale green', 'spring green', 'lawn green', 
    'medium spring green', 'green yellow', 'lime green', 'yellow green', 'forest green', 'olive drab', 
    'dark khaki', 'khaki', 'pale goldenrod', 'light goldenrod yellow', 'light yellow', 'light goldenrod', 
    'goldenrod', 'dark goldenrod', 'rosy brown', 'indian red', 'saddle brown', 'sandy brown',
    'dark salmon', 'salmon', 'light salmon', 'dark orange', 'coral', 'light coral', 'tomato', 
    'orange red', 'hot pink', 'deep pink', 'pink', 'light pink', 'pale violet red', 'maroon', 
    'medium violet red', 'violet red', 'medium orchid', 'dark orchid', 'dark violet', 'blue violet', 
    'purple', 'medium purple', 'thistle', 'snow2', 'snow3', 'snow4', 'seashell2', 'seashell3', 'seashell4',  
    'bisque2', 'bisque3', 'bisque4', 'PeachPuff2', 'PeachPuff3', 'PeachPuff4',  'LemonChiffon2', 
    'LemonChiffon3', 'LemonChiffon4', 'cornsilk2', 'cornsilk3', 'cornsilk4', 'ivory2', 'ivory3', 'ivory4', 
    'honeydew2', 'honeydew3', 'honeydew4','LavenderBlush2', 'LavenderBlush3', 'LavenderBlush4', 'MistyRose2',
    'MistyRose3', 'MistyRose4', 'azure2', 'azure3', 'azure4', 'SlateBlue1', 'SlateBlue2', 'SlateBlue3', 
    'SlateBlue4', 'RoyalBlue1', 'RoyalBlue2', 'RoyalBlue3', 'RoyalBlue4', 'blue', 'blue2', 'blue4', 
    'DodgerBlue2', 'DodgerBlue3', 'DodgerBlue4', 'SteelBlue1', 'SteelBlue2', 'SteelBlue3', 'SteelBlue4', 
    'DeepSkyBlue2', 'DeepSkyBlue3', 'DeepSkyBlue4', 'SkyBlue1', 'SkyBlue2', 'SkyBlue3', 'SkyBlue4', 
    'LightSkyBlue1', 'LightSkyBlue2', 'LightSkyBlue3', 'LightSkyBlue4', 'SlateGray1', 'SlateGray2', 
    'SlateGray3', 'SlateGray4', 'LightSteelBlue1', 'LightSteelBlue2', 'LightSteelBlue3',
    'LightSteelBlue4', 'LightBlue1', 'LightBlue2', 'LightBlue3', 'LightBlue4', 'LightCyan2', 
    'LightCyan3', 'LightCyan4', 'PaleTurquoise1', 'PaleTurquoise2', 'PaleTurquoise3', 
    'PaleTurquoise4', 'CadetBlue1', 'CadetBlue2', 'CadetBlue3', 'CadetBlue4', 'turquoise1', 
    'turquoise2', 'turquoise3', 'turquoise4', 'cyan', 'cyan2', 'cyan3', 'cyan4', 'DarkSlateGray1', 
    'DarkSlateGray2', 'DarkSlateGray3', 'DarkSlateGray4', 'aquamarine2', 'aquamarine4', 
    'DarkSeaGreen1', 'DarkSeaGreen2', 'DarkSeaGreen3', 'DarkSeaGreen4', 'SeaGreen1', 'SeaGreen2', 
    'SeaGreen3', 'PaleGreen1', 'PaleGreen2', 'PaleGreen3', 'PaleGreen4', 'SpringGreen2', 
    'SpringGreen3', 'SpringGreen4', 'green', 'green2', 'green3', 'green4', 'chartreuse2', 'chartreuse3', 
    'chartreuse4', 'OliveDrab1', 'OliveDrab2', 'OliveDrab4', 'DarkOliveGreen1', 'DarkOliveGreen2',
    'DarkOliveGreen3', 'DarkOliveGreen4', 'khaki1', 'khaki2', 'khaki3', 'khaki4', 'LightGoldenrod1', 
    'LightGoldenrod2', 'LightGoldenrod3', 'LightGoldenrod4','LightYellow2', 'LightYellow3', 
    'LightYellow4', 'yellow', 'yellow2', 'yellow3', 'yellow4', 'gold', 'gold2', 'gold3', 'gold4', 
    'goldenrod1', 'goldenrod2', 'goldenrod3', 'goldenrod4', 'DarkGoldenrod1', 'DarkGoldenrod2', 
    'DarkGoldenrod3', 'DarkGoldenrod4', 'RosyBrown1', 'RosyBrown2', 'RosyBrown3', 'RosyBrown4', 
    'IndianRed1', 'IndianRed2', 'IndianRed3', 'IndianRed4', 'sienna1', 'sienna2', 'sienna3', 'sienna4', 
    'burlywood1', 'burlywood2', 'burlywood3', 'burlywood4', 'wheat1', 'wheat2', 'wheat3', 'wheat4', 
    'tan1', 'tan2', 'tan4', 'chocolate1', 'chocolate2', 'chocolate3', 'firebrick1', 'firebrick2',
    'firebrick3', 'firebrick4', 'brown1', 'brown2', 'brown3', 'brown4', 'salmon1', 'salmon2',
    'salmon3', 'salmon4', 'LightSalmon2', 'LightSalmon3', 'LightSalmon4', 'orange', 'orange2', 'orange3', 
    'orange4', 'DarkOrange1', 'DarkOrange2', 'DarkOrange3', 'DarkOrange4', 'coral1', 'coral2', 
    'coral3', 'coral4', 'tomato2', 'tomato3', 'tomato4', 'OrangeRed2', 'OrangeRed3', 'OrangeRed4', 
    'red', 'red2', 'red3', 'red4', 'DeepPink2', 'DeepPink3', 'DeepPink4', 'HotPink1', 'HotPink2', 
    'HotPink3', 'HotPink4', 'pink1', 'pink2', 'pink3', 'pink4', 'LightPink1', 'LightPink2', 
    'LightPink3', 'LightPink4', 'PaleVioletRed1', 'PaleVioletRed2', 'PaleVioletRed3', 
    'PaleVioletRed4', 'maroon1', 'maroon2', 'maroon3', 'maroon4', 'VioletRed1', 'VioletRed2', 
    'VioletRed3', 'VioletRed4', 'magenta2', 'magenta3', 'magenta4', 'orchid1', 'orchid2', 
    'orchid3', 'orchid4', 'plum1', 'plum2', 'plum3', 'plum4', 'MediumOrchid1', 'MediumOrchid2', 
    'MediumOrchid3', 'MediumOrchid4', 'DarkOrchid1', 'DarkOrchid2', 'DarkOrchid3', 'DarkOrchid4',
    'purple1', 'purple2', 'purple3', 'purple4', 'MediumPurple1', 'MediumPurple2', 'MediumPurple3', 
    'MediumPurple4', 'thistle1', 'thistle2', 'thistle3', 'thistle4', 'gray1', 'gray2', 'gray3', 
    'gray4', 'gray5', 'gray6', 'gray7', 'gray8', 'gray9', 'gray10', 'gray11', 'gray12', 'gray13', 
    'gray14', 'gray15', 'gray16', 'gray17', 'gray18', 'gray19', 'gray20', 'gray21', 'gray22', 
    'gray23', 'gray24', 'gray25', 'gray26', 'gray27', 'gray28', 'gray29', 'gray30', 'gray31', 
    'gray32', 'gray33', 'gray34', 'gray35', 'gray36', 'gray37', 'gray38', 'gray39', 'gray40', 
    'gray41', 'gray42', 'gray43', 'gray44', 'gray45', 'gray46', 'gray47', 'gray48', 'gray49', 
    'gray50', 'gray51', 'gray52', 'gray53', 'gray54', 'gray55', 'gray56', 'gray57', 'gray58', 
    'gray59', 'gray60', 'gray61', 'gray62', 'gray63', 'gray64', 'gray65', 'gray66', 'gray67', 
    'gray68', 'gray69', 'gray70', 'gray71', 'gray72', 'gray73', 'gray74', 'gray75', 'gray76', 
    'gray77', 'gray78', 'gray79', 'gray80', 'gray81', 'gray82', 'gray83', 'gray84', 'gray85', 
    'gray86', 'gray87', 'gray88', 'gray89', 'gray90', 'gray91', 'gray92', 'gray93', 'gray94', 
    'gray95', 'gray97', 'gray98', 'gray99']
    
dark = ['dark slate gray', 'midnight blue', 'dark slate blue', 'saddle brown', 'maroon', 
    'SlateBlue4', 'RoyalBlue4', 'navy', 'medium blue', 'dark green', 'blue', 'blue2', 'blue4', 
    'gray1', 'gray2', 'gray3', 'gray4', 'gray5', 'gray6', 'gray7', 'gray8', 'gray9', 'gray10', 
    'gray11', 'gray12', 'gray13', 'gray14', 'gray15', 'gray16', 'gray17', 'gray18', 'gray19', 
    'gray20', 'gray21', 'gray22', 'gray23', 'gray24', 'gray25', 'gray26', 'gray27', 'gray28', 
    'gray29', 'gray30', 'gray31', 'gray32', 'gray33', 'gray34', 'gray35', 'gray36', 'gray37', 
    'gray38', 'gray39', 'gray40']

chart = Tk()
chart.title("Tkinter color chart")
row, col = 0, 0

for c in colors:
    if c in dark:
        fg = 'white'
    else:
        fg = 'black'
    e = Label(chart, text=c, bg=c, fg=fg, relief=RAISED, bd=2, font=fontSize)
    e.grid(row=row, column=col, sticky=E+W)
    row += 1
    if (row > 36):
        row = 0
        col += 1

chart.mainloop()
 
Old 04-15-2020, 01:34 AM   #4
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 19,872
Blog Entries: 12

Rep: Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053
^ Nice!
Wouldn't it be better to use Xorg's rgb.txt directly?
I just looked, it's built straight into the server executable nowadays, what a bummer.
But e.g. xplanet still ships a text version: /usr/share/xplanet/rgb.txt

Can tkinter do tooltips? Then you could avoid the problem with dark/light font.
 
Old 04-15-2020, 11:40 AM   #5
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,147

Original Poster
Blog Entries: 6

Rep: Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834
Quote:
Can tkinter do tooltips? Then you could avoid the problem with dark/light font.
Never thought of that.
https://stackoverflow.com/questions/...ips-in-tkinter

For the bored kids in your house.

Kid game. Enter the colors before the clock runs out. The timer will run faster if you enter a wrong answer. The answer is given to them at first, if they figure that out. Age range 3-8 I would say. Teaches colors and typing. Modify it how you wish.
Code:
#!/usr/bin/python 

#Enter the color you see game.

from tkinter import *
from random import shuffle
  
color = ['Red','Orange','Yellow','Blue','Green','Pink',
        'White','Purple','Brown','Gray', 'Maroon', 'Cyan',
        'Tan', 'Violet']

class colorGame():
    def __init__(self, score, timeleft, hurry):
        super(colorGame, self).__init__()
        
        self.score = score
        self.timeleft = timeleft
        self.hurry = hurry

        def start(event): 
            if self.timeleft > 0:
                colors()
            if self.score == score:
                count()
          
        def colors():
            if self.timeleft > 0: 
                if ent.get().lower() == color[1].lower(): 
                    self.score += 1
                       
                ent.delete(0, END) 
                shuffle(color)
                colorLabel.config(fg=str(color[1]), 
                                            text=str(color[0]))
                scoreLabel.config(text="\nScore: " 
                                            + str(self.score))
                                             
        def count():
            t=[]
            for c in color:
                t.append(c)
            hurryLabel.config(text='\nColors are ' + str(t), 
                                            font=fontSize3)
            if self.timeleft > 0:
                self.timeleft -= 1
                timeLabel.config(text="\nTime left: "
                        + str(self.timeleft)+'\n', font=fontSize)
                timeLabel.after('1000', count)
                        
            if self.timeleft in range(1, 30):
                hurryLabel.config(text="\nHurry!", font=fontSize)
                    
            if self.timeleft == 0:
                hurryLabel.config(text="\nDone! " 
                + "Can you get a better score?", font=fontSize)

        bg = 'black'
        fg = 'white'
        root = Tk()
        root.config(bg=bg)
        root.title("Enter The Color Game") 
        root.geometry("1400x700")
        fontSize = ('Monospace', 34)
        fontSize2 = ('Monospace', 80)
        fontSize3 = ('Monospace', 12) 
         
        inst = Label(root,
            text="Enter color of words before the clock runs out",
            font=fontSize, bg=bg, fg=fg) 
        inst.pack()
          
        scoreLabel = Label(root,
            text="Press Enter to start", font=fontSize, bg=bg, fg=fg) 
        scoreLabel.pack() 
          
        timeLabel = Label(root, text = "\nTime left: " +
                        str(timeleft), font=fontSize, bg=bg, fg=fg) 
        timeLabel.pack() 
          
        colorLabel = Label(root, font=fontSize2, bg=bg, fg=fg)
        colorLabel.pack() 

        ent = Entry(root)
        ent.config(font=fontSize)
        ent.pack() 
        ent.focus_set() 
        
        hurryLabel = Label(root, font=fontSize, bg=bg, fg=fg)
        hurryLabel.pack()
        
        root.bind('<Return>', start)
        root.mainloop() 
    
if __name__ == "__main__":
    
    score = 0
    #Set clock times here
    timeleft = 60
    hurry = 30
    colorGame(score, timeleft, hurry)
 
Old 04-25-2020, 06:20 AM   #6
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,147

Original Poster
Blog Entries: 6

Rep: Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834
Little tkinter calculator that scrolls and logs to file. All it needs is python3. Change it if you are bored.
Code:
#!/usr/bin/python

from tkinter import *

class Calc(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.option_add("*Font", "Monospace 30 bold")
        self.config(bg='green')
        self.pack(expand=YES, fill=BOTH)
        self.master.title("Tk Calculator")
        self._display()
        self.listbox = Listbox(
            self, width=30, relief=SUNKEN, bd=6, 
            highlightcolor='yellow', highlightthickness=2, 
            highlightbackground='black')
        self.listbox.pack()
        self.count = 0

    def _display(self):
        display = StringVar()
        self.entry = Entry(
            self, relief=SUNKEN, bd=6, textvariable=display, 
            highlightcolor='yellow', highlightthickness=2, 
            highlightbackground='black', justify='center')
        self.entry.pack(side=TOP)
        self.entry.focus()
        self.master.bind("<KP_Enter>", lambda x: self.calc(display))
        self.master.bind("<Return>", lambda x: self.calc(display))
        self.master.bind("<Escape>", lambda x: display.set(""))
    
    def calc(self, display):
        try:
            dis = eval(display.get())
            if str(display.get()).isdigit:
                txt = (display.get() + " = " + str(dis))
                self.listbox.insert(END, txt) 
                with open('TkCalc.txt', 'a') as f:
                    f.write(txt + '\n')
                display.set("")
                self.count += 1      
        except NameError as e:
            display.set("!Integer Press Esc.")
        if self.count > 10:
            self.listbox.delete([0])
                
if __name__ == '__main__':
    Calc().mainloop()
Code:
2+3 = 5
27*(9/5)+32 = 80.6
3.1415927*(21**2) = 1385.4423807
(2*3.1415927)*21 = 131.9468934
(80-32)*.56 = 26.880000000000003
25**.5 = 5.0
12*(10**2) = 1200
(6/9)+(3*2) = 6.666666666666667
Really big clock, if you need one.
Code:
#!/usr/bin/python

from tkinter import *
from time import strftime

def digClock():
    def sec():
        timeSec = strftime("%H:%M:%S")
        c.config(text=timeSec)
        c.after(1000,sec)
        
    root = Tk()
    root.title('Clock')   
    bg = 'black'
    fg = 'yellow'
    root.config(bg=bg)
    cfont = ('Monospace', 250, 'bold')
    dfont = ('Monospace', 100, 'bold')
    
    c = Label(root, font=cfont, bg=bg, fg=fg)
    c.grid(row=0, column=0)
    
    d = strftime("%a %d %b %Y")
    dl = Label(root, text=d, font=dfont, bg=bg, fg=fg)
    dl.grid(row=2, column=0)

    sec()
    root.mainloop()
    
if __name__ == "__main__":
    digClock()
A tkinter timer.

Code:
#!/usr/bin/python

#Execute something, time limit it.

from tkinter import *
from time import strftime

class timer():
    def __init__(self):
        def countdown(count):
            self.count = count
            if count > 0:
                root.after(1000, countdown, count-1)
                running ['text']='Running'
            else:
                done ['text']='  Done  '
               
        def getTime():
            done ['text']=''
            h, m, s = hEntry.get(), mEntry.get(), sEntry.get()
            if h and m and s.isdigit():
                t=int(h)*3600+int(m)*60+int(s)
                countdown(t)
            else:
                done ['text']='You entered a non digit'
                t = 0
            self.stop = False
            return t
                
        def stop():
            self.stop = True
            root.after(10, countdown(0))
            
        def start():
            run = getTime()
            newWin = Toplevel(root)
            newWin.title('Python code running here')
            
            #Example of executing something, Stop/Exit when timer 
            #stops. In this example, a clock
            ############
            def sec():
                timeSec = strftime("%H:%M:%S")
                clock.config(text=timeSec)
                clock.after(1000,sec)
                
            cfont=('Monospace', 100, 'bold')
            clock=Label(newWin, font=cfont, bg="black", fg='yellow')
            clock.grid(row=0, column=0)
            
            while self.count > 0 and self.stop is False:
                sec()
                newWin.after(1000, newWin.update(), run-1)
            else:
                newWin.destroy()
            ############
            
        root = Tk()
        root.title('Timer')
        root.config(bg='black')
        font = ('Monospace', 40)
        fg = 'white'
        bg = 'black'
        
        hTime = Label(root, text="Hours:", font=font, fg=fg, bg=bg)
        hTime.grid(row=1, column=1, sticky=W)
        hEntry = Entry(
            root, width=3, font=font, highlightcolor='blue', 
            highlightthickness=3)
        hEntry.insert(END, '0')                    
        hEntry.grid(row=1, column=2,)

        mTime = Label(root, text="Minutes:", font=font, fg=fg, bg=bg)
        mTime.grid(row=2, column=1, sticky=W)
        mEntry = Entry(
            root, width=3, font=font, highlightcolor='blue', 
            highlightthickness=3)
        mEntry.insert(END, '0')
        mEntry.grid(row=2, column=2)

        sTime = Label(root, text="Seconds:", font=font, fg=fg, bg=bg)
        sTime.grid(row=3, column=1, sticky=W)
        sEntry = Entry(
            root, width=3, font=font, highlightcolor='blue', 
            highlightthickness=3)
        sEntry.insert(END, '5')
        sEntry.grid(row=3, column=2)

        blank = Label(root, font=font, bg='black')
        blank.grid(row=4)

        button1=Button(
            root, text="Start", font=font, fg=fg, bg='green', 
            highlightcolor='blue', highlightthickness=3, 
            command=start)
        button1.grid(row=5, column=1, sticky=W)#columnspan=3)
        root.bind('<Return>', lambda event=None: start())
        
        button2=Button(
            root, text="Stop", font=font, fg=fg, bg='red', 
            highlightcolor='blue', highlightthickness=3, 
            command=stop)
        button2.grid(row=5, column=2)#columnspan=3)

        running = Label(root, fg='green', bg=bg, font=font)
        running.grid(row=6, columnspan=3)

        done = Label(root, fg='red', bg=bg, font=font)
        done.grid(row=6, columnspan=3)

        root.mainloop()
    
if __name__ == "__main__":
    timer()
[humor]Have you guessed that I'm sitting at home practicing tkinter?[/humor]
 
Old 04-25-2020, 02:53 PM   #7
Michael Uplawski
Senior Member
 
Registered: Dec 2015
Posts: 1,622
Blog Entries: 40

Rep: Reputation: Disabled
[Ruby] Busy-Indicator

The title of my post says half of it.

Half of the pride comes from the fact that I found the name “busy-indicator” for my Ruby-Class right before I learned that this is what you usually call such a routine or object... also in different languages than Ruby.

So this serves to show activity while a program does something “in the background”. The whole code is in an archive that I link here: http://www.uplawski.eu/busy_indicato...dicator.tar.xz
A GnuPG-signature for the archive is attached to this post (key RSA C36288CD0571D71B0D7642A1688FE966708A032E).

I show you here the principle class “BusyIndicator” and continue my palaver below:
Code:
#encoding 'UTF-8'
# ©2011 - 2020 Michael Uplawski <michael.uplawski@uplawski.eu>
require_relative 'color_output'

class BusyIndicator
	attr_writer :width
	def initialize(start = true, width = nil)
		@width = width && width >= 3 ? width : 3
		@thr = busy_indicator(@width) if start
                
                #default sequence
                @sequence = %w"OOO ooo ___ ooo"
	end
	
        def sequence=(seq)
          if seq.respond_to?(:to_str)
            seq = seq.split
            seq.each do |w| 
              if w.length > @width
                puts "ATTN! At least one element of the sequence is too long (> ${@width}) !"
                exit false
              end
            end

            @sequence = seq 
          else
            puts "ATTN! Sequence is not a string (but ${seq.class.name}). Exiting."
            exit false
          end
        end

	def run() 
		@thr = busy_indicator(@width)
	end

	def stop(comment)
		@thr.terminate
		@thr.join
		print ("\b" * @width)
		print ("%+#{@width}s\n" %comment)
	end
	private
	def busy_indicator(width)
		tobj = Thread.new() do
			loop do 
                                @sequence.each do |s|
					print "%+#{width}s" %s
					sleep 0.1
					print ("\b" * width)
				end
			end
		end
	end
end
# =========== TEST ============
if($0 == __FILE__)
	width = 20
	2.times do |n|
		puts "I am busy, pse wait..."
		thr = BusyIndicator.new(true, width)
		sleep 3
		thr.stop(cyan("\n\tOkay") ) if n == 1
                thr.stop(yellow("\n\tDone")) if n != 1
	end
        bi = BusyIndicator.new(false, width)
	bi.run
	sleep 3
	bi.stop("Stopped")
        bi.sequence = ".. :: || II CC OO oo --"
	bi.width = 8
	bi.run
	sleep 3
	bi.stop(red "stopped again")
end
# Ω
The test-code at the bottom is executed when you call the Ruby-Interpreter with this file as argument, i.e. when you do as if it were an executable and provided, the file “color_output.rb” is present, too.

The archive contains in the sub-directory “bin” an executable Ruby-program which serves as example-code and demonstrator. It can also be called in shell-scripts... I think of something like this:
Code:
# --- some shell script code omitted ---
busy "|| TT YY VV UU OO oo uu -- .. -- uu oo OO UU YY TT" &
# --- some shell script code omitted ---
killall --signal SIGTERM busy
# EOF
You can call “busy” without argument to see a default text-animation. Otherwise, the argument is a list of symbols which will be at display while the program runs, each one for 0.1 seconds.

You must hit the key 'q' to end the busy-program, other keys are ignored

Here is the code of the example-program “busy”:
Code:
#!/bin/env ruby

require_relative "../busy_indicator"
require_relative "../user_input"

bi = BusyIndicator.new(false, 4)

if !ARGV.empty? 
  bi.sequence=ARGV[0]
end
bi.run
while true
  if wait_for_user().chr == 'q'
    bi.stop("")
    exit true
  end
end
Attached Files
File Type: txt busy_indicator.tar.xz.txt (659 Bytes, 12 views)

Last edited by Michael Uplawski; 04-26-2020 at 12:22 PM. Reason: Nicer example sequence
 
Old 04-26-2020, 10:40 AM   #8
jmgibson1981
Senior Member
 
Registered: Jun 2015
Location: Tucson, AZ USA
Distribution: Debian
Posts: 1,150

Rep: Reputation: 393Reputation: 393Reputation: 393Reputation: 393
In another thread a few here helped me get a download script sorted out. Happy about that. Another I've been fiddling with over time is my backup script. It's gone through several rewrites and minor changes. I'm fairly happy with it now.

Code:
for user in /home/* ; do
  BACKUPUSER=$(basename "$user")
  TARGETDIR="$POOLLOC"/backups/homebackups/"$HOSTNAME"/"$BACKUPUSER"
  if [[ ! -d "$TARGETDIR" ]] ; then
    mkdir -p "$TARGETDIR"
    chown -R "$BACKUPUSER":"$BACKUPUSER" "$TARGETDIR"
  fi
  su -c "duplicity \
  --exclude-if-present .nobackup \
  --no-encryption \
  --full-if-older-than 1M \
  su -c \
  "duplicity remove-all-but-n-full 4 --force file://${TARGETDIR}" \
  "$BACKUPUSER"		
done
 
Old 04-26-2020, 04:47 PM   #9
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,147

Original Poster
Blog Entries: 6

Rep: Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834Reputation: 1834
Beginnings of a paint/draw program.
Needs python-pillow, python-pyscreenshot for saving.
Or #from pyscreenshot import * to use without. You could use a global in getXY and addLine instead of appending xy to list. I wanted to see if I could make a scrolling list. You could also return something from those functions and share variables with the class.
Code:
#!/usr/bin/python

#Draw with mouse, save to file

from tkinter import *
from pyscreenshot import *

class Paint():
    def __init__(self):  
        Lxy = []
        def getXY(self):
            Lx = self.x
            Ly = self.y
            Lxy.append(Lx)
            Lxy.append(Ly)

        def addLine(self):
            getXY(self)
            canvas.create_line((Lxy[0], Lxy[1], Lxy[2], Lxy[3]))
            del Lxy[:-2]
            
        def clear(self):
            canvas.delete("all")
            
        def save(self):
            x=root.winfo_rootx()+canvas.winfo_x()
            y=root.winfo_rooty()+canvas.winfo_y()
            x1=x+canvas.winfo_width()
            y1=y+canvas.winfo_height()
            im = grab((x, y, x1, y1))
            im.save("TkDraw.png")

        root = Tk()
        root.geometry("800x600")
        root.title('TkDraw')
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)

        canvas = Canvas(root)
        canvas.config(bg='white')
        canvas.grid(column=0, row=0, sticky=(N, W, E, S))
        #Left button draw
        canvas.bind("<Button-1>", getXY)
        canvas.bind("<B1-Motion>", addLine)
        #Right click on 3 button mouse, clear canvas
        canvas.bind("<Button-3>", clear)
        #Control s save to file
        root.bind("<Control-s>", save)
        
        root.mainloop()
            
if __name__ == '__main__':
    Paint()
 
Old 04-30-2020, 07:26 AM   #10
WideOpenSkies
Member
 
Registered: May 2019
Location: /home/
Distribution: Arch Linux
Posts: 166

Rep: Reputation: 61
I've been teaching myself C and, with all this time at home, have rewritten most of my bash scripts in C. It's been a journey, but I see why so many people like the language: It's fast, gives you all control, and extremely portable.
 
Old 05-02-2020, 06:29 PM   #11
jmgibson1981
Senior Member
 
Registered: Jun 2015
Location: Tucson, AZ USA
Distribution: Debian
Posts: 1,150

Rep: Reputation: 393Reputation: 393Reputation: 393Reputation: 393
In the last few days since I've been playing more games on Windows lately I've added an Xserver to my installation via Xming, set up an lxd container on my server just for ssh tunneling. I use Kate for my scripts mainly and can now use it on Windows. I just finished hacking together a script for backing up Windows user directories via sharing directories and mounting them with systemd autmount in /etc/fstab. This script runs at 3 am and also backs up the big download part of World of Warcraft. I need to find a better way of getting the mac addresses but this seems to work, and it's not run often enough to be a problem in regards to using available muscle.

Code:
for windowsbox in "$@" ; do
		if ! ping -c 1 "$windowsbox".mylan.home ; then
			WINMACADDRESS=$(ssh jason@services.mylan.home grep -A 1 "$windowsbox" \
			/etc/dhcp/ltsp.dhcpd.conf | grep ethernet | awk '{print $3}' \
			| cut -d \; -f 1)
			wakeonlan "$WINMACADDRESS"
			sleep 180
		fi
		WINMOUNT=/mnt/windows/"$windowsbox"
		BACKUPDIR="$POOLLOC"/backups/homebackups/"$windowsbox".windows
		if [[ -d "$WINMOUNT"/wow ]] ; then
			[[ -d "$BACKUPDIR"/wow ]] || mkdir -p "$BACKUPDIR"/wow/
			for folder in Data _retail_ ; do
				rsync -auv "$WINMOUNT"/wow/"$folder" "$BACKUPDIR"/wow/
			done
		fi
		if [[ -d "$WINMOUNT"/users/Default ]] ; then
			for folder in $(find "$WINMOUNT"/users/ -maxdepth 1 -mindepth 1 -type d) \
			; do
				FOLDER=$(basename "$folder")
				if [[ "$FOLDER" != Default ]] && [[ "$FOLDER" != desktop.ini ]] ; then
					# this case is only because i haven't bothered changing my windows username.
                                        case "$FOLDER" in
						jmgib)
							SERVERUSER=jason
							;;
					esac
					if [[ ! -d "$BACKUPDIR"/users/"$FOLDER" ]] ; then
						echo "${BACKUPDIR}/users/${FOLDER}"
						mkdir -p "$BACKUPDIR"/users/"$FOLDER"
						chown "$SERVERUSER":"$SERVERUSER" "$BACKUPDIR"/users/"$FOLDER"
					fi
					su -c "rsync -auv --exclude={'OneDrive','MicrosoftEdgeBackups'} \
					${folder} ${BACKUPDIR}/users/" "$SERVERUSER"
				fi
			done
		fi
	done
 
Old 05-05-2020, 09:47 AM   #12
Steve R.
Member
 
Registered: Jun 2009
Location: Morehead City, NC
Distribution: Mint 20.3
Posts: 521

Rep: Reputation: 98
Google Chrome has an irritating habit of asking for a password to unlock login keyring. This happens every time there is an update to google chrome. (I don't know if this issue applies to other Linux distributions. I am using Mint) I used to fix manually, it's a quick and easy manual fix; but I got tired of doing that so I wrote the script below. It was a very good learning experience.

Code:
#!/bin/bash
#
# Google Chrome Asks Password to Unlock Login Keyring
# This script must be run using sudo. "sudo bash fix_chrome.sh" 
# https://tipsonubuntu.com/2017/12/20/...login-keyring/
# https://www.cyberciti.biz/faq/how-to...ux-unix-shell/
# April 28, 2020
#
chrome_dir="/usr/share/applications"
chrome_filename="google-chrome.desktop"
# Check how to use regex in Linux
# ^ needed to limit search to string at begging of line.
# $ End of line anchor
# . Any one intervening characters
# Note the %U in the string "Exec=/usr/bin/google-chrome-stable %U". Does not appear to be normal ASCII characters
# Substituting ... for the space plus %U
# The / need to be escaped with \ result -> \/
# Exec=/usr/bin/google-chrome-stable --password-store=basic %U
string_to_find='^Exec=/usr/bin/google-chrome-stable...$'
string_to_insert='password-store=basic'
string_new='Exec=\/usr\/bin\/google-chrome-stable --password-store=basic %U'
#
# Move to the Directory the Chrome file is located in.
cd ${chrome_dir}

# Check if file exists
if [ -f ${chrome_filename} ] 
    then 
        printf "\nFile Exists" 
    else
        printf "\nFile Does NOT Exist. Is Chrome installed? Program terminated\n"
        exit 1
    fi

# Verify whether google-chrome.desktop has or has not been already inserted
if grep -qw ${string_to_insert} ${chrome_filename}
    then
        printf "\n${string_to_insert} string found. Nothing needs to be done. Already fixed.\n" 
        exit 1
    fi  

# Check if there is a unique occurance of the string-to-find.
num_found=$(grep -c  ${string_to_find} ${chrome_filename})
if [ $num_found -eq 1 ]
    then
        printf "\nUnique occurance found.\n"
        line_number=$(grep -n  ${string_to_find} ${chrome_filename} |  cut -f1 -d: )
        printf "Line Number: ${line_number}\n"
    else
        printf "\nFAILED. A total of ${num_found} occurance were found. Must have only one unique occurance. Program terminated.\n"
        exit 1
    fi

# Make Modification to file
sed -i "${line_number}s/.*/${string_new}/" "${chrome_filename}"
    if [ $? -eq 0 ]
        then
            echo "Success. Correction Made."
        else
            echo "FAILED to update"
        fi
Hopefully others may find this quick fix useful.

PS: Purposely avoided the use of nested if statements.
 
Old 05-05-2020, 12:17 PM   #13
Hacker7691
LQ Newbie
 
Registered: May 2020
Posts: 4

Rep: Reputation: 1
I'd recommend you StackOverFlow. Seems like this forum knows every single answer to programming questions. I also started to do some coding during quarantine. And if I don't know or understand something, I go there. Sometimes even GitHub has some part of code that you need. Hope this will help you in the future.
 
Old 05-06-2020, 02:12 AM   #14
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 19,872
Blog Entries: 12

Rep: Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053
Quote:
Originally Posted by Steve R. View Post
Google Chrome has an irritating habit of asking for a password to unlock login keyring.
Using closed-source data mining software on a FOSS system... :smh:
Just use Chromium, no such problems there.
 
Old 05-06-2020, 07:00 AM   #15
Steve R.
Member
 
Registered: Jun 2009
Location: Morehead City, NC
Distribution: Mint 20.3
Posts: 521

Rep: Reputation: 98
Quote:
Originally Posted by ondoho View Post
Using closed-source data mining software on a FOSS system... :smh:
Just use Chromium, no such problems there.
Point taken.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Clam AV finds but does not quarantine, how shall one work it? Novatian Linux - Software 4 06-08-2014 08:20 AM
Is it possible to run VB scrips in Linux? to_veera Linux - Software 2 06-07-2004 06:44 AM
EXIM - sending spam from PHP scrips GRisha Linux - Software 0 06-05-2004 11:07 AM
Two simple questions...shell scrips and icons vdogvictor Linux - General 4 04-12-2004 05:49 PM
Creating init scrips in Linux rootking Linux - General 9 03-22-2003 01:54 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 01:12 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration