Page 1 of 1

my conky circle theme

Posted: Sat Feb 01, 2025 10:45 am
by django013
Hi,

I'd like to share my conky stuff:
MX-Circles1.jpg
Attached file is an archive. Rename it to *.tar.xz
It contains the font files, a lua-script and a python install script.

The python script tries to read your hardware and generates the conky config files and copies the files to their destinations.
Each group of circles consists of relative positions, so it is easy to move the entire group around.

Update
I changed script according the recomendations of fehlix so that it is now completely written in lua.
You'll need to install "lua-filesystem" to avoid script errors.
The updated archive is a zip, that contains a *.tar.xz file.
The new script works in two modes:
  • for big hardware will create the picture from above
  • smaller hardware will create this picture:
    MX-Circles0.jpg
Both pictures without manual polishing.

How to install
  • extract the archive to $HOME/.conky
  • open a terminal and go to $HOME/.conky/MX-Circles
  • call "conky -c MX-Circles" - which will determine your hardware and create the config files
  • when the rings appear hit Strg+C to terminate conky
  • start the real conky with "conky -c MX-Circles -d"
Have fun!

Re: my conky circle theme

Posted: Sat Feb 01, 2025 10:57 am
by AVLinux
Wow, that's cool!

You've put a LOT of work into those!

Re: my conky circle theme

Posted: Sat Feb 01, 2025 11:34 am
by CharlesV
+1 that is VERY nice!

Re: my conky circle theme

Posted: Sat Feb 01, 2025 4:46 pm
by Germ
Looks great!

Re: my conky circle theme

Posted: Sun Feb 02, 2025 1:14 am
by django013
Thank you guys!

Here's a workaround for sensor handling (until I know, howto call inline lua functions).
Save this as /usr/bin/conky_printTemperature
(of cause you need an installed lua interpreter for it to work)

Code: Select all

#!/usr/bin/lua

function cpT(path)
    local f = io.open(path, "rb")
    if not f then return 0 end
    local rv = f:read("*a")
    f:close()
    return tonumber(rv) / 1000.0
end

print(cpT(arg[1]))
and here's the installer, that uses above script:

Code: Select all

#!/usr/bin/env python3
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2021 django013
#
# project: conky.arcs theme for conky
# purpose: installation script for conky.arcs theme
#          Script explores running system and generates matching
#          gui elements for conky system monitor.
import os
import sys
import glob
import array
import logging as log
import pathlib
import subprocess
from operator import itemgetter, attrgetter, methodcaller


def conkyCAPsFailed(app):
    print(f'conky [{app}] is not capable to run conky.arcs theme.')
    print('conky.arcs needs conky with lua and cairo support.')
    print('Try to install "conky-all" or use an appimage from ')
    print('https://github.com/brndnmtthws/conky/releases')
    print('')
    exit(-1)


def first(iterable, condition = lambda x: True):
    return next(x for x in iterable if condition(x))


def usage():
    print('conky.arc is a theme for system monitor app "conky"')
    print('this install-script evaluates your system and generates')
    print('matching config file.')
    print('')
    print('if conky is not found by PATH environment, ...')
    print('please provide the path to conky application as commandline ')
    print('argument. Install script then checks capability of conky.')
    print('')
    exit(0)


class ConfigReader:
  def __init__(self, conky):
      hasLua, hasCairo = self.evalConky(conky)
      if not hasLua or not hasCairo:
         conkyCAPsFailed(conky)
      self.app     = conky
      self.curdir  = os.getcwd()
      self.homedir = pathlib.Path.home()
      self.sensors = self.getSensors()
      self.nCPU    = self.countCPU()
      self.netIF   = self.networkIF()
      self.md      = self.mounted_drives()
      self.hds     = self.harddisks()
      self.cpuFreqs()


  def countCPU(self, maxCPU = 6):
      cpus0 = glob.glob('/sys/devices/system/cpu/cpu?')
      cpus1 = glob.glob('/sys/devices/system/cpu/cpu??')
      cpus = sorted(cpus0 + cpus1)

      return len(cpus)


  def cpuFreqs(self):
      f0 = 10000
      f1 = 0
      with open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq') as f:
           for line in f:
               f0 = int(line) / 1000

      with open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq') as f:
           for line in f:
               f1 = int(line) / 1000
      self.fCPU0 = f0
      self.fCPU1 = f1


  def evalConky(self, app):
      p = pathlib.Path(app)
      if not p.exists():
         print('conky NOT found. Please install "conky-all"')
         print('')
         exit(0)
      p = subprocess.Popen([app, '--version'], stdout=subprocess.PIPE)
      line = p.stdout.readline()
      hasLua   = False
      hasCairo = False
      while line:
            if line.strip().startswith(b'Lua bind'):
               hasLua = True
               line = p.stdout.readline()
               while line.strip().startswith(b'*'):
                     parts = line.strip().split(b' ')
                     if parts[1] == b'Cairo':
                        hasCairo = True
                        return [hasLua, hasCairo]
                     line = p.stdout.readline()
            line = p.stdout.readline()
      return [hasLua, hasCairo]


  def getSensors(self):
      sensors = []
      paths = glob.glob('/sys/class/hwmon/hwmon*')
      for i in range(0, len(paths)):
          sensor = {}
          with open('/sys/class/hwmon/hwmon{}/name'.format(i)) as f:
              name = f.read().strip()
              sensor['type'] = name
              sensor['name'] = name
              if ('nvme' in name):
                  subname = first(glob.glob('/sys/class/hwmon/hwmon{0}/device/nvme*'.format(i)))
                  subname = os.path.basename(subname)
                  sensor['name'] = subname
              elif ('spd5118' in name):
                  subname = 'SMBus PIIX4'
                  sensor['name'] = subname
              temp = 0
              path = '/sys/class/hwmon/hwmon{}/temp1_input'.format(i)
              p = pathlib.Path(path)
              if not p.exists():
                  continue
              sensor['path'] = path
              with open(path) as ft:
                   temp = float(ft.read()) / 1000.0
              sensor['temp'] = temp
              sensors.append(sensor)
      sensors.sort(key=lambda x: x['name'], reverse=False)
      return sensors


  def mounted_drives(self):
      drives = []
      check = []    # avoid duplicates
      with open('/proc/mounts') as f:
           for line in f:
               if not line.startswith('/'):
                  continue
               if 'loop' in line:
                  continue
               parts = line.split()
               mountPoint = {}
               if parts[0] in check:
                  continue
               else:
                  check.append(parts[0])
                  sp = parts[0].split('/')
                  mountPoint['part'] = sp[2]
                  mountPoint['mounted'] = parts[1]
               drives.append(mountPoint)
      drives.sort(key=lambda x: x['mounted'], reverse=False)
      return drives


  def harddisks(self):
      disks0 = sorted(glob.glob('/dev/sd?'))
      disks1 = sorted(glob.glob('/dev/nvme?n?'))
      hdds = {}
      for d in disks0:
          parts = d.split('/')
          hdds[parts[2]] = d
      for d in disks1:
          parts = d.split('/')
          hdds[parts[2]] = d
      return hdds


  def networkIF(self):
      nif = []
      with open('/proc/net/dev') as f:
           for line in f:
               ifName = line.split()[0]
               if ifName == 'Inter-|' or ifName == 'face' or ifName == 'lo:':
                  continue
               nif.append(ifName.split(':')[0])
      return nif


  def dump(self):
      print('============================================================')
      print('conky [{0}] has lua and cairo support'.format(self.app))
      print('curdir:\t' + self.curdir)
      print('homedir:\t' + str(self.homedir))
      print('found {0} cpu cores'.format(self.nCPU))
      if len(self.sensors) < 2:
         print('NO temperatures for gpu!')
      print('network interfaces:')
      for n in self.netIF:
          print('\tfound interface:\t{0}'.format(n))
      print('harddisks:')
      for d in self.hds:
          print('\tfound disk:\t{0}'.format(d))
      print('mounted partitions:')
      for d in self.md:
          print('\t{0}\twith path:\t{1}'.format(d['part'], d['mounted']))
      print('sensors')
      for s in self.sensors:
          print('sensor {0} has temp {1}\t\tat: {2}'.format(s['name'], s['temp'], s['path']))
      print('============================================================')


