How does one obtain SeDebugPrivilege in Python? I believe both the Ctypes api and PyWin32 are both individually capable of doing it, i don't care which API is used. I found this broken code in the wild, it's probably pretty close
import win32api
import win32con
import win32security
#
def get_extra_privs():
# Try to give ourselves some extra privs (only works if we're admin):
# SeBackupPrivilege - so we can read anything
# SeDebugPrivilege - so we can find out about other processes (otherwise OpenProcess will fail for some)
# SeSecurityPrivilege - ??? what does this do?
# Problem: Vista+ support "Protected" processes, e.g. audiodg.exe. We can't see info about these.
# Interesting post on why Protected Process aren't really secure anyway: http://www.alex-ionescu.com/?p=34
th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32con.TOKEN_ADJUST_PRIVILEGES | win32con.TOKEN_QUERY)
TokenPrivileges = 1
privs = win32security.GetTokenInformation(th, TokenPrivileges)
newprivs = []
for privtuple in privs:
if privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeBackupPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeDebugPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeSecurityPrivilege"):
print ("Added privilege " + str(privtuple[0]))
# privtuple[1] = 2 # tuples are immutable. WHY?!
newprivs.append((privtuple[0], 2)) # SE_PRIVILEGE_ENABLED
else:
newprivs.append((privtuple[0], privtuple[1]))
# Adjust privs
privs = tuple(newprivs)
str(win32security.AdjustTokenPrivileges(th, False , privs))
but it crashes with
File "C:\cygwin64\home\hans\tibia\pybot\pybot.py", line 11, in init
processStuff.get_extra_privs()
File "C:\cygwin64\home\hans\tibia\pybot\processStuff.py", line 20, in get_extra_privs
if privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeBackupPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server
, "SeDebugPrivilege") or privtuple[0] == win32security.LookupPrivilegeValue(remote_server, "SeSecurityPrivilege"):
TypeError: 'PySID' object is not subscriptable
Problems:
Wrong TokenPrivileges value: it should be 3, as per [MS.Docs]: TOKEN_INFORMATION_CLASS enumeration (winnt.h). Use the value exported by PyWin32
remote_server undefined
LookupPrivilegeValue being called 3 times for each existing privilege (minor optimization)
codee00.py:
#!/usr/bin/env python
import sys
import win32api as wapi
import win32con as wcon
import win32security as wsec
PRIV_NAMES = (
wsec.SE_BACKUP_NAME,
wsec.SE_DEBUG_NAME,
wsec.SE_SECURITY_NAME,
)
def enable_privs(remote_server=None, priv_names=PRIV_NAMES):
priv_ids = sorted(wsec.LookupPrivilegeValue(remote_server, e) for e in priv_names)
print("Privileges to be enabled IDs:", priv_ids)
tok = wsec.OpenProcessToken(wapi.GetCurrentProcess(), wcon.TOKEN_ADJUST_PRIVILEGES | wcon.TOKEN_QUERY)
proc_privs = wsec.GetTokenInformation(tok, wsec.TokenPrivileges)
print("Existing process privileges:", proc_privs)
new_proc_privs = []
need_change = False
for proc_priv in proc_privs:
if proc_priv[0] in priv_ids:
print("Checking privilege " + str(proc_priv[0]))
if proc_priv[1] != wcon.SE_PRIVILEGE_ENABLED:
need_change = True
new_proc_privs.append((proc_priv[0], wcon.SE_PRIVILEGE_ENABLED))
else:
new_proc_privs.append(proc_priv)
print("New process privileges:", new_proc_privs)
if need_change:
modif_privs = wsec.AdjustTokenPrivileges(tok, False, new_proc_privs)
res = wapi.GetLastError()
print("Changed privileges:", modif_privs) # Changed ones
if res != 0:
print("Error (partial) setting privileges:", res)
else:
print("Already set")
#wsec.GetTokenInformation(tok, wsec.TokenPrivileges) # To compare with proc_privs
wapi.CloseHandle(tok)
def main(*argv):
enable_privs()
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
Output:
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q072193556]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32
Privileges to be added IDs: [17, 20, 8]
Existing process privileges: ((2, 0), (3, 0), (4, 0), (5, 0), (7, 0), (8, 2), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), (14, 0), (15, 0), (16, 0), (17, 0), (18, 0), (19, 0), (20, 0), (21, 0), (22, 0), (23, 3), (24, 0), (25, 0), (26, 0), (28, 0), (29, 3), (30, 3), (31, 0), (32, 0), (33, 0), (34, 0), (35, 0), (36, 0))
Checking privilege 8
Checking privilege 17
Checking privilege 20
New process privileges: [(2, 0), (3, 0), (4, 0), (5, 0), (7, 0), (8, 2), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), (14, 0), (15, 0), (16, 0), (17, 2), (18, 0), (19, 0), (20, 2), (21, 0), (22, 0), (23, 3), (24, 0), (25, 0), (26, 0), (28, 0), (29, 3), (30, 3), (31, 0), (32, 0), (33, 0), (34, 0), (35, 0), (36, 0)]
Changed privileges: ((17, 0), (20, 0))
Done
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q072193556]> :: Attempt running as non Administrator user
[cfati#CFATI-5510-0:e:\Work\Dev\StackOverflow\q072193556]> runas /user:test "cmd /k e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe %CD%\code00.py"
Enter the password for test:
Attempting to start cmd /k e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe e:\Work\Dev\StackOverflow\q072193556\code00.py as user "CFATI-5510-0\test" ...
And the newly launched terminal:
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32
Privileges to be enabled IDs: [8, 17, 20]
Existing process privileges: ((19, 0), (23, 3), (25, 0), (33, 0), (34, 0))
New process privileges: [(19, 0), (23, 3), (25, 0), (33, 0), (34, 0)]
Already set
Done.
As for the final goal (adding extra new privileges - which is not clearly stated in the question):
It can't be done, which makes perfect sense, as one non privileged process being able to grant itself (administrative) privileges, would beat the purpose of the whole Windows security (privileges and rights) mechanism
Even if one would try adding new privilege entries to the existing list (which automatically implies that current code has design flaws), AdjustTokenPrivileges would (silently) fail with ERROR_NOT_ALL_ASSIGNED (1300). For more details, check [MS.Docs]: Changing Privileges in a Token which states (emphasis is mine):
AdjustTokenPrivileges cannot add or remove privileges from the token. It can only enable existing privileges that are currently disabled or disable existing privileges that are currently enabled.
Related
**I print the id(Q) in calcul(), but the output is not same in different process, Why? **
Shouldn't the message queue be unique, so why would the output ids be different?
Code
from multiprocessing import Pool, Manager, freeze_support, Lock, Queue
class Model(object):
def __init__(self, id=0):
self.id = id
def act(self, s):
return len(str(s)) - len(str(self.id))
def init(lock1, lock2, lock3):
global model_lock_, text_lock_, rst_lock_
model_lock_ = lock1
text_lock_ = lock2
rst_lock_ = lock3
def calcul(s):
s, Q = s
request = Q.get(True)
rst = request.id
print((s, rst, os.getpid(), id(request), id(Q)))
time.sleep(1)
Q.put(request)
return (s, rst)
if __name__ == "__main__":
ptime = time.time()
freeze_support()
Q = Manager().Queue(5)
for i in range(5):
Q.put(Model(i))
params = [(i, Q) for i in range(100)]
p = Pool(processes=3)
rst = p.map(calcul, params)
Output:
(0, 0, 28836, 2421423514192, 2421423266352)
(9, 1, 26440, 1942850478672, 1942850230832)
(18, 2, 14888, 2433320739408, 2433320479280)
(1, 3, 28836, 2421423517504, 2421423266352)
(10, 0, 26440, 1942850481984, 1942850230832)
(19, 4, 14888, 2433320742720, 2433320479280)
(2, 1, 28836, 2421423514192, 2421423266352)
(20, 2, 14888, 2433320739408, 2433320479280)
(11, 3, 26440, 1942850478672, 1942850230832)
(3, 4, 28836, 2421423517504, 2421423266352)(21, 0, 14888, 2433320742720, 2433320479280)
(12, 1, 26440, 1942850481984, 1942850230832)
(22, 2, 14888, 2433320739408, 2433320479280)(4, 3, 28836, 2421423514192, 2421423266352)
(13, 4, 26440, 1942850478672, 1942850230832)
(5, 0, 28836, 2421423517504, 2421423266352)(14, 1, 26440, 1942850481984, 1942850230832)
(23, 3, 14888, 2433320742720, 2433320479280)
Can someone teach me:
Why the Queue id is not unique, or where I wrote wrong?
I am very inexperienced with Python so bear with me. I am trying to set a schedule in a RaspiPico to turn a RaspPi 3B+ on at given times. The RaspPi 3B+ turns itself off at the 57th minute of every hour, the Pico is supposed to turn it on at the 24th minute of every hour. I've worked out the code so that when I manually run it through Thonny, it turns the RaspPi 3B+ on and doesnt give any error messages anymore, but when left on its own it doesn't appear to be following a schedule. To clarify there is also an RTC included in this Raspi Sandwich. Here is my code:
from machine import Pin, Timer, RTC, I2C
import time
import binascii
# I2C PINSs
I2C_PORT = 0
I2C_SDA = 20
I2C_SCL = 21
# Times when the B+ should be booted up
# Format is (hours, minutes)
# For now, keep it sorted; we could do this in code, but why not just do it by hand
SCHEDULE = [(0, 24, 0), (0, 56, 0), (1, 24, 0), (1, 56, 0), (2, 24, 0), (2, 56, 0), (3, 24, 0), (3, 56, 0), (4, 24, 0), (4, 56, 0), (5, 24, 0), (5, 56, 0), (6, 24, 0), (6, 56, 0), (7, 24, 0), (7, 56, 0), (8, 24, 0), (8, 56, 0), (9, 24, 0), (9, 56, 0)
, (10, 24, 0), (10, 56, 0), (11, 24, 0), (11, 56, 0), (12, 24, 0), (12, 56, 0), (13, 24, 0), (13, 56, 0), (14, 24, 0), (14, 56, 0), (15, 24, 0), (15, 56, 0), (16, 24, 0), (16, 56, 0), (17, 24, 0), (17, 56, 0), (18, 24, 0), (18, 56, 0), (19, 24, 0), (19, 56, 0),
(20, 24, 0), (20, 56, 0), (21, 24, 0), (21, 56, 0), (22, 24, 0), (22, 56, 0), (23, 24, 0), (23, 56, 0)]
# This function returns the next scheduled boot time
# after the hour, minute we provide
def grab_next_time(time_tuple):
current_hour, current_min, current_sec = time_tuple
# Go through all scheudled boot times from first to last
for h, m, s in SCHEDULE:
# If current hour is before hour, we found the next time
if current_hour < h:
return (h,m, s)
# If its the same hour
elif current_hour == h:
# If its before the minute, we found the next time
if current_min < m:
return (h,m,s)
# if its the same minute
elif current_min == m:
# If its before the second, we found the next time
if current_sec < s:
return (h,m,s)
# If the current hour is not the same or before, lets try the next
# We tried all times in the schedule,
# we're after, so lets start from the begining
return SCHEDULE[0]
# This function returns the time we need to wait in seconds
def wait_time(current_time, next_time):
next_hour, next_min, next_sec = next_time[0], next_time[1], next_time[2]
current_hour, current_min, current_sec = current_time[0], current_time[1], current_time[2]
wait_time = (next_sec - current_sec)
if (next_sec - current_sec) < 0:
wait_time += 60
next_min -= 1
wait_time += (next_min - current_min) * 60
if (next_min - current_min) < 0:
wait_time += (60 * 60)
next_hour -= 1
wait_time += (next_hour - current_hour) * 60 * 60
if (next_hour - current_hour) < 0:
wait_time += (60 * 60 * 24)
return wait_time
def start_local_rtc(date=(2017, 8, 23, 2, 12, 48, 0, 0)):
rtc = machine.RTC()
rtc.datetime(date)
return rtc
class ds3231(object):
NowTime = b'\x00\x45\x13\x02\x24\x05\x21'
w = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
address = 0x68
start_reg = 0x00
alarm1_reg = 0x07
control_reg = 0x0e
status_reg = 0x0f
def __init__(self,i2c_port,i2c_scl,i2c_sda):
self.bus = I2C(i2c_port,scl=Pin(i2c_scl),sda=Pin(i2c_sda))
def read_time(self):
t = self.bus.readfrom_mem(int(self.address),int(self.start_reg),7)
seconds = (t[0]&0x0F) + 10 * ((t[0]>>4)&0x07)
minutes = (t[1]&0x0F) + 10 * ((t[1]>>4)&0x07)
hour = (t[2]&0x0F) + 20 * ((t[2]>>4)&0x02) #hour ( 11 1111)
dow = t[3]&0x07 #week
day = (t[4]&0x0F) + 10 * ((t[4]>>4)&0x03) #day
month = (t[5]&0x1F) + 10 * ((t[5]>>4)&0x01) #month
year = 2000 + ((t[6]&0x0F) + 10 * ((t[6]>>4)&0x0F)) # year
#(year, month, day, weekday, hours, minutes, seconds, subseconds)
return (year, month, day, hour, minutes, seconds, 0, 0)
#print("20%x/%02x/%02x %02x:%02x:%02x %s" %(t[6],t[5],t[4],t[2],t[1],t[0],self.w[t[3]-1]))
# For a second, override the start pin
def start_pi():
boot = Pin(21, Pin.OUT)
boot.value(0)
time.sleep(0.1) # 100 ms should do
boot = Pin(21, Pin.IN) # Setting the pin to IN should make it float
if __name__ == '__main__':
# On startup grab the time from the RTC
hw_rtc = ds3231(I2C_PORT, I2C_SCL, I2C_SDA)
time_rtc = hw_rtc.read_time()
print(time_rtc)
local_rtc = start_local_rtc()
# This is our infinite loop
while True:
current_time = local_rtc.datetime()
current_time = (current_time[4], current_time[5], current_time[6])
print(current_time)
next_time = grab_next_time(current_time)
print("Next time: ", next_time)
wait_time = wait_time(current_time, next_time)
print("Wait time: ", wait_time)
# If within 30 seconds, start the pi
if wait_time < 30:
start_pi()
# If we need to wait for more than 30 seconds, keep waiting
else:
time.sleep(wait_time - 5)
The person I am working with mentioned that the Pico will "poke" the Pi and not stop poking until the specified time. hence why my schedule is listed as "(0,24,0), (0,56,0)". In my head this is supposed to start the "poking" at the 24th minute after midnight and stop at the 56th minute. As was stated before this obviously isn't what is going on. Some help would be appreciated.
I am trying to search an typed item from QTlistwidget, and when selected it will then search again to see if exist from the ziped list.
However, the first time running it will able to find the match. The second time i select, the zip_cmsdata will return me an empty list
For example:
1st time search "Hello", and then selected "Hello" from the Qtlistwdiget
output:
item click
[('Hello', 1, 'US', None)]
If i click (select) Hello from the QTlistwdiget again, or search other items. I will get the following output
item click
[]
I am guessing the cmsdata were only added once, so the second time it got cleared. If this is true, how can i solve it?
Please find my code below:
from asyncio.windows_events import NULL
from re import A
import sys
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QDialog, QApplication, QStackedWidget, QWidget, QTabWidget, QTableView, QListView
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QStringListModel
import requests
import images
import json
cmsdata = []
cmsdisplayname = []
cid = []
cmscountry = []
parentId = []
zip_cmsdata = []
class PerfectConsoleSearch(QDialog):
def __init__(self):
super(PerfectConsoleSearch, self).__init__()
loadUi("pfsearch.ui",self)
with open('cmsdata.json') as json_file:
data = json.load(json_file)
global cmsdata
cmsdata = data
#print(data)
#print(cmsdata)
#print(type(cmsdata))
#self.login_button.clicked.connect(self.gotologin)
#self.cms_display_list.setStringList(self.cmsdata)
#self.cms_display_list.setModel(main.cmsdata)
self.load_cmslist()
print(self.customer_searchbar.text())
self.customer_searchbar.textChanged.connect(lambda: self.Search(self.customer_searchbar.text()))
self.cms_display_list.itemClicked.connect(self.show_select_item)
def load_cmslist(self):
n = 0
a=[]
#print(cmsdata['customerList'][0]['displayName'])
while n < len(cmsdata['customerList']):
#print(cmsdata['customerList'][n]['displayName'])
cmsdisplay_name = cmsdata['customerList'][n]['displayName']
cms_cid = cmsdata['customerList'][n]['cid']
cms_country = cmsdata['customerList'][n]['country']
cms_parentId = cmsdata['customerList'][n]['parentId']
a.append(cmsdisplay_name)
global cmsdisplayname, cid, cmscountry, parentId
cmsdisplayname.append(cmsdisplay_name)
cid.append(cms_cid)
cmscountry.append(cms_country)
parentId.append(cms_parentId)
self.cms_hidden_list.addItem(str(cmsdisplay_name))
n=n+1
global zip_cmsdata
zip_cmsdata = zip(cmsdisplayname,cid,cmscountry,parentId)
def Search(self, text):
#print(text)
#print(cmsdisplayname)
#print(self.cms_display_list.count)
self.cms_display_list.clear()
items = self.cms_hidden_list.findItems(text, QtCore.Qt.MatchFlag.MatchContains)
#print(items)
#print(type(items))
for i in items:
#print(i.text())
self.cms_display_list.addItem(i.text())
def show_select_item(self):
print('item click')
item = self.cms_display_list.selectedItems()[0]
print(item.text())
#self.label.setText(text_0 + item.text())
#Find account info
print([i for i in zip_cmsdata if item.text() in i])
A zip object is an iterable, which means that it can only be iterated over once. Since it is a global, it is never renewed and returns nothing.
a = [i for i in range(0,10)]
b = [i for i in range(20,30)]
Z = zip(a,b)
print("first run")
for i in Z:
print(i)
print("second run")
for i in Z:
print(i)
Output:
first run
(0, 20)
(1, 21)
(2, 22)
(3, 23)
(4, 24)
(5, 25)
(6, 26)
(7, 27)
(8, 28)
(9, 29)
second run
You can fix this by regenerating your zip object:
a = [i for i in range(0,10)]
b = [i for i in range(20,30)]
print("first run")
Z = zip(a,b)
for i in Z:
print(i)
print("second run")
Z = zip(a,b)
for i in Z:
print(i)
Ouput:
first run
(0, 20)
(1, 21)
(2, 22)
(3, 23)
(4, 24)
(5, 25)
(6, 26)
(7, 27)
(8, 28)
(9, 29)
second run
(0, 20)
(1, 21)
(2, 22)
(3, 23)
(4, 24)
(5, 25)
(6, 26)
(7, 27)
(8, 28)
(9, 29)
I am writing a program to find all the subsets of a given string. However it is resulting in an error called Killed: 9.
class Strings(object):
def __init__(self,string):
self.list1=[]
self.string=string
def appendToList(self,ch1):
self.list1.append(ch1)
for i in self.list1:
self.list1.append(i+ch1)
def subset(self):
for ch in self.string:
self.list1=self.appendToList(ch)
print self.list1
obj=Strings('hello')
obj.subset()
You can ask the program kill what that signal means:
$ kill -l | column
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
So your program receives a SIGKILL (probably by the OOM killer):
SIGKILL
Default action: Abnormal termination of the process.
Description: Kill (cannot be caught or ignored).
C.f. Who "Killed" my process and why?
you enter an infinite loop when you append an element every time you iterate over existing elements (in appendToList) and no need to assign in subset method, check this out:
class Strings(object):
def __init__(self,string):
self.list1=[]
self.string=string
def appendToList(self,ch1):
self.list1.append(ch1)
# for i in self.list1: # no needed for loop is in subset
# self.list1.append(i+ch1)
def subset(self):
for ch in self.string:
self.appendToList(ch) # no need to assign to self.list1
print self.list1
obj=Strings('hello')
obj.subset()
for pic,fn,xoff in [(self.PNew,self.New,0),(self.POpen,self.Open,0),(self.PSave,self.Save,0),(self.POrigin,self.SetOrigin,20),(self.PFiducial,self.SetFiducial,0),(self.PLine,self.AddLine,20),(self.PPoint,self.AddPoint,0),(self.PMove,self.AddMove,0),(self.PCircle,self.AddCircle,0),(self.PCall,self.AddCall,0)]: Button(ButtonRow,image=pic,command=fn).pack(side=LEFT,padx=(xoff,0))
items = [
(self.PNew, self.New, 0),
(self.POpen, self.Open, 0),
(self.PSave, self.Save, 0),
(self.POrigin, self.SetOrigin, 20),
(self.PFiducial, self.SetFiducial, 0),
(self.PLine, self.AddLine, 20),
(self.PPoint, self.AddPoint, 0),
(self.PMove, self.AddMove, 0),
(self.PCircle, self.AddCircle, 0),
(self.PCall, self.AddCall, 0)
]
for pic, fn, xoff in items:
Button(ButtonRow, image=pic, command=fn).pack(side=LEFT, padx=(xoff, 0))
You could make it into a function...
def add_button(pic, fn, xoff=0):
Button(ButtonRow, image=pic, command=fn).pack(side=LEFT, padx=(xoff, 0))
add_button(self.PNew, self.New)
add_button(self.POpen, self.Open)
add_button(self.PSave, self.Save)
add_button(self.POrigin, self.SetOrigin, 20)
add_button(self.PFiducial, self.SetFiducial)
add_button(self.PLine, self.AddLine, 20)
add_button(self.PPoint, self.AddPoint)
add_button(self.PMove, self.AddMove)
add_button(self.PCircle, self.AddCircle)
add_button(self.PCall, self.AddCall)