Best Python code snippet using playwright-python
structure_set_gui.py
Source:structure_set_gui.py
1'''2Created on Aug 26 20173@author: Greg Salomons4GUI interface for DirScan.5Classes6 DirGui:7 Primary GUI window8 sub class of TKinter.Frame9'''10import tkinter.filedialog as tkf11import tkinter as tk12from tkinter import messagebox13from pathlib import Path14from functools import partial15from structure_set_parameters import ParametersBase16from structure_set_parameters import DirScanParameters17from file_type_definitions import FileTypeError18from file_type_definitions import FileTypes19#from dir_scan_parser import parse_dir_scan20#from dir_scan import scan_and_parse21# pylint: disable=too-many-ancestors22class GuiBase(object):23 '''Base class for Gui frames containing defaults and parameters.24 '''25 def __init__(self, scan_param=None):26 '''Principle Scan parameters27 '''28 if not scan_param:29 scan_param = DirScanParameters()30 self.data = scan_param31class GuiFrameBase(tk.Frame, GuiBase):32 '''Base class for Gui frames containing defaults and parameters.33 '''34 def __init__(self, scan_param=None, owner_frame=tk.Tk(), **kwargs):35 '''initialize the TK frame and link it with the frame it is36 embedded in.37 '''38 self.update_master_frame(self, owner_frame)39 tk.Frame.__init__(owner_frame, **kwargs)40 DirGuiBase.__init__(scan_param)41 def update_master_frame(self, master_frame):42 ''' Update the calling TK frame.43 '''44 if issubclass(master_frame, tk.Tk):45 self.master_frame = master_frame46 else:47 raise TypeError('master_frame must be a TK widget or frame.\n\t'48 'Got type %'%type(master_frame))49 def action_message(self, message_type=None, **kwargs):50 '''Generic message box51 message_type can be one of:52 'showinfo' (default)53 'showwarning'54 'showerror'55 'askretrycancel'56 Parameter options are:57 title='This is a message box',58 message='This is the message',59 detail='Some more details',60 '''61 if not message_type:62 return messagebox.showinfo(parent=self, **kwargs)63 elif 'askretrycancel' in message_type:64 return messagebox.askretrycancel(parent=self, **kwargs)65 elif 'showerror' in message_type:66 return messagebox.showerror(parent=self, **kwargs)67 elif 'showwarning' in message_type:68 return messagebox.showwarning(parent=self, **kwargs)69 elif 'showinfo' in message_type:70 return messagebox.showinfo(parent=self, **kwargs)71 else:72 raise ValueError('message_type must be one of:\t\nshowinfo\t'73 '(default)\n\t'74 'showwarning\n\t'75 'showerror\n\t'76 'askretrycancel')77 def build(self, build_instructions):78 '''Configure the GUI frame and add sub-frames.79 This method may be overwritten for sub-classes.80 Parameter:81 build_instructions: Type list of length 3 tuples.82 Each tuple contains:83 (sub-GUI object, 'pack'|'grid', pack or grid keyword dict)84 '''85 for (subgui, method, kwargs) in build_instructions:86 if not kwargs:87 kwargs = {'fill': tk.X, 'padx': 10, 'pady': 5, 'side': tk.LEFT}88 subgui.pack(**kwargs)89 grid(column=1, row=1,pady=0, padx=0, sticky=tk.E)90class SelectFileParameters(ParametersBase):91 '''Contains all parameters required for selecting a file or directory.92 Class Attributes:93 action_types:94 Type: list of strings95 A list of valid action types96 Default = ['save', 'dir' 'open']97 action_types_str:98 Type: string99 A string defining all valid action types.100 Built from action_types101 Default = ['save', 'dir' 'open']102 valid_file_types103 Type: list of strings104 A list of valid file types105 Default = list of keys from FileTypes.file_types106 valid_types_str107 Type: string108 A string defining all valid file types.109 Built from valid_file_types110 Instance Attributes:111 heading112 Type: str113 The window title for the selection dialog.114 Default = ''115 file_types116 Type: str117 A string describing a directory or type of file to select.118 must reference an item in the FileTypes class119 Default = 'All Files'120 type_select121 Type: FileTypes122 Defined based on file_types value123 Default = None124 action125 Type: str126 A string indicating if the file is to be opened for reading or127 writing.128 One of 'save' or 'open'.129 Default = 'save'130 starting_path131 Type: Path132 The initial path, possibly including a default file name, for the133 file selection.134 Default = base_path135 initial_file_string136 Type: str137 The initial file name for the file selection.138 Default = None139 extension140 Type: str141 The initial extension for the file selection.142 Default = None143 The boolean option attribute:144 exist145 Indicates whether the selected file or directory must already146 exist.147 Default = False148 overwrite149 Indicates whether to silently overwrite existing files or send a150 warning message.151 Default = False152 Methods153 __init__154 Set attributes155 Verify that attributes are reasonable156 The following methods are used to check and update parameters157 update_update_heading(heading)158 update_file_types(file_types)159 update_action(action)160 update_starting_path(starting_path)161 update_initial_file_string(initial_file_string)162 update_extension(extension)163 The following methods set boolean options to True or False164 set_exist(exist)165 set_overwrite(overwrite)166 '''167 # Initialize class level parameters168 action_types = ['save', 'dir' 'open']169 action_types_str = '["' + '",\n\t"'.join(ft for ft in action_types) + '"]'170 valid_file_types = list(ft for ft in FileTypes.file_types)171 valid_file_types_str = '["' +\172 '",\n\t"'.join(ft for ft in valid_file_types) +\173 '"]'174 #The following method initializes parameters175 def __init__(self, base_path=None, **kwargs):176 '''177 Initialize all parameters, using default values if a value is178 not supplied179 '''180 # Initialize all parameters using default values181 super().__init__(base_path, **kwargs)182 self.heading = ''183 self.file_types = 'All Files'184 self.type_select = None185 self.action = 'save'186 self.starting_path = self.base_path187 self.initial_file_string = None188 self.extension = None189 # Initialize boolean option attributes, using default values190 exist = False191 overwrite = False192 # Set all passed parameter values193 self.update_parameters(base_path, **kwargs)194 #The following methods are used to check and update parameters195 def update_heading(self, heading):196 ''' Update the window title.197 '''198 try:199 self.heading = str(heading)200 except TypeError as err:201 raise err('heading must be a string.\n\t'202 'Got type %'%type(heading))203 # If directory_path is a string treat it as a partial and204 # combine it with the base path, then check that the directory exits.205 if self.do_dir_scan:206 scan_path = self.valid_dir(self.insert_base_path(directory_path))207 self.directory_to_scan = scan_path208 def update_file_types(self, file_types):209 ''' Update the allowable file types to select from.210 '''211 if 'dir' in file_types:212 self.file_types = 'dir'213 self.type_select = None214 elif isinstance(file_types,str):215 if file_types in self.valid_file_types:216 self.file_types = [file_types]217 self.type_select = FileTypes(self.file_types)218 else:219 raise TypeError('file_types must be a string or '220 'a list of strings with the following '221 'values:\n\t' + self.valid_file_types_str)222 else:223 try:224 self.filetypes = list(file_types)225 except TypeError as err:226 raise err('file_types must be a string or a list of strings.'227 '\n\tGot type %'%type(file_types))228 else:229 self.type_select = FileTypes(self.file_types)230 def update_action(self, action):231 ''' Update the type of file selection.232 '''233 if action in self.action_types:234 self.action = str(action)235 else:236 raise TypeError('action must be one of: \n\t' +237 self.action_types_str)238 def update_starting_path(self, starting_path):239 ''' Update the starting file selection path.240 '''241 file_path = self.insert_base_path(starting_path)242 if file_path.is_file():243 self.starting_path = file_path.parent244 if not self.initial_file_string:245 self.initial_file_string = file_path.name246 self.extension = file_path.suffix247 elif self.valid_dir(file_path):248 self.starting_path = file_path249 else:250 raise TypeError('starting_path must type Path or type str.\n\t'251 'Got type %'%type(starting_path))252 def update_initial_file_string(self, initial_file_string):253 ''' Update the default file name.254 '''255 try:256 self.initial_file_string = str(initial_file_string)257 except TypeError as err:258 raise err('initial_file_string must be a string.\n\t'259 'Got type %'%type(initial_file_string))260 # If directory_path is a string treat it as a partial and261 # combine it with the base path, then check that the directory exits.262 if self.do_dir_scan:263 scan_path = self.valid_dir(self.insert_base_path(directory_path))264 self.directory_to_scan = scan_path265 # If directory_path is a string treat it as a partial and266 # combine it with the base path, then check that the directory exits.267 if self.do_dir_scan:268 scan_path = self.valid_dir(self.insert_base_path(directory_path))269 self.directory_to_scan = scan_path270 def update_extension(self, extension):271 ''' Update the default extension.272 '''273 try:274 self.extension = str(extension)275 except TypeError as err:276 raise err('extension must be a string.\n\t'277 'Got type %'%type(extension))278 # The following methods set boolean options to True or False279 def set_exist(self, exist: bool):280 '''Indicate whether a directory or file should already exist.281 Parameter282 exist: Type bool283 True: Raise error if file does not exist.284 False: Do not check if the file exists.285 Raises286 TypeError287 '''288 try:289 self.exist = bool(exist)290 except TypeError as err:291 raise err('exist must be True or False')292 def set_overwrite(self, overwrite: bool):293 '''Indicate whether a file should be silently overwritten if it294 already exists.295 Parameter296 overwrite: Type bool297 True: Do not check if the file exists.298 False: Raise warning if the file exists.299 Raises300 TypeError301 '''302 try:303 self.overwrite = bool(overwrite)304 except TypeError as err:305 raise err('overwrite must be True or False')306##307class DirGuiElementFrame(DirGuiBase):308 '''DirGui Base class for selecting or changing a specific309 DirScanParameters element.310 '''311 # TK variable types to link with parameter values312 var_type_dict = {313 'string': tk.StringVar,314 'int': tk.IntVar315 }316 def __init__(self, parameter_name='base_path', var_type='string',317 scan_param=DirScanParameters(), **kwargs):318 '''Build a frame and link the access variable to a specific parameter.319 '''320 super().__init__(**kwargs)321 var_select = self.var_type_dict[var_type]322 self.select_var = var_select()323 self.parameter = parameter_name324 def set(self, select_value: str):325 '''Set the path_string frame variable.326 '''327 self.select_var.set(select_value)328 def get(self):329 '''Get the path_string frame variable.330 '''331 return self.select_var.get()332 def initialize(self):333 '''Initialize the Gui variable from the initial DirScanParameters values.334 '''335 value = self.data.__getattribute__(self.parameter)336 self.set(value)337 def update(self):338 '''Update the DirScanParameters data element from the Gui variable.339 '''340 param = {self.parameter: self.get()}341 self.data.update_parameters(self.empty_label.get())342 def build(self, **kwargs):343 '''Configure the GUI frame and add sub-frames.344 This method is to be overwritten for each sub-class.345 '''346 self.initialize()347 super().build(self, **kwargs)348class DirGuiLabelFrame(DirGuiBase, tk.LabelFrame):349 '''DirGui Base class for grouping one or more DirGuiElementFrames350 together within a labeled frame.351 '''352 def __init__(self, form_title=None, **kwargs):353 '''Build the frame and define the access variable354 '''355 super().__init__(owner_frame, text=form_title, **kwargs)356 def build(self, **kwargs):357 '''Configure the GUI frame and add sub-frames.358 This method is to be overwritten for each sub-class.359 '''360 self.initialize()361 super().build(self, **kwargs)362class FileSelectGui(DirGuiElementFrame, SelectFileParameters):363 '''GUI frame for selecting a file or directory.364 sub class of TKinter.LabelFrame.365 used inside the InputPathsFrame and OutputPathsFrame.366 '''367 def select_file_dialog(self):368 '''Open a dialog to select a file or directory.369 Returns the selected file or directory370 '''371 starting_path = self.starting_path372 if self.action is 'save':373 select_method = tkf.asksaveasfilename374 else:375 select_method = tkf.askopenfilename376 if 'dir' in self.file_types:377 selected_file_string = \378 tkf.askdirectory(parent=self.master_frame,379 title=file_parameters.heading,380 mustexist=file_parameters.exist,381 initialdir=file_parameters.starting_path)382 else:383 extension = file_parameters.extension384 if not extension:385 extension = '.txt'386 selected_file_string = select_method(387 parent=file_parameters.master_frame,388 title=file_parameters.heading,389 initialdir=file_parameters.starting_path,390 initialfile=file_parameters.initial_file_string,391 defaultextension=extension,392 filetypes=file_parameters.type_select)393 # ToDo Move confirm overwrite to parameters driven method394 # confirmoverwrite=exist,395 return selected_file_string396 def build(self, select_cmd):397 file_show = tk.Entry(self, textvariable=self.select_var, width=100)398 select_file = tk.Button(self, text='Browse', command=select_cmd)399 file_show.pack(fill=tk.X, padx=10, pady=5, side=tk.LEFT)400 select_file.pack(padx=5, pady=5, side=tk.RIGHT)401 #if selected_file_string is not '':402 # try:403 # file_select = Path(selected_file_string)404 # file_parem_set(file_select)405 # except (TypeError, FileTypeError) as err:406 # #TODO add warning to status407 # print("OS error: {0}".format(err))408 # else:409 # master_frame.set(selected_file_string)410class InputSelectFrame(DirGuiSubFrame):411 '''GUI frame for selecting whether to scan a directory or parse a file.412 used inside the InputPathsFrame.413 '''414 def build(self):415 '''Build the frame to select a file or directory.416 Add the form to select a directory to scan.417 '''418 choose_dir = tk.Radiobutton(self, text="", variable=self.select_var, value=0)419 choose_file = tk.Radiobutton(self, text="",variable=self.select_var, value=1)420 choose_dir.grid(column=1, row=1,pady=0, padx=0, sticky=tk.E)421 choose_file.grid(column=1, row=2,pady=0, padx=0, sticky=tk.E)422class InputPathsFrame(DirGuiFrame):423 '''GUI frame for selecting the file or directory path to scan or parse.424 used inside the main GUI.425 '''426 def get_input_option(self,choose_widget):427 '''Set a radial button widget variable to the current input option428 from parameters as an integer.429 0 Scan a directory430 1 Parse a file.431 '''432 if self.data.do_dir_scan:433 choose_widget.set(0)434 else:435 choose_widget.set(1)436 def set_input_option(self, input_select):437 '''Update the parameter flag to indicate whether to scan a directory438 or parse a file.439 '''440 if input_select == 0:441 self.data.update_do_dir_scan(True)442 else:443 self.data.update_do_dir_scan(False)444 def set_choice(self, value, choose_widget, event):445 '''Update the radial button widget variable and the parameter flag to446 indicate whether to scan a directory or parse a file.447 The event parameter is from the binding and is not used.448 '''449 self.set_input_option(value)450 choose_widget.set(value)451 def build(self):452 '''Build the frame to select a file or directory.453 Add the form to select either a directory to scan or a file to parse.454 '''455 # Add the select buttons456 select_source_header = 'Select the directory to scan or file to parse.'457 choose_source = InputSelectFrame(self, select_source_header, 'int')458 choose_source.build()459 choose_source.grid(column=1, row=1, rowspan=2, pady=3, padx=0,460 sticky=tk.E)461 self.get_input_option(choose_source)462 self.choose_source = choose_source463 # Add the directory selection464 select_dir_header = 'Select the directory to scan.'465 select_dir = FileSelectGui(choose_source, select_dir_header)466 if self.data.directory_to_scan:467 select_dir.set(str(self.data.directory_to_scan))468 dir_select_dialog = partial(469 select_file_dialog,470 master_frame=select_dir,471 heading=select_dir_header,472 file_parem_set=self.data.update_directory_to_scan,473 filetypes='dir',474 exist=True)475 select_dir.build(dir_select_dialog)476 select_dir.grid(column=2, row=1, columnspan=2,477 pady=3, padx=0, sticky=tk.W)478 choose_dir = partial(self.set_choice, 0, choose_source)479 select_dir.bind('<FocusIn>', choose_dir)480 self.select_dir = select_dir481 # Add the file source selection482 select_file_header = 'Select the file to parse.'483 select_file = FileSelectGui(choose_source, select_file_header)484 if self.data.file_to_scan:485 select_file.set(str(self.data.file_to_scan))486 file_select_dialog = partial(487 select_file_dialog,488 master_frame=select_file,489 heading=select_file_header,490 file_parem_set=self.data.update_file_to_scan,491 filetypes='Text File',492 action='open',493 exist=True)494 select_file.build(file_select_dialog)495 select_file.grid(column=2, row=2, columnspan=2,496 pady=3, padx=0, sticky=tk.W)497 choose_file = partial(self.set_choice, 1, choose_source)498 select_file.bind('<FocusIn>', choose_file)499 self.select_file = select_file500 def update(self):501 '''Update all data values from the GUI sub-frames.502 '''503 self.data.update_directory_to_scan(self.select_dir.get())504 self.data.update_file_to_scan(self.select_file.get())505 self.set_input_option(self.choose_source.select_var.get())506class OutputFileSelectGui(DirGuiSubFrame):507 '''GUI frame for selecting the file or directory path to scan or parse.508 used inside the main GUI.509 '''510 def __init__(self, owner_frame, header=None, var_type='string'):511 '''Build the frame and define the select and file path variable.512 '''513 super().__init__(owner_frame, form_title=header)514 self.use_file = tk.IntVar()515 def set_choice(self, value, event=None):516 '''Update the check box to indicate whether use the file.517 The event parameter is from the binding and is not used.518 '''519 self.use_file.set(int(value))520 def get_choice(self, event=None):521 '''Return true or false if box selected.522 The event parameter is from the binding and is not used.523 '''524 return bool(self.use_file.get())525 def change_choice(self, event=None):526 '''Return true or false if box selected.527 The event parameter is from the binding and is not used.528 '''529 current_val = bool(self.use_file.get())530 flip = int(not(current_val))531 self.use_file.set(flip)532 def build(self, select_cmd):533 '''Build the frame to select an output file and usage Check Box.534 '''535 # Add the select button536 file_checkbox = tk.Checkbutton(self, text="", variable=self.use_file)537 file_checkbox.pack(pady=0, padx=0, side=tk.LEFT)538 # Add the directory selection539 file_show = tk.Entry(self, textvariable=self.select_var, width=100)540 select_file = tk.Button(self, text='Browse', command=select_cmd)541 file_show.pack(fill=tk.X, padx=10, pady=5, side=tk.LEFT)542 select_file.pack(padx=5, pady=5, side=tk.RIGHT)543 choose_file = partial(self.set_choice, 1)544 file_show.bind('<FocusIn>', choose_file)545class OutputPathsFrame(DirGuiFrame):546 '''GUI frame for selecting the files to store the output from547 Scan and Parse.548 used inside the main GUI.549 '''550 def build(self):551 '''Build the frame to select a file or directory.552 Add the form to select a directory to scan.553 '''554 # TODO add check that at least one output is selected in GUI555 # Add frame to select a file to save the DIR output556 scan_output_header = 'Select the file to save the DIR scan output'557 scan_output_select = OutputFileSelectGui(self, scan_output_header)558 scan_output_select.set(str(self.data.directory_scan_output))559 scan_output_select.set_choice(int(self.data.save_scan_output))560 scan_output_dialog = partial(561 select_file_dialog,562 master_frame=scan_output_select,563 heading=scan_output_header,564 file_parem_set=self.data.update_directory_scan_output,565 filetypes='Text File',566 action='save')567 scan_output_select.build(scan_output_dialog)568 scan_output_select.grid(column=1, row=1, columnspan=4,569 pady=3, padx=10, sticky=tk.W)570 self.scan_output_file = scan_output_select571 # Add frame to select a file to save file data from the parse output572 file_data_header = 'Select the file to save the parsed file info.'573 file_data_select = OutputFileSelectGui(self, file_data_header)574 file_data_select.set(str(self.data.file_data_output))575 file_data_select.set_choice(int(self.data.output_file_data))576 file_data_dialog = partial(577 select_file_dialog,578 master_frame=file_data_select,579 heading=file_data_header,580 file_parem_set=self.data.update_file_data_output,581 filetypes=['Comma Separated Variable File', 'Excel Files'],582 action='save',583 extension='.csv')584 file_data_select.build(file_data_dialog)585 file_data_select.grid(column=1, row=2, columnspan=4,586 pady=3, padx=10, sticky=tk.W)587 self.file_data_output = file_data_select588 # Add frame to select a file to save directory data from the parse output589 dir_data_header = 'Select the file to save the parsed directory info.'590 dir_data_select = OutputFileSelectGui(self, dir_data_header)591 dir_data_select.set(str(self.data.dir_data_output))592 dir_data_select.set_choice(int(self.data.output_dir_data))593 dir_data_dialog = partial(594 select_file_dialog,595 master_frame=dir_data_select,596 heading=dir_data_header,597 file_parem_set=self.data.update_dir_data_output,598 filetypes=['Comma Separated Variable File', 'Excel Files'],599 action='save',600 extension='.csv')601 dir_data_select.build(dir_data_dialog)602 dir_data_select.grid(column=1, row=3, columnspan=4,603 pady=3, padx=10, sticky=tk.W)604 self.dir_data_output = dir_data_select605 def update(self):606 '''Update all data values from the GUI sub-frames.607 This method is to be overwritten for each sub-class.608 '''609 self.data.update_directory_scan_output(self.scan_output_file.get())610 self.data.update_file_data_output(self.file_data_output.get())611 self.data.update_dir_data_output(self.dir_data_output.get())612 self.data.update_save_scan_output(self.scan_output_file.get_choice())613 self.data.update_output_file_data(self.file_data_output.get_choice())614 self.data.update_output_dir_data(self.dir_data_output.get_choice())615class ActionButtonsFrame(DirGuiFrame):616 '''Add the buttons to start or cancel the scan.617 '''618 def build(self):619 '''Configure the GUI frame and add sub-frames.620 This method is to be overwritten for each sub-class.621 '''622 #self.status = 'Making Selections'623 action_label = self.data.action_text()624 run = self.master.run_method625 cancel = self.master.cancel_method626 self.run_button = tk.Button(self, text=action_label, command=run)627 self.cancel_button = tk.Button(self, text='Cancel', command=cancel)628 self.run_button.grid(column=1, row=1, padx=5)629 self.cancel_button.grid(column=2, row=1, padx=5)630 def update(self):631 '''Update all data values from the GUI sub-frames.632 This method is to be overwritten for each sub-class.633 '''634 action_label = self.data.action_text()635 self.run_button.config(text=action_label)636class StatusTextFrame(DirGuiFrame):637 '''GUI frame for indicating current status of the Actions.638 '''639 def build(self, initial_status='Enter Selections'):640 '''Build the frame to display the status.641 '''642 self.master.status_text.set(initial_status)643 status_box = tk.Label(self, textvariable=self.master.status_text)644 status_box.pack()645class DirGui(tk.Frame):646 '''TKinter GUI class used for the DIR Scan program main GUI.647 '''648 def __init__(self, scan_param: DirScanParameters, master, run_cmd):649 '''Create the DIR Scan GUI and set the initial parameters650 '''651 super().__init__(master)652 self.data = scan_param653 self.run_method = partial(self.update_and_run, run_cmd)654 self.cancel_method = master.destroy655 self.status_text = tk.StringVar()656 def window_format(self):657 '''Format and label main GUI window.658 Add a window title,659 Add a window icon,660 Add a header label661 '''662 root = self._root()663 root.title("Directory Scan")664 # Add a window icon665 ico_pict = r'.\Icon test.png'666 root.iconphoto(root, tk.PhotoImage(file=ico_pict))667 #Add Top header668 header = tk.Label(self, text='Directory Scan')669 header.config(font=('system', 20, 'bold'))670 header.grid(column=1, row=1, columnspan=3)671 def build(self):672 '''Configure the main GUI window and add sub-frames.673 '''674 self.window_format()675 input_select_frame = InputPathsFrame(self.data, self)676 input_select_frame.build()677 input_select_frame.grid(column=1, row=2, columnspan=3,678 padx=10, sticky=tk.W)679 self.input_select = input_select_frame680 output_select_frame = OutputPathsFrame(self.data, self)681 output_select_frame.build()682 output_select_frame.grid(column=1, row=3, columnspan=3,683 padx=10, sticky=tk.W)684 self.output_select = output_select_frame685 action_buttons_frame = ActionButtonsFrame(self.data, self)686 action_buttons_frame.build()687 action_buttons_frame.grid(column=1, row=5, columnspan=3, pady=2)688 status_message_frame = StatusTextFrame(self.data, self)689 status_message_frame.build()690 status_message_frame.grid(column=1, row=6, columnspan=3,691 padx=10, sticky=tk.W)692 def update(self):693 '''Update all scan and parse parameters from the GUI frames.694 '''695 self.input_select.update()696 self.output_select.update()697 def update_and_run(self, run_cmd):698 '''Set all values for the scan parameters from the GUI.699 '''700 self.update()701 # Perhaps split up scan and parse commands702 run_cmd(self.data, self)703def activate_gui(scan_param: DirScanParameters, run_cmd):704 '''Activate the GUI and return the selected parameters.705 '''706 root = tk.Tk()707 dir_gui = DirGui(scan_param, root, run_cmd)708 dir_gui.build()709 dir_gui.pack()710 root.mainloop()711 return dir_gui712def main():713 '''Test the activate_gui function call.714 '''715 def param_string(scan_param: DirScanParameters):716 '''Display the parameters in a pop-up message a test function.717 '''718 param_dict = {719 'dir_command_switches': str(scan_param.dir_command_switches),720 'file_data_variables': str(scan_param.file_data_variables),721 'dir_summary_variables': str(scan_param.dir_summary_variables),722 'base_path': str(scan_param.base_path),723 'directory_to_scan': str(scan_param.directory_to_scan),724 'file_to_scan': str(scan_param.file_to_scan),725 'do_dir_scan': str(scan_param.do_dir_scan),726 'parse_dir_data': str(scan_param.parse_dir_data),727 'directory_scan_output': str(scan_param.directory_scan_output),728 'save_scan_output': str(scan_param.save_scan_output),729 'file_data_output': str(scan_param.file_data_output),730 'output_file_data': str(scan_param.output_file_data),731 'dir_data_output': str(scan_param.dir_data_output),732 'output_dir_data': str(scan_param.output_dir_data),733 'file_data_sheet': str(scan_param.file_data_sheet),734 'dir_data_sheet': str(scan_param.dir_data_sheet),735 'top_dir': str(scan_param.top_dir),736 'source': str(scan_param.source),737 'time_type': str(scan_param.time_type)738 }739 param_text = 'Scan Parameters'740 param_text += '\n' + 'base_path = {base_path}'741 param_text += '\n' + 'directory to scan = {directory_to_scan}'742 param_text += '\n' + 'file to parse = {file_to_scan}'743 param_text += '\n' + 'Scan a directory = {do_dir_scan}'744 param_text += '\n' + 'Parse dir data = {parse_dir_data}'745 param_text += '\n' + 'File to save directory scan = {directory_scan_output}'746 param_text += '\n' + 'Save scan output = {save_scan_output}'747 param_text += '\n' + 'File-data output file = {file_data_output}'748 param_text += '\n' + 'Save file data = {output_file_data}'749 param_text += '\n' + 'Dir-data output file = {dir_data_output}'750 param_text += '\n' + 'Save dir data = {output_dir_data}'751 param_text = param_text.format(**param_dict)752 return param_text753 def test_message(scan_param: DirScanParameters):754 '''Display a message box containing parameter info.755 '''756 message_text = param_string(scan_param)757 results = messagebox.showinfo(title='Parameters',758 message=message_text)759 test_scan_param = DirScanParameters(\760 base_path=Path('.'),761 file_to_scan='Test_Files.txt',762 time_type="C",763 source='Test Files',764 top_dir=Path('.'),765 file_data_output='Test_files_data.csv',766 output_dir_data=False,767 dir_data_output='Test_dir_data.csv')768 #run_cmd = partial(test_message, test_scan_param)769 dir_gui = activate_gui(test_scan_param, test_message)770if __name__ == '__main__':...
test_element_handle.py
Source:test_element_handle.py
...128 await utils.attach_frame(page, "frame1", server.EMPTY_PAGE)129 frame = page.frames[1]130 element_handle = await frame.evaluate_handle("document.documentElement")131 assert await element_handle.content_frame() is None132async def test_owner_frame(page, server, utils):133 await page.goto(server.EMPTY_PAGE)134 await utils.attach_frame(page, "frame1", server.EMPTY_PAGE)135 frame = page.frames[1]136 element_handle = await frame.evaluate_handle("document.body")137 assert await element_handle.owner_frame() == frame138async def test_owner_frame_for_cross_process_iframes(page, server, utils):139 await page.goto(server.EMPTY_PAGE)140 await utils.attach_frame(141 page, "frame1", server.CROSS_PROCESS_PREFIX + "/empty.html"142 )143 frame = page.frames[1]144 element_handle = await frame.evaluate_handle("document.body")145 assert await element_handle.owner_frame() == frame146async def test_owner_frame_for_document(page, server, utils):147 await page.goto(server.EMPTY_PAGE)148 await utils.attach_frame(page, "frame1", server.EMPTY_PAGE)149 frame = page.frames[1]150 element_handle = await frame.evaluate_handle("document")151 assert await element_handle.owner_frame() == frame152async def test_owner_frame_for_iframe_elements(page, server, utils):153 await page.goto(server.EMPTY_PAGE)154 await utils.attach_frame(page, "frame1", server.EMPTY_PAGE)155 frame = page.main_frame156 element_handle = await frame.evaluate_handle('document.querySelector("#frame1")')157 assert await element_handle.owner_frame() == frame158async def test_owner_frame_for_cross_frame_evaluations(page, server, utils):159 await page.goto(server.EMPTY_PAGE)160 await utils.attach_frame(page, "frame1", server.EMPTY_PAGE)161 frame = page.main_frame162 element_handle = await frame.evaluate_handle(163 'document.querySelector("#frame1").contentWindow.document.body'164 )165 assert await element_handle.owner_frame() == frame.child_frames[0]166async def test_owner_frame_for_detached_elements(page, server):167 await page.goto(server.EMPTY_PAGE)168 div_handle = await page.evaluate_handle(169 """() => {170 div = document.createElement('div');171 document.body.appendChild(div);172 return div;173 }"""174 )175 assert await div_handle.owner_frame() == page.main_frame176 await page.evaluate(177 """() => {178 div = document.querySelector('div')179 document.body.removeChild(div)180 }"""181 )182 assert await div_handle.owner_frame() == page.main_frame183async def test_owner_frame_for_adopted_elements(page, server):184 await page.goto(server.EMPTY_PAGE)185 async with page.expect_popup() as popup_info:186 await page.evaluate(187 "url => window.__popup = window.open(url)", server.EMPTY_PAGE188 )189 popup = await popup_info.value190 div_handle = await page.evaluate_handle(191 """() => {192 div = document.createElement('div');193 document.body.appendChild(div);194 return div;195 }"""196 )197 assert await div_handle.owner_frame() == page.main_frame198 await popup.wait_for_load_state("domcontentloaded")199 await page.evaluate(200 """() => {201 div = document.querySelector('div');202 window.__popup.document.body.appendChild(div);203 }"""204 )205 assert await div_handle.owner_frame() == popup.main_frame206async def test_click(page, server):207 await page.goto(server.PREFIX + "/input/button.html")208 button = await page.query_selector("button")209 await button.click()210 assert await page.evaluate("result") == "Clicked"211async def test_click_with_node_removed(page, server):212 await page.goto(server.PREFIX + "/input/button.html")213 await page.evaluate('delete window["Node"]')214 button = await page.query_selector("button")215 await button.click()216 assert await page.evaluate("result") == "Clicked"217async def test_click_for_shadow_dom_v1(page, server):218 await page.goto(server.PREFIX + "/shadow.html")219 button_handle = await page.evaluate_handle("button")...
parser.py
Source:parser.py
1import re2import json3def parse_netlog(file, match_id, user_id):4 regex = re.compile("\d\d\d\d-\d\d-\d\dT\d\d-\d\d-\d\d")5 windows = []6 incoming = [0]7 outgoing = [0]8 time = [0]9 filetext = file.read()10 filetext = filetext.decode('UTF-8')11 for line in filetext.splitlines():12 try:13 if regex.match(line):14 continue15 tokens = line.split(',')16 time.append(int(tokens[0]))17 incoming.append(int(tokens[2]))18 outgoing.append(int(tokens[3]))19 time_window = int(tokens[0])20 ping_window = int(tokens[8])21 jitter_window = int(tokens[16])22 try:23 loss_percentage_window = float(tokens[15]) * 10024 except ValueError:25 # fill in zero for -nan(ind) error in netlog26 loss_percentage_window = float(0)27 in_bandwidth_window = (incoming[-1] - incoming[-2]) / ((time[-1] - time[-2]))28 out_bandwidth_window = (outgoing[-1] - outgoing[-2]) / ((time[-1] - time[-2]))29 windows.append({30 'match_id': match_id,31 'user_id': user_id,32 'time': time_window,33 'ping': ping_window,34 'jitter': jitter_window,35 'in_bandwidth': in_bandwidth_window,36 'out_bandwidth': out_bandwidth_window,37 'loss': loss_percentage_window})38 except Exception as ex:39 print('2: ' + str(ex))40 return windows41def parse_match(match, user_id, username):42 teams = {43 'winner': [],44 'loser': [],45 }46 win = False47 try:48 for participant in match['participantIdentities']:49 participant_id = participant['participantId']50 participant_data = match['participants'][participant_id - 1]51 participant_to_save = {52 'name': participant['player']['summonerName'],53 'champion': participant_data['championId'],54 'profile_icon_id': participant['player']['profileIcon'],55 'id': participant['participantId'],56 'role': participant_data['timeline']['role'],57 'lane': participant_data['timeline']['lane'],58 'kills': participant_data['stats']['kills'],59 'deaths': participant_data['stats']['deaths'],60 'assists': participant_data['stats']['assists'],61 'total_dmg': participant_data['stats']['totalDamageDealt'],62 'total_damage_taken': participant_data['stats']['totalDamageDealt'],63 'gold_earned': participant_data['stats']['goldEarned'],64 }65 if participant_data['stats']['win']:66 teams['winner'].append(participant_to_save)67 else:68 teams['loser'].append(participant_to_save)69 if str(participant_to_save['name']).casefold() == str(username).casefold():70 win = participant_data['stats']['win']71 match_to_save = {72 'user_id': user_id,73 'match_id': match['gameId'],74 'queue_id': match['queueId'],75 'game_type': match['gameType'],76 'game_duration': match['gameDuration'],77 'game_start': match['gameCreation'],78 'platform_id': match['platformId'],79 'season_id': match['seasonId'],80 'map_id': match['mapId'],81 'game_mode': match['gameMode'],82 'teams': json.dumps(teams),83 'won': win84 }85 return match_to_save86 except Exception as ex:87 print('1: ' + str(ex))88 return False89def parse_event(event, pov_id, match_id, events, user_id):90 if event['type'] == 'CHAMPION_KILL':91 if (event['killerId'] == pov_id or event['victimId'] == pov_id):92 type = 'CHAMPION_KILL' if event['killerId'] == pov_id else 'CHAMPION_DEATH'93 events.append({94 "match_id": match_id,95 "user_id": user_id,96 "timestamp": event['timestamp'],97 "x": event['position']['x'],98 "y": event['position']['y'],99 "active_participant": event['killerId'],100 "passive_participant": event['victimId'],101 "assisting_participants": event['assistingParticipantIds'],102 "type": type,103 })104 elif event['type'] == 'WARD_PLACED':105 if (event['creatorId'] == pov_id):106 events.append({107 "match_id": match_id,108 "user_id": user_id,109 "timestamp": event['timestamp'],110 "x": 0,111 "y": 0,112 "active_participant": event['creatorId'],113 "passive_participant": '',114 "assisting_participants": [],115 "type": event['type'],116 })117 elif event['type'] == 'WARD_KILL':118 if event['killerId'] == pov_id:119 events.append({120 "match_id": match_id,121 "user_id": user_id,122 "timestamp": event['timestamp'],123 "x": 0,124 "y": 0,125 "active_participant": event['killerId'],126 "passive_participant": '',127 "assisting_participants": [],128 "type": event['type'],129 })130 elif event['type'] == 'BUILDING_KILL':131 if event['killerId'] == pov_id:132 events.append({133 "match_id": match_id,134 "user_id": user_id,135 "timestamp": event['timestamp'],136 "x": event['position']['x'],137 "y": event['position']['y'],138 "active_participant": event['killerId'],139 "passive_participant": event['buildingType'],140 "assisting_participants": event['assistingParticipantIds'],141 "type": event['type'],142 })143 elif event['type'] == 'ELITE_MONSTER_KILL':144 if event['killerId'] == pov_id:145 events.append({146 "match_id": match_id,147 "user_id": user_id,148 "timestamp": event['timestamp'],149 "x": event['position']['x'],150 "y": event['position']['y'],151 "active_participant": event['killerId'],152 "passive_participant": event['monsterType'],153 "assisting_participants": [],154 "type": event['type'],155 })156 elif event['type'] == 'ITEM_PURCHASED':157 if event['participantId'] == pov_id:158 events.append({159 "match_id": match_id,160 "user_id": user_id,161 "timestamp": event['timestamp'],162 "x": 0,163 "y": 0,164 "active_participant": event['participantId'],165 "passive_participant": event['itemId'],166 "assisting_participants": [],167 "type": event['type'],168 })169 elif event['type'] == 'ITEM_SOLD':170 if event['participantId'] == pov_id:171 events.append({172 "match_id": match_id,173 "user_id": user_id,174 "timestamp": event['timestamp'],175 "x": 0,176 "y": 0,177 "active_participant": event['participantId'],178 "passive_participant": event['itemId'],179 "assisting_participants": [],180 "type": event['type'],181 })182 elif event['type'] == 'ITEM_DESTROYED':183 if event['participantId'] == pov_id:184 events.append({185 "match_id": match_id,186 "user_id": user_id,187 "timestamp": event['timestamp'],188 "x": 0,189 "y": 0,190 "active_participant": event['participantId'],191 "passive_participant": event['itemId'],192 "assisting_participants": [],193 "type": event['type'],194 })195 elif event['type'] == 'ITEM_UNDO':196 if event['participantId'] == pov_id:197 events.append({198 "match_id": match_id,199 "user_id": user_id,200 "timestamp": event['timestamp'],201 "x": 0,202 "y": 0,203 "active_participant": event['participantId'],204 "passive_participant": '',205 "assisting_participants": [],206 "type": event['type'],207 })208 elif event['type'] == 'SKILL_LEVEL_UP':209 if event['participantId'] == pov_id:210 events.append({211 "match_id": match_id,212 "user_id": user_id,213 "timestamp": event['timestamp'],214 "x": 0,215 "y": 0,216 "active_participant": event['participantId'],217 "passive_participant": '',218 "assisting_participants": [],219 "type": event['type'],220 })221 elif event['type'] == 'ASCENDED_EVENT':222 if event['participantId'] == pov_id or event['killerId'] == pov_id:223 events.append({224 "match_id": match_id,225 "user_id": user_id,226 "timestamp": event['timestamp'],227 "x": 0,228 "y": 0,229 "active_participant": event['participantId'],230 "passive_participant": '',231 "assisting_participants": [],232 "type": event['type'],233 })234 elif event['type'] == 'CAPTURE_POINT':235 if event['participantId'] == pov_id or event['killerId'] == pov_id:236 events.append({237 "match_id": match_id,238 "user_id": user_id,239 "timestamp": event['timestamp'],240 "x": 0,241 "y": 0,242 "active_participant": event['participantId'],243 "passive_participant": '',244 "assisting_participants": [],245 "type": event['type'],246 })247 elif event['type'] == 'PORO_KING_SUMMON':248 if event['participantId'] == pov_id or event['killerId'] == pov_id:249 events.append({250 "match_id": match_id,251 "user_id": user_id,252 "timestamp": event['timestamp'],253 "x": 0,254 "y": 0,255 "active_participant": event['participantId'],256 "passive_participant": '',257 "assisting_participants": [],258 "type": event['type'],259 })260def parse_timeline(timeline, log_owner_id, match_id, user_id):261 events = []262 frames = []263 for frame in timeline['frames']:264 try:265 owner_frame = frame['participantFrames'][str(log_owner_id)]266 frames.append({267 'user_id': user_id,268 'match_id': match_id,269 'timestamp': frame['timestamp'],270 'exp': owner_frame['xp'],271 'gold': owner_frame['totalGold'],272 'creep_score': owner_frame['minionsKilled'],273 'neutral_score': owner_frame['jungleMinionsKilled'],274 'level': owner_frame['level'],275 })276 for event in frame['events']:277 parse_event(event, log_owner_id, match_id, events, user_id)278 except Exception as ex:279 print('3: ' + str(ex))280 return frames, events281# def safeAssign(current_object, keys):282# for i in range(0, len(keys)):283# if current_object is not None and hasattr(current_object, keys[i]):284# print('get attr ' + keys[i] + 'from ' + str(current_object))285# current_object = getattr(current_object, keys[i])286# print('gottem')287# else:288# print(str(current_object) + ' has no attr ' + keys[i])289# return ''...
main.py
Source:main.py
1import copy2from imutils.video import FPS3import imutils4from static_object import *5from intensity_processing import *6from pathlib import Path7import numpy as np8import os9import sys10import cv211import tensorflow as tf12from queue import Queue13from matplotlib import pyplot as plt14from object_detection.utils import label_map_util15from object_detection.utils import visualization_utils as vis_util16from app_utils import draw_boxes_and_labels17sys.path.append("..")18MODEL_NAME = '/home/pcroot/Documents/models/research/object_detection/left_luggage/final2_training'19# Path to frozen detection graph. This is the actual model that is used for the object detection.20PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb'21# List of the strings that is used to add correct label for each box.22PATH_TO_LABELS = os.path.join('/home/pcroot/Documents/models/research/object_detection/training', 'object-detection.pbtxt')23NUM_CLASSES = 224gamma = 1.725# Loading label map26label_map = label_map_util.load_labelmap(PATH_TO_LABELS)27categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES,28 use_display_name=True)29category_index = label_map_util.create_category_index(categories)30IMAGE_SIZE = (12, 8)31def load_image_into_numpy_array(image):32 (im_width, im_height) = image.size33 return np.array(image.getdata()).reshape(34 (im_height, im_width, 3)).astype(np.uint8)35def detect_objects(image_np, sess, detection_graph):36 # Definite input and output Tensors for detection_graph37 image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')38 # Each box represents a part of the image where a particular object was detected.39 detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')40 # Each score represent how level of confidence for each of the objects.41 # Score is shown on the result image, together with the class label.42 detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')43 detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')44 num_detections = detection_graph.get_tensor_by_name('num_detections:0')45 46 47 # Expand dimensions since the model expects images to have shape: [1, None, None, 3]48 image_np_expanded = np.expand_dims(image_np, axis=0)49 # Actual detection.50 (boxes, scores, classes, num) = sess.run(51 [detection_boxes, detection_scores, detection_classes, num_detections],52 feed_dict={image_tensor: image_np_expanded})53 # Visualization of the results of a detection.54 rect_points, class_names, class_colors = draw_boxes_and_labels(55 boxes=np.squeeze(boxes),56 classes=np.squeeze(classes).astype(np.int32),57 scores=np.squeeze(scores),58 category_index=category_index,59 min_score_thresh=.560 )61 return dict(rect_points=rect_points, class_names=class_names, class_colors=class_colors)62def check_bbox_not_moved(bbox_last_frame_proposals, bbox_current_frame_proposals, old_frame, current_frame):63 bbox_to_add = []64 if len(bbox_last_frame_proposals) > 0: # not on first frame of video65 for old in bbox_last_frame_proposals:66 old_drawn = False67 for curr in bbox_current_frame_proposals:68 if rect_similarity2(old, curr):69 old_drawn = True70 break71 if not old_drawn:72 # Check if the area defined by the bounding box in the old frame and in the new one is still the same73 old_section = old_frame[old[1]:old[1] + old[3], old[0]:old[0] + old[2]].flatten()74 new_section = current_frame[old[1]:old[1] + old[3], old[0]:old[0] + old[2]].flatten()75 if norm_correlate(old_section, new_section)[0] > 0.9:76 bbox_to_add.append(old)77 return bbox_to_add78if __name__ == '__main__':79 detection_graph = tf.Graph()80 with detection_graph.as_default():81 od_graph_def = tf.GraphDef()82 with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:83 serialized_graph = fid.read()84 od_graph_def.ParseFromString(serialized_graph)85 tf.import_graph_def(od_graph_def, name='')86 config = tf.ConfigProto(87 device_count = {'GPU': 0}88 )89 sess = tf.Session(graph=detection_graph, config=config)90 my_file = Path("/home/pcroot/Documents/documents/videos/out1.mp4")91 if not my_file.is_file():92 print("Video does not exist")93 exit()94 95 stream = cv2.VideoCapture("/home/pcroot/Documents/documents/videos/use8.mp4")96# stream = cv2.VideoCapture(0)97 fps = FPS().start()98 first_run = True99 (ret, frame) = stream.read()100 while not ret:101 (ret, frame) = stream.read()102 103 frame = imutils.resize(frame, width=450)104 adjusted = adjust_gamma(frame, gamma=gamma) # gamma correction105 frame = adjusted106 (height, width, channel) = frame.shape107 image_shape = (height, width)108 rgb = IntensityProcessing(image_shape)109 110 bbox_last_frame_proposals = []111 static_objects = []112 count=0113 n_frame=0114 while 1:115 (ret, frame) = stream.read()116 if not ret:117 break118 else:119 frame = imutils.resize(frame, width=450)120 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)121 frame = np.dstack([frame, frame, frame])122 123 rgb.current_frame = frame # .getNumpy()124 if first_run:125 old_rgb_frame = copy.copy(rgb.current_frame) # old frame is the new frame126 first_run = False127 128 rgb.compute_foreground_masks(rgb.current_frame) # compute foreground masks129 rgb.update_detection_aggregator() # detect if new object proposed130 131 rgb_proposal_bbox = rgb.extract_proposal_bbox() # bounding boxes of the areas proposed132 foreground_rgb_proposal = rgb.proposal_foreground # rgb proposals133 134 bbox_current_frame_proposals = rgb_proposal_bbox135 final_result_image = rgb.current_frame.copy()136 137 old_bbox_still_present = check_bbox_not_moved(bbox_last_frame_proposals, bbox_current_frame_proposals,138 old_rgb_frame, rgb.current_frame.copy())139 140 # add the old bbox still present in the current frame to the bbox detected141 bbox_last_frame_proposals = bbox_current_frame_proposals + old_bbox_still_present142 143 '''144 145 BBOX_LAST_FRAME_PROPOSALS BU FRAME DE OLAN TUM BBOX DEGERLERI LISTE OLARAK VAR.146 147 '''148 old_rgb_frame = rgb.current_frame.copy()149 150 # static object ######################151 if len(bbox_last_frame_proposals) > 0: # not on first frame of video152 for old in bbox_last_frame_proposals:153 old_drawn = False154 for curr in static_objects:155 if rect_similarity2(curr.bbox_info, old):156 old_drawn = True157 break158 if not old_drawn:159 owner_frame = rgb.current_frame.copy()160 # draw_bounding_box2(owner_frame, old)161 count+=1162 163 frame_rgb = cv2.cvtColor(dim_image(owner_frame, old), cv2.COLOR_BGR2RGB)164# image_np = load_image_into_numpy_array(owner_frame)165 data = detect_objects(frame_rgb, sess, detection_graph)166 rec_points = data['rect_points']167 class_names = data['class_names']168 class_colors = data['class_colors']169 for point, name, color in zip(rec_points, class_names, class_colors):170 cv2.rectangle(rgb.current_frame, (int(point['xmin'] * width), int(point['ymin'] * height)),171 (int(point['xmax'] * width), int(point['ymax'] * height)), color, 3)172 cv2.rectangle(rgb.current_frame, (int(point['xmin'] * width), int(point['ymin'] * height)),173 (int(point['xmin'] * width) + len(name[0]) * 6,174 int(point['ymin'] * height) - 10), color, -1, cv2.LINE_AA)175 cv2.putText(rgb.current_frame, name[0], (int(point['xmin'] * width), int(point['ymin'] * height)), cv2.FONT_HERSHEY_SIMPLEX,176 0.3, (0, 0, 0), 1)177 cv2.imshow('Final Result', rgb.current_frame)178 179 180 181 static_objects.append(StaticObject(old, owner_frame, 0))182 183 count=0184 185 186 cv2.imshow('Original Frame', final_result_image)187 cv2.imshow('Background Modelling Result', foreground_rgb_proposal)188# cv2.imshow('frame', frame)189 n_frame+=1190 k = cv2.waitKey(25)191 fps.update()192 fps.stop()193 print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))194 print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))195 print("[INFO] number of frame: {}".format(n_frame))196 stream.release()...
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!