class ConfigWriter:
  def __init__(self, cfgReader):
      self.cr = cfgReader
      self.base = str(self.cr.homedir) + '/.conky/MX-Circles'
      self.LuaTemplates = {}
      self.ConkyTemplates = {}
      self.ConkyTemplates['intro'] = """conky.config = {
    background = false,
    update_interval = 2,
    cpu_avg_samples = 2,
    net_avg_samples = 2,
    temperature_unit = 'celsius',
    double_buffer = true,
    no_buffers = true,
    text_buffer_size = 2048,
    gap_x = 2,
    gap_y = 50,
    minimum_width = 1020,
    minimum_height = 1200,
    own_window = true,
    own_window_type = 'normal',
    own_window_transparent = true,
    own_window_argb_visual = true,
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
    border_inner_margin = 0,
    border_outer_margin = 0,
    alignment = 'top_right',
    draw_shades = false,
    draw_outline = false,
    draw_borders = false,
    draw_graph_borders = false,
    override_utf8_locale = true,
    use_xft = true,
    xftalpha = 0.5,
    font = 'Cuprum:size=7',
    top_cpu_separate = true,
    lua_load = './conky.arcs.lua',
    lua_draw_hook_post = 'main'
}

conky.text = [[
${voffset 460}"""
      self.ConkyTemplates['diskRead'] = '\n${{goto {0}}}${{diskiograph_read    44, 300 ADFF2F 32CD32 -t -l}}'
      self.ConkyTemplates['diskWrite'] = '\n${{goto {0}}}${{diskiograph_write   44, 300 FF0000 8B0000 -t -l}}'
      self.ConkyTemplates['netDown'] = '\n${{goto {0}}}${{downspeedgraph {1} 21, 240 ADFF2F 32CD32 -t -l}}'
      self.ConkyTemplates['netUp'] = '\n${{goto {0}}}${{upspeedgraph   {1} 21, 240 FF0000 8B0000 -t -l}}'
      self.ConkyTemplates['fin'] = '\n]]'
      self.LuaTemplates['intro'] = """local config = {}

function config.setup()
  return {"""
      self.LuaTemplates['inter0'] = """
    },
    sub = {"""
      self.LuaTemplates['inter1'] = """
    },
    rings = {"""
      self.LuaTemplates['fin'] = """
}
end

return config"""
      self.LuaTemplates['main0'] = """
  x     = 11,
  y     =  0,
  fs    = 18,
  font  = 'Cuprum',
  hFont = 'Vast Shadow',
  rCol  = '95275C',
  rBgC  = '565656',
  color = 'FFFFFF',
  col1  = 'F2B70E',
  col2  = 'E90812',
  col3  = 'CDCDCD',
  bgCol = '993636',
  alpha = 0.9,
  sb    = false,
  uptime = {
  --[[
      x/y the position where the text should be aligned to
      a: L means text ends at x/y or is on the left side of position
      a: R means text starts at x/y or is on the right side of position
      text starting with '$' will be translated by conky engine
    ]]--
    { x = 37, y = 2, a = 'L', s = 30, text = 'Uptime' },
    { x = 38, y = 2, a = 'R', s = 30, text = '${uptime_short}' },
  },
  date = {"""
      self.LuaTemplates['mainDate'] = """
    {{ x = 27, y = {0}, a = 'L', s = 40, text = '${{time %A}}' }},
    {{ x = 29, y = {0}, a = 'R', s = 40, text = '${{time %x}}' }},
  }},
"""
      self.LuaTemplates['mainTime'] = """
  time = {{
    {{ x = 35, y = {0}, a = 'L', s = 80, text = '${{time %H}}' }},
    {{ x = 37, y = {0}, a = 'R', s = 80, text = ':' }},
    {{ x = 41, y = {0}, a = 'R', s = 80, text = '${{time %M}}' }},
  }},
  sys = {{"""
      self.LuaTemplates['main1'] = """
    {{ x = 37, y =  {0},   a = 'L', s = 20, text = 'public IP:' }},
    {{ x = 38, y =  {0},   a = 'R', s = 20, text = "${{execi 5 wget -q -O - checkip.dyndns.org | sed -e 's/[^[:digit:]\\\\|.]//g'}}" }},
    {{ x = 15, y = {1},   a = 'R', s = 30, text = "${{execi 3600 cat /etc/mx-version | awk '{{ print $1 \\" \\" $2; }}' | sed -e 's/_/ /g'}}" }},
    {{ x = 15, y = {2},   a = 'R', s = 20, text = "${{execi 3600 grep PRETTY_NAME /etc/os-release | sed -e 's/=/ /' | awk '{{ print $2 \\" \\" $3 \\" \\" $4 \\" \\" $5; }}' | sed -e 's/\\"//g'}}" }},"""
      self.LuaTemplates['IOheaders'] = """
    {{ x = 87, y = {0},   a = 'R', s = 25, text = 'network Traffic' }},
    {{ x = 87, y = {1},   a = 'R', s = 25, text = 'disk Traffic' }},
  }},
"""
      self.LuaTemplates['netMain'] = "\n    {{ x = 37, y =  {0},   a = 'L', s = 20, text = 'local {1}:' }},\n    {{ x = 38, y =  {0},   a = 'R', s = 20, text = '${{addr {1}}}' }},"
      self.LuaTemplates['cpuInit'] = """
  cpu = {
    x = 79,
    y =  0,
    w = 30,
    h = 13,
    level1 = 3500,
    level2 = 4500,
    main = {"""
      self.LuaTemplates['cpuMain0'] = """
      {{ x =  0, y =  {0}, a = 'R', text = 'CPU' }},
      {{ x = 15, y =  {0}, a = 'L', text = '${{cpu cpu{1}}}%' }},"""
      self.LuaTemplates['cpuMainN'] = """
      {{ x =  0, y =  {0}, a = 'R', text = 'CPU {1}' }},
      {{ x = 10, y =  {0}, a = 'L', text = '${{freq {1}}}' }},
      {{ x = 15, y =  {0}, a = 'L', text = '${{cpu cpu{1}}}%' }},"""
      self.LuaTemplates['cpuTop'] = """
      {{ x =  6, y = {0}, a = 'R', text = '${{top name {1}}}' }},
      {{ x = 26, y = {0}, a = 'L', text = '${{top cpu {1}}}%' }},"""
      self.LuaTemplates['cpuRing0'] = "\n      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'cw', name = 'cpu', arg = '', max = 100, sa = 0, ea = 300, ba = {2}, fg = 'F38A07' }},"
      self.LuaTemplates['cpuRingN'] = "\n      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'cw', name = 'cpu', arg = 'cpu{2}', max = 100, sa = 0, ea = 300, ba = {3} }},"
      self.LuaTemplates['cpuFin'] = """
    },
  },
"""
      self.LuaTemplates['memInit'] = """
  mem = {
    x = 49,
    y = 38,
    w = 36,
    h = 16,
    main = {
      { x = 15, y = 1, a = 'R', text = 'Mem:' },
      { x = 28, y = 1, a = 'L', text = '${mem}' },
      { x = 29, y = 1, a = 'L', text = '/' },
      { x = 30, y = 1, a = 'R', text = '${memmax}' },
      { x = 15, y = 2, a = 'R', text = 'Swap:' },
      { x = 28, y = 2, a = 'L', text = '${swap}' },
      { x = 29, y = 2, a = 'L', text = '/' },
      { x = 30, y = 2, a = 'R', text = '${swapmax}' },
    },
    sub = {"""
      self.LuaTemplates['memVal'] = """
      {{ x = 15, y =  {0}, a = 'R', text = '{2} Down' }},
      {{ x = 36, y =  {0}, a = 'L', text = '${{totaldown {2}}}' }},
      {{ x = 15, y =  {1}, a = 'R', text = '{2} UP' }},
      {{ x = 36, y =  {1}, a = 'L', text = '${{totalup {2}}}' }},
"""
      self.LuaTemplates['memTop'] = """
      {{ x = 10, y =  {0}, a = 'R', text = '${{top_mem name {1}}}' }},
      {{ x = 28, y =  {0}, a = 'L', text = '${{top_mem mem  {1}}}%' }},"""
      self.LuaTemplates['memRing0'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'ccw', name = 'memperc',    arg = '',     max = 100, sa = 0, ea = 65, ba = 0.8 }},
      {{ x = 15, y = {0}, r = {2}, h = 0.5, d = 'ccw', name = 'swapperc',   arg = '',     max = 100, sa = 0, ea = 60, ba = 0.8 }},"""
      self.LuaTemplates['memRingN'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'ccw', name = 'downspeedf', arg = '{2}', max = 100, sa = 0, ea = 55, ba = {3} }},
      {{ x = 15, y = {0}, r = {4}, h = 0.9, d = 'ccw', name = 'upspeedf',   arg = '{2}', max = 100, sa = 0, ea = 45, ba = {3} }},"""
      self.LuaTemplates['memFin'] = """    },
  },
"""
      self.LuaTemplates['hddInit'] = """
  lh  = 1,
  lw  = 1,
  hdd = {
    x = 87,
    y = 60,
    w = 29,
    h =  6,
    main = {"""
      self.LuaTemplates['hddMain'] = """
      {{ x = 11.5, y = {0},  a = 'R', text = '{1}' }},"""
      self.LuaTemplates['hddSub']  = """
      {{ x = 22, y = {0},  a = 'R', text = '{1}' }},"""
      self.LuaTemplates['hddRing'] = """
      {{ x = 11, y = {0}, r = {1}, h = 0.7, d = 'cw', name = 'fs_used_perc', arg = '{2}', max = 100, sa = 180, ea = 455, ba = {3} }},"""
      self.LuaTemplates['hddFin'] = """    },
  },
"""
      self.LuaTemplates['tempInit'] = """
  temp = {
    x = 36,
    y = 17,
    w = 31,
    h = 16,
    main = {"""
      self.LuaTemplates['tempMain_notYet'] = """
      {{ x = 10, y = {0}, a = 'R', text = '${{lua printTemperature({1})}}°' }},"""
      self.LuaTemplates['tempMain'] = """
      {{ x = 10, y = {0}, a = 'R', text = '${{exec conky_printTemperature {1}}}°' }},"""
      self.LuaTemplates['tempName'] = """
      {{ x =  9, y = {0}, a = 'L', text = '{1}' }},"""
      self.LuaTemplates['tempRing'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'cw', name = 'exec conky_printTemperature', arg = '{2}', max = 100, sa = 0, ea = 270, ba = {3}}},"""
      self.LuaTemplates['tempFin'] = """
    },
  },"""


  def copyFonts(self):
      fd = '/usr/share/fonts/truetype/freefonts'
      print("need to install some fonts\n")
      subprocess.check_call(['sudo', 'mkdir', '-p', fd])
      fonts = glob.glob('fonts/*.ttf')
      for f in fonts:
          subprocess.check_call(['sudo', 'cp', f, fd])


  def install(self):
      p = pathlib.Path(self.base)
      if not p.exists():
          os.makedirs(self.base)
      self.copyFonts()
      subprocess.check_call(['cp', 'conky.arcs.lua', self.base])
      self.writeConkyConfig()
      self.writeLuaConfig()


  def writeConkyConfig(self):
      with open(self.base + '/MX-Circles', 'w') as f:
           f.write(self.ConkyTemplates['intro'])
           for ifn in self.cr.netIF:
               f.write(self.ConkyTemplates['netDown'].format(750, ifn))
           for ifn in self.cr.netIF:
               f.write(self.ConkyTemplates['netUp'].format(750, ifn))
           f.write(self.ConkyTemplates['diskRead'].format(710))
           f.write(self.ConkyTemplates['diskWrite'].format(710))
           f.write(self.ConkyTemplates['fin'])


  def writeLuaConfig(self):
      with open(self.base + '/config.lua', 'w') as f:
           f.write(self.LuaTemplates['intro'])
           self.writeMainSection(f)
           self.writeCpuSection(f)
           self.writeIOSection(f)
           self.writeMemSection(f)
           self.writeHddSection(f)
           self.writeTempSection(f)
           f.write(self.LuaTemplates['fin'])


  def writeMainSection(self, f):
      f.write(self.LuaTemplates['main0'])
      y = 5 + len(self.cr.netIF)
      f.write(self.LuaTemplates['mainDate'].format(y))
      y = 9 + len(self.cr.netIF)
      f.write(self.LuaTemplates['mainTime'].format(y))
      y = 3
      for ifn in self.cr.netIF:
          f.write(self.LuaTemplates['netMain'].format(y, ifn))
          y += 1
      y0 =  y
      y1 =  8 + y
      y2 =  9 + y
      f.write(self.LuaTemplates['main1'].format(y0, y1, y2))
      y0 = 30 + len(self.cr.netIF) * 2
      y1 = 36 + len(self.cr.netIF) * 3
      f.write(self.LuaTemplates['IOheaders'].format(y0, y1))


  def writeCpuSection(self, f):
      f.write(self.LuaTemplates['cpuInit'])
      f.write(self.LuaTemplates['cpuMain0'].format(1, 0))
      for i in range(1, 1 + self.cr.nCPU):
          f.write(self.LuaTemplates['cpuMainN'].format(1 + i, i))
      f.write(self.LuaTemplates['inter0'])
      for i in range(0, min(10, self.cr.nCPU)):
          f.write(self.LuaTemplates['cpuTop'].format(3 + self.cr.nCPU + i, 1 + i))
      f.write(self.LuaTemplates['inter1'])
      trans = 0.7
      f.write(self.LuaTemplates['cpuRing0'].format(1 + self.cr.nCPU, 2 + self.cr.nCPU, trans))
      for i in range(0, self.cr.nCPU):
          if (i % 2 == 0):
             trans -= 0.1
          trans = max(0.3, trans)
          f.write(self.LuaTemplates['cpuRingN'].format(1 + self.cr.nCPU, 1 + self.cr.nCPU - i, 1 + i, trans))
      f.write(self.LuaTemplates['cpuFin'])


  def writeIOSection(self, f):
      f.write("""
  io = {
    x = 73,
    y = 28,
    w = 35,
    h =  9,
    main = {
      { x = 15, y = 5, a = 'R', text = 'Disk Reads' },
      { x = 14, y = 5, a = 'L', text = '${diskio_read}' },
      { x = 15, y = 6, a = 'R', text = 'Disk Writes' },
      { x = 14, y = 6, a = 'L', text = '${diskio_write}' },
    },
    sub = {
      { x = 15, y =  7, a = 'R', text = '${top_io name 1}' },
      { x = 14, y =  7, a = 'L', text = '${top_io io_write 1}' },
      { x = 15, y =  8, a = 'R', text = '${top_io name 2}' },
      { x = 14, y =  8, a = 'L', text = '${top_io io_write 2}' },
      { x = 15, y =  9, a = 'R', text = '${top_io name 3}' },
      { x = 14, y =  9, a = 'L', text = '${top_io io_write 3}' },
    },
    rings = {
      { x = 6, y = 3, r = 2, h = 0.8, d = 'ccw', name = 'top_io io_perc 1', arg = '', max = 100, sa = 80, ea = 180, ba = 0.3  },
      { x = 6, y = 3, r = 3, h = 0.8, d = 'ccw', name = 'top_io io_perc 2', arg = '', max = 100, sa = 80, ea = 180, ba = 0.45 },
      { x = 6, y = 3, r = 4, h = 0.8, d = 'ccw', name = 'top_io io_perc 3', arg = '', max = 100, sa = 80, ea = 180, ba = 0.6  },
    },
  },
