├── .gitignore ├── LICENSE ├── README.md ├── camera-controller.py ├── gui-testing.py ├── gui.py └── visca_control_gui.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VISCA-IP-Controller-GUI 2 | A graphical program for controlling PTZ cameras using VISCA commands over a local network. 3 | 4 | This uses code from [VISCA-IP-Controller](https://github.com/misterhay/VISCA-IP-Controller). 5 | 6 | Ensure you have [Python](https://www.python.org/) installed, along with [Tkinter](https://www.tutorialspoint.com/how-to-install-tkinter-in-python). 7 | -------------------------------------------------------------------------------- /camera-controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # https://pro.sony/s3/2019/01/16020225/AES6100121.pdf 3 | # https://gitlab.viarezo.fr/2018corona/viscaoverip/blob/master/camera2.py 4 | # sudo apt install python3-tk 5 | # pip install visca-over-ip 6 | 7 | from visca_over_ip import Camera 8 | import socket 9 | import binascii # for printing the messages we send, not really necessary 10 | from time import sleep 11 | 12 | c = Camera('192.168.0.100') # camera IP or hostname 13 | camera_port = 52381 # this is hardcoded into the library for now 14 | 15 | # for receiving 16 | #buffer_size = 1024 17 | #s.bind(('', camera_port)) # use the port one higher than the camera's port 18 | #s.settimeout(1) # only wait for a response for 1 second 19 | 20 | def save_preset_labels(): 21 | with open('preset_labels.txt', 'w') as f: 22 | for entry in entry_boxes: 23 | f.write(entry.get()) 24 | f.write('\n') 25 | f.close() 26 | 27 | def store(n): 28 | c.info_display(False) 29 | c.save_preset(n) 30 | 31 | def recall(n): 32 | c.info_display(False) 33 | c.recall_preset(n) 34 | 35 | def key_press(event): 36 | key = event.char 37 | #print(key, type(key)) 38 | if key == '0': 39 | recall(0) 40 | if key == '1': 41 | recall(1) 42 | if key == '2': 43 | recall(2) 44 | if key == '3': 45 | recall(3) 46 | if key == '4': 47 | recall(4) 48 | if key == '5': 49 | recall(5) 50 | if key == '6': 51 | recall(6) 52 | if key == '7': 53 | recall(7) 54 | if key == '8': 55 | recall(8) 56 | if key == '9': 57 | recall(9) 58 | 59 | def disableTextEntries(): 60 | for entry in entry_boxes: 61 | entry.config(state = 'readonly') 62 | enableButton['state'] = 'normal' 63 | disableButton['state'] = 'disabled' 64 | 65 | def enableTextEntries(): 66 | for entry in entry_boxes: 67 | entry.config(state='normal') 68 | enableButton['state'] = 'disabled' 69 | disableButton['state'] = 'normal' 70 | 71 | # GUI 72 | 73 | from tkinter import Tk, StringVar, Button, Label, Entry, Scale 74 | root = Tk() 75 | display_message = StringVar() 76 | root.title('VISCA IP Camera Controller') 77 | root['background'] = 'white' 78 | 79 | store_column = 0 80 | label_column = 1 81 | recall_column = 2 82 | slider_column = 3 83 | slider_row = 1 84 | on_off_column = 3 85 | on_off_row = 14 86 | focus_buttons_row = 10 87 | button_width = 8 88 | store_color = 'red' 89 | recall_color = 'light grey' 90 | pan_tilt_color = 'white' 91 | zoom_color = 'light blue' 92 | focus_color = 'cyan' 93 | on_off_color = 'violet' 94 | 95 | # Preset store buttons 96 | Label(root, text='Store', bg=store_color).grid(row=1, column=store_column) 97 | Button(root, text=0, width=3, bg=store_color, command=lambda: store(0)).grid(row=2, column=store_column) 98 | Button(root, text=1, width=3, bg=store_color, command=lambda: store(1)).grid(row=3, column=store_column) 99 | Button(root, text=2, width=3, bg=store_color, command=lambda: store(2)).grid(row=4, column=store_column) 100 | Button(root, text=3, width=3, bg=store_color, command=lambda: store(3)).grid(row=5, column=store_column) 101 | Button(root, text=4, width=3, bg=store_color, command=lambda: store(4)).grid(row=6, column=store_column) 102 | Button(root, text=5, width=3, bg=store_color, command=lambda: store(5)).grid(row=7, column=store_column) 103 | Button(root, text=6, width=3, bg=store_color, command=lambda: store(6)).grid(row=8, column=store_column) 104 | Button(root, text=7, width=3, bg=store_color, command=lambda: store(7)).grid(row=9, column=store_column) 105 | Button(root, text=8, width=3, bg=store_color, command=lambda: store(8)).grid(row=10, column=store_column) 106 | Button(root, text=9, width=3, bg=store_color, command=lambda: store(9)).grid(row=11, column=store_column) 107 | Button(root, text='A', width=3, bg=store_color, command=lambda: store(10)).grid(row=12, column=store_column) 108 | Button(root, text='B', width=3, bg=store_color, command=lambda: store(11)).grid(row=13, column=store_column) 109 | Button(root, text='C', width=3, bg=store_color, command=lambda: store(12)).grid(row=14, column=store_column) 110 | Button(root, text='D', width=3, bg=store_color, command=lambda: store(13)).grid(row=15, column=store_column) 111 | Button(root, text='E', width=3, bg=store_color, command=lambda: store(14)).grid(row=16, column=store_column) 112 | Button(root, text='F', width=3, bg=store_color, command=lambda: store(15)).grid(row=17, column=store_column) 113 | 114 | # Recall buttons and entries (as labels) 115 | Label(root, text='Recall', bg=recall_color).grid(row=1, column=recall_column) 116 | Button(root, text=0, width=5, bg=recall_color, command=lambda: recall(0)).grid(row=2, column=recall_column) 117 | Button(root, text=1, width=5, bg=recall_color, command=lambda: recall(1)).grid(row=3, column=recall_column) 118 | Button(root, text=2, width=5, bg=recall_color, command=lambda: recall(2)).grid(row=4, column=recall_column) 119 | Button(root, text=3, width=5, bg=recall_color, command=lambda: recall(3)).grid(row=5, column=recall_column) 120 | Button(root, text=4, width=5, bg=recall_color, command=lambda: recall(4)).grid(row=6, column=recall_column) 121 | Button(root, text=5, width=5, bg=recall_color, command=lambda: recall(5)).grid(row=7, column=recall_column) 122 | Button(root, text=6, width=5, bg=recall_color, command=lambda: recall(6)).grid(row=8, column=recall_column) 123 | Button(root, text=7, width=5, bg=recall_color, command=lambda: recall(7)).grid(row=9, column=recall_column) 124 | Button(root, text=8, width=5, bg=recall_color, command=lambda: recall(8)).grid(row=10, column=recall_column) 125 | Button(root, text=9, width=5, bg=recall_color, command=lambda: recall(9)).grid(row=11, column=recall_column) 126 | Button(root, text='A', width=5, bg=recall_color, command=lambda: recall(10)).grid(row=12, column=recall_column) 127 | Button(root, text='B', width=5, bg=recall_color, command=lambda: recall(11)).grid(row=13, column=recall_column) 128 | Button(root, text='C', width=5, bg=recall_color, command=lambda: recall(12)).grid(row=14, column=recall_column) 129 | Button(root, text='D', width=5, bg=recall_color, command=lambda: recall(13)).grid(row=15, column=recall_column) 130 | Button(root, text='E', width=5, bg=recall_color, command=lambda: recall(14)).grid(row=16, column=recall_column) 131 | Button(root, text='F', width=5, bg=recall_color, command=lambda: recall(15)).grid(row=17, column=recall_column) 132 | try: 133 | with open('preset_labels.txt') as f: 134 | labels = f.read().splitlines() 135 | f.close() 136 | except: 137 | pass 138 | entry_boxes = [] 139 | for e in range(16): 140 | box = Entry(root, justify='right') 141 | try: 142 | box.insert(-1, labels[e]) 143 | except: 144 | pass 145 | box.grid(row=e+2, column=label_column) 146 | entry_boxes.append(box) 147 | Button(root, text='Save preset labels', bg=store_color, command=save_preset_labels).grid(row=1, column=label_column) 148 | enableButton = Button(root, text='Enable text entry', state='disabled', bg=store_color, command=enableTextEntries) 149 | enableButton.grid(row=18, column=label_column) 150 | disableButton = Button(root, text='Disable', bg=recall_color, command=disableTextEntries) 151 | disableButton.grid(row=18, column=recall_column) 152 | 153 | # Pan and tilt buttons 154 | ''' 155 | Button(root, text='↑', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up)).grid(row=pan_tilt_row, column=pan_tilt_column+1) 156 | Button(root, text='←', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_left)).grid(row=pan_tilt_row+1, column=pan_tilt_column) 157 | Button(root, text='→', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_right)).grid(row=pan_tilt_row+1, column=pan_tilt_column+2) 158 | Button(root, text='↓', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 159 | Button(root, text='↖', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_left)).grid(row=pan_tilt_row, column=pan_tilt_column) 160 | Button(root, text='↗', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_right)).grid(row=pan_tilt_row, column=pan_tilt_column+2) 161 | Button(root, text='↙', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_left)).grid(row=pan_tilt_row+2, column=pan_tilt_column) 162 | Button(root, text='↘', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_right)).grid(row=pan_tilt_row+2, column=pan_tilt_column+2) 163 | Button(root, text='■', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_stop)).grid(row=pan_tilt_row+1, column=pan_tilt_column+1) 164 | ''' 165 | #Button(root, text='Home', command=lambda: send_message(pan_home)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 166 | 167 | # Pan speed and Tilt speed 168 | ''' 169 | Label(root, text='Pan Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column) 170 | pan_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 171 | pan_speed_slider.set(5) 172 | pan_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column, rowspan=4) 173 | Label(root, text='Tilt Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column+1) 174 | tilt_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 175 | tilt_speed_slider.set(5) 176 | tilt_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column+1, rowspan=4) 177 | #Button(root, text='test pan speed', command=lambda: pan()).grid(row=pan_tilt_row+5, column=pan_tilt_column+1) 178 | 179 | # slider to set speed for pan_speed and tilt_speed (0x01 to 0x17) 180 | # still not quite sure about this... 181 | #Scale(root, from_=0, to=17, variable=movement_speed, orient=HORIZONTAL, label='Speed').grid(row=5, column=2, columnspan=3) 182 | #''' 183 | 184 | def reset_sliders(e): 185 | pan_slider.set(0) 186 | tilt_slider.set(0) 187 | zoom_slider.set(0) 188 | focus_slider.set(0) 189 | 190 | # Pan slider 191 | def pan_this(pan_var): 192 | c.pantilt(int(pan_var), 0) 193 | Label(root, text='Pan', bg=pan_tilt_color, width=button_width).grid(row=slider_row, column=slider_column) 194 | pan_slider = Scale(root, bg=pan_tilt_color, from_=24, to=-24, orient='horizontal', length=200, command=pan_this) 195 | pan_slider.set(0) 196 | pan_slider.bind("", reset_sliders) 197 | pan_slider.grid(row=slider_row+1, column=slider_column, rowspan=2) 198 | 199 | # Tilt slider 200 | def tilt_this(tilt_var): 201 | c.pantilt(0, int(tilt_var)) 202 | Label(root, text='Tilt', bg=pan_tilt_color, width=button_width).grid(row=slider_row, column=slider_column+1) 203 | tilt_slider = Scale(root, bg=pan_tilt_color, from_=24, to=-24, length=200, command=tilt_this) 204 | tilt_slider.set(0) 205 | tilt_slider.bind("", reset_sliders) 206 | tilt_slider.grid(row=slider_row+1, column=slider_column+1, rowspan=8) 207 | 208 | # Zoom slider 209 | def zoom_this(zoom_var): 210 | c.zoom(int(zoom_var)) 211 | Label(root, text='Zoom', bg=zoom_color, width=button_width).grid(row=slider_row, column=slider_column+2) 212 | zoom_slider = Scale(root, bg=zoom_color, from_=7, to=-7, length=200, command=zoom_this) 213 | zoom_slider.set(0) 214 | zoom_slider.bind("", reset_sliders) 215 | zoom_slider.grid(row=slider_row+1, column=slider_column+2, rowspan=8) 216 | 217 | # Focus slider 218 | def focus_this(focus_var): 219 | c.manual_focus(int(focus_var)) 220 | Label(root, text='Focus', bg=focus_color, width=button_width).grid(row=slider_row, column=slider_column+3) 221 | focus_slider = Scale(root, bg=focus_color, from_=7, to=-7, length=200, command=focus_this) 222 | focus_slider.set(0) 223 | focus_slider.bind("", reset_sliders) 224 | focus_slider.grid(row=slider_row+1, column=slider_column+3, rowspan=8) 225 | 226 | # Focus buttons 227 | def one_push_focus(): 228 | c.set_focus_mode('auto') 229 | c.set_autofocus_mode('one push trigger') 230 | Label(root, text='Focus', bg=focus_color, width=button_width).grid(row=focus_buttons_row, column=on_off_column) 231 | Button(root, text='Auto', bg=focus_color, width=button_width, command=lambda: c.set_focus_mode('auto')).grid(row=focus_buttons_row+1, column=on_off_column) 232 | Button(root, text='Manual', bg=focus_color, width=button_width, command=lambda: c.set_focus_mode('manual')).grid(row=focus_buttons_row+2, column=on_off_column) 233 | #Button(root, text='One Push', bg=focus_color, width=button_width, command=one_push_focus).grid(row=focus_buttons_row+3, column=on_off_column) 234 | 235 | # On off connect buttons 236 | Label(root, text='Camera', bg=on_off_color, width=button_width).grid(row=on_off_row, column=on_off_column) 237 | Button(root, text='On', bg=on_off_color, width=button_width, command=lambda: c.set_power(True)).grid(row=on_off_row+1, column=on_off_column) 238 | Button(root, text='Off', bg=on_off_color, width=button_width, command=lambda: c.set_power(False)).grid(row=on_off_row+2, column=on_off_column) 239 | Button(root, text='Info Off', bg=on_off_color, width=button_width, command=lambda: c.info_display(False)).grid(row=on_off_row+3, column=on_off_column) 240 | 241 | # IP Label 242 | #Label(root, text=camera_ip+':'+str(camera_port)).grid(row=6, column=0, columnspan=3) 243 | # Connection Label 244 | #Label(root, textvariable=display_message).grid(row=6, column=4, columnspan=3) 245 | 246 | root.bind('', key_press) # so we have keyboard events triggering that function 247 | root.mainloop() -------------------------------------------------------------------------------- /gui-testing.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | 3 | def print_this(x): 4 | print(x) 5 | 6 | root = Tk() 7 | root.geometry("750x250") 8 | 9 | mousejoy = 0 10 | colors = ['red','lightgrey'] 11 | 12 | def motion(event): 13 | x, y = event.x, event.y 14 | print('{}, {}'.format(x, y)) 15 | 16 | def nomotion(event): 17 | pass 18 | 19 | def joypad(): 20 | global mousejoy 21 | b.configure(bg=colors[mousejoy]) 22 | if mousejoy == 0: 23 | root.bind('', motion) 24 | # root.bind('', handler) # bind click to stopping motion tracking 25 | mousejoy = 1 26 | else: 27 | root.bind('', nomotion) 28 | mousejoy = 0 29 | 30 | 31 | 32 | 33 | b = Button(root, command=joypad, text="Testing") 34 | b.pack(anchor=N) 35 | scale = Scale(root, from_=7, to=-7, command=print_this) 36 | scale.pack(anchor=CENTER) 37 | 38 | root.mainloop() -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # https://pro.sony/s3/2019/01/16020225/AES6100121.pdf 3 | # https://gitlab.viarezo.fr/2018corona/viscaoverip/blob/master/camera2.py 4 | # sudo apt install python3-tk 5 | # pip install visca-over-ip 6 | 7 | from visca_over_ip import Camera 8 | import socket 9 | import binascii # for printing the messages we send, not really necessary 10 | from time import sleep 11 | 12 | c = Camera('192.168.0.100') # camera IP or hostname 13 | camera_port = 52381 # this is hardcoded into the library for now 14 | 15 | # for receiving 16 | #buffer_size = 1024 17 | #s.bind(('', camera_port)) # use the port one higher than the camera's port 18 | #s.settimeout(1) # only wait for a response for 1 second 19 | 20 | def save_preset_labels(): 21 | with open('preset_labels.txt', 'w') as f: 22 | for entry in entry_boxes: 23 | f.write(entry.get()) 24 | f.write('\n') 25 | f.close() 26 | 27 | def store(n): 28 | c.info_display(False) 29 | c.save_preset(n) 30 | 31 | def recall(n): 32 | print('Recalling', n) 33 | c.info_display(False) 34 | c.recall_preset(n) 35 | 36 | def key_press(event): 37 | key = event.char 38 | #print(key, type(key)) 39 | if key == '0': 40 | recall(0) 41 | if key == '1': 42 | recall(1) 43 | if key == '2': 44 | recall(2) 45 | if key == '3': 46 | recall(3) 47 | if key == '4': 48 | recall(4) 49 | if key == '5': 50 | recall(5) 51 | if key == '6': 52 | recall(6) 53 | if key == '7': 54 | recall(7) 55 | if key == '8': 56 | recall(8) 57 | if key == '9': 58 | recall(9) 59 | 60 | # GUI 61 | 62 | from tkinter import Tk, StringVar, Button, Label, Entry, Scale 63 | root = Tk() 64 | display_message = StringVar() 65 | root.title('VISCA IP Camera Controller') 66 | root['background'] = 'white' 67 | 68 | store_column = 0 69 | label_column = 1 70 | recall_column = 2 71 | slider_column = 3 72 | slider_row = 1 73 | on_off_column = 3 74 | on_off_row = 14 75 | focus_buttons_row = 10 76 | button_width = 8 77 | store_color = 'red' 78 | recall_color = 'light grey' 79 | pan_tilt_color = 'white' 80 | zoom_color = 'light blue' 81 | focus_color = 'cyan' 82 | on_off_color = 'violet' 83 | 84 | # Preset store buttons 85 | Label(root, text='Store', bg=store_color).grid(row=1, column=store_column) 86 | Button(root, text=0, width=3, bg=store_color, command=lambda: store(0)).grid(row=2, column=store_column) 87 | Button(root, text=1, width=3, bg=store_color, command=lambda: store(1)).grid(row=3, column=store_column) 88 | Button(root, text=2, width=3, bg=store_color, command=lambda: store(2)).grid(row=4, column=store_column) 89 | Button(root, text=3, width=3, bg=store_color, command=lambda: store(3)).grid(row=5, column=store_column) 90 | Button(root, text=4, width=3, bg=store_color, command=lambda: store(4)).grid(row=6, column=store_column) 91 | Button(root, text=5, width=3, bg=store_color, command=lambda: store(5)).grid(row=7, column=store_column) 92 | Button(root, text=6, width=3, bg=store_color, command=lambda: store(6)).grid(row=8, column=store_column) 93 | Button(root, text=7, width=3, bg=store_color, command=lambda: store(7)).grid(row=9, column=store_column) 94 | Button(root, text=8, width=3, bg=store_color, command=lambda: store(8)).grid(row=10, column=store_column) 95 | Button(root, text=9, width=3, bg=store_color, command=lambda: store(9)).grid(row=11, column=store_column) 96 | Button(root, text='A', width=3, bg=store_color, command=lambda: store(10)).grid(row=12, column=store_column) 97 | Button(root, text='B', width=3, bg=store_color, command=lambda: store(11)).grid(row=13, column=store_column) 98 | Button(root, text='C', width=3, bg=store_color, command=lambda: store(12)).grid(row=14, column=store_column) 99 | Button(root, text='D', width=3, bg=store_color, command=lambda: store(13)).grid(row=15, column=store_column) 100 | Button(root, text='E', width=3, bg=store_color, command=lambda: store(14)).grid(row=16, column=store_column) 101 | Button(root, text='F', width=3, bg=store_color, command=lambda: store(15)).grid(row=17, column=store_column) 102 | 103 | # Recall buttons and entries (as labels) 104 | Label(root, text='Recall', bg=recall_color).grid(row=1, column=recall_column) 105 | Button(root, text=0, width=5, bg=recall_color, command=lambda: recall(0)).grid(row=2, column=recall_column) 106 | Button(root, text=1, width=5, bg=recall_color, command=lambda: recall(1)).grid(row=3, column=recall_column) 107 | Button(root, text=2, width=5, bg=recall_color, command=lambda: recall(2)).grid(row=4, column=recall_column) 108 | Button(root, text=3, width=5, bg=recall_color, command=lambda: recall(3)).grid(row=5, column=recall_column) 109 | Button(root, text=4, width=5, bg=recall_color, command=lambda: recall(4)).grid(row=6, column=recall_column) 110 | Button(root, text=5, width=5, bg=recall_color, command=lambda: recall(5)).grid(row=7, column=recall_column) 111 | Button(root, text=6, width=5, bg=recall_color, command=lambda: recall(6)).grid(row=8, column=recall_column) 112 | Button(root, text=7, width=5, bg=recall_color, command=lambda: recall(7)).grid(row=9, column=recall_column) 113 | Button(root, text=8, width=5, bg=recall_color, command=lambda: recall(8)).grid(row=10, column=recall_column) 114 | Button(root, text=9, width=5, bg=recall_color, command=lambda: recall(9)).grid(row=11, column=recall_column) 115 | Button(root, text='A', width=5, bg=recall_color, command=lambda: recall(10)).grid(row=12, column=recall_column) 116 | Button(root, text='B', width=5, bg=recall_color, command=lambda: recall(11)).grid(row=13, column=recall_column) 117 | Button(root, text='C', width=5, bg=recall_color, command=lambda: recall(12)).grid(row=14, column=recall_column) 118 | Button(root, text='D', width=5, bg=recall_color, command=lambda: recall(13)).grid(row=15, column=recall_column) 119 | Button(root, text='E', width=5, bg=recall_color, command=lambda: recall(14)).grid(row=16, column=recall_column) 120 | Button(root, text='F', width=5, bg=recall_color, command=lambda: recall(15)).grid(row=17, column=recall_column) 121 | try: 122 | with open('preset_labels.txt') as f: 123 | labels = f.read().splitlines() 124 | f.close() 125 | except: 126 | pass 127 | entry_boxes = [] 128 | for e in range(16): 129 | box = Entry(root, justify='right') 130 | try: 131 | box.insert(-1, labels[e]) 132 | except: 133 | pass 134 | box.grid(row=e+2, column=label_column) 135 | entry_boxes.append(box) 136 | Button(root, text='Save preset labels', bg=store_color, command=save_preset_labels).grid(row=1, column=label_column) 137 | 138 | # Pan and tilt buttons 139 | ''' 140 | Button(root, text='↑', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up)).grid(row=pan_tilt_row, column=pan_tilt_column+1) 141 | Button(root, text='←', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_left)).grid(row=pan_tilt_row+1, column=pan_tilt_column) 142 | Button(root, text='→', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_right)).grid(row=pan_tilt_row+1, column=pan_tilt_column+2) 143 | Button(root, text='↓', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 144 | Button(root, text='↖', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_left)).grid(row=pan_tilt_row, column=pan_tilt_column) 145 | Button(root, text='↗', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_right)).grid(row=pan_tilt_row, column=pan_tilt_column+2) 146 | Button(root, text='↙', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_left)).grid(row=pan_tilt_row+2, column=pan_tilt_column) 147 | Button(root, text='↘', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_right)).grid(row=pan_tilt_row+2, column=pan_tilt_column+2) 148 | Button(root, text='■', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_stop)).grid(row=pan_tilt_row+1, column=pan_tilt_column+1) 149 | ''' 150 | #Button(root, text='Home', command=lambda: send_message(pan_home)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 151 | 152 | # Pan speed and Tilt speed 153 | ''' 154 | Label(root, text='Pan Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column) 155 | pan_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 156 | pan_speed_slider.set(5) 157 | pan_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column, rowspan=4) 158 | Label(root, text='Tilt Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column+1) 159 | tilt_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 160 | tilt_speed_slider.set(5) 161 | tilt_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column+1, rowspan=4) 162 | #Button(root, text='test pan speed', command=lambda: pan()).grid(row=pan_tilt_row+5, column=pan_tilt_column+1) 163 | 164 | # slider to set speed for pan_speed and tilt_speed (0x01 to 0x17) 165 | # still not quite sure about this... 166 | #Scale(root, from_=0, to=17, variable=movement_speed, orient=HORIZONTAL, label='Speed').grid(row=5, column=2, columnspan=3) 167 | #''' 168 | 169 | def reset_sliders(e): 170 | pan_slider.set(0) 171 | tilt_slider.set(0) 172 | zoom_slider.set(0) 173 | focus_slider.set(0) 174 | 175 | # Pan slider 176 | def pan_this(pan_var): 177 | c.pantilt(int(pan_var), 0) 178 | Label(root, text='Pan', bg=pan_tilt_color, width=button_width).grid(row=slider_row, column=slider_column) 179 | pan_slider = Scale(root, bg=pan_tilt_color, from_=24, to=-24, orient='horizontal', length=200, command=pan_this) 180 | pan_slider.set(0) 181 | pan_slider.bind("", reset_sliders) 182 | pan_slider.grid(row=slider_row+1, column=slider_column, rowspan=2) 183 | 184 | # Tilt slider 185 | def tilt_this(tilt_var): 186 | c.pantilt(0, int(tilt_var)) 187 | Label(root, text='Tilt', bg=pan_tilt_color, width=button_width).grid(row=slider_row, column=slider_column+1) 188 | tilt_slider = Scale(root, bg=pan_tilt_color, from_=24, to=-24, length=200, command=tilt_this) 189 | tilt_slider.set(0) 190 | tilt_slider.bind("", reset_sliders) 191 | tilt_slider.grid(row=slider_row+1, column=slider_column+1, rowspan=8) 192 | 193 | 194 | # Zoom slider 195 | def zoom_this(zoom_var): 196 | c.zoom(int(zoom_var)) 197 | Label(root, text='Zoom', bg=zoom_color, width=button_width).grid(row=slider_row, column=slider_column+2) 198 | zoom_slider = Scale(root, bg=zoom_color, from_=7, to=-7, length=200, command=zoom_this) 199 | zoom_slider.set(0) 200 | zoom_slider.bind("", reset_sliders) 201 | zoom_slider.grid(row=slider_row+1, column=slider_column+2, rowspan=8) 202 | 203 | # Focus slider 204 | def focus_this(focus_var): 205 | c.manual_focus(int(focus_var)) 206 | Label(root, text='Focus', bg=focus_color, width=button_width).grid(row=slider_row, column=slider_column+3) 207 | focus_slider = Scale(root, bg=focus_color, from_=7, to=-7, length=200, command=focus_this) 208 | focus_slider.set(0) 209 | focus_slider.bind("", reset_sliders) 210 | focus_slider.grid(row=slider_row+1, column=slider_column+3, rowspan=8) 211 | 212 | # Focus buttons 213 | def one_push_focus(): 214 | c.set_focus_mode('auto') 215 | c.set_autofocus_mode('one push trigger') 216 | Label(root, text='Focus', bg=focus_color, width=button_width).grid(row=focus_buttons_row, column=on_off_column) 217 | Button(root, text='Auto', bg=focus_color, width=button_width, command=lambda: c.set_focus_mode('auto')).grid(row=focus_buttons_row+1, column=on_off_column) 218 | Button(root, text='Manual', bg=focus_color, width=button_width, command=lambda: c.set_focus_mode('manual')).grid(row=focus_buttons_row+2, column=on_off_column) 219 | #Button(root, text='One Push', bg=focus_color, width=button_width, command=one_push_focus).grid(row=focus_buttons_row+3, column=on_off_column) 220 | 221 | # On off connect buttons 222 | Label(root, text='Camera', bg=on_off_color, width=button_width).grid(row=on_off_row, column=on_off_column) 223 | Button(root, text='On', bg=on_off_color, width=button_width, command=lambda: c.set_power(True)).grid(row=on_off_row+1, column=on_off_column) 224 | Button(root, text='Off', bg=on_off_color, width=button_width, command=lambda: c.set_power(False)).grid(row=on_off_row+2, column=on_off_column) 225 | Button(root, text='Info Off', bg=on_off_color, width=button_width, command=lambda: c.info_display(False)).grid(row=on_off_row+3, column=on_off_column) 226 | 227 | # IP Label 228 | #Label(root, text=camera_ip+':'+str(camera_port)).grid(row=6, column=0, columnspan=3) 229 | # Connection Label 230 | #Label(root, textvariable=display_message).grid(row=6, column=4, columnspan=3) 231 | 232 | root.bind('', key_press) # so we have keyboard events triggering that function 233 | root.mainloop() -------------------------------------------------------------------------------- /visca_control_gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # https://pro.sony/s3/2019/01/16020225/AES6100121.pdf 3 | # https://gitlab.viarezo.fr/2018corona/viscaoverip/blob/master/camera2.py 4 | # sudo apt install python3-tk 5 | 6 | import socket 7 | import binascii # for printing the messages we send, not really necessary 8 | from time import sleep 9 | 10 | camera_ip = '192.168.0.100' 11 | #camera_ip = '127.0.0.1' 12 | camera_port = 52381 13 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # IPv4, UDP 14 | # for receiving 15 | #buffer_size = 1024 16 | #s.bind(('', camera_port)) # use the port one higher than the camera's port 17 | #s.settimeout(1) # only wait for a response for 1 second 18 | 19 | 20 | # Payloads 21 | received_message = '' # a place to store the OSC messages we'll receive 22 | sequence_number = 1 # a global variable that we'll iterate each command, remember 0x0001 23 | reset_sequence_number = '02 00 00 01 00 00 00 01 01' 24 | 25 | camera_on = '81 01 04 00 02 FF' 26 | camera_off = '81 01 04 00 03 FF' 27 | 28 | information_display_on = '81 01 7E 01 18 02 FF' 29 | information_display_off = '81 01 7E 01 18 03 FF' 30 | 31 | zoom_stop = '81 01 04 07 00 FF' 32 | zoom_tele = '81 01 04 07 02 FF' 33 | zoom_wide = '81 01 04 07 03 FF' 34 | zoom_tele_variable = '81 01 04 07 2p FF' # p=0 (Low) to 7 (High) 35 | zoom_wide_variable = '81 01 04 07 3p FF' # p=0 (Low) to 7 (High) 36 | zoom_direct = '81 01 04 47 0p 0q 0r 0s FF' # pqrs: Zoom Position 37 | 38 | memory_reset = '81 01 04 3F 00 0p FF' 39 | memory_set = '81 01 04 3F 01 0p FF' # p: Memory number (=0 to F) 40 | memory_recall = '81 01 04 3F 02 0p FF' # p: Memory number (=0 to F) 41 | 42 | #Pan-tilt Drive 43 | # VV: Pan speed setting 0x01 (low speed) to 0x18 44 | # WW: Tilt speed setting 0x01 (low speed) to 0x17 45 | movement_speed = '05' 46 | pan_speed = movement_speed 47 | tilt_speed = movement_speed 48 | 49 | # YYYY: Pan Position DE00 to 2200 (CENTER 0000) 50 | # ZZZZ: Tilt Position FC00 to 1200 (CENTER 0000) 51 | #YYYY = '0000' 52 | #ZZZZ = '0000' 53 | pan_up = '81 01 06 01 VV WW 03 01 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 54 | pan_down = '81 01 06 01 VV WW 03 02 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 55 | pan_left = '81 01 06 01 VV WW 01 03 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 56 | pan_right = '81 01 06 01 VV WW 02 03 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 57 | pan_up_left = '81 01 06 01 VV WW 01 01 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 58 | pan_up_right = '81 01 06 01 VV WW 02 01 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 59 | pan_down_left = '81 01 06 01 VV WW 01 02 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 60 | pan_down_right = '81 01 06 01 VV WW 02 02 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 61 | pan_stop = '81 01 06 01 VV WW 03 03 FF'.replace('VV', str(pan_speed)).replace('WW', str(tilt_speed)) 62 | #pan_absolute_position = '81 01 06 02 VV WW 0Y 0Y 0Y 0Y 0Z 0Z 0Z 0Z FF'.replace('VV', str(VV)) #YYYY[0] 63 | #pan_relative_position = '81 01 06 03 VV WW 0Y 0Y 0Y 0Y 0Z 0Z 0Z 0Z FF'.replace('VV', str(VV)) 64 | pan_home = '81 01 06 04 FF' 65 | pan_reset = '81 01 06 05 FF' 66 | zoom_direct = '81 01 04 47 0p 0q 0r 0s FF' # pqrs: Zoom Position 67 | zoom_focus_direct = '81 01 04 47 0p 0q 0r 0s 0t 0u 0v 0w FF' # pqrs: Zoom Position tuvw: Focus Position 68 | 69 | inquiry_lens_control = '81 09 7E 7E 00 FF' 70 | # response: 81 50 0p 0q 0r 0s 0H 0L 0t 0u 0v 0w 00 xx xx FF 71 | inquiry_camera_control = '81 09 7E 7E 01 FF' 72 | 73 | focus_stop = '81 01 04 08 00 FF' 74 | focus_far = '81 01 04 08 02 FF' 75 | focus_near = '81 01 04 08 03 FF' 76 | focus_far_variable = '81 01 04 08 2p FF'.replace('p', '7') # 0 low to 7 high 77 | focus_near_variable = '81 01 04 08 3p FF'.replace('p', '7') # 0 low to 7 high 78 | focus_direct = '81 01 04 48 0p 0q 0r 0s FF' #.replace('p', ) q, r, s 79 | focus_auto = '81 01 04 38 02 FF' 80 | focus_manual = '81 01 04 38 03 FF' 81 | focus_infinity = '81 01 04 18 02 FF' 82 | 83 | def memory_recall_function(memory_number): 84 | send_message(information_display_off) # otherwise we see a message on the camera output 85 | sleep(0.25) 86 | message_string = memory_recall.replace('p', str(memory_number)) 87 | message = send_message(message_string) 88 | sleep(1) 89 | send_message(information_display_off) # to make sure it doesn't display "done" 90 | return message 91 | 92 | def memory_set_function(memory_number): 93 | hexadecimal_memory_number = hex(memory_number)[-1] 94 | message_string = memory_set.replace('p', hexadecimal_memory_number) 95 | #print(message_string) 96 | message = send_message(message_string) 97 | return message 98 | 99 | def send_message(message_string): 100 | global sequence_number 101 | #global received_message 102 | payload_type = bytearray.fromhex('01 00') 103 | payload = bytearray.fromhex(message_string) 104 | payload_length = len(payload).to_bytes(2, 'big') 105 | message = payload_type + payload_length + sequence_number.to_bytes(4, 'big') + payload 106 | #if message_string == reset_sequence_number: 107 | # sequence_number = 1 108 | # #sequence_number = 4294967295 109 | #else: 110 | # sequence_number += 1 111 | sequence_number += 1 112 | s.sendto(message, (camera_ip, camera_port)) 113 | #print(binascii.hexlify(message), 'sent to', camera_ip, camera_port, sequence_number) 114 | # add a timeout in case we don't hear back 115 | ''' 116 | try: 117 | data = s.recvfrom(buffer_size) 118 | received_message = binascii.hexlify(data[0]) 119 | print('Received', received_message) 120 | data = s.recvfrom(buffer_size) 121 | received_message = binascii.hexlify(data[0]) 122 | print('Received', received_message) 123 | except socket.timeout: # s.settimeout(2.0) #above 124 | received_message = 'No response from camera' 125 | print(received_message) 126 | 127 | #if received_message == b'01110003000000119051ff': 128 | if received_message[0:4] == '0111': 129 | display_message.set('Connected') 130 | else: 131 | display_message.set(received_message[0:4]) 132 | #''' 133 | return received_message 134 | 135 | #def pan(direction): 136 | def pan(): 137 | pan_speed = hex(pan_speed_slider.get())[2:] 138 | tilt_speed = hex(tilt_speed_slider.get())[2:] 139 | if len(pan_speed) == 1: 140 | pan_speed = '0'+pan_speed 141 | if len(tilt_speed) == 1: 142 | tilt_speed = '0'+tilt_speed 143 | print(len(pan_speed), pan_speed, len(tilt_speed), tilt_speed) 144 | 145 | def save_preset_labels(): 146 | with open('preset_labels.txt', 'w') as f: 147 | for entry in entry_boxes: 148 | f.write(entry.get()) 149 | f.write('\n') 150 | f.close() 151 | 152 | def reset_sequence_number_function(): 153 | global sequence_number 154 | reset_sequence_number_message = bytearray.fromhex('02 00 00 01 00 00 00 01 01') 155 | s.sendto(reset_sequence_number_message,(camera_ip, camera_port)) 156 | sequence_number = 1 157 | return sequence_number 158 | 159 | # start by resetting the sequence number 160 | reset_sequence_number_function() 161 | 162 | # GUI 163 | 164 | from tkinter import Tk, StringVar, Button, Label, Entry, Scale, W 165 | root = Tk() 166 | display_message = StringVar() 167 | root.title('VISCA IP Camera Controller') 168 | root['background'] = 'white' 169 | #Label(root, text='VISCA IP Camera Controller').grid(row=0, column=0, columnspan=100) 170 | 171 | store_column = 0 172 | label_column = 1 173 | recall_column = 2 174 | pan_tilt_column = 5 175 | pan_tilt_row = 1 176 | zoom_column = 3 177 | zoom_row = 4 178 | 179 | focus_column = 3 180 | focus_row = 8 181 | on_off_column = 3 182 | on_off_row = 11 183 | button_width = 8 184 | store_color = 'red' 185 | recall_color = 'light grey' 186 | pan_tilt_color = 'white' 187 | zoom_color = 'light blue' 188 | focus_color = 'cyan' 189 | on_off_color = 'violet' 190 | 191 | # Preset store buttons 192 | Label(root, text='Store', bg=store_color).grid(row=1, column=store_column) 193 | Button(root, text=0, width=3, bg=store_color, command=lambda: memory_set_function(0)).grid(row=2, column=store_column) 194 | Button(root, text=1, width=3, bg=store_color, command=lambda: memory_set_function(1)).grid(row=3, column=store_column) 195 | Button(root, text=2, width=3, bg=store_color, command=lambda: memory_set_function(2)).grid(row=4, column=store_column) 196 | Button(root, text=3, width=3, bg=store_color, command=lambda: memory_set_function(3)).grid(row=5, column=store_column) 197 | Button(root, text=4, width=3, bg=store_color, command=lambda: memory_set_function(4)).grid(row=6, column=store_column) 198 | Button(root, text=5, width=3, bg=store_color, command=lambda: memory_set_function(5)).grid(row=7, column=store_column) 199 | Button(root, text=6, width=3, bg=store_color, command=lambda: memory_set_function(6)).grid(row=8, column=store_column) 200 | Button(root, text=7, width=3, bg=store_color, command=lambda: memory_set_function(7)).grid(row=9, column=store_column) 201 | Button(root, text=8, width=3, bg=store_color, command=lambda: memory_set_function(8)).grid(row=10, column=store_column) 202 | Button(root, text=9, width=3, bg=store_color, command=lambda: memory_set_function(9)).grid(row=11, column=store_column) 203 | Button(root, text='A', width=3, bg=store_color, command=lambda: memory_set_function(10)).grid(row=12, column=store_column) 204 | Button(root, text='B', width=3, bg=store_color, command=lambda: memory_set_function(11)).grid(row=13, column=store_column) 205 | Button(root, text='C', width=3, bg=store_color, command=lambda: memory_set_function(12)).grid(row=14, column=store_column) 206 | Button(root, text='D', width=3, bg=store_color, command=lambda: memory_set_function(13)).grid(row=15, column=store_column) 207 | Button(root, text='E', width=3, bg=store_color, command=lambda: memory_set_function(14)).grid(row=16, column=store_column) 208 | Button(root, text='F', width=3, bg=store_color, command=lambda: memory_set_function(15)).grid(row=17, column=store_column) 209 | 210 | # Recall buttons and entries (as labels) 211 | Label(root, text='Recall', bg=recall_color).grid(row=1, column=recall_column) 212 | Button(root, text=0, width=5, bg=recall_color, command=lambda: memory_recall_function(0)).grid(row=2, column=recall_column) 213 | Button(root, text=1, width=5, bg=recall_color, command=lambda: memory_recall_function(1)).grid(row=3, column=recall_column) 214 | Button(root, text=2, width=5, bg=recall_color, command=lambda: memory_recall_function(2)).grid(row=4, column=recall_column) 215 | Button(root, text=3, width=5, bg=recall_color, command=lambda: memory_recall_function(3)).grid(row=5, column=recall_column) 216 | Button(root, text=4, width=5, bg=recall_color, command=lambda: memory_recall_function(4)).grid(row=6, column=recall_column) 217 | Button(root, text=5, width=5, bg=recall_color, command=lambda: memory_recall_function(5)).grid(row=7, column=recall_column) 218 | Button(root, text=6, width=5, bg=recall_color, command=lambda: memory_recall_function(6)).grid(row=8, column=recall_column) 219 | Button(root, text=7, width=5, bg=recall_color, command=lambda: memory_recall_function(7)).grid(row=9, column=recall_column) 220 | Button(root, text=8, width=5, bg=recall_color, command=lambda: memory_recall_function(8)).grid(row=10, column=recall_column) 221 | Button(root, text=9, width=5, bg=recall_color, command=lambda: memory_recall_function(9)).grid(row=11, column=recall_column) 222 | Button(root, text='A', width=5, bg=recall_color, command=lambda: memory_recall_function('A')).grid(row=12, column=recall_column) 223 | Button(root, text='B', width=5, bg=recall_color, command=lambda: memory_recall_function('B')).grid(row=13, column=recall_column) 224 | Button(root, text='C', width=5, bg=recall_color, command=lambda: memory_recall_function('C')).grid(row=14, column=recall_column) 225 | Button(root, text='D', width=5, bg=recall_color, command=lambda: memory_recall_function('D')).grid(row=15, column=recall_column) 226 | Button(root, text='E', width=5, bg=recall_color, command=lambda: memory_recall_function('E')).grid(row=16, column=recall_column) 227 | Button(root, text='F', width=5, bg=recall_color, command=lambda: memory_recall_function('F')).grid(row=17, column=recall_column) 228 | try: 229 | with open('preset_labels.txt') as f: 230 | labels = f.read().splitlines() 231 | f.close() 232 | except: 233 | pass 234 | entry_boxes = [] 235 | for e in range(16): 236 | box = Entry(root, justify='right') 237 | try: 238 | box.insert(-1, labels[e]) 239 | except: 240 | pass 241 | box.grid(row=e+2, column=label_column) 242 | entry_boxes.append(box) 243 | Button(root, text='Save preset labels', bg=store_color, command=save_preset_labels).grid(row=1, column=label_column) 244 | 245 | # Pan and tilt buttons 246 | Button(root, text='↑', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up)).grid(row=pan_tilt_row, column=pan_tilt_column+1) 247 | Button(root, text='←', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_left)).grid(row=pan_tilt_row+1, column=pan_tilt_column) 248 | Button(root, text='→', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_right)).grid(row=pan_tilt_row+1, column=pan_tilt_column+2) 249 | Button(root, text='↓', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 250 | Button(root, text='↖', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_left)).grid(row=pan_tilt_row, column=pan_tilt_column) 251 | Button(root, text='↗', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_up_right)).grid(row=pan_tilt_row, column=pan_tilt_column+2) 252 | Button(root, text='↙', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_left)).grid(row=pan_tilt_row+2, column=pan_tilt_column) 253 | Button(root, text='↘', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_down_right)).grid(row=pan_tilt_row+2, column=pan_tilt_column+2) 254 | Button(root, text='■', width=3, bg=pan_tilt_color, command=lambda: send_message(pan_stop)).grid(row=pan_tilt_row+1, column=pan_tilt_column+1) 255 | #Button(root, text='Home', command=lambda: send_message(pan_home)).grid(row=pan_tilt_row+2, column=pan_tilt_column+1) 256 | 257 | # Pan speed and Tilt speed 258 | ''' 259 | Label(root, text='Pan Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column) 260 | pan_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 261 | pan_speed_slider.set(5) 262 | pan_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column, rowspan=4) 263 | Label(root, text='Tilt Speed', bg=pan_tilt_color).grid(row=pan_tilt_row+3, column=pan_tilt_column+1) 264 | tilt_speed_slider = Scale(root, from_=24, to=0, bg=pan_tilt_color) 265 | tilt_speed_slider.set(5) 266 | tilt_speed_slider.grid(row=pan_tilt_row+4, column=pan_tilt_column+1, rowspan=4) 267 | #Button(root, text='test pan speed', command=lambda: pan()).grid(row=pan_tilt_row+5, column=pan_tilt_column+1) 268 | 269 | # slider to set speed for pan_speed and tilt_speed (0x01 to 0x17) 270 | # still not quite sure about this... 271 | #Scale(root, from_=0, to=17, variable=movement_speed, orient=HORIZONTAL, label='Speed').grid(row=5, column=2, columnspan=3) 272 | #''' 273 | 274 | # Zoom buttons 275 | Label(root, text='Zoom', bg=zoom_color, width=button_width).grid(row=zoom_row, column=zoom_column) 276 | Button(root, text='In', bg=zoom_color, width=button_width, command=lambda: send_message(zoom_tele)).grid(row=zoom_row+1, column=zoom_column) 277 | Button(root, text='Stop', bg=zoom_color, width=button_width, command=lambda: send_message(zoom_stop)).grid(row=zoom_row+2, column=zoom_column) 278 | Button(root, text='Out', bg=zoom_color, width=button_width, command=lambda: send_message(zoom_wide)).grid(row=zoom_row+3, column=zoom_column) 279 | # Focus buttons 280 | #Label(root, text='Focus', width=button_width, bg=focus_color).grid(row=focus_row, column=focus_column) 281 | #Button(root, text='Near', width=button_width, bg=focus_color, command=lambda: send_message(focus_near)).grid(row=focus_row+1, column=focus_column) 282 | #Button(root, text='Far', width=button_width, bg=focus_color, command=lambda: send_message(focus_far)).grid(row=focus_row+2, column=focus_column) 283 | 284 | # On off connect buttons 285 | Label(root, text='Camera', bg=on_off_color, width=button_width).grid(row=on_off_row, column=on_off_column) 286 | Button(root, text='On', bg=on_off_color, width=button_width, command=lambda: send_message(camera_on)).grid(row=on_off_row+1, column=on_off_column) 287 | Button(root, text='Connect', bg=on_off_color, width=button_width, command=reset_sequence_number_function()).grid(row=on_off_row+2, column=on_off_column) 288 | Button(root, text='Off', bg=on_off_color, width=button_width, command=lambda: send_message(camera_off)).grid(row=on_off_row+3, column=on_off_column) 289 | Button(root, text='Info Off', bg=on_off_color, width=button_width, command=lambda: send_message(information_display_off)).grid(row=on_off_row+4, column=on_off_column) 290 | 291 | # IP Label 292 | #Label(root, text=camera_ip+':'+str(camera_port)).grid(row=6, column=0, columnspan=3) 293 | # Connection Label 294 | #Label(root, textvariable=display_message).grid(row=6, column=4, columnspan=3) 295 | 296 | root.mainloop() 297 | --------------------------------------------------------------------------------