""")


  def writeMemSection(self, f):
      f.write(self.LuaTemplates['memInit'])
      n=0
      for nif in self.cr.netIF:
          f.write(self.LuaTemplates['memVal'].format(3 + n, 4 + n, nif))
          n += 2
      mx = 2 * len(self.cr.netIF)
      for i in range(1, min(8, 2 + mx)):
          f.write(self.LuaTemplates['memTop'].format(3 + mx + i, i))
      f.write(self.LuaTemplates['inter1'])
      f.write(self.LuaTemplates['memRing0'].format(2 + mx, 3 + mx, 2 + mx))
      n=0
      trans = 0.6
      for nif in self.cr.netIF:
          f.write(self.LuaTemplates['memRingN'].format(2 + mx, 1 + mx - n, nif, trans, mx - n))
          trans -= 0.2
          n += 2
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['memFin'])


  def writeHddSection(self, f):
      f.write(self.LuaTemplates['hddInit'])
      mx = len(self.cr.md)
      n=0
      for mp in self.cr.md:
          f.write(self.LuaTemplates['hddMain'].format(4 + n, mp['part']))
          n += 1
      f.write(self.LuaTemplates['inter0'])
      n=0
      for mp in self.cr.md:
          f.write(self.LuaTemplates['hddSub'].format(4 + n, mp['mounted']))
          n += 1
      f.write(self.LuaTemplates['inter1'])
      revDrives = self.cr.md
      revDrives.sort(key=lambda x: x['mounted'], reverse=True)
      trans = 0.9
      n=0
      for mp in revDrives:
          f.write(self.LuaTemplates['hddRing'].format(0, 1 + mx - n, mp['mounted'], trans))
          n += 1
          if (n % 2 == 1):
             trans -= 0.1
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['hddFin'])


  def writeTempSection(self, f):
      f.write(self.LuaTemplates['tempInit'])
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['tempMain'].format(1 + n, s['path']))
          n += 1
      f.write(self.LuaTemplates['inter0'])
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['tempName'].format(1 + n, s['name']))
          n += 1
      f.write(self.LuaTemplates['inter1'])
      mx = len(self.cr.sensors)
      trans = 0.9
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['tempRing'].format(mx, 1 + mx - n, s['path'], trans))
          n += 1
          trans -= 0.1
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['tempFin'])


if __name__ == "__main__":
   tmp = subprocess.check_output(["/bin/bash", "which", "conky"])
   if not tmp:
      usage()
   conkyapp = tmp.decode('utf-8').strip()
   if len(sys.argv) > 1:
      if sys.argv[1] == '--help':
         usage()
      else:
         p = pathlib.Path(sys.argv[1])
         if p.exists():
            conkyapp = sys.argv[1]
         else:
            usage()
   cr = ConfigReader(conkyapp)
   cr.dump()
   cw = ConfigWriter(cr)
   cw.install()

Re: my conky circle theme

Posted: Mon Feb 03, 2025 3:20 am
by fehlix
django013 wrote: Sun Feb 02, 2025 1:14 am Thank you guys!

Here's a workaround for sensor handling (until I know, howto call inline lua functions).
Save this as /usr/bin/conky_printTemperature
(of cause you need an installed lua interpreter for it to work)
Hmm, ... OK actually I made it work with lua function call ${lua conky_printTemperature /sys..hwmon...path}, so no need the the exec external script.
I did actually placed the lua function into config.lua and renamed your original, also copied into config.lua.
With your original I couldn't get rid of the decimal place, so I adjusted to

Code: Select all

function conky_printTemperature(path)
    local f = io.open(path, "rb")
    if not f then return string.format("% 3.0f", 0) end
    local rv = f:read("*a")
    f:close()
    local temp = tonumber(rv) / 1000.0
    return string.format("% 3.0f", temp)
end
But actually in this special hwmon-case, we don't need a lua function to be execute b/c we can just use conky's build hwmon, e.g. this way;
within the install script changing the below the line
sensor['path'] = path
by adding and hwmon entry into the sensor-table, which holds the parameter to the conky-hwmon call:
sensor['hwmon'] = f"{i} temp 1"
so it look like this:

Code: Select all

              sensor['path'] = path
              sensor['hwmon'] = f"{i} temp 1"
or if you want to be a bit more flexible in cases where temp1_input would not the right one but another one e.g temp2_input
convert to hwmon-path to the conky-hwmon paramter e.g with a "replace" like this:

Code: Select all

  sensor['hwmon'] = (
            path.replace("/sys/class/hwmon/hwmon", "hwmon ")
                .replace("/temp", " temp ")
                .replace("_input", "")
        )
And we can now adjust within lua-conky snipplets these two exec calls :

Code: Select all

       {{ x = 10, y = {0}, a = 'R', text = '${{exec conky_printTemperature {1}}}°' }},"""
and

Code: Select all

       {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'cw', name = 'exec conky_printTemperature', arg = '{2}', max = 100, sa = 0, ea = 270, ba = {3}}},"""
with these two conky-hwmon calls:

Code: Select all

      {{ x = 10, y = {0}, a = 'R', text = '${{hwmon {1}}}°' }},"""
and

Code: Select all

      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'cw', name = 'hwmon', arg = '{2}', max = 100, sa = 0, ea = 270, ba = {3}}},"""
The temps are now properly displayed and also the temp-rings filled accordingly.
Also perhaps, you may consider to filter-out those temp-sensors which do not properly report temperature indicated by a value of "0" e.g. like "acpitz", which seem not to be activated.
A further note. The install-script is relay nice, but maybe you consider to move that "setup" logic into
a lua-function e.g. named like circle_setup(), where you re-formulate the python-logic into this lua-function,
and populate a "global" setup-table. The circle_setup() you could now call
within "MX-Circles" conky.config below the lua_load like this:

Code: Select all

    lua_load = './conky.arcs.lua',
    lua_draw_hook_post = 'main',
    lua_startup_hook = 'circle_setup',
The startup_hook is called only once at start or reload, where you put all detected properties into a global setup-table.
In addition if you would keep the fonts e.g within the sub folder of the conky ".conky/MX-Circles/fonts" you could now refresh the fontconfig cache with the setup-function e.g with something like "fc-cache -r ~/.conky/MX-Circles/fonts",
which would only be needed to run when those fonts are not already available with font-config cache, e.g. which you can check with "fc-list | grep fontname".
What else ... maybe adjust the layout a bit within the setup script, so it will fit into smaller screens, even on my 1920x1080 display the conky was a bit to large b/c the disks-rings didn't fit into the screen properly .
OK, so that's it from my side with a few comments.
Thanks

Re: my conky circle theme

Posted: Mon Feb 03, 2025 5:00 am
by AK-47
This is gorgeous.

Re: my conky circle theme

Posted: Mon Feb 03, 2025 9:15 am
by django013
Thank you fehlix, for your interest!

I know about the problem with decimal points from original post. Therefore I wanted to replace the exec calls.
My problem in using hwmon from conky: I don't know how to enumerate sensors instead of inputs.
I have 4 sensors of type nvme ...
... that's why I wanted to go for /system filesystem directly.

Any way - I got it with the help from conky forum.
If you're interested, here are the new working files (and the best: new version takes only half of system load :happy: )
First the lua script, that should be used without modifications:

Code: Select all

--[[
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2021-2025 django013
#
# project: conky.arcs theme for conky
# purpose: lua script which processes config.lua file and draws
#          system monitor with conky provided values
#          All customizable stuff is placed in config.lua
#          this file contains code to process that config file
#          and for so no need to change this script
]]--
require 'cairo'
cfg = require 'config'
settings = cfg.setup()


function splitString(str, sep)
  if sep == nil then
    sep = "%s"
  end
  local t = {}
  for s in string.gmatch(str, "([^"..sep.."]+)") do
    table.insert(t, s)
  end
  return t
end


function printTemperature(path, unit)
  local f = io.open(path, "rb")
  if not f then return 0 end
  local rv = f:read("*a")
  f:close()
  if (not unit == nil) then
     return string.format('%s°', tonumber(rv))
  else
     return string.format('%s', tonumber(rv))
  end
end


function printTemperatureMilli(path, unit)
  local f = io.open(path, "rb")
  if not f then return 0 end
  local rv = f:read("*a")
  f:close()
  if (not unit == nil) then
     return string.format('%s°', tonumber(rv) / 1000.0)
  else
     return string.format('%s', tonumber(rv) / 1000.0)
  end
end


function drawBackground(ctx, xOff, yOff, cfg)
  local w = cfg['w'] * settings['lw']
  local h = cfg['h'] * settings['lh']

  cairo_set_line_width(ctx, 0)
  cairo_rectangle(ctx, xOff, yOff, w, h)
  cairo_set_source_rgba(ctx, rgb2r_g_b(settings['bgCol'], 0.5))
  cairo_fill_preserve(ctx)
  cairo_stroke(ctx)
end


function drawRing(ctx, xOff, yOff, rd, cfg)
  local r  = rd['r'] * settings['lh']
  local cx = xOff + rd['x'] * settings['lw']
  local cy = yOff + rd['y'] * settings['lh'] + 1.7 * settings['lh']
  local s  = ''
  local sa = deg2rad(rd['sa'])
  local ea = deg2rad(rd['ea'])
  local ca = rd['ea'] - rd['sa']

  if (rd['name'] == 'printTemperature') then
     s = printTemperature(rd['arg'])
  elseif (rd['name'] == 'printTemperatureMilli') then
     s = printTemperatureMilli(rd['arg'])
  else
     s = conky_parse(string.format('${%s %s}', rd['name'], rd['arg']))
  end
  local v = tonumber(s)

  if v == nil then v = 0 end
  if v > 100  then v = 100 end
  if rd['d'] == 'ccw' then
     ca = 360 - rd['ea'] + rd['sa']
  end
  ca = ca / 100.0 * v
  if rd['d'] == 'ccw' then
    cairo_arc_negative(ctx, cx, cy, r, sa, ea)
  else
    cairo_arc(ctx, cx, cy, r, sa, ea)
  end
  local bg = rd['bg']
  local ba = rd['ba']
  local fg = rd['fg']

  if bg == nil then bg = settings['rBgC'] end
  if ba == nil then ba = settings['alpha'] end
  if fg == nil then fg = settings['rCol']  end
  cairo_set_source_rgba(ctx, rgb2r_g_b(bg, ba))
  cairo_set_line_width(ctx, settings['lh'] * rd['h'])
  cairo_stroke(ctx)

  if rd['d'] == 'ccw' then
    cairo_arc_negative(ctx, cx, cy, r, sa, deg2rad(rd['sa'] - ca))
  else
    cairo_arc(ctx, cx, cy, r, sa, deg2rad(rd['sa'] + ca))
  end
  cairo_set_source_rgba(ctx, rgb2r_g_b(fg, 0.8))
  cairo_set_line_width(ctx, settings['lh'] * rd['h'])
  cairo_stroke(ctx)
end


function printMainText(ctx, te, xOff, yOff, td, cfg)
  local x = xOff + td['x'] * settings['lw']
  local y = yOff + td['y'] * settings['lh']
  local c = settings['color']
  local a = settings['alpha']
  local fs = td['s']

  if fs == nil then fs = 0 end
  if td['text']:sub(0, 1) == '$' then
     s = conky_parse(td['text'])
     if td['text']:sub(3,5) == 'fre' then
        v = tonumber(s)
        if v == nil then v = 0 end
        local l1 = cfg['level1']
        local l2 = cfg['level2']

        if l1 == nil then l1 = 2000 end
        if l2 == nil then l2 = 2990 end
        if v > l2 then     c,a = settings['col2'],1
        elseif v > l1 then c,a = settings['col1'],1
        end
        cairo_select_font_face(ctx
                             , settings['font']
                             , CAIRO_FONT_SLANT_NORMAL
                             , CAIRO_FONT_WEIGHT_BOLD)
     else
        if fs > 0 then
           cairo_set_font_size(ctx, fs)
        else
           cairo_select_font_face(ctx
                                , settings['font']
                                , CAIRO_FONT_SLANT_NORMAL
                                , CAIRO_FONT_WEIGHT_NORMAL)
        end
     end
  elseif td['text']:sub(0,4) == 'LIF:' then
     local p = splitString(td['text'], ' ')
     if (p[2] == 'printTemperature') then
        s = printTemperature(p[3], '°')
    elseif (p[2] == 'printTemperatureMilli') then
        s = printTemperatureMilli(p[3], '°')
     end
  else
     if fs > 0 then
        cairo_set_font_size(ctx, fs)
     end
     s = td['text']
  end

  if td['a'] == 'L' then
     cairo_text_extents(ctx, s, te)
     x = x - te.width
  end
  cairo_move_to(ctx, x, y)
  cairo_set_source_rgba(ctx, rgb2r_g_b(c, settings['alpha']))
  cairo_show_text(ctx, s)
  cairo_stroke(ctx)
end


function printText(ctx, te, xOff, yOff, td)
  local x = xOff + td['x'] * settings['lw']
  local y = yOff + td['y'] * settings['lh']

  if td['text']:sub(0,1) == '$' then
     s = conky_parse(td['text'])
  else
     s = td['text']
  end
  if td['a'] == 'L' then
     cairo_text_extents(ctx, s, te)
     x = x - te.width
  end
  cairo_move_to(ctx, x, y)
  cairo_show_text(ctx, s)
  cairo_stroke(ctx)
end


function deg2rad(d)
  return (d - 90) * math.pi / 180
end


function rgb2r_g_b(col, alpha)
  local c = tonumber(col, 16)

  return ((c / 0x10000) % 0x100) / 255.
       , ((c / 0x100)   % 0x100) / 255.
       ,  (c % 0x100)            / 255.
       ,  alpha
end


function genStatics(ctx, te)
  local xOff = settings['x'] * settings['lw']
  local yOff = settings['y'] * settings['lh']
  cairo_select_font_face(ctx
                       , settings['hFont']
                       , CAIRO_FONT_SLANT_NORMAL
                       , CAIRO_FONT_WEIGHT_NORMAL)
  cairo_set_font_size(ctx, 30)
  cairo_set_source_rgba(ctx, rgb2r_g_b(settings['color'], settings['alpha']))

  for i in pairs(settings['uptime']) do
      printMainText(ctx, te, xOff, yOff, settings['uptime'][i])
  end
  for i in pairs(settings['date']) do
      printMainText(ctx, te, xOff, yOff, settings['date'][i])
  end
  for i in pairs(settings['time']) do
      printMainText(ctx, te, xOff, yOff, settings['time'][i])
  end
  cairo_select_font_face(ctx
                       , settings['font']
                       , CAIRO_FONT_SLANT_NORMAL
                       , CAIRO_FONT_WEIGHT_NORMAL)
  cairo_set_font_size(ctx, settings['fs'])
  cairo_set_source_rgba(ctx, rgb2r_g_b(settings['color'], settings['alpha']))
  for i in pairs(settings['sys']) do
      printMainText(ctx, te, xOff, yOff, settings['sys'][i])
  end
end


function genPanel(ctx, te, panelName)
  local cfg = settings[panelName]
  local xOff = cfg['x'] * settings['lw']
  local yOff = cfg['y'] * settings['lh']

  if settings['sb'] then drawBackground(ctx, xOff, yOff, cfg) end
  for i in pairs(cfg['rings']) do
      drawRing(ctx, xOff, yOff, cfg['rings'][i], cfg)
  end
  for i in pairs(cfg['main']) do
      printMainText(ctx, te, xOff, yOff, cfg['main'][i], cfg)
  end
  local fg = cfg['color']

  if fg == nil then fg = settings['col3'] end
  cairo_set_source_rgba(ctx, rgb2r_g_b(fg, settings['alpha']))
  for i in pairs(cfg['sub']) do
      printText(ctx, te, xOff, yOff, cfg['sub'][i])
  end
end


function conky_main()
  if conky_window == nil then return end
  local cs = cairo_xlib_surface_create(conky_window.display
                                     , conky_window.drawable
                                     , conky_window.visual
                                     , conky_window.width
                                     , conky_window.height)
  local ctx = cairo_create(cs)
  local updates = conky_parse('${updates}')

  nUpdate = tonumber(updates)
  if nUpdate > 2 then
     local te = cairo_text_extents_t:create()

     tolua.takeownership(te)
     cairo_select_font_face(ctx
                          , settings['font']
                          , CAIRO_FONT_SLANT_NORMAL
                          , CAIRO_FONT_WEIGHT_NORMAL)
     cairo_set_font_size(ctx, settings['fs'])
     cairo_text_extents(ctx, 'Wy', te)
     -- calculate position units based on font size
     settings['lh'] = te.height
     settings['lw'] = te.width * 0.4

     genStatics(ctx, te)
     cairo_select_font_face(ctx
                          , settings['font']
                          , CAIRO_FONT_SLANT_NORMAL
                          , CAIRO_FONT_WEIGHT_NORMAL)
     cairo_set_font_size(ctx, settings['fs'])
     genPanel(ctx, te, 'cpu')
     genPanel(ctx, te, 'io')
     genPanel(ctx, te, 'mem')
     genPanel(ctx, te, 'sensors')
     genPanel(ctx, te, 'hddTemp')
     genPanel(ctx, te, 'hdd')
  end
  cairo_surface_destroy(cs)
  cairo_destroy(ctx)
end
... and the installer that checks existing hardware:

Code: Select all

#!/usr/bin/env python3
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2021-2025 django013
#
# project: conky.arcs theme for conky
# purpose: installation script for conky.arcs theme
#          Script explores running system and generates matching
#          gui elements for conky system monitor.
import os
import sys
import glob
import array
import logging as log
import pathlib
import subprocess
from operator import itemgetter, attrgetter, methodcaller


def conkyCAPsFailed(app):
    print(f'conky [{app}] is not capable to run conky.arcs theme.')
    print('conky.arcs needs conky with lua and cairo support.')
    print('Try to install "conky-all" or use an appimage from ')
    print('https://github.com/brndnmtthws/conky/releases')
    print('')
    exit(-1)


def first(iterable, condition = lambda x: True):
    return next(x for x in iterable if condition(x))


def usage():
    print('conky.arc is a theme for system monitor app "conky"')
    print('this install-script evaluates your system and generates')
    print('matching config file.')
    print('')
    print('if conky is not found by PATH environment, ...')
    print('please provide the path to conky application as commandline ')
    print('argument. Install script then checks capability of conky.')
    print('')
    exit(0)


class ConfigReader:
  def __init__(self, conky):
      hasLua, hasCairo = self.evalConky(conky)
      if not hasLua or not hasCairo:
         conkyCAPsFailed(conky)
      self.app     = conky
      self.curdir  = os.getcwd()
      self.homedir = pathlib.Path.home()
      self.sensors = self.getSensors()
      self.nCPU    = self.countCPU()
      self.netIF   = self.networkIF()
      self.md      = self.mounted_drives()
      self.hds     = self.harddisks()
      self.hdTemps = self.driveTemps()
      self.cpuFreqs()


  def countCPU(self, maxCPU = 6):
      cpus0 = glob.glob('/sys/devices/system/cpu/cpu?')
      cpus1 = glob.glob('/sys/devices/system/cpu/cpu??')
      cpus = sorted(cpus0 + cpus1)

      return len(cpus)


  def cpuFreqs(self):
      f0 = 10000
      f1 = 0
      with open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq') as f:
           for line in f:
               f0 = int(line) / 1000

      with open('/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq') as f:
           for line in f:
               f1 = int(line) / 1000
      self.fCPU0 = f0
      self.fCPU1 = f1


  def driveTemps(self):
      driveTemps = []
      for d in glob.glob('/tmp/temps/*'):
          p = d.split('/')
          dt = {}
          dt['name'] = p[3]
          dt['path'] = d
          driveTemps.append(dt)
      return driveTemps


  def evalConky(self, app):
      p = pathlib.Path(app)
      if not p.exists():
         print('conky NOT found. Please install "conky-all"')
         print('')
         exit(0)
      p = subprocess.Popen([app, '--version'], stdout=subprocess.PIPE)
      line = p.stdout.readline()
      hasLua   = False
      hasCairo = False
      while line:
            if line.strip().startswith(b'Lua bind'):
               hasLua = True
               line = p.stdout.readline()
               while line.strip().startswith(b'*'):
                     parts = line.strip().split(b' ')
                     if parts[1] == b'Cairo':
                        hasCairo = True
                        return [hasLua, hasCairo]
                     line = p.stdout.readline()
            line = p.stdout.readline()
      return [hasLua, hasCairo]


  def getSensors(self):
      sensors = []
      paths = glob.glob('/sys/class/hwmon/hwmon*')
      for i in range(0, len(paths)):
          sensor = {}
          with open('/sys/class/hwmon/hwmon{}/name'.format(i)) as f:
              name = f.read().strip()
              sensor['type'] = name
              sensor['name'] = name
              if ('nvme' in name):
                  subname = first(glob.glob('/sys/class/hwmon/hwmon{0}/device/nvme*'.format(i)))
                  subname = os.path.basename(subname)
                  sensor['name'] = subname
              elif ('spd5118' in name):
                  subname = 'SMBus PIIX4'
                  sensor['name'] = subname
              temp = 0
              path = '/sys/class/hwmon/hwmon{}/temp1_input'.format(i)
              p = pathlib.Path(path)
              if not p.exists():
                  continue
              sensor['path'] = path
              with open(path) as ft:
                   temp = float(ft.read()) / 1000.0
              sensor['temp'] = temp
              sensors.append(sensor)
      sensors.sort(key=lambda x: x['name'], reverse=False)
      return sensors


  def mounted_drives(self):
      drives = []
      check = []    # avoid duplicates
      with open('/proc/mounts') as f:
           for line in f:
               if not line.startswith('/'):
                  continue
               if 'loop' in line:
                  continue
               parts = line.split()
               mountPoint = {}
               if parts[0] in check:
                  continue
               else:
                  check.append(parts[0])
                  sp = parts[0].split('/')
                  mountPoint['part'] = sp[2]
                  mountPoint['mounted'] = parts[1]
               drives.append(mountPoint)
      drives.sort(key=lambda x: x['mounted'], reverse=False)
      return drives


  def harddisks(self):
      disks0 = sorted(glob.glob('/dev/sd?'))
      disks1 = sorted(glob.glob('/dev/nvme?n?'))
      hdds = {}
      for d in disks0:
          parts = d.split('/')
          hdds[parts[2]] = d
      for d in disks1:
          parts = d.split('/')
          hdds[parts[2]] = d
      return hdds


  def networkIF(self):
      nif = []
      with open('/proc/net/dev') as f:
           for line in f:
               ifName = line.split()[0]
               if ifName == 'Inter-|' or ifName == 'face' or ifName == 'lo:':
                  continue
               nif.append(ifName.split(':')[0])
      return nif


  def dump(self):
      print('============================================================')
      print('conky [{0}] has lua and cairo support'.format(self.app))
      print('curdir:\t' + self.curdir)
      print('homedir:\t' + str(self.homedir))
      print('found {0} cpu cores'.format(self.nCPU))
      if len(self.sensors) < 2:
         print('NO temperatures for gpu!')
      print('network interfaces:')
      for n in self.netIF:
          print('\tfound interface:\t{0}'.format(n))
      print('harddisks:')
      for d in self.hds:
          print('\tfound disk:\t{0}'.format(d))
      print('mounted partitions:')
      for d in self.md:
          print('\t{0}\twith path:\t{1}'.format(d['part'], d['mounted']))
      print('sensors')
      for s in self.sensors:
          print('sensor {0} has temp {1}\t\tat: {2}'.format(s['name'], s['temp'], s['path']))
      print('drive temperatures:')
      for d in self.hdTemps:
          print('drive {0} with temp-path {1}'.format(d['name'], d['path']))
      print('============================================================')


class ConfigWriter:
  def __init__(self, cfgReader):
      self.cr = cfgReader
      self.base = str(self.cr.homedir) + '/.conky/MX-Circles'
      self.LuaTemplates = {}
      self.ConkyTemplates = {}
      self.ConkyTemplates['intro'] = """conky.config = {
    background = false,
    update_interval = 2,
    cpu_avg_samples = 2,
    net_avg_samples = 2,
    temperature_unit = 'celsius',
    double_buffer = true,
    no_buffers = true,
    text_buffer_size = 2048,
    gap_x = 2,
    gap_y = 50,
    minimum_width = 1020,
    minimum_height = 1200,
    own_window = true,
    own_window_type = 'normal',
    own_window_transparent = true,
    own_window_argb_visual = true,
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
    border_inner_margin = 0,
    border_outer_margin = 0,
    alignment = 'top_right',
    draw_shades = false,
    draw_outline = false,
    draw_borders = false,
    draw_graph_borders = false,
    override_utf8_locale = true,
    use_xft = true,
    xftalpha = 0.5,
    font = 'Cuprum:size=7',
    top_cpu_separate = true,
    lua_load = './conky.arcs.lua',
    lua_draw_hook_post = 'main'
}

conky.text = [[
${voffset 460}"""
      self.ConkyTemplates['diskRead'] = '\n${{goto {0}}}${{diskiograph_read    44, 300 ADFF2F 32CD32 -t -l}}'
      self.ConkyTemplates['diskWrite'] = '\n${{goto {0}}}${{diskiograph_write   44, 300 FF0000 8B0000 -t -l}}'
      self.ConkyTemplates['netDown'] = '\n${{goto {0}}}${{downspeedgraph {1} 21, 240 ADFF2F 32CD32 -t -l}}'
      self.ConkyTemplates['netUp'] = '\n${{goto {0}}}${{upspeedgraph   {1} 21, 240 FF0000 8B0000 -t -l}}'
      self.ConkyTemplates['fin'] = '\n]]'
      self.LuaTemplates['intro'] = """local config = {}

function config.setup()
  return {"""
      self.LuaTemplates['inter0'] = """
    },
    sub = {"""
      self.LuaTemplates['inter1'] = """
    },
    rings = {"""
      self.LuaTemplates['fin'] = """
}
end

return config"""
      self.LuaTemplates['main0'] = """
  x     = 11,
  y     =  0,
  fs    = 18,
  font  = 'Cuprum',
  hFont = 'Vast Shadow',
  rCol  = '95275C',
  rBgC  = '565656',
  color = 'FFFFFF',
  col1  = 'F2B70E',
  col2  = 'E90812',
  col3  = 'CDCDCD',
  bgCol = '993636',
  alpha = 0.9,
  sb    = false,
  uptime = {
  --[[
      x/y the position where the text should be aligned to
      a: L means text ends at x/y or is on the left side of position
      a: R means text starts at x/y or is on the right side of position
      text starting with '$' will be translated by conky engine
    ]]--
    { x = 37, y = 2, a = 'L', s = 30, text = 'Uptime' },
    { x = 38, y = 2, a = 'R', s = 30, text = '${uptime_short}' },
  },
  date = {"""
      self.LuaTemplates['mainDate'] = """
    {{ x = 27, y = {0}, a = 'L', s = 40, text = '${{time %A}}' }},
    {{ x = 29, y = {0}, a = 'R', s = 40, text = '${{time %x}}' }},
  }},
"""
      self.LuaTemplates['mainTime'] = """
  time = {{
    {{ x = 35, y = {0}, a = 'L', s = 80, text = '${{time %H}}' }},
    {{ x = 37, y = {0}, a = 'R', s = 80, text = ':' }},
    {{ x = 41, y = {0}, a = 'R', s = 80, text = '${{time %M}}' }},
  }},
  sys = {{"""
      self.LuaTemplates['main1'] = """
    {{ x = 37, y =  {0},   a = 'L', s = 20, text = 'public IP:' }},
    {{ x = 38, y =  {0},   a = 'R', s = 20, text = "${{execi 5 wget -q -O - checkip.dyndns.org | sed -e 's/[^[:digit:]\\\\|.]//g'}}" }},
    {{ x = 15, y = {1},   a = 'R', s = 30, text = "${{execi 3600 cat /etc/mx-version | awk '{{ print $1 \\" \\" $2; }}' | sed -e 's/_/ /g'}}" }},
    {{ x = 15, y = {2},   a = 'R', s = 20, text = "${{execi 3600 grep PRETTY_NAME /etc/os-release | sed -e 's/=/ /' | awk '{{ print $2 \\" \\" $3 \\" \\" $4 \\" \\" $5; }}' | sed -e 's/\\"//g'}}" }},"""
      self.LuaTemplates['IOheaders'] = """
    {{ x = 87, y = {0},   a = 'R', s = 25, text = 'network Traffic' }},
    {{ x = 87, y = {1},   a = 'R', s = 25, text = 'disk Traffic' }},
  }},
"""
      self.LuaTemplates['netMain'] = "\n    {{ x = 37, y =  {0},   a = 'L', s = 20, text = 'local {1}:' }},\n    {{ x = 38, y =  {0},   a = 'R', s = 20, text = '${{addr {1}}}' }},"
      self.LuaTemplates['cpuInit'] = """
  cpu = {
    x = 79,
    y =  0,
    w = 30,
    h = 13,
    level1 = 3500,
    level2 = 4500,
    main = {"""
      self.LuaTemplates['cpuMain0'] = """
      {{ x =  0, y =  {0}, a = 'R', text = 'CPU' }},
      {{ x = 15, y =  {0}, a = 'L', text = '${{cpu cpu{1}}}%' }},"""
      self.LuaTemplates['cpuMainN'] = """
      {{ x =  0, y =  {0}, a = 'R', text = 'CPU {1}' }},
      {{ x = 10, y =  {0}, a = 'L', text = '${{freq {1}}}' }},
      {{ x = 15, y =  {0}, a = 'L', text = '${{cpu cpu{1}}}%' }},"""
      self.LuaTemplates['cpuTop'] = """
      {{ x =  6, y = {0}, a = 'R', text = '${{top name {1}}}' }},
      {{ x = 26, y = {0}, a = 'L', text = '${{top cpu {1}}}%' }},"""
      self.LuaTemplates['cpuRing0'] = "\n      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'cw', name = 'cpu', arg = '', max = 100, sa = 0, ea = 300, ba = {2}, fg = 'F38A07' }},"
      self.LuaTemplates['cpuRingN'] = "\n      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'cw', name = 'cpu', arg = 'cpu{2}', max = 100, sa = 0, ea = 300, ba = {3} }},"
      self.LuaTemplates['cpuFin'] = """
    },
  },
"""
      self.LuaTemplates['memInit'] = """
  mem = {
    x = 49,
    y = 38,
    w = 36,
    h = 16,
    main = {
      { x = 15, y = 1, a = 'R', text = 'Mem:' },
      { x = 28, y = 1, a = 'L', text = '${mem}' },
      { x = 29, y = 1, a = 'L', text = '/' },
      { x = 30, y = 1, a = 'R', text = '${memmax}' },
      { x = 15, y = 2, a = 'R', text = 'Swap:' },
      { x = 28, y = 2, a = 'L', text = '${swap}' },
      { x = 29, y = 2, a = 'L', text = '/' },
      { x = 30, y = 2, a = 'R', text = '${swapmax}' },
    },
    sub = {"""
      self.LuaTemplates['memVal'] = """
      {{ x = 15, y =  {0}, a = 'R', text = '{2} Down' }},
      {{ x = 36, y =  {0}, a = 'L', text = '${{totaldown {2}}}' }},
      {{ x = 15, y =  {1}, a = 'R', text = '{2} UP' }},
      {{ x = 36, y =  {1}, a = 'L', text = '${{totalup {2}}}' }},
"""
      self.LuaTemplates['memTop'] = """
      {{ x = 10, y =  {0}, a = 'R', text = '${{top_mem name {1}}}' }},
      {{ x = 28, y =  {0}, a = 'L', text = '${{top_mem mem  {1}}}%' }},"""
      self.LuaTemplates['memRing0'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.9, d = 'ccw', name = 'memperc',    arg = '',     max = 100, sa = 0, ea = 65, ba = 0.8 }},
      {{ x = 15, y = {0}, r = {2}, h = 0.5, d = 'ccw', name = 'swapperc',   arg = '',     max = 100, sa = 0, ea = 60, ba = 0.8 }},"""
      self.LuaTemplates['memRingN'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'ccw', name = 'downspeedf', arg = '{2}', max = 100, sa = 0, ea = 55, ba = {3} }},
      {{ x = 15, y = {0}, r = {4}, h = 0.9, d = 'ccw', name = 'upspeedf',   arg = '{2}', max = 100, sa = 0, ea = 45, ba = {3} }},"""
      self.LuaTemplates['memFin'] = """    },
  },
"""
      self.LuaTemplates['hddInit'] = """
  lh  = 1,
  lw  = 1,
  hdd = {
    x = 87,
    y = 60,
    w = 29,
    h =  6,
    main = {"""
      self.LuaTemplates['hddMain'] = """
      {{ x = 11.5, y = {0},  a = 'R', text = '{1}' }},"""
      self.LuaTemplates['hddSub']  = """
      {{ x = 22, y = {0},  a = 'R', text = '{1}' }},"""
      self.LuaTemplates['hddRing'] = """
      {{ x = 11, y = {0}, r = {1}, h = 0.7, d = 'cw', name = 'fs_used_perc', arg = '{2}', max = 100, sa = 180, ea = 455, ba = {3} }},"""
      self.LuaTemplates['hddFin'] = """    },
  },
"""
      self.LuaTemplates['sensorsInit'] = """
  sensors = {
    x = 36,
    y = 17,
    w = 31,
    h = 16,
    main = {"""
      self.LuaTemplates['sensorsMain'] = """
      {{ x = 10, y = {0}, a = 'R', text = 'LIF: printTemperatureMilli {1}' }},"""
      self.LuaTemplates['sensorsName'] = """
      {{ x =  9, y = {0}, a = 'L', text = '{1}' }},"""
      self.LuaTemplates['sensorsRing'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'cw', name = 'printTemperatureMilli', arg = '{2}', max = 90, sa = 0, ea = 270, ba = {3}}},"""
      self.LuaTemplates['sensorsFin'] = """
    },
  },
"""
      self.LuaTemplates['hddTempInit'] = """
  hddTemp = {
    x = 43,
    y = 57,
    w = 31,
    h = 16,
    main = {"""
      self.LuaTemplates['hddTempMain'] = """
      {{ x = 10, y = {0}, a = 'R', text = 'LIF: printTemperature {1}' }},"""
      self.LuaTemplates['hddTempName'] = """
      {{ x =  9, y = {0}, a = 'L', text = '{1}' }},"""
      self.LuaTemplates['hddTempRing'] = """
      {{ x = 15, y = {0}, r = {1}, h = 0.8, d = 'cw', name = 'printTemperature', arg = '{2}', max = 90, sa = 0, ea = 270, ba = {3}}},"""
      self.LuaTemplates['hddTempFin'] = """
    },
  },"""


  def copyFonts(self):
      fd = '/usr/share/fonts/truetype/freefonts'
      print("need to install some fonts\n")
      subprocess.check_call(['sudo', 'mkdir', '-p', fd])
      fonts = glob.glob('fonts/*.ttf')
      for f in fonts:
          subprocess.check_call(['sudo', 'cp', f, fd])


  def install(self):
      p = pathlib.Path(self.base)
      if not p.exists():
          os.makedirs(self.base)
      self.copyFonts()
      subprocess.check_call(['cp', 'conky.arcs.lua', self.base])
      self.writeConkyConfig()
      self.writeLuaConfig()


  def writeConkyConfig(self):
      with open(self.base + '/MX-Circles', 'w') as f:
           f.write(self.ConkyTemplates['intro'])
           for ifn in self.cr.netIF:
               f.write(self.ConkyTemplates['netDown'].format(750, ifn))
           for ifn in self.cr.netIF:
               f.write(self.ConkyTemplates['netUp'].format(750, ifn))
           f.write(self.ConkyTemplates['diskRead'].format(710))
           f.write(self.ConkyTemplates['diskWrite'].format(710))
           f.write(self.ConkyTemplates['fin'])


  def writeLuaConfig(self):
      with open(self.base + '/config.lua', 'w') as f:
           f.write(self.LuaTemplates['intro'])
           self.writeMainSection(f)
           self.writeCpuSection(f)
           self.writeIOSection(f)
           self.writeMemSection(f)
           self.writeHddSection(f)
           self.writeSensorsSection(f)
           self.writeHddTempSection(f)
           f.write(self.LuaTemplates['fin'])


  def writeMainSection(self, f):
      f.write(self.LuaTemplates['main0'])
      y = 5 + len(self.cr.netIF)
      f.write(self.LuaTemplates['mainDate'].format(y))
      y = 9 + len(self.cr.netIF)
      f.write(self.LuaTemplates['mainTime'].format(y))
      y = 3
      for ifn in self.cr.netIF:
          f.write(self.LuaTemplates['netMain'].format(y, ifn))
          y += 1
      y0 =  y
      y1 =  8 + y
      y2 =  9 + y
      f.write(self.LuaTemplates['main1'].format(y0, y1, y2))
      y0 = 30 + len(self.cr.netIF) * 2
      y1 = 36 + len(self.cr.netIF) * 3
      f.write(self.LuaTemplates['IOheaders'].format(y0, y1))


  def writeCpuSection(self, f):
      f.write(self.LuaTemplates['cpuInit'])
      f.write(self.LuaTemplates['cpuMain0'].format(1, 0))
      for i in range(1, 1 + self.cr.nCPU):
          f.write(self.LuaTemplates['cpuMainN'].format(1 + i, i))
      f.write(self.LuaTemplates['inter0'])
      for i in range(0, min(10, self.cr.nCPU)):
          f.write(self.LuaTemplates['cpuTop'].format(3 + self.cr.nCPU + i, 1 + i))
      f.write(self.LuaTemplates['inter1'])
      trans = 0.7
      f.write(self.LuaTemplates['cpuRing0'].format(1 + self.cr.nCPU, 2 + self.cr.nCPU, trans))
      for i in range(0, self.cr.nCPU):
          if (i % 2 == 0):
             trans -= 0.1
          trans = max(0.3, trans)
          f.write(self.LuaTemplates['cpuRingN'].format(1 + self.cr.nCPU, 1 + self.cr.nCPU - i, 1 + i, trans))
      f.write(self.LuaTemplates['cpuFin'])


  def writeIOSection(self, f):
      f.write("""
  io = {
    x = 73,
    y = 28,
    w = 35,
    h =  9,
    main = {
      { x = 15, y = 5, a = 'R', text = 'Disk Reads' },
      { x = 14, y = 5, a = 'L', text = '${diskio_read}' },
      { x = 15, y = 6, a = 'R', text = 'Disk Writes' },
      { x = 14, y = 6, a = 'L', text = '${diskio_write}' },
    },
    sub = {
      { x = 15, y =  7, a = 'R', text = '${top_io name 1}' },
      { x = 14, y =  7, a = 'L', text = '${top_io io_write 1}' },
      { x = 15, y =  8, a = 'R', text = '${top_io name 2}' },
      { x = 14, y =  8, a = 'L', text = '${top_io io_write 2}' },
      { x = 15, y =  9, a = 'R', text = '${top_io name 3}' },
      { x = 14, y =  9, a = 'L', text = '${top_io io_write 3}' },
    },
    rings = {
      { x = 6, y = 3, r = 2, h = 0.8, d = 'ccw', name = 'top_io io_perc 1', arg = '', max = 100, sa = 80, ea = 180, ba = 0.3  },
      { x = 6, y = 3, r = 3, h = 0.8, d = 'ccw', name = 'top_io io_perc 2', arg = '', max = 100, sa = 80, ea = 180, ba = 0.45 },
      { x = 6, y = 3, r = 4, h = 0.8, d = 'ccw', name = 'top_io io_perc 3', arg = '', max = 100, sa = 80, ea = 180, ba = 0.6  },
    },
  },
""")


  def writeMemSection(self, f):
      f.write(self.LuaTemplates['memInit'])
      n=0
      for nif in self.cr.netIF:
          f.write(self.LuaTemplates['memVal'].format(3 + n, 4 + n, nif))
          n += 2
      mx = 2 * len(self.cr.netIF)
      for i in range(1, min(8, 2 + mx)):
          f.write(self.LuaTemplates['memTop'].format(3 + mx + i, i))
      f.write(self.LuaTemplates['inter1'])
      f.write(self.LuaTemplates['memRing0'].format(2 + mx, 3 + mx, 2 + mx))
      n=0
      trans = 0.6
      for nif in self.cr.netIF:
          f.write(self.LuaTemplates['memRingN'].format(2 + mx, 1 + mx - n, nif, trans, mx - n))
          trans -= 0.2
          n += 2
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['memFin'])


  def writeHddSection(self, f):
      f.write(self.LuaTemplates['hddInit'])
      mx = len(self.cr.md)
      n=0
      for mp in self.cr.md:
          f.write(self.LuaTemplates['hddMain'].format(4 + n, mp['part']))
          n += 1
      f.write(self.LuaTemplates['inter0'])
      n=0
      for mp in self.cr.md:
          f.write(self.LuaTemplates['hddSub'].format(4 + n, mp['mounted']))
          n += 1
      f.write(self.LuaTemplates['inter1'])
      revDrives = self.cr.md
      revDrives.sort(key=lambda x: x['mounted'], reverse=True)
      trans = 0.9
      n=0
      for mp in revDrives:
          f.write(self.LuaTemplates['hddRing'].format(0, 1 + mx - n, mp['mounted'], trans))
          n += 1
          if (n % 2 == 1):
             trans -= 0.1
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['hddFin'])


  def writeSensorsSection(self, f):
      f.write(self.LuaTemplates['sensorsInit'])
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['sensorsMain'].format(1 + n, s['path']))
          n += 1
      f.write(self.LuaTemplates['inter0'])
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['sensorsName'].format(1 + n, s['name']))
          n += 1
      f.write(self.LuaTemplates['inter1'])
      mx = len(self.cr.sensors)
      trans = 0.9
      n=0
      for s in self.cr.sensors:
          f.write(self.LuaTemplates['sensorsRing'].format(mx, 1 + mx - n, s['path'], trans))
          n += 1
          trans -= 0.1
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['sensorsFin'])


  def writeHddTempSection(self, f):
      f.write(self.LuaTemplates['hddTempInit'])
      n=0
      for d in self.cr.hdTemps:
          f.write(self.LuaTemplates['hddTempMain'].format(1 + n, d['path']))
          n += 1
      f.write(self.LuaTemplates['inter0'])
      n=0
      for d in self.cr.hdTemps:
          f.write(self.LuaTemplates['hddTempName'].format(1 + n, d['name']))
          n += 1
      f.write(self.LuaTemplates['inter1'])
      mx = len(self.cr.hdTemps)
      trans = 0.9
      n=0
      for d in self.cr.hdTemps:
          f.write(self.LuaTemplates['hddTempRing'].format(mx, 1 + mx - n, d['path'], trans))
          n += 1
          trans -= 0.1
          trans = max(0.3, trans)
      f.write(self.LuaTemplates['hddTempFin'])


if __name__ == "__main__":
   tmp = subprocess.check_output(["/bin/bash", "which", "conky"])
   if not tmp:
      usage()
   conkyapp = tmp.decode('utf-8').strip()
   if len(sys.argv) > 1:
      if sys.argv[1] == '--help':
         usage()
      else:
         p = pathlib.Path(sys.argv[1])
         if p.exists():
            conkyapp = sys.argv[1]
         else:
            usage()
   cr = ConfigReader(conkyapp)
   cr.dump()
   cw = ConfigWriter(cr)
   cw.install()
I need a bit to understand your setup hints, but I'll go for it.

... about polishing:
the installer is about to create the config setup from existing hardware.
As each group of rings is a single gui element, manual polishing by adjusting just x/y from the group is a question of few minutes.
I use 3 monitors rotated to portrait mode, so - for me - size is no issue :eek:

As I noted, that the nvme-sensor does not reflect the temperature of the drive, but possibly the temperature of the mainboard chip, I added former used drive temperatures like this:
Conky_MX-Circles_2.jpg
Can you post the config.lua from the installer on your system together with a screen shot?
Then I could check what to optimize.

Re: my conky circle theme

Posted: Mon Feb 03, 2025 9:28 am
by fehlix
django013 wrote: Mon Feb 03, 2025 9:15 am I know about the problem with decimal points from original post. Therefore I wanted to replace the exec calls.
My problem in using hwmon from conky: I don't know how to enumerate sensors instead of inputs.
I have 4 sensors of type nvme ...
... that's why I wanted to go for /system filesystem directly.

Any way - I got it with the help from conky forum.
Maybe explain this issue, so somone can follow.
As long as all different sensores for all nvme's do have a different hwmon sys-path, no need to get it directly,
b/c syspath mappes 1-1 into hwmon call. At least here with having 2 nvme's and they do show separately,
using only hwmon-calls:
temps-rings.jpg

Re: my conky circle theme

Posted: Mon Feb 03, 2025 9:53 am
by django013
but maybe you consider to move that "setup" logic into
a lua-function e.g. named like circle_setup(), where you re-formulate the python-logic into this lua-function,
and populate a "global" setup-table.
I thought already doing this.
conky.arcs.lua starts with:

Code: Select all

cfg = require 'config'
settings = cfg.setup()
... and generated config.lua looks like this:

Code: Select all

local config = {}

function config.setup()
  return {
  ...
  }
end

return config
and function conky_main then uses global table "settings"
Maybe explain this issue, so somone can follow.
may be I don't understand the point, you're leading me to.

Code: Select all

$ cat /sys/class/hwmon/hwmon0/name
nvme
which is the same for all nvme-sensors.

... and

Code: Select all

$ ll /sys/class/hwmon/hwmon0/device/
insgesamt 0
drwxr-xr-x 10 root root    0  3. Feb 14:08 nvme1n1
...
so from device's subdirectory I take the "real" name of the sensor

Re: my conky circle theme

Posted: Mon Feb 03, 2025 2:31 pm
by fehlix
django013 wrote: Mon Feb 03, 2025 9:53 am
but maybe you consider to move that "setup" logic into
a lua-function e.g. named like circle_setup(), where you re-formulate the python-logic into this lua-function,
and populate a "global" setup-table.
I thought already doing this.
conky.arcs.lua starts with:

Code: Select all

cfg = require 'config'
settings = cfg.setup()
... and generated config.lua looks like this:

Code: Select all

local config = {}

function config.setup()
  return {
  ...
  }
end

return config
and function conky_main then uses global table "settings"
OK, thanks. I was mentioning in case you want to avoid the install-python script,
to put all the detection work into a one-time running function,
you could then probably consider to use "lua_startup_hook".
Currently the global setup table is populated during lua_load,
which is fine, b/c that's a "static" table, which was genrated externaly with the python-script. But as I understood, they
introduced the "lua_startup_hook" to provide one spot, where all heavy loading/detection stuff can be run.
django013 wrote: Mon Feb 03, 2025 9:53 am
Maybe explain this issue, so somone can follow.
may be I don't understand the point, you're leading me to.

Code: Select all

$ cat /sys/class/hwmon/hwmon0/name
nvme
which is the same for all nvme-sensors.

... and

Code: Select all

$ ll /sys/class/hwmon/hwmon0/device/
insgesamt 0
drwxr-xr-x 10 root root    0  3. Feb 14:08 nvme1n1
...
so from device's subdirectory I take the "real" name of the sensor
The point I mentioned was to use hwmon calls instead of lua-read-temp.
Ahh, so the "issue" you mentioned was to combine the device "name" for those sensor types like "nvme".
and display the device name. OK, now I understand.
Also I tested you latest one, so seems you prefer to show temp with decimal places, which show here up to three decimals,
so you now can watch the temps in milleCentis. I could only avoid those decimals by letting conky get the temp with a ${hwmon ...} call.
Performance wise, probably not a big difference using lua-call or a hwmon-call, but still assume the compiled in hwmon call
is a bit faster then the lua-read-temp call.
Maybe final note: I really like the whole thing, but remember, MX is providing snapshot feature, and creation of
LiveUSBs, and it would be really nice if your conky would include a internal setup, instead requiring re-installation/re-setup,
when running on different systems. b/c when you move around onto another system,
with eg. LiveUSB or a personal Snapshot-ISO booted with Ventoy or a normally installed
on external removal drives. Or even only on internal installed system, I had the effect
the nvme's got different names depending whether I boot different different kernel. Can't remember
which one caused it. I was testing different things with both sysVinit and systemd and differernt liquorix and normal debian kernels,
where suddenly the nvme swapped the names.
And as you current conky is only prepared for the current running system after a re-setup/re-install.
Actually this would also be a show stopper for potentially getting this conky as it is currently setup, into a the MX collection of offered conky's.
So all is prepared by you. Great! May be you can find a way to move the python-setup stuff into the lua-setup-run-at-start thing.
Thanks

Re: my conky circle theme

Posted: Mon Feb 03, 2025 10:45 pm
by django013
I really like the whole thing, but remember, MX is providing snapshot feature, and creation of
LiveUSBs, and it would be really nice if your conky would include a internal setup, instead requiring re-installation/re-setup,
when running on different systems.
Thanks for the food for thought!

What I did was to learn both languages. But now I'll gonna follow your advices and move the python stuff to lua.
Might be a challenge for me, but I like that 9_9

Re: my conky circle theme

Posted: Thu Feb 06, 2025 10:42 am
by django013
Hi,

I made the setup with lua working (without conky) and I'm working to put all together.
I failed to handle font files following your hints:
In addition if you would keep the fonts e.g within the sub folder of the conky ".conky/MX-Circles/fonts" you could now refresh the fontconfig cache with the setup-function e.g with something like "fc-cache -r ~/.conky/MX-Circles/fonts",
which would only be needed to run when those fonts are not already available with font-config cache, e.g. which you can check with "fc-list | grep fontname".
I removed the font files from system directories and executed the "fc-cache" command, which took a while before return, but it did not add the font files to the system cache. "fc-list" does not show the font files from subdirectory "fonts".

Re: my conky circle theme

Posted: Thu Feb 06, 2025 11:46 am
by fehlix
django013 wrote: Thu Feb 06, 2025 10:42 am Hi,

I made the setup with lua working (without conky) and I'm working to put all together.
Great.
django013 wrote: Thu Feb 06, 2025 10:42 am I failed to handle font files following your hints:
In addition if you would keep the fonts e.g within the sub folder of the conky ".conky/MX-Circles/fonts" you could now refresh the fontconfig cache with the setup-function e.g with something like "fc-cache -r ~/.conky/MX-Circles/fonts",
which would only be needed to run when those fonts are not already available with font-config cache, e.g. which you can check with "fc-list | grep fontname".
I removed the font files from system directories and executed the "fc-cache" command, which took a while before return, but it did not add the font files to the system cache. "fc-list" does not show the font files from subdirectory "fonts".
Yes, I was probably misleading. The simplest copy the font into the standard-location for user-installed fonts
according to "XDG Base Directory Specification" :
from "~/.conky/MX-Cirlces" (i.e. conky-base-dir/fonts) to "~/.local/share/fonts", needs to be created if not exists.
The fonts cache will be generated automatically. Not sure about the timing, but background fonts generation is very fast
Either check files already exist, or double check with fc-list | grep, or check the ~/.cache/fontconfig.
and "fc-cache ~/.local/share/fonts/" if needed, so no need to regnerate, if exist already.

Re: my conky circle theme

Posted: Thu Feb 06, 2025 10:39 pm
by django013
The simplest copy the font into the standard-location for user-installed fonts
according to "XDG Base Directory Specification" :
from "~/.conky/MX-Cirlces" (i.e. conky-base-dir/fonts) to "~/.local/share/fonts", needs to be created if not exists.
Well, that copy-action leads to require an installer, as every font-directory is owned by root.
I read in /etc/fonts/fonts.conf, that font- and fontconfig-directories from users home will be removed, so not the best place.

As conky is default on mx-systems, how about to add a directory entry for fontconfig to search for fonts in ~/.conky and subdirectories?
Using my installer it is no issue to copy font files to system location, but with implicit setup from running conky I can't use sudo for it.

So what is your recomendation?

P.S. don't know, whether you know it: lua_startup_hook is executed after conky_main.
So for me there's no reason to use startup-hook

Re: my conky circle theme

Posted: Fri Feb 07, 2025 1:36 am
by fehlix
django013 wrote: Thu Feb 06, 2025 10:39 pm
The simplest copy the font into the standard-location for user-installed fonts
according to "XDG Base Directory Specification" :
from "~/.conky/MX-Cirlces" (i.e. conky-base-dir/fonts) to "~/.local/share/fonts", needs to be created if not exists.
Well, that copy-action leads to require an installer, as every font-directory is owned by root.
I read in /etc/fonts/fonts.conf, that font- and fontconfig-directories from users home will be removed, so not the best place.

As conky is default on mx-systems, how about to add a directory entry for fontconfig to search for fonts in ~/.conky and subdirectories?

Using my installer it is no issue to copy font files to system location, but with implicit setup from running conky I can't use sudo for it.

So what is your recomendation?
You probably referring to the legacy fonts directories ~/.fontconfig ~/.fonts, which are since a long time "marked" as to be removed,
as mention in /etc/fonts/fonts.conf, like this:

Code: Select all

    <dir prefix="xdg">fonts</dir>
    <!-- the following element will be removed in the future -->
    <dir>~/.fonts</dir>
and

Code: Select all

    <cachedir prefix="xdg">fontconfig</cachedir>
    <!-- the following element will be removed in the future -->
    <cachedir>~/.fontconfig</cachedir>
below the two xdg-lines. Where the line

Code: Select all

	<dir prefix="xdg">fonts</dir>
referes to
$XDG_DATA_HOME/fonts, which defauts to $HOME/.local/share/fonts
and the line

Code: Select all

	<cachedir prefix="xdg">fontconfig</cachedir>
referes to
$XDG_CACHE_HOME/fontconfig, which defauts to $HOME/.cache/fontconfig
So, yes, don't use legacy directories, but only the default xdg-directories, which are
used by fontconfig.
django013 wrote: Thu Feb 06, 2025 10:39 pm P.S. don't know, whether you know it: lua_startup_hook is executed after conky_main.
So for me there's no reason to use startup-hook
Not sure I understand or maybe I misunderstand what you are saying.
Whithin conky.config you would have something like this:

Code: Select all

conky.confg = {
...
	lua_load = './conky.arcs.lua',
	lua_startup_hook = "conky_setup",
	lua_draw_hook_post = conky_main'
}
also you don't need or you can avoid a the "require", you corrently are using:

Code: Select all

cfg = require 'config'
settings = cfg.setup()
and load both script with lua_load this way:

Code: Select all

	lua_load = "conky.arcs.lua config.lua",
So you would have only loaded the function, defined with both scripts,
with no execution of any function.
The next would be the lua_startup_hook , where you call a conky_setup function,
which would do one time only populatimg the gobal settings table.
And finally with lua_draw_hook_post the function conky_main will be called
by Conky through each iteration after drawing to the window, where the
settings table will be read.

For reference, what "man conky" mentions about lua_load and the hooks:

Code: Select all

lua_load
      Loads the Lua scripts separated by spaces.

lua_startup_hook function_name [`function arguments']
      This  function, if defined, will be called by Conky at startup
      or when the configuration is reloaded.  Use this hook to  ini‐
      tialize  values, or for any run-once applications.  Conky puts
      `conky_' in front of function_name to prevent accidental calls
      to the wrong function unless you place `conky_' in front of it
      yourself.
and pre and post hooks:

Code: Select all

lua_draw_hook_post function_name [`function arguments']
      This  function,  if  defined,  will be called by Conky through
      each iteration after drawing to the window.  Requires  X  sup‐
      port.   Takes any number of optional arguments.  Use this hook
      for drawing things on top of what  Conky  draws.   Conky  puts
      `conky_' in front of function_name to prevent accidental calls
      to the wrong function unless you place `conky_' in front of it
      yourself.

lua_draw_hook_pre function_name [`function arguments']
      This  function,  if  defined,  will be called by Conky through
      each iteration before drawing to the window.  Requires X  sup‐
      port.   Takes any number of optional arguments.  Use this hook
      for drawing things on top of what  Conky  draws.   Conky  puts
      `conky_' in front of function_name to prevent accidental calls
      to the wrong function unless you place `conky_' in front of it
      yourself.

Re: my conky circle theme

Posted: Fri Feb 07, 2025 4:50 am
by django013
Hi,

my XDG_DATA was screwed up by flatpack

Code: Select all

XDG_DATA_DIRS=/home/[user]/.local/share/flatpak/exports/share
              /var/lib/flatpak/exports/share
              /usr/local/share
              /usr/share
... and I don't have XDG_DATA_HOME or XDG_CACHE_HOME

I'll check fontconfig again.

I tested conky with

Code: Select all

conky.confg = {
...
	lua_load = './conky.arcs.lua',
	lua_startup_hook = 'setup',
	lua_draw_hook_post = 'main'
}
In lua-code I added a simple oneliner to output some text (kindof logging) from each function.
Logfile showed, that conky_main will be called first.
After terminating first call of conky_main conky_setup will be called.
So that's rubbish.

another conky issue: conky uses german locale on "tonumber(x)" calls, which of cause breaks generated lua sources :mad:
... but I found a workaround

Well, I did it my way ;)
... and it works. - I used the old font-directory ~/.fonts so far
I extended the script to support smaller hardware too.
Small hardware will result in this output (without manual polishing):
MX-Circles0.jpg
and bigger hardware will result in this output (without manual polishing):
MX-Circles1.jpg
I you like to give it a try:
rename attachment to *.tar.xz and unpack it to ~/.conky
Then issue a "conky -c MX-Circles"
When the circles appear, hit ^C and restart conky as usual

Re: my conky circle theme

Posted: Fri Feb 07, 2025 11:04 am
by fehlix
django013 wrote: Fri Feb 07, 2025 4:50 am Hi,

my XDG_DATA was screwed up by flatpack

Code: Select all

XDG_DATA_DIRS=/home/[user]/.local/share/flatpak/exports/share
              /var/lib/flatpak/exports/share
              /usr/local/share
              /usr/share
... and I don't have XDG_DATA_HOME or XDG_CACHE_HOME

I'll check fontconfig again.

I tested conky with

Code: Select all

conky.confg = {
...
	lua_load = './conky.arcs.lua',
	lua_startup_hook = 'setup',
	lua_draw_hook_post = 'main'
}
In lua-code I added a simple oneliner to output some text (kindof logging) from each function.
Logfile showed, that conky_main will be called first.
After terminating first call of conky_main conky_setup will be called.
So that's rubbish.

another conky issue: conky uses german locale on "tonumber(x)" calls, which of cause breaks generated lua sources :mad:
... but I found a workaround

Well, I did it my way ;)
... and it works. - I used the old font-directory ~/.fonts so far
I extended the script to support smaller hardware too.
Small hardware will result in this output (without manual polishing):
MX-Circles0.jpg

and bigger hardware will result in this output (without manual polishing):
MX-Circles1.jpg

I you like to give it a try:
rename attachment to *.tar.xz and unpack it to ~/.conky
Then issue a "conky -c MX-Circles"
When the circles appear, hit ^C and restart conky as usual
Thanks, will look into this over the weekend ...
(Yes, XDG_DATA_HOME or XDG_CACHE_HOME, if not set, they are used by the system in away as if they would been set to the default, as already mentioned.)

Note:You can attach zip-files, e.g by wrapping/putting the current archive-files again into a zip.

Re: my conky circle theme

Posted: Fri Feb 07, 2025 10:55 pm
by django013
Hi,
fehlix wrote: Fri Feb 07, 2025 11:04 am Yes, XDG_DATA_HOME or XDG_CACHE_HOME, if not set, they are used by the system in away as if they would been set to the default, as already mentioned.
Well, I tried that, and it did not work.

I removed the font-files from ~/.fonts and changed the script to create and use ~/.local/share/fonts - but the system did not show up the fonts.
So I went back to use ~/.fonts which worked right out of the box.

May be, you (MX-developers) can extend fontconfig to scan ~/.conky as a font directory
So I consider my work as final for now and update the